@gajae-code/coding-agent 0.2.5 → 0.3.1
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 +28 -0
- package/dist/types/async/job-manager.d.ts +91 -2
- package/dist/types/cli/args.d.ts +1 -1
- package/dist/types/commands/deep-interview.d.ts +3 -0
- package/dist/types/commands/harness.d.ts +37 -0
- package/dist/types/config/keybindings.d.ts +5 -0
- package/dist/types/config/settings-schema.d.ts +10 -4
- package/dist/types/config/settings.d.ts +2 -0
- package/dist/types/debug/crash-diagnostics.d.ts +45 -0
- package/dist/types/debug/runtime-gauges.d.ts +6 -0
- package/dist/types/deep-interview/render-middleware.d.ts +6 -0
- package/dist/types/eval/py/executor.d.ts +2 -0
- package/dist/types/eval/py/kernel.d.ts +2 -0
- package/dist/types/exec/bash-executor.d.ts +10 -0
- package/dist/types/extensibility/custom-tools/types.d.ts +1 -0
- package/dist/types/extensibility/extensions/types.d.ts +6 -0
- package/dist/types/extensibility/shared-events.d.ts +1 -0
- package/dist/types/gjc-runtime/cli-write-receipt.d.ts +24 -0
- package/dist/types/gjc-runtime/deep-interview-runtime.d.ts +1 -0
- package/dist/types/gjc-runtime/state-graph.d.ts +4 -0
- package/dist/types/gjc-runtime/state-migrations.d.ts +33 -0
- package/dist/types/gjc-runtime/state-renderer.d.ts +65 -0
- package/dist/types/gjc-runtime/state-runtime.d.ts +2 -0
- package/dist/types/gjc-runtime/state-schema.d.ts +317 -0
- package/dist/types/gjc-runtime/state-validation.d.ts +6 -0
- package/dist/types/gjc-runtime/state-writer.d.ts +147 -0
- package/dist/types/gjc-runtime/team-runtime.d.ts +81 -7
- package/dist/types/gjc-runtime/workflow-command-ref.d.ts +43 -0
- package/dist/types/gjc-runtime/workflow-manifest.d.ts +54 -0
- package/dist/types/harness-control-plane/classifier.d.ts +13 -0
- package/dist/types/harness-control-plane/control-endpoint.d.ts +31 -0
- package/dist/types/harness-control-plane/finalize.d.ts +47 -0
- package/dist/types/harness-control-plane/frame-mapper.d.ts +29 -0
- package/dist/types/harness-control-plane/operate.d.ts +35 -0
- package/dist/types/harness-control-plane/owner.d.ts +46 -0
- package/dist/types/harness-control-plane/preserve.d.ts +19 -0
- package/dist/types/harness-control-plane/receipts.d.ts +88 -0
- package/dist/types/harness-control-plane/rpc-adapter.d.ts +66 -0
- package/dist/types/harness-control-plane/seams.d.ts +21 -0
- package/dist/types/harness-control-plane/session-lease.d.ts +65 -0
- package/dist/types/harness-control-plane/state-machine.d.ts +19 -0
- package/dist/types/harness-control-plane/storage.d.ts +53 -0
- package/dist/types/harness-control-plane/types.d.ts +162 -0
- package/dist/types/hooks/skill-keywords.d.ts +2 -1
- package/dist/types/hooks/skill-state.d.ts +23 -29
- package/dist/types/internal-urls/agent-protocol.d.ts +2 -2
- package/dist/types/internal-urls/artifact-protocol.d.ts +2 -2
- package/dist/types/internal-urls/registry-helpers.d.ts +8 -7
- package/dist/types/internal-urls/types.d.ts +4 -0
- package/dist/types/lsp/index.d.ts +10 -10
- package/dist/types/modes/bridge/auth.d.ts +12 -0
- package/dist/types/modes/bridge/bridge-client-bridge.d.ts +9 -0
- package/dist/types/modes/bridge/bridge-mode.d.ts +44 -0
- package/dist/types/modes/bridge/bridge-ui-context.d.ts +88 -0
- package/dist/types/modes/bridge/event-stream.d.ts +8 -0
- package/dist/types/modes/components/custom-editor.d.ts +6 -0
- package/dist/types/modes/components/hook-selector.d.ts +1 -0
- package/dist/types/modes/components/jobs-overlay-model.d.ts +31 -0
- package/dist/types/modes/components/jobs-overlay.d.ts +30 -0
- package/dist/types/modes/components/status-line/types.d.ts +2 -0
- package/dist/types/modes/components/status-line.d.ts +2 -0
- package/dist/types/modes/controllers/input-controller.d.ts +1 -0
- package/dist/types/modes/controllers/selector-controller.d.ts +8 -0
- package/dist/types/modes/index.d.ts +1 -0
- package/dist/types/modes/interactive-mode.d.ts +2 -0
- package/dist/types/modes/jobs-observer.d.ts +57 -0
- package/dist/types/modes/rpc/host-tools.d.ts +1 -16
- package/dist/types/modes/rpc/host-uris.d.ts +1 -38
- package/dist/types/modes/shared/agent-wire/command-dispatch.d.ts +20 -0
- package/dist/types/modes/shared/agent-wire/command-validation.d.ts +2 -0
- package/dist/types/modes/shared/agent-wire/event-envelope.d.ts +24 -0
- package/dist/types/modes/shared/agent-wire/handshake.d.ts +46 -0
- package/dist/types/modes/shared/agent-wire/host-tool-bridge.d.ts +16 -0
- package/dist/types/modes/shared/agent-wire/host-uri-bridge.d.ts +17 -0
- package/dist/types/modes/shared/agent-wire/protocol.d.ts +44 -0
- package/dist/types/modes/shared/agent-wire/responses.d.ts +4 -0
- package/dist/types/modes/shared/agent-wire/scopes.d.ts +18 -0
- package/dist/types/modes/shared/agent-wire/ui-request-broker.d.ts +42 -0
- package/dist/types/modes/shared/agent-wire/ui-result.d.ts +27 -0
- package/dist/types/modes/types.d.ts +2 -0
- package/dist/types/sdk.d.ts +4 -0
- package/dist/types/session/agent-session.d.ts +19 -1
- package/dist/types/skill-state/active-state.d.ts +2 -0
- package/dist/types/skill-state/deep-interview-mutation-guard.d.ts +1 -1
- package/dist/types/skill-state/workflow-state-contract.d.ts +25 -2
- package/dist/types/skill-state/workflow-state-version.d.ts +3 -0
- package/dist/types/task/executor.d.ts +3 -0
- package/dist/types/task/id.d.ts +7 -0
- package/dist/types/task/index.d.ts +5 -0
- package/dist/types/task/receipt.d.ts +85 -0
- package/dist/types/task/spawn-gate.d.ts +38 -0
- package/dist/types/task/types.d.ts +198 -14
- package/dist/types/tools/cron.d.ts +6 -0
- package/dist/types/tools/index.d.ts +2 -0
- package/dist/types/tools/path-utils.d.ts +1 -0
- package/dist/types/tools/subagent.d.ts +26 -1
- package/package.json +7 -7
- package/scripts/build-binary.ts +7 -0
- package/src/async/job-manager.ts +334 -6
- package/src/cli/args.ts +9 -2
- package/src/cli/auth-broker-cli.ts +1 -0
- package/src/cli/config-cli.ts +10 -2
- package/src/cli.ts +2 -0
- package/src/commands/deep-interview.ts +1 -0
- package/src/commands/harness.ts +862 -0
- package/src/commands/launch.ts +2 -2
- package/src/commands/state.ts +2 -1
- package/src/commands/team.ts +54 -39
- package/src/config/keybindings.ts +6 -0
- package/src/config/settings-schema.ts +13 -3
- package/src/config/settings.ts +5 -0
- package/src/dap/client.ts +17 -3
- package/src/debug/crash-diagnostics.ts +223 -0
- package/src/debug/runtime-gauges.ts +20 -0
- package/src/deep-interview/render-middleware.ts +372 -0
- package/src/defaults/gjc/skills/deep-interview/SKILL.md +1 -1
- package/src/defaults/gjc/skills/ralplan/SKILL.md +31 -2
- package/src/defaults/gjc/skills/team/SKILL.md +47 -21
- package/src/defaults/gjc/skills/ultragoal/SKILL.md +106 -13
- package/src/eval/py/executor.ts +21 -1
- package/src/eval/py/kernel.ts +15 -0
- package/src/exec/bash-executor.ts +41 -0
- package/src/extensibility/custom-tools/types.ts +1 -0
- package/src/extensibility/extensions/types.ts +6 -0
- package/src/extensibility/shared-events.ts +1 -0
- package/src/gjc-runtime/cli-write-receipt.ts +31 -0
- package/src/gjc-runtime/deep-interview-runtime.ts +98 -42
- package/src/gjc-runtime/goal-mode-request.ts +11 -3
- package/src/gjc-runtime/ralplan-runtime.ts +235 -43
- package/src/gjc-runtime/state-graph.ts +86 -0
- package/src/gjc-runtime/state-migrations.ts +179 -0
- package/src/gjc-runtime/state-renderer.ts +345 -0
- package/src/gjc-runtime/state-runtime.ts +1155 -46
- package/src/gjc-runtime/state-schema.ts +192 -0
- package/src/gjc-runtime/state-validation.ts +49 -0
- package/src/gjc-runtime/state-writer.ts +749 -0
- package/src/gjc-runtime/team-runtime.ts +1255 -189
- package/src/gjc-runtime/ultragoal-runtime.ts +460 -43
- package/src/gjc-runtime/workflow-command-ref.ts +239 -0
- package/src/gjc-runtime/workflow-manifest.generated.json +1601 -0
- package/src/gjc-runtime/workflow-manifest.ts +427 -0
- package/src/harness-control-plane/classifier.ts +128 -0
- package/src/harness-control-plane/control-endpoint.ts +148 -0
- package/src/harness-control-plane/finalize.ts +222 -0
- package/src/harness-control-plane/frame-mapper.ts +286 -0
- package/src/harness-control-plane/operate.ts +225 -0
- package/src/harness-control-plane/owner.ts +600 -0
- package/src/harness-control-plane/preserve.ts +102 -0
- package/src/harness-control-plane/receipts.ts +216 -0
- package/src/harness-control-plane/rpc-adapter.ts +276 -0
- package/src/harness-control-plane/seams.ts +39 -0
- package/src/harness-control-plane/session-lease.ts +388 -0
- package/src/harness-control-plane/state-machine.ts +98 -0
- package/src/harness-control-plane/storage.ts +257 -0
- package/src/harness-control-plane/types.ts +214 -0
- package/src/hooks/skill-keywords.ts +4 -2
- package/src/hooks/skill-state.ts +197 -64
- package/src/internal-urls/agent-protocol.ts +68 -21
- package/src/internal-urls/artifact-protocol.ts +12 -17
- package/src/internal-urls/docs-index.generated.ts +3 -2
- package/src/internal-urls/registry-helpers.ts +19 -16
- package/src/internal-urls/types.ts +4 -0
- package/src/lsp/client.ts +18 -2
- package/src/main.ts +21 -5
- package/src/modes/bridge/auth.ts +41 -0
- package/src/modes/bridge/bridge-client-bridge.ts +47 -0
- package/src/modes/bridge/bridge-mode.ts +520 -0
- package/src/modes/bridge/bridge-ui-context.ts +200 -0
- package/src/modes/bridge/event-stream.ts +70 -0
- package/src/modes/components/assistant-message.ts +5 -1
- package/src/modes/components/custom-editor.ts +101 -0
- package/src/modes/components/hook-selector.ts +133 -20
- package/src/modes/components/jobs-overlay-model.ts +109 -0
- package/src/modes/components/jobs-overlay.ts +172 -0
- package/src/modes/components/status-line/presets.ts +7 -5
- package/src/modes/components/status-line/segments.ts +25 -0
- package/src/modes/components/status-line/types.ts +2 -0
- package/src/modes/components/status-line.ts +9 -1
- package/src/modes/controllers/event-controller.ts +71 -6
- package/src/modes/controllers/extension-ui-controller.ts +43 -1
- package/src/modes/controllers/input-controller.ts +105 -9
- package/src/modes/controllers/selector-controller.ts +31 -1
- package/src/modes/index.ts +1 -0
- package/src/modes/interactive-mode.ts +28 -0
- package/src/modes/jobs-observer.ts +204 -0
- package/src/modes/rpc/host-tools.ts +1 -186
- package/src/modes/rpc/host-uris.ts +1 -235
- package/src/modes/rpc/rpc-client.ts +25 -10
- package/src/modes/rpc/rpc-mode.ts +12 -381
- package/src/modes/shared/agent-wire/command-dispatch.ts +341 -0
- package/src/modes/shared/agent-wire/command-validation.ts +131 -0
- package/src/modes/shared/agent-wire/event-envelope.ts +108 -0
- package/src/modes/shared/agent-wire/handshake.ts +117 -0
- package/src/modes/shared/agent-wire/host-tool-bridge.ts +194 -0
- package/src/modes/shared/agent-wire/host-uri-bridge.ts +236 -0
- package/src/modes/shared/agent-wire/protocol.ts +96 -0
- package/src/modes/shared/agent-wire/responses.ts +17 -0
- package/src/modes/shared/agent-wire/scopes.ts +89 -0
- package/src/modes/shared/agent-wire/ui-request-broker.ts +150 -0
- package/src/modes/shared/agent-wire/ui-result.ts +48 -0
- package/src/modes/types.ts +2 -0
- package/src/prompts/agents/executor.md +13 -0
- package/src/prompts/tools/subagent.md +39 -4
- package/src/prompts/tools/task-summary.md +3 -9
- package/src/prompts/tools/task.md +5 -1
- package/src/sdk.ts +8 -0
- package/src/session/agent-session.ts +445 -71
- package/src/session/session-manager.ts +13 -1
- package/src/skill-state/active-state.ts +58 -65
- package/src/skill-state/deep-interview-mutation-guard.ts +114 -17
- package/src/skill-state/initial-phase.ts +2 -0
- package/src/skill-state/workflow-state-contract.ts +33 -4
- package/src/skill-state/workflow-state-version.ts +3 -0
- package/src/slash-commands/builtin-registry.ts +8 -0
- package/src/task/executor.ts +79 -13
- package/src/task/id.ts +33 -0
- package/src/task/index.ts +376 -74
- package/src/task/output-manager.ts +5 -4
- package/src/task/receipt.ts +297 -0
- package/src/task/render.ts +54 -134
- package/src/task/spawn-gate.ts +132 -0
- package/src/task/types.ts +104 -10
- package/src/tools/ask.ts +88 -27
- package/src/tools/ast-edit.ts +1 -0
- package/src/tools/ast-grep.ts +1 -0
- package/src/tools/bash.ts +1 -1
- package/src/tools/cron.ts +48 -0
- package/src/tools/find.ts +4 -1
- package/src/tools/index.ts +2 -0
- package/src/tools/path-utils.ts +3 -2
- package/src/tools/read.ts +1 -0
- package/src/tools/search.ts +1 -0
- package/src/tools/skill.ts +6 -1
- package/src/tools/subagent.ts +423 -79
|
@@ -1,186 +1 @@
|
|
|
1
|
-
|
|
2
|
-
import type { Static, TSchema } from "@gajae-code/ai";
|
|
3
|
-
import { Snowflake } from "@gajae-code/utils";
|
|
4
|
-
import { applyToolProxy } from "../../extensibility/tool-proxy";
|
|
5
|
-
import type { Theme } from "../../modes/theme/theme";
|
|
6
|
-
import type {
|
|
7
|
-
RpcHostToolCallRequest,
|
|
8
|
-
RpcHostToolCancelRequest,
|
|
9
|
-
RpcHostToolDefinition,
|
|
10
|
-
RpcHostToolResult,
|
|
11
|
-
RpcHostToolUpdate,
|
|
12
|
-
} from "./rpc-types";
|
|
13
|
-
|
|
14
|
-
type RpcHostToolOutput = (frame: RpcHostToolCallRequest | RpcHostToolCancelRequest) => void;
|
|
15
|
-
|
|
16
|
-
type PendingHostToolCall = {
|
|
17
|
-
resolve: (result: AgentToolResult<unknown>) => void;
|
|
18
|
-
reject: (error: Error) => void;
|
|
19
|
-
onUpdate?: AgentToolUpdateCallback<unknown>;
|
|
20
|
-
};
|
|
21
|
-
|
|
22
|
-
function isAgentToolResult(value: unknown): value is AgentToolResult<unknown> {
|
|
23
|
-
if (!value || typeof value !== "object") return false;
|
|
24
|
-
const content = (value as { content?: unknown }).content;
|
|
25
|
-
return Array.isArray(content);
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
export function isRpcHostToolResult(value: unknown): value is RpcHostToolResult {
|
|
29
|
-
if (!value || typeof value !== "object") return false;
|
|
30
|
-
const frame = value as { type?: unknown; id?: unknown; result?: unknown };
|
|
31
|
-
return frame.type === "host_tool_result" && typeof frame.id === "string" && isAgentToolResult(frame.result);
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
export function isRpcHostToolUpdate(value: unknown): value is RpcHostToolUpdate {
|
|
35
|
-
if (!value || typeof value !== "object") return false;
|
|
36
|
-
const frame = value as { type?: unknown; id?: unknown; partialResult?: unknown };
|
|
37
|
-
return frame.type === "host_tool_update" && typeof frame.id === "string" && isAgentToolResult(frame.partialResult);
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
class RpcHostToolAdapter<TParams extends TSchema = TSchema, TTheme extends Theme = Theme>
|
|
41
|
-
implements AgentTool<TParams, unknown, TTheme>
|
|
42
|
-
{
|
|
43
|
-
declare name: string;
|
|
44
|
-
declare label: string;
|
|
45
|
-
declare description: string;
|
|
46
|
-
declare parameters: TParams;
|
|
47
|
-
readonly strict = true;
|
|
48
|
-
concurrency: "shared" | "exclusive" = "shared";
|
|
49
|
-
#bridge: RpcHostToolBridge;
|
|
50
|
-
#definition: RpcHostToolDefinition;
|
|
51
|
-
|
|
52
|
-
constructor(definition: RpcHostToolDefinition, bridge: RpcHostToolBridge) {
|
|
53
|
-
this.#definition = definition;
|
|
54
|
-
this.#bridge = bridge;
|
|
55
|
-
applyToolProxy(definition, this);
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
execute(
|
|
59
|
-
toolCallId: string,
|
|
60
|
-
params: Static<TParams>,
|
|
61
|
-
signal?: AbortSignal,
|
|
62
|
-
onUpdate?: AgentToolUpdateCallback<unknown>,
|
|
63
|
-
): Promise<AgentToolResult<unknown>> {
|
|
64
|
-
return this.#bridge.requestExecution(
|
|
65
|
-
this.#definition,
|
|
66
|
-
toolCallId,
|
|
67
|
-
params as Record<string, unknown>,
|
|
68
|
-
signal,
|
|
69
|
-
onUpdate,
|
|
70
|
-
);
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
export class RpcHostToolBridge {
|
|
75
|
-
#output: RpcHostToolOutput;
|
|
76
|
-
#definitions = new Map<string, RpcHostToolDefinition>();
|
|
77
|
-
#pendingCalls = new Map<string, PendingHostToolCall>();
|
|
78
|
-
|
|
79
|
-
constructor(output: RpcHostToolOutput) {
|
|
80
|
-
this.#output = output;
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
getToolNames(): string[] {
|
|
84
|
-
return Array.from(this.#definitions.keys());
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
setTools(tools: RpcHostToolDefinition[]): AgentTool[] {
|
|
88
|
-
this.#definitions = new Map(tools.map(tool => [tool.name, tool]));
|
|
89
|
-
return tools.map(tool => new RpcHostToolAdapter(tool, this));
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
handleResult(frame: RpcHostToolResult): boolean {
|
|
93
|
-
const pending = this.#pendingCalls.get(frame.id);
|
|
94
|
-
if (!pending) return false;
|
|
95
|
-
this.#pendingCalls.delete(frame.id);
|
|
96
|
-
if (frame.isError) {
|
|
97
|
-
const text = frame.result.content
|
|
98
|
-
.filter(
|
|
99
|
-
(item): item is { type: "text"; text: string } => item.type === "text" && typeof item.text === "string",
|
|
100
|
-
)
|
|
101
|
-
.map(item => item.text)
|
|
102
|
-
.join("\n")
|
|
103
|
-
.trim();
|
|
104
|
-
pending.reject(new Error(text || "Host tool execution failed"));
|
|
105
|
-
return true;
|
|
106
|
-
}
|
|
107
|
-
pending.resolve(frame.result);
|
|
108
|
-
return true;
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
handleUpdate(frame: RpcHostToolUpdate): boolean {
|
|
112
|
-
const pending = this.#pendingCalls.get(frame.id);
|
|
113
|
-
if (!pending) return false;
|
|
114
|
-
pending.onUpdate?.(frame.partialResult);
|
|
115
|
-
return true;
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
requestExecution(
|
|
119
|
-
definition: RpcHostToolDefinition,
|
|
120
|
-
toolCallId: string,
|
|
121
|
-
args: Record<string, unknown>,
|
|
122
|
-
signal?: AbortSignal,
|
|
123
|
-
onUpdate?: AgentToolUpdateCallback<unknown>,
|
|
124
|
-
): Promise<AgentToolResult<unknown>> {
|
|
125
|
-
if (signal?.aborted) {
|
|
126
|
-
return Promise.reject(new Error(`Host tool "${definition.name}" was aborted`));
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
const id = Snowflake.next() as string;
|
|
130
|
-
const { promise, resolve, reject } = Promise.withResolvers<AgentToolResult<unknown>>();
|
|
131
|
-
let settled = false;
|
|
132
|
-
|
|
133
|
-
const cleanup = () => {
|
|
134
|
-
signal?.removeEventListener("abort", onAbort);
|
|
135
|
-
this.#pendingCalls.delete(id);
|
|
136
|
-
};
|
|
137
|
-
|
|
138
|
-
const onAbort = () => {
|
|
139
|
-
if (settled) return;
|
|
140
|
-
settled = true;
|
|
141
|
-
cleanup();
|
|
142
|
-
this.#output({
|
|
143
|
-
type: "host_tool_cancel",
|
|
144
|
-
id: Snowflake.next() as string,
|
|
145
|
-
targetId: id,
|
|
146
|
-
});
|
|
147
|
-
reject(new Error(`Host tool "${definition.name}" was aborted`));
|
|
148
|
-
};
|
|
149
|
-
|
|
150
|
-
signal?.addEventListener("abort", onAbort, { once: true });
|
|
151
|
-
this.#pendingCalls.set(id, {
|
|
152
|
-
resolve: result => {
|
|
153
|
-
if (settled) return;
|
|
154
|
-
settled = true;
|
|
155
|
-
cleanup();
|
|
156
|
-
resolve(result);
|
|
157
|
-
},
|
|
158
|
-
reject: error => {
|
|
159
|
-
if (settled) return;
|
|
160
|
-
settled = true;
|
|
161
|
-
cleanup();
|
|
162
|
-
reject(error);
|
|
163
|
-
},
|
|
164
|
-
onUpdate,
|
|
165
|
-
});
|
|
166
|
-
|
|
167
|
-
this.#output({
|
|
168
|
-
type: "host_tool_call",
|
|
169
|
-
id,
|
|
170
|
-
toolCallId,
|
|
171
|
-
toolName: definition.name,
|
|
172
|
-
arguments: args,
|
|
173
|
-
});
|
|
174
|
-
|
|
175
|
-
return promise;
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
rejectAllPending(message: string): void {
|
|
179
|
-
const error = new Error(message);
|
|
180
|
-
const pendingCalls = Array.from(this.#pendingCalls.values());
|
|
181
|
-
this.#pendingCalls.clear();
|
|
182
|
-
for (const pending of pendingCalls) {
|
|
183
|
-
pending.reject(error);
|
|
184
|
-
}
|
|
185
|
-
}
|
|
186
|
-
}
|
|
1
|
+
export * from "../shared/agent-wire/host-tool-bridge";
|
|
@@ -1,235 +1 @@
|
|
|
1
|
-
|
|
2
|
-
import { InternalUrlRouter } from "../../internal-urls";
|
|
3
|
-
import type {
|
|
4
|
-
InternalResource,
|
|
5
|
-
InternalUrl,
|
|
6
|
-
ProtocolHandler,
|
|
7
|
-
ResolveContext,
|
|
8
|
-
WriteContext,
|
|
9
|
-
} from "../../internal-urls/types";
|
|
10
|
-
import type {
|
|
11
|
-
RpcHostUriCancelRequest,
|
|
12
|
-
RpcHostUriRequest,
|
|
13
|
-
RpcHostUriResult,
|
|
14
|
-
RpcHostUriSchemeDefinition,
|
|
15
|
-
} from "./rpc-types";
|
|
16
|
-
|
|
17
|
-
type RpcHostUriOutput = (frame: RpcHostUriRequest | RpcHostUriCancelRequest) => void;
|
|
18
|
-
|
|
19
|
-
type PendingUriRequest = {
|
|
20
|
-
operation: "read" | "write";
|
|
21
|
-
url: string;
|
|
22
|
-
resolve: (frame: RpcHostUriResult) => void;
|
|
23
|
-
reject: (error: Error) => void;
|
|
24
|
-
};
|
|
25
|
-
|
|
26
|
-
/** Type guard for inbound `host_uri_result` frames coming from the host. */
|
|
27
|
-
export function isRpcHostUriResult(value: unknown): value is RpcHostUriResult {
|
|
28
|
-
if (!value || typeof value !== "object") return false;
|
|
29
|
-
const frame = value as { type?: unknown; id?: unknown };
|
|
30
|
-
return frame.type === "host_uri_result" && typeof frame.id === "string";
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
/**
|
|
34
|
-
* One handler instance per host-registered scheme. Delegates reads and (when
|
|
35
|
-
* the scheme was registered as writable) writes to the bridge, which serializes
|
|
36
|
-
* them over the RPC transport.
|
|
37
|
-
*/
|
|
38
|
-
class RpcHostUriProtocolHandler implements ProtocolHandler {
|
|
39
|
-
readonly scheme: string;
|
|
40
|
-
readonly immutable: boolean;
|
|
41
|
-
readonly write?: (url: InternalUrl, content: string, context?: WriteContext) => Promise<void>;
|
|
42
|
-
readonly #bridge: RpcHostUriBridge;
|
|
43
|
-
|
|
44
|
-
constructor(definition: RpcHostUriSchemeDefinition, bridge: RpcHostUriBridge) {
|
|
45
|
-
this.scheme = definition.scheme;
|
|
46
|
-
this.immutable = definition.immutable === true;
|
|
47
|
-
this.#bridge = bridge;
|
|
48
|
-
if (definition.writable === true) {
|
|
49
|
-
this.write = (url, content, context) => this.#bridge.requestWrite(this.scheme, url, content, context);
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
resolve(url: InternalUrl, context?: ResolveContext): Promise<InternalResource> {
|
|
54
|
-
return this.#bridge.requestRead(this.scheme, url, context);
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
/**
|
|
59
|
-
* Bidirectional bridge that lets the RPC host own a set of URI schemes.
|
|
60
|
-
*
|
|
61
|
-
* The host registers schemes via `set_host_uri_schemes`; the bridge installs
|
|
62
|
-
* a `RpcHostUriProtocolHandler` per scheme into the process-global
|
|
63
|
-
* {@link InternalUrlRouter}. Reads land on the read tool through the existing
|
|
64
|
-
* router; writes are intercepted by the write tool and dispatched through
|
|
65
|
-
* `requestWrite`.
|
|
66
|
-
*/
|
|
67
|
-
export class RpcHostUriBridge {
|
|
68
|
-
#output: RpcHostUriOutput;
|
|
69
|
-
#router: InternalUrlRouter;
|
|
70
|
-
#definitions = new Map<string, RpcHostUriSchemeDefinition>();
|
|
71
|
-
#pending = new Map<string, PendingUriRequest>();
|
|
72
|
-
|
|
73
|
-
constructor(output: RpcHostUriOutput, router: InternalUrlRouter = InternalUrlRouter.instance()) {
|
|
74
|
-
this.#output = output;
|
|
75
|
-
this.#router = router;
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
getSchemes(): string[] {
|
|
79
|
-
return Array.from(this.#definitions.keys());
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
/**
|
|
83
|
-
* Replace the registered set of host URI schemes. Previously registered
|
|
84
|
-
* schemes that no longer appear in the new set are unregistered from the
|
|
85
|
-
* router; surviving and new schemes get fresh handler instances.
|
|
86
|
-
*/
|
|
87
|
-
setSchemes(schemes: RpcHostUriSchemeDefinition[]): string[] {
|
|
88
|
-
const normalized = new Map<string, RpcHostUriSchemeDefinition>();
|
|
89
|
-
for (const raw of schemes) {
|
|
90
|
-
const scheme = typeof raw?.scheme === "string" ? raw.scheme.trim().toLowerCase() : "";
|
|
91
|
-
if (!scheme) {
|
|
92
|
-
throw new Error("Host URI scheme must be a non-empty string");
|
|
93
|
-
}
|
|
94
|
-
if (!/^[a-z][a-z0-9+.-]*$/.test(scheme)) {
|
|
95
|
-
throw new Error(`Host URI scheme contains invalid characters: ${raw.scheme}`);
|
|
96
|
-
}
|
|
97
|
-
normalized.set(scheme, {
|
|
98
|
-
scheme,
|
|
99
|
-
description: typeof raw.description === "string" ? raw.description : undefined,
|
|
100
|
-
writable: raw.writable === true,
|
|
101
|
-
immutable: raw.immutable === true,
|
|
102
|
-
});
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
for (const previous of this.#definitions.keys()) {
|
|
106
|
-
if (!normalized.has(previous)) {
|
|
107
|
-
this.#router.unregister(previous);
|
|
108
|
-
}
|
|
109
|
-
}
|
|
110
|
-
for (const definition of normalized.values()) {
|
|
111
|
-
this.#router.register(new RpcHostUriProtocolHandler(definition, this));
|
|
112
|
-
}
|
|
113
|
-
this.#definitions = normalized;
|
|
114
|
-
return Array.from(normalized.keys());
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
/**
|
|
118
|
-
* Unregister every host scheme from the router and reject any in-flight
|
|
119
|
-
* requests. Called on RPC shutdown to keep the global router clean for
|
|
120
|
-
* subsequent sessions in the same process (used by tests).
|
|
121
|
-
*/
|
|
122
|
-
clear(message: string = "Host URI bridge shut down"): void {
|
|
123
|
-
for (const scheme of this.#definitions.keys()) {
|
|
124
|
-
this.#router.unregister(scheme);
|
|
125
|
-
}
|
|
126
|
-
this.#definitions.clear();
|
|
127
|
-
this.rejectAllPending(message);
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
/** Resolve a pending request by id; called by `rpc-mode` on inbound results. */
|
|
131
|
-
handleResult(frame: RpcHostUriResult): boolean {
|
|
132
|
-
const pending = this.#pending.get(frame.id);
|
|
133
|
-
if (!pending) return false;
|
|
134
|
-
this.#pending.delete(frame.id);
|
|
135
|
-
pending.resolve(frame);
|
|
136
|
-
return true;
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
rejectAllPending(message: string): void {
|
|
140
|
-
const error = new Error(message);
|
|
141
|
-
const pending = Array.from(this.#pending.values());
|
|
142
|
-
this.#pending.clear();
|
|
143
|
-
for (const entry of pending) {
|
|
144
|
-
entry.reject(error);
|
|
145
|
-
}
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
async requestRead(scheme: string, url: InternalUrl, context?: ResolveContext): Promise<InternalResource> {
|
|
149
|
-
const result = await this.#dispatch("read", url.href, undefined, context?.signal);
|
|
150
|
-
if (result.isError) {
|
|
151
|
-
throw new Error(result.error || result.content || `Host URI read failed for ${url.href}`);
|
|
152
|
-
}
|
|
153
|
-
const content = result.content ?? "";
|
|
154
|
-
const contentType = result.contentType ?? "text/plain";
|
|
155
|
-
const definition = this.#definitions.get(scheme);
|
|
156
|
-
return {
|
|
157
|
-
url: url.href,
|
|
158
|
-
content,
|
|
159
|
-
contentType,
|
|
160
|
-
size: Buffer.byteLength(content, "utf-8"),
|
|
161
|
-
notes: result.notes && result.notes.length > 0 ? [...result.notes] : undefined,
|
|
162
|
-
immutable: result.immutable ?? definition?.immutable === true,
|
|
163
|
-
};
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
async requestWrite(_scheme: string, url: InternalUrl, content: string, context?: WriteContext): Promise<void> {
|
|
167
|
-
const result = await this.#dispatch("write", url.href, content, context?.signal);
|
|
168
|
-
if (result.isError) {
|
|
169
|
-
throw new Error(result.error || result.content || `Host URI write failed for ${url.href}`);
|
|
170
|
-
}
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
#dispatch(
|
|
174
|
-
operation: "read" | "write",
|
|
175
|
-
url: string,
|
|
176
|
-
content: string | undefined,
|
|
177
|
-
signal: AbortSignal | undefined,
|
|
178
|
-
): Promise<RpcHostUriResult> {
|
|
179
|
-
if (signal?.aborted) {
|
|
180
|
-
return Promise.reject(new Error(`Host URI ${operation} for ${url} was aborted`));
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
const id = Snowflake.next() as string;
|
|
184
|
-
const { promise, resolve, reject } = Promise.withResolvers<RpcHostUriResult>();
|
|
185
|
-
let settled = false;
|
|
186
|
-
|
|
187
|
-
const cleanup = () => {
|
|
188
|
-
signal?.removeEventListener("abort", onAbort);
|
|
189
|
-
this.#pending.delete(id);
|
|
190
|
-
};
|
|
191
|
-
|
|
192
|
-
const onAbort = () => {
|
|
193
|
-
if (settled) return;
|
|
194
|
-
settled = true;
|
|
195
|
-
cleanup();
|
|
196
|
-
this.#output({
|
|
197
|
-
type: "host_uri_cancel",
|
|
198
|
-
id: Snowflake.next() as string,
|
|
199
|
-
targetId: id,
|
|
200
|
-
});
|
|
201
|
-
reject(new Error(`Host URI ${operation} for ${url} was aborted`));
|
|
202
|
-
};
|
|
203
|
-
|
|
204
|
-
signal?.addEventListener("abort", onAbort, { once: true });
|
|
205
|
-
this.#pending.set(id, {
|
|
206
|
-
operation,
|
|
207
|
-
url,
|
|
208
|
-
resolve: frame => {
|
|
209
|
-
if (settled) return;
|
|
210
|
-
settled = true;
|
|
211
|
-
cleanup();
|
|
212
|
-
resolve(frame);
|
|
213
|
-
},
|
|
214
|
-
reject: err => {
|
|
215
|
-
if (settled) return;
|
|
216
|
-
settled = true;
|
|
217
|
-
cleanup();
|
|
218
|
-
reject(err);
|
|
219
|
-
},
|
|
220
|
-
});
|
|
221
|
-
|
|
222
|
-
const frame: RpcHostUriRequest = {
|
|
223
|
-
type: "host_uri_request",
|
|
224
|
-
id,
|
|
225
|
-
operation,
|
|
226
|
-
url,
|
|
227
|
-
};
|
|
228
|
-
if (operation === "write") {
|
|
229
|
-
frame.content = content ?? "";
|
|
230
|
-
}
|
|
231
|
-
this.#output(frame);
|
|
232
|
-
|
|
233
|
-
return promise;
|
|
234
|
-
}
|
|
235
|
-
}
|
|
1
|
+
export * from "../shared/agent-wire/host-uri-bridge";
|
|
@@ -186,7 +186,12 @@ export class RpcClient {
|
|
|
186
186
|
cwd: this.options.cwd,
|
|
187
187
|
env: { ...Bun.env, ...this.options.env },
|
|
188
188
|
stdin: "pipe",
|
|
189
|
+
stderr: "full",
|
|
189
190
|
});
|
|
191
|
+
const startupStderrPromise = this.#process.stderr
|
|
192
|
+
? new Response(this.#process.stderr).text().catch(() => "")
|
|
193
|
+
: Promise.resolve("");
|
|
194
|
+
const getStartupStderr = async () => this.#process?.peekStderr() || (await startupStderrPromise);
|
|
190
195
|
|
|
191
196
|
// Wait for the "ready" signal or process exit
|
|
192
197
|
const { promise: readyPromise, resolve: readyResolve, reject: readyReject } = Promise.withResolvers<void>();
|
|
@@ -203,10 +208,20 @@ export class RpcClient {
|
|
|
203
208
|
}
|
|
204
209
|
this.#handleLine(line);
|
|
205
210
|
}
|
|
206
|
-
// Stream ended without ready signal — process exited
|
|
211
|
+
// Stream ended without ready signal — process exited. Wait for the
|
|
212
|
+
// managed process wrapper so stderr is fully drained before reporting.
|
|
207
213
|
if (!readySettled) {
|
|
208
|
-
|
|
209
|
-
|
|
214
|
+
const proc = this.#process;
|
|
215
|
+
const exitCode = proc ? await proc.exited.catch(() => proc.exitCode ?? -1) : undefined;
|
|
216
|
+
if (!readySettled) {
|
|
217
|
+
const stderr = await getStartupStderr();
|
|
218
|
+
readySettled = true;
|
|
219
|
+
readyReject(
|
|
220
|
+
new Error(
|
|
221
|
+
`Agent process exited${exitCode === undefined ? "" : ` with code ${exitCode}`} before ready. Stderr: ${stderr}`,
|
|
222
|
+
),
|
|
223
|
+
);
|
|
224
|
+
}
|
|
210
225
|
}
|
|
211
226
|
})().catch((err: Error) => {
|
|
212
227
|
if (!readySettled) {
|
|
@@ -216,12 +231,12 @@ export class RpcClient {
|
|
|
216
231
|
});
|
|
217
232
|
|
|
218
233
|
// Also race against process exit (in case stdout closes before we read it)
|
|
219
|
-
void this.#process.exited.then((exitCode: number) => {
|
|
234
|
+
void this.#process.exited.then(async (exitCode: number) => {
|
|
220
235
|
if (!readySettled) {
|
|
236
|
+
const stderr = await getStartupStderr();
|
|
237
|
+
if (readySettled) return;
|
|
221
238
|
readySettled = true;
|
|
222
|
-
readyReject(
|
|
223
|
-
new Error(`Agent process exited with code ${exitCode}. Stderr: ${this.#process?.peekStderr() ?? ""}`),
|
|
224
|
-
);
|
|
239
|
+
readyReject(new Error(`Agent process exited with code ${exitCode}. Stderr: ${stderr}`));
|
|
225
240
|
}
|
|
226
241
|
});
|
|
227
242
|
|
|
@@ -229,9 +244,9 @@ export class RpcClient {
|
|
|
229
244
|
const readyTimeout = this.#startTimeout(30000, () => {
|
|
230
245
|
if (readySettled) return;
|
|
231
246
|
readySettled = true;
|
|
232
|
-
|
|
233
|
-
new Error(`Timeout waiting for agent to become ready. Stderr: ${
|
|
234
|
-
);
|
|
247
|
+
void getStartupStderr().then(stderr => {
|
|
248
|
+
readyReject(new Error(`Timeout waiting for agent to become ready. Stderr: ${stderr}`));
|
|
249
|
+
});
|
|
235
250
|
});
|
|
236
251
|
|
|
237
252
|
try {
|