@kenkaiiii/ggcoder 5.30.3 → 5.32.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 (103) hide show
  1. package/dist/app-sidecar.js +40 -0
  2. package/dist/app-sidecar.js.map +1 -1
  3. package/dist/core/agent-session-compaction.test.js +36 -1
  4. package/dist/core/agent-session-compaction.test.js.map +1 -1
  5. package/dist/core/agent-session.d.ts.map +1 -1
  6. package/dist/core/agent-session.js +41 -12
  7. package/dist/core/agent-session.js.map +1 -1
  8. package/dist/core/compaction/compactor.d.ts +10 -0
  9. package/dist/core/compaction/compactor.d.ts.map +1 -1
  10. package/dist/core/compaction/compactor.js +31 -2
  11. package/dist/core/compaction/compactor.js.map +1 -1
  12. package/dist/core/compaction/compactor.test.js +32 -0
  13. package/dist/core/compaction/compactor.test.js.map +1 -1
  14. package/dist/core/compaction/query-aware-selector.d.ts +33 -0
  15. package/dist/core/compaction/query-aware-selector.d.ts.map +1 -0
  16. package/dist/core/compaction/query-aware-selector.js +202 -0
  17. package/dist/core/compaction/query-aware-selector.js.map +1 -0
  18. package/dist/core/compaction/query-aware-selector.test.d.ts +2 -0
  19. package/dist/core/compaction/query-aware-selector.test.d.ts.map +1 -0
  20. package/dist/core/compaction/query-aware-selector.test.js +96 -0
  21. package/dist/core/compaction/query-aware-selector.test.js.map +1 -0
  22. package/dist/core/event-bus.d.ts +6 -0
  23. package/dist/core/event-bus.d.ts.map +1 -1
  24. package/dist/core/event-bus.js.map +1 -1
  25. package/dist/core/extensions/loader.d.ts.map +1 -1
  26. package/dist/core/extensions/loader.js +28 -8
  27. package/dist/core/extensions/loader.js.map +1 -1
  28. package/dist/core/extensions/plugin-bundles.d.ts +23 -0
  29. package/dist/core/extensions/plugin-bundles.d.ts.map +1 -0
  30. package/dist/core/extensions/plugin-bundles.js +228 -0
  31. package/dist/core/extensions/plugin-bundles.js.map +1 -0
  32. package/dist/core/extensions/plugin-bundles.test.d.ts +2 -0
  33. package/dist/core/extensions/plugin-bundles.test.d.ts.map +1 -0
  34. package/dist/core/extensions/plugin-bundles.test.js +110 -0
  35. package/dist/core/extensions/plugin-bundles.test.js.map +1 -0
  36. package/dist/core/ken-context.d.ts.map +1 -1
  37. package/dist/core/ken-context.js +8 -0
  38. package/dist/core/ken-context.js.map +1 -1
  39. package/dist/core/ken-context.test.js +40 -0
  40. package/dist/core/ken-context.test.js.map +1 -1
  41. package/dist/core/ken-prompt.js +3 -0
  42. package/dist/core/ken-prompt.js.map +1 -1
  43. package/dist/core/ken-prompt.test.js +5 -0
  44. package/dist/core/ken-prompt.test.js.map +1 -1
  45. package/dist/core/persistent-shell.d.ts +3 -2
  46. package/dist/core/persistent-shell.d.ts.map +1 -1
  47. package/dist/core/persistent-shell.js +6 -2
  48. package/dist/core/persistent-shell.js.map +1 -1
  49. package/dist/core/process-manager.d.ts +2 -1
  50. package/dist/core/process-manager.d.ts.map +1 -1
  51. package/dist/core/process-manager.js +2 -2
  52. package/dist/core/process-manager.js.map +1 -1
  53. package/dist/core/sandbox-domains.d.ts +14 -0
  54. package/dist/core/sandbox-domains.d.ts.map +1 -0
  55. package/dist/core/sandbox-domains.js +76 -0
  56. package/dist/core/sandbox-domains.js.map +1 -0
  57. package/dist/core/sandbox-feedback.d.ts +22 -0
  58. package/dist/core/sandbox-feedback.d.ts.map +1 -0
  59. package/dist/core/sandbox-feedback.js +69 -0
  60. package/dist/core/sandbox-feedback.js.map +1 -0
  61. package/dist/core/sandbox-feedback.test.d.ts +2 -0
  62. package/dist/core/sandbox-feedback.test.d.ts.map +1 -0
  63. package/dist/core/sandbox-feedback.test.js +32 -0
  64. package/dist/core/sandbox-feedback.test.js.map +1 -0
  65. package/dist/core/sandbox.d.ts +91 -0
  66. package/dist/core/sandbox.d.ts.map +1 -0
  67. package/dist/core/sandbox.js +254 -0
  68. package/dist/core/sandbox.js.map +1 -0
  69. package/dist/core/sandbox.test.d.ts +2 -0
  70. package/dist/core/sandbox.test.d.ts.map +1 -0
  71. package/dist/core/sandbox.test.js +157 -0
  72. package/dist/core/sandbox.test.js.map +1 -0
  73. package/dist/core/settings-manager.d.ts +5 -0
  74. package/dist/core/settings-manager.d.ts.map +1 -1
  75. package/dist/core/settings-manager.js +20 -0
  76. package/dist/core/settings-manager.js.map +1 -1
  77. package/dist/core/verification-evidence.d.ts +17 -0
  78. package/dist/core/verification-evidence.d.ts.map +1 -0
  79. package/dist/core/verification-evidence.js +283 -0
  80. package/dist/core/verification-evidence.js.map +1 -0
  81. package/dist/core/verification-evidence.test.d.ts +2 -0
  82. package/dist/core/verification-evidence.test.d.ts.map +1 -0
  83. package/dist/core/verification-evidence.test.js +103 -0
  84. package/dist/core/verification-evidence.test.js.map +1 -0
  85. package/dist/modes/acp-mode.d.ts +13 -0
  86. package/dist/modes/acp-mode.d.ts.map +1 -1
  87. package/dist/modes/acp-mode.js +101 -0
  88. package/dist/modes/acp-mode.js.map +1 -1
  89. package/dist/modes/acp-mode.test.js +80 -9
  90. package/dist/modes/acp-mode.test.js.map +1 -1
  91. package/dist/tools/bash.d.ts +2 -1
  92. package/dist/tools/bash.d.ts.map +1 -1
  93. package/dist/tools/bash.js +54 -16
  94. package/dist/tools/bash.js.map +1 -1
  95. package/dist/tools/index.d.ts +3 -0
  96. package/dist/tools/index.d.ts.map +1 -1
  97. package/dist/tools/index.js +1 -1
  98. package/dist/tools/index.js.map +1 -1
  99. package/dist/tools/read-only-bash.d.ts +3 -0
  100. package/dist/tools/read-only-bash.d.ts.map +1 -1
  101. package/dist/tools/read-only-bash.js +7 -9
  102. package/dist/tools/read-only-bash.js.map +1 -1
  103. package/package.json +6 -5
