super-dsh 0.1.0 → 0.1.2
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/agent-agy/bridge/agy_bridge.py +250 -0
- package/agent-agy/bridge/gemini_openai_server.mjs +98 -0
- package/agent-agy/cordis.patch.yml +49 -0
- package/agent-agy/dist/adapter.d.ts +15 -0
- package/agent-agy/dist/adapter.js +42 -0
- package/agent-agy/dist/agent-preset-agy.d.ts +47 -0
- package/agent-agy/dist/agent-preset-agy.js +169 -0
- package/agent-agy/dist/agent-preset-projection.d.ts +31 -0
- package/agent-agy/dist/agent-preset-projection.js +33 -0
- package/agent-agy/dist/agent.d.ts +137 -0
- package/agent-agy/dist/agent.js +493 -0
- package/agent-agy/dist/agy-cli-client.d.ts +137 -0
- package/agent-agy/dist/agy-cli-client.js +301 -0
- package/agent-agy/dist/agy-client.d.ts +160 -0
- package/agent-agy/dist/agy-client.js +252 -0
- package/agent-agy/dist/agy-sessions.d.ts +24 -0
- package/agent-agy/dist/agy-sessions.js +116 -0
- package/agent-agy/dist/agy-store.d.ts +39 -0
- package/agent-agy/dist/agy-store.js +83 -0
- package/agent-agy/dist/inbox.d.ts +26 -0
- package/agent-agy/dist/inbox.js +53 -0
- package/agent-agy/dist/index.d.ts +80 -0
- package/agent-agy/dist/index.js +505 -0
- package/agent-agy/dist/models.d.ts +41 -0
- package/agent-agy/dist/models.js +74 -0
- package/agent-agy/dist/world-plugin.d.ts +9 -0
- package/agent-agy/dist/world-plugin.js +97 -0
- package/agent-agy/package.json +64 -0
- package/agent-claude/cordis.patch.yml +42 -0
- package/agent-claude/dist/adapter.js +62 -0
- package/agent-claude/dist/agent-preset-claude.js +169 -0
- package/agent-claude/dist/agent-preset-projection.js +30 -0
- package/agent-claude/dist/agent.js +1271 -0
- package/agent-claude/dist/claude-client.js +357 -0
- package/agent-claude/dist/claude-events.js +223 -0
- package/agent-claude/dist/claude-home.js +29 -0
- package/agent-claude/dist/commands.js +182 -0
- package/agent-claude/dist/content.js +134 -0
- package/agent-claude/dist/inbox.js +53 -0
- package/agent-claude/dist/index.js +555 -0
- package/agent-claude/dist/input-queue.js +64 -0
- package/agent-claude/dist/interaction.js +271 -0
- package/agent-claude/dist/models.js +158 -0
- package/agent-claude/dist/permission.js +120 -0
- package/agent-claude/dist/session-id.js +20 -0
- package/agent-claude/dist/session-map.js +165 -0
- package/agent-claude/dist/world-plugin.js +14 -0
- package/agent-claude/package.json +74 -0
- package/agent-codex/README.md +74 -0
- package/agent-codex/cordis.patch.yml +77 -0
- package/agent-codex/dist/adapter.js +55 -0
- package/agent-codex/dist/agent-preset-codex.js +171 -0
- package/agent-codex/dist/agent-preset-projection.js +37 -0
- package/agent-codex/dist/agent.js +1056 -0
- package/agent-codex/dist/app-home.js +65 -0
- package/agent-codex/dist/codex-client.js +469 -0
- package/agent-codex/dist/codex-events.js +297 -0
- package/agent-codex/dist/codex-store.js +190 -0
- package/agent-codex/dist/inbox.js +53 -0
- package/agent-codex/dist/index.js +605 -0
- package/agent-codex/dist/models.js +152 -0
- package/agent-codex/dist/permission.js +71 -0
- package/agent-codex/dist/session-map.js +170 -0
- package/agent-codex/dist/spike.js +46 -0
- package/agent-codex/dist/world-plugin.js +96 -0
- package/agent-codex/package.json +78 -0
- package/agent-hermes/README.md +144 -0
- package/agent-hermes/cordis.patch.yml +83 -0
- package/agent-hermes/dist/adapter.js +83 -0
- package/agent-hermes/dist/agent-preset-hermes.js +171 -0
- package/agent-hermes/dist/agent-preset-projection.js +34 -0
- package/agent-hermes/dist/agent.js +1068 -0
- package/agent-hermes/dist/hermes-client.js +953 -0
- package/agent-hermes/dist/hermes-events.js +289 -0
- package/agent-hermes/dist/hermes-store.js +158 -0
- package/agent-hermes/dist/inbox.js +53 -0
- package/agent-hermes/dist/index.js +541 -0
- package/agent-hermes/dist/models.js +324 -0
- package/agent-hermes/dist/permission.js +60 -0
- package/agent-hermes/dist/world-plugin.js +101 -0
- package/agent-hermes/package.json +76 -0
- package/agent-hub/cordis.patch.yml +12 -0
- package/agent-hub/dist/agent-roster.d.ts +34 -0
- package/agent-hub/dist/agent-roster.js +27 -0
- package/agent-hub/dist/carrier.d.ts +43 -0
- package/agent-hub/dist/carrier.js +241 -0
- package/agent-hub/dist/client-shim.d.ts +28 -0
- package/agent-hub/dist/client-shim.js +251 -0
- package/agent-hub/dist/envelope.d.ts +54 -0
- package/agent-hub/dist/envelope.js +92 -0
- package/agent-hub/dist/gateway.d.ts +28 -0
- package/agent-hub/dist/gateway.js +18 -0
- package/agent-hub/dist/index-pass.d.ts +22 -0
- package/agent-hub/dist/index-pass.js +47 -0
- package/agent-hub/dist/index.d.ts +29 -0
- package/agent-hub/dist/index.js +29 -0
- package/agent-hub/dist/labels.d.ts +45 -0
- package/agent-hub/dist/labels.js +83 -0
- package/agent-hub/dist/ownership.d.ts +37 -0
- package/agent-hub/dist/ownership.js +54 -0
- package/agent-hub/dist/roster.d.ts +28 -0
- package/agent-hub/dist/roster.js +38 -0
- package/agent-hub/dist/rpc.d.ts +38 -0
- package/agent-hub/dist/rpc.js +52 -0
- package/agent-hub/dist/spawn-world.d.ts +46 -0
- package/agent-hub/dist/spawn-world.js +72 -0
- package/agent-hub/dist/targets.d.ts +33 -0
- package/agent-hub/dist/targets.js +30 -0
- package/agent-hub/dist/world-entry.d.ts +31 -0
- package/agent-hub/dist/world-entry.js +51 -0
- package/agent-hub/dist/world-host.d.ts +69 -0
- package/agent-hub/dist/world-host.js +44 -0
- package/agent-hub/dist/world-join.d.ts +16 -0
- package/agent-hub/dist/world-join.js +122 -0
- package/agent-hub/dist/world-mount.d.ts +52 -0
- package/agent-hub/dist/world-mount.js +52 -0
- package/agent-hub/dist/world-mux.d.ts +72 -0
- package/agent-hub/dist/world-mux.js +224 -0
- package/agent-hub/dist/world-provision.d.ts +20 -0
- package/agent-hub/dist/world-provision.js +122 -0
- package/agent-hub/dist/world-web-server.d.ts +162 -0
- package/agent-hub/dist/world-web-server.js +248 -0
- package/agent-hub/lib/client/index.js +242 -0
- package/agent-hub/package.json +65 -0
- package/agent-omp/README.md +77 -0
- package/agent-omp/cordis.patch.yml +92 -0
- package/agent-omp/dist/adapter.js +36 -0
- package/agent-omp/dist/agent-preset-omp.js +171 -0
- package/agent-omp/dist/agent-preset-projection.js +36 -0
- package/agent-omp/dist/agent.js +1145 -0
- package/agent-omp/dist/app-home.js +86 -0
- package/agent-omp/dist/discovery.js +116 -0
- package/agent-omp/dist/inbox.js +53 -0
- package/agent-omp/dist/index.js +574 -0
- package/agent-omp/dist/knobs.js +50 -0
- package/agent-omp/dist/lazy-rpc.js +161 -0
- package/agent-omp/dist/mobile/gesture.js +148 -0
- package/agent-omp/dist/mobile/zoom-guard.js +294 -0
- package/agent-omp/dist/mobile-boot.js +38 -0
- package/agent-omp/dist/models.js +299 -0
- package/agent-omp/dist/omp-cli.js +147 -0
- package/agent-omp/dist/omp-disk-discovery.js +178 -0
- package/agent-omp/dist/omp-store.js +518 -0
- package/agent-omp/dist/pairing.js +45 -0
- package/agent-omp/dist/permission.js +96 -0
- package/agent-omp/dist/protocol.js +13 -0
- package/agent-omp/dist/rpc-types.js +1 -0
- package/agent-omp/dist/sdk-client.js +378 -0
- package/agent-omp/dist/sidecar-client.js +195 -0
- package/agent-omp/dist/store/adapter.js +47 -0
- package/agent-omp/dist/store/db.js +210 -0
- package/agent-omp/dist/store/index.js +59 -0
- package/agent-omp/dist/store/reconcile.js +51 -0
- package/agent-omp/dist/store/schema.js +40 -0
- package/agent-omp/dist/world-plugin.js +96 -0
- package/agent-omp/package.json +97 -0
- package/agent-omp/sidecar/main.ts +481 -0
- package/agent-pi/README.md +67 -0
- package/agent-pi/cordis.patch.yml +80 -0
- package/agent-pi/dist/adapter.js +53 -0
- package/agent-pi/dist/agent-preset-pi.js +169 -0
- package/agent-pi/dist/agent-preset-projection.js +29 -0
- package/agent-pi/dist/agent.js +949 -0
- package/agent-pi/dist/inbox.js +53 -0
- package/agent-pi/dist/index.js +595 -0
- package/agent-pi/dist/knobs.js +24 -0
- package/agent-pi/dist/models.js +200 -0
- package/agent-pi/dist/permission.js +66 -0
- package/agent-pi/dist/pi-client.js +224 -0
- package/agent-pi/dist/pi-events.js +155 -0
- package/agent-pi/dist/pi-home.js +55 -0
- package/agent-pi/dist/safe-json.js +25 -0
- package/agent-pi/dist/session-map.js +119 -0
- package/agent-pi/dist/world-plugin.js +97 -0
- package/agent-pi/package.json +77 -0
- package/cordis.patch.yml +22 -19
- package/package.json +88 -25
- package/client.js +0 -4
|
@@ -0,0 +1,301 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* agy-cli-client.ts — primary live path: the agy CLI headless stream-json
|
|
3
|
+
* session (`agy --print --input-format stream-json --output-format stream-json`).
|
|
4
|
+
*
|
|
5
|
+
* Auth: GEMINI_API_KEY env (adapter stores the key; never written to native
|
|
6
|
+
* home) + `modelProvider: "gemini"` merged into the CLI's own settings file
|
|
7
|
+
* (~/.gemini/antigravity-cli/settings.json — single-key merge, user-sanctioned
|
|
8
|
+
* 2026-09-18). Geofenced deployments pass HTTPS_PROXY via AGY_PROXY.
|
|
9
|
+
*
|
|
10
|
+
* Event mapping (superset of the SDK bridge — CLI is richer):
|
|
11
|
+
* init → tools list + permission mode (trace)
|
|
12
|
+
* step_update → system_message | agent_response (text deltas) |
|
|
13
|
+
* tool (ACTIVE→tool_call, DONE→tool_result w/ output)
|
|
14
|
+
* result → done (conversation_id for resume via --conversation)
|
|
15
|
+
*
|
|
16
|
+
* Same surface as AgyBridgeClient so AgyAgent needs no changes.
|
|
17
|
+
*/
|
|
18
|
+
import { spawn } from "node:child_process";
|
|
19
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
20
|
+
import { join } from "node:path";
|
|
21
|
+
/** Merge `modelProvider: "gemini"` into the CLI's own settings (single key). */
|
|
22
|
+
export function ensureCliKeyMode(nativeCliHome) {
|
|
23
|
+
const dir = join(nativeCliHome, "antigravity-cli");
|
|
24
|
+
mkdirSync(dir, { recursive: true });
|
|
25
|
+
const p = join(dir, "settings.json");
|
|
26
|
+
let settings = {};
|
|
27
|
+
if (existsSync(p)) {
|
|
28
|
+
try {
|
|
29
|
+
settings = JSON.parse(readFileSync(p, "utf8"));
|
|
30
|
+
}
|
|
31
|
+
catch {
|
|
32
|
+
settings = {};
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
if (settings.modelProvider === "gemini")
|
|
36
|
+
return;
|
|
37
|
+
settings.modelProvider = "gemini";
|
|
38
|
+
writeFileSync(p, JSON.stringify(settings, null, 2) + "\n");
|
|
39
|
+
}
|
|
40
|
+
export class AgyCliClient {
|
|
41
|
+
child;
|
|
42
|
+
buffer = "";
|
|
43
|
+
waiters = [];
|
|
44
|
+
pending = [];
|
|
45
|
+
opts;
|
|
46
|
+
dead = false;
|
|
47
|
+
conversationIdValue = "";
|
|
48
|
+
toolsSeen = 0;
|
|
49
|
+
onThinkingDelta;
|
|
50
|
+
onToolEvent;
|
|
51
|
+
setApprovalHandler(_handler) {
|
|
52
|
+
// CLI ask-mode approval is not wired in this iteration (approval_mode
|
|
53
|
+
// stays request-review; tools soft-deny in headless).
|
|
54
|
+
}
|
|
55
|
+
setSystemMessageHandler(handler) {
|
|
56
|
+
this.onSystemMessage = handler;
|
|
57
|
+
}
|
|
58
|
+
onSystemMessage;
|
|
59
|
+
constructor(opts) {
|
|
60
|
+
this.opts = opts;
|
|
61
|
+
}
|
|
62
|
+
get conversationId() {
|
|
63
|
+
return this.conversationIdValue;
|
|
64
|
+
}
|
|
65
|
+
get lastUsage() {
|
|
66
|
+
return this.lastUsageValue;
|
|
67
|
+
}
|
|
68
|
+
lastUsageValue;
|
|
69
|
+
nativeCliHome() {
|
|
70
|
+
return this.opts.nativeCliHome ?? join(process.env.HOME ?? "", ".gemini");
|
|
71
|
+
}
|
|
72
|
+
spawnCli() {
|
|
73
|
+
if (this.child || this.dead)
|
|
74
|
+
return;
|
|
75
|
+
ensureCliKeyMode(this.nativeCliHome());
|
|
76
|
+
const attempts = this.opts.spawnAttempts ?? 3;
|
|
77
|
+
const agyBin = this.opts.agyBin ?? "agy";
|
|
78
|
+
// Order matters: --print swallows the next token as a prompt value
|
|
79
|
+
// (flag-collision, A2A skill) — keep it LAST, after all value flags.
|
|
80
|
+
// = form everywhere: bare value flags swallow the next token (collision).
|
|
81
|
+
const args = [
|
|
82
|
+
"--input-format=stream-json", "--output-format=stream-json",
|
|
83
|
+
"--disable-slash-commands",
|
|
84
|
+
];
|
|
85
|
+
if (this.opts.conversationId)
|
|
86
|
+
args.push(`--conversation=${this.opts.conversationId}`);
|
|
87
|
+
if (this.opts.model)
|
|
88
|
+
args.push(`--model=${this.opts.model}`);
|
|
89
|
+
if (this.opts.effort)
|
|
90
|
+
args.push(`--effort=${this.opts.effort}`);
|
|
91
|
+
if (this.opts.skipPermissions)
|
|
92
|
+
args.push("--dangerously-skip-permissions");
|
|
93
|
+
// NOTE: no --print — stream-json input mode reads prompts from stdin
|
|
94
|
+
// (verified); --print would demand a prompt argument and exit code 2.
|
|
95
|
+
let lastError;
|
|
96
|
+
for (let attempt = 1; attempt <= attempts; attempt++) {
|
|
97
|
+
try {
|
|
98
|
+
const key = typeof this.opts.apiKey === "function" ? this.opts.apiKey() : this.opts.apiKey;
|
|
99
|
+
if (!key)
|
|
100
|
+
throw new Error("agy cli: no GEMINI_API_KEY resolved");
|
|
101
|
+
const env = { ...process.env, GEMINI_API_KEY: key, NO_PROXY: "127.0.0.1,localhost" };
|
|
102
|
+
if (this.opts.home)
|
|
103
|
+
env.HOME = this.opts.home;
|
|
104
|
+
if (this.opts.proxy) {
|
|
105
|
+
env.HTTPS_PROXY = this.opts.proxy;
|
|
106
|
+
env.HTTPS_PROXY = this.opts.proxy;
|
|
107
|
+
}
|
|
108
|
+
this.child = spawn(agyBin, args, { cwd: this.opts.cwd, env, stdio: ["pipe", "pipe", "pipe"] });
|
|
109
|
+
break;
|
|
110
|
+
}
|
|
111
|
+
catch (err) {
|
|
112
|
+
lastError = err;
|
|
113
|
+
this.opts.trace?.(`spawn attempt ${attempt} failed:`, err);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
if (!this.child)
|
|
117
|
+
throw lastError ?? new Error("agy spawn failed");
|
|
118
|
+
const child = this.child;
|
|
119
|
+
child.stdout.setEncoding("utf8");
|
|
120
|
+
child.stdout.on("data", (d) => this.consume(d));
|
|
121
|
+
child.stderr.setEncoding("utf8");
|
|
122
|
+
child.stderr.on("data", (d) => this.opts.trace?.("[agy stderr]", d.trimEnd()));
|
|
123
|
+
child.on("exit", (code) => {
|
|
124
|
+
this.child = undefined;
|
|
125
|
+
this.dispatch({ event: "error", error: `agy exited code=${code}`, recoverable: !this.dead });
|
|
126
|
+
});
|
|
127
|
+
}
|
|
128
|
+
consume(data) {
|
|
129
|
+
this.buffer += data;
|
|
130
|
+
let idx;
|
|
131
|
+
while ((idx = this.buffer.indexOf("\n")) >= 0) {
|
|
132
|
+
const line = this.buffer.slice(0, idx).trim();
|
|
133
|
+
this.buffer = this.buffer.slice(idx + 1);
|
|
134
|
+
if (!line)
|
|
135
|
+
continue;
|
|
136
|
+
let parsed;
|
|
137
|
+
try {
|
|
138
|
+
parsed = JSON.parse(line);
|
|
139
|
+
}
|
|
140
|
+
catch (err) {
|
|
141
|
+
this.opts.trace?.("bad agy json:", line.slice(0, 120), err);
|
|
142
|
+
continue;
|
|
143
|
+
}
|
|
144
|
+
if (parsed.event === "init") {
|
|
145
|
+
const init = parsed.init;
|
|
146
|
+
this.toolsSeen = Array.isArray(init?.tools) ? init.tools.length : 0;
|
|
147
|
+
this.dispatch({ event: "ready", tools: this.toolsSeen, permissionMode: init?.permission_mode });
|
|
148
|
+
}
|
|
149
|
+
else if (parsed.event === "step_update") {
|
|
150
|
+
this.consumeStep(parsed.step_update);
|
|
151
|
+
}
|
|
152
|
+
else if (parsed.event === "result") {
|
|
153
|
+
const r = parsed.result;
|
|
154
|
+
if (r?.conversation_id)
|
|
155
|
+
this.conversationIdValue = r.conversation_id;
|
|
156
|
+
if (r?.status !== "SUCCESS") {
|
|
157
|
+
this.dispatch({ event: "error", error: r?.error ?? `agy result status=${r?.status}`, recoverable: false });
|
|
158
|
+
}
|
|
159
|
+
else {
|
|
160
|
+
this.dispatch({ event: "done", conversation_id: this.conversationIdValue, turn_ms: 0 });
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
consumeStep(su) {
|
|
166
|
+
const st = su.step_type;
|
|
167
|
+
if (st === "system_message") {
|
|
168
|
+
this.onSystemMessage?.(su.text_delta ?? "");
|
|
169
|
+
return;
|
|
170
|
+
}
|
|
171
|
+
if (st === "agent_response") {
|
|
172
|
+
// Short turns carry their delta on the DONE frame — dispatch any delta.
|
|
173
|
+
if (su.text_delta) {
|
|
174
|
+
this.dispatch({ event: "chunk", text: su.text_delta });
|
|
175
|
+
}
|
|
176
|
+
if (su.usage) {
|
|
177
|
+
this.lastUsageValue = {
|
|
178
|
+
input_tokens: su.usage.input_tokens ?? 0,
|
|
179
|
+
output_tokens: su.usage.output_tokens ?? 0,
|
|
180
|
+
thinking_tokens: su.usage.thinking_tokens ?? 0,
|
|
181
|
+
total_tokens: su.usage.total_tokens ?? 0,
|
|
182
|
+
};
|
|
183
|
+
this.dispatch({
|
|
184
|
+
event: "usage",
|
|
185
|
+
input_tokens: this.lastUsageValue.input_tokens,
|
|
186
|
+
output_tokens: this.lastUsageValue.output_tokens,
|
|
187
|
+
thinking_tokens: this.lastUsageValue.thinking_tokens,
|
|
188
|
+
total_tokens: this.lastUsageValue.total_tokens,
|
|
189
|
+
});
|
|
190
|
+
}
|
|
191
|
+
return;
|
|
192
|
+
}
|
|
193
|
+
if (st === "tool") {
|
|
194
|
+
const name = su.tool_name ?? su.tool_info?.name ?? "unknown";
|
|
195
|
+
const id = `${this.conversationIdValue}:${su.step_index ?? 0}`;
|
|
196
|
+
if (su.state === "ACTIVE") {
|
|
197
|
+
this.dispatch({
|
|
198
|
+
event: "tool_call", id, name,
|
|
199
|
+
args: JSON.stringify(su.tool_info?.parameters ?? {}),
|
|
200
|
+
});
|
|
201
|
+
this.onToolEvent?.({ event: "tool_call", id, name, args: JSON.stringify(su.tool_info?.parameters ?? {}) });
|
|
202
|
+
}
|
|
203
|
+
else if (su.state === "DONE" || su.state === "ERROR") {
|
|
204
|
+
const output = su.tool_info?.output ?? "";
|
|
205
|
+
const isError = su.state === "ERROR";
|
|
206
|
+
this.dispatch({
|
|
207
|
+
event: "tool_result", id, name, is_error: isError,
|
|
208
|
+
result: output || (isError ? "tool failed" : "[agy CLI reported no output summary]"),
|
|
209
|
+
});
|
|
210
|
+
this.onToolEvent?.({
|
|
211
|
+
event: "tool_result", id, name, is_error: isError,
|
|
212
|
+
result: output || (isError ? "tool failed" : "[agy CLI reported no output summary]"),
|
|
213
|
+
});
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
dispatch(ev) {
|
|
218
|
+
const waiter = this.waiters.shift();
|
|
219
|
+
if (waiter)
|
|
220
|
+
waiter(ev);
|
|
221
|
+
else
|
|
222
|
+
this.pending.push(ev);
|
|
223
|
+
}
|
|
224
|
+
nextEvent() {
|
|
225
|
+
const pending = this.pending.shift();
|
|
226
|
+
if (pending)
|
|
227
|
+
return Promise.resolve(pending);
|
|
228
|
+
return new Promise((resolve) => this.waiters.push(resolve));
|
|
229
|
+
}
|
|
230
|
+
send(obj) {
|
|
231
|
+
if (!this.child)
|
|
232
|
+
throw new Error("agy not running");
|
|
233
|
+
this.child.stdin.write(JSON.stringify(obj) + "\n");
|
|
234
|
+
}
|
|
235
|
+
/** One turn. First call spawns the CLI (lazy per §14-i) and keeps it resident. */
|
|
236
|
+
async *turn(text) {
|
|
237
|
+
this.spawnCli();
|
|
238
|
+
await this.expectReady();
|
|
239
|
+
this.send({ event: "user", message: { content: text } });
|
|
240
|
+
let full = "";
|
|
241
|
+
for (;;) {
|
|
242
|
+
const ev = await this.nextEvent();
|
|
243
|
+
if (ev.event === "chunk") {
|
|
244
|
+
full += ev.text;
|
|
245
|
+
yield ev.text;
|
|
246
|
+
}
|
|
247
|
+
else if (ev.event === "system_message") {
|
|
248
|
+
this.onSystemMessage?.(ev.text);
|
|
249
|
+
}
|
|
250
|
+
else if (ev.event === "tool_call" || ev.event === "tool_result") {
|
|
251
|
+
this.onToolEvent?.(ev);
|
|
252
|
+
}
|
|
253
|
+
else if (ev.event === "usage") {
|
|
254
|
+
this.lastUsageValue = {
|
|
255
|
+
input_tokens: ev.input_tokens, output_tokens: ev.output_tokens,
|
|
256
|
+
thinking_tokens: ev.thinking_tokens, total_tokens: ev.total_tokens,
|
|
257
|
+
};
|
|
258
|
+
}
|
|
259
|
+
else if (ev.event === "done") {
|
|
260
|
+
return full;
|
|
261
|
+
}
|
|
262
|
+
else if (ev.event === "error") {
|
|
263
|
+
throw new Error(`agy cli: ${ev.error}`);
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
readyReceived = false;
|
|
268
|
+
async expectReady() {
|
|
269
|
+
if (this.readyReceived)
|
|
270
|
+
return;
|
|
271
|
+
for (;;) {
|
|
272
|
+
const ev = await this.nextEvent();
|
|
273
|
+
if (ev.event === "ready") {
|
|
274
|
+
this.readyReceived = true;
|
|
275
|
+
return;
|
|
276
|
+
}
|
|
277
|
+
if (ev.event === "error")
|
|
278
|
+
throw new Error(`agy cli: ${ev.error}`);
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
async ping() {
|
|
282
|
+
this.spawnCli();
|
|
283
|
+
await this.expectReady();
|
|
284
|
+
}
|
|
285
|
+
async close() {
|
|
286
|
+
if (!this.child)
|
|
287
|
+
return;
|
|
288
|
+
this.dead = true;
|
|
289
|
+
const child = this.child;
|
|
290
|
+
try {
|
|
291
|
+
child.stdin.end();
|
|
292
|
+
}
|
|
293
|
+
catch { /* already gone */ }
|
|
294
|
+
setTimeout(() => {
|
|
295
|
+
if (!child.killed)
|
|
296
|
+
child.kill("SIGKILL");
|
|
297
|
+
}, 2000).unref();
|
|
298
|
+
await new Promise((resolve) => child.on("exit", () => resolve()));
|
|
299
|
+
this.child = undefined;
|
|
300
|
+
}
|
|
301
|
+
}
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* agy-client.ts — drives the Python SDK bridge (bridge/agy_bridge.py) over
|
|
3
|
+
* JSONL stdio.
|
|
4
|
+
*
|
|
5
|
+
* Discipline (agent-adapter-dev-rules §14-i / §16a):
|
|
6
|
+
* - LAZY: no python child until the first real prompt. `start` config is
|
|
7
|
+
* buffered and flushed with the first `prompt` (one round trip).
|
|
8
|
+
* - Transient spawn failures retry in-place up to AGY_SPAWN_ATTEMPTS (3);
|
|
9
|
+
* prompt-level errors are surfaced, never auto-retried.
|
|
10
|
+
* - The agy CLI is never invoked; auth is transparent (api_key param or ADC
|
|
11
|
+
* environment picked up by the SDK itself).
|
|
12
|
+
*/
|
|
13
|
+
export interface AgyClientOptions {
|
|
14
|
+
/** Gemini API key; omit to let the SDK fall back to ADC. */
|
|
15
|
+
apiKey?: string;
|
|
16
|
+
model?: string;
|
|
17
|
+
/** Thinking level for the start frame (undefined = endpoint default). */
|
|
18
|
+
thinkingLevel?: string;
|
|
19
|
+
/** Tool-approval posture: "allow" (default) auto-approves; "ask" defers to
|
|
20
|
+
* the onApprovalRequest handler (fail closed). */
|
|
21
|
+
approvalMode?: "allow" | "ask";
|
|
22
|
+
project?: string;
|
|
23
|
+
location?: string;
|
|
24
|
+
workspaces?: string[];
|
|
25
|
+
saveDir?: string;
|
|
26
|
+
/** Python interpreter (test knob; prod resolves via PATH). */
|
|
27
|
+
pythonBin?: string;
|
|
28
|
+
/** Bridge script override (tests point this at a mock). */
|
|
29
|
+
bridgePath?: string;
|
|
30
|
+
spawnAttempts?: number;
|
|
31
|
+
trace?: (...parts: unknown[]) => void;
|
|
32
|
+
/**
|
|
33
|
+
* Host-side approval handler for approval_mode "ask": resolves true=allow.
|
|
34
|
+
* NEVER consulted in "allow" mode. Fail-closed lives with the handler.
|
|
35
|
+
*/
|
|
36
|
+
onApprovalRequest?: (req: {
|
|
37
|
+
id: string;
|
|
38
|
+
tool: string;
|
|
39
|
+
args: string;
|
|
40
|
+
}) => Promise<boolean>;
|
|
41
|
+
}
|
|
42
|
+
export interface AgyTurnUsage {
|
|
43
|
+
input_tokens: number;
|
|
44
|
+
output_tokens: number;
|
|
45
|
+
thinking_tokens: number;
|
|
46
|
+
total_tokens: number;
|
|
47
|
+
}
|
|
48
|
+
export type BridgeEvent = {
|
|
49
|
+
event: "pong";
|
|
50
|
+
} | {
|
|
51
|
+
event: "started";
|
|
52
|
+
} | {
|
|
53
|
+
event: "chunk";
|
|
54
|
+
text: string;
|
|
55
|
+
} | {
|
|
56
|
+
event: "thinking";
|
|
57
|
+
text: string;
|
|
58
|
+
} | {
|
|
59
|
+
event: "tool";
|
|
60
|
+
name: string;
|
|
61
|
+
} | {
|
|
62
|
+
event: "tool_call";
|
|
63
|
+
id: string;
|
|
64
|
+
name: string;
|
|
65
|
+
args: string;
|
|
66
|
+
} | {
|
|
67
|
+
event: "tool_result";
|
|
68
|
+
id: string;
|
|
69
|
+
name: string;
|
|
70
|
+
is_error: boolean;
|
|
71
|
+
result: string;
|
|
72
|
+
} | {
|
|
73
|
+
event: "usage";
|
|
74
|
+
input_tokens: number;
|
|
75
|
+
output_tokens: number;
|
|
76
|
+
thinking_tokens: number;
|
|
77
|
+
total_tokens: number;
|
|
78
|
+
} | {
|
|
79
|
+
event: "approval_request";
|
|
80
|
+
id: string;
|
|
81
|
+
tool: string;
|
|
82
|
+
args: string;
|
|
83
|
+
} | {
|
|
84
|
+
event: "done";
|
|
85
|
+
conversation_id: string;
|
|
86
|
+
turn_ms: number;
|
|
87
|
+
} | {
|
|
88
|
+
event: "closed";
|
|
89
|
+
} | {
|
|
90
|
+
event: "error";
|
|
91
|
+
error: string;
|
|
92
|
+
recoverable: boolean;
|
|
93
|
+
};
|
|
94
|
+
export declare class AgyBridgeClient {
|
|
95
|
+
private child?;
|
|
96
|
+
private buffer;
|
|
97
|
+
private readonly waiters;
|
|
98
|
+
private readonly pending;
|
|
99
|
+
private readonly opts;
|
|
100
|
+
private spawnFailures;
|
|
101
|
+
/** Thinking-delta sink (agent buffers these into a reasoning block). */
|
|
102
|
+
onThinkingDelta?: (text: string) => void;
|
|
103
|
+
/** Tool lifecycle sink: tool_call / tool_result (agent writes DSH events). */
|
|
104
|
+
onToolEvent?: (ev: {
|
|
105
|
+
event: "tool_call";
|
|
106
|
+
id: string;
|
|
107
|
+
name: string;
|
|
108
|
+
args: string;
|
|
109
|
+
} | {
|
|
110
|
+
event: "tool_result";
|
|
111
|
+
id: string;
|
|
112
|
+
name: string;
|
|
113
|
+
is_error: boolean;
|
|
114
|
+
result: string;
|
|
115
|
+
}) => void;
|
|
116
|
+
/** Bridge-protocol parity for the agent test harness (fake clients). */
|
|
117
|
+
setToolEventHandler?(handler: (ev: {
|
|
118
|
+
event: "tool_call";
|
|
119
|
+
id: string;
|
|
120
|
+
name: string;
|
|
121
|
+
args: string;
|
|
122
|
+
} | {
|
|
123
|
+
event: "tool_result";
|
|
124
|
+
id: string;
|
|
125
|
+
name: string;
|
|
126
|
+
is_error: boolean;
|
|
127
|
+
result: string;
|
|
128
|
+
}) => void): void;
|
|
129
|
+
private dead;
|
|
130
|
+
private startedSent;
|
|
131
|
+
private lastUsageValue;
|
|
132
|
+
/** Per-turn token usage from the bridge (undefined when the bridge skipped it). */
|
|
133
|
+
get lastUsage(): AgyTurnUsage | undefined;
|
|
134
|
+
/** Register the ask-mode approval handler (agent wires its approval service). */
|
|
135
|
+
setApprovalHandler(handler: NonNullable<AgyClientOptions["onApprovalRequest"]>): void;
|
|
136
|
+
private conversationIdValue;
|
|
137
|
+
constructor(opts?: AgyClientOptions);
|
|
138
|
+
get conversationId(): string;
|
|
139
|
+
private resolveBridgePath;
|
|
140
|
+
/** Durable venv created by scripts/setup-venv.sh (pkg-root/.venv). */
|
|
141
|
+
private resolvePackagedVenvPython;
|
|
142
|
+
private spawnBridge;
|
|
143
|
+
private consume;
|
|
144
|
+
private dispatch;
|
|
145
|
+
private nextEvent;
|
|
146
|
+
private send;
|
|
147
|
+
/** Wait for a specific event kind, surfacing `error` events as throws. */
|
|
148
|
+
private expect;
|
|
149
|
+
/** Ping round trip (spawns the bridge if needed — use for liveness probes). */
|
|
150
|
+
ping(): Promise<void>;
|
|
151
|
+
/**
|
|
152
|
+
* Run one turn. First call materializes the bridge child and sends the
|
|
153
|
+
* buffered `start` config followed by `prompt` (lazy spawn per §14-i).
|
|
154
|
+
* Yields streamed text chunks; resolves with the full turn text.
|
|
155
|
+
*/
|
|
156
|
+
turn(text: string): AsyncGenerator<string, string, void>;
|
|
157
|
+
close(): Promise<void>;
|
|
158
|
+
}
|
|
159
|
+
/** Convenience: read the venv python path recorded by scripts/setup-venv.sh. */
|
|
160
|
+
export declare function resolveVenvPython(worldHome: string): string | undefined;
|