@gajae-code/coding-agent 0.4.3 → 0.4.4
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 +2 -0
- package/dist/types/async/job-manager.d.ts +19 -1
- package/dist/types/cli/setup-cli.d.ts +14 -1
- package/dist/types/commands/coordinator.d.ts +19 -0
- package/dist/types/commands/mcp-serve.d.ts +24 -0
- package/dist/types/commands/setup.d.ts +41 -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 +52 -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/modes/components/hook-selector.d.ts +11 -0
- package/dist/types/setup/hermes-setup.d.ts +71 -0
- package/dist/types/task/render.d.ts +7 -1
- package/dist/types/tools/subagent-render.d.ts +25 -0
- package/dist/types/tools/subagent.d.ts +5 -1
- package/package.json +7 -7
- package/src/async/job-manager.ts +43 -1
- package/src/cli/setup-cli.ts +86 -2
- package/src/cli.ts +2 -0
- package/src/commands/coordinator.ts +70 -0
- package/src/commands/mcp-serve.ts +62 -0
- package/src/commands/setup.ts +30 -1
- package/src/coordinator/contract.ts +20 -0
- package/src/coordinator-mcp/policy.ts +160 -0
- package/src/coordinator-mcp/safety.ts +80 -0
- package/src/coordinator-mcp/server.ts +1316 -0
- package/src/extensibility/extensions/types.ts +13 -0
- package/src/gjc-runtime/session-state-sidecar.ts +79 -0
- package/src/internal-urls/docs-index.generated.ts +3 -2
- package/src/modes/components/hook-selector.ts +109 -5
- package/src/modes/controllers/extension-ui-controller.ts +16 -1
- 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/session/agent-session.ts +6 -0
- package/src/setup/hermes/templates/operator-instructions.v1.md +29 -0
- package/src/setup/hermes-setup.ts +429 -0
- package/src/task/index.ts +2 -0
- package/src/task/render.ts +14 -0
- package/src/tools/ask.ts +30 -10
- package/src/tools/renderers.ts +2 -0
- package/src/tools/subagent-render.ts +160 -0
- package/src/tools/subagent.ts +49 -7
package/CHANGELOG.md
CHANGED
|
@@ -17,9 +17,11 @@
|
|
|
17
17
|
### Changed
|
|
18
18
|
|
|
19
19
|
- Added conservative `timeout-minutes` values to all CI workflow jobs to prevent indefinite hangs.
|
|
20
|
+
- Made coordinator MCP turn waiting state-backed by durable turn/session files, with runtime session sidecar updates for running/completed/error states and Meeseeks guidance that avoids fixed sleep/capture-pane loops.
|
|
20
21
|
|
|
21
22
|
### Fixed
|
|
22
23
|
|
|
24
|
+
- Failed stale coordinator turns quickly when their recorded tmux session is gone, clearing active-turn state instead of burning await timeouts.
|
|
23
25
|
- Improved the grep limit-reached message to show the current limit value and suggest using `--limit` for more results.
|
|
24
26
|
- Passed the active model's `maxTokens` (reserved completion budget) into the auto-compaction threshold and context-usage reserve so prompt packing reserves output for large-window models, keeping the safe input budget below the total context window (e.g. ~272K for a 400K/128K model) instead of filling the whole window ([#442](https://github.com/Yeachan-Heo/gajae-code/issues/442)).
|
|
25
27
|
- Fixed a `gjc harness` recovery deadlock where a session created by `start` without `--detach` (persisted as `started` with no owner lease/endpoint) could never get a live owner: `recover` refused to spawn one because no prior endpoint existed, while `start` reported `session-already-exists`. `recover` now bootstraps a fresh owner for a never-started session (no lease, no endpoint, no owner-run evidence) without writing a misleading `vanish` receipt, reported via `bootstrappedOwner: true`. Bootstrap is independent of the vanish classifier's `ownerRequired` verdict (nothing has vanished), so a session started in a non-git workspace (git delta `unknown`) is recovered too, while a deleted worktree is still refused (#421).
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { AgentSource } from "../task/types";
|
|
1
|
+
import type { AgentProgress, AgentSource } from "../task/types";
|
|
2
2
|
export interface AsyncJob {
|
|
3
3
|
id: string;
|
|
4
4
|
type: "bash" | "task";
|
|
@@ -189,6 +189,24 @@ export declare class AsyncJobManager {
|
|
|
189
189
|
registerLiveHandle(subagentId: string, handle: SubagentLiveHandle): void;
|
|
190
190
|
getLiveHandle(subagentId: string): SubagentLiveHandle | undefined;
|
|
191
191
|
removeLiveHandle(subagentId: string): void;
|
|
192
|
+
/**
|
|
193
|
+
* Retain the latest live `AgentProgress` for a subagent (deep-cloned so later
|
|
194
|
+
* mutation of the live object cannot corrupt retained state). Read by the
|
|
195
|
+
* `subagent` await panel; cleared on terminal/cancel/purge/dispose.
|
|
196
|
+
*
|
|
197
|
+
* Ignored for ids without a canonical `SubagentRecord` (e.g. foreground/inline
|
|
198
|
+
* task runs that share the executor path) so the map only holds detached
|
|
199
|
+
* subagent progress and never accumulates untracked foreground task state.
|
|
200
|
+
*/
|
|
201
|
+
recordSubagentProgress(subagentId: string, progress: AgentProgress): void;
|
|
202
|
+
getSubagentProgress(subagentId: string): AgentProgress | undefined;
|
|
203
|
+
/**
|
|
204
|
+
* True only when a live, in-session progress producer exists for this id: a
|
|
205
|
+
* canonical registered record with a live handle or an in-memory running job.
|
|
206
|
+
* False for `SubagentTool` backward-compat job synthesis and resumed-from-disk
|
|
207
|
+
* records, which have no live producer to stream from.
|
|
208
|
+
*/
|
|
209
|
+
hasLiveSubagent(subagentId: string, filter?: AsyncJobFilter): boolean;
|
|
192
210
|
/** Install the TaskTool-owned resume runner. Returns the new job id, or undefined on failure. */
|
|
193
211
|
setResumeRunner(runner: (subagentId: string, message?: string, descriptor?: ResumeDescriptor) => string | undefined): void;
|
|
194
212
|
registerResumeDescriptor(descriptor: ResumeDescriptor): void;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export type SetupComponent = "defaults" | "hooks" | "provider" | "python" | "stt";
|
|
1
|
+
export type SetupComponent = "defaults" | "hermes" | "hooks" | "provider" | "python" | "stt";
|
|
2
2
|
export interface SetupCommandArgs {
|
|
3
3
|
component: SetupComponent;
|
|
4
4
|
flags: {
|
|
@@ -12,6 +12,19 @@ export interface SetupCommandArgs {
|
|
|
12
12
|
apiKeyEnv?: string;
|
|
13
13
|
model?: string[];
|
|
14
14
|
modelsPath?: string;
|
|
15
|
+
smoke?: boolean;
|
|
16
|
+
install?: boolean;
|
|
17
|
+
root?: string[];
|
|
18
|
+
repo?: string;
|
|
19
|
+
profile?: string;
|
|
20
|
+
sessionCommand?: string;
|
|
21
|
+
stateRoot?: string;
|
|
22
|
+
mutation?: string[];
|
|
23
|
+
artifactByteCap?: string;
|
|
24
|
+
serverKey?: string;
|
|
25
|
+
gjcCommand?: string;
|
|
26
|
+
target?: string;
|
|
27
|
+
profileDir?: string;
|
|
15
28
|
};
|
|
16
29
|
}
|
|
17
30
|
/**
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { Command } from "@gajae-code/utils/cli";
|
|
2
|
+
export default class Coordinator extends Command {
|
|
3
|
+
static description: string;
|
|
4
|
+
static strict: boolean;
|
|
5
|
+
static args: {
|
|
6
|
+
action: import("@gajae-code/utils/cli").ArgDescriptor & {
|
|
7
|
+
description: string;
|
|
8
|
+
required: false;
|
|
9
|
+
};
|
|
10
|
+
};
|
|
11
|
+
static flags: {
|
|
12
|
+
json: import("@gajae-code/utils/cli").FlagDescriptor<"boolean"> & {
|
|
13
|
+
char: string;
|
|
14
|
+
description: string;
|
|
15
|
+
default: boolean;
|
|
16
|
+
};
|
|
17
|
+
};
|
|
18
|
+
run(): Promise<void>;
|
|
19
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { Command } from "@gajae-code/utils/cli";
|
|
2
|
+
export declare function validateMcpServeSubcommandForTest(server: string | undefined): void;
|
|
3
|
+
export default class McpServe extends Command {
|
|
4
|
+
static description: string;
|
|
5
|
+
static strict: boolean;
|
|
6
|
+
static args: {
|
|
7
|
+
server: import("@gajae-code/utils/cli").ArgDescriptor & {
|
|
8
|
+
description: string;
|
|
9
|
+
required: false;
|
|
10
|
+
};
|
|
11
|
+
};
|
|
12
|
+
static flags: {
|
|
13
|
+
json: import("@gajae-code/utils/cli").FlagDescriptor<"boolean"> & {
|
|
14
|
+
char: string;
|
|
15
|
+
description: string;
|
|
16
|
+
default: boolean;
|
|
17
|
+
};
|
|
18
|
+
check: import("@gajae-code/utils/cli").FlagDescriptor<"boolean"> & {
|
|
19
|
+
description: string;
|
|
20
|
+
default: boolean;
|
|
21
|
+
};
|
|
22
|
+
};
|
|
23
|
+
run(): Promise<void>;
|
|
24
|
+
}
|
|
@@ -24,6 +24,47 @@ export default class Setup extends Command {
|
|
|
24
24
|
json: import("@gajae-code/utils/cli").FlagDescriptor<"boolean"> & {
|
|
25
25
|
description: string;
|
|
26
26
|
};
|
|
27
|
+
smoke: import("@gajae-code/utils/cli").FlagDescriptor<"boolean"> & {
|
|
28
|
+
description: string;
|
|
29
|
+
};
|
|
30
|
+
install: import("@gajae-code/utils/cli").FlagDescriptor<"boolean"> & {
|
|
31
|
+
description: string;
|
|
32
|
+
};
|
|
33
|
+
root: import("@gajae-code/utils/cli").FlagDescriptor<"string"> & {
|
|
34
|
+
description: string;
|
|
35
|
+
multiple: true;
|
|
36
|
+
};
|
|
37
|
+
repo: import("@gajae-code/utils/cli").FlagDescriptor<"string"> & {
|
|
38
|
+
description: string;
|
|
39
|
+
};
|
|
40
|
+
profile: import("@gajae-code/utils/cli").FlagDescriptor<"string"> & {
|
|
41
|
+
description: string;
|
|
42
|
+
};
|
|
43
|
+
"session-command": import("@gajae-code/utils/cli").FlagDescriptor<"string"> & {
|
|
44
|
+
description: string;
|
|
45
|
+
};
|
|
46
|
+
"state-root": import("@gajae-code/utils/cli").FlagDescriptor<"string"> & {
|
|
47
|
+
description: string;
|
|
48
|
+
};
|
|
49
|
+
mutation: import("@gajae-code/utils/cli").FlagDescriptor<"string"> & {
|
|
50
|
+
description: string;
|
|
51
|
+
multiple: true;
|
|
52
|
+
};
|
|
53
|
+
"artifact-byte-cap": import("@gajae-code/utils/cli").FlagDescriptor<"string"> & {
|
|
54
|
+
description: string;
|
|
55
|
+
};
|
|
56
|
+
"server-key": import("@gajae-code/utils/cli").FlagDescriptor<"string"> & {
|
|
57
|
+
description: string;
|
|
58
|
+
};
|
|
59
|
+
"gjc-command": import("@gajae-code/utils/cli").FlagDescriptor<"string"> & {
|
|
60
|
+
description: string;
|
|
61
|
+
};
|
|
62
|
+
target: import("@gajae-code/utils/cli").FlagDescriptor<"string"> & {
|
|
63
|
+
description: string;
|
|
64
|
+
};
|
|
65
|
+
"profile-dir": import("@gajae-code/utils/cli").FlagDescriptor<"string"> & {
|
|
66
|
+
description: string;
|
|
67
|
+
};
|
|
27
68
|
preset: import("@gajae-code/utils/cli").FlagDescriptor<"string"> & {
|
|
28
69
|
description: string;
|
|
29
70
|
};
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export declare const COORDINATOR_MCP_PROTOCOL_VERSION = "2024-11-05";
|
|
2
|
+
export declare const COORDINATOR_MCP_SERVER_NAME = "gjc-coordinator-mcp";
|
|
3
|
+
export declare const COORDINATOR_MCP_TOOL_NAMES: readonly ["gjc_coordinator_list_sessions", "gjc_coordinator_read_status", "gjc_coordinator_read_tail", "gjc_coordinator_list_questions", "gjc_coordinator_list_artifacts", "gjc_coordinator_read_artifact", "gjc_coordinator_read_coordination_status", "gjc_coordinator_start_session", "gjc_coordinator_send_prompt", "gjc_coordinator_submit_question_answer", "gjc_coordinator_read_turn", "gjc_coordinator_await_turn", "gjc_coordinator_report_status"];
|
|
4
|
+
export type CoordinatorToolName = (typeof COORDINATOR_MCP_TOOL_NAMES)[number];
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
export type CoordinatorMutationClass = "sessions" | "questions" | "reports";
|
|
2
|
+
export interface CoordinatorNamespace {
|
|
3
|
+
profile: string | null;
|
|
4
|
+
repo: string | null;
|
|
5
|
+
}
|
|
6
|
+
export interface CoordinatorMcpConfig {
|
|
7
|
+
allowedRoots: string[];
|
|
8
|
+
mutationClasses: Set<CoordinatorMutationClass>;
|
|
9
|
+
artifactByteCap: number;
|
|
10
|
+
namespace: CoordinatorNamespace;
|
|
11
|
+
stateRoot: string;
|
|
12
|
+
sessionCommand: string | null;
|
|
13
|
+
}
|
|
14
|
+
export interface CoordinatorMutationRequest {
|
|
15
|
+
allow_mutation?: boolean;
|
|
16
|
+
}
|
|
17
|
+
export declare function buildCoordinatorMcpConfig(env?: NodeJS.ProcessEnv): CoordinatorMcpConfig;
|
|
18
|
+
export declare function assertCoordinatorWorkdir(config: CoordinatorMcpConfig, cwd: unknown): Promise<string>;
|
|
19
|
+
export declare function assertCoordinatorArtifactPath(config: CoordinatorMcpConfig, artifactPath: unknown): Promise<{
|
|
20
|
+
path: string;
|
|
21
|
+
byteCap: number;
|
|
22
|
+
}>;
|
|
23
|
+
export declare function requireCoordinatorMutation(config: CoordinatorMcpConfig, mutationClass: CoordinatorMutationClass, request: CoordinatorMutationRequest): void;
|
|
24
|
+
export declare function coordinatorNamespacePath(config: CoordinatorMcpConfig): string;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { type CoordinatorMutationClass } from "./policy";
|
|
2
|
+
export declare const COORDINATOR_MUTATION_CLASSES: readonly ["sessions", "questions", "reports"];
|
|
3
|
+
export type { CoordinatorMutationClass };
|
|
4
|
+
export interface CoordinatorSafetyConfig {
|
|
5
|
+
allowedRoots: string[];
|
|
6
|
+
artifactMaxBytes: number;
|
|
7
|
+
enabledMutationClasses: Set<CoordinatorMutationClass>;
|
|
8
|
+
repo?: string;
|
|
9
|
+
profile?: string;
|
|
10
|
+
}
|
|
11
|
+
export interface CoordinatorSafetyPolicy {
|
|
12
|
+
config: CoordinatorSafetyConfig;
|
|
13
|
+
resolveWorkdir(input: unknown): Promise<string>;
|
|
14
|
+
resolveArtifactPath(input: unknown): Promise<string>;
|
|
15
|
+
assertMutationAllowed(mutationClass: CoordinatorMutationClass, args: Record<string, unknown>): {
|
|
16
|
+
ok: true;
|
|
17
|
+
} | CoordinatorFailure;
|
|
18
|
+
}
|
|
19
|
+
export interface CoordinatorFailure {
|
|
20
|
+
ok: false;
|
|
21
|
+
reason: string;
|
|
22
|
+
[key: string]: unknown;
|
|
23
|
+
}
|
|
24
|
+
export declare function createCoordinatorSafetyPolicy(options?: {
|
|
25
|
+
env?: NodeJS.ProcessEnv;
|
|
26
|
+
}): Promise<CoordinatorSafetyPolicy>;
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { COORDINATOR_MCP_PROTOCOL_VERSION, COORDINATOR_MCP_SERVER_NAME, COORDINATOR_MCP_TOOL_NAMES, type CoordinatorToolName } from "../coordinator/contract";
|
|
2
|
+
import { type CoordinatorMcpConfig } from "./policy";
|
|
3
|
+
export type { CoordinatorToolName };
|
|
4
|
+
export { COORDINATOR_MCP_PROTOCOL_VERSION, COORDINATOR_MCP_SERVER_NAME, COORDINATOR_MCP_TOOL_NAMES };
|
|
5
|
+
interface JsonRpcRequest {
|
|
6
|
+
jsonrpc: "2.0";
|
|
7
|
+
id?: string | number | null;
|
|
8
|
+
method: string;
|
|
9
|
+
params?: unknown;
|
|
10
|
+
}
|
|
11
|
+
interface JsonRpcResponse {
|
|
12
|
+
jsonrpc: "2.0";
|
|
13
|
+
id: string | number | null;
|
|
14
|
+
result?: any;
|
|
15
|
+
error?: {
|
|
16
|
+
code: number;
|
|
17
|
+
message: string;
|
|
18
|
+
data?: unknown;
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
interface SessionStartInput {
|
|
22
|
+
cwd: string;
|
|
23
|
+
prompt?: string;
|
|
24
|
+
namespace: {
|
|
25
|
+
profile: string | null;
|
|
26
|
+
repo: string | null;
|
|
27
|
+
};
|
|
28
|
+
worktree: true;
|
|
29
|
+
}
|
|
30
|
+
interface CoordinatorServices {
|
|
31
|
+
listSessions?: () => unknown[] | Promise<unknown[]>;
|
|
32
|
+
startSession?: (input: SessionStartInput) => unknown | Promise<unknown>;
|
|
33
|
+
}
|
|
34
|
+
interface CoordinatorMcpServerOptions {
|
|
35
|
+
env?: NodeJS.ProcessEnv;
|
|
36
|
+
services?: CoordinatorServices;
|
|
37
|
+
}
|
|
38
|
+
interface LegacyHandlerOptions {
|
|
39
|
+
env?: NodeJS.ProcessEnv;
|
|
40
|
+
createSession?: () => unknown;
|
|
41
|
+
}
|
|
42
|
+
export declare function readCoordinatorArtifact(config: CoordinatorMcpConfig, args: {
|
|
43
|
+
path: unknown;
|
|
44
|
+
}): Promise<Record<string, unknown>>;
|
|
45
|
+
export declare function createCoordinatorMcpServer(options?: CoordinatorMcpServerOptions): {
|
|
46
|
+
config: CoordinatorMcpConfig;
|
|
47
|
+
callTool: (name: string, args?: Record<string, unknown>) => Promise<Record<string, unknown>>;
|
|
48
|
+
handleJsonRpc: (request: JsonRpcRequest) => Promise<JsonRpcResponse>;
|
|
49
|
+
handle: (request: JsonRpcRequest) => Promise<JsonRpcResponse>;
|
|
50
|
+
};
|
|
51
|
+
export declare function handleCoordinatorMcpRequest(request: JsonRpcRequest, options?: LegacyHandlerOptions): Promise<any>;
|
|
52
|
+
export declare function runCoordinatorMcpStdio(options?: CoordinatorMcpServerOptions): Promise<void>;
|
|
@@ -62,6 +62,19 @@ export interface ExtensionUIDialogOptions {
|
|
|
62
62
|
* select-only rendering hint; non-TUI bridges drop it and do not serialize it.
|
|
63
63
|
*/
|
|
64
64
|
scrollTitleRows?: number;
|
|
65
|
+
/**
|
|
66
|
+
* For interactive TUI select dialogs, handle the option with `optionLabel`
|
|
67
|
+
* inline: selecting it keeps the title and option list on screen and opens
|
|
68
|
+
* a free-text input below the list. Submitting calls `onSubmit` with the
|
|
69
|
+
* typed text and resolves the select with `optionLabel`; Escape returns to
|
|
70
|
+
* option selection. Non-TUI bridges (RPC, ACP) drop it; callers must keep
|
|
71
|
+
* a fallback path for selects that resolve `optionLabel` without invoking
|
|
72
|
+
* `onSubmit`.
|
|
73
|
+
*/
|
|
74
|
+
customInput?: {
|
|
75
|
+
optionLabel: string;
|
|
76
|
+
onSubmit: (text: string) => void;
|
|
77
|
+
};
|
|
65
78
|
}
|
|
66
79
|
/** Raw terminal input listener for extensions. */
|
|
67
80
|
export type TerminalInputHandler = (data: string) => {
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export declare const GJC_COORDINATOR_SESSION_STATE_FILE_ENV = "GJC_COORDINATOR_SESSION_STATE_FILE";
|
|
2
|
+
export declare const GJC_COORDINATOR_SESSION_ID_ENV = "GJC_COORDINATOR_SESSION_ID";
|
|
3
|
+
interface RuntimeStateEvent {
|
|
4
|
+
type: string;
|
|
5
|
+
messages?: unknown[];
|
|
6
|
+
}
|
|
7
|
+
interface RuntimeStateContext {
|
|
8
|
+
sessionId: string;
|
|
9
|
+
cwd: string;
|
|
10
|
+
sessionFile?: string | null;
|
|
11
|
+
}
|
|
12
|
+
export declare function persistCoordinatorRuntimeStateFromEvent(event: RuntimeStateEvent, context: RuntimeStateContext): Promise<void>;
|
|
13
|
+
export {};
|
|
@@ -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;
|
|
@@ -0,0 +1,71 @@
|
|
|
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
|
+
stateRoot?: string;
|
|
15
|
+
mutation?: string[];
|
|
16
|
+
artifactByteCap?: string;
|
|
17
|
+
serverKey?: string;
|
|
18
|
+
gjcCommand?: string;
|
|
19
|
+
target?: string;
|
|
20
|
+
profileDir?: string;
|
|
21
|
+
}
|
|
22
|
+
export interface CoordinatorSetupSpec {
|
|
23
|
+
schemaVersion: 1;
|
|
24
|
+
coordinator: "hermes";
|
|
25
|
+
serverKey: string;
|
|
26
|
+
serverName: typeof COORDINATOR_MCP_SERVER_NAME;
|
|
27
|
+
protocolVersion: typeof COORDINATOR_MCP_PROTOCOL_VERSION;
|
|
28
|
+
gjcCommand: string;
|
|
29
|
+
args: ["mcp-serve", "coordinator"];
|
|
30
|
+
roots: string[];
|
|
31
|
+
namespace: {
|
|
32
|
+
profile?: string;
|
|
33
|
+
repo?: string;
|
|
34
|
+
};
|
|
35
|
+
sessionCommand?: string;
|
|
36
|
+
stateRoot?: string;
|
|
37
|
+
mutationPolicy: {
|
|
38
|
+
classes: HermesMutationClass[];
|
|
39
|
+
perCallConsentRequired: true;
|
|
40
|
+
};
|
|
41
|
+
artifactByteCap?: number;
|
|
42
|
+
installTarget?: {
|
|
43
|
+
kind: "profile-dir" | "config-file";
|
|
44
|
+
path: string;
|
|
45
|
+
};
|
|
46
|
+
operatorTemplateVersion: 1;
|
|
47
|
+
contractDocVersion: 1;
|
|
48
|
+
}
|
|
49
|
+
export interface HermesSetupResult {
|
|
50
|
+
ok: boolean;
|
|
51
|
+
mode: HermesSetupMode;
|
|
52
|
+
files_written: string[];
|
|
53
|
+
previews: Array<{
|
|
54
|
+
path: string;
|
|
55
|
+
content: string;
|
|
56
|
+
}>;
|
|
57
|
+
warnings: string[];
|
|
58
|
+
smoke: null | {
|
|
59
|
+
ok: boolean;
|
|
60
|
+
protocolVersion: string;
|
|
61
|
+
serverName: string;
|
|
62
|
+
requiredTools: string[];
|
|
63
|
+
missingTools: string[];
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
export declare function buildHermesSetupSpec(flags: HermesSetupFlags): CoordinatorSetupSpec;
|
|
67
|
+
export declare function computeHermesSetupSignature(spec: CoordinatorSetupSpec): string;
|
|
68
|
+
export declare function renderHermesServerBlock(spec: CoordinatorSetupSpec): Record<string, unknown>;
|
|
69
|
+
export declare function runHermesSetup(flags: HermesSetupFlags): Promise<HermesSetupResult>;
|
|
70
|
+
export declare function formatHermesSetupResult(result: HermesSetupResult): string;
|
|
71
|
+
export declare function hermesSetupExitCode(error: unknown): number;
|
|
@@ -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,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.4",
|
|
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",
|
|
@@ -50,12 +50,12 @@
|
|
|
50
50
|
"@agentclientprotocol/sdk": "0.21.0",
|
|
51
51
|
"@babel/parser": "^7.29.3",
|
|
52
52
|
"@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.
|
|
53
|
+
"@gajae-code/stats": "0.4.4",
|
|
54
|
+
"@gajae-code/agent-core": "0.4.4",
|
|
55
|
+
"@gajae-code/ai": "0.4.4",
|
|
56
|
+
"@gajae-code/natives": "0.4.4",
|
|
57
|
+
"@gajae-code/tui": "0.4.4",
|
|
58
|
+
"@gajae-code/utils": "0.4.4",
|
|
59
59
|
"@puppeteer/browsers": "^2.13.0",
|
|
60
60
|
"@types/turndown": "5.0.6",
|
|
61
61
|
"@xterm/headless": "^6.0.0",
|
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();
|