@gajae-code/coding-agent 0.3.1 → 0.4.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 (166) hide show
  1. package/CHANGELOG.md +46 -0
  2. package/README.md +1 -1
  3. package/dist/types/cli/args.d.ts +2 -0
  4. package/dist/types/commands/launch.d.ts +6 -0
  5. package/dist/types/config/model-profile-activation.d.ts +30 -0
  6. package/dist/types/config/model-profiles.d.ts +19 -0
  7. package/dist/types/config/model-registry.d.ts +25 -10
  8. package/dist/types/config/model-resolver.d.ts +1 -1
  9. package/dist/types/config/models-config-schema.d.ts +84 -0
  10. package/dist/types/config/settings-schema.d.ts +15 -0
  11. package/dist/types/edit/diff.d.ts +16 -0
  12. package/dist/types/edit/modes/replace.d.ts +7 -0
  13. package/dist/types/extensibility/gjc-plugins/activation.d.ts +14 -0
  14. package/dist/types/extensibility/gjc-plugins/index.d.ts +9 -0
  15. package/dist/types/extensibility/gjc-plugins/injection.d.ts +31 -0
  16. package/dist/types/extensibility/gjc-plugins/loader.d.ts +3 -0
  17. package/dist/types/extensibility/gjc-plugins/paths.d.ts +8 -0
  18. package/dist/types/extensibility/gjc-plugins/schema.d.ts +3 -0
  19. package/dist/types/extensibility/gjc-plugins/state.d.ts +9 -0
  20. package/dist/types/extensibility/gjc-plugins/tools.d.ts +8 -0
  21. package/dist/types/extensibility/gjc-plugins/types.d.ts +64 -0
  22. package/dist/types/extensibility/gjc-plugins/validation.d.ts +4 -0
  23. package/dist/types/extensibility/skills.d.ts +9 -1
  24. package/dist/types/gjc-runtime/state-runtime.d.ts +22 -0
  25. package/dist/types/harness-control-plane/storage.d.ts +7 -0
  26. package/dist/types/lsp/client.d.ts +1 -0
  27. package/dist/types/main.d.ts +10 -1
  28. package/dist/types/modes/bridge/bridge-mode.d.ts +2 -0
  29. package/dist/types/modes/components/custom-provider-wizard.d.ts +10 -0
  30. package/dist/types/modes/components/model-selector.d.ts +6 -1
  31. package/dist/types/modes/components/provider-onboarding-selector.d.ts +1 -1
  32. package/dist/types/modes/controllers/selector-controller.d.ts +1 -0
  33. package/dist/types/modes/prompt-action-autocomplete.d.ts +2 -2
  34. package/dist/types/modes/rpc/rpc-client.d.ts +9 -1
  35. package/dist/types/modes/rpc/rpc-types.d.ts +179 -2
  36. package/dist/types/modes/shared/agent-wire/approval-gate.d.ts +57 -0
  37. package/dist/types/modes/shared/agent-wire/command-dispatch.d.ts +16 -1
  38. package/dist/types/modes/shared/agent-wire/deep-interview-gate.d.ts +47 -0
  39. package/dist/types/modes/shared/agent-wire/event-envelope.d.ts +7 -0
  40. package/dist/types/modes/shared/agent-wire/handshake.d.ts +11 -1
  41. package/dist/types/modes/shared/agent-wire/protocol.d.ts +3 -1
  42. package/dist/types/modes/shared/agent-wire/responses.d.ts +1 -1
  43. package/dist/types/modes/shared/agent-wire/unattended-action-policy.d.ts +27 -0
  44. package/dist/types/modes/shared/agent-wire/unattended-audit.d.ts +68 -0
  45. package/dist/types/modes/shared/agent-wire/unattended-run-controller.d.ts +161 -0
  46. package/dist/types/modes/shared/agent-wire/unattended-session.d.ts +61 -0
  47. package/dist/types/modes/shared/agent-wire/workflow-gate-broker.d.ts +114 -0
  48. package/dist/types/modes/shared/agent-wire/workflow-gate-schema.d.ts +39 -0
  49. package/dist/types/modes/theme/theme.d.ts +2 -1
  50. package/dist/types/modes/types.d.ts +1 -0
  51. package/dist/types/runtime-mcp/transports/stdio.d.ts +0 -4
  52. package/dist/types/sdk.d.ts +8 -1
  53. package/dist/types/session/agent-session.d.ts +10 -0
  54. package/dist/types/session/blob-store.d.ts +17 -0
  55. package/dist/types/session/messages.d.ts +3 -0
  56. package/dist/types/session/session-storage.d.ts +6 -0
  57. package/dist/types/skill-state/active-state.d.ts +13 -0
  58. package/dist/types/task/executor.d.ts +1 -0
  59. package/dist/types/thinking.d.ts +3 -2
  60. package/dist/types/tools/hindsight-recall.d.ts +0 -2
  61. package/dist/types/tools/hindsight-reflect.d.ts +0 -2
  62. package/dist/types/tools/hindsight-retain.d.ts +0 -2
  63. package/dist/types/tools/index.d.ts +7 -4
  64. package/package.json +9 -7
  65. package/src/cli/args.ts +10 -0
  66. package/src/cli.ts +14 -0
  67. package/src/commands/harness.ts +192 -7
  68. package/src/commands/launch.ts +8 -0
  69. package/src/commands/ultragoal.ts +1 -21
  70. package/src/config/model-equivalence.ts +1 -1
  71. package/src/config/model-profile-activation.ts +157 -0
  72. package/src/config/model-profiles.ts +155 -0
  73. package/src/config/model-registry.ts +51 -5
  74. package/src/config/model-resolver.ts +3 -2
  75. package/src/config/models-config-schema.ts +42 -1
  76. package/src/config/settings-schema.ts +14 -1
  77. package/src/defaults/gjc/skills/ultragoal/SKILL.md +11 -1
  78. package/src/defaults/gjc/skills/ultragoal/ai-slop-cleaner.md +61 -0
  79. package/src/defaults/gjc-defaults.ts +7 -0
  80. package/src/discovery/claude-plugins.ts +25 -5
  81. package/src/edit/diff.ts +64 -1
  82. package/src/edit/modes/replace.ts +60 -2
  83. package/src/extensibility/gjc-plugins/activation.ts +87 -0
  84. package/src/extensibility/gjc-plugins/index.ts +9 -0
  85. package/src/extensibility/gjc-plugins/injection.ts +114 -0
  86. package/src/extensibility/gjc-plugins/loader.ts +131 -0
  87. package/src/extensibility/gjc-plugins/paths.ts +66 -0
  88. package/src/extensibility/gjc-plugins/schema.ts +79 -0
  89. package/src/extensibility/gjc-plugins/state.ts +29 -0
  90. package/src/extensibility/gjc-plugins/tools.ts +47 -0
  91. package/src/extensibility/gjc-plugins/types.ts +97 -0
  92. package/src/extensibility/gjc-plugins/validation.ts +76 -0
  93. package/src/extensibility/skills.ts +39 -7
  94. package/src/gjc-runtime/state-runtime.ts +93 -2
  95. package/src/gjc-runtime/state-writer.ts +17 -1
  96. package/src/gjc-runtime/ultragoal-runtime.ts +62 -2
  97. package/src/gjc-runtime/workflow-manifest.generated.json +5 -0
  98. package/src/gjc-runtime/workflow-manifest.ts +2 -2
  99. package/src/harness-control-plane/storage.ts +144 -2
  100. package/src/hashline/hash.ts +23 -0
  101. package/src/hooks/skill-state.ts +2 -0
  102. package/src/internal-urls/docs-index.generated.ts +8 -11
  103. package/src/lsp/client.ts +7 -0
  104. package/src/main.ts +67 -1
  105. package/src/modes/acp/acp-agent.ts +25 -2
  106. package/src/modes/bridge/bridge-mode.ts +124 -2
  107. package/src/modes/components/custom-provider-wizard.ts +318 -0
  108. package/src/modes/components/model-selector.ts +108 -18
  109. package/src/modes/components/provider-onboarding-selector.ts +6 -1
  110. package/src/modes/controllers/input-controller.ts +14 -2
  111. package/src/modes/controllers/selector-controller.ts +57 -1
  112. package/src/modes/prompt-action-autocomplete.ts +49 -10
  113. package/src/modes/rpc/rpc-client.ts +57 -3
  114. package/src/modes/rpc/rpc-mode.ts +67 -0
  115. package/src/modes/rpc/rpc-types.ts +224 -2
  116. package/src/modes/shared/agent-wire/approval-gate.ts +151 -0
  117. package/src/modes/shared/agent-wire/command-dispatch.ts +97 -4
  118. package/src/modes/shared/agent-wire/command-validation.ts +25 -1
  119. package/src/modes/shared/agent-wire/deep-interview-gate.ts +222 -0
  120. package/src/modes/shared/agent-wire/event-envelope.ts +13 -0
  121. package/src/modes/shared/agent-wire/handshake.ts +43 -3
  122. package/src/modes/shared/agent-wire/protocol.ts +7 -0
  123. package/src/modes/shared/agent-wire/responses.ts +2 -2
  124. package/src/modes/shared/agent-wire/scopes.ts +2 -0
  125. package/src/modes/shared/agent-wire/unattended-action-policy.ts +341 -0
  126. package/src/modes/shared/agent-wire/unattended-audit.ts +175 -0
  127. package/src/modes/shared/agent-wire/unattended-run-controller.ts +406 -0
  128. package/src/modes/shared/agent-wire/unattended-session.ts +180 -0
  129. package/src/modes/shared/agent-wire/workflow-gate-broker.ts +324 -0
  130. package/src/modes/shared/agent-wire/workflow-gate-schema.ts +331 -0
  131. package/src/modes/theme/theme.ts +6 -0
  132. package/src/modes/types.ts +1 -0
  133. package/src/prompts/memories/consolidation.md +1 -1
  134. package/src/prompts/memories/read-path.md +6 -7
  135. package/src/prompts/memories/unavailable.md +2 -2
  136. package/src/prompts/tools/bash.md +1 -1
  137. package/src/prompts/tools/irc.md +1 -1
  138. package/src/prompts/tools/read.md +2 -2
  139. package/src/prompts/tools/recall.md +1 -0
  140. package/src/prompts/tools/reflect.md +1 -0
  141. package/src/prompts/tools/retain.md +1 -0
  142. package/src/runtime-mcp/client.ts +7 -4
  143. package/src/runtime-mcp/manager.ts +45 -13
  144. package/src/runtime-mcp/transports/http.ts +40 -14
  145. package/src/runtime-mcp/transports/stdio.ts +11 -10
  146. package/src/sdk.ts +48 -1
  147. package/src/session/agent-session.ts +211 -2
  148. package/src/session/blob-store.ts +84 -0
  149. package/src/session/messages.ts +3 -0
  150. package/src/session/session-manager.ts +390 -33
  151. package/src/session/session-storage.ts +26 -0
  152. package/src/setup/provider-onboarding.ts +2 -2
  153. package/src/skill-state/active-state.ts +89 -1
  154. package/src/slash-commands/builtin-registry.ts +1 -1
  155. package/src/task/discovery.ts +7 -1
  156. package/src/task/executor.ts +18 -2
  157. package/src/task/index.ts +2 -0
  158. package/src/thinking.ts +8 -2
  159. package/src/tools/ask.ts +39 -9
  160. package/src/tools/hindsight-recall.ts +0 -2
  161. package/src/tools/hindsight-reflect.ts +0 -2
  162. package/src/tools/hindsight-retain.ts +0 -2
  163. package/src/tools/index.ts +7 -18
  164. package/src/tools/read.ts +3 -3
  165. package/src/tools/skill.ts +15 -3
  166. package/src/utils/edit-mode.ts +1 -1
