@guuey/host 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +47 -0
- package/dist/agent-entry.d.ts +38 -0
- package/dist/agent-entry.d.ts.map +1 -0
- package/dist/agent-entry.js +87 -0
- package/dist/boot-context.d.ts +50 -0
- package/dist/boot-context.d.ts.map +1 -0
- package/dist/boot-context.js +22 -0
- package/dist/creds.d.ts +26 -0
- package/dist/creds.d.ts.map +1 -0
- package/dist/creds.js +45 -0
- package/dist/frameworks/claude-options.d.ts +149 -0
- package/dist/frameworks/claude-options.d.ts.map +1 -0
- package/dist/frameworks/claude-options.js +208 -0
- package/dist/frameworks/claude-runner.d.ts +3 -0
- package/dist/frameworks/claude-runner.d.ts.map +1 -0
- package/dist/frameworks/claude-runner.js +33 -0
- package/dist/frameworks/claude.d.ts +72 -0
- package/dist/frameworks/claude.d.ts.map +1 -0
- package/dist/frameworks/claude.js +80 -0
- package/dist/frameworks/google-adk.d.ts +105 -0
- package/dist/frameworks/google-adk.d.ts.map +1 -0
- package/dist/frameworks/google-adk.js +269 -0
- package/dist/frameworks/openai-runner.d.ts +3 -0
- package/dist/frameworks/openai-runner.d.ts.map +1 -0
- package/dist/frameworks/openai-runner.js +37 -0
- package/dist/frameworks/openai.d.ts +65 -0
- package/dist/frameworks/openai.d.ts.map +1 -0
- package/dist/frameworks/openai.js +207 -0
- package/dist/index.d.ts +31 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +134 -0
- package/dist/options.d.ts +173 -0
- package/dist/options.d.ts.map +1 -0
- package/dist/options.js +248 -0
- package/dist/preamble.d.ts +16 -0
- package/dist/preamble.d.ts.map +1 -0
- package/dist/preamble.js +34 -0
- package/dist/run-openai.d.ts +65 -0
- package/dist/run-openai.d.ts.map +1 -0
- package/dist/run-openai.js +207 -0
- package/dist/run.d.ts +72 -0
- package/dist/run.d.ts.map +1 -0
- package/dist/run.js +80 -0
- package/dist/sdk-version.d.ts +5 -0
- package/dist/sdk-version.d.ts.map +1 -0
- package/dist/sdk-version.js +56 -0
- package/package.json +78 -0
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
import { GUUEY_DEFAULT_SYSTEM_PROMPT, defaultModelFor } from "@guuey/config";
|
|
2
|
+
/**
|
|
3
|
+
* Default Claude model — only used when the snapshot omits `model`. Derived
|
|
4
|
+
* from the `@guuey/config` registry (single source of truth per the
|
|
5
|
+
* model-release playbook §8 item A) rather than a bare literal, so a
|
|
6
|
+
* registry default change propagates here automatically.
|
|
7
|
+
*/
|
|
8
|
+
const DEFAULT_MODEL = defaultModelFor("claude-agent-sdk");
|
|
9
|
+
/** Default cap on agent-loop turns per user message (matches the SDK/runner default). */
|
|
10
|
+
const DEFAULT_MAX_TURNS = 25;
|
|
11
|
+
/**
|
|
12
|
+
* Env-var names the Router injects so agent code reaches the home/app layers
|
|
13
|
+
* portably. Host-owned copies of `@guuey/fs`'s `ENV_HOME_DIR`/`ENV_APP_DIR`
|
|
14
|
+
* (trivial string literals — not imported, to keep this package OSS-legal).
|
|
15
|
+
*/
|
|
16
|
+
export const ENV_HOME_DIR = "GUUEY_HOME_DIR";
|
|
17
|
+
export const ENV_APP_DIR = "GUUEY_APP_DIR";
|
|
18
|
+
/**
|
|
19
|
+
* File tools enabled when GuueyFS layers are bound. `Bash` is added separately
|
|
20
|
+
* (see {@link BASH_TOOL}) so the two are independently testable.
|
|
21
|
+
*/
|
|
22
|
+
const FS_TOOLS = ["Read", "Write", "Edit", "Glob", "Grep"];
|
|
23
|
+
/**
|
|
24
|
+
* Real shell exec, enabled alongside the file tools when GuueyFS layers are
|
|
25
|
+
* bound. Unlike the source runner — which enabled `Bash` only when the SDK's OWN
|
|
26
|
+
* `sandbox:{}` block was on — this host runs entirely INSIDE the Router's
|
|
27
|
+
* bubblewrap jail, so the OS isolation is always present whenever fs is bound.
|
|
28
|
+
* The SDK `sandbox:{}` block is therefore NOT set here (it would spawn a nested
|
|
29
|
+
* bubblewrap inside the Router's bwrap); the Router's bwrap IS the isolation.
|
|
30
|
+
*/
|
|
31
|
+
const BASH_TOOL = "Bash";
|
|
32
|
+
/**
|
|
33
|
+
* Build the Claude Agent SDK `Options` for one invoke. Pure: all disk/env access
|
|
34
|
+
* is injected via {@link BuildOptionsContext}. Throws on an unresolved `{file}`
|
|
35
|
+
* system prompt or a missing API key (the same loud failures the source had).
|
|
36
|
+
*/
|
|
37
|
+
export function buildOptions(snapshot, ctx) {
|
|
38
|
+
const apiKey = ctx.apiKey;
|
|
39
|
+
const baseUrl = ctx.baseUrl;
|
|
40
|
+
const authToken = ctx.authToken;
|
|
41
|
+
// Require either the loopback proxy credentials (hosted/broker path) or a
|
|
42
|
+
// direct API key (local-dev fallback). Neither → fail loudly.
|
|
43
|
+
if (!((baseUrl !== undefined && authToken !== undefined) || apiKey)) {
|
|
44
|
+
throw new Error("@guuey/host: either (baseUrl + authToken) for the managed-LLM proxy, " +
|
|
45
|
+
"or ANTHROPIC_API_KEY for local dev, is required.");
|
|
46
|
+
}
|
|
47
|
+
// Snapshots reach the worker fully resolved — the CLI's loader inlines any
|
|
48
|
+
// `{file}` system prompt before upload. A `{file}` reaching the worker means
|
|
49
|
+
// someone hit the API directly with an un-resolved snapshot; reject loudly.
|
|
50
|
+
if (snapshot.systemPrompt !== undefined && typeof snapshot.systemPrompt !== "string") {
|
|
51
|
+
throw new Error(`@guuey/host: snapshot.systemPrompt must be a resolved string (got ${JSON.stringify(snapshot.systemPrompt)}). The CLI inlines {file} references before upload; workers never read the filesystem.`);
|
|
52
|
+
}
|
|
53
|
+
const mcpServers = resolveMcpServers(ctx);
|
|
54
|
+
const allowedTools = buildAllowedTools(snapshot, Object.keys(mcpServers), Boolean(ctx.fs));
|
|
55
|
+
const systemPrompt = withContextPreamble(snapshot.systemPrompt ?? GUUEY_DEFAULT_SYSTEM_PROMPT, ctx.history, ctx.priorMemory, ctx.priorState);
|
|
56
|
+
const model = snapshot.model ?? DEFAULT_MODEL;
|
|
57
|
+
const maxTurns = snapshot.runtime?.maxTurns ?? DEFAULT_MAX_TURNS;
|
|
58
|
+
const fs = ctx.fs;
|
|
59
|
+
// Build the subprocess env. Two mutually-exclusive paths:
|
|
60
|
+
//
|
|
61
|
+
// - Proxy path (baseUrl + authToken present): route the Claude CLI
|
|
62
|
+
// subprocess through the managed-LLM broker. baseUrl + authToken are
|
|
63
|
+
// spread LAST so a builder's snapshot.env cannot override them.
|
|
64
|
+
// ANTHROPIC_API_KEY is intentionally absent — the proxy owns auth.
|
|
65
|
+
// CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC suppresses telemetry pings
|
|
66
|
+
// that would bypass the proxy.
|
|
67
|
+
//
|
|
68
|
+
// - Local-dev fallback (only apiKey present): pass the real key directly.
|
|
69
|
+
// The guard above guarantees apiKey is non-null on this branch.
|
|
70
|
+
// Explicit Record<string, string> annotation prevents TypeScript from widening
|
|
71
|
+
// the ternary to a union `{ K: string } | {}`, which would make spread targets
|
|
72
|
+
// produce optional-undefined keys that conflict with Record<string, string>.
|
|
73
|
+
const fsEnv = fs
|
|
74
|
+
? { [ENV_HOME_DIR]: fs.home, [ENV_APP_DIR]: fs.app }
|
|
75
|
+
: {};
|
|
76
|
+
let env;
|
|
77
|
+
if (baseUrl !== undefined && authToken !== undefined) {
|
|
78
|
+
env = {
|
|
79
|
+
...(snapshot.env ?? {}),
|
|
80
|
+
...fsEnv,
|
|
81
|
+
ANTHROPIC_BASE_URL: baseUrl,
|
|
82
|
+
ANTHROPIC_AUTH_TOKEN: authToken,
|
|
83
|
+
CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC: "1",
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
else {
|
|
87
|
+
// apiKey is guaranteed non-null by the guard; the if-check narrows the type.
|
|
88
|
+
if (!apiKey)
|
|
89
|
+
throw new Error("unreachable: apiKey guard should have prevented this path");
|
|
90
|
+
env = {
|
|
91
|
+
ANTHROPIC_API_KEY: apiKey,
|
|
92
|
+
...(snapshot.env ?? {}),
|
|
93
|
+
...fsEnv,
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
// Whether the operator pinned a Claude permission mode in agent.json. When
|
|
97
|
+
// set we forward it verbatim and let the SDK's mode govern the posture; when
|
|
98
|
+
// unset we install the auto-allow `canUseTool` below so the default no-code
|
|
99
|
+
// agent's Bash runs prompt-free (a never-answered prompt would hang the pod).
|
|
100
|
+
const explicitMode = snapshot.claude?.permissions?.mode;
|
|
101
|
+
const options = {
|
|
102
|
+
model,
|
|
103
|
+
mcpServers,
|
|
104
|
+
allowedTools,
|
|
105
|
+
// With GuueyFS layers bound, expose the file tools PLUS real `Bash`; without
|
|
106
|
+
// them this is byte-identical to the source (purely MCP-driven). `Bash` is
|
|
107
|
+
// safe here because the host already runs inside the Router's bubblewrap
|
|
108
|
+
// jail — that bwrap, NOT the SDK's own `sandbox:{}` block, is the isolation.
|
|
109
|
+
tools: fs ? [...FS_TOOLS, BASH_TOOL] : [],
|
|
110
|
+
// Settings isolation. Empty array = "no filesystem settings loaded" — guards
|
|
111
|
+
// against a future SDK change auto-pulling `~/.claude/settings.json` and
|
|
112
|
+
// leaking the operator's logged-in Claude Code MCPs into the tool catalog.
|
|
113
|
+
settingSources: [],
|
|
114
|
+
strictMcpConfig: true,
|
|
115
|
+
maxTurns,
|
|
116
|
+
env,
|
|
117
|
+
systemPrompt,
|
|
118
|
+
// GuueyFS binding (opt-in): session dir as cwd, home+app as extra roots.
|
|
119
|
+
...(fs ? { cwd: fs.session, additionalDirectories: [fs.home, fs.app] } : {}),
|
|
120
|
+
// Permission posture. Two mutually-exclusive paths:
|
|
121
|
+
//
|
|
122
|
+
// - Operator pinned `claude.permissions.mode` → forward it verbatim; the
|
|
123
|
+
// operator owns the posture (e.g. `acceptEdits`).
|
|
124
|
+
// - No explicit mode + fs bound → install an auto-allow `canUseTool`. In
|
|
125
|
+
// the SDK permission flow (hooks → deny → allow → ask → mode/canUseTool),
|
|
126
|
+
// `default` mode with no callback routes Bash subcommands through an
|
|
127
|
+
// interactive permission prompt — which, in this headless ephemeral pod,
|
|
128
|
+
// no one answers, so the agent would HANG. The auto-allow callback short-
|
|
129
|
+
// circuits that: every tool the model picks (already constrained to
|
|
130
|
+
// `tools`/`allowedTools` + `settingSources:[]` + `strictMcpConfig`) is
|
|
131
|
+
// allowed without a prompt. This is safe precisely because the Router's
|
|
132
|
+
// bubblewrap jail is the real isolation boundary — NOT the SDK's own
|
|
133
|
+
// `sandbox:{}` block (which is intentionally absent to avoid a nested
|
|
134
|
+
// bwrap inside the Router's bwrap). We do NOT use `bypassPermissions`
|
|
135
|
+
// here: it requires `allowDangerouslySkipPermissions` and globally
|
|
136
|
+
// disables hooks/deny-rule evaluation, whereas the callback keeps the
|
|
137
|
+
// deny/hook stages intact while only collapsing the final ask stage.
|
|
138
|
+
...(explicitMode
|
|
139
|
+
? { permissionMode: explicitMode }
|
|
140
|
+
: fs
|
|
141
|
+
? { canUseTool: autoAllowTool }
|
|
142
|
+
: {}),
|
|
143
|
+
...(ctx.abortController ? { abortController: ctx.abortController } : {}),
|
|
144
|
+
};
|
|
145
|
+
return options;
|
|
146
|
+
}
|
|
147
|
+
/**
|
|
148
|
+
* Auto-allow permission callback. Installed when fs is bound and the operator
|
|
149
|
+
* did NOT pin `claude.permissions.mode`, so the default no-code agent's `Bash`
|
|
150
|
+
* (and the file tools) run prompt-free. Returns `{ behavior: 'allow' }` for
|
|
151
|
+
* every request, passing the input through unchanged.
|
|
152
|
+
*
|
|
153
|
+
* Safe because the model's tool surface is already locked down BEFORE the
|
|
154
|
+
* callback ever fires — `tools`/`allowedTools` cap which tools exist,
|
|
155
|
+
* `settingSources:[]` blocks filesystem-loaded settings, `strictMcpConfig`
|
|
156
|
+
* pins the MCP catalog — and the real OS isolation is the Router's bubblewrap
|
|
157
|
+
* jail this whole process runs inside. The callback only collapses the SDK's
|
|
158
|
+
* final interactive "ask" stage (which would otherwise hang a headless pod);
|
|
159
|
+
* the earlier hook/deny-rule stages of the permission flow still run.
|
|
160
|
+
*/
|
|
161
|
+
export const autoAllowTool = (_toolName, input) => Promise.resolve({ behavior: "allow", updatedInput: input });
|
|
162
|
+
/**
|
|
163
|
+
* Map the Router-resolved cred files to the framework-neutral SdkMcpServer map.
|
|
164
|
+
* The Router (credential-broker) owns ALL resolution — default, federation, mint,
|
|
165
|
+
* env-substitution; this worker just reads `<session>/.guuey/credentials/*.json`
|
|
166
|
+
* (via ctx.listCredentials) and shapes each entry. Keyed by the server name.
|
|
167
|
+
*/
|
|
168
|
+
export function resolveMcpServers(ctx) {
|
|
169
|
+
const out = {};
|
|
170
|
+
for (const { name, cred } of ctx.listCredentials()) {
|
|
171
|
+
out[name] = {
|
|
172
|
+
type: cred.transport,
|
|
173
|
+
url: cred.url,
|
|
174
|
+
// Declared MCP servers ARE this agent's tool surface. Without
|
|
175
|
+
// alwaysLoad the CLI defers MCP tools behind its ToolSearch built-in —
|
|
176
|
+
// absent here (tools: []) — leaving the model tool-less. Empirically
|
|
177
|
+
// load-bearing; mirrors the scaffold template's worker (see
|
|
178
|
+
// create-agentic-app templates-src claude worker.ts).
|
|
179
|
+
alwaysLoad: true,
|
|
180
|
+
...(Object.keys(cred.headers).length > 0 ? { headers: cred.headers } : {}),
|
|
181
|
+
};
|
|
182
|
+
}
|
|
183
|
+
return out;
|
|
184
|
+
}
|
|
185
|
+
/**
|
|
186
|
+
* Build the SDK's `allowedTools` array. MCP tools are auto-namespaced by the SDK
|
|
187
|
+
* as `mcp__<server>__<tool>`; we can't enumerate the namespaces ahead of time,
|
|
188
|
+
* so the allowlist is necessarily wildcard-ish.
|
|
189
|
+
*
|
|
190
|
+
* - explicit `tools.allowlist` → pass those literal names through.
|
|
191
|
+
* - else → allow every tool from every declared server via `mcp__<server>`.
|
|
192
|
+
*
|
|
193
|
+
* When GuueyFS layers are bound, the file tools AND `Bash` join the allowlist
|
|
194
|
+
* (an allow rule in the SDK permission flow), so the model may use them
|
|
195
|
+
* alongside the MCP allowlist. The auto-allow `canUseTool` (or the operator's
|
|
196
|
+
* pinned mode) governs whether those still prompt — see `buildOptions`.
|
|
197
|
+
*/
|
|
198
|
+
function buildAllowedTools(snapshot, declaredServerNames, fsBound) {
|
|
199
|
+
const explicit = snapshot.tools?.allowlist;
|
|
200
|
+
const base = explicit && explicit.length > 0
|
|
201
|
+
? explicit.slice()
|
|
202
|
+
: declaredServerNames.map((s) => `mcp__${s}`);
|
|
203
|
+
return fsBound ? [...base, ...FS_TOOLS, BASH_TOOL] : base;
|
|
204
|
+
}
|
|
205
|
+
// withContextPreamble now lives in ../preamble.js (framework-neutral — the
|
|
206
|
+
// ADK runner renders the same preamble); re-exported for existing importers.
|
|
207
|
+
export { withContextPreamble } from "../preamble.js";
|
|
208
|
+
import { withContextPreamble } from "../preamble.js";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"claude-runner.d.ts","sourceRoot":"","sources":["../../src/frameworks/claude-runner.ts"],"names":[],"mappings":"AAiBA,OAAO,KAAK,EAAE,eAAe,EAA0B,MAAM,aAAa,CAAC;AAK3E,wBAAgB,YAAY,IAAI,eAAe,CAa9C"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Claude runner module — the `FrameworkRunner` adapter around the existing
|
|
3
|
+
* claude turn loop (`claude.ts#runInvoke`). Imports the SDK at module top
|
|
4
|
+
* level so the host's lazy `import()` of this file is what pulls the
|
|
5
|
+
* optional peer.
|
|
6
|
+
*
|
|
7
|
+
* Credential posture (unchanged from the pre-restructure host):
|
|
8
|
+
* - hosted/broker: `ANTHROPIC_BASE_URL` + `ANTHROPIC_AUTH_TOKEN` injected by
|
|
9
|
+
* the Router's `buildWorkerEnv`; the real `ANTHROPIC_API_KEY` is
|
|
10
|
+
* intentionally absent so it cannot leak to agent code.
|
|
11
|
+
* - local-dev: only `ANTHROPIC_API_KEY` is set; `buildOptions` falls back to
|
|
12
|
+
* the direct-key path.
|
|
13
|
+
* A missing key is NOT fatal at boot — the run path emits a clear `error`
|
|
14
|
+
* per invoke if neither form is present.
|
|
15
|
+
*/
|
|
16
|
+
import { query } from "@anthropic-ai/claude-agent-sdk";
|
|
17
|
+
import { runInvoke } from "./claude.js";
|
|
18
|
+
import { listCredentials } from "../creds.js";
|
|
19
|
+
import { buildHostContext } from "../boot-context.js";
|
|
20
|
+
export function createRunner() {
|
|
21
|
+
const bootCtx = buildHostContext(process.env);
|
|
22
|
+
return {
|
|
23
|
+
async runTurn(snapshot, turn, emit) {
|
|
24
|
+
const runtime = {
|
|
25
|
+
listCredentials: listCredentials(turn.fs),
|
|
26
|
+
...(bootCtx.anthropicApiKey !== undefined ? { apiKey: bootCtx.anthropicApiKey } : {}),
|
|
27
|
+
...(bootCtx.anthropicBaseUrl !== undefined ? { baseUrl: bootCtx.anthropicBaseUrl } : {}),
|
|
28
|
+
...(bootCtx.anthropicAuthToken !== undefined ? { authToken: bootCtx.anthropicAuthToken } : {}),
|
|
29
|
+
};
|
|
30
|
+
await runInvoke(snapshot, turn, runtime, emit, query);
|
|
31
|
+
},
|
|
32
|
+
};
|
|
33
|
+
}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The testable core of the worker loop. `runInvoke` runs one invoke through the
|
|
3
|
+
* Claude Agent SDK and emits each native `SDKMessage` to fd-3 as a `native`
|
|
4
|
+
* WorkerEvent (the Router dispatches them to the matching normalizer). On the
|
|
5
|
+
* SDK result message it emits `done`; on a throw or a framework-gate violation
|
|
6
|
+
* it emits `error`. Emits `hello` FIRST (§8 item B) — the SDK-version handshake.
|
|
7
|
+
*
|
|
8
|
+
* `query` is injected so the loop is unit-testable without a live model. The
|
|
9
|
+
* worker entrypoint (`index.ts`) passes the real `@anthropic-ai/claude-agent-sdk`
|
|
10
|
+
* `query`.
|
|
11
|
+
*/
|
|
12
|
+
import type { Options, SDKMessage } from "@anthropic-ai/claude-agent-sdk";
|
|
13
|
+
import type { Emitter, Fs, HistoryMessage, Identity, JsonValue } from "@guuey/worker";
|
|
14
|
+
import { buildOptions, type CredentialFile, type PriorMemoryRecord } from "./claude-options.js";
|
|
15
|
+
/**
|
|
16
|
+
* The invoke this host consumes. A superset of `@guuey/worker`'s `Invoke`:
|
|
17
|
+
* `priorMemory`/`priorState` are the §1.4 push-by-value context the worker reads
|
|
18
|
+
* for the preamble (the Worker Protocol `Invoke` is EXTENDED with these in
|
|
19
|
+
* Task 3; until then the worker loop parses them off the raw control line).
|
|
20
|
+
*/
|
|
21
|
+
export interface HostInvoke {
|
|
22
|
+
input: string;
|
|
23
|
+
identity: Identity;
|
|
24
|
+
fs: Fs;
|
|
25
|
+
history: HistoryMessage[];
|
|
26
|
+
priorMemory?: PriorMemoryRecord[];
|
|
27
|
+
priorState?: JsonValue;
|
|
28
|
+
}
|
|
29
|
+
/** Per-process config the worker resolves once at boot. */
|
|
30
|
+
export interface HostRuntime {
|
|
31
|
+
/**
|
|
32
|
+
* Anthropic API key — local-dev fallback when `baseUrl`+`authToken` are
|
|
33
|
+
* absent. One of (`baseUrl`+`authToken`) or `apiKey` must be provided;
|
|
34
|
+
* `buildOptions` throws at invoke time if neither is present.
|
|
35
|
+
*/
|
|
36
|
+
apiKey?: string;
|
|
37
|
+
/**
|
|
38
|
+
* Loopback proxy base URL (hosted/broker mode). Task 8 injects this as
|
|
39
|
+
* `ANTHROPIC_BASE_URL` via `buildWorkerEnv`. When set together with
|
|
40
|
+
* `authToken`, the Claude CLI subprocess routes through the managed-LLM
|
|
41
|
+
* broker; the real API key is intentionally absent to prevent leaks.
|
|
42
|
+
*/
|
|
43
|
+
baseUrl?: string;
|
|
44
|
+
/**
|
|
45
|
+
* Opaque session token for the loopback proxy (hosted/broker mode). Task 8
|
|
46
|
+
* injects this as `ANTHROPIC_AUTH_TOKEN`. Required when `baseUrl` is set.
|
|
47
|
+
*/
|
|
48
|
+
authToken?: string;
|
|
49
|
+
/**
|
|
50
|
+
* Returns every credential the Router broker wrote to
|
|
51
|
+
* `<sessionDir>/.guuey/credentials/` this invoke. Injected so the run path
|
|
52
|
+
* stays pure (no disk access inside `runInvoke`).
|
|
53
|
+
*/
|
|
54
|
+
listCredentials: () => Array<{
|
|
55
|
+
name: string;
|
|
56
|
+
cred: CredentialFile;
|
|
57
|
+
}>;
|
|
58
|
+
}
|
|
59
|
+
/** The `query` surface the loop needs — the real SDK `query` satisfies it. */
|
|
60
|
+
export type QueryFn = (params: {
|
|
61
|
+
prompt: string;
|
|
62
|
+
options: Options;
|
|
63
|
+
}) => AsyncIterable<SDKMessage>;
|
|
64
|
+
/**
|
|
65
|
+
* Run one invoke. Emits `native` per SDKMessage, `done` on the result, `error`
|
|
66
|
+
* on a framework-gate violation or a thrown error. Never throws — every failure
|
|
67
|
+
* path becomes an `error` event so the Router always sees a terminal event.
|
|
68
|
+
*/
|
|
69
|
+
export declare function runInvoke(snapshot: {
|
|
70
|
+
framework?: string;
|
|
71
|
+
} & Parameters<typeof buildOptions>[0], invoke: HostInvoke, runtime: HostRuntime, emit: Emitter, query: QueryFn): Promise<void>;
|
|
72
|
+
//# sourceMappingURL=claude.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"claude.d.ts","sourceRoot":"","sources":["../../src/frameworks/claude.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AACH,OAAO,KAAK,EAAE,OAAO,EAAE,UAAU,EAAE,MAAM,gCAAgC,CAAC;AAC1E,OAAO,KAAK,EAAE,OAAO,EAAE,EAAE,EAAE,cAAc,EAAE,QAAQ,EAAE,SAAS,EAAc,MAAM,eAAe,CAAC;AAClG,OAAO,EACL,YAAY,EAEZ,KAAK,cAAc,EACnB,KAAK,iBAAiB,EACvB,MAAM,qBAAqB,CAAC;AAQ7B;;;;;GAKG;AACH,MAAM,WAAW,UAAU;IACzB,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,QAAQ,CAAC;IACnB,EAAE,EAAE,EAAE,CAAC;IACP,OAAO,EAAE,cAAc,EAAE,CAAC;IAC1B,WAAW,CAAC,EAAE,iBAAiB,EAAE,CAAC;IAClC,UAAU,CAAC,EAAE,SAAS,CAAC;CACxB;AAED,2DAA2D;AAC3D,MAAM,WAAW,WAAW;IAC1B;;;;OAIG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB;;;;;OAKG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB;;;OAGG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB;;;;OAIG;IACH,eAAe,EAAE,MAAM,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,cAAc,CAAA;KAAE,CAAC,CAAC;CACtE;AAED,8EAA8E;AAC9E,MAAM,MAAM,OAAO,GAAG,CAAC,MAAM,EAAE;IAC7B,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,OAAO,CAAC;CAClB,KAAK,aAAa,CAAC,UAAU,CAAC,CAAC;AA0BhC;;;;GAIG;AACH,wBAAsB,SAAS,CAC7B,QAAQ,EAAE;IAAE,SAAS,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,UAAU,CAAC,OAAO,YAAY,CAAC,CAAC,CAAC,CAAC,EACrE,MAAM,EAAE,UAAU,EAClB,OAAO,EAAE,WAAW,EACpB,IAAI,EAAE,OAAO,EACb,KAAK,EAAE,OAAO,GACb,OAAO,CAAC,IAAI,CAAC,CAgDf"}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import { buildOptions, } from "./claude-options.js";
|
|
2
|
+
import { resolveSdkVersion } from "../sdk-version.js";
|
|
3
|
+
/** The framework THIS run path runs. OpenAI has its own path (`run-openai.ts`). */
|
|
4
|
+
const CLAUDE_FRAMEWORK = "claude-agent-sdk";
|
|
5
|
+
/** The npm package whose installed version is this framework's `hello.sdkVersion`. */
|
|
6
|
+
const CLAUDE_SDK_PACKAGE = "@anthropic-ai/claude-agent-sdk";
|
|
7
|
+
function isResultMessage(msg) {
|
|
8
|
+
return msg.type === "result";
|
|
9
|
+
}
|
|
10
|
+
/** Map the SDK result message → the Worker Protocol `done` stopReason + result. */
|
|
11
|
+
function resultToDone(msg) {
|
|
12
|
+
const result = typeof msg.result === "string" ? msg.result : "";
|
|
13
|
+
const stopReason = msg.subtype === "success"
|
|
14
|
+
? "end_turn"
|
|
15
|
+
: msg.subtype === "error_max_turns"
|
|
16
|
+
? "max_turns"
|
|
17
|
+
: "error";
|
|
18
|
+
return { stopReason, result };
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Run one invoke. Emits `native` per SDKMessage, `done` on the result, `error`
|
|
22
|
+
* on a framework-gate violation or a thrown error. Never throws — every failure
|
|
23
|
+
* path becomes an `error` event so the Router always sees a terminal event.
|
|
24
|
+
*/
|
|
25
|
+
export async function runInvoke(snapshot, invoke, runtime, emit, query) {
|
|
26
|
+
// The SDK-version handshake (§8 item B, additive-optional) — ALWAYS the
|
|
27
|
+
// first event of this invoke's fd-3 stream, before the framework gate or any
|
|
28
|
+
// native/turn event. `sdkVersion` is resolved at runtime, never hardcoded;
|
|
29
|
+
// `null` (SDK not resolvable) is tolerated by the Router.
|
|
30
|
+
emit.hello(CLAUDE_FRAMEWORK, CLAUDE_SDK_PACKAGE, resolveSdkVersion(CLAUDE_SDK_PACKAGE));
|
|
31
|
+
// Framework gate: this run path is the Claude SDK. OpenAI agents route to
|
|
32
|
+
// `runInvokeOpenai` (selected in `index.ts` by `snapshot.framework`) and never
|
|
33
|
+
// reach here. A non-claude framework arriving here (e.g. `google-adk`,
|
|
34
|
+
// `vanilla`) has no run path yet → a clear `error`, never a silent mis-run.
|
|
35
|
+
if (snapshot.framework && snapshot.framework !== CLAUDE_FRAMEWORK) {
|
|
36
|
+
emit.error(`@guuey/host: the claude run path got framework '${snapshot.framework}'; no run path for it yet.`);
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
let options;
|
|
40
|
+
try {
|
|
41
|
+
const ctx = {
|
|
42
|
+
input: invoke.input,
|
|
43
|
+
identity: invoke.identity,
|
|
44
|
+
fs: invoke.fs,
|
|
45
|
+
history: invoke.history,
|
|
46
|
+
listCredentials: runtime.listCredentials,
|
|
47
|
+
...(runtime.apiKey !== undefined ? { apiKey: runtime.apiKey } : {}),
|
|
48
|
+
...(runtime.baseUrl !== undefined ? { baseUrl: runtime.baseUrl } : {}),
|
|
49
|
+
...(runtime.authToken !== undefined ? { authToken: runtime.authToken } : {}),
|
|
50
|
+
...(invoke.priorMemory !== undefined ? { priorMemory: invoke.priorMemory } : {}),
|
|
51
|
+
...(invoke.priorState !== undefined ? { priorState: invoke.priorState } : {}),
|
|
52
|
+
};
|
|
53
|
+
options = buildOptions(snapshot, ctx);
|
|
54
|
+
}
|
|
55
|
+
catch (err) {
|
|
56
|
+
emit.error(err instanceof Error ? err.message : String(err));
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
try {
|
|
60
|
+
let done;
|
|
61
|
+
for await (const msg of query({ prompt: invoke.input, options })) {
|
|
62
|
+
emit.native(CLAUDE_FRAMEWORK, toJson(msg));
|
|
63
|
+
if (isResultMessage(msg))
|
|
64
|
+
done = resultToDone(msg);
|
|
65
|
+
}
|
|
66
|
+
emit.done(done?.result ?? "", done?.stopReason ?? "end_turn");
|
|
67
|
+
}
|
|
68
|
+
catch (err) {
|
|
69
|
+
emit.error(err instanceof Error ? err.message : String(err));
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Coerce one SDKMessage to the `JsonValue` the `native` event carries. SDK
|
|
74
|
+
* messages are plain JSON-serializable objects; the round-trip drops any
|
|
75
|
+
* non-JSON surface (functions/symbols never appear on them) and yields the
|
|
76
|
+
* exact shape the Router's normalizer parses off the wire.
|
|
77
|
+
*/
|
|
78
|
+
function toJson(msg) {
|
|
79
|
+
return JSON.parse(JSON.stringify(msg));
|
|
80
|
+
}
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import type { GuueyContext } from "@guuey/config";
|
|
2
|
+
import type { Emitter, JsonValue } from "@guuey/worker";
|
|
3
|
+
import type { FrameworkRunner, HostSnapshot, HostTurn } from "../index.js";
|
|
4
|
+
import { type CredentialFile } from "../creds.js";
|
|
5
|
+
/**
|
|
6
|
+
* Given a require-resolved entry file inside a package, walk up to the
|
|
7
|
+
* package root and return the absolute path of the root export's `import`
|
|
8
|
+
* condition (string conditions only — `@google/adk` 1.3.0's shape), or
|
|
9
|
+
* `undefined` when there is no exports map / no import condition.
|
|
10
|
+
*/
|
|
11
|
+
export declare function importConditionEntry(resolvedEntry: string): string | undefined;
|
|
12
|
+
/**
|
|
13
|
+
* The narrow structural slice of `@google/adk`'s module surface this runner
|
|
14
|
+
* consumes. Structural (not `import type` from the peer's d.ts) so the module
|
|
15
|
+
* type-checks even where the optional peer is absent — the same posture as
|
|
16
|
+
* silverprotocol's facet-side `AdkEvent` contract.
|
|
17
|
+
*/
|
|
18
|
+
interface AdkModule {
|
|
19
|
+
LlmAgent: new (params: {
|
|
20
|
+
name: string;
|
|
21
|
+
model: string;
|
|
22
|
+
instruction: string;
|
|
23
|
+
tools: unknown[];
|
|
24
|
+
}) => AdkAgent;
|
|
25
|
+
InMemoryRunner: new (params: {
|
|
26
|
+
agent: AdkAgent;
|
|
27
|
+
}) => AdkRunner;
|
|
28
|
+
MCPToolset: new (connectionParams: {
|
|
29
|
+
type: "StreamableHTTPConnectionParams";
|
|
30
|
+
url: string;
|
|
31
|
+
transportOptions?: {
|
|
32
|
+
requestInit?: {
|
|
33
|
+
headers?: Record<string, string>;
|
|
34
|
+
};
|
|
35
|
+
};
|
|
36
|
+
}) => unknown;
|
|
37
|
+
}
|
|
38
|
+
/** Opaque agent handle — constructed here (no-code) or by the dev (graceful). */
|
|
39
|
+
export type AdkAgent = object;
|
|
40
|
+
interface AdkRunner {
|
|
41
|
+
readonly appName: string;
|
|
42
|
+
readonly sessionService: {
|
|
43
|
+
createSession(request: {
|
|
44
|
+
appName: string;
|
|
45
|
+
userId: string;
|
|
46
|
+
}): Promise<{
|
|
47
|
+
id: string;
|
|
48
|
+
}>;
|
|
49
|
+
};
|
|
50
|
+
runAsync(params: {
|
|
51
|
+
userId: string;
|
|
52
|
+
sessionId: string;
|
|
53
|
+
newMessage: {
|
|
54
|
+
role: "user";
|
|
55
|
+
parts: Array<{
|
|
56
|
+
text: string;
|
|
57
|
+
}>;
|
|
58
|
+
};
|
|
59
|
+
runConfig: {
|
|
60
|
+
streamingMode: "sse";
|
|
61
|
+
};
|
|
62
|
+
}): AsyncGenerator<JsonValue, void, undefined>;
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Load `@google/adk`, honoring the single-copy rule: when `entryPath` (the
|
|
66
|
+
* graceful agent module) is given, resolve the SDK from THAT tree so the
|
|
67
|
+
* runner drives the same copy the dev's agent was built with; otherwise
|
|
68
|
+
* import from the host's own tree. A resolution failure surfaces as the
|
|
69
|
+
* actionable missing-peer error.
|
|
70
|
+
*/
|
|
71
|
+
export declare function loadAdk(entryPath?: string): Promise<AdkModule>;
|
|
72
|
+
/**
|
|
73
|
+
* Map the broker's credential files to ADK MCP toolsets. Throws on an `sse`
|
|
74
|
+
* credential — the JS toolset has no SSE transport (unlike the Python-era
|
|
75
|
+
* host); the error names the server and the supported path.
|
|
76
|
+
*/
|
|
77
|
+
export declare function buildToolsets(adk: Pick<AdkModule, "MCPToolset">, creds: Array<{
|
|
78
|
+
name: string;
|
|
79
|
+
cred: CredentialFile;
|
|
80
|
+
}>): unknown[];
|
|
81
|
+
/**
|
|
82
|
+
* Extract a native event's last non-thought text part ("" when none).
|
|
83
|
+
* Structural narrowing from `JsonValue` — the event stays untyped JSON on its
|
|
84
|
+
* way to the normalizer; only this thin slice is inspected.
|
|
85
|
+
*/
|
|
86
|
+
export declare function finalTextOf(event: JsonValue): string;
|
|
87
|
+
/**
|
|
88
|
+
* One turn against a caller-supplied agent + module (the seam graceful mode
|
|
89
|
+
* (T3) and the unit tests share). Emits hello → native* → done|error.
|
|
90
|
+
*/
|
|
91
|
+
export declare function runAdkTurn(adk: Pick<AdkModule, "InMemoryRunner">, agent: AdkAgent, turn: HostTurn, emit: Emitter, sdkVersion: string | null): Promise<void>;
|
|
92
|
+
/**
|
|
93
|
+
* Assemble the per-turn {@link GuueyContext} — the ONE discoverable object a
|
|
94
|
+
* graceful factory receives (spec §2.2). `instruction` carries the standard
|
|
95
|
+
* context preamble already prepended; the raw fields ride alongside for
|
|
96
|
+
* factories that render context themselves.
|
|
97
|
+
*/
|
|
98
|
+
export declare function buildGuueyContext(snapshot: HostSnapshot, turn: HostTurn, instruction: string, mcpToolsets: unknown[]): GuueyContext;
|
|
99
|
+
/** Injectable boot deps — the unit-test seam for the no-code path. */
|
|
100
|
+
export interface AdkRunnerDeps {
|
|
101
|
+
load?: typeof loadAdk;
|
|
102
|
+
}
|
|
103
|
+
export declare function createRunner(deps?: AdkRunnerDeps): FrameworkRunner;
|
|
104
|
+
export {};
|
|
105
|
+
//# sourceMappingURL=google-adk.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"google-adk.d.ts","sourceRoot":"","sources":["../../src/frameworks/google-adk.ts"],"names":[],"mappings":"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"}
|