@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
@@ -40,7 +40,9 @@ export interface SessionStorage {
40
40
  readTextPrefix(path: string, maxBytes: number): Promise<string>;
41
41
  writeText(path: string, content: string): Promise<void>;
42
42
  rename(path: string, nextPath: string): Promise<void>;
43
+ renameSync(path: string, nextPath: string): void;
43
44
  unlink(path: string): Promise<void>;
45
+ unlinkSync(path: string): void;
44
46
  deleteSessionWithArtifacts(sessionPath: string): Promise<void>;
45
47
  openWriter(path: string, options?: { flags?: "a" | "w"; onError?: (err: Error) => void }): SessionStorageWriter;
46
48
  }
@@ -198,10 +200,22 @@ export class FileSessionStorage implements SessionStorage {
198
200
  }
199
201
  }
200
202
 
203
+ renameSync(path: string, nextPath: string): void {
204
+ try {
205
+ fs.renameSync(path, nextPath);
206
+ } catch (err) {
207
+ throw toError(err);
208
+ }
209
+ }
210
+
201
211
  unlink(path: string): Promise<void> {
202
212
  return fs.promises.unlink(path);
203
213
  }
204
214
 
215
+ unlinkSync(path: string): void {
216
+ fs.unlinkSync(path);
217
+ }
218
+
205
219
  openWriter(path: string, options?: { flags?: "a" | "w"; onError?: (err: Error) => void }): SessionStorageWriter {
206
220
  return new FileSessionStorageWriter(path, options);
207
221
  }
@@ -375,10 +389,22 @@ export class MemorySessionStorage implements SessionStorage {
375
389
  return Promise.resolve();
376
390
  }
377
391
 
392
+ renameSync(path: string, nextPath: string): void {
393
+ const entry = this.#files.get(path);
394
+ if (!entry) throw new Error(`File not found: ${path}`);
395
+ this.#files.set(nextPath, entry);
396
+ this.#files.delete(path);
397
+ }
398
+
378
399
  unlink(path: string): Promise<void> {
379
400
  this.#files.delete(path);
380
401
  return Promise.resolve();
381
402
  }
403
+
404
+ unlinkSync(path: string): void {
405
+ this.#files.delete(path);
406
+ }
407
+
382
408
  deleteSessionWithArtifacts(_sessionPath: string): Promise<void> {
383
409
  return Promise.resolve();
384
410
  }
@@ -79,7 +79,7 @@ export const PROVIDER_PRESETS: readonly ProviderPreset[] = [
79
79
  providerId: "minimax-code",
80
80
  baseUrl: "https://api.minimax.io/v1",
81
81
  apiKeyEnv: "MINIMAX_CODE_API_KEY",
82
- models: ["MiniMax-M2.5"],
82
+ models: ["minimax-m3"],
83
83
  compat: MINIMAX_OPENAI_COMPAT,
84
84
  },