@@ -0,0 +1,283 @@
1
+ import { hasUnsafeShellSyntax, splitShellCommandSegments } from "../tools/read-only-bash.js";
2
+ const LONG_RUNNING_FLAGS = new Set([
3
+ "--watch",
4
+ "--watchall",
5
+ "--watchall=false",
6
+ "--ui",
7
+ "--inspect",
8
+ "--inspect-brk",
9
+ "-w",
10
+ ]);
11
+ const MUTATING_FLAGS = new Set([
12
+ "--init",
13
+ "--build",
14
+ "-b",
15
+ "--clean",
16
+ "--fix",
17
+ "--write",
18
+ "--update",
19
+ "-u",
20
+ "--updatesnapshot",
21
+ "--incremental",
22
+ "--tsbuildinfofile",
23
+ "--emitdeclarationonly",
24
+ ]);
25
+ const AMBIGUOUS_FLAGS = new Set([
26
+ "--nocheck",
27
+ "--listfilesonly",
28
+ "--showconfig",
29
+ "--help",
30
+ "-h",
31
+ "--version",
32
+ "--generatetrace",
33
+ "--traceresolution",
34
+ "--diagnostics",
35
+ "--extendeddiagnostics",
36
+ "--generatecpuprofile",
37
+ "--collect-only",
38
+ "--listtests",
39
+ ]);
40
+ const SAFE_PACKAGE_SCRIPTS = /^(?:test(?::(?:unit|integration|e2e))?|check|typecheck|type-check|lint(?::check)?|format(?::check|-check)|prettier:check)$/i;
41
+ const UNSAFE_PACKAGE_SCRIPTS = /^(?:build|clean|dev|serve|start|watch|preview|prepare|install|format|lint:fix|test:watch)(?::|$)/i;
42
+ const VERIFIER_WORDS = /(?:^|\s|\/)(?:tsc|vitest|jest|pytest|eslint|prettier|pyright|mypy|ruff|cargo|go|shellcheck)(?:\s|$)/i;
43
+ function tokenize(segment) {
44
+ return segment
45
+ .trim()
46
+ .split(/\s+/)
47
+ .map((token) => token.replace(/^["']|["']$/g, ""))
48
+ .filter(Boolean);
49
+ }
50
+ function lowerFlags(tokens) {
51
+ return new Set(tokens.filter((token) => token.startsWith("-")).map((token) => token.toLowerCase()));
52
+ }
53
+ function hasFlag(flags, denied) {
54
+ for (const flag of flags) {
55
+ const name = flag.split("=")[0];
56
+ if (denied.has(flag) || denied.has(name))
57
+ return true;
58
+ }
59
+ return false;
60
+ }
61
+ function rejected(candidate, reason) {
62
+ return { accepted: false, candidate, reason };
63
+ }
64
+ function accepted(reason) {
65
+ return { accepted: true, candidate: true, reason };
66
+ }
67
+ function classifyTsc(tokens) {
68
+ const flags = lowerFlags(tokens);
69
+ if (hasFlag(flags, LONG_RUNNING_FLAGS))
70
+ return rejected(true, "long-running watch/debug mode");
71
+ if (hasFlag(flags, MUTATING_FLAGS))
72
+ return rejected(true, "mutating or artifact-producing mode");
73
+ if (hasFlag(flags, AMBIGUOUS_FLAGS) || flags.has("-v")) {
74
+ return rejected(true, "does not prove type correctness");
75
+ }
76
+ if (!flags.has("--noemit"))
77
+ return rejected(true, "tsc must explicitly use --noEmit");
78
+ return accepted("bounded TypeScript no-emit check");
79
+ }
80
+ function classifyTestRunner(executable, tokens) {
81
+ const flags = lowerFlags(tokens);
82
+ if (hasFlag(flags, LONG_RUNNING_FLAGS) || flags.has("--watch=false")) {
83
+ return rejected(true, "long-running or interactive test mode");
84
+ }
85
+ if (hasFlag(flags, MUTATING_FLAGS))
86
+ return rejected(true, "mutating test/update mode");
87
+ if (hasFlag(flags, AMBIGUOUS_FLAGS))
88
+ return rejected(true, "does not execute the test suite");
89
+ if (executable === "vitest") {
90
+ const positional = tokens.slice(1).filter((token) => !token.startsWith("-"));
91
+ if (!positional.includes("run") && !flags.has("--run")) {
92
+ return rejected(true, "vitest must explicitly use one-shot run mode");
93
+ }
94
+ }
95
+ return accepted("bounded one-shot test check");
96
+ }
97
+ function classifyDirect(tokens) {
98
+ const executable = tokens[0]?.replace(/^.*[\\/]/, "").toLowerCase();
99
+ if (!executable)
100
+ return rejected(false, "empty command");
101
+ if (executable === "tsc")
102
+ return classifyTsc(tokens);
103
+ if (executable === "vitest" || executable === "jest" || executable === "pytest") {
104
+ return classifyTestRunner(executable, tokens);
105
+ }
106
+ const flags = lowerFlags(tokens);
107
+ if (["eslint", "prettier", "ruff"].includes(executable)) {
108
+ if (hasFlag(flags, LONG_RUNNING_FLAGS))
109
+ return rejected(true, "long-running mode");
110
+ if (hasFlag(flags, MUTATING_FLAGS))
111
+ return rejected(true, "mutating formatter/linter mode");
112
+ if (hasFlag(flags, AMBIGUOUS_FLAGS))
113
+ return rejected(true, "does not execute a static check");
114
+ if (executable === "prettier" && !flags.has("--check")) {
115
+ return rejected(true, "prettier must explicitly use --check");
116
+ }
117
+ if (executable === "ruff" && tokens[1] === "format" && !flags.has("--check")) {
118
+ return rejected(true, "ruff format must explicitly use --check");
119
+ }
120
+ return accepted("bounded static check");
121
+ }
122
+ if (["pyright", "mypy", "shellcheck"].includes(executable)) {
123
+ if (hasFlag(flags, LONG_RUNNING_FLAGS))
124
+ return rejected(true, "long-running mode");
125
+ if (hasFlag(flags, AMBIGUOUS_FLAGS))
126
+ return rejected(true, "does not execute a static check");
127
+ return accepted("bounded static check");
128
+ }
129
+ if (executable === "cargo") {
130
+ const subcommand = tokens[1]?.toLowerCase();
131
+ if (subcommand === "build" || subcommand === "clean" || subcommand === "run") {
132
+ return rejected(true, "artifact-producing Cargo command");
133
+ }
134
+ if (subcommand === "fmt" && !flags.has("--check")) {
135
+ return rejected(true, "cargo fmt must explicitly use --check");
136
+ }
137
+ return ["check", "clippy", "test", "fmt"].includes(subcommand)
138
+ ? accepted("bounded Cargo check")
139
+ : rejected(false, "not a recognized verification command");
140
+ }
141
+ if (executable === "go") {
142
+ const subcommand = tokens[1]?.toLowerCase();
143
+ return subcommand === "test" || subcommand === "vet"
144
+ ? accepted("bounded Go check")
145
+ : rejected(subcommand === "build" || subcommand === "clean", "not a bounded Go check");
146
+ }
147
+ return rejected(VERIFIER_WORDS.test(tokens.join(" ")), "not a recognized verification command");
148
+ }
149
+ function classifyPackageRunner(tokens) {
150
+ const runner = tokens[0].toLowerCase();
151
+ let index = 1;
152
+ while (index < tokens.length) {
153
+ const token = tokens[index].toLowerCase();
154
+ if (["--filter", "-f", "--dir", "-c"].includes(token)) {
155
+ index += 2;
156
+ continue;
157
+ }
158
+ if (token === "--workspace-root" ||
159
+ token === "-w" ||
160
+ token.startsWith("--filter=") ||
161
+ token.startsWith("--dir=")) {
162
+ index += 1;
163
+ continue;
164
+ }
165
+ break;
166
+ }
167
+ const action = tokens[index]?.toLowerCase();
168
+ if (!action)
169
+ return rejected(false, "package runner has no command");
170
+ if (["exec", "x", "dlx"].includes(action))
171
+ return classifyDirect(tokens.slice(index + 1));
172
+ const scriptIndex = action === "run" ? index + 1 : index;
173
+ const script = tokens[scriptIndex]?.toLowerCase();
174
+ if (!script)
175
+ return rejected(false, "package runner has no script");
176
+ if (UNSAFE_PACKAGE_SCRIPTS.test(script)) {
177
+ return rejected(true, "mutating, artifact-producing, or long-running package script");
178
+ }
179
+ if (!SAFE_PACKAGE_SCRIPTS.test(script)) {
180
+ return rejected(false, "package script is not a recognized verification check");
181
+ }
182
+ const scriptArgs = tokens.slice(scriptIndex + 1).filter((token) => token !== "--");
183
+ const flags = lowerFlags(scriptArgs);
184
+ if (hasFlag(flags, LONG_RUNNING_FLAGS))
185
+ return rejected(true, "long-running package-script mode");
186
+ if (hasFlag(flags, MUTATING_FLAGS))
187
+ return rejected(true, "mutating package-script mode");
188
+ if (hasFlag(flags, AMBIGUOUS_FLAGS))
189
+ return rejected(true, "package script does not prove correctness");
190
+ return accepted(`bounded ${runner} verification script`);
191
+ }
192
+ function classifySegment(segment) {
193
+ const candidate = VERIFIER_WORDS.test(segment) || /(?:^|\s)(?:pnpm|npm|yarn|bun)(?:\s|$)/i.test(segment);
194
+ if (hasUnsafeShellSyntax(segment))
195
+ return rejected(candidate, "unsafe shell syntax or redirection");
196
+ const tokens = tokenize(segment);
197
+ const first = tokens[0]?.toLowerCase();
198
+ if (["pnpm", "npm", "yarn", "bun"].includes(first))
199
+ return classifyPackageRunner(tokens);
200
+ if (["npx", "bunx"].includes(first))
201
+ return classifyDirect(tokens.slice(1));
202
+ return classifyDirect(tokens);
203
+ }
204
+ /** Fail-closed semantic classifier: every shell segment must be a bounded check. */
205
+ export function classifyVerificationCommand(command) {
206
+ const candidate = VERIFIER_WORDS.test(command) || /(?:^|\s)(?:pnpm|npm|yarn|bun)(?:\s|$)/i.test(command);
207
+ // Only && preserves fail-closed evidence across a chain. Pipes, OR, semicolons,
208
+ // and newlines can hide a failed check behind a later zero exit status.
209
+ if (command.includes("||") ||
210
+ command.includes(";") ||
211
+ command.includes("\n") ||
212
+ /(^|[^|])\|([^|]|$)/.test(command)) {
213
+ return rejected(candidate, "shell control operator can hide a failed check");
214
+ }
215
+ const segments = splitShellCommandSegments(command);
216
+ if (segments.length === 0)
217
+ return rejected(false, "empty command");
218
+ const results = segments.map(classifySegment);
219
+ const firstRejected = results.find((result) => !result.accepted);
220
+ if (firstRejected) {
221
+ return rejected(results.some((result) => result.candidate), firstRejected.reason);
222
+ }
223
+ return accepted(segments.length === 1 ? results[0].reason : "bounded verification command chain");
224
+ }
225
+ function resultText(result) {
226
+ if (typeof result.content === "string")
227
+ return result.content;
228
+ return result.content
229
+ .filter((part) => part.type === "text")
230
+ .map((part) => part.text)
231
+ .join("\n");
232
+ }
233
+ /** Extract harness-owned evidence from completed bash calls in a transcript. */
234
+ export function collectVerificationEvidence(messages) {
235
+ const calls = new Map();
236
+ const evidence = [];
237
+ for (const message of messages) {
238
+ if (message.role === "assistant" && Array.isArray(message.content)) {
239
+ for (const part of message.content) {
240
+ if (part.type !== "tool_call" || part.name !== "bash")
241
+ continue;
242
+ const command = typeof part.args.command === "string" ? part.args.command.trim() : "";
243
+ const background = part.args.run_in_background === true || part.args.persist === true;
244
+ calls.set(part.id, {
245
+ command,
246
+ classification: classifyVerificationCommand(command),
247
+ background,
248
+ });
249
+ }
250
+ }
251
+ if (message.role !== "tool")
252
+ continue;
253
+ for (const result of message.content) {
254
+ const call = calls.get(result.toolCallId);
255
+ if (!call || !call.classification.candidate)
256
+ continue;
257
+ if (call.background) {
258
+ evidence.push({
259
+ command: call.command,
260
+ status: "rejected",
261
+ reason: "background or persistent commands are not bounded evidence",
262
+ });
263
+ continue;
264
+ }
265
+ if (!call.classification.accepted) {
266
+ evidence.push({
267
+ command: call.command,
268
+ status: "rejected",
269
+ reason: call.classification.reason,
270
+ });
271
+ continue;
272
+ }
273
+ const passed = !result.isError && /^Exit code:\s*0(?:\s|$)/i.test(resultText(result).trim());
274
+ evidence.push({
275
+ command: call.command,
276
+ status: passed ? "passed" : "failed",
277
+ reason: passed ? call.classification.reason : "bounded check did not exit successfully",
278
+ });
279
+ }
280
+ }
281
+ return evidence;
282
+ }
283
+ //# sourceMappingURL=verification-evidence.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"verification-evidence.js","sourceRoot":"","sources":["../../src/core/verification-evidence.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=verification-evidence.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"verification-evidence.test.d.ts","sourceRoot":"","sources":["../../src/core/verification-evidence.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,103 @@
1
+ import { describe, expect, it } from "vitest";
2
+ import { classifyVerificationCommand, collectVerificationEvidence, } from "./verification-evidence.js";
3
+ describe("classifyVerificationCommand", () => {
4
+ it.each([
5
+ "tsc --noEmit",
6
+ "pnpm exec tsc --noEmit --pretty false",
7
+ "pnpm --filter @kenkaiiii/gg-ai check",
8
+ "pnpm -w typecheck",
9
+ "vitest run src/foo.test.ts",
10
+ "pnpm test -- --runInBand",
11
+ "cargo fmt --check && cargo clippy",
12
+ "ruff format --check .",
13
+ ])("accepts bounded check: %s", (command) => {
14
+ expect(classifyVerificationCommand(command)).toMatchObject({
15
+ accepted: true,
16
+ candidate: true,
17
+ });
18
+ });
19
+ it.each([
20
+ ["tsc --init", "mutating"],
21
+ ["tsc --build", "mutating"],
22
+ ["tsc --noEmit --incremental", "mutating"],
23
+ ["tsc --noEmit --tsBuildInfoFile cache.tsbuildinfo", "mutating"],
24
+ ["prettier --write src", "mutating"],
25
+ ["pnpm build", "artifact-producing"],
26
+ ["tsc --watch --noEmit", "long-running"],
27
+ ["vitest --watch", "long-running"],
28
+ ["pnpm dev", "long-running"],
29
+ ["tsc", "--noEmit"],
30
+ ["tsc --noEmit --noCheck", "does not prove"],
31
+ ["tsc --noEmit --listFilesOnly", "does not prove"],
32
+ ["tsc --showConfig", "does not prove"],
33
+ ["tsc --help", "does not prove"],
34
+ ["tsc --version", "does not prove"],
35
+ ["tsc --noEmit --generateTrace trace", "does not prove"],
36
+ ["tsc --noEmit --generateCpuProfile cpu.cpuprofile", "does not prove"],
37
+ ["tsc --noEmit > result.txt", "unsafe shell"],
38
+ ["tsc --noEmit | cat", "control operator"],
39
+ ["tsc --noEmit || echo ignored", "control operator"],
40
+ ["tsc --noEmit; echo ignored", "control operator"],
41
+ ["tsc --noEmit && npm run clean", "mutating"],
42
+ ])("rejects non-evidence command: %s", (command, reason) => {
43
+ expect(classifyVerificationCommand(command)).toMatchObject({
44
+ accepted: false,
45
+ candidate: true,
46
+ reason: expect.stringContaining(reason),
47
+ });
48
+ });
49
+ it("rejects unknown commands without mislabeling ordinary shell work as verification", () => {
50
+ expect(classifyVerificationCommand("git status --short")).toMatchObject({
51
+ accepted: false,
52
+ candidate: false,
53
+ });
54
+ });
55
+ });
56
+ function bashExchange(id, command, result, args = {}) {
57
+ return [
58
+ {
59
+ role: "assistant",
60
+ content: [{ type: "tool_call", id, name: "bash", args: { command, ...args } }],
61
+ },
62
+ {
63
+ role: "tool",
64
+ content: [{ type: "tool_result", toolCallId: id, content: result }],
65
+ },
66
+ ];
67
+ }
68
+ describe("collectVerificationEvidence", () => {
69
+ it("records only successful bounded checks as passed evidence", () => {
70
+ const messages = [
71
+ ...bashExchange("pass", "tsc --noEmit", "Exit code: 0\n"),
72
+ ...bashExchange("fail", "vitest run src/foo.test.ts", "Exit code: 1\n1 test failed"),
73
+ ...bashExchange("watch", "tsc --watch --noEmit", "Exit code: 0\nWatching"),
74
+ ...bashExchange("ordinary", "git status --short", "Exit code: 0\n"),
75
+ ...bashExchange("background", "vitest run", "Background process started.", {
76
+ run_in_background: true,
77
+ }),
78
+ ];
79
+ expect(collectVerificationEvidence(messages)).toEqual([
80
+ {
81
+ command: "tsc --noEmit",
82
+ status: "passed",
83
+ reason: "bounded TypeScript no-emit check",
84
+ },
85
+ {
86
+ command: "vitest run src/foo.test.ts",
87
+ status: "failed",
88
+ reason: "bounded check did not exit successfully",
89
+ },
90
+ {
91
+ command: "tsc --watch --noEmit",
92
+ status: "rejected",
93
+ reason: "long-running watch/debug mode",
94
+ },
95
+ {
96
+ command: "vitest run",
97
+ status: "rejected",
98
+ reason: "background or persistent commands are not bounded evidence",
99
+ },
100
+ ]);
101
+ });
102
+ });
103
+ //# sourceMappingURL=verification-evidence.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"verification-evidence.test.js","sourceRoot":"","sources":["../../src/core/verification-evidence.test.ts"],"names":[],"mappings":"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"}
@@ -33,6 +33,19 @@ export interface AcpAgentSession {
33
33
  setPlanMode(active: boolean): Promise<void>;
34
34
  /** Bake an approved plan into the prompt so [DONE:n] progress markers work. */
35
35
  setApprovedPlan(planPath: string | undefined): Promise<void>;
36
+ /**
37
+ * Registry commands (/model, /compact, …), including any an extension
38
+ * registered during `initialize`. Optional so a test double need not carry a
39
+ * registry — a session without one simply advertises no registry commands.
40
+ */
41
+ readonly slashCommands?: {
42
+ getAll(): {
43
+ name: string;
44
+ aliases: string[];
45
+ description: string;
46
+ usage: string;
47
+ }[];
48
+ };
36
49
  }
37
50
  export interface AcpModeOptions {
38
51
  provider: Provider;
@@ -1 +1 @@
1
- {"version":3,"file":"acp-mode.d.ts","sourceRoot":"","sources":["../../src/modes/acp-mode.ts"],"names":[],"mappings":"AAsBA,OAAO,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAIzE,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AAerD,oFAAoF;AACpF,eAAO,MAAM,oBAAoB,IAAI,CAAC;AA8BtC;;;;;;GAMG;AACH,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,QAAQ,EAAE,IAAI,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAC;IACxC,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IAC5B,MAAM,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACvC,QAAQ,IAAI;QAAE,SAAS,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,QAAQ,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC;IACrE,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IACzB,4EAA4E;IAC5E,SAAS,CAAC,MAAM,EAAE,WAAW,GAAG,IAAI,CAAC;IACrC,yDAAyD;IACzD,WAAW,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAChD,0EAA0E;IAC1E,WAAW,IAAI,OAAO,EAAE,CAAC;IACzB,WAAW,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC5D,gBAAgB,IAAI,aAAa,GAAG,SAAS,CAAC;IAC9C,gBAAgB,CAAC,KAAK,EAAE,aAAa,GAAG,SAAS,GAAG,IAAI,CAAC;IACzD,0EAA0E;IAC1E,WAAW,IAAI,OAAO,CAAC;IACvB,WAAW,CAAC,MAAM,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC5C,+EAA+E;IAC/E,eAAe,CAAC,QAAQ,EAAE,MAAM,GAAG,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CAC9D;AAED,MAAM,WAAW,cAAc;IAC7B,QAAQ,EAAE,QAAQ,CAAC;IACnB,KAAK,EAAE,MAAM,CAAC;IACd,GAAG,EAAE,MAAM,CAAC;IACZ,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,aAAa,CAAC,EAAE,aAAa,CAAC;IAC9B,sDAAsD;IACtD,KAAK,CAAC,EAAE,MAAM,CAAC,cAAc,CAAC;IAC9B,MAAM,CAAC,EAAE,MAAM,CAAC,cAAc,CAAC;IAC/B;;;OAGG;IACH,aAAa,CAAC,EAAE,CAAC,MAAM,EAAE,WAAW,KAAK,eAAe,CAAC;CAC1D;AAsED,2EAA2E;AAC3E,eAAO,MAAM,eAAe,UAAU,CAAC;AACvC,eAAO,MAAM,kBAAkB,aAAa,CAAC;AAC7C,eAAO,MAAM,cAAc,SAAS,CAAC;AA0HrC;;;;;;;;;GASG;AACH,wBAAgB,cAAc,CAAC,QAAQ,EAAE,SAAS,OAAO,EAAE,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,CAgEtF;AAID;;;;GAIG;AACH,wBAAsB,UAAU,CAAC,OAAO,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC,CAqgBvE;AAED;;;;;GAKG;AACH,wBAAsB,aAAa,CAAC,OAAO,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC,CAiB1E"}
1
+ {"version":3,"file":"acp-mode.d.ts","sourceRoot":"","sources":["../../src/modes/acp-mode.ts"],"names":[],"mappings":"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"}
@@ -23,6 +23,8 @@ import readline from "node:readline";
23
23
  import { isAbortError } from "@kenkaiiii/gg-agent";
24
24
  import { getAllModels, getMaxThinkingLevel, getModel } from "@kenkaiiii/gg-core";
25
25
  import { AgentSession } from "../core/agent-session.js";
26
+ import { PROMPT_COMMANDS } from "../core/prompt-commands.js";
27
+ import { loadCustomCommands } from "../core/custom-commands.js";
26
28
  import { findSessionById, listAllSessions, listSessionSummaries, loadSessionCheckpointChain, } from "../session.js";
27
29
  import { getHistoryMessageVisibility, reconstructCheckpointHistory, restoreUserRow, } from "../core/session-history.js";
28
30
  import { formatUserError } from "../utils/error-handler.js";
@@ -186,6 +188,73 @@ function configOptionsFor(session) {
186
188
  },
187
189
  ];
188
190
  }
191
+ /** What a prompt-template command does with whatever follows the name. */
192
+ const TEMPLATE_ARG_HINT = "extra instructions (optional)";
193
+ /**
194
+ * The argument hint for a registry command, taken from its own usage string.
195
+ *
196
+ * `/add-dir [path] — no path lists the current roots` becomes `[path]`: the
197
+ * client renders a hint, not a man page, and the prose after the dash is the
198
+ * description's job.
199
+ */
200
+ function hintFromUsage(name, usage) {
201
+ const withoutName = usage.replace(new RegExp(`^\\s*/${name}\\b`), "").trim();
202
+ const hint = withoutName.split("—")[0].split(" - ")[0].trim();
203
+ return hint ? { hint } : undefined;
204
+ }
205
+ /**
206
+ * Every slash command this session would actually honour, in the precedence
207
+ * `AgentSession.resolveSlashInput` uses: built-in prompt templates, then
208
+ * `.gg/commands/*.md` from the project, then registry commands.
209
+ *
210
+ * That order is not cosmetic. `resolveSlashInput` looks for a template body
211
+ * FIRST and only falls through to the registry when there is none, so a project
212
+ * file named `new.md` really does shadow the registry's `/new`. Listing the
213
+ * registry entry for that name would point a client's user at a description of
214
+ * something the agent will not run.
215
+ *
216
+ * Names already claimed — including a built-in's ALIASES, which
217
+ * `getPromptCommand` matches — are dropped rather than listed twice.
218
+ */
219
+ async function availableCommands(session, cwd) {
220
+ const commands = [];
221
+ const taken = new Set();
222
+ const add = (command) => {
223
+ if (taken.has(command.name))
224
+ return;
225
+ taken.add(command.name);
226
+ commands.push(command);
227
+ };
228
+ for (const command of PROMPT_COMMANDS) {
229
+ for (const alias of command.aliases)
230
+ taken.add(alias);
231
+ add({
232
+ name: command.name,
233
+ description: command.description,
234
+ input: { hint: TEMPLATE_ARG_HINT },
235
+ });
236
+ }
237
+ // Project commands are files on disk, so a client that never rescans still
238
+ // gets whatever existed when the session opened.
239
+ for (const command of await loadCustomCommands(cwd)) {
240
+ add({
241
+ name: command.name,
242
+ description: command.description,
243
+ input: { hint: TEMPLATE_ARG_HINT },
244
+ });
245
+ }
246
+ // Last: anything above with the same NAME beats a registry command. An alias
247
+ // collision does not, so aliases are deliberately not claimed here — a
248
+ // project `q.md` shadows `/q` without hiding `/quit` itself.
249
+ for (const command of session.slashCommands?.getAll() ?? []) {
250
+ add({
251
+ name: command.name,
252
+ description: command.description,
253
+ input: hintFromUsage(command.name, command.usage),
254
+ });
255
+ }
256
+ return commands;
257
+ }
189
258
  /** The `modes` block ACP clients like Zed read from session/new and session/load. */
190
259
  function sessionModes(session) {
191
260
  return {
@@ -338,6 +407,36 @@ export async function runAcpMode(options) {
338
407
  configOptions: configOptionsFor(session),
339
408
  });
340
409
  }
410
+ /**
411
+ * Tell the client which slash commands exist, once the session is up.
412
+ *
413
+ * Sent as a notification AFTER the response that created the session (a
414
+ * timer, not a microtask, so it cannot overtake it): the payload is addressed
415
+ * by sessionId, and a client that has not yet seen its own session/new result
416
+ * has nowhere to put it. The agent is the only party that can know its
417
+ * built-ins, so a client scanning `.gg/commands` on its own would miss them.
418
+ */
419
+ function notifyAvailableCommands(target) {
420
+ const forSession = sessionId;
421
+ setTimeout(() => {
422
+ // A second session/new (or a dispose) beat us here; that session will
423
+ // announce its own commands.
424
+ if (session !== target || sessionId !== forSession)
425
+ return;
426
+ void availableCommands(target, options.cwd)
427
+ .then((commands) => {
428
+ if (session !== target || sessionId !== forSession)
429
+ return;
430
+ notifyUpdate({
431
+ sessionUpdate: "available_commands_update",
432
+ availableCommands: commands,
433
+ });
434
+ })
435
+ // Command discovery reads the disk. A failure there must not take down
436
+ // a session that is otherwise fine.
437
+ .catch(() => { });
438
+ }, 0);
439
+ }
341
440
  /**
342
441
  * Bridge ggcoder's event bus onto `session/update` notifications.
343
442
  *
@@ -491,6 +590,7 @@ export async function runAcpMode(options) {
491
590
  }
492
591
  async function handleNewSession() {
493
592
  const created = await startSession();
593
+ notifyAvailableCommands(created);
494
594
  return { sessionId, configOptions: configOptionsFor(created), modes: sessionModes(created) };
495
595
  }
496
596
  /** The directory a request is about, defaulting to the one we were started in. */
@@ -559,6 +659,7 @@ export async function runAcpMode(options) {
559
659
  sessionId = requested;
560
660
  for (const update of historyUpdates(displayMessages))
561
661
  notifyUpdate(update);
662
+ notifyAvailableCommands(restored);
562
663
  return { configOptions: configOptionsFor(restored), modes: sessionModes(restored) };
563
664
  }
564
665
  /**