@gajae-code/coding-agent 0.12.5 → 0.12.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +34 -0
- package/README.md +4 -1
- package/dist/types/async/job-manager.d.ts +36 -1
- package/dist/types/extensibility/extensions/runner.d.ts +9 -7
- package/dist/types/gjc-runtime/launch-tmux.d.ts +22 -3
- package/dist/types/gjc-runtime/psmux-detect.d.ts +3 -0
- package/dist/types/gjc-runtime/team-launch.d.ts +3 -1
- package/dist/types/gjc-runtime/team-runtime.d.ts +14 -2
- package/dist/types/gjc-runtime/team-store.d.ts +0 -4
- package/dist/types/gjc-runtime/team-workers.d.ts +5 -0
- package/dist/types/gjc-runtime/tmux-common.d.ts +1 -0
- package/dist/types/gjc-runtime/tmux-provider-context.d.ts +57 -0
- package/dist/types/gjc-runtime/tmux-sessions.d.ts +8 -1
- package/dist/types/gjc-runtime/ultragoal-change-set.d.ts +3 -0
- package/dist/types/gjc-runtime/ultragoal-runtime.d.ts +21 -4
- package/dist/types/modes/components/gjc-bundle-settings.d.ts +1 -0
- package/dist/types/modes/components/plugin-settings.d.ts +2 -0
- package/dist/types/modes/components/settings-selector.d.ts +2 -0
- package/dist/types/sdk/broker/broker.d.ts +18 -0
- package/dist/types/sdk/broker/lifecycle-ledger.d.ts +13 -0
- package/dist/types/sdk/broker/lifecycle.d.ts +1 -0
- package/dist/types/sdk/bus/chat-daemon-control.d.ts +2 -3
- package/dist/types/sdk/bus/index.d.ts +4 -0
- package/dist/types/sdk/bus/telegram-daemon-contract.d.ts +2 -2
- package/dist/types/sdk/bus/telegram-daemon.d.ts +25 -3
- package/dist/types/sdk/bus/topic-registry.d.ts +63 -3
- package/dist/types/session/agent-session.d.ts +5 -4
- package/dist/types/session/attempt-record-store.d.ts +19 -0
- package/dist/types/session/session-manager.d.ts +1 -0
- package/dist/types/session/session-storage.d.ts +8 -0
- package/dist/types/task/receipt.d.ts +1 -0
- package/dist/types/task/types.d.ts +43 -0
- package/dist/types/tools/subagent.d.ts +15 -0
- package/dist/types/tools/todo-write.d.ts +3 -0
- package/package.json +8 -8
- package/scripts/capture-sticky-viewport-showcase.ts +131 -5
- package/scripts/verify-gjc-sdk-canonicalization.ts +128 -183
- package/scripts/verify-sticky-viewport-showcase.ts +620 -93
- package/src/async/job-manager.ts +430 -46
- package/src/config/model-profiles.ts +8 -0
- package/src/config/settings-schema.ts +17 -3
- package/src/defaults/gjc/skills/ultragoal/SKILL.md +2 -0
- package/src/extensibility/extensions/runner.ts +80 -6
- package/src/extensibility/extensions/wrapper.ts +22 -15
- package/src/extensibility/runtime-skill-discovery.ts +4 -6
- package/src/gjc-runtime/launch-tmux.ts +309 -293
- package/src/gjc-runtime/managed-owner-supervisor.ts +6 -2
- package/src/gjc-runtime/psmux-detect.ts +52 -6
- package/src/gjc-runtime/team-launch.ts +50 -2
- package/src/gjc-runtime/team-runtime.ts +716 -251
- package/src/gjc-runtime/team-store.ts +25 -7
- package/src/gjc-runtime/team-workers.ts +161 -1
- package/src/gjc-runtime/tmux-common.ts +16 -4
- package/src/gjc-runtime/tmux-gc.ts +37 -5
- package/src/gjc-runtime/tmux-owner-isolation.ts +2 -0
- package/src/gjc-runtime/tmux-provider-context.ts +451 -0
- package/src/gjc-runtime/tmux-sessions.ts +409 -156
- package/src/gjc-runtime/ultragoal-change-set.ts +90 -44
- package/src/gjc-runtime/ultragoal-receipt-freshness.ts +7 -0
- package/src/gjc-runtime/ultragoal-runtime.ts +360 -156
- package/src/internal-urls/docs-index.generated.ts +4 -4
- package/src/internal-urls/local-protocol.ts +39 -18
- package/src/modes/acp/acp-agent.ts +27 -13
- package/src/modes/components/gjc-bundle-settings.ts +2 -0
- package/src/modes/components/plugin-settings.ts +5 -0
- package/src/modes/components/settings-selector.ts +4 -0
- package/src/modes/controllers/selector-controller.ts +1 -0
- package/src/prompts/tools/monitor.md +4 -4
- package/src/sdk/broker/broker.ts +93 -10
- package/src/sdk/broker/lifecycle-ledger.ts +227 -11
- package/src/sdk/broker/lifecycle.ts +691 -92
- package/src/sdk/bus/chat-daemon-control.ts +30 -6
- package/src/sdk/bus/index.ts +23 -6
- package/src/sdk/bus/telegram-daemon-contract.ts +2 -2
- package/src/sdk/bus/telegram-daemon.ts +283 -44
- package/src/sdk/bus/topic-registry.ts +133 -10
- package/src/sdk/session.ts +7 -6
- package/src/session/agent-session.ts +752 -339
- package/src/session/attempt-record-store.ts +106 -0
- package/src/session/internal/managed-session-scope.ts +252 -67
- package/src/session/internal/managed-session-storage.ts +1 -0
- package/src/session/session-manager.ts +7 -0
- package/src/session/session-storage.ts +146 -24
- package/src/task/index.ts +230 -19
- package/src/task/provider-retry-status.ts +4 -1
- package/src/task/receipt.ts +2 -0
- package/src/task/types.ts +10 -0
- package/src/tools/monitor.ts +7 -1
- package/src/tools/output-meta.ts +10 -2
- package/src/tools/subagent.ts +97 -75
- package/src/tools/todo-write.ts +47 -0
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import * as fs from "node:fs/promises";
|
|
2
2
|
import * as path from "node:path";
|
|
3
|
+
|
|
3
4
|
import {
|
|
4
5
|
renderStickyViewportShowcase,
|
|
5
6
|
STICKY_VIEWPORT_SHOWCASE_ENTRIES,
|
|
@@ -7,6 +8,10 @@ import {
|
|
|
7
8
|
type StickyViewportShowcaseEntry,
|
|
8
9
|
} from "../test/fixtures/tui/sticky-viewport-showcase";
|
|
9
10
|
|
|
11
|
+
export const REPOSITORY_ROOT = path.resolve(import.meta.dir, "../../..");
|
|
12
|
+
export const resolveRepositoryPath = (repositoryRelativePath: string): string =>
|
|
13
|
+
path.join(REPOSITORY_ROOT, repositoryRelativePath);
|
|
14
|
+
|
|
10
15
|
const COMMAND =
|
|
11
16
|
"bun packages/coding-agent/scripts/capture-sticky-viewport-showcase.ts --out .gjc/qa/sticky-viewport-<run>";
|
|
12
17
|
const REVISION = "sticky-viewport-showcase-v2";
|
|
@@ -168,17 +173,126 @@ export function ansiToHtml(value: string): string {
|
|
|
168
173
|
return `<!doctype html><html lang="en"><meta charset="utf-8"><title>Sticky viewport showcase</title><style>body{margin:0;background:#110b0b;color:#ffe7dc}pre{white-space:pre;font-family:ui-monospace,monospace}@keyframes blink{50%{visibility:hidden}}</style><pre>${body}</pre></html>\n`;
|
|
169
174
|
}
|
|
170
175
|
const json = (value: unknown) => `${JSON.stringify(value, null, 2)}\n`;
|
|
171
|
-
const hash = (value: string) => new Bun.CryptoHasher("sha256").update(value).digest("hex");
|
|
176
|
+
const hash = (value: string | Uint8Array) => new Bun.CryptoHasher("sha256").update(value).digest("hex");
|
|
177
|
+
const PROVENANCE_SOURCES = [
|
|
178
|
+
"packages/coding-agent/test/fixtures/tui/sticky-viewport-showcase.ts",
|
|
179
|
+
"packages/coding-agent/scripts/capture-sticky-viewport-showcase.ts",
|
|
180
|
+
"packages/coding-agent/scripts/verify-sticky-viewport-showcase.ts",
|
|
181
|
+
"packages/coding-agent/src/modes/interactive-mode.ts",
|
|
182
|
+
"packages/coding-agent/src/modes/components/irc-sidebar.ts",
|
|
183
|
+
"packages/tui/src/tui.ts",
|
|
184
|
+
] as const;
|
|
185
|
+
// Working-tree scope for `git_diff_binary_sha256`, persisted alongside the digest
|
|
186
|
+
// as `git_diff_scope` so a reviewer reads the covered surface off the bundle
|
|
187
|
+
// instead of inferring it.
|
|
188
|
+
//
|
|
189
|
+
// This digest used to hash `git diff --binary HEAD --` over the ENTIRE worktree.
|
|
190
|
+
// The verifier recomputes it live at verify time, so that coupled bundle validity
|
|
191
|
+
// to every tracked file in the repo: an unrelated edit anywhere — a doc typo,
|
|
192
|
+
// another package's test — retroactively made every already-captured bundle
|
|
193
|
+
// "stale". Those are false positives, and they are nondeterministic, because any
|
|
194
|
+
// write landing in the capture→verify window flips the digest mid-run and masks
|
|
195
|
+
// whichever guard was actually under test.
|
|
196
|
+
//
|
|
197
|
+
// The scope below is the transitive render-dependency closure of the fixture:
|
|
198
|
+
// every workspace package the capture reaches (coding-agent → agent, ai,
|
|
199
|
+
// bridge-client, natives, stats, tui, utils), the fixture plus the virtual
|
|
200
|
+
// terminal it paints into, both showcase scripts, and the lockfile pinning the
|
|
201
|
+
// installed dependency versions. Uncommitted edits inside this closure still
|
|
202
|
+
// invalidate a bundle — that is the property the staleness guard exists to
|
|
203
|
+
// enforce. Edits outside it no longer can, because they cannot change the paint.
|
|
204
|
+
export const PROVENANCE_DIFF_SCOPE = [
|
|
205
|
+
"Cargo.lock",
|
|
206
|
+
"Cargo.toml",
|
|
207
|
+
"bun.lock",
|
|
208
|
+
"crates",
|
|
209
|
+
|
|
210
|
+
"packages/agent/src",
|
|
211
|
+
"packages/ai/src",
|
|
212
|
+
"packages/bridge-client/src",
|
|
213
|
+
"packages/coding-agent/scripts/capture-sticky-viewport-showcase.ts",
|
|
214
|
+
"packages/coding-agent/scripts/verify-sticky-viewport-showcase.ts",
|
|
215
|
+
"packages/coding-agent/src",
|
|
216
|
+
"packages/coding-agent/test/fixtures/tui/sticky-viewport-showcase.ts",
|
|
217
|
+
"packages/natives/native",
|
|
218
|
+
"packages/stats/src",
|
|
219
|
+
"packages/tui/src",
|
|
220
|
+
"packages/tui/test/virtual-terminal.ts",
|
|
221
|
+
"packages/utils/src",
|
|
222
|
+
] as const;
|
|
223
|
+
async function git(args: string[]): Promise<Uint8Array> {
|
|
224
|
+
const result = Bun.spawn(["git", ...args], { cwd: REPOSITORY_ROOT, stdout: "pipe", stderr: "pipe" });
|
|
225
|
+
if ((await result.exited) !== 0)
|
|
226
|
+
throw new Error(`git ${args.join(" ")} failed: ${await new Response(result.stderr).text()}`);
|
|
227
|
+
return new Uint8Array(await new Response(result.stdout).arrayBuffer());
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
// Digest of a path's COMMITTED blob at a given commit, read straight out of the
|
|
231
|
+
// object database. This is the only provenance input a bundle author cannot
|
|
232
|
+
// restamp: `captureProvenance()` hashes the worktree, so mutating an oracle file
|
|
233
|
+
// and re-running it yields a self-consistent stamp. The committed blob is fixed
|
|
234
|
+
// by the commit id, so changing it requires a new commit -- which changes
|
|
235
|
+
// `git_head` and is therefore visible to the reviewer.
|
|
236
|
+
/** sha256 of every distinct blob this path has ever had in any ref-reachable commit. */
|
|
237
|
+
export async function gitObjectType(commitish: string): Promise<string | null> {
|
|
238
|
+
try {
|
|
239
|
+
const out = new TextDecoder().decode(await git(["cat-file", "-t", commitish])).trim();
|
|
240
|
+
return out || null;
|
|
241
|
+
} catch {
|
|
242
|
+
return null;
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
export async function committedBlobSha256(commit: string, filePath: string): Promise<string | null> {
|
|
246
|
+
const result = Bun.spawn(["git", "cat-file", "blob", `${commit}:${filePath}`], {
|
|
247
|
+
cwd: REPOSITORY_ROOT,
|
|
248
|
+
stdout: "pipe",
|
|
249
|
+
stderr: "pipe",
|
|
250
|
+
});
|
|
251
|
+
const bytes = new Uint8Array(await new Response(result.stdout).arrayBuffer());
|
|
252
|
+
if ((await result.exited) !== 0) return null;
|
|
253
|
+
return hash(bytes);
|
|
254
|
+
}
|
|
255
|
+
export type CaptureProvenance = {
|
|
256
|
+
git_head: string;
|
|
257
|
+
oracle_commit: string;
|
|
258
|
+
git_diff_scope: readonly string[];
|
|
259
|
+
git_diff_binary_sha256: string;
|
|
260
|
+
source_sha256: Record<string, string>;
|
|
261
|
+
};
|
|
262
|
+
|
|
263
|
+
// Single source of truth: the verifier imports this so capture and verify can
|
|
264
|
+
// never drift into computing the field two different ways.
|
|
265
|
+
export async function captureProvenance(): Promise<CaptureProvenance> {
|
|
266
|
+
const gitHead = new TextDecoder().decode(await git(["rev-parse", "HEAD"])).trim();
|
|
267
|
+
const sourceSha256 = Object.fromEntries(
|
|
268
|
+
await Promise.all(
|
|
269
|
+
PROVENANCE_SOURCES.map(async source => [
|
|
270
|
+
source,
|
|
271
|
+
hash(new Uint8Array(await Bun.file(resolveRepositoryPath(source)).arrayBuffer())),
|
|
272
|
+
]),
|
|
273
|
+
),
|
|
274
|
+
);
|
|
275
|
+
return {
|
|
276
|
+
git_head: gitHead,
|
|
277
|
+
oracle_commit: process.env.GJC_STICKY_VIEWPORT_ORACLE_COMMIT?.trim() ?? gitHead,
|
|
278
|
+
git_diff_scope: PROVENANCE_DIFF_SCOPE,
|
|
279
|
+
git_diff_binary_sha256: hash(await git(["diff", "--binary", "HEAD", "--", ...PROVENANCE_DIFF_SCOPE])),
|
|
280
|
+
source_sha256: sourceSha256,
|
|
281
|
+
};
|
|
282
|
+
}
|
|
172
283
|
function out(args: string[]): string {
|
|
173
284
|
if (args.length !== 2 || args[0] !== "--out" || !args[1]) throw new Error(`Usage: ${COMMAND}`);
|
|
174
285
|
return args[1];
|
|
175
286
|
}
|
|
176
|
-
async function capture(entry: StickyViewportShowcaseEntry, root: string) {
|
|
287
|
+
async function capture(entry: StickyViewportShowcaseEntry, root: string, sourceProvenance: CaptureProvenance) {
|
|
177
288
|
const rendered = await renderStickyViewportShowcase(entry);
|
|
178
289
|
if (!rendered.state.composer_visible)
|
|
179
290
|
throw new Error(`${entry.key}: focused composer was not visible in production frame`);
|
|
180
|
-
if (
|
|
181
|
-
|
|
291
|
+
if (
|
|
292
|
+
(entry.stateId === "manual-new-output" && rendered.state.notice !== true) ||
|
|
293
|
+
(entry.stateId !== "manual-new-output" && rendered.state.notice !== false)
|
|
294
|
+
)
|
|
295
|
+
throw new Error(`${entry.key}: renderer-owned output notice precondition failed`);
|
|
182
296
|
if (
|
|
183
297
|
JSON.stringify(rendered.cjkPhraseBoundaries) !==
|
|
184
298
|
JSON.stringify(entry.stateId === "narrow-cjk" ? CJK_PHRASE_BOUNDARIES : [])
|
|
@@ -210,6 +324,7 @@ async function capture(entry: StickyViewportShowcaseEntry, root: string) {
|
|
|
210
324
|
fixed_clock: true,
|
|
211
325
|
author_identity: "capture-sticky-viewport-showcase",
|
|
212
326
|
executor_identity: "capture-sticky-viewport-showcase",
|
|
327
|
+
...sourceProvenance,
|
|
213
328
|
},
|
|
214
329
|
cjk_phrase_boundaries: rendered.cjkPhraseBoundaries,
|
|
215
330
|
});
|
|
@@ -241,8 +356,9 @@ async function capture(entry: StickyViewportShowcaseEntry, root: string) {
|
|
|
241
356
|
async function main() {
|
|
242
357
|
const root = path.resolve(out(process.argv.slice(2)));
|
|
243
358
|
await fs.mkdir(root, { recursive: true });
|
|
359
|
+
const sourceProvenance = await captureProvenance();
|
|
244
360
|
const entries = [];
|
|
245
|
-
for (const entry of STICKY_VIEWPORT_SHOWCASE_ENTRIES) entries.push(await capture(entry, root));
|
|
361
|
+
for (const entry of STICKY_VIEWPORT_SHOWCASE_ENTRIES) entries.push(await capture(entry, root, sourceProvenance));
|
|
246
362
|
const manifest = json({
|
|
247
363
|
schema_version: 2,
|
|
248
364
|
fixture_revision: REVISION,
|
|
@@ -258,6 +374,7 @@ async function main() {
|
|
|
258
374
|
fixed_clock: true,
|
|
259
375
|
author_identity: "capture-sticky-viewport-showcase",
|
|
260
376
|
executor_identity: "capture-sticky-viewport-showcase",
|
|
377
|
+
...sourceProvenance,
|
|
261
378
|
},
|
|
262
379
|
review_input_file: "review-input.json",
|
|
263
380
|
entries,
|
|
@@ -281,6 +398,15 @@ async function main() {
|
|
|
281
398
|
acceptance_version: ACCEPTANCE_VERSION,
|
|
282
399
|
design_version: DESIGN_VERSION,
|
|
283
400
|
host_matrix: HOST_MATRIX,
|
|
401
|
+
provenance: {
|
|
402
|
+
capture_mode: "production-tui-virtual-terminal",
|
|
403
|
+
live_pty: false,
|
|
404
|
+
network: false,
|
|
405
|
+
fixed_clock: true,
|
|
406
|
+
author_identity: "capture-sticky-viewport-showcase",
|
|
407
|
+
executor_identity: "capture-sticky-viewport-showcase",
|
|
408
|
+
...sourceProvenance,
|
|
409
|
+
},
|
|
284
410
|
narrow_cjk: {
|
|
285
411
|
entry_key: "narrow-cjk/48x10/unicode-color",
|
|
286
412
|
phrase_boundaries: ["의미 있는 문장 경계", "意味のある文の境界", "保留语义短语边界"],
|
|
@@ -1421,13 +1421,6 @@ function braceBlockRange(contents: string, openingBrace: number): ShellRange | u
|
|
|
1421
1421
|
return undefined;
|
|
1422
1422
|
}
|
|
1423
1423
|
|
|
1424
|
-
function scopeMatches<T extends RegExpMatchArray>(matches: T[], range: ShellRange): T[] {
|
|
1425
|
-
return matches.filter(match => {
|
|
1426
|
-
const index = match.index ?? -1;
|
|
1427
|
-
return index >= range.start && index < range.end;
|
|
1428
|
-
});
|
|
1429
|
-
}
|
|
1430
|
-
|
|
1431
1424
|
function findFunctionRange(contents: string, headerPattern: RegExp): ShellRange | undefined {
|
|
1432
1425
|
const header = headerPattern.exec(contents);
|
|
1433
1426
|
if (!header) return undefined;
|
|
@@ -1504,121 +1497,62 @@ function exactMemoryGuardSendKeysRanges(contents: string): ShellRange[] {
|
|
|
1504
1497
|
];
|
|
1505
1498
|
}
|
|
1506
1499
|
function exactTeamRuntimeSendKeysRanges(contents: string): ShellRange[] {
|
|
1507
|
-
|
|
1508
|
-
|
|
1509
|
-
|
|
1510
|
-
|
|
1511
|
-
|
|
1512
|
-
const mainFnRange = findFunctionRange(
|
|
1500
|
+
const executor =
|
|
1501
|
+
/function\s+executeTeamTmuxMutation\s*\([\s\S]*?\)\s*:\s*Bun\.SyncSubprocess<"pipe",\s*"pipe">\s*\{/.exec(
|
|
1502
|
+
contents,
|
|
1503
|
+
);
|
|
1504
|
+
const continuation = /async\s+function\s+continueStalledGjcTeamWorkers\s*\([^)]*\)\s*:\s*Promise<void>\s*\{/.exec(
|
|
1513
1505
|
contents,
|
|
1514
|
-
/async\s+function\s+startTmuxSession\s*\([\s\S]{0,600}?\)\s*:\s*Promise<GjcTeamWorker\[\]>\s*\{/,
|
|
1515
|
-
) ?? { start: 0, end: contents.length };
|
|
1516
|
-
const guardMatches = scopeMatches([...contents.matchAll(/if\s*\(\s*useSendKeysFallback\s*\)\s*\{/g)], mainFnRange);
|
|
1517
|
-
const payloadMatches = scopeMatches(
|
|
1518
|
-
[
|
|
1519
|
-
...contents.matchAll(
|
|
1520
|
-
/Bun\.spawnSync\(\s*\[\s*config\.tmux_command\s*,\s*["']send-keys["']\s*,\s*["']-l["']\s*,\s*["']-t["']\s*,\s*paneId\s*,\s*workerCommand\s*\]\s*,\s*\{[\s\S]{0,200}?stdout\s*:\s*["']ignore["'][\s\S]{0,200}?stderr\s*:\s*["']ignore["'][\s\S]{0,100}?\}\s*\)\s*;/g,
|
|
1521
|
-
),
|
|
1522
|
-
],
|
|
1523
|
-
mainFnRange,
|
|
1524
|
-
);
|
|
1525
|
-
const continuationPromptMatches = [
|
|
1526
|
-
...contents.matchAll(
|
|
1527
|
-
/(?:const\s+GJC_TEAM_CONTINUATION_PROMPT\s*=\s*["']Continue only your current claimed GJC team task\. Re-read current GJC team state; do not replay prior output; report status\.["']\s*;|import\s*\{[\s\S]{0,1000}?\bGJC_TEAM_CONTINUATION_PROMPT\b[\s\S]{0,1000}?\}\s*from\s*["']\.\/team-workers["']\s*;)/g,
|
|
1528
|
-
),
|
|
1529
|
-
];
|
|
1530
|
-
const continuationMatches = [
|
|
1531
|
-
...contents.matchAll(
|
|
1532
|
-
/const\s+args\s*=\s*Object\.freeze\(\s*\[\s*["']send-keys["']\s*,\s*["']-l["']\s*,\s*["']-t["']\s*,\s*worker\.pane_id\s*,\s*GJC_TEAM_CONTINUATION_PROMPT\s*,\s*["'];["']\s*,\s*["']send-keys["']\s*,\s*["']-t["']\s*,\s*worker\.pane_id\s*,\s*["']Enter["']\s*,?\s*\]\s*\)\s*;\s*const\s+dispatch\s*=\s*gjcTeamRuntimeTestSeams\?\.continuationTmuxDispatch\s*\?\s*gjcTeamRuntimeTestSeams\.continuationTmuxDispatch\(config\.tmux_command\s*,\s*args\)\s*:\s*Bun\.spawnSync\(\[config\.tmux_command\s*,\s*\.\.\.args\]\s*,\s*\{\s*stdout\s*:\s*["']ignore["']\s*,\s*stderr\s*:\s*["']ignore["']\s*,\s*timeout\s*:\s*GJC_TEAM_CONTINUATION_DISPATCH_TIMEOUT_MS\s*,?\s*\}\s*\)\s*;/g,
|
|
1533
|
-
),
|
|
1534
|
-
];
|
|
1535
|
-
const enterMatches = scopeMatches(
|
|
1536
|
-
[
|
|
1537
|
-
...contents.matchAll(
|
|
1538
|
-
/const\s+sendKeys\s*=\s*Bun\.spawnSync\(\s*\[\s*config\.tmux_command\s*,\s*["']send-keys["']\s*,\s*["']-t["']\s*,\s*paneId\s*,\s*["']Enter["']\s*\]\s*,\s*\{[\s\S]{0,200}?stdout\s*:\s*["']ignore["'][\s\S]{0,200}?stderr\s*:\s*["']ignore["'][\s\S]{0,100}?\}\s*\)\s*;/g,
|
|
1539
|
-
),
|
|
1540
|
-
],
|
|
1541
|
-
mainFnRange,
|
|
1542
|
-
);
|
|
1543
|
-
const fallbackPredicateMatches = [
|
|
1544
|
-
...contents.matchAll(
|
|
1545
|
-
/function\s+shouldDispatchWorkerWithSendKeys\([^)]*\)\s*:\s*boolean\s*\{\s*return\s+platform\s*===\s*["']win32["']\s*\|\|\s*path\.basename\(tmuxCommand\)\.toLowerCase\(\)\s*===\s*["']psmux["']\s*;\s*\}/g,
|
|
1546
|
-
),
|
|
1547
|
-
];
|
|
1548
|
-
const useFallbackMatches = scopeMatches(
|
|
1549
|
-
[
|
|
1550
|
-
...contents.matchAll(
|
|
1551
|
-
/const\s+useSendKeysFallback\s*=\s*shouldDispatchWorkerWithSendKeys\(config\.tmux_command\)\s*;/g,
|
|
1552
|
-
),
|
|
1553
|
-
],
|
|
1554
|
-
mainFnRange,
|
|
1555
|
-
);
|
|
1556
|
-
const splitWorkerCommandMatches = scopeMatches(
|
|
1557
|
-
[...contents.matchAll(/\.\.\.\(useSendKeysFallback\s*\?\s*\[\]\s*:\s*\[workerCommand\]\)\s*,/g)],
|
|
1558
|
-
mainFnRange,
|
|
1559
1506
|
);
|
|
1507
|
+
if (!executor || !continuation) return [];
|
|
1508
|
+
|
|
1509
|
+
const executorRange = braceBlockRange(contents, (executor.index ?? 0) + executor[0].lastIndexOf("{"));
|
|
1510
|
+
const continuationRange = braceBlockRange(contents, (continuation.index ?? 0) + continuation[0].lastIndexOf("{"));
|
|
1511
|
+
if (!executorRange || !continuationRange) return [];
|
|
1512
|
+
|
|
1513
|
+
const executorBody = contents.slice(executorRange.start, executorRange.end);
|
|
1514
|
+
const literalSend =
|
|
1515
|
+
/\?\s*\[\s*["']send-keys["']\s*,\s*["']-l["']\s*,\s*["']-t["']\s*,\s*operation\.paneId\s*,\s*operation\.text\s*\]/.exec(
|
|
1516
|
+
executorBody,
|
|
1517
|
+
);
|
|
1518
|
+
const keySend =
|
|
1519
|
+
/operation\.type\s*===\s*["']key-send["']\s*\?\s*\[\s*["']send-keys["']\s*,\s*["']-t["']\s*,\s*operation\.paneId\s*,\s*operation\.key\s*\]/.exec(
|
|
1520
|
+
executorBody,
|
|
1521
|
+
);
|
|
1522
|
+
const authorityChecks = executorBody.match(/assertGjcTmuxMutationAuthoritySync\(authority\)/g) ?? [];
|
|
1560
1523
|
if (
|
|
1561
|
-
|
|
1562
|
-
|
|
1563
|
-
|
|
1564
|
-
|
|
1565
|
-
|
|
1566
|
-
|
|
1567
|
-
useFallbackMatches.length !== 1 ||
|
|
1568
|
-
splitWorkerCommandMatches.length !== 1
|
|
1524
|
+
!literalSend ||
|
|
1525
|
+
!keySend ||
|
|
1526
|
+
!executorBody.includes("const authority = teamProviderAuthority(config);") ||
|
|
1527
|
+
!executorBody.includes("assertTeamTmuxMutationPreproof(config, operation);") ||
|
|
1528
|
+
authorityChecks.length < 2 ||
|
|
1529
|
+
!executorBody.includes("Bun.spawnSync(")
|
|
1569
1530
|
)
|
|
1570
1531
|
return [];
|
|
1571
|
-
|
|
1572
|
-
const
|
|
1573
|
-
const
|
|
1574
|
-
|
|
1532
|
+
|
|
1533
|
+
const continuationBody = contents.slice(continuationRange.start, continuationRange.end);
|
|
1534
|
+
const continuationArgs =
|
|
1535
|
+
/const\s+args\s*=\s*Object\.freeze\(\s*\[\s*["']send-keys["']\s*,\s*["']-l["']\s*,\s*["']-t["']\s*,\s*worker\.pane_id\s*,\s*continuationPrompt\s*,\s*["'];["']\s*,\s*["']send-keys["']\s*,\s*["']-t["']\s*,\s*worker\.pane_id\s*,\s*["']Enter["']\s*,?\s*\]\s*\)\s*;/.exec(
|
|
1536
|
+
continuationBody,
|
|
1537
|
+
);
|
|
1575
1538
|
if (
|
|
1576
|
-
|
|
1577
|
-
(
|
|
1578
|
-
|
|
1579
|
-
|
|
1580
|
-
|
|
1581
|
-
|
|
1582
|
-
|
|
1583
|
-
const openingBrace = (guard.index ?? 0) + guard[0].lastIndexOf("{");
|
|
1584
|
-
const range = braceBlockRange(contents, openingBrace);
|
|
1585
|
-
const continuationFunction =
|
|
1586
|
-
/async\s+function\s+continueStalledGjcTeamWorkers\s*\([^)]*\)\s*:\s*Promise<void>\s*\{/.exec(contents);
|
|
1587
|
-
const monitorFence = /await\s+withGjcTeamTaskMutation\([\s\S]{0,500}?async\s+capability\s*=>\s*\{/.exec(contents);
|
|
1588
|
-
if (!range || !continuationFunction || !monitorFence) return [];
|
|
1589
|
-
const continuationOpeningBrace = (continuationFunction.index ?? 0) + continuationFunction[0].lastIndexOf("{");
|
|
1590
|
-
const continuationRange = braceBlockRange(contents, continuationOpeningBrace);
|
|
1591
|
-
const monitorOpeningBrace = (monitorFence.index ?? 0) + monitorFence[0].lastIndexOf("{");
|
|
1592
|
-
const monitorRange = braceBlockRange(contents, monitorOpeningBrace);
|
|
1593
|
-
if (!continuationRange || !monitorRange) return [];
|
|
1594
|
-
const monitorBody = contents.slice(monitorRange.start, monitorRange.end);
|
|
1595
|
-
const continuationCall = /await\s+continueStalledGjcTeamWorkers\s*\(/.exec(monitorBody);
|
|
1596
|
-
const reconciliationCall = /await\s+reconcileGjcTeamStaleClaimsUnlocked\s*\(/.exec(monitorBody);
|
|
1597
|
-
const continuationStart = continuationMatches[0].index ?? 0;
|
|
1598
|
-
const finalValidationStart = contents.lastIndexOf("const revalidationReason =", continuationStart);
|
|
1599
|
-
const skippedBranch = finalValidationStart === -1 ? "" : contents.slice(finalValidationStart, continuationStart);
|
|
1600
|
-
if (
|
|
1601
|
-
payloadStart < range.start ||
|
|
1602
|
-
payloadStart >= range.end ||
|
|
1603
|
-
enterStart < range.start ||
|
|
1604
|
-
enterStart >= range.end ||
|
|
1605
|
-
contents.indexOf("void sendKeys.exitCode;", enterStart) >= range.end ||
|
|
1606
|
-
continuationStart < continuationRange.start ||
|
|
1607
|
-
continuationStart >= continuationRange.end ||
|
|
1608
|
-
finalValidationStart < continuationRange.start ||
|
|
1609
|
-
!continuationCall ||
|
|
1610
|
-
!reconciliationCall ||
|
|
1611
|
-
(continuationCall.index ?? 0) >= (reconciliationCall.index ?? 0) ||
|
|
1612
|
-
!/if\s*\(\s*revalidationReason\s*\)[\s\S]*?return\s*;/.test(skippedBranch)
|
|
1539
|
+
!continuationArgs ||
|
|
1540
|
+
!continuationBody.includes("const revalidationReason =") ||
|
|
1541
|
+
!continuationBody.includes("await createJsonNoClobber(") ||
|
|
1542
|
+
!continuationBody.includes('type: "literal-send"') ||
|
|
1543
|
+
!continuationBody.includes('type: "key-send"') ||
|
|
1544
|
+
!continuationBody.includes('deferredProof: "continuation-outcome"') ||
|
|
1545
|
+
/args\s*\.\s*(?:push|unshift|splice)\s*\(/.test(continuationBody)
|
|
1613
1546
|
)
|
|
1614
1547
|
return [];
|
|
1548
|
+
|
|
1549
|
+
const literalStart = executorRange.start + (literalSend.index ?? 0);
|
|
1550
|
+
const keyStart = executorRange.start + (keySend.index ?? 0);
|
|
1551
|
+
const argsStart = continuationRange.start + (continuationArgs.index ?? 0);
|
|
1615
1552
|
return [
|
|
1616
|
-
{ start:
|
|
1617
|
-
{ start:
|
|
1618
|
-
{
|
|
1619
|
-
start: continuationMatches[0].index ?? 0,
|
|
1620
|
-
end: (continuationMatches[0].index ?? 0) + continuationMatches[0][0].length,
|
|
1621
|
-
},
|
|
1553
|
+
{ start: literalStart, end: literalStart + literalSend[0].length },
|
|
1554
|
+
{ start: keyStart, end: keyStart + keySend[0].length },
|
|
1555
|
+
{ start: argsStart, end: argsStart + continuationArgs[0].length },
|
|
1622
1556
|
];
|
|
1623
1557
|
}
|
|
1624
1558
|
|
|
@@ -3219,47 +3153,68 @@ fi
|
|
|
3219
3153
|
"tmux send-keys content injection is outside sanctioned process lifecycle",
|
|
3220
3154
|
);
|
|
3221
3155
|
const canonicalTeamRuntimeSendKeysFixture = `
|
|
3222
|
-
|
|
3223
|
-
|
|
3224
|
-
|
|
3225
|
-
function
|
|
3226
|
-
|
|
3227
|
-
|
|
3228
|
-
|
|
3229
|
-
const
|
|
3230
|
-
|
|
3231
|
-
|
|
3232
|
-
|
|
3233
|
-
|
|
3234
|
-
|
|
3235
|
-
|
|
3236
|
-
|
|
3237
|
-
|
|
3238
|
-
|
|
3239
|
-
|
|
3156
|
+
type TeamTmuxMutation =
|
|
3157
|
+
| { type: "literal-send"; paneId: string; text: string; deferredProof: "continuation-outcome" }
|
|
3158
|
+
| { type: "key-send"; paneId: string; key: string; deferredProof: "continuation-outcome" };
|
|
3159
|
+
function executeTeamTmuxMutation(
|
|
3160
|
+
config: GjcTeamConfig,
|
|
3161
|
+
operation: TeamTmuxMutation,
|
|
3162
|
+
): Bun.SyncSubprocess<"pipe", "pipe"> {
|
|
3163
|
+
const authority = teamProviderAuthority(config);
|
|
3164
|
+
assertTeamTmuxMutationPreproof(config, operation);
|
|
3165
|
+
const args =
|
|
3166
|
+
operation.type === "literal-send"
|
|
3167
|
+
? ["send-keys", "-l", "-t", operation.paneId, operation.text]
|
|
3168
|
+
: operation.type === "key-send"
|
|
3169
|
+
? ["send-keys", "-t", operation.paneId, operation.key]
|
|
3170
|
+
: [];
|
|
3171
|
+
assertGjcTmuxMutationAuthoritySync(authority);
|
|
3172
|
+
const result = Bun.spawnSync(args);
|
|
3173
|
+
assertGjcTmuxMutationAuthoritySync(authority);
|
|
3174
|
+
return result;
|
|
3240
3175
|
}
|
|
3241
|
-
|
|
3242
|
-
const
|
|
3243
|
-
const
|
|
3244
|
-
|
|
3245
|
-
|
|
3246
|
-
|
|
3247
|
-
|
|
3248
|
-
|
|
3249
|
-
";"
|
|
3250
|
-
|
|
3251
|
-
|
|
3252
|
-
|
|
3253
|
-
|
|
3254
|
-
|
|
3255
|
-
|
|
3256
|
-
|
|
3257
|
-
|
|
3258
|
-
|
|
3259
|
-
|
|
3260
|
-
|
|
3261
|
-
|
|
3262
|
-
|
|
3176
|
+
async function continueStalledGjcTeamWorkers(): Promise<void> {
|
|
3177
|
+
const reservationPath = "reservation";
|
|
3178
|
+
const reservation = {};
|
|
3179
|
+
await createJsonNoClobber(
|
|
3180
|
+
reservationPath,
|
|
3181
|
+
reservation,
|
|
3182
|
+
stateWriterOptions(reservationPath, "state", "continuation-reservation"),
|
|
3183
|
+
);
|
|
3184
|
+
const continuationPrompt = "Continue only your current claimed GJC team task. Re-read current GJC team state; do not replay prior output; report status.";
|
|
3185
|
+
const revalidationReason = await validateGjcContinuationEligibility(dir, config, worker);
|
|
3186
|
+
if (revalidationReason) {
|
|
3187
|
+
return;
|
|
3188
|
+
}
|
|
3189
|
+
const args = Object.freeze([
|
|
3190
|
+
"send-keys",
|
|
3191
|
+
"-l",
|
|
3192
|
+
"-t",
|
|
3193
|
+
worker.pane_id,
|
|
3194
|
+
continuationPrompt,
|
|
3195
|
+
";",
|
|
3196
|
+
"send-keys",
|
|
3197
|
+
"-t",
|
|
3198
|
+
worker.pane_id,
|
|
3199
|
+
"Enter",
|
|
3200
|
+
]);
|
|
3201
|
+
const dispatch = gjcTeamRuntimeTestSeams?.continuationTmuxDispatch
|
|
3202
|
+
? gjcTeamRuntimeTestSeams.continuationTmuxDispatch(config.tmux_command, args)
|
|
3203
|
+
: (() => {
|
|
3204
|
+
executeTeamTmuxMutation(config, {
|
|
3205
|
+
type: "literal-send",
|
|
3206
|
+
paneId: worker.pane_id!,
|
|
3207
|
+
text: continuationPrompt,
|
|
3208
|
+
deferredProof: "continuation-outcome",
|
|
3209
|
+
});
|
|
3210
|
+
return executeTeamTmuxMutation(config, {
|
|
3211
|
+
type: "key-send",
|
|
3212
|
+
paneId: worker.pane_id!,
|
|
3213
|
+
key: "Enter",
|
|
3214
|
+
deferredProof: "continuation-outcome",
|
|
3215
|
+
});
|
|
3216
|
+
})();
|
|
3217
|
+
void dispatch;
|
|
3263
3218
|
}
|
|
3264
3219
|
async function monitorGjcTeam(): Promise<void> {
|
|
3265
3220
|
await withGjcTeamTaskMutation(taskStore(dir), async capability => {
|
|
@@ -3275,18 +3230,8 @@ async function monitorGjcTeam(): Promise<void> {
|
|
|
3275
3230
|
await runSelfTestFixture(
|
|
3276
3231
|
{
|
|
3277
3232
|
"packages/coding-agent/src/gjc-runtime/team-runtime.ts": canonicalTeamRuntimeSendKeysFixture.replace(
|
|
3278
|
-
"
|
|
3279
|
-
"
|
|
3280
|
-
),
|
|
3281
|
-
},
|
|
3282
|
-
1,
|
|
3283
|
-
"tmux send-keys content injection is outside sanctioned process lifecycle",
|
|
3284
|
-
);
|
|
3285
|
-
await runSelfTestFixture(
|
|
3286
|
-
{
|
|
3287
|
-
"packages/coding-agent/src/gjc-runtime/team-runtime.ts": canonicalTeamRuntimeSendKeysFixture.replace(
|
|
3288
|
-
"if (revalidationReason) return;",
|
|
3289
|
-
"if (revalidationReason) {}",
|
|
3233
|
+
"\tassertTeamTmuxMutationPreproof(config, operation);\n",
|
|
3234
|
+
"",
|
|
3290
3235
|
),
|
|
3291
3236
|
},
|
|
3292
3237
|
1,
|
|
@@ -3295,8 +3240,8 @@ async function monitorGjcTeam(): Promise<void> {
|
|
|
3295
3240
|
await runSelfTestFixture(
|
|
3296
3241
|
{
|
|
3297
3242
|
"packages/coding-agent/src/gjc-runtime/team-runtime.ts": canonicalTeamRuntimeSendKeysFixture.replace(
|
|
3298
|
-
"
|
|
3299
|
-
"",
|
|
3243
|
+
"\tassertGjcTmuxMutationAuthoritySync(authority);\n\treturn result;",
|
|
3244
|
+
"\treturn result;",
|
|
3300
3245
|
),
|
|
3301
3246
|
},
|
|
3302
3247
|
1,
|
|
@@ -3305,8 +3250,8 @@ async function monitorGjcTeam(): Promise<void> {
|
|
|
3305
3250
|
await runSelfTestFixture(
|
|
3306
3251
|
{
|
|
3307
3252
|
"packages/coding-agent/src/gjc-runtime/team-runtime.ts": canonicalTeamRuntimeSendKeysFixture.replace(
|
|
3308
|
-
"
|
|
3309
|
-
"
|
|
3253
|
+
"async function continueStalledGjcTeamWorkers(): Promise<void>",
|
|
3254
|
+
"async function relocatedContinuation(): Promise<void>",
|
|
3310
3255
|
),
|
|
3311
3256
|
},
|
|
3312
3257
|
1,
|
|
@@ -3315,8 +3260,8 @@ async function monitorGjcTeam(): Promise<void> {
|
|
|
3315
3260
|
await runSelfTestFixture(
|
|
3316
3261
|
{
|
|
3317
3262
|
"packages/coding-agent/src/gjc-runtime/team-runtime.ts": canonicalTeamRuntimeSendKeysFixture.replace(
|
|
3318
|
-
"
|
|
3319
|
-
|
|
3263
|
+
"if (revalidationReason) {\n\t\treturn;\n\t}",
|
|
3264
|
+
"if (revalidationReason) {}",
|
|
3320
3265
|
),
|
|
3321
3266
|
},
|
|
3322
3267
|
1,
|
|
@@ -3325,8 +3270,8 @@ async function monitorGjcTeam(): Promise<void> {
|
|
|
3325
3270
|
await runSelfTestFixture(
|
|
3326
3271
|
{
|
|
3327
3272
|
"packages/coding-agent/src/gjc-runtime/team-runtime.ts": canonicalTeamRuntimeSendKeysFixture.replace(
|
|
3328
|
-
"
|
|
3329
|
-
"
|
|
3273
|
+
"await continueStalledGjcTeamWorkers();",
|
|
3274
|
+
"",
|
|
3330
3275
|
),
|
|
3331
3276
|
},
|
|
3332
3277
|
1,
|
|
@@ -3335,8 +3280,8 @@ async function monitorGjcTeam(): Promise<void> {
|
|
|
3335
3280
|
await runSelfTestFixture(
|
|
3336
3281
|
{
|
|
3337
3282
|
"packages/coding-agent/src/gjc-runtime/team-runtime.ts": canonicalTeamRuntimeSendKeysFixture.replace(
|
|
3338
|
-
|
|
3339
|
-
"
|
|
3283
|
+
"await continueStalledGjcTeamWorkers();\n\t\tawait reconcileGjcTeamStaleClaimsUnlocked(workerOrchestrationRuntime, teamName, dir, config, env, capability);",
|
|
3284
|
+
"await reconcileGjcTeamStaleClaimsUnlocked(workerOrchestrationRuntime, teamName, dir, config, env, capability);\n\t\tawait continueStalledGjcTeamWorkers();",
|
|
3340
3285
|
),
|
|
3341
3286
|
},
|
|
3342
3287
|
1,
|
|
@@ -3345,8 +3290,8 @@ async function monitorGjcTeam(): Promise<void> {
|
|
|
3345
3290
|
await runSelfTestFixture(
|
|
3346
3291
|
{
|
|
3347
3292
|
"packages/coding-agent/src/gjc-runtime/team-runtime.ts": canonicalTeamRuntimeSendKeysFixture.replace(
|
|
3348
|
-
|
|
3349
|
-
'
|
|
3293
|
+
"const dispatch =",
|
|
3294
|
+
'args.push("forged");\nconst dispatch =',
|
|
3350
3295
|
),
|
|
3351
3296
|
},
|
|
3352
3297
|
1,
|
|
@@ -3372,8 +3317,8 @@ async function monitorGjcTeam(): Promise<void> {
|
|
|
3372
3317
|
await runSelfTestFixture(
|
|
3373
3318
|
{
|
|
3374
3319
|
"packages/coding-agent/src/gjc-runtime/team-runtime.ts": canonicalTeamRuntimeSendKeysFixture.replace(
|
|
3375
|
-
'
|
|
3376
|
-
"`$" + "{
|
|
3320
|
+
'continuationPrompt,\n\t\t";",',
|
|
3321
|
+
"`$" + "{continuationPrompt}" + '`,\n\t\t";",',
|
|
3377
3322
|
),
|
|
3378
3323
|
},
|
|
3379
3324
|
1,
|
|
@@ -3382,8 +3327,8 @@ async function monitorGjcTeam(): Promise<void> {
|
|
|
3382
3327
|
await runSelfTestFixture(
|
|
3383
3328
|
{
|
|
3384
3329
|
"packages/coding-agent/src/gjc-runtime/team-runtime.ts": canonicalTeamRuntimeSendKeysFixture.replace(
|
|
3385
|
-
'"send-keys",\n\t"-l",\n\t"-t",\n\tworker.pane_id,\n\
|
|
3386
|
-
'"send-keys", "-t", worker.pane_id, "Enter", ";", "send-keys", "-l", "-t", worker.pane_id,
|
|
3330
|
+
'"send-keys",\n\t\t"-l",\n\t\t"-t",\n\t\tworker.pane_id,\n\t\tcontinuationPrompt,\n\t\t";",\n\t\t"send-keys",\n\t\t"-t",\n\t\tworker.pane_id,\n\t\t"Enter",',
|
|
3331
|
+
'"send-keys", "-t", worker.pane_id, "Enter", ";", "send-keys", "-l", "-t", worker.pane_id, continuationPrompt,',
|
|
3387
3332
|
),
|
|
3388
3333
|
},
|
|
3389
3334
|
1,
|
|
@@ -3392,8 +3337,8 @@ async function monitorGjcTeam(): Promise<void> {
|
|
|
3392
3337
|
await runSelfTestFixture(
|
|
3393
3338
|
{
|
|
3394
3339
|
"packages/coding-agent/src/gjc-runtime/team-runtime.ts": canonicalTeamRuntimeSendKeysFixture.replace(
|
|
3395
|
-
"
|
|
3396
|
-
|
|
3340
|
+
": (() => {",
|
|
3341
|
+
": Bun.spawnSync([config.tmux_command, ...args])",
|
|
3397
3342
|
),
|
|
3398
3343
|
},
|
|
3399
3344
|
1,
|