@@ -20,6 +20,85 @@ import * as os from "node:os";
20
20
  import * as path from "node:path";
21
21
  import type { EventEnvelope, ReceiptFamily, SessionState } from "./types";
22
22
 
23
+ interface HarnessRootRegistryEntry {
24
+ root: string;
25
+ updatedAt: string;
26
+ }
27
+
28
+ interface HarnessRootRegistry {
29
+ sessionId: string;
30
+ roots: HarnessRootRegistryEntry[];
31
+ }
32
+
33
+ interface ResolveHarnessSessionRootOptions {
34
+ expectedWorkspace?: string;
35
+ }
36
+
37
+ export function canonicalWorkspacePath(workspace: string): string {
38
+ return path.resolve(workspace);
39
+ }
40
+
41
+ function samePath(left: string, right: string): boolean {
42
+ return canonicalWorkspacePath(left) === canonicalWorkspacePath(right);
43
+ }
44
+
45
+ async function ensurePrivateDir(dir: string): Promise<void> {
46
+ await fs.mkdir(dir, { recursive: true, mode: 0o700 });
47
+ await fs.chmod(dir, 0o700);
48
+ }
49
+
50
+ function ensurePrivateDirSync(dir: string): void {
51
+ fsSync.mkdirSync(dir, { recursive: true, mode: 0o700 });
52
+ fsSync.chmodSync(dir, 0o700);
53
+ }
54
+
55
+ function sessionMatchesWorkspace(state: SessionState, expectedWorkspace: string): boolean {
56
+ return samePath(state.handle.workspace, expectedWorkspace);
57
+ }
58
+
59
+ function harnessRootRegistryDir(env: NodeJS.ProcessEnv = process.env): string {
60
+ const override = env.GJC_HARNESS_ROOT_REGISTRY_DIR?.trim();
61
+ if (override) return path.resolve(override);
62
+ return path.join(os.tmpdir(), `gjch${process.getuid?.() ?? "u"}`, "harness-roots");
63
+ }
64
+
65
+ function harnessRootRegistryPath(sessionId: string, env: NodeJS.ProcessEnv = process.env): string {
66
+ assertSafeSessionId(sessionId);
67
+ return path.join(harnessRootRegistryDir(env), `${sessionId}.json`);
68
+ }
69
+
70
+ async function readHarnessRootRegistry(
71
+ sessionId: string,
72
+ env: NodeJS.ProcessEnv = process.env,
73
+ ): Promise<HarnessRootRegistry> {
74
+ const file = harnessRootRegistryPath(sessionId, env);
75
+ try {
76
+ const raw = await fs.readFile(file, "utf8");
77
+ const parsed = JSON.parse(raw) as HarnessRootRegistry;
78
+ if (parsed.sessionId === sessionId && Array.isArray(parsed.roots)) return parsed;
79
+ } catch (error) {
80
+ if ((error as NodeJS.ErrnoException).code !== "ENOENT") throw error;
81
+ }
82
+ return { sessionId, roots: [] };
83
+ }
84
+
85
+ async function writeJsonAtomicPrivate(file: string, value: unknown): Promise<void> {
86
+ await ensurePrivateDir(path.dirname(file));
87
+ const tmp = `${file}.tmp-${randomBytes(4).toString("hex")}`;
88
+ await fs.writeFile(tmp, `${JSON.stringify(value, null, 2)}\n`, { encoding: "utf8", mode: 0o600 });
89
+ await fs.rename(tmp, file);
90
+ await fs.chmod(file, 0o600);
91
+ }
92
+
93
+ async function writeHarnessRootRegistry(
94
+ registry: HarnessRootRegistry,
95
+ env: NodeJS.ProcessEnv = process.env,
96
+ ): Promise<void> {
97
+ const dir = harnessRootRegistryDir(env);
98
+ await ensurePrivateDir(dir);
99
+ const file = harnessRootRegistryPath(registry.sessionId, env);
100
+ await writeJsonAtomicPrivate(file, registry);
101
+ }
23
102
  const SESSION_ID_RE = /^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/;
