@gajae-code/coding-agent 0.4.3 → 0.4.5
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 +42 -0
- package/dist/types/async/job-manager.d.ts +19 -1
- package/dist/types/cli/fast-help.d.ts +1 -0
- package/dist/types/cli/setup-cli.d.ts +16 -1
- package/dist/types/commands/coordinator.d.ts +19 -0
- package/dist/types/commands/harness.d.ts +3 -0
- package/dist/types/commands/mcp-serve.d.ts +24 -0
- package/dist/types/commands/setup.d.ts +47 -0
- package/dist/types/config/model-registry.d.ts +3 -0
- package/dist/types/config/models-config-schema.d.ts +5 -0
- package/dist/types/coordinator/contract.d.ts +4 -0
- package/dist/types/coordinator-mcp/policy.d.ts +24 -0
- package/dist/types/coordinator-mcp/safety.d.ts +26 -0
- package/dist/types/coordinator-mcp/server.d.ts +58 -0
- package/dist/types/extensibility/extensions/types.d.ts +13 -0
- package/dist/types/gjc-runtime/session-state-sidecar.d.ts +13 -0
- package/dist/types/harness-control-plane/finalize.d.ts +5 -0
- package/dist/types/harness-control-plane/phase-rollup.d.ts +23 -0
- package/dist/types/harness-control-plane/receipt-ingest.d.ts +19 -0
- package/dist/types/harness-control-plane/receipts.d.ts +46 -0
- package/dist/types/harness-control-plane/rpc-adapter.d.ts +3 -0
- package/dist/types/harness-control-plane/types.d.ts +9 -1
- package/dist/types/main.d.ts +2 -2
- package/dist/types/modes/components/hook-selector.d.ts +11 -0
- package/dist/types/modes/utils/abort-message.d.ts +4 -0
- package/dist/types/session/session-manager.d.ts +8 -0
- package/dist/types/setup/hermes-setup.d.ts +78 -0
- package/dist/types/task/fork-context-advisory.d.ts +13 -0
- package/dist/types/task/receipt.d.ts +1 -0
- package/dist/types/task/render.d.ts +7 -1
- package/dist/types/task/roi-reconciliation.d.ts +27 -0
- package/dist/types/task/types.d.ts +10 -0
- package/dist/types/tools/subagent-render.d.ts +25 -0
- package/dist/types/tools/subagent.d.ts +5 -1
- package/package.json +8 -7
- package/scripts/build-binary.ts +4 -0
- package/src/async/job-manager.ts +43 -1
- package/src/cli/fast-help.ts +80 -0
- package/src/cli/setup-cli.ts +95 -2
- package/src/cli.ts +109 -16
- package/src/commands/coordinator.ts +113 -0
- package/src/commands/harness.ts +92 -9
- package/src/commands/mcp-serve.ts +63 -0
- package/src/commands/setup.ts +34 -1
- package/src/config/models-config-schema.ts +1 -0
- package/src/coordinator/contract.ts +21 -0
- package/src/coordinator-mcp/policy.ts +160 -0
- package/src/coordinator-mcp/safety.ts +80 -0
- package/src/coordinator-mcp/server.ts +1519 -0
- package/src/cursor.ts +30 -2
- package/src/extensibility/extensions/types.ts +13 -0
- package/src/gjc-runtime/launch-worktree.ts +12 -1
- package/src/gjc-runtime/session-state-sidecar.ts +117 -0
- package/src/harness-control-plane/finalize.ts +39 -5
- package/src/harness-control-plane/owner.ts +9 -1
- package/src/harness-control-plane/phase-rollup.ts +96 -0
- package/src/harness-control-plane/receipt-ingest.ts +127 -0
- package/src/harness-control-plane/receipts.ts +229 -1
- package/src/harness-control-plane/rpc-adapter.ts +8 -0
- package/src/harness-control-plane/types.ts +29 -1
- package/src/internal-urls/docs-index.generated.ts +6 -4
- package/src/main.ts +7 -3
- package/src/modes/components/hook-selector.ts +109 -5
- package/src/modes/components/status-line.ts +6 -6
- package/src/modes/controllers/event-controller.ts +5 -4
- package/src/modes/controllers/extension-ui-controller.ts +16 -1
- package/src/modes/interactive-mode.ts +4 -5
- package/src/modes/print-mode.ts +1 -1
- package/src/modes/theme/theme.ts +2 -2
- package/src/modes/utils/abort-message.ts +41 -0
- package/src/modes/utils/context-usage.ts +15 -8
- package/src/modes/utils/ui-helpers.ts +5 -6
- package/src/prompts/agents/architect.md +6 -0
- package/src/prompts/agents/critic.md +6 -0
- package/src/prompts/agents/planner.md +8 -1
- package/src/sdk.ts +9 -4
- package/src/session/agent-session.ts +22 -5
- package/src/session/session-manager.ts +20 -0
- package/src/setup/hermes/templates/operator-instructions.v1.md +30 -0
- package/src/setup/hermes-setup.ts +484 -0
- package/src/task/fork-context-advisory.ts +99 -0
- package/src/task/index.ts +33 -2
- package/src/task/receipt.ts +2 -0
- package/src/task/render.ts +14 -0
- package/src/task/roi-reconciliation.ts +90 -0
- package/src/task/types.ts +7 -0
- package/src/tools/ask.ts +30 -10
- package/src/tools/index.ts +2 -2
- package/src/tools/renderers.ts +2 -0
- package/src/tools/subagent-render.ts +169 -0
- package/src/tools/subagent.ts +49 -7
- package/src/utils/title-generator.ts +16 -2
|
@@ -22,6 +22,17 @@ export interface HookSelectorOptions {
|
|
|
22
22
|
*/
|
|
23
23
|
wrapFocused?: boolean;
|
|
24
24
|
scrollTitleRows?: number;
|
|
25
|
+
/**
|
|
26
|
+
* Inline free-text entry for the option with this label (e.g. the ask
|
|
27
|
+
* tool's "Other (type your own)"). Selecting it keeps the title and option
|
|
28
|
+
* list on screen and opens a prompt-style editor below the list instead of
|
|
29
|
+
* replacing the whole selector. Enter submits via `onSubmit`; Escape
|
|
30
|
+
* returns to option selection.
|
|
31
|
+
*/
|
|
32
|
+
customInput?: {
|
|
33
|
+
optionLabel: string;
|
|
34
|
+
onSubmit: (text: string) => void;
|
|
35
|
+
};
|
|
25
36
|
}
|
|
26
37
|
export declare class HookSelectorComponent extends Container {
|
|
27
38
|
#private;
|
|
@@ -401,6 +401,14 @@ export declare class SessionManager {
|
|
|
401
401
|
appendCompaction<T = unknown>(summary: string, shortSummary: string | undefined, firstKeptEntryId: string, tokensBefore: number, details?: T, fromExtension?: boolean, preserveData?: Record<string, unknown>): string;
|
|
402
402
|
/** Append a custom entry (for extensions) as child of current leaf, then advance leaf. Returns entry id. */
|
|
403
403
|
appendCustomEntry(customType: string, data?: unknown): string;
|
|
404
|
+
/**
|
|
405
|
+
* Write mutated message entries back into the canonical entry store by id.
|
|
406
|
+
*
|
|
407
|
+
* `getBranch()` materializes resident-blob entries into copies, so in-place
|
|
408
|
+
* mutation of returned entries (e.g. pruning tool outputs) does not affect
|
|
409
|
+
* the canonical store. This applies such mutations for real.
|
|
410
|
+
*/
|
|
411
|
+
applyEntryMessageUpdates(entries: readonly SessionMessageEntry[]): void;
|
|
404
412
|
/**
|
|
405
413
|
* Rewrite the session file after in-place entry updates.
|
|
406
414
|
* Use sparingly (e.g., pruning old tool outputs).
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { COORDINATOR_MCP_PROTOCOL_VERSION, COORDINATOR_MCP_SERVER_NAME } from "../coordinator/contract";
|
|
2
|
+
export type HermesMutationClass = "sessions" | "questions" | "reports";
|
|
3
|
+
export type HermesSetupMode = "render" | "install" | "check" | "smoke";
|
|
4
|
+
export interface HermesSetupFlags {
|
|
5
|
+
json?: boolean;
|
|
6
|
+
check?: boolean;
|
|
7
|
+
smoke?: boolean;
|
|
8
|
+
install?: boolean;
|
|
9
|
+
force?: boolean;
|
|
10
|
+
root?: string[];
|
|
11
|
+
repo?: string;
|
|
12
|
+
profile?: string;
|
|
13
|
+
sessionCommand?: string;
|
|
14
|
+
noWorktree?: boolean;
|
|
15
|
+
worktreeName?: string;
|
|
16
|
+
stateRoot?: string;
|
|
17
|
+
mutation?: string[];
|
|
18
|
+
artifactByteCap?: string;
|
|
19
|
+
serverKey?: string;
|
|
20
|
+
gjcCommand?: string;
|
|
21
|
+
target?: string;
|
|
22
|
+
profileDir?: string;
|
|
23
|
+
}
|
|
24
|
+
export interface CoordinatorSetupSpec {
|
|
25
|
+
schemaVersion: 1;
|
|
26
|
+
coordinator: "hermes";
|
|
27
|
+
serverKey: string;
|
|
28
|
+
serverName: typeof COORDINATOR_MCP_SERVER_NAME;
|
|
29
|
+
protocolVersion: typeof COORDINATOR_MCP_PROTOCOL_VERSION;
|
|
30
|
+
gjcCommand: string;
|
|
31
|
+
args: ["mcp-serve", "coordinator"];
|
|
32
|
+
roots: string[];
|
|
33
|
+
namespace: {
|
|
34
|
+
profile?: string;
|
|
35
|
+
repo?: string;
|
|
36
|
+
};
|
|
37
|
+
sessionCommand?: string;
|
|
38
|
+
sessionCommandSource: "default" | "explicit";
|
|
39
|
+
worktree: {
|
|
40
|
+
enabled: boolean;
|
|
41
|
+
name?: string;
|
|
42
|
+
};
|
|
43
|
+
stateRoot?: string;
|
|
44
|
+
mutationPolicy: {
|
|
45
|
+
classes: HermesMutationClass[];
|
|
46
|
+
perCallConsentRequired: true;
|
|
47
|
+
};
|
|
48
|
+
artifactByteCap?: number;
|
|
49
|
+
installTarget?: {
|
|
50
|
+
kind: "profile-dir" | "config-file";
|
|
51
|
+
path: string;
|
|
52
|
+
};
|
|
53
|
+
operatorTemplateVersion: 1;
|
|
54
|
+
contractDocVersion: 1;
|
|
55
|
+
}
|
|
56
|
+
export interface HermesSetupResult {
|
|
57
|
+
ok: boolean;
|
|
58
|
+
mode: HermesSetupMode;
|
|
59
|
+
files_written: string[];
|
|
60
|
+
previews: Array<{
|
|
61
|
+
path: string;
|
|
62
|
+
content: string;
|
|
63
|
+
}>;
|
|
64
|
+
warnings: string[];
|
|
65
|
+
smoke: null | {
|
|
66
|
+
ok: boolean;
|
|
67
|
+
protocolVersion: string;
|
|
68
|
+
serverName: string;
|
|
69
|
+
requiredTools: string[];
|
|
70
|
+
missingTools: string[];
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
export declare function buildHermesSetupSpec(flags: HermesSetupFlags): CoordinatorSetupSpec;
|
|
74
|
+
export declare function computeHermesSetupSignature(spec: CoordinatorSetupSpec): string;
|
|
75
|
+
export declare function renderHermesServerBlock(spec: CoordinatorSetupSpec): Record<string, unknown>;
|
|
76
|
+
export declare function runHermesSetup(flags: HermesSetupFlags): Promise<HermesSetupResult>;
|
|
77
|
+
export declare function formatHermesSetupResult(result: HermesSetupResult): string;
|
|
78
|
+
export declare function hermesSetupExitCode(error: unknown): number;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { ForkContextMode } from "./types";
|
|
2
|
+
export interface ForkContextAdvisory {
|
|
3
|
+
recommendedMode: ForkContextMode;
|
|
4
|
+
reasons: string[];
|
|
5
|
+
estimatedClonedTokens: Record<ForkContextMode, number>;
|
|
6
|
+
callerModeRespected: true;
|
|
7
|
+
}
|
|
8
|
+
export declare function adviseForkContextMode(input: {
|
|
9
|
+
assignment: string;
|
|
10
|
+
context?: string;
|
|
11
|
+
explicitMode?: ForkContextMode;
|
|
12
|
+
parentContextTokens?: number;
|
|
13
|
+
}): ForkContextAdvisory;
|
|
@@ -1,11 +1,17 @@
|
|
|
1
1
|
import type { Component } from "@gajae-code/tui";
|
|
2
2
|
import type { RenderResultOptions } from "../extensibility/custom-tools/types";
|
|
3
3
|
import type { Theme } from "../modes/theme/theme";
|
|
4
|
-
import type { TaskParams, TaskToolDetails } from "./types";
|
|
4
|
+
import type { AgentProgress, TaskParams, TaskToolDetails } from "./types";
|
|
5
5
|
/**
|
|
6
6
|
* Render the tool call arguments.
|
|
7
7
|
*/
|
|
8
8
|
export declare function renderCall(args: TaskParams, _options: RenderResultOptions, theme: Theme): Component;
|
|
9
|
+
/**
|
|
10
|
+
* Public wrapper to render a single subagent's live `AgentProgress` for the
|
|
11
|
+
* `subagent` await panel. Reuses the internal task-progress renderer so the
|
|
12
|
+
* await panel stays at parity with the inline task panel.
|
|
13
|
+
*/
|
|
14
|
+
export declare function renderSubagentLiveProgress(progress: AgentProgress, expanded: boolean, theme: Theme, spinnerFrame?: number): string[];
|
|
9
15
|
/**
|
|
10
16
|
* Render the tool result.
|
|
11
17
|
*/
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type { TaskResultReceipt } from "./receipt";
|
|
2
|
+
import type { SpawnPlanReceipt } from "./spawn-gate";
|
|
3
|
+
/**
|
|
4
|
+
* Pure, advisory-only reconciliation between a spawn plan's inline-token promise
|
|
5
|
+
* and receipt-safe child outputs. These signals never change task success/failure
|
|
6
|
+
* semantics or runtime behavior; they only describe budget/ROI observations for
|
|
7
|
+
* model-facing summaries.
|
|
8
|
+
*/
|
|
9
|
+
export interface SpawnRoiChildReconciliation {
|
|
10
|
+
id: string;
|
|
11
|
+
inlineTokens: number;
|
|
12
|
+
maxInlineTokens: number;
|
|
13
|
+
overBudget: boolean;
|
|
14
|
+
overageTokens: number;
|
|
15
|
+
lowRoi: boolean;
|
|
16
|
+
}
|
|
17
|
+
export interface SpawnRoiReconciliation {
|
|
18
|
+
childCount: number;
|
|
19
|
+
promisedMaxInlineTokens: number;
|
|
20
|
+
children: SpawnRoiChildReconciliation[];
|
|
21
|
+
overBudgetChildIds: string[];
|
|
22
|
+
lowRoiChildIds: string[];
|
|
23
|
+
totalInlineTokens: number;
|
|
24
|
+
totalOverageTokens: number;
|
|
25
|
+
advisoryFlags: string[];
|
|
26
|
+
}
|
|
27
|
+
export declare function reconcileSpawnRoi(plan: SpawnPlanReceipt | undefined, receipts: readonly TaskResultReceipt[]): SpawnRoiReconciliation | undefined;
|
|
@@ -2,6 +2,7 @@ import type { ThinkingLevel } from "@gajae-code/agent-core";
|
|
|
2
2
|
import type { Usage } from "@gajae-code/ai";
|
|
3
3
|
import * as z from "zod/v4";
|
|
4
4
|
import type { TaskResultReceipt } from "./receipt";
|
|
5
|
+
import type { SpawnRoiReconciliation } from "./roi-reconciliation";
|
|
5
6
|
import { type TaskSimpleMode } from "./simple-mode";
|
|
6
7
|
import type { SpawnPlanReceipt } from "./spawn-gate";
|
|
7
8
|
import type { NestedRepoPatch } from "./worktree";
|
|
@@ -413,6 +414,14 @@ export interface SingleResult {
|
|
|
413
414
|
mode: ForkContextMode;
|
|
414
415
|
clonedTokens: number;
|
|
415
416
|
};
|
|
417
|
+
/**
|
|
418
|
+
* Advisory fork-context mode recommendation for this task (logged only;
|
|
419
|
+
* never changes the actual mode selection).
|
|
420
|
+
*/
|
|
421
|
+
forkContextAdvisory?: {
|
|
422
|
+
recommendedMode: ForkContextMode;
|
|
423
|
+
reasons: string[];
|
|
424
|
+
};
|
|
416
425
|
}
|
|
417
426
|
/** Tool details for TUI rendering */
|
|
418
427
|
export interface TaskToolDetails {
|
|
@@ -431,6 +440,7 @@ export interface TaskToolDetails {
|
|
|
431
440
|
/** Advisory ids for terminal children that spent tokens without detectable output/review/changes. */
|
|
432
441
|
lowRoiChildIds: string[];
|
|
433
442
|
};
|
|
443
|
+
roiReconciliation?: SpawnRoiReconciliation;
|
|
434
444
|
progress?: AgentProgress[];
|
|
435
445
|
async?: {
|
|
436
446
|
state: "running" | "paused" | "queued" | "completed" | "failed";
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* TUI renderer for the `subagent` tool.
|
|
3
|
+
*
|
|
4
|
+
* The await panel surfaces each awaited subagent's live streaming status at
|
|
5
|
+
* parity with the inline `task` panel by reusing `renderSubagentLiveProgress`.
|
|
6
|
+
* Falls back to a `running, no activity yet` placeholder when a live producer
|
|
7
|
+
* exists but has not emitted yet, and to a static status line when no live
|
|
8
|
+
* producer is available (resumed-from-disk or backward-compat records).
|
|
9
|
+
*/
|
|
10
|
+
import type { Component } from "@gajae-code/tui";
|
|
11
|
+
import type { RenderResultOptions } from "../extensibility/custom-tools/types";
|
|
12
|
+
import type { Theme } from "../modes/theme/theme";
|
|
13
|
+
import type { SubagentToolDetails } from "./subagent";
|
|
14
|
+
export declare const subagentToolRenderer: {
|
|
15
|
+
inline: boolean;
|
|
16
|
+
renderCall(_args: unknown, _options: RenderResultOptions, theme: Theme): Component;
|
|
17
|
+
renderResult(result: {
|
|
18
|
+
content: Array<{
|
|
19
|
+
type: string;
|
|
20
|
+
text?: string;
|
|
21
|
+
}>;
|
|
22
|
+
details?: SubagentToolDetails;
|
|
23
|
+
}, options: RenderResultOptions, theme: Theme): Component;
|
|
24
|
+
mergeCallAndResult: boolean;
|
|
25
|
+
};
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { AgentTool, AgentToolContext, AgentToolResult, AgentToolUpdateCallback } from "@gajae-code/agent-core";
|
|
2
2
|
import * as z from "zod/v4";
|
|
3
|
-
import type { AgentSource } from "../task/types";
|
|
3
|
+
import type { AgentProgress, AgentSource } from "../task/types";
|
|
4
4
|
import type { ToolSession } from "./index";
|
|
5
5
|
declare const subagentSchema: z.ZodObject<{
|
|
6
6
|
action: z.ZodEnum<{
|
|
@@ -42,6 +42,10 @@ export interface SubagentSnapshot {
|
|
|
42
42
|
outputRef?: string;
|
|
43
43
|
truncated?: boolean;
|
|
44
44
|
guidance?: string;
|
|
45
|
+
/** Live streaming progress for the awaited subagent (await panel only; UI detail). */
|
|
46
|
+
progress?: AgentProgress;
|
|
47
|
+
/** True when a live in-session progress producer exists for this subagent. */
|
|
48
|
+
liveProgressAvailable?: boolean;
|
|
45
49
|
}
|
|
46
50
|
export interface SubagentToolDetails {
|
|
47
51
|
subagents: SubagentSnapshot[];
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"type": "module",
|
|
3
3
|
"name": "@gajae-code/coding-agent",
|
|
4
|
-
"version": "0.4.
|
|
4
|
+
"version": "0.4.5",
|
|
5
5
|
"description": "Gajae Code CLI with read, bash, edit, write tools and session management",
|
|
6
6
|
"homepage": "https://gaebal-gajae.dev",
|
|
7
7
|
"author": "Yeachan-Heo",
|
|
@@ -36,6 +36,7 @@
|
|
|
36
36
|
"check:types": "tsgo -p tsconfig.json --noEmit",
|
|
37
37
|
"lint": "biome lint .",
|
|
38
38
|
"test": "bun test",
|
|
39
|
+
"bench:context": "bun run bench/context-optimization.bench.ts",
|
|
39
40
|
"generate-schemas": "bun ../../scripts/generate-json-schemas.ts",
|
|
40
41
|
"check:schemas": "bun ../../scripts/generate-json-schemas.ts --check",
|
|
41
42
|
"fix": "biome check --write --unsafe . && bun run format-prompts && bun run generate-docs-index",
|
|
@@ -50,12 +51,12 @@
|
|
|
50
51
|
"@agentclientprotocol/sdk": "0.21.0",
|
|
51
52
|
"@babel/parser": "^7.29.3",
|
|
52
53
|
"@mozilla/readability": "^0.6.0",
|
|
53
|
-
"@gajae-code/stats": "0.4.
|
|
54
|
-
"@gajae-code/agent-core": "0.4.
|
|
55
|
-
"@gajae-code/ai": "0.4.
|
|
56
|
-
"@gajae-code/natives": "0.4.
|
|
57
|
-
"@gajae-code/tui": "0.4.
|
|
58
|
-
"@gajae-code/utils": "0.4.
|
|
54
|
+
"@gajae-code/stats": "0.4.5",
|
|
55
|
+
"@gajae-code/agent-core": "0.4.5",
|
|
56
|
+
"@gajae-code/ai": "0.4.5",
|
|
57
|
+
"@gajae-code/natives": "0.4.5",
|
|
58
|
+
"@gajae-code/tui": "0.4.5",
|
|
59
|
+
"@gajae-code/utils": "0.4.5",
|
|
59
60
|
"@puppeteer/browsers": "^2.13.0",
|
|
60
61
|
"@types/turndown": "5.0.6",
|
|
61
62
|
"@xterm/headless": "^6.0.0",
|
package/scripts/build-binary.ts
CHANGED
|
@@ -39,6 +39,10 @@ async function main(): Promise<void> {
|
|
|
39
39
|
"bun",
|
|
40
40
|
"build",
|
|
41
41
|
"--compile",
|
|
42
|
+
// Minify shrinks the bundled JS the compiled binary must parse at
|
|
43
|
+
// startup (302MB → ~114MB --help RSS measured on darwin-arm64).
|
|
44
|
+
// --keep-names below preserves identifiers for error reports.
|
|
45
|
+
"--minify",
|
|
42
46
|
"--no-compile-autoload-bunfig",
|
|
43
47
|
"--no-compile-autoload-dotenv",
|
|
44
48
|
"--no-compile-autoload-tsconfig",
|
package/src/async/job-manager.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { logger } from "@gajae-code/utils";
|
|
2
|
-
import type { AgentSource } from "../task/types";
|
|
2
|
+
import type { AgentProgress, AgentSource } from "../task/types";
|
|
3
3
|
|
|
4
4
|
const DELIVERY_RETRY_BASE_MS = 500;
|
|
5
5
|
const DELIVERY_RETRY_MAX_MS = 30_000;
|
|
@@ -248,6 +248,7 @@ export class AsyncJobManager {
|
|
|
248
248
|
#disposed = false;
|
|
249
249
|
readonly #subagentRecords = new Map<string, SubagentRecord>();
|
|
250
250
|
readonly #liveHandles = new Map<string, SubagentLiveHandle>();
|
|
251
|
+
readonly #subagentProgress = new Map<string, AgentProgress>();
|
|
251
252
|
readonly #resumeQueue: ResumeQueueEntry[] = [];
|
|
252
253
|
#resumeSeq = 0;
|
|
253
254
|
#resumeRunner?: (subagentId: string, message?: string, descriptor?: ResumeDescriptor) => string | undefined;
|
|
@@ -531,6 +532,38 @@ export class AsyncJobManager {
|
|
|
531
532
|
this.#liveHandles.delete(subagentId);
|
|
532
533
|
}
|
|
533
534
|
|
|
535
|
+
/**
|
|
536
|
+
* Retain the latest live `AgentProgress` for a subagent (deep-cloned so later
|
|
537
|
+
* mutation of the live object cannot corrupt retained state). Read by the
|
|
538
|
+
* `subagent` await panel; cleared on terminal/cancel/purge/dispose.
|
|
539
|
+
*
|
|
540
|
+
* Ignored for ids without a canonical `SubagentRecord` (e.g. foreground/inline
|
|
541
|
+
* task runs that share the executor path) so the map only holds detached
|
|
542
|
+
* subagent progress and never accumulates untracked foreground task state.
|
|
543
|
+
*/
|
|
544
|
+
recordSubagentProgress(subagentId: string, progress: AgentProgress): void {
|
|
545
|
+
if (!this.#subagentRecords.has(subagentId)) return;
|
|
546
|
+
this.#subagentProgress.set(subagentId, structuredClone(progress));
|
|
547
|
+
}
|
|
548
|
+
|
|
549
|
+
getSubagentProgress(subagentId: string): AgentProgress | undefined {
|
|
550
|
+
return this.#subagentProgress.get(subagentId);
|
|
551
|
+
}
|
|
552
|
+
|
|
553
|
+
/**
|
|
554
|
+
* True only when a live, in-session progress producer exists for this id: a
|
|
555
|
+
* canonical registered record with a live handle or an in-memory running job.
|
|
556
|
+
* False for `SubagentTool` backward-compat job synthesis and resumed-from-disk
|
|
557
|
+
* records, which have no live producer to stream from.
|
|
558
|
+
*/
|
|
559
|
+
hasLiveSubagent(subagentId: string, filter?: AsyncJobFilter): boolean {
|
|
560
|
+
const rec = this.getSubagentRecord(subagentId, filter);
|
|
561
|
+
if (!rec) return false;
|
|
562
|
+
if (this.#liveHandles.has(rec.subagentId)) return true;
|
|
563
|
+
const job = rec.currentJobId ? this.#jobs.get(rec.currentJobId) : undefined;
|
|
564
|
+
return job?.status === "running";
|
|
565
|
+
}
|
|
566
|
+
|
|
534
567
|
/** Install the TaskTool-owned resume runner. Returns the new job id, or undefined on failure. */
|
|
535
568
|
setResumeRunner(
|
|
536
569
|
runner: (subagentId: string, message?: string, descriptor?: ResumeDescriptor) => string | undefined,
|
|
@@ -561,6 +594,7 @@ export class AsyncJobManager {
|
|
|
561
594
|
if (rec) {
|
|
562
595
|
rec.status = "paused";
|
|
563
596
|
this.#liveHandles.delete(rec.subagentId);
|
|
597
|
+
this.#subagentProgress.delete(rec.subagentId);
|
|
564
598
|
}
|
|
565
599
|
}
|
|
566
600
|
|
|
@@ -569,6 +603,7 @@ export class AsyncJobManager {
|
|
|
569
603
|
if (!rec) return;
|
|
570
604
|
rec.status = status;
|
|
571
605
|
this.#liveHandles.delete(rec.subagentId);
|
|
606
|
+
this.#subagentProgress.delete(rec.subagentId);
|
|
572
607
|
}
|
|
573
608
|
|
|
574
609
|
/** Request a graceful safe-boundary pause of a running subagent. */
|
|
@@ -626,6 +661,9 @@ export class AsyncJobManager {
|
|
|
626
661
|
message?: string,
|
|
627
662
|
): { ok: boolean; status?: SubagentLifecycle; jobId?: string; reason?: string } {
|
|
628
663
|
const prevJobId = rec.currentJobId;
|
|
664
|
+
// Clear any retained progress from the previous run so a resumed subagent
|
|
665
|
+
// never renders the prior run's tool/output as live before it emits again.
|
|
666
|
+
this.#subagentProgress.delete(rec.subagentId);
|
|
629
667
|
const newJobId = this.#resumeRunner?.(rec.subagentId, message, this.#resumeDescriptors.get(rec.subagentId));
|
|
630
668
|
if (!newJobId) return { ok: false, reason: "resume_failed" };
|
|
631
669
|
if (prevJobId && prevJobId !== newJobId) rec.historicalJobIds.push(prevJobId);
|
|
@@ -663,6 +701,7 @@ export class AsyncJobManager {
|
|
|
663
701
|
}
|
|
664
702
|
rec.status = "cancelled";
|
|
665
703
|
this.#liveHandles.delete(rec.subagentId);
|
|
704
|
+
this.#subagentProgress.delete(rec.subagentId);
|
|
666
705
|
this.#drainResumeQueue();
|
|
667
706
|
return true;
|
|
668
707
|
}
|
|
@@ -671,6 +710,7 @@ export class AsyncJobManager {
|
|
|
671
710
|
if (idx !== -1) this.#resumeQueue.splice(idx, 1);
|
|
672
711
|
rec.status = "cancelled";
|
|
673
712
|
rec.queued = undefined;
|
|
713
|
+
this.#subagentProgress.delete(rec.subagentId);
|
|
674
714
|
return true;
|
|
675
715
|
}
|
|
676
716
|
return false;
|
|
@@ -685,6 +725,7 @@ export class AsyncJobManager {
|
|
|
685
725
|
this.#liveHandles.delete(sid);
|
|
686
726
|
this.#resumeDescriptors.delete(sid);
|
|
687
727
|
this.#subagentRecords.delete(sid);
|
|
728
|
+
this.#subagentProgress.delete(sid);
|
|
688
729
|
}
|
|
689
730
|
}
|
|
690
731
|
}
|
|
@@ -1021,6 +1062,7 @@ export class AsyncJobManager {
|
|
|
1021
1062
|
this.#ownerCleanups.clear();
|
|
1022
1063
|
this.#subagentRecords.clear();
|
|
1023
1064
|
this.#liveHandles.clear();
|
|
1065
|
+
this.#subagentProgress.clear();
|
|
1024
1066
|
this.#resumeDescriptors.clear();
|
|
1025
1067
|
this.#resumeQueue.length = 0;
|
|
1026
1068
|
this.#notifyChange();
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import { APP_NAME, CONFIG_DIR_NAME } from "@gajae-code/utils/dirs";
|
|
2
|
+
|
|
3
|
+
export function getExtraHelpText(): string {
|
|
4
|
+
return `Environment Variables:
|
|
5
|
+
# Core Providers
|
|
6
|
+
ANTHROPIC_API_KEY - Anthropic Claude models
|
|
7
|
+
ANTHROPIC_OAUTH_TOKEN - Anthropic OAuth (takes precedence over API key)
|
|
8
|
+
CLAUDE_CODE_USE_FOUNDRY - Enable Anthropic Foundry mode (uses Foundry endpoint + mTLS)
|
|
9
|
+
FOUNDRY_BASE_URL - Anthropic Foundry base URL (e.g., https://<foundry-host>)
|
|
10
|
+
ANTHROPIC_FOUNDRY_API_KEY - Anthropic token used as Authorization: Bearer <token> in Foundry mode
|
|
11
|
+
ANTHROPIC_CUSTOM_HEADERS - Extra Foundry headers (e.g., "user-id: USERNAME")
|
|
12
|
+
CLAUDE_CODE_CLIENT_CERT - Client certificate (PEM path or inline PEM) for mTLS
|
|
13
|
+
CLAUDE_CODE_CLIENT_KEY - Client private key (PEM path or inline PEM) for mTLS
|
|
14
|
+
NODE_EXTRA_CA_CERTS - CA bundle path (or inline PEM) for server certificate validation
|
|
15
|
+
OPENAI_API_KEY - OpenAI GPT models
|
|
16
|
+
GEMINI_API_KEY - Google Gemini models
|
|
17
|
+
GITHUB_TOKEN - GitHub Copilot (or GH_TOKEN, COPILOT_GITHUB_TOKEN)
|
|
18
|
+
|
|
19
|
+
# Additional LLM Providers
|
|
20
|
+
AZURE_OPENAI_API_KEY - Azure OpenAI models
|
|
21
|
+
GROQ_API_KEY - Groq models
|
|
22
|
+
CEREBRAS_API_KEY - Cerebras models
|
|
23
|
+
XAI_API_KEY - xAI Grok models
|
|
24
|
+
OPENROUTER_API_KEY - OpenRouter aggregated models
|
|
25
|
+
KILO_API_KEY - Kilo Gateway models
|
|
26
|
+
MISTRAL_API_KEY - Mistral models
|
|
27
|
+
ZAI_API_KEY - z.ai models (ZhipuAI/GLM)
|
|
28
|
+
MINIMAX_API_KEY - MiniMax models
|
|
29
|
+
OPENCODE_API_KEY - OpenCode Zen/OpenCode Go models
|
|
30
|
+
CURSOR_ACCESS_TOKEN - Cursor AI models
|
|
31
|
+
AI_GATEWAY_API_KEY - Vercel AI Gateway
|
|
32
|
+
|
|
33
|
+
# Cloud Providers
|
|
34
|
+
AWS_PROFILE - AWS Bedrock (or AWS_ACCESS_KEY_ID + AWS_SECRET_ACCESS_KEY)
|
|
35
|
+
GOOGLE_CLOUD_PROJECT - Google Vertex AI (requires GOOGLE_CLOUD_LOCATION)
|
|
36
|
+
GOOGLE_APPLICATION_CREDENTIALS - Service account for Vertex AI
|
|
37
|
+
|
|
38
|
+
# Search & Tools
|
|
39
|
+
EXA_API_KEY - Exa web search
|
|
40
|
+
BRAVE_API_KEY - Brave web search
|
|
41
|
+
PERPLEXITY_API_KEY - Perplexity web search (API)
|
|
42
|
+
PERPLEXITY_COOKIES - Perplexity web search (session cookie)
|
|
43
|
+
TAVILY_API_KEY - Tavily web search
|
|
44
|
+
ANTHROPIC_SEARCH_API_KEY - Anthropic search provider
|
|
45
|
+
|
|
46
|
+
# Configuration
|
|
47
|
+
GJC_CODING_AGENT_DIR - Session storage directory (default: ~/${CONFIG_DIR_NAME}/agent)
|
|
48
|
+
GJC_PACKAGE_DIR - Override package directory (for Nix/Guix store paths)
|
|
49
|
+
GJC_SMOL_MODEL - Override smol/fast model (see --smol)
|
|
50
|
+
GJC_SLOW_MODEL - Override slow/reasoning model (see --slow)
|
|
51
|
+
GJC_PLAN_MODEL - Override planning model (see --plan)
|
|
52
|
+
GJC_NO_PTY - Disable PTY-based interactive bash execution
|
|
53
|
+
--tmux - Launch interactive startup inside a new tmux session
|
|
54
|
+
gjc session - List, inspect, create, remove, or attach tagged GJC-managed tmux sessions
|
|
55
|
+
GJC_LAUNCH_POLICY - Launch policy for --tmux startup: tmux or direct
|
|
56
|
+
GJC_TMUX_SESSION - Explicit tmux session name override for --tmux startup
|
|
57
|
+
|
|
58
|
+
For complete environment variable reference, see:
|
|
59
|
+
docs/environment-variables.md
|
|
60
|
+
Available Tools (default-enabled unless noted):
|
|
61
|
+
read - Read file contents
|
|
62
|
+
bash - Execute bash commands
|
|
63
|
+
edit - Edit files with find/replace
|
|
64
|
+
write - Write files (creates/overwrites)
|
|
65
|
+
grep - Search file contents
|
|
66
|
+
find - Find files by glob pattern
|
|
67
|
+
lsp - Language server protocol (code intelligence)
|
|
68
|
+
python - Execute Python code (requires: ${APP_NAME} setup python)
|
|
69
|
+
notebook - Edit Jupyter notebooks
|
|
70
|
+
inspect_image - Analyze images with a vision model
|
|
71
|
+
browser - Browser automation (Puppeteer)
|
|
72
|
+
task - Launch sub-agents for parallel tasks
|
|
73
|
+
todo_write - Manage todo/task lists
|
|
74
|
+
web_search - Search the web
|
|
75
|
+
ask - Ask user questions (interactive mode only)
|
|
76
|
+
|
|
77
|
+
Useful Commands:
|
|
78
|
+
${APP_NAME} --list-models - List configured provider models
|
|
79
|
+
${APP_NAME} --help - Show this help`;
|
|
80
|
+
}
|
package/src/cli/setup-cli.ts
CHANGED
|
@@ -14,6 +14,12 @@ import {
|
|
|
14
14
|
readGjcManagedCodexHooksStatus,
|
|
15
15
|
} from "../hooks/codex-native-hooks-config";
|
|
16
16
|
import { theme } from "../modes/theme/theme";
|
|
17
|
+
import {
|
|
18
|
+
formatHermesSetupResult,
|
|
19
|
+
type HermesSetupFlags,
|
|
20
|
+
hermesSetupExitCode,
|
|
21
|
+
runHermesSetup,
|
|
22
|
+
} from "../setup/hermes-setup";
|
|
17
23
|
import {
|
|
18
24
|
addApiCompatibleProvider,
|
|
19
25
|
formatProviderPresetList,
|
|
@@ -21,7 +27,7 @@ import {
|
|
|
21
27
|
parseProviderCompatibility,
|
|
22
28
|
} from "../setup/provider-onboarding";
|
|
23
29
|
|
|
24
|
-
export type SetupComponent = "defaults" | "hooks" | "provider" | "python" | "stt";
|
|
30
|
+
export type SetupComponent = "defaults" | "hermes" | "hooks" | "provider" | "python" | "stt";
|
|
25
31
|
|
|
26
32
|
export interface SetupCommandArgs {
|
|
27
33
|
component: SetupComponent;
|
|
@@ -36,10 +42,25 @@ export interface SetupCommandArgs {
|
|
|
36
42
|
apiKeyEnv?: string;
|
|
37
43
|
model?: string[];
|
|
38
44
|
modelsPath?: string;
|
|
45
|
+
smoke?: boolean;
|
|
46
|
+
install?: boolean;
|
|
47
|
+
root?: string[];
|
|
48
|
+
repo?: string;
|
|
49
|
+
profile?: string;
|
|
50
|
+
sessionCommand?: string;
|
|
51
|
+
noWorktree?: boolean;
|
|
52
|
+
worktreeName?: string;
|
|
53
|
+
stateRoot?: string;
|
|
54
|
+
mutation?: string[];
|
|
55
|
+
artifactByteCap?: string;
|
|
56
|
+
serverKey?: string;
|
|
57
|
+
gjcCommand?: string;
|
|
58
|
+
target?: string;
|
|
59
|
+
profileDir?: string;
|
|
39
60
|
};
|
|
40
61
|
}
|
|
41
62
|
|
|
42
|
-
const VALID_COMPONENTS: SetupComponent[] = ["defaults", "hooks", "provider", "python", "stt"];
|
|
63
|
+
const VALID_COMPONENTS: SetupComponent[] = ["defaults", "hermes", "hooks", "provider", "python", "stt"];
|
|
43
64
|
|
|
44
65
|
function hasProviderSetupFlags(flags: SetupCommandArgs["flags"]): boolean {
|
|
45
66
|
return (
|
|
@@ -88,6 +109,36 @@ export function parseSetupArgs(args: string[]): SetupCommandArgs | undefined {
|
|
|
88
109
|
flags.check = true;
|
|
89
110
|
} else if (arg === "--force" || arg === "-f") {
|
|
90
111
|
flags.force = true;
|
|
112
|
+
} else if (arg === "--smoke") {
|
|
113
|
+
flags.smoke = true;
|
|
114
|
+
} else if (arg === "--install") {
|
|
115
|
+
flags.install = true;
|
|
116
|
+
} else if (arg === "--root") {
|
|
117
|
+
flags.root = [...(flags.root ?? []), args[++i] ?? ""];
|
|
118
|
+
} else if (arg === "--repo") {
|
|
119
|
+
flags.repo = args[++i];
|
|
120
|
+
} else if (arg === "--profile") {
|
|
121
|
+
flags.profile = args[++i];
|
|
122
|
+
} else if (arg === "--session-command") {
|
|
123
|
+
flags.sessionCommand = args[++i];
|
|
124
|
+
} else if (arg === "--no-worktree") {
|
|
125
|
+
flags.noWorktree = true;
|
|
126
|
+
} else if (arg === "--worktree-name") {
|
|
127
|
+
flags.worktreeName = args[++i];
|
|
128
|
+
} else if (arg === "--state-root") {
|
|
129
|
+
flags.stateRoot = args[++i];
|
|
130
|
+
} else if (arg === "--mutation") {
|
|
131
|
+
flags.mutation = [...(flags.mutation ?? []), args[++i] ?? ""];
|
|
132
|
+
} else if (arg === "--artifact-byte-cap") {
|
|
133
|
+
flags.artifactByteCap = args[++i];
|
|
134
|
+
} else if (arg === "--server-key") {
|
|
135
|
+
flags.serverKey = args[++i];
|
|
136
|
+
} else if (arg === "--gjc-command") {
|
|
137
|
+
flags.gjcCommand = args[++i];
|
|
138
|
+
} else if (arg === "--target") {
|
|
139
|
+
flags.target = args[++i];
|
|
140
|
+
} else if (arg === "--profile-dir") {
|
|
141
|
+
flags.profileDir = args[++i];
|
|
91
142
|
} else if (arg === "--compat") {
|
|
92
143
|
flags.compat = args[++i];
|
|
93
144
|
} else if (arg === "--preset") {
|
|
@@ -177,6 +228,9 @@ export async function runSetupCommand(cmd: SetupCommandArgs): Promise<void> {
|
|
|
177
228
|
case "defaults":
|
|
178
229
|
await handleDefaultsSetup(cmd.flags);
|
|
179
230
|
break;
|
|
231
|
+
case "hermes":
|
|
232
|
+
await handleHermesSetup(cmd.flags);
|
|
233
|
+
break;
|
|
180
234
|
case "hooks":
|
|
181
235
|
await handleHooksSetup(cmd.flags);
|
|
182
236
|
break;
|
|
@@ -192,6 +246,26 @@ export async function runSetupCommand(cmd: SetupCommandArgs): Promise<void> {
|
|
|
192
246
|
}
|
|
193
247
|
}
|
|
194
248
|
|
|
249
|
+
async function handleHermesSetup(flags: HermesSetupFlags): Promise<void> {
|
|
250
|
+
try {
|
|
251
|
+
const result = await runHermesSetup(flags);
|
|
252
|
+
if (flags.json) {
|
|
253
|
+
process.stdout.write(`${JSON.stringify(result, null, 2)}\n`);
|
|
254
|
+
return;
|
|
255
|
+
}
|
|
256
|
+
process.stdout.write(`${chalk.green(`${theme.status.success} Hermes MCP setup ready`)}\n`);
|
|
257
|
+
process.stdout.write(`${chalk.dim(formatHermesSetupResult(result))}\n`);
|
|
258
|
+
} catch (error) {
|
|
259
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
260
|
+
if (flags.json) {
|
|
261
|
+
process.stdout.write(`${JSON.stringify({ ok: false, error: message }, null, 2)}\n`);
|
|
262
|
+
} else {
|
|
263
|
+
process.stderr.write(`${chalk.red(`${theme.status.error} Hermes MCP setup failed`)}\n`);
|
|
264
|
+
process.stderr.write(`${chalk.dim(message)}\n`);
|
|
265
|
+
}
|
|
266
|
+
process.exit(hermesSetupExitCode(error));
|
|
267
|
+
}
|
|
268
|
+
}
|
|
195
269
|
async function handleProviderSetup(flags: {
|
|
196
270
|
json?: boolean;
|
|
197
271
|
force?: boolean;
|
|
@@ -410,6 +484,7 @@ ${chalk.bold("Usage:")}
|
|
|
410
484
|
|
|
411
485
|
${chalk.bold("Components:")}
|
|
412
486
|
defaults Install bundled GJC default workflow skills (default)
|
|
487
|
+
hermes Optional: render/install a Hermes MCP bridge setup package
|
|
413
488
|
hooks Optional: install GJC native Codex UserPromptSubmit/Stop skill-state hooks
|
|
414
489
|
provider Optional: add a preset, OpenAI-compatible, or Anthropic-compatible API provider
|
|
415
490
|
python Optional: verify a Python 3 interpreter is reachable for code execution
|
|
@@ -421,6 +496,12 @@ ${chalk.bold("Provider example:")}
|
|
|
421
496
|
${APP_NAME} setup provider --preset glm
|
|
422
497
|
MY_PROVIDER_KEY=sk-... ${APP_NAME} setup provider --compat openai --provider my-oai --base-url https://api.example.com/v1 --api-key-env MY_PROVIDER_KEY --model gpt-example
|
|
423
498
|
|
|
499
|
+
${chalk.bold("Hermes example:")}
|
|
500
|
+
${APP_NAME} setup hermes --root /path/to/repo
|
|
501
|
+
${APP_NAME} setup hermes --root /path/to/repo --profile my-bot --repo gajae-code --profile-dir /path/to/hermes/profile --install
|
|
502
|
+
${APP_NAME} setup hermes --root /path/to/repo --worktree-name hermes-gajae-code
|
|
503
|
+
${APP_NAME} setup hermes --root /path/to/repo --session-command "gjc --worktree hermes-custom --model <provider/model>"
|
|
504
|
+
|
|
424
505
|
${chalk.bold("Options:")}
|
|
425
506
|
-c, --check Check if dependencies are installed without installing
|
|
426
507
|
-f, --force Overwrite existing default workflow skill files
|
|
@@ -432,6 +513,17 @@ ${chalk.bold("Options:")}
|
|
|
432
513
|
--api-key-env Read provider API key from this environment variable
|
|
433
514
|
--model, --models Model id to add (repeat or comma-separate)
|
|
434
515
|
--models-path Override models config path
|
|
516
|
+
--smoke Run Hermes MCP setup smoke checks
|
|
517
|
+
--install Install generated Hermes setup files
|
|
518
|
+
--root Allowed Hermes MCP workdir/artifact root (repeatable)
|
|
519
|
+
--profile Hermes MCP profile namespace
|
|
520
|
+
--repo Hermes MCP repo namespace
|
|
521
|
+
--session-command Explicit GJC session command; disables generated worktree flags
|
|
522
|
+
--no-worktree Disable default GJC --worktree isolation for Hermes sessions
|
|
523
|
+
--worktree-name Named GJC --worktree branch for Hermes sessions
|
|
524
|
+
--mutation Hermes MCP mutation classes: sessions,questions,reports,all
|
|
525
|
+
--target Hermes config file target for config-only install
|
|
526
|
+
--profile-dir Hermes profile directory for full setup install
|
|
435
527
|
|
|
436
528
|
${chalk.bold("Examples:")}
|
|
437
529
|
${APP_NAME} setup Install bundled GJC default workflow skills
|
|
@@ -439,6 +531,7 @@ ${chalk.bold("Examples:")}
|
|
|
439
531
|
${APP_NAME} setup defaults --check Check bundled GJC default workflow skills are installed
|
|
440
532
|
${APP_NAME} setup hooks Install native Codex skill-state hooks
|
|
441
533
|
${APP_NAME} setup hooks --check Check native Codex skill-state hooks
|
|
534
|
+
${APP_NAME} setup hermes --root /path/to/repo Render a model-agnostic Hermes MCP setup preview
|
|
442
535
|
${APP_NAME} setup python Install Python execution dependencies
|
|
443
536
|
${APP_NAME} setup stt Install speech-to-text dependencies
|
|
444
537
|
${APP_NAME} setup stt --check Check if STT dependencies are available
|