@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.
Files changed (48) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +47 -0
  3. package/dist/agent-entry.d.ts +38 -0
  4. package/dist/agent-entry.d.ts.map +1 -0
  5. package/dist/agent-entry.js +87 -0
  6. package/dist/boot-context.d.ts +50 -0
  7. package/dist/boot-context.d.ts.map +1 -0
  8. package/dist/boot-context.js +22 -0
  9. package/dist/creds.d.ts +26 -0
  10. package/dist/creds.d.ts.map +1 -0
  11. package/dist/creds.js +45 -0
  12. package/dist/frameworks/claude-options.d.ts +149 -0
  13. package/dist/frameworks/claude-options.d.ts.map +1 -0
  14. package/dist/frameworks/claude-options.js +208 -0
  15. package/dist/frameworks/claude-runner.d.ts +3 -0
  16. package/dist/frameworks/claude-runner.d.ts.map +1 -0
  17. package/dist/frameworks/claude-runner.js +33 -0
  18. package/dist/frameworks/claude.d.ts +72 -0
  19. package/dist/frameworks/claude.d.ts.map +1 -0
  20. package/dist/frameworks/claude.js +80 -0
  21. package/dist/frameworks/google-adk.d.ts +105 -0
  22. package/dist/frameworks/google-adk.d.ts.map +1 -0
  23. package/dist/frameworks/google-adk.js +269 -0
  24. package/dist/frameworks/openai-runner.d.ts +3 -0
  25. package/dist/frameworks/openai-runner.d.ts.map +1 -0
  26. package/dist/frameworks/openai-runner.js +37 -0
  27. package/dist/frameworks/openai.d.ts +65 -0
  28. package/dist/frameworks/openai.d.ts.map +1 -0
  29. package/dist/frameworks/openai.js +207 -0
  30. package/dist/index.d.ts +31 -0
  31. package/dist/index.d.ts.map +1 -0
  32. package/dist/index.js +134 -0
  33. package/dist/options.d.ts +173 -0
  34. package/dist/options.d.ts.map +1 -0
  35. package/dist/options.js +248 -0
  36. package/dist/preamble.d.ts +16 -0
  37. package/dist/preamble.d.ts.map +1 -0
  38. package/dist/preamble.js +34 -0
  39. package/dist/run-openai.d.ts +65 -0
  40. package/dist/run-openai.d.ts.map +1 -0
  41. package/dist/run-openai.js +207 -0
  42. package/dist/run.d.ts +72 -0
  43. package/dist/run.d.ts.map +1 -0
  44. package/dist/run.js +80 -0
  45. package/dist/sdk-version.d.ts +5 -0
  46. package/dist/sdk-version.d.ts.map +1 -0
  47. package/dist/sdk-version.js +56 -0
  48. 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,3 @@
1
+ import type { FrameworkRunner } from "../index.js";
2
+ export declare function createRunner(): FrameworkRunner;
3
+ //# sourceMappingURL=claude-runner.d.ts.map
@@ -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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"}