@kenkaiiii/ggcoder 5.44.2 → 5.45.0

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.
Files changed (65) hide show
  1. package/dist/core/agent-session-compaction.test.js +60 -0
  2. package/dist/core/agent-session-compaction.test.js.map +1 -1
  3. package/dist/core/agent-session-verification-gate.test.d.ts +2 -0
  4. package/dist/core/agent-session-verification-gate.test.d.ts.map +1 -0
  5. package/dist/core/agent-session-verification-gate.test.js +135 -0
  6. package/dist/core/agent-session-verification-gate.test.js.map +1 -0
  7. package/dist/core/agent-session.d.ts +2 -0
  8. package/dist/core/agent-session.d.ts.map +1 -1
  9. package/dist/core/agent-session.js +41 -0
  10. package/dist/core/agent-session.js.map +1 -1
  11. package/dist/core/process-manager-wake.test.d.ts +2 -0
  12. package/dist/core/process-manager-wake.test.d.ts.map +1 -0
  13. package/dist/core/process-manager-wake.test.js +96 -0
  14. package/dist/core/process-manager-wake.test.js.map +1 -0
  15. package/dist/core/process-manager.d.ts +39 -1
  16. package/dist/core/process-manager.d.ts.map +1 -1
  17. package/dist/core/process-manager.js +146 -2
  18. package/dist/core/process-manager.js.map +1 -1
  19. package/dist/core/provider-cache.e2e.test.d.ts +2 -0
  20. package/dist/core/provider-cache.e2e.test.d.ts.map +1 -0
  21. package/dist/core/provider-cache.e2e.test.js +125 -0
  22. package/dist/core/provider-cache.e2e.test.js.map +1 -0
  23. package/dist/core/session-injections.test.d.ts +2 -0
  24. package/dist/core/session-injections.test.d.ts.map +1 -0
  25. package/dist/core/session-injections.test.js +117 -0
  26. package/dist/core/session-injections.test.js.map +1 -0
  27. package/dist/core/session-reconstructability.test.d.ts +2 -0
  28. package/dist/core/session-reconstructability.test.d.ts.map +1 -0
  29. package/dist/core/session-reconstructability.test.js +105 -0
  30. package/dist/core/session-reconstructability.test.js.map +1 -0
  31. package/dist/core/settings-manager.d.ts +1 -0
  32. package/dist/core/settings-manager.d.ts.map +1 -1
  33. package/dist/core/settings-manager.js +5 -0
  34. package/dist/core/settings-manager.js.map +1 -1
  35. package/dist/core/verification-gate.d.ts +55 -0
  36. package/dist/core/verification-gate.d.ts.map +1 -0
  37. package/dist/core/verification-gate.js +188 -0
  38. package/dist/core/verification-gate.js.map +1 -0
  39. package/dist/core/verification-gate.test.d.ts +2 -0
  40. package/dist/core/verification-gate.test.d.ts.map +1 -0
  41. package/dist/core/verification-gate.test.js +102 -0
  42. package/dist/core/verification-gate.test.js.map +1 -0
  43. package/dist/system-prompt.test.js +33 -0
  44. package/dist/system-prompt.test.js.map +1 -1
  45. package/dist/tools/bash.d.ts +4 -0
  46. package/dist/tools/bash.d.ts.map +1 -1
  47. package/dist/tools/bash.js +57 -3
  48. package/dist/tools/bash.js.map +1 -1
  49. package/dist/tools/bash.test.js +37 -0
  50. package/dist/tools/bash.test.js.map +1 -1
  51. package/dist/tools/read-only-bash.d.ts +6 -0
  52. package/dist/tools/read-only-bash.d.ts.map +1 -1
  53. package/dist/tools/read-only-bash.js +168 -9
  54. package/dist/tools/read-only-bash.js.map +1 -1
  55. package/dist/tools/read-only-bash.test.js +49 -0
  56. package/dist/tools/read-only-bash.test.js.map +1 -1
  57. package/dist/tools/tool-catalog.test.d.ts +2 -0
  58. package/dist/tools/tool-catalog.test.d.ts.map +1 -0
  59. package/dist/tools/tool-catalog.test.js +124 -0
  60. package/dist/tools/tool-catalog.test.js.map +1 -0
  61. package/dist/ui/App.d.ts +3 -0
  62. package/dist/ui/App.d.ts.map +1 -1
  63. package/dist/ui/App.js +35 -1
  64. package/dist/ui/App.js.map +1 -1
  65. package/package.json +4 -4