85
85
  {
@@ -92,7 +92,7 @@ export const PROVIDER_PRESETS: readonly ProviderPreset[] = [
92
92
  providerId: "minimax-code-cn",
93
93
  baseUrl: "https://api.minimaxi.com/v1",
94
94
  apiKeyEnv: "MINIMAX_CODE_CN_API_KEY",
95
- models: ["MiniMax-M2.5"],
95
+ models: ["minimax-m3"],
96
96
  compat: MINIMAX_OPENAI_COMPAT,
97
97
  },
98
98
  {
@@ -32,6 +32,17 @@ export interface WorkflowHudSummary {
32
32
 
33
33
  export type { WorkflowStateReceipt } from "./workflow-state-contract";
34
34
 
35
+ export interface ActiveSubskillEntry {
36
+ plugin: string;
37
+ subskillName: string;
38
+ parent: string;
39
+ bindsTo: string;
40
+ phase: string;
41
+ activationArg: string;
42
+ filePath: string;
43
+ toolPaths: string[];
44
+ }
45
+
35
46
  export interface SkillActiveEntry {
36
47
  skill: string;
37
48
  phase?: string;
@@ -47,6 +58,7 @@ export interface SkillActiveEntry {
47
58
  handoff_from?: string;
48
59
  handoff_to?: string;
49
60
  handoff_at?: string;
61
+ active_subskills?: ActiveSubskillEntry[];
50
62
  }
51
63
 
52
64
  export interface SkillActiveState {
@@ -64,6 +76,7 @@ export interface SkillActiveState {
64
76
  initialized_mode?: CanonicalGjcWorkflowSkill;
65
77
  initialized_state_path?: string;
66
78
  active_skills?: SkillActiveEntry[];
79
+ active_subskills?: ActiveSubskillEntry[];
67
80
  [key: string]: unknown;
68
81
  }
69
82
 
@@ -87,6 +100,7 @@ export interface SyncSkillActiveStateOptions {
87
100
  handoff_from?: string;
88
101
  handoff_to?: string;
89
102
  handoff_at?: string;
103
+ active_subskills?: ActiveSubskillEntry[];
90
104
  }
91
105
 
92
106
  const HUD_TEXT_LIMIT = 80;
@@ -188,6 +202,48 @@ function normalizeWorkflowStateReceipt(raw: unknown): WorkflowStateReceipt | und
188
202
  mutation_id: mutationId,
189
203
  };
190
204
  }
205
+ function normalizeActiveSubskillEntry(raw: unknown): ActiveSubskillEntry | null {
206
+ if (!raw || typeof raw !== "object") return null;
207
+ const record = raw as Record<string, unknown>;
208
+ const plugin = safeString(record.plugin).trim();
209
+ const subskillName = safeString(record.subskillName).trim();
210
+ const parent = safeString(record.parent).trim();
211
+ const bindsTo = safeString(record.bindsTo).trim();
212
+ const phase = safeString(record.phase).trim();
213
+ const activationArg = safeString(record.activationArg).trim();
214
+ const filePath = safeString(record.filePath).trim();
215
+ const toolPaths = Array.isArray(record.toolPaths)
216
+ ? record.toolPaths.map(item => safeString(item).trim()).filter(Boolean)
217
+ : [];
218
+ if (!plugin || !subskillName || !parent || !bindsTo || !phase || !activationArg || !filePath) return null;
219
+ return { plugin, subskillName, parent, bindsTo, phase, activationArg, filePath, toolPaths };
220
+ }
221
+
222
+ function normalizeActiveSubskillEntries(raw: unknown): ActiveSubskillEntry[] | undefined {
223
+ if (!Array.isArray(raw)) return undefined;
224
+ const entries = raw
225
+ .map(normalizeActiveSubskillEntry)
226
+ .filter((entry): entry is ActiveSubskillEntry => entry !== null);
227
+ return entries.length > 0 ? entries : undefined;
228
+ }
229
+
230
+ function activeSubskillEntryKey(entry: ActiveSubskillEntry): string {
231
+ return [entry.plugin, entry.parent, entry.phase, entry.activationArg].join("\0");
232
+ }
233
+
234
+ function unionActiveSubskillEntries(...entrySets: Array<ActiveSubskillEntry[] | undefined>): ActiveSubskillEntry[] {
235
+ const merged: ActiveSubskillEntry[] = [];
236
+ const seen = new Set<string>();
237
+ for (const entries of entrySets) {
238
+ for (const entry of entries ?? []) {
239
+ const key = activeSubskillEntryKey(entry);
240
+ if (seen.has(key)) continue;
241
+ seen.add(key);
242
+ merged.push(entry);
243
+ }
244
+ }
245
+ return merged;
246
+ }
191
247
 
192
248
  function encodePathSegment(value: string): string {
193
249
  return encodeURIComponent(value).replaceAll(".", "%2E");
@@ -204,6 +260,7 @@ function normalizeEntry(raw: unknown): SkillActiveEntry | null {
204
260
  if (!skill) return null;
205
261
  const hud = normalizeWorkflowHudSummary(record.hud);
206
262
  const receipt = normalizeWorkflowStateReceipt(record.receipt);
263
+ const activeSubskills = normalizeActiveSubskillEntries(record.active_subskills);
207
264
  return {
208
265
  ...record,
209
266
  skill,
@@ -219,6 +276,7 @@ function normalizeEntry(raw: unknown): SkillActiveEntry | null {
219
276
  handoff_at: safeString(record.handoff_at).trim() || undefined,
220
277
  ...(hud ? { hud } : {}),
221
278
  ...(receipt ? { receipt } : {}),
279
+ ...(activeSubskills ? { active_subskills: activeSubskills } : {}),
222
280
  stale: undefined,
223
281
  };
224
282
  }
@@ -278,6 +336,7 @@ export function normalizeSkillActiveState(raw: unknown): SkillActiveState | null
278
336
  session_id: safeString(state.session_id).trim() || primary?.session_id || undefined,
279
337
  thread_id: safeString(state.thread_id).trim() || primary?.thread_id || undefined,
280
338
  turn_id: safeString(state.turn_id).trim() || primary?.turn_id || undefined,
339
+ active_subskills: activeSkills.flatMap(entry => entry.active_subskills ?? []),
281
340
  active_skills: activeSkills.length > 0 ? activeSkills : [],
282
341
  };
283
342
  }
@@ -512,6 +571,7 @@ export async function readVisibleSkillActiveState(cwd: string, sessionId?: strin
512
571
  phase: primary?.phase ?? "",
513
572
  session_id: safeString(sessionId).trim() || primary?.session_id,
514
573
  active_skills: activeSkills,
574
+ active_subskills: activeSkills.flatMap(entry => entry.active_subskills ?? []),
515
575
  };
516
576
  }
517
577
 
@@ -552,7 +612,25 @@ async function rebuildActiveState(cwd: string, sessionScope?: ActiveSessionScope
552
612
  await rebuildActiveSnapshot(cwd, sessionScope, { cwd, audit: activeStateWriterAudit("rebuild-active-snapshot") });
553
613
  }
554
614
 
615
+ async function activeSubskillsForExistingEntry(
616
+ cwd: string,
617
+ sessionId: string | undefined,
618
+ skill: string,
619
+ ): Promise<ActiveSubskillEntry[] | undefined> {
620
+ const { rootPath, sessionPath } = getSkillActiveStatePaths(cwd, sessionId);
621
+ const [rootState, sessionState] = await Promise.all([
622
+ readRawActiveStateForHandoff(rootPath, false),
623
+ sessionPath ? readRawActiveStateForHandoff(sessionPath, false) : Promise.resolve(null),
624
+ ]);
625
+ const existing = mergeVisibleEntries(sessionState, rootState, sessionId).find(entry => entry.skill === skill);
626
+ return existing?.active_subskills;
627
+ }
628
+
555
629
  export async function syncSkillActiveState(options: SyncSkillActiveStateOptions): Promise<void> {
630
+ const preservedActiveSubskills =
631
+ options.active_subskills === undefined
632
+ ? await activeSubskillsForExistingEntry(options.cwd, options.sessionId, options.skill)
633
+ : undefined;
556
634
  const nowIso = options.nowIso ?? new Date().toISOString();
557
635
  const hud = normalizeWorkflowHudSummary(options.hud);
558
636
  const entry: SkillActiveEntry = {
@@ -569,6 +647,11 @@ export async function syncSkillActiveState(options: SyncSkillActiveStateOptions)
569
647
  ...(options.handoff_at ? { handoff_at: options.handoff_at } : {}),
570
648
  ...(hud ? { hud } : {}),
571
649
  ...(options.receipt ? { receipt: options.receipt } : {}),
650
+ ...(options.active_subskills !== undefined
651
+ ? { active_subskills: options.active_subskills }
652
+ : preservedActiveSubskills
653
+ ? { active_subskills: preservedActiveSubskills }
654
+ : {}),
572
655
  };
573
656
  await persistActiveEntry(options.cwd, undefined, entry);
574
657
  await rebuildActiveState(options.cwd);
@@ -636,9 +719,13 @@ export async function applyHandoffToActiveState(options: ApplyHandoffOptions): P
636
719
  ...(priorCaller.handoff_from && !callerEntry.handoff_from
637
720
  ? { handoff_from: priorCaller.handoff_from }
638
721
  : {}),
722
+ ...(priorCaller.active_subskills ? { active_subskills: priorCaller.active_subskills } : {}),
639
723
  }
640
724
  : callerEntry;
641
- return [...kept, mergedCaller, calleeEntry];
725
+ const activeSubskills = unionActiveSubskillEntries(priorCaller?.active_subskills, calleeEntry.active_subskills);
726
+ const mergedCallee: SkillActiveEntry =
727
+ activeSubskills.length > 0 ? { ...calleeEntry, active_subskills: activeSubskills } : calleeEntry;
728
+ return [...kept, mergedCaller, mergedCallee];
642
729
  };
643
730
  const writeEntries = async (
644
731
  sessionScope: ActiveSessionScope | undefined,
@@ -675,5 +762,6 @@ function buildSyncEntry(options: SyncSkillActiveStateOptions, nowIso: string): S
675
762
  ...(options.handoff_at ? { handoff_at: options.handoff_at } : {}),
676
763
  ...(hud ? { hud } : {}),
677
764
  ...(options.receipt ? { receipt: options.receipt } : {}),
765
+ ...(options.active_subskills ? { active_subskills: options.active_subskills } : {}),
678
766
  };
679
767
  }
