feat(fs-search): spawn the packaged ripgrep binary through the subprocess seam
glob/grep now run the @vscode/ripgrep binary via ctx.subprocess with a plain argv vector: no system rg install, no shell layer, unconditional registration. The load-time command -v rg probe and the bash-seam coupling are removed; timeouts ride the cooperative exec.signal plus the seam's terminate escalation. The fs-glob-sampling ACP snapshot executes the real packaged binary against an mtime-pinned fixture. Adds the packaged-ripgrep-search Agent Note, updates the roster-note facts and both shipped-composition e2es, and regenerates the doc catalogs and third-party notices (surfacing pre-existing manifest drift plus the new @vscode/ripgrep row; the notices generator also learns pnpm 11's truncated virtual-store names).
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
import { fileURLToPath } from 'node:url'
|
||||
import { readFileSync } from 'node:fs'
|
||||
import { mkdir, writeFile } from 'node:fs/promises'
|
||||
import { mkdir, utimes, writeFile } from 'node:fs/promises'
|
||||
import { dirname, join } from 'node:path'
|
||||
import { homedir } from 'node:os'
|
||||
import { expect, it } from 'vitest'
|
||||
@@ -44,7 +44,6 @@ const SESSION_TITLE_CONFIG = fileURLToPath(new URL('../session-title.cordis.yml'
|
||||
const LSP_CONFIG = fileURLToPath(new URL('./lsp.cordis.yml', import.meta.url))
|
||||
const WEB_CONFIG = fileURLToPath(new URL('../web.cordis.yml', import.meta.url))
|
||||
const FS_SEARCH_CONFIG = fileURLToPath(new URL('./fs-search.cordis.yml', import.meta.url))
|
||||
const FS_SEARCH_BIN = fileURLToPath(new URL('./fixtures/fs-search-bin', import.meta.url))
|
||||
const SNAPSHOTS_DIR = join(dirname(fileURLToPath(import.meta.url)), 'snapshots')
|
||||
const PACKED_CHUNKS_SOURCE = 'hook-cc-pretool-deny'
|
||||
|
||||
@@ -57,6 +56,33 @@ async function prepareDelimiterPathWorkspace(cwd: string): Promise<void> {
|
||||
])
|
||||
}
|
||||
|
||||
/**
|
||||
* Seed the over-cap glob fixture: eight files under `tree/` with fixed mtimes,
|
||||
* so the packaged ripgrep's `--sort=modified` order is deterministic — three
|
||||
* files under `archive/`, one each under `docs/`, `src/`, and `test/`, plus
|
||||
* two flat files (six top-level entries). Scoping the search to `tree/` keeps
|
||||
* the harness's own session artifacts out of the listing.
|
||||
*/
|
||||
async function prepareFsSearchWorkspace(cwd: string): Promise<void> {
|
||||
const tree = join(cwd, 'tree')
|
||||
const files: Array<[relative: string, mtime: Date]> = [
|
||||
[join('archive', 'a.ts'), new Date(2000, 0, 1, 0, 0, 0, 1)],
|
||||
[join('archive', 'b.ts'), new Date(2000, 0, 1, 0, 0, 0, 2)],
|
||||
[join('archive', 'c.ts'), new Date(2000, 0, 1, 0, 0, 0, 3)],
|
||||
[join('docs', 'guide.md'), new Date(2000, 0, 1, 0, 0, 0, 4)],
|
||||
[join('src', 'index.ts'), new Date(2000, 0, 1, 0, 0, 0, 5)],
|
||||
[join('test', 'spec.ts'), new Date(2000, 0, 1, 0, 0, 0, 6)],
|
||||
['top.txt', new Date(2000, 0, 1, 0, 0, 0, 7)],
|
||||
['notes.md', new Date(2000, 0, 1, 0, 0, 0, 8)],
|
||||
]
|
||||
for (const [relative, mtime] of files) {
|
||||
const target = join(tree, relative)
|
||||
await mkdir(dirname(target), { recursive: true })
|
||||
await writeFile(target, 'fixture\n')
|
||||
await utimes(target, mtime, mtime)
|
||||
}
|
||||
}
|
||||
|
||||
// FIXME: Migrate backend-oriented scenarios to the headless stream-json suite;
|
||||
// this ACP suite should eventually retain only automation-protocol contracts.
|
||||
|
||||
@@ -150,9 +176,12 @@ const SCENARIOS: Scenario[] = [
|
||||
hasModelTurn: true,
|
||||
recorded: true,
|
||||
},
|
||||
// The real Loader/app/bash path executes a deterministic rg stand-in at the
|
||||
// external-process seam, pinning over-cap glob sampling without depending on
|
||||
// a host-installed ripgrep binary.
|
||||
// The real Loader/app/subprocess path executes the PACKAGED ripgrep binary
|
||||
// against a prepared workspace whose fixed mtimes pin the
|
||||
// `--sort=modified` order, pinning over-cap glob sampling without depending
|
||||
// on a host-installed ripgrep binary or a PATH stand-in. POSIX-only because
|
||||
// the displayed paths carry `/` separators the session-log comparison
|
||||
// cannot normalize.
|
||||
{
|
||||
name: 'fs-glob-sampling',
|
||||
hasModelTurn: true,
|
||||
@@ -160,7 +189,7 @@ const SCENARIOS: Scenario[] = [
|
||||
pinsHeader: true,
|
||||
headerClass: 'fs-search',
|
||||
configPath: FS_SEARCH_CONFIG,
|
||||
env: { PATH: `${FS_SEARCH_BIN}:${process.env.PATH ?? ''}` },
|
||||
prepareWorkspace: prepareFsSearchWorkspace,
|
||||
posixOnly: true,
|
||||
},
|
||||
{ name: 'fs-read', hasModelTurn: true, recorded: true },
|
||||
|
||||
@@ -1,10 +0,0 @@
|
||||
#!/bin/sh
|
||||
printf '%s\n' \
|
||||
'archive/a.ts' \
|
||||
'archive/b.ts' \
|
||||
'archive/c.ts' \
|
||||
'old\one' \
|
||||
'old\two' \
|
||||
'src/index.ts' \
|
||||
'docs/guide.md' \
|
||||
'test/spec.ts'
|
||||
@@ -2,6 +2,6 @@
|
||||
"steps": [
|
||||
{ "op": "initialize" },
|
||||
{ "op": "newSession" },
|
||||
{ "op": "prompt", "text": "Call glob exactly once with pattern * and no path. Then reply with exactly GLOB_SAMPLED and nothing else." }
|
||||
{ "op": "prompt", "text": "Call glob exactly once with pattern * and path tree. Then reply with exactly GLOB_SAMPLED and nothing else." }
|
||||
]
|
||||
}
|
||||
|
||||
@@ -1,18 +1,18 @@
|
||||
{"type":"session","version":0,"id":"f5a99d52-3eaa-4ce7-858d-61d4fd77df2a","createdAt":1785218400000,"cwd":"{{cwd}}","delegationDepth":0}
|
||||
{"type":"turn/start","seq":0,"time":1785218400001,"data":{"turn":1,"trigger":{"kind":"message","source":{"kind":"user"}}}}
|
||||
{"type":"user/message","seq":1,"time":1785218400002,"data":{"content":[{"type":"text","text":"Call glob exactly once with pattern * and no path. Then reply with exactly GLOB_SAMPLED and nothing else."}],"source":{"kind":"user"},"role":"user","id":"6790985f-1de2-42f8-a7f1-24e46d6439c7"},"surfaceOp":"append"}
|
||||
{"type":"user/message","seq":1,"time":1785218400002,"data":{"content":[{"type":"text","text":"Call glob exactly once with pattern * and path tree. Then reply with exactly GLOB_SAMPLED and nothing else."}],"source":{"kind":"user"},"role":"user","id":"6790985f-1de2-42f8-a7f1-24e46d6439c7"},"surfaceOp":"append"}
|
||||
{"type":"session/title","seq":2,"time":1785218400003,"data":{"title":"Call glob exactly once with","messageSeqs":[1],"source":{"kind":"fallback"}}}
|
||||
{"type":"step/start","seq":3,"time":1785218400004,"data":{"turn":1,"step":1}}
|
||||
{"type":"request/header","seq":4,"time":1785218400005,"data":{"header":{"config":{"provider":"deepseek","model":"deepseek-v4-pro"},"system":"{{system}}","tools":"{{tools}}"},"reason":"initial"}}
|
||||
{"type":"request/context","seq":5,"time":1785483397569,"data":{"provider":"deepseek","model":"deepseek-v4-pro"}}
|
||||
{"type":"assistant/chunk","seq":6,"time":1785218400007,"data":{"turn":1,"step":1,"chunk":{"type":"block-start","index":0,"blockType":"tool-call"}}}
|
||||
{"type":"assistant/chunk","seq":7,"time":1785218400008,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":0,"id":"glob-sampling-call","name":"glob","argumentsDelta":"{\"pattern\":\"*\"}"}}}
|
||||
{"type":"assistant/chunk","seq":8,"time":1785218400009,"data":{"turn":1,"step":1,"chunk":{"type":"block-end","index":0,"block":{"type":"tool-call","id":"glob-sampling-call","name":"glob","arguments":"{\"pattern\":\"*\"}"}}}}
|
||||
{"type":"assistant/chunk","seq":7,"time":1785218400008,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":0,"id":"glob-sampling-call","name":"glob","argumentsDelta":"{\"pattern\":\"*\",\"path\":\"tree\"}"}}}
|
||||
{"type":"assistant/chunk","seq":8,"time":1785218400009,"data":{"turn":1,"step":1,"chunk":{"type":"block-end","index":0,"block":{"type":"tool-call","id":"glob-sampling-call","name":"glob","arguments":"{\"pattern\":\"*\",\"path\":\"tree\"}"}}}}
|
||||
{"type":"assistant/chunk","seq":9,"time":1785218400010,"data":{"turn":1,"step":1,"chunk":{"type":"usage","usage":{"inputTokens":1,"outputTokens":1}}}}
|
||||
{"type":"assistant/chunk","seq":10,"time":1785483397579,"data":{"turn":1,"step":1,"chunk":{"type":"finish","reason":{"kind":"tool-calls"}}}}
|
||||
{"type":"assistant/message","seq":11,"time":1785483397579,"data":{"turn":1,"step":1,"message":{"role":"assistant","content":[{"type":"tool-call","id":"glob-sampling-call","name":"glob","arguments":"{\"pattern\":\"*\"}"}],"source":{"kind":"model","provider":"deepseek","model":"deepseek-v4-pro"},"id":"a127cfe5-39fb-462c-8e5a-a8c79bd0e52b"},"usage":{"inputTokens":1,"outputTokens":1}},"sourceEventSeqs":[6,7,8,9,10],"surfaceOp":"append"}
|
||||
{"type":"tool/call","seq":12,"time":1785483397579,"data":{"turn":1,"step":1,"callId":"glob-sampling-call","name":"glob","arguments":"{\"pattern\":\"*\"}"}}
|
||||
{"type":"tool/result","seq":13,"time":1785483398062,"data":{"turn":1,"step":1,"message":{"source":{"kind":"tool","callId":"glob-sampling-call"},"content":[{"type":"tool-result","toolCallId":"glob-sampling-call","content":[{"type":"text","text":"archive/a.ts\nold\\one\nold\\two\nsrc/index.ts\n\n(Showing 4 of 8 paths, sampled across 4 of the 6 top-level entries this pattern matched instead of taken in modification-time order. Narrow path to inspect a specific subtree. The complete result could not be saved; narrow pattern or path to see more.)"}],"isError":false}],"role":"user","id":"2beecb2e-627d-43dc-a936-03e1dc874093"},"meta":{"shape":"paths","paths":["archive/a.ts","old\\one","old\\two","src/index.ts"],"truncated":true,"total":8}},"sourceEventSeqs":[12],"surfaceOp":"append"}
|
||||
{"type":"assistant/message","seq":11,"time":1785483397579,"data":{"turn":1,"step":1,"message":{"role":"assistant","content":[{"type":"tool-call","id":"glob-sampling-call","name":"glob","arguments":"{\"pattern\":\"*\",\"path\":\"tree\"}"}],"source":{"kind":"model","provider":"deepseek","model":"deepseek-v4-pro"},"id":"a127cfe5-39fb-462c-8e5a-a8c79bd0e52b"},"usage":{"inputTokens":1,"outputTokens":1}},"sourceEventSeqs":[6,7,8,9,10],"surfaceOp":"append"}
|
||||
{"type":"tool/call","seq":12,"time":1785483397579,"data":{"turn":1,"step":1,"callId":"glob-sampling-call","name":"glob","arguments":"{\"pattern\":\"*\",\"path\":\"tree\"}"}}
|
||||
{"type":"tool/result","seq":13,"time":1785483398062,"data":{"turn":1,"step":1,"message":{"source":{"kind":"tool","callId":"glob-sampling-call"},"content":[{"type":"tool-result","toolCallId":"glob-sampling-call","content":[{"type":"text","text":"tree/archive/a.ts\ntree/docs/guide.md\ntree/src/index.ts\ntree/test/spec.ts\n\n(Showing 4 of 8 paths, sampled across 4 of the 6 top-level entries this pattern matched instead of taken in modification-time order. Narrow path to inspect a specific subtree. The complete result could not be saved; narrow pattern or path to see more.)"}],"isError":false}],"role":"user","id":"2beecb2e-627d-43dc-a936-03e1dc874093"},"meta":{"shape":"paths","paths":["tree/archive/a.ts","tree/docs/guide.md","tree/src/index.ts","tree/test/spec.ts"],"truncated":true,"total":8}},"sourceEventSeqs":[12],"surfaceOp":"append"}
|
||||
{"type":"step/end","seq":14,"time":1785483398062,"data":{"turn":1,"step":1}}
|
||||
{"type":"step/start","seq":15,"time":1785483398072,"data":{"turn":1,"step":2}}
|
||||
{"type":"assistant/chunk","seq":16,"time":1785218400017,"data":{"turn":1,"step":2,"chunk":{"type":"block-start","index":0,"blockType":"text"}}}
|
||||
|
||||
Reference in New Issue
Block a user