@@ -0,0 +1,105 @@
1
+ /**
2
+ * Replay invariant: history re-derived from the persisted session log equals
3
+ * the message array the provider actually received (deepseek-harness takeaway
4
+ * #1 — "model-visible means logged", the reconstruction half).
5
+ *
6
+ * A scripted palsu factory CAPTURES the request messages on every call (the
7
+ * sent side). The same session file is then re-loaded through the resume path
8
+ * (`SessionManager.load` + `getMessages`) and the derived history is compared
9
+ * against the final captured request. Divergence means something reached the
10
+ * model without being persisted (or persisted differently) — a resumed or
11
+ * replayed session would show the model a history that never happened.
12
+ *
13
+ * Documented, deliberate divergences between log and request:
14
+ * - `MAX_PERSISTED_TOOL_TEXT_CHARS` (40k) truncation of oversized tool text
15
+ * at persist time (session-storage.ts). Kept out of this test's happy path
16
+ * by keeping tool results small; the exemption is named here so a future
17
+ * failure can be checked against it.
18
+ * - Externalized media / display-only rows — not exercised here.
19
+ */
20
+ import fs from "node:fs/promises";
21
+ import os from "node:os";
22
+ import path from "node:path";
23
+ import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
24
+ import { registerPalsuProvider } from "@kenkaiiii/gg-ai";
25
+ import { useFakeHome } from "../test-support/fake-home.js";
26
+ const shouldCompactMock = vi.hoisted(() => vi.fn());
27
+ const compactMock = vi.hoisted(() => vi.fn());
28
+ vi.mock("./compaction/compactor.js", async () => {
29
+ const actual = await vi.importActual("./compaction/compactor.js");
30
+ return { ...actual, shouldCompact: shouldCompactMock, compact: compactMock };
31
+ });
32
+ let restoreHome;
33
+ let tmpHome;
34
+ let tmpProject;
35
+ let palsu;
36
+ beforeEach(async () => {
37
+ tmpHome = await fs.mkdtemp(path.join(os.tmpdir(), "gg-replay-home-"));
38
+ tmpProject = await fs.mkdtemp(path.join(os.tmpdir(), "gg-replay-project-"));
39
+ restoreHome = useFakeHome(tmpHome);
40
+ palsu = registerPalsuProvider();
41
+ shouldCompactMock.mockReset().mockReturnValue(false);
42
+ compactMock.mockReset();
43
+ await fs.mkdir(path.join(tmpHome, ".gg"), { recursive: true });
44
+ await fs.writeFile(path.join(tmpHome, ".gg", "auth.json"), JSON.stringify({
45
+ palsu: { accessToken: "t", refreshToken: "r", expiresAt: Date.now() + 3_600_000 },
46
+ }), "utf-8");
47
+ });
48
+ afterEach(async () => {
49
+ palsu.unregister();
50
+ restoreHome?.();
51
+ await fs.rm(tmpHome, { recursive: true, force: true });
52
+ await fs.rm(tmpProject, { recursive: true, force: true });
53
+ vi.clearAllMocks();
54
+ });
55
+ describe("session log reconstructs exactly what the model received", () => {
56
+ it("derived history equals the final captured request", async () => {
57
+ const { AgentSession } = await import("./agent-session.js");
58
+ const { SessionManager } = await import("./session-manager.js");
59
+ // Every request the provider saw, deep-cloned at call time (the loop
60
+ // mutates its message array in place, so references alone would all show
61
+ // the final state). Each scripted reply is a factory that captures the
62
+ // request and returns the next scripted assistant message.
63
+ const capturedRequests = [];
64
+ const replies = [
65
+ { content: [{ type: "tool_call", id: "call_1", name: "boom", args: {} }] },
66
+ { content: [{ type: "text", text: "Recovered after the failed call." }] },
67
+ ];
68
+ palsu.setResponses(replies.map((reply) => (messages) => {
69
+ capturedRequests.push(structuredClone(messages));
70
+ return { role: "assistant", ...reply };
71
+ }));
72
+ const session = new AgentSession({
73
+ provider: "palsu",
74
+ model: "claude-sonnet-5",
75
+ cwd: tmpProject,
76
+ systemPrompt: "test system prompt",
77
+ });
78
+ await session.initialize();
79
+ session.queueMessage("Steering: keep going."); // drained at the loop's initial poll
80
+ await session.prompt("Do the task.");
81
+ const sessionPath = session.getState().sessionPath; // before dispose() clears it
82
+ await session.dispose();
83
+ // The run must have made multiple requests (user→tool-call, then result).
84
+ expect(capturedRequests.length).toBeGreaterThanOrEqual(2);
85
+ const finalRequest = capturedRequests[capturedRequests.length - 1];
86
+ // Re-derive history exactly as resume does.
87
+ const manager = new SessionManager(path.join(tmpHome, ".gg", "sessions"));
88
+ const { entries } = await manager.load(sessionPath);
89
+ const derived = manager.getMessages(entries);
90
+ // The final request = [system, ...history]; getMessages drops the system
91
+ // row (resume re-attaches a freshly built system prompt).
92
+ // `provenance` is the ONE documented exemption: log-side bookkeeping
93
+ // (source/kind/visibility) that persistence adds and the wire strips —
94
+ // it never reaches the model, so the model-visible comparison drops it.
95
+ const stripProvenance = (messages) => messages.filter((m) => m.role !== "system").map(({ provenance: _p, ...rest }) => rest);
96
+ const derivedRows = stripProvenance(derived);
97
+ const sentRows = stripProvenance(finalRequest);
98
+ // The final assistant reply is persisted AFTER the last request (it IS the
99
+ // response to it), so the derived history = sent history + that reply.
100
+ expect(derivedRows.slice(0, sentRows.length)).toEqual(sentRows);
101
+ expect(derivedRows.length).toBe(sentRows.length + 1); // exactly one trailing reply
102
+ expect(derivedRows[derivedRows.length - 1].role).toBe("assistant");
103
+ }, 60_000);
104
+ });
105
+ //# sourceMappingURL=session-reconstructability.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"session-reconstructability.test.js","sourceRoot":"","sources":["../../src/core/session-reconstructability.test.ts"],"names":[],"mappings":"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"}
@@ -37,6 +37,7 @@ declare const SettingsSchema: z.ZodObject<{
37
37
  }>>;