24
103
  export const MAX_UNIX_SOCKET_PATH_BYTES = 100;
25
104
 
@@ -36,7 +115,7 @@ function socketBase(env: NodeJS.ProcessEnv, allowOverride: boolean): { base: str
36
115
 
37
116
  function socketPathForBase(root: string, sessionId: string, base: string): string {
38
117
  const digest = createHash("sha256").update(`${root}\0${sessionId}`).digest("hex");
39
- fsSync.mkdirSync(base, { recursive: true });
118
+ ensurePrivateDirSync(base);
40
119
  for (const len of [16, 24, 32, 48, 64]) {
41
120
  const stem = `c-${digest.slice(0, len)}`;
42
121
  const metadataPath = path.join(base, `${stem}.json`);
@@ -46,7 +125,10 @@ function socketPathForBase(root: string, sessionId: string, base: string): strin
46
125
  if (existing.root === root && existing.sessionId === sessionId) return path.join(base, `${stem}.sock`);
47
126
  } catch (error) {
48
127
  if ((error as NodeJS.ErrnoException).code !== "ENOENT") throw error;
49
- fsSync.writeFileSync(metadataPath, `${JSON.stringify(metadata, null, 2)}\n`, "utf8");
128
+ fsSync.writeFileSync(metadataPath, `${JSON.stringify(metadata, null, 2)}\n`, {
129
+ encoding: "utf8",
130
+ mode: 0o600,
131
+ });
50
132
  return path.join(base, `${stem}.sock`);
51
133
  }
52
134
  }
@@ -149,6 +231,66 @@ async function readJson<T>(file: string): Promise<T | null> {
149
231
  export async function readSessionState(root: string, sessionId: string): Promise<SessionState | null> {
150
232
  return readJson<SessionState>(sessionPaths(root, sessionId).state);
151
233
  }
234
+ export async function rememberHarnessSessionRoot(
235
+ root: string,
236
+ sessionId: string,
237
+ env: NodeJS.ProcessEnv = process.env,
238
+ ): Promise<void> {
239
+ assertSafeSessionId(sessionId);
240
+ const resolvedRoot = path.resolve(root);
241
+ const registry = await readHarnessRootRegistry(sessionId, env);
242
+ const now = new Date().toISOString();
243
+ registry.roots = [
244
+ { root: resolvedRoot, updatedAt: now },
245
+ ...registry.roots.filter(entry => path.resolve(entry.root) !== resolvedRoot),
246
+ ].slice(0, 8);
247
+ await writeHarnessRootRegistry(registry, env);
248
+ }
249
+
250
+ export async function resolveHarnessSessionRoot(
251
+ root: string,
252
+ sessionId: string,
253
+ env: NodeJS.ProcessEnv = process.env,
254
+ options: ResolveHarnessSessionRootOptions = {},
255
+ ): Promise<string> {
256
+ assertSafeSessionId(sessionId);
257
+ const resolvedRoot = path.resolve(root);
258
+ const candidates: { root: string; state: SessionState }[] = [];
259
+ const seenRoots = new Set<string>();
260
+ const addCandidate = async (candidateRoot: string): Promise<void> => {
261
+ const candidate = path.resolve(candidateRoot);
262
+ if (seenRoots.has(candidate)) return;
263
+ seenRoots.add(candidate);
264
+ const state = await readSessionState(candidate, sessionId);
265
+ if (state !== null) candidates.push({ root: candidate, state });
266
+ };
267
+
268
+ await addCandidate(resolvedRoot);
269
+ const registry = await readHarnessRootRegistry(sessionId, env);
270
+ for (const entry of registry.roots) await addCandidate(entry.root);
271
+
272
+ if (!options.expectedWorkspace) {
273
+ if (candidates.some(candidate => candidate.root === resolvedRoot)) return resolvedRoot;
274
+ if (candidates.length === 1) return candidates[0].root;
275
+ if (candidates.length > 1) {
276
+ throw new StorageError(`ambiguous_harness_session_root:${sessionId}`, "ambiguous_harness_session_root");
277
+ }
278
+ return resolvedRoot;
279
+ }
280
+
281
+ const expectedWorkspace = canonicalWorkspacePath(options.expectedWorkspace);
282
+ const matchingCandidates = candidates.filter(candidate =>
283
+ sessionMatchesWorkspace(candidate.state, expectedWorkspace),
284
+ );
285
+ if (matchingCandidates.length === 1) return matchingCandidates[0].root;
286
+ if (matchingCandidates.length > 1) {
287
+ throw new StorageError(`ambiguous_harness_session_root:${sessionId}`, "ambiguous_harness_session_root");
288
+ }
289
+ if (candidates.length > 0) {
290
+ throw new StorageError(`session_workspace_mismatch:${sessionId}`, "session_workspace_mismatch");
291
+ }
292
+ return resolvedRoot;
293
+ }
152
294
 
153
295
  export async function writeSessionState(root: string, state: SessionState): Promise<void> {
154
296
  const paths = sessionPaths(root, state.sessionId);
@@ -5,6 +5,20 @@
5
5
 
6
6
  import bigrams from "./bigrams.json" with { type: "json" };
7
7
 
8
+ // Optional native acceleration for formatHashLines. Loaded WITHOUT throwing at
9
+ // module evaluation so this core module (and its re-exported helpers) stays
10
+ // usable and falls back to the TS loop if the native addon is unavailable.
11
+ let formatHashLinesNative: ((text: string, startLine?: number) => string) | undefined;
12
+ void import("../../../natives/native/index.js")
13
+ .then(mod => {
14
+ if (typeof mod.h06FormatHashLines === "function") {
15
+ formatHashLinesNative = mod.h06FormatHashLines;
16
+ }
17
+ })
18
+ .catch(() => {
19
+ // Native unavailable; formatHashLines uses the TS loop.
20
+ });
21
+
8
22
  /**
9
23
  * 647 single-token BPE bigrams for hashline anchors. Every entry tokenizes as
10
24
  * exactly one token in modern BPE vocabularies (cl100k / o200k / Anthropic model family),
@@ -168,6 +182,15 @@ export function formatHashLine(lineNumber: number, line: string): string {
168
182
  * ```
169
183
  */
170
184
  export function formatHashLines(text: string, startLine = 1): string {
185
+ // Native path only for the supported startLine domain (non-negative integer);
186
+ // other values fall through to JS numeric semantics in the TS loop.
187
+ if (formatHashLinesNative && Number.isInteger(startLine) && startLine >= 0) {
188
+ try {
189
+ return formatHashLinesNative(text, startLine);
190
+ } catch {
191
+ // Native hashline formatting is an optimization only; preserve the TS contract.
192
+ }
193
+ }
171
194
  const lines = text.split("\n");
172
195
  return lines.map((line, i) => formatHashLine(startLine + i, line)).join("\n");
173
196
  }
@@ -382,6 +382,8 @@ async function seedSkillActivationState(
382
382
  return state;
383
383
  }
384
384
 
385
+ // Fallback for native-hook prompts when SkillPromptDetails.subskillActivation is absent;
386
+ // real /skill dispatch paths resolve sub-skill activation before prompt construction.
385
387
  export async function recordSkillActivation(input: RecordSkillActivationInput): Promise<SkillActiveState | null> {
386
388
  const match = detectPrimarySkillKeyword(input.text);
387
389
  if (!match) return null;