@@ -748,7 +748,7 @@ const BUILTIN_SLASH_COMMAND_REGISTRY: ReadonlyArray<SlashCommandSpec> = [
748
748
  return;
749
749
  }
750
750
 
751
- void runtime.ctx.showOAuthSelector("login");
751
+ runtime.ctx.showProviderOnboarding();
752
752
  runtime.ctx.editor.setText("");
753
753
  },
754
754
  },
@@ -15,6 +15,7 @@ import { logger } from "@gajae-code/utils";
15
15
  import { isProviderEnabled } from "../capability";
16
16
  import { findAllNearestProjectConfigDirs, getConfigDirs } from "../config";
17
17
  import { listClaudePluginRoots } from "../discovery/helpers";
18
+ import { rootContainsGjcManifest } from "../extensibility/gjc-plugins/paths";
18
19
  import { loadBundledAgents, parseAgent } from "./agents";
19
20
  import type { AgentDefinition, AgentSource } from "./types";
20
21
 
@@ -93,7 +94,12 @@ export async function discoverAgents(cwd: string, home: string = os.homedir()):
93
94
  const { roots: pluginRoots } = isProviderEnabled("claude-plugins")
94
95
  ? await listClaudePluginRoots(home, resolvedCwd)
95
96
  : { roots: [] };