38
38
  showTokenUsage: z.ZodDefault<z.ZodBoolean>;
39
39
  idealReviewEnabled: z.ZodDefault<z.ZodBoolean>;
40
+ verificationGateEnabled: z.ZodDefault<z.ZodBoolean>;
40
41
  lspDiagnostics: z.ZodDefault<z.ZodBoolean>;
41
42
  allowOutsideWorkspaceWrites: z.ZodDefault<z.ZodBoolean>;
42
43
  networkMode: z.ZodDefault<z.ZodEnum<{
@@ -1 +1 @@
1
- {"version":3,"file":"settings-manager.d.ts","sourceRoot":"","sources":["../../src/core/settings-manager.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAKxB,QAAA,MAAM,cAAc;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAsGlB,CAAC;AAEH,MAAM,MAAM,QAAQ,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,cAAc,CAAC,CAAC;AAEtD,eAAO,MAAM,gBAAgB,EAAE,QAsB9B,CAAC;AAIF,qBAAa,eAAe;IAC1B,OAAO,CAAC,QAAQ,CAAqC;IACrD,OAAO,CAAC,QAAQ,CAAS;IACzB,OAAO,CAAC,MAAM,CAAS;gBAEX,QAAQ,CAAC,EAAE,MAAM;IAIvB,IAAI,IAAI,OAAO,CAAC,QAAQ,CAAC;IAazB,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAK3B,GAAG,CAAC,CAAC,SAAS,MAAM,QAAQ,EAAE,GAAG,EAAE,CAAC,GAAG,QAAQ,CAAC,CAAC,CAAC;IAI5C,GAAG,CAAC,CAAC,SAAS,MAAM,QAAQ,EAAE,GAAG,EAAE,CAAC,EAAE,KAAK,EAAE,QAAQ,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC;IAK9E,MAAM,IAAI,QAAQ;IAIlB;;4CAEwC;IACxC,gBAAgB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO;IAMtC;qCACiC;IAC3B,YAAY,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;CAO/C"}
1
+ {"version":3,"file":"settings-manager.d.ts","sourceRoot":"","sources":["../../src/core/settings-manager.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAKxB,QAAA,MAAM,cAAc;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBA0GlB,CAAC;AAEH,MAAM,MAAM,QAAQ,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,cAAc,CAAC,CAAC;AAEtD,eAAO,MAAM,gBAAgB,EAAE,QAuB9B,CAAC;AAIF,qBAAa,eAAe;IAC1B,OAAO,CAAC,QAAQ,CAAqC;IACrD,OAAO,CAAC,QAAQ,CAAS;IACzB,OAAO,CAAC,MAAM,CAAS;gBAEX,QAAQ,CAAC,EAAE,MAAM;IAIvB,IAAI,IAAI,OAAO,CAAC,QAAQ,CAAC;IAazB,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAK3B,GAAG,CAAC,CAAC,SAAS,MAAM,QAAQ,EAAE,GAAG,EAAE,CAAC,GAAG,QAAQ,CAAC,CAAC,CAAC;IAI5C,GAAG,CAAC,CAAC,SAAS,MAAM,QAAQ,EAAE,GAAG,EAAE,CAAC,EAAE,KAAK,EAAE,QAAQ,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC;IAK9E,MAAM,IAAI,QAAQ;IAIlB;;4CAEwC;IACxC,gBAAgB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO;IAMtC;qCACiC;IAC3B,YAAY,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;CAO/C"}
@@ -38,6 +38,10 @@ const SettingsSchema = z.object({
38
38
  .default("auto"),
39
39
  showTokenUsage: z.boolean().default(true),
40
40
  idealReviewEnabled: z.boolean().default(true),
41
+ /** Pre-stop gate: when code was edited but no test/typecheck/lint/build
42
+ * command completed since the last edit, the turn is continued once with a
43
+ * demand to verify (then one escalation demanding an honest statement). */
44
+ verificationGateEnabled: z.boolean().default(true),
41
45
  /** Append LSP diagnostics to edit/write tool results. */
42
46
  lspDiagnostics: z.boolean().default(true),
43
47
  /** Allow write/edit outside the workspace (cwd, tmpdir, ~/.gg). Off by
@@ -115,6 +119,7 @@ export const DEFAULT_SETTINGS = {
115
119
  theme: "auto",
116
120
  showTokenUsage: true,
117
121
  idealReviewEnabled: true,
122
+ verificationGateEnabled: true,
118
123
  lspDiagnostics: true,
119
124
  allowOutsideWorkspaceWrites: false,
120
125
  networkMode: "off",
@@ -1 +1 @@
1
- {"version":3,"file":"settings-manager.js","sourceRoot":"","sources":["../../src/core/settings-manager.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"settings-manager.js","sourceRoot":"","sources":["../../src/core/settings-manager.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,55 @@
1
+ /**
2
+ * Verification gate — the turn cannot claim "done" while a promised check is
3
+ * still owed.
4
+ *
5
+ * When a run mutated code files and no test / typecheck / lint / build command
6
+ * completed after the last mutation, the pre-stop hook injects a follow-up
7
+ * demanding the project's verification be run, and blocks completion until it
8
+ * is (or the budget escalates to an honest "this went unverified" statement).
9
+ * Prompt-only "verify before finishing" instructions can be ignored; this gate
10
+ * is harness-owned bookkeeping on what actually executed.
11
+ *
12
+ * Simplification: verification is recognised by a conservative runner-shape
13
+ * classifier (package-manager/runner + test/lint/check keyword). Both error
14
+ * directions are safe — a missed recognition leaves the gate silent (today's
15
+ * behavior), a false positive merely skips one continuation.
16
+ */
17
+ import type { Message } from "@kenkaiiii/gg-ai";
18
+ /** Follow-ups per run before the gate escalates to "state what went unverified". */
19
+ export declare const MAX_VERIFICATION_INJECTIONS = 1;
20
+ /**
21
+ * True when the command looks like a test/typecheck/lint/build invocation:
22
+ * wrappers stripped, a known runner leading, and a verification keyword among
23
+ * its operands. Deliberately strict — `grep test x`, `git commit -m test` and
24
+ * `./run_tests.sh` all read as NOT verification.
25
+ */
26
+ export declare function isVerificationCommand(command: string): boolean;
27
+ /** True for paths that look like source code (not docs, config or assets). */
28
+ export declare function isCodeFilePath(filePath: string): boolean;
29
+ export declare function buildVerificationFollowUpMessage(files: readonly string[]): Message;
30
+ export declare function buildVerificationEscalationMessage(files: readonly string[]): Message;
31
+ /**
32
+ * Bookkeeping for "code was edited, nothing proved it since". Callers record
33
+ * successful edit/write mutations on code files and completed foreground
34
+ * verification commands, in occurrence order; the gate is owed whenever the
35
+ * newest recorded event is a mutation.
36
+ */
37
+ export declare class VerificationGate {
38
+ private seq;
39
+ private lastMutationSeq;
40
+ private lastVerificationSeq;
41
+ private injections;
42
+ /** Code files mutated since the last verification — the gate's file list. */
43
+ private mutatedFiles;
44
+ recordMutation(filePath: string): void;
45
+ recordVerification(): void;
46
+ isOwed(): boolean;
47
+ /**
48
+ * The blocking message for the pre-stop hook, or null when nothing is owed
49
+ * or the injection budget is spent. First the demand (budget 1), then one
50
+ * terminal escalation requiring an honest unverified statement, then silent.
51
+ */
52
+ followUp(): Message[] | null;
53
+ reset(): void;
54
+ }
55
+ //# sourceMappingURL=verification-gate.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"verification-gate.d.ts","sourceRoot":"","sources":["../../src/core/verification-gate.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AACH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,kBAAkB,CAAC;AAEhD,oFAAoF;AACpF,eAAO,MAAM,2BAA2B,IAAI,CAAC;AAkF7C;;;;;GAKG;AACH,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAuB9D;AAMD,8EAA8E;AAC9E,wBAAgB,cAAc,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAExD;AAED,wBAAgB,gCAAgC,CAAC,KAAK,EAAE,SAAS,MAAM,EAAE,GAAG,OAAO,CAWlF;AAED,wBAAgB,kCAAkC,CAAC,KAAK,EAAE,SAAS,MAAM,EAAE,GAAG,OAAO,CAUpF;AAED;;;;;GAKG;AACH,qBAAa,gBAAgB;IAC3B,OAAO,CAAC,GAAG,CAAK;IAChB,OAAO,CAAC,eAAe,CAAK;IAC5B,OAAO,CAAC,mBAAmB,CAAK;IAChC,OAAO,CAAC,UAAU,CAAK;IACvB,6EAA6E;IAC7E,OAAO,CAAC,YAAY,CAAqB;IAEzC,cAAc,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI;IAKtC,kBAAkB,IAAI,IAAI;IAK1B,MAAM,IAAI,OAAO;IAIjB;;;;OAIG;IACH,QAAQ,IAAI,OAAO,EAAE,GAAG,IAAI;IAY5B,KAAK,IAAI,IAAI;CAOd"}
@@ -0,0 +1,188 @@
1
+ /** Follow-ups per run before the gate escalates to "state what went unverified". */
2
+ export const MAX_VERIFICATION_INJECTIONS = 1;
3
+ /** Words that may precede the real command without changing its shape. */
4
+ const SHELL_WRAPPERS = new Set([
5
+ "env",
6
+ "nohup",
7
+ "nice",
8
+ "time",
9
+ "sudo",
10
+ "stdbuf",
11
+ "command",
12
+ "exec",
13
+ ]);
14
+ /** Leading words that mark a command as a build/test-tool invocation. Anything
15
+ * else (git, grep, ./script.sh, bash …) is conservatively not verification. */
16
+ const RUNNERS = new Set([
17
+ "npm",
18
+ "pnpm",
19
+ "yarn",
20
+ "bun",
21
+ "npx",
22
+ "nr",
23
+ "deno",
24
+ "python",
25
+ "python3",
26
+ "py",
27
+ "uv",
28
+ "go",
29
+ "cargo",
30
+ "rustc",
31
+ "make",
32
+ "cmake",
33
+ "gradle",
34
+ "mvn",
35
+ "dotnet",
36
+ "sbt",
37
+ "swift",
38
+ "tsc",
39
+ "vitest",
40
+ "jest",
41
+ "mocha",
42
+ "pytest",
43
+ "unittest",
44
+ "eslint",
45
+ "biome",
46
+ "ruff",
47
+ "mypy",
48
+ "pylint",
49
+ ]);
50
+ /** A non-flag word after the runner that makes the command a verification. */
51
+ const VERIFY_KEYWORDS = [
52
+ "test",
53
+ "tests",
54
+ "vitest",
55
+ "jest",
56
+ "mocha",
57
+ "pytest",
58
+ "unittest",
59
+ "tsc",
60
+ "typecheck",
61
+ "type-check",
62
+ "eslint",
63
+ "biome",
64
+ "ruff",
65
+ "mypy",
66
+ "pylint",
67
+ "lint",
68
+ "clippy",
69
+ "vet",
70
+ "check",
71
+ "build",
72
+ "compile",
73
+ "verify",
74
+ ];
75
+ function isVerifyWord(word) {
76
+ // Exact keyword, or a script name built on one (npm run test:unit).
77
+ return VERIFY_KEYWORDS.some((keyword) => word === keyword || word.startsWith(`${keyword}:`));
78
+ }
79
+ /**
80
+ * True when the command looks like a test/typecheck/lint/build invocation:
81
+ * wrappers stripped, a known runner leading, and a verification keyword among
82
+ * its operands. Deliberately strict — `grep test x`, `git commit -m test` and
83
+ * `./run_tests.sh` all read as NOT verification.
84
+ */
85
+ export function isVerificationCommand(command) {
86
+ const words = command.trim().split(/\s+/);
87
+ let i = 0;
88
+ while (i < words.length) {
89
+ const word = words[i];
90
+ if (SHELL_WRAPPERS.has(word) || /^[A-Za-z_][A-Za-z0-9_]*=/.test(word)) {
91
+ i += 1;
92
+ continue;
93
+ }
94
+ // `timeout <duration> runner …` — skip the duration argument too.
95
+ if (word === "timeout") {
96
+ i += 2;
97
+ continue;
98
+ }
99
+ break;
100
+ }
101
+ const runner = words[i] ?? "";
102
+ if (!RUNNERS.has(runner))
103
+ return false;
104
+ if (isVerifyWord(runner))
105
+ return true; // bare vitest / jest / tsc / eslint …
106
+ return words
107
+ .slice(i + 1)
108
+ .filter((word) => !word.startsWith("-"))
109
+ .some(isVerifyWord);
110
+ }
111
+ /** Source-file extensions whose edits count as code mutations. */
112
+ const CODE_EXT_RE = /\.(?:[cm]?[jt]sx?|py|rb|go|rs|java|kt|kts|swift|c|h|cpp|cc|hpp|cs|php|vue|svelte|scala|sh|sql|m|mm|zig|ex|exs|erl|dart|lua|pl|r|jl|hs|clj)$/;
113
+ /** True for paths that look like source code (not docs, config or assets). */
114
+ export function isCodeFilePath(filePath) {
115
+ return CODE_EXT_RE.test(filePath);
116
+ }
117
+ export function buildVerificationFollowUpMessage(files) {
118
+ return {
119
+ role: "user",
120
+ provenance: { source: "runtime", kind: "completion_gate", visibility: "hidden" },
121
+ content: "Verification gate: you changed code in this run, but no test, typecheck, lint or build " +
122
+ "command has completed since the last edit:\n" +
123
+ files.map((filePath) => `- ${filePath}`).join("\n") +
124
+ "\nRun the project's verification now (its test command, or the closest equivalent) and " +
125
+ "address any failures. Do not describe the change as tested or working without having run it.",
126
+ };
127
+ }
128
+ export function buildVerificationEscalationMessage(files) {
129
+ return {
130
+ role: "user",
131
+ provenance: { source: "runtime", kind: "completion_gate", visibility: "hidden" },
132
+ content: `Verification is still outstanding after ${MAX_VERIFICATION_INJECTIONS} attempt(s); ` +
133
+ "the gate will not prompt again. Give your final response now and state plainly which of " +
134
+ "these changes went unverified and why, so the user can check them:\n" +
135
+ files.map((filePath) => `- ${filePath}`).join("\n"),
136
+ };
137
+ }
138
+ /**
139
+ * Bookkeeping for "code was edited, nothing proved it since". Callers record
140
+ * successful edit/write mutations on code files and completed foreground
141
+ * verification commands, in occurrence order; the gate is owed whenever the
142
+ * newest recorded event is a mutation.
143
+ */
144
+ export class VerificationGate {
145
+ seq = 0;
146
+ lastMutationSeq = 0;
147
+ lastVerificationSeq = 0;
148
+ injections = 0;
149
+ /** Code files mutated since the last verification — the gate's file list. */
150
+ mutatedFiles = new Set();
151
+ recordMutation(filePath) {
152
+ this.lastMutationSeq = ++this.seq;
153
+ this.mutatedFiles.add(filePath);
154
+ }
155
+ recordVerification() {
156
+ this.lastVerificationSeq = ++this.seq;
157
+ this.mutatedFiles.clear();
158
+ }
159
+ isOwed() {
160
+ return this.lastMutationSeq > this.lastVerificationSeq;
161
+ }
162
+ /**
163
+ * The blocking message for the pre-stop hook, or null when nothing is owed
164
+ * or the injection budget is spent. First the demand (budget 1), then one
165
+ * terminal escalation requiring an honest unverified statement, then silent.
166
+ */
167
+ followUp() {
168
+ if (!this.isOwed())
169
+ return null;
170
+ if (this.injections >= MAX_VERIFICATION_INJECTIONS + 1)
171
+ return null;
172
+ this.injections += 1;
173
+ const files = [...this.mutatedFiles].sort();
174
+ return [
175
+ this.injections > MAX_VERIFICATION_INJECTIONS
176
+ ? buildVerificationEscalationMessage(files)
177
+ : buildVerificationFollowUpMessage(files),
178
+ ];
179
+ }
180
+ reset() {
181
+ this.seq = 0;
182
+ this.lastMutationSeq = 0;
183
+ this.lastVerificationSeq = 0;
184
+ this.injections = 0;
185
+ this.mutatedFiles.clear();
186
+ }
187
+ }
188
+ //# sourceMappingURL=verification-gate.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"verification-gate.js","sourceRoot":"","sources":["../../src/core/verification-gate.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=verification-gate.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"verification-gate.test.d.ts","sourceRoot":"","sources":["../../src/core/verification-gate.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,102 @@
1
+ import { describe, expect, it } from "vitest";
2
+ import { VerificationGate, isCodeFilePath, isVerificationCommand, MAX_VERIFICATION_INJECTIONS, } from "./verification-gate.js";
3
+ describe("isVerificationCommand", () => {
4
+ const yes = [
5
+ ["npm test", "npm test"],
6
+ ["pnpm test:unit", "script name with test: prefix"],
7
+ ["yarn run build", "yarn run build"],
8
+ ["npx vitest run src", "npx vitest"],
9
+ ["vitest run", "bare vitest"],
10
+ ["jest --coverage", "bare jest"],
11
+ ["tsc --noEmit", "bare tsc"],
12
+ ["eslint src/", "bare eslint"],
13
+ ["go test ./...", "go test"],
14
+ ["cargo test", "cargo test"],
15
+ ["cargo clippy", "cargo clippy"],
16
+ ["make check", "make check"],
17
+ ["python -m pytest -x", "python -m pytest"],
18
+ ["pytest tests/", "bare pytest"],
19
+ ["uv run pytest", "uv run pytest"],
20
+ ["timeout 30s npm test", "timeout wrapper with duration"],
21
+ ["CI=1 npm test", "env-var wrapper"],
22
+ ["ruff check src", "ruff check"],
23
+ ["biome check .", "biome check"],
24
+ ["gradle test", "gradle test"],
25
+ ["dotnet test", "dotnet test"],
26
+ ["deno task test", "deno task test"],
27
+ ];
28
+ const no = [
29
+ ["git commit -m fix test", "commit message mentioning test"],
30
+ ["grep test file.ts", "grep for the word test"],
31
+ ["cat test-output.log", "cat a log"],
32
+ ["./run_tests.sh", "direct script"],
33
+ ["bash scripts/test.sh", "bash-invoked script"],
34
+ ["npm install", "package install"],
35
+ ["npm run dev", "dev server"],
36
+ ["go run main.go", "go run"],
37
+ ["python train.py", "python script"],
38
+ ["echo running tests soon", "echo mentioning tests"],
39
+ ];
40
+ it.each(yes)("classifies %s as verification (%s)", (command) => {
41
+ expect(isVerificationCommand(command)).toBe(true);
42
+ });
43
+ it.each(no)("classifies %s as NOT verification (%s)", (command) => {
44
+ expect(isVerificationCommand(command)).toBe(false);
45
+ });
46
+ });
47
+ describe("isCodeFilePath", () => {
48
+ it("accepts source files", () => {
49
+ expect(isCodeFilePath("src/a.ts")).toBe(true);
50
+ expect(isCodeFilePath("src/a.test.tsx")).toBe(true);
51
+ expect(isCodeFilePath("lib/x.py")).toBe(true);
52
+ expect(isCodeFilePath("src/main.rs")).toBe(true);
53
+ expect(isCodeFilePath("cmd/tool.go")).toBe(true);
54
+ });
55
+ it("rejects non-code files", () => {
56
+ expect(isCodeFilePath("README.md")).toBe(false);
57
+ expect(isCodeFilePath("package.json")).toBe(false);
58
+ expect(isCodeFilePath("logo.svg")).toBe(false);
59
+ expect(isCodeFilePath("notes.txt")).toBe(false);
60
+ });
61
+ });
62
+ describe("VerificationGate", () => {
63
+ it("is silent with no mutations", () => {
64
+ const gate = new VerificationGate();
65
+ gate.recordVerification();
66
+ expect(gate.isOwed()).toBe(false);
67
+ expect(gate.followUp()).toBeNull();
68
+ });
69
+ it("is owed after a mutation until a verification follows", () => {
70
+ const gate = new VerificationGate();
71
+ gate.recordVerification();
72
+ gate.recordMutation("a.ts");
73
+ expect(gate.isOwed()).toBe(true);
74
+ const followUp = gate.followUp();
75
+ expect(followUp).toHaveLength(1);
76
+ expect(String(followUp[0].content)).toContain("a.ts");
77
+ gate.recordVerification();
78
+ expect(gate.isOwed()).toBe(false);
79
+ expect(gate.followUp()).toBeNull();
80
+ });
81
+ it("demands once, escalates once, then goes silent", () => {
82
+ const gate = new VerificationGate();
83
+ gate.recordMutation("a.ts");
84
+ const demand = gate.followUp();
85
+ expect(String(demand[0].content)).toContain("Run the project's verification");
86
+ // Model stopped again without verifying.
87
+ const escalation = gate.followUp();
88
+ expect(String(escalation[0].content)).toContain("unverified");
89
+ expect(gate.followUp()).toBeNull();
90
+ expect(MAX_VERIFICATION_INJECTIONS).toBe(1);
91
+ });
92
+ it("a later mutation re-arms an already-satisfied gate after a fresh budget reset", () => {
93
+ const gate = new VerificationGate();
94
+ gate.recordMutation("a.ts");
95
+ gate.followUp();
96
+ gate.recordVerification();
97
+ expect(gate.followUp()).toBeNull();
98
+ gate.reset();
99
+ expect(gate.isOwed()).toBe(false);
100
+ });
101
+ });
102
+ //# sourceMappingURL=verification-gate.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"verification-gate.test.js","sourceRoot":"","sources":["../../src/core/verification-gate.test.ts"],"names":[],"mappings":"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"}
@@ -3,6 +3,7 @@ import os from "node:os";
3
3
  import path from "node:path";
4
4
  import { afterEach, describe, expect, it } from "vitest";
5
5
  import { buildSubAgentSystemPrompt, buildSystemPrompt, collectProjectContext, PROJECT_CONTEXT_MAX_BYTES, } from "./system-prompt.js";
6
+ import { buildKenSystemPrompt } from "./core/ken-prompt.js";
6
7
  const tempDirs = [];
7
8
  async function makeProject(files = {}) {
8
9
  const cwd = await fs.mkdtemp(path.join(os.tmpdir(), "ggcoder-system-prompt-"));
@@ -453,6 +454,38 @@ describe("buildSystemPrompt", () => {
453
454
  expect(openai.startsWith("You are GG Coder by Ken Kai")).toBe(true);
454
455
  expect(openai).not.toContain("You are Claude Code");
455
456
  });
457
+ it("is byte-stable across builds in one process (prefix-cache safety)", async () => {
458
+ // Deterministic arm of bench/baseline/04-prefix-stability.mjs, promoted to
459
+ // a unit test so a volatile section landing in the cached prefix fails
460
+ // `pnpm test`, not a manual bench run. The live cache-hit e2e
461
+ // (core/provider-cache.e2e.test.ts) guards the same property end-to-end.
462
+ const cwd = await makeProject({
463
+ "CLAUDE.md": "Project rules win.",
464
+ "package.json": JSON.stringify({ scripts: { check: "tsc --noEmit" } }),
465
+ });
466
+ const args = {
467
+ skills: [],
468
+ planMode: false,
469
+ approvedPlanPath: undefined,
470
+ toolNames: ["read", "edit", "bash"],
471
+ activeLanguages: new Set(["typescript"]),
472
+ };
473
+ const a = await buildSystemPrompt(cwd, args.skills, args.planMode, args.approvedPlanPath, args.toolNames, args.activeLanguages);
474
+ const b = await buildSystemPrompt(cwd, args.skills, args.planMode, args.approvedPlanPath, args.toolNames, args.activeLanguages);
475
+ expect(a).toBe(b);
476
+ // Same for the Ken advisor prompt — its marker must also partition
477
+ // volatile bytes out of the cached prefix (ken-prompt.ts pins the marker
478
+ // as byte-identical to the build prompt's).
479
+ const kenA = await buildKenSystemPrompt(cwd);
480
+ const kenB = await buildKenSystemPrompt(cwd);
481
+ expect(kenA).toBe(kenB);
482
+ for (const prompt of [a, kenA]) {
483
+ expect(prompt).toContain("<!-- uncached -->");
484
+ // All volatile content (currently only the date) sits AFTER the marker.
485
+ const markerAt = prompt.indexOf("<!-- uncached -->");
486
+ expect(prompt.slice(markerAt)).toMatch(/Today's date: \d{1,2} \w+ \d{4}/);
487
+ }
488
+ });
456
489
  });
457
490
  describe("collectProjectContext", () => {
458
491
  it("picks one file per directory — AGENTS.md shadows CLAUDE.md and the rest", async () => {