@gajae-code/coding-agent 0.3.2 → 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.
- package/CHANGELOG.md +32 -0
- package/dist/types/config/model-registry.d.ts +17 -10
- package/dist/types/config/models-config-schema.d.ts +37 -0
- package/dist/types/config/settings-schema.d.ts +5 -0
- package/dist/types/edit/diff.d.ts +16 -0
- package/dist/types/edit/modes/replace.d.ts +7 -0
- package/dist/types/extensibility/gjc-plugins/activation.d.ts +14 -0
- package/dist/types/extensibility/gjc-plugins/index.d.ts +9 -0
- package/dist/types/extensibility/gjc-plugins/injection.d.ts +31 -0
- package/dist/types/extensibility/gjc-plugins/loader.d.ts +3 -0
- package/dist/types/extensibility/gjc-plugins/paths.d.ts +8 -0
- package/dist/types/extensibility/gjc-plugins/schema.d.ts +3 -0
- package/dist/types/extensibility/gjc-plugins/state.d.ts +9 -0
- package/dist/types/extensibility/gjc-plugins/tools.d.ts +8 -0
- package/dist/types/extensibility/gjc-plugins/types.d.ts +64 -0
- package/dist/types/extensibility/gjc-plugins/validation.d.ts +4 -0
- package/dist/types/extensibility/skills.d.ts +9 -1
- package/dist/types/gjc-runtime/state-runtime.d.ts +22 -0
- package/dist/types/gjc-runtime/ultragoal-runtime.d.ts +1 -2
- package/dist/types/harness-control-plane/storage.d.ts +7 -0
- package/dist/types/lsp/client.d.ts +1 -0
- package/dist/types/modes/bridge/bridge-mode.d.ts +2 -0
- package/dist/types/modes/prompt-action-autocomplete.d.ts +2 -2
- package/dist/types/modes/rpc/rpc-client.d.ts +9 -1
- package/dist/types/modes/rpc/rpc-types.d.ts +179 -2
- package/dist/types/modes/shared/agent-wire/approval-gate.d.ts +57 -0
- package/dist/types/modes/shared/agent-wire/command-dispatch.d.ts +16 -1
- package/dist/types/modes/shared/agent-wire/deep-interview-gate.d.ts +47 -0
- package/dist/types/modes/shared/agent-wire/event-envelope.d.ts +7 -0
- package/dist/types/modes/shared/agent-wire/handshake.d.ts +11 -1
- package/dist/types/modes/shared/agent-wire/protocol.d.ts +3 -1
- package/dist/types/modes/shared/agent-wire/responses.d.ts +1 -1
- package/dist/types/modes/shared/agent-wire/unattended-action-policy.d.ts +27 -0
- package/dist/types/modes/shared/agent-wire/unattended-audit.d.ts +68 -0
- package/dist/types/modes/shared/agent-wire/unattended-run-controller.d.ts +161 -0
- package/dist/types/modes/shared/agent-wire/unattended-session.d.ts +61 -0
- package/dist/types/modes/shared/agent-wire/workflow-gate-broker.d.ts +114 -0
- package/dist/types/modes/shared/agent-wire/workflow-gate-schema.d.ts +39 -0
- package/dist/types/modes/theme/theme.d.ts +2 -1
- package/dist/types/runtime-mcp/transports/stdio.d.ts +0 -4
- package/dist/types/sdk.d.ts +7 -0
- package/dist/types/session/agent-session.d.ts +10 -0
- package/dist/types/session/blob-store.d.ts +17 -0
- package/dist/types/session/messages.d.ts +3 -0
- package/dist/types/session/session-storage.d.ts +6 -0
- package/dist/types/skill-state/active-state.d.ts +13 -0
- package/dist/types/thinking.d.ts +3 -2
- package/dist/types/tools/index.d.ts +3 -0
- package/package.json +9 -7
- package/src/cli.ts +14 -0
- package/src/commands/harness.ts +192 -7
- package/src/commands/ultragoal.ts +1 -21
- package/src/config/model-equivalence.ts +1 -1
- package/src/config/model-registry.ts +32 -5
- package/src/config/models-config-schema.ts +7 -2
- package/src/config/settings-schema.ts +4 -1
- package/src/discovery/claude-plugins.ts +25 -5
- package/src/edit/diff.ts +64 -1
- package/src/edit/modes/replace.ts +60 -2
- package/src/extensibility/gjc-plugins/activation.ts +87 -0
- package/src/extensibility/gjc-plugins/index.ts +9 -0
- package/src/extensibility/gjc-plugins/injection.ts +114 -0
- package/src/extensibility/gjc-plugins/loader.ts +131 -0
- package/src/extensibility/gjc-plugins/paths.ts +66 -0
- package/src/extensibility/gjc-plugins/schema.ts +79 -0
- package/src/extensibility/gjc-plugins/state.ts +29 -0
- package/src/extensibility/gjc-plugins/tools.ts +47 -0
- package/src/extensibility/gjc-plugins/types.ts +97 -0
- package/src/extensibility/gjc-plugins/validation.ts +76 -0
- package/src/extensibility/skills.ts +39 -7
- package/src/gjc-runtime/state-runtime.ts +93 -2
- package/src/gjc-runtime/state-writer.ts +17 -1
- package/src/gjc-runtime/ultragoal-runtime.ts +76 -121
- package/src/gjc-runtime/workflow-manifest.generated.json +5 -0
- package/src/gjc-runtime/workflow-manifest.ts +2 -2
- package/src/harness-control-plane/storage.ts +144 -2
- package/src/hashline/hash.ts +23 -0
- package/src/hooks/skill-state.ts +2 -0
- package/src/internal-urls/docs-index.generated.ts +5 -5
- package/src/lsp/client.ts +7 -0
- package/src/modes/acp/acp-agent.ts +25 -2
- package/src/modes/bridge/bridge-mode.ts +124 -2
- package/src/modes/controllers/input-controller.ts +14 -2
- package/src/modes/prompt-action-autocomplete.ts +49 -10
- package/src/modes/rpc/rpc-client.ts +57 -3
- package/src/modes/rpc/rpc-mode.ts +67 -0
- package/src/modes/rpc/rpc-types.ts +224 -2
- package/src/modes/shared/agent-wire/approval-gate.ts +151 -0
- package/src/modes/shared/agent-wire/command-dispatch.ts +97 -4
- package/src/modes/shared/agent-wire/command-validation.ts +25 -1
- package/src/modes/shared/agent-wire/deep-interview-gate.ts +222 -0
- package/src/modes/shared/agent-wire/event-envelope.ts +13 -0
- package/src/modes/shared/agent-wire/handshake.ts +43 -3
- package/src/modes/shared/agent-wire/protocol.ts +7 -0
- package/src/modes/shared/agent-wire/responses.ts +2 -2
- package/src/modes/shared/agent-wire/scopes.ts +2 -0
- package/src/modes/shared/agent-wire/unattended-action-policy.ts +341 -0
- package/src/modes/shared/agent-wire/unattended-audit.ts +175 -0
- package/src/modes/shared/agent-wire/unattended-run-controller.ts +406 -0
- package/src/modes/shared/agent-wire/unattended-session.ts +180 -0
- package/src/modes/shared/agent-wire/workflow-gate-broker.ts +324 -0
- package/src/modes/shared/agent-wire/workflow-gate-schema.ts +331 -0
- package/src/modes/theme/theme.ts +6 -0
- package/src/runtime-mcp/client.ts +7 -4
- package/src/runtime-mcp/manager.ts +45 -13
- package/src/runtime-mcp/transports/http.ts +40 -14
- package/src/runtime-mcp/transports/stdio.ts +11 -10
- package/src/sdk.ts +47 -0
- package/src/session/agent-session.ts +211 -2
- package/src/session/blob-store.ts +84 -0
- package/src/session/messages.ts +3 -0
- package/src/session/session-manager.ts +390 -33
- package/src/session/session-storage.ts +26 -0
- package/src/setup/provider-onboarding.ts +2 -2
- package/src/skill-state/active-state.ts +89 -1
- package/src/task/discovery.ts +7 -1
- package/src/task/executor.ts +16 -2
- package/src/thinking.ts +8 -2
- package/src/tools/ask.ts +39 -9
- package/src/tools/index.ts +3 -0
- package/src/tools/skill.ts +15 -3
- 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: ["
|
|
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: ["
|
|
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
|
-
|
|
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
|
}
|
package/src/task/discovery.ts
CHANGED
|
@@ -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
|
|
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
|
});
|
package/src/task/executor.ts
CHANGED
|
@@ -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";
|
|
@@ -1159,6 +1160,12 @@ export async function runSubprocess(options: ExecutorOptions): Promise<SingleRes
|
|
|
1159
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.`
|
|
1160
1161
|
: "";
|
|
1161
1162
|
|
|
1163
|
+
const agentSubskillBlock = await buildAgentSubskillInjection({
|
|
1164
|
+
cwd,
|
|
1165
|
+
sessionId: options.parentSessionId,
|
|
1166
|
+
agentName: agent.name,
|
|
1167
|
+
});
|
|
1168
|
+
|
|
1162
1169
|
const { session } = await awaitAbortable(
|
|
1163
1170
|
createAgentSession({
|
|
1164
1171
|
cwd: worktree ?? cwd,
|
|
@@ -1185,15 +1192,22 @@ export async function runSubprocess(options: ExecutorOptions): Promise<SingleRes
|
|
|
1185
1192
|
ircSelfId: ircEnabled ? id : "",
|
|
1186
1193
|
forkContext: forkContextNotice,
|
|
1187
1194
|
});
|
|
1195
|
+
const promptWithSubskills = `${subagentPrompt}${agentSubskillBlock}`;
|
|
1188
1196
|
return defaultPrompt.length === 0
|
|
1189
|
-
? [
|
|
1190
|
-
: [...defaultPrompt.slice(0, -1),
|
|
1197
|
+
? [promptWithSubskills]
|
|
1198
|
+
: [...defaultPrompt.slice(0, -1), promptWithSubskills, defaultPrompt[defaultPrompt.length - 1]];
|
|
1191
1199
|
},
|
|
1192
1200
|
sessionManager,
|
|
1193
1201
|
hasUI: false,
|
|
1194
1202
|
spawns: spawnsEnv,
|
|
1195
1203
|
taskDepth: childDepth,
|
|
1196
1204
|
currentAgentType: agent.name,
|
|
1205
|
+
gjcSubskillToolContext: {
|
|
1206
|
+
cwd,
|
|
1207
|
+
sessionId: options.parentSessionId,
|
|
1208
|
+
parent: agent.name,
|
|
1209
|
+
phase: "prompt",
|
|
1210
|
+
},
|
|
1197
1211
|
parentHindsightSessionState: options.parentHindsightSessionState,
|
|
1198
1212
|
parentTaskPrefix: id,
|
|
1199
1213
|
agentId: id,
|
package/src/thinking.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import { type ResolvedThinkingLevel, ThinkingLevel } from "@gajae-code/agent-core";
|
|
2
|
-
import { clampThinkingLevelForModel, type Effort,
|
|
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
|
-
|
|
446
|
-
|
|
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
|
|
455
|
+
const extensionUi = context?.ui;
|
|
452
456
|
const ui: UIContext = {
|
|
453
|
-
select: (prompt, options, dialogOptions) =>
|
|
454
|
-
|
|
455
|
-
extensionUi.
|
|
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
|
|
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
|
|
614
|
+
context?.abort();
|
|
585
615
|
throw new ToolAbortError("Ask tool was cancelled by the user");
|
|
586
616
|
}
|
|
587
617
|
|
package/src/tools/index.ts
CHANGED
|
@@ -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";
|
|
@@ -192,6 +193,8 @@ export interface ToolSession {
|
|
|
192
193
|
getPlanModeState?: () => PlanModeState | undefined;
|
|
193
194
|
/** Goal mode state (if active or paused) */
|
|
194
195
|
getGoalModeState?: () => GoalModeState | undefined;
|
|
196
|
+
/** Unattended workflow-gate emitter (present only when unattended mode is negotiated). */
|
|
197
|
+
getWorkflowGateEmitter?: () => WorkflowGateEmitter | undefined;
|
|
195
198
|
/** Goal runtime for the active agent session. */
|
|
196
199
|
getGoalRuntime?: () => GoalRuntime | undefined;
|
|
197
200
|
/** Bridge to the connected client (e.g. ACP editor host). Tools should route fs/terminal/permission requests through this when available. */
|
package/src/tools/skill.ts
CHANGED
|
@@ -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
|
|
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:
|
|
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:
|
|
164
|
+
args: activationResult.cleanedArgs || undefined,
|
|
153
165
|
lineCount: built.details.lineCount,
|
|
154
166
|
},
|
|
155
167
|
};
|
package/src/utils/edit-mode.ts
CHANGED