96
- const sortedPluginRoots = [...pluginRoots].sort((a, b) => {
97
+ const nonGjcPluginRoots = [];
98
+ for (const plugin of pluginRoots) {
99
+ if (await rootContainsGjcManifest(plugin.path)) continue;
100
+ nonGjcPluginRoots.push(plugin);
101
+ }
102
+ const sortedPluginRoots = nonGjcPluginRoots.sort((a, b) => {
97
103
  if (a.scope === b.scope) return 0;
98
104
  return a.scope === "project" ? -1 : 1;
99
105
  });
@@ -19,6 +19,7 @@ import { Settings } from "../config/settings";
19
19
  import { SETTINGS_SCHEMA, type SettingPath } from "../config/settings-schema";
20
20
  import { runExtensionCompact, runExtensionSetModel } from "../extensibility/extensions/compact-handler";
21
21
  import { getSessionSlashCommands } from "../extensibility/extensions/get-commands-handler";
22
+ import { buildAgentSubskillInjection } from "../extensibility/gjc-plugins";
22
23
  import { buildSkillPromptMessage, type Skill } from "../extensibility/skills";
23
24
  import type { HindsightSessionState } from "../hindsight/state";
24
25
  import type { LocalProtocolOptions } from "../internal-urls";
@@ -123,6 +124,7 @@ export interface ExecutorOptions {
123
124
  * if the resolved subagent model has no working credentials. See #985.
124
125
  */
125
126
  parentActiveModelPattern?: string;
127
+ parentSessionId?: string;
126
128
  thinkingLevel?: ThinkingLevel;
127
129
  outputSchema?: unknown;
128
130
  /** Parent task recursion depth (0 = top-level, 1 = first child, etc.) */
@@ -1094,6 +1096,7 @@ export async function runSubprocess(options: ExecutorOptions): Promise<SingleRes
1094
1096
  options.parentActiveModelPattern,
1095
1097
  modelRegistry,
1096
1098
  settings,
1099
+ options.parentSessionId,
1097
1100
  ),
1098
1101
  );
1099
1102
  if (authFallbackUsed && model) {
@@ -1157,6 +1160,12 @@ export async function runSubprocess(options: ExecutorOptions): Promise<SingleRes
1157
1160
  ? `This subagent was started with a forked snapshot of the parent conversation. Included ${options.forkContextSeed.metadata.includedMessages} message(s), skipped ${options.forkContextSeed.metadata.skippedMessages}, approximately ${options.forkContextSeed.metadata.approximateTokens} tokens. The snapshot is not live; use IRC for live coordination when enabled.`
1158
1161
  : "";
1159
1162
 
1163
+ const agentSubskillBlock = await buildAgentSubskillInjection({
1164
+ cwd,
1165
+ sessionId: options.parentSessionId,
1166
+ agentName: agent.name,
1167
+ });
1168
+
1160
1169
  const { session } = await awaitAbortable(
1161
1170
  createAgentSession({
1162
1171
  cwd: worktree ?? cwd,
@@ -1183,15 +1192,22 @@ export async function runSubprocess(options: ExecutorOptions): Promise<SingleRes
1183
1192
  ircSelfId: ircEnabled ? id : "",
1184
1193
  forkContext: forkContextNotice,
1185
1194
  });
1195
+ const promptWithSubskills = `${subagentPrompt}${agentSubskillBlock}`;
1186
1196
  return defaultPrompt.length === 0
1187
- ? [subagentPrompt]
1188
- : [...defaultPrompt.slice(0, -1), subagentPrompt, defaultPrompt[defaultPrompt.length - 1]];
1197
+ ? [promptWithSubskills]
1198
+ : [...defaultPrompt.slice(0, -1), promptWithSubskills, defaultPrompt[defaultPrompt.length - 1]];
1189
1199
  },
1190
1200
  sessionManager,
1191
1201
  hasUI: false,
1192
1202
  spawns: spawnsEnv,
1193
1203
  taskDepth: childDepth,
1194
1204
  currentAgentType: agent.name,
1205
+ gjcSubskillToolContext: {
1206
+ cwd,
1207
+ sessionId: options.parentSessionId,
1208
+ parent: agent.name,
1209
+ phase: "prompt",
1210
+ },
1195
1211
  parentHindsightSessionState: options.parentHindsightSessionState,
1196
1212
  parentTaskPrefix: id,
1197
1213
  agentId: id,
package/src/task/index.ts CHANGED
@@ -1310,6 +1310,7 @@ export class TaskTool implements AgentTool<TaskToolSchemaInstance, TaskToolDetai
1310
1310
  taskDepth,
1311
1311
  modelOverride,
1312
1312
  parentActiveModelPattern,
1313
+ parentSessionId: this.session.getSessionId?.() ?? undefined,
1313
1314
  thinkingLevel: thinkingLevelOverride,
1314
1315
  outputSchema: effectiveOutputSchema,
1315
1316
  sessionFile: taskSessionFile,
@@ -1369,6 +1370,7 @@ export class TaskTool implements AgentTool<TaskToolSchemaInstance, TaskToolDetai
1369
1370
  taskDepth,
1370
1371
  modelOverride,
1371
1372
  parentActiveModelPattern,
1373
+ parentSessionId: this.session.getSessionId?.() ?? undefined,
1372
1374
  thinkingLevel: thinkingLevelOverride,
1373
1375
  outputSchema: effectiveOutputSchema,
1374
1376
  sessionFile: taskSessionFile,
package/src/thinking.ts CHANGED
@@ -1,5 +1,6 @@
1
- import { type ResolvedThinkingLevel, ThinkingLevel } from "@gajae-code/agent-core";
2
- import { clampThinkingLevelForModel, type Effort, type Model, THINKING_EFFORTS } from "@gajae-code/ai";
1
+ import { type ResolvedThinkingLevel, ThinkingLevel } from "@gajae-code/agent-core/thinking";
2
+ import { clampThinkingLevelForModel, type Effort, THINKING_EFFORTS } from "@gajae-code/ai/model-thinking";
3
+ import type { Model } from "@gajae-code/ai/types";
3
4
 
4
5
  /**
5
6
  * Metadata used to render thinking selector values in the coding-agent UI.
@@ -34,6 +35,11 @@ const THINKING_LEVEL_METADATA: Record<ThinkingLevel, ThinkingLevelMetadata> = {
34
35
  label: "xhigh",
35
36
  description: "Maximum reasoning (~32k tokens)",
36
37
  },
38
+ [ThinkingLevel.Max]: {
39
+ value: ThinkingLevel.Max,
40
+ label: "max",
41
+ description: "Opus maximum reasoning",
42
+ },
37
43
  };
38
44
 
39
45
  const THINKING_LEVELS = new Set<string>([ThinkingLevel.Inherit, ThinkingLevel.Off, ...THINKING_EFFORTS]);
package/src/tools/ask.ts CHANGED
@@ -34,6 +34,7 @@ import {
34
34
  renderDeepInterviewAskQuestion,
35
35
  } from "../deep-interview/render-middleware";
36
36
  import type { RenderResultOptions } from "../extensibility/custom-tools/types";
37
+ import { gateAnswerToResult, questionToGate } from "../modes/shared/agent-wire/deep-interview-gate";
37
38
  import { getMarkdownTheme, type Theme, theme } from "../modes/theme/theme";
38
39
  import askDescription from "../prompts/tools/ask.md" with { type: "text" };
39
40
  import { renderStatusLine } from "../tui";
@@ -425,7 +426,7 @@ export class AskTool implements AgentTool<typeof askSchema, AskToolDetails> {
425
426
  }
426
427
 
427
428
  static createIf(session: ToolSession): AskTool | null {
428
- return session.hasUI ? new AskTool(session) : null;
429
+ return session.hasUI || session.getWorkflowGateEmitter?.() ? new AskTool(session) : null;
429
430
  }
430
431
 
431
432
  /** Send terminal notification when ask tool is waiting for input */
@@ -442,17 +443,25 @@ export class AskTool implements AgentTool<typeof askSchema, AskToolDetails> {
442
443
  _onUpdate?: AgentToolUpdateCallback<AskToolDetails>,
443
444
  context?: AgentToolContext,
444
445
  ): Promise<AgentToolResult<AskToolDetails>> {
445
- // Headless fallback
446
- if (!context?.hasUI || !context.ui) {
446
+ const gateEmitter = this.session.getWorkflowGateEmitter?.();
447
+ const canUseWorkflowGate = gateEmitter?.isUnattended() === true;
448
+
449
+ // Headless fallback: unattended workflow gates are the non-TUI answer path.
450
+ if (!canUseWorkflowGate && (!context?.hasUI || !context.ui)) {
447
451
  context?.abort();
448
452
  throw new ToolAbortError("Ask tool requires interactive mode");
449
453
  }
450
454
 
451
- const extensionUi = context.ui;
455
+ const extensionUi = context?.ui;
452
456
  const ui: UIContext = {
453
- select: (prompt, options, dialogOptions) => extensionUi.select(prompt, options, dialogOptions),
454
- editor: (title, prefill, dialogOptions, editorOptions) =>
455
- extensionUi.editor(title, prefill, dialogOptions, editorOptions),
457
+ select: (prompt, options, dialogOptions) => {
458
+ if (!extensionUi) throw new ToolAbortError("Ask tool requires interactive mode");
459
+ return extensionUi.select(prompt, options, dialogOptions);
460
+ },
461
+ editor: (title, prefill, dialogOptions, editorOptions) => {
462
+ if (!extensionUi) throw new ToolAbortError("Ask tool requires interactive mode");
463
+ return extensionUi.editor(title, prefill, dialogOptions, editorOptions);
464
+ },
456
465
  };
457
466
 
458
467
  // Determine timeout based on settings and plan mode
@@ -477,6 +486,27 @@ export class AskTool implements AgentTool<typeof askSchema, AskToolDetails> {
477
486
  options?: { previous?: QuestionResult; navigation?: NavigationControls },
478
487
  ) => {
479
488
  const rawOptionLabels = q.options.map(o => o.label);
489
+ // Unattended (#316/#323/G011): route the question through the workflow-gate
490
+ // emitter instead of the interactive UI; the external agent answers over RPC.
491
+ if (gateEmitter && canUseWorkflowGate) {
492
+ const gateQuestion = {
493
+ id: q.id,
494
+ question: q.question,
495
+ options: q.options,
496
+ multi: q.multi,
497
+ recommended: q.recommended,
498
+ };
499
+ const answer = await gateEmitter.emitGate(questionToGate(gateQuestion));
500
+ const decoded = gateAnswerToResult(gateQuestion, answer);
501
+ return {
502
+ optionLabels: rawOptionLabels,
503
+ selectedOptions: decoded.selectedOptions,
504
+ customInput: decoded.customInput,
505
+ navigation: undefined as NavigationControls | undefined,
506
+ cancelled: false,
507
+ timedOut: false,
508
+ };
509
+ }
480
510
  try {
481
511
  const deepInterviewPrompt = formatDeepInterviewSelectorPrompt(q.question);
482
512
  const displayQuestion = deepInterviewPrompt ?? q.question;
@@ -529,7 +559,7 @@ export class AskTool implements AgentTool<typeof askSchema, AskToolDetails> {
529
559
  const { optionLabels, selectedOptions, customInput, cancelled, timedOut } = await askQuestion(q);
530
560
 
531
561
  if (!timedOut && (cancelled || (selectedOptions.length === 0 && customInput === undefined))) {
532
- context.abort();
562
+ context?.abort();
533
563
  throw new ToolAbortError("Ask tool was cancelled by the user");
534
564
  }
535
565
  const details: AskToolDetails = {
@@ -581,7 +611,7 @@ export class AskTool implements AgentTool<typeof askSchema, AskToolDetails> {
581
611
  } = await askQuestion(q, { previous, navigation });
582
612
 
583
613
  if (cancelled && !timedOut) {
584
- context.abort();
614
+ context?.abort();
585
615
  throw new ToolAbortError("Ask tool was cancelled by the user");
586
616
  }
587
617
 
@@ -17,8 +17,6 @@ export class HindsightRecallTool implements AgentTool<typeof hindsightRecallSche
17
17
  readonly description = recallDescription;
18
18
  readonly parameters = hindsightRecallSchema;
19
19
  readonly strict = true;
20
- readonly loadMode = "discoverable";
21
- readonly summary = "Search hindsight memory for relevant prior context";
22
20
 
23
21
  constructor(private readonly session: ToolSession) {}
24
22
 
@@ -18,8 +18,6 @@ export class HindsightReflectTool implements AgentTool<typeof hindsightReflectSc
18
18
  readonly description = reflectDescription;
19
19
  readonly parameters = hindsightReflectSchema;
20
20
  readonly strict = true;
21
- readonly loadMode = "discoverable";
22
- readonly summary = "Reflect on recent work and write hindsight memory";
23
21
 
24
22
  constructor(private readonly session: ToolSession) {}
25
23
 
@@ -22,8 +22,6 @@ export class HindsightRetainTool implements AgentTool<typeof hindsightRetainSche
22
22
  readonly description = retainDescription;
23
23
  readonly parameters = hindsightRetainSchema;
24
24
  readonly strict = true;
25
- readonly loadMode = "discoverable";
26
- readonly summary = "Store important facts in hindsight memory";
27
25
 
28
26
  constructor(private readonly session: ToolSession) {}
29
27
 
@@ -10,6 +10,7 @@ import type { GoalModeState, GoalRuntime } from "../goals";
10
10
  import { GoalTool } from "../goals/tools/goal-tool";
11
11
  import type { HindsightSessionState } from "../hindsight/state";
12
12
  import { LspTool } from "../lsp";
13
+ import type { WorkflowGateEmitter } from "../modes/shared/agent-wire/unattended-session";
13
14
  import type { PlanModeState } from "../plan-mode/state";
14
15
  import type { AgentRegistry } from "../registry/agent-registry";
15
16
  import type { ForkContextSeed, ForkContextSeedOptions } from "../session/agent-session";
@@ -36,9 +37,6 @@ import { DebugTool } from "./debug";
36
37
  import { EvalTool } from "./eval";
37
38
  import { FindTool } from "./find";
38
39
  import { GithubTool } from "./gh";
39
- import { HindsightRecallTool } from "./hindsight-recall";
40
- import { HindsightReflectTool } from "./hindsight-reflect";
41
- import { HindsightRetainTool } from "./hindsight-retain";
42
40
  import { InspectImageTool } from "./inspect-image";
43
41
  import { IrcTool } from "./irc";
44
42
  import { JobTool } from "./job";
@@ -76,9 +74,6 @@ export * from "./debug";
76
74
  export * from "./eval";
77
75
  export * from "./find";
78
76
  export * from "./gh";
79
- export * from "./hindsight-recall";
80
- export * from "./hindsight-reflect";
81
- export * from "./hindsight-retain";
82
77
  export * from "./image-gen";
83
78
  export * from "./inspect-image";
84
79
  export * from "./irc";
@@ -198,6 +193,8 @@ export interface ToolSession {
198
193
  getPlanModeState?: () => PlanModeState | undefined;
199
194
  /** Goal mode state (if active or paused) */
200
195
  getGoalModeState?: () => GoalModeState | undefined;
196
+ /** Unattended workflow-gate emitter (present only when unattended mode is negotiated). */
197
+ getWorkflowGateEmitter?: () => WorkflowGateEmitter | undefined;
201
198
  /** Goal runtime for the active agent session. */
202
199
  getGoalRuntime?: () => GoalRuntime | undefined;
203
200
  /** Bridge to the connected client (e.g. ACP editor host). Tools should route fs/terminal/permission requests through this when available. */
@@ -302,7 +299,10 @@ export function computeEssentialBuiltinNames(settings: Settings): string[] {
302
299
 
303
300
  /**
304
301
  * Public callable factory map. External callers may invoke `BUILTIN_TOOLS.read(session)` or
305
- * `BUILTIN_TOOLS[name](session)` to construct a tool directly.
302
+ * `BUILTIN_TOOLS[name](session)` to construct a public coding-harness tool directly.
303
+ *
304
+ * Hindsight memory helpers are intentionally excluded: memory is a private backend
305
+ * integration, not a public gajae-code tool surface.
306
306
  */
307
307
  export const BUILTIN_TOOLS: Record<string, ToolFactory> = {
308
308
  read: s => new ReadTool(s),
@@ -337,9 +337,6 @@ export const BUILTIN_TOOLS: Record<string, ToolFactory> = {
337
337
  web_search: s => new WebSearchTool(s),
338
338
  search_tool_bm25: SearchToolBm25Tool.createIf,
339
339
  write: s => new WriteTool(s),
340
- retain: HindsightRetainTool.createIf,
341
- recall: HindsightRecallTool.createIf,
342
- reflect: HindsightReflectTool.createIf,
343
340
  skill: SkillTool.createIf,
344
341
  goal: s => new GoalTool(s),
345
342
  };
@@ -460,11 +457,6 @@ export async function createTools(session: ToolSession, toolNames?: string[]): P
460
457
  ) {
461
458
  requestedTools.push("recipe");
462
459
  }
463
- if (session.settings.get("memory.backend") === "hindsight") {
464
- for (const name of ["recall", "retain", "reflect"]) {
465
- if (!requestedTools.includes(name)) requestedTools.push(name);
466
- }
467
- }
468
460
  }
469
461
  // Resolve effective tool discovery mode.
470
462
  // tools.discoveryMode takes precedence; mcp.discoveryMode is a back-compat alias for "mcp-only".
@@ -512,9 +504,6 @@ export async function createTools(session: ToolSession, toolNames?: string[]): P
512
504
  return true;
513
505
  }
514
506
  if (name === "recipe") return session.settings.get("recipe.enabled");
515
- if (name === "retain" || name === "recall" || name === "reflect") {
516
- return session.settings.get("memory.backend") === "hindsight";
517
- }
518
507
  if (name === "task") {
519
508
  const maxDepth = session.settings.get("task.maxRecursionDepth") ?? 2;
520
509
  const currentDepth = session.taskDepth ?? 0;
package/src/tools/read.ts CHANGED
@@ -1031,7 +1031,7 @@ export class ReadTool implements AgentTool<typeof readSchema, ReadToolDetails> {
1031
1031
  }
1032
1032
 
1033
1033
  const collectedLines = streamResult.lines;
1034
- if (!rawSelector && maxColumns > 0) {
1034
+ if (!rawSelector && !shouldAddHashLines && maxColumns > 0) {
1035
1035
  for (let i = 0; i < collectedLines.length; i++) {
1036
1036
  const { text, wasTruncated } = truncateLine(collectedLines[i], maxColumns);
1037
1037
  if (wasTruncated) {
@@ -1789,8 +1789,9 @@ export class ReadTool implements AgentTool<typeof readSchema, ReadToolDetails> {
1789
1789
  // counts in `truncation` keep reflecting the source, not the trimmed
1790
1790
  // view — column truncation surfaces separately via `.limits()`.
1791
1791
  const rawSelector = isRawSelector(parsed);
1792
+ const shouldAddHashLines = !rawSelector && displayMode.hashLines;
1792
1793
  const maxColumns = resolveOutputMaxColumns(this.session.settings);
1793
- if (!rawSelector && maxColumns > 0) {
1794
+ if (!rawSelector && !shouldAddHashLines && maxColumns > 0) {
1794
1795
  for (let i = 0; i < collectedLines.length; i++) {
1795
1796
  const { text, wasTruncated } = truncateLine(collectedLines[i], maxColumns);
1796
1797
  if (wasTruncated) {
@@ -1824,7 +1825,6 @@ export class ReadTool implements AgentTool<typeof readSchema, ReadToolDetails> {
1824
1825
  getFileReadCache(this.session).recordContiguous(absolutePath, startLineDisplay, collectedLines);
1825
1826
  }
1826
1827
 
1827
- const shouldAddHashLines = !rawSelector && displayMode.hashLines;
1828
1828
  const shouldAddLineNumbers = rawSelector ? false : shouldAddHashLines ? false : displayMode.lineNumbers;
1829
1829
  let capturedDisplayContent: { text: string; startLine: number } | undefined;
1830
1830
  const formatText = (text: string, startNum: number): string => {
@@ -18,6 +18,7 @@
18
18
  import type { AgentTool, AgentToolResult } from "@gajae-code/agent-core";
19
19
  import { prompt, untilAborted } from "@gajae-code/utils";
20
20
  import * as z from "zod/v4";
21
+ import { resolveSubskillActivationForSkillInvocation } from "../extensibility/gjc-plugins";
21
22
  import { buildSkillPromptMessage } from "../extensibility/skills";
22
23
  import { runNativeStateCommand } from "../gjc-runtime/state-runtime";
23
24
  import skillDescription from "../prompts/tools/skill.md" with { type: "text" };
@@ -125,7 +126,18 @@ export class SkillTool implements AgentTool<typeof skillSchema, SkillToolDetails
125
126
  }
126
127
 
127
128
  const args = (input.args ?? "").trim();
128
- const built = await buildSkillPromptMessage(skill, args);
129
+ const activationResult = await resolveSubskillActivationForSkillInvocation({
130
+ cwd: this.#session.cwd,
131
+ sessionId: this.#session.getSessionId?.() ?? activeState?.session_id?.trim() ?? undefined,
132
+ skillName: skill.name,
133
+ args,
134
+ });
135
+ const built = await buildSkillPromptMessage(skill, activationResult.cleanedArgs, {
136
+ subskillActivation: activationResult.activation,
137
+ subskillActivationSet: activationResult.activeSubskillsToPersist,
138
+ cwd: this.#session.cwd,
139
+ sessionId: this.#session.getSessionId?.() ?? activeState?.session_id?.trim() ?? undefined,
140
+ });
129
141
 
130
142
  await sendCustomMessage(
131
143
  {
@@ -141,7 +153,7 @@ export class SkillTool implements AgentTool<typeof skillSchema, SkillToolDetails
141
153
  const summary = JSON.stringify({
142
154
  callee: skill.name,
143
155
  path: skill.filePath,
144
- args: args || undefined,
156
+ args: activationResult.cleanedArgs || undefined,
145
157
  lineCount: built.details.lineCount,
146
158
  });
147
159
  return {
@@ -149,7 +161,7 @@ export class SkillTool implements AgentTool<typeof skillSchema, SkillToolDetails
149
161
  details: {
150
162
  name: skill.name,
151
163
  path: skill.filePath,
152
- args: args || undefined,
164
+ args: activationResult.cleanedArgs || undefined,
153
165
  lineCount: built.details.lineCount,
154
166
  },
155
167
  };
@@ -1,4 +1,4 @@
1
- import { $env } from "@gajae-code/utils";
1
+ import { $env } from "@gajae-code/utils/env";
2
2
 
3
3
  export type EditMode = "replace" | "patch" | "hashline" | "vim" | "apply_patch";
4
4