@hellohelen-ai/goliath 0.0.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.
Files changed (71) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +142 -0
  3. package/dist/budget.d.ts +16 -0
  4. package/dist/budget.d.ts.map +1 -0
  5. package/dist/budget.js +36 -0
  6. package/dist/budget.js.map +1 -0
  7. package/dist/compress/structural.d.ts +4 -0
  8. package/dist/compress/structural.d.ts.map +1 -0
  9. package/dist/compress/structural.js +68 -0
  10. package/dist/compress/structural.js.map +1 -0
  11. package/dist/conductor.d.ts +75 -0
  12. package/dist/conductor.d.ts.map +1 -0
  13. package/dist/conductor.js +102 -0
  14. package/dist/conductor.js.map +1 -0
  15. package/dist/create-goliath.d.ts +22 -0
  16. package/dist/create-goliath.d.ts.map +1 -0
  17. package/dist/create-goliath.js +68 -0
  18. package/dist/create-goliath.js.map +1 -0
  19. package/dist/fallback/http-fallback.d.ts +20 -0
  20. package/dist/fallback/http-fallback.d.ts.map +1 -0
  21. package/dist/fallback/http-fallback.js +34 -0
  22. package/dist/fallback/http-fallback.js.map +1 -0
  23. package/dist/index.d.ts +14 -0
  24. package/dist/index.d.ts.map +1 -0
  25. package/dist/index.js +9 -0
  26. package/dist/index.js.map +1 -0
  27. package/dist/judge.d.ts +22 -0
  28. package/dist/judge.d.ts.map +1 -0
  29. package/dist/judge.js +23 -0
  30. package/dist/judge.js.map +1 -0
  31. package/dist/memory/in-memory.d.ts +6 -0
  32. package/dist/memory/in-memory.d.ts.map +1 -0
  33. package/dist/memory/in-memory.js +13 -0
  34. package/dist/memory/in-memory.js.map +1 -0
  35. package/dist/memory/key-value.d.ts +14 -0
  36. package/dist/memory/key-value.d.ts.map +1 -0
  37. package/dist/memory/key-value.js +25 -0
  38. package/dist/memory/key-value.js.map +1 -0
  39. package/dist/prompts.d.ts +41 -0
  40. package/dist/prompts.d.ts.map +1 -0
  41. package/dist/prompts.js +104 -0
  42. package/dist/prompts.js.map +1 -0
  43. package/dist/run-turn.d.ts +26 -0
  44. package/dist/run-turn.d.ts.map +1 -0
  45. package/dist/run-turn.js +260 -0
  46. package/dist/run-turn.js.map +1 -0
  47. package/dist/scribe.d.ts +17 -0
  48. package/dist/scribe.d.ts.map +1 -0
  49. package/dist/scribe.js +44 -0
  50. package/dist/scribe.js.map +1 -0
  51. package/dist/testing/fake-model.d.ts +27 -0
  52. package/dist/testing/fake-model.d.ts.map +1 -0
  53. package/dist/testing/fake-model.js +57 -0
  54. package/dist/testing/fake-model.js.map +1 -0
  55. package/dist/testing/index.d.ts +3 -0
  56. package/dist/testing/index.d.ts.map +1 -0
  57. package/dist/testing/index.js +2 -0
  58. package/dist/testing/index.js.map +1 -0
  59. package/dist/tools/define-tool.d.ts +21 -0
  60. package/dist/tools/define-tool.d.ts.map +1 -0
  61. package/dist/tools/define-tool.js +10 -0
  62. package/dist/tools/define-tool.js.map +1 -0
  63. package/dist/types.d.ts +182 -0
  64. package/dist/types.d.ts.map +1 -0
  65. package/dist/types.js +2 -0
  66. package/dist/types.js.map +1 -0
  67. package/dist/worker.d.ts +53 -0
  68. package/dist/worker.d.ts.map +1 -0
  69. package/dist/worker.js +124 -0
  70. package/dist/worker.js.map +1 -0
  71. package/package.json +69 -0
@@ -0,0 +1,260 @@
1
+ import { plan } from "./conductor.js";
2
+ import { judgeAnswer, judgeStep, judgeToolFailures } from "./judge.js";
3
+ import { DEFAULT_INSTRUCTIONS } from "./prompts.js";
4
+ import { remember } from "./scribe.js";
5
+ import { runAnswerStep, runToolStep } from "./worker.js";
6
+ /**
7
+ * One turn, start to finish: recall, then up to `maxSteps` stones, then an
8
+ * answer, then remember. Every escalation path goes through `escalate` so the
9
+ * cloud always receives the same shape.
10
+ */
11
+ const runTurn = async (input) => {
12
+ const instructions = input.instructions ?? DEFAULT_INSTRUCTIONS;
13
+ const trace = [];
14
+ const steps = [];
15
+ const emit = (event) => {
16
+ trace.push(event);
17
+ input.onEvent?.(event);
18
+ };
19
+ const state = await input.memory.load();
20
+ emit({ type: "recall", summary: state.summary, recent: state.recent.length });
21
+ const escalate = async (reason, error) => {
22
+ emit({ type: "escalate", reason, ...(error ? { error } : {}) });
23
+ if (!input.fallback) {
24
+ // No cloud to hand to. smolagents still answers from memory on max
25
+ // steps rather than returning nothing; so does Goliath, unless the
26
+ // model itself is what failed.
27
+ if (reason === "model-error")
28
+ return { text: "", handledBy: "device", steps, trace };
29
+ const text = await bestEffortAnswer(input, instructions, state.summary, steps);
30
+ if (text)
31
+ emit({ type: "answer", text });
32
+ return { text, handledBy: "device", bestEffort: true, steps, trace };
33
+ }
34
+ const { text } = await input.fallback({
35
+ ask: input.ask,
36
+ summary: state.summary,
37
+ recent: state.recent,
38
+ steps,
39
+ reason,
40
+ ...(error ? { error } : {}),
41
+ ...(input.signal ? { signal: input.signal } : {}),
42
+ });
43
+ await commit(input, state, { ask: input.ask, answer: text, at: Date.now() }, emit);
44
+ return { text, handledBy: "cloud", steps, trace };
45
+ };
46
+ const hasTools = Object.keys(input.tools).length > 0;
47
+ try {
48
+ return await stones();
49
+ }
50
+ catch (error) {
51
+ // Guardrail violations, refusals, a dead session after context overflow,
52
+ // an unavailable model: none of these are the app's fault, and none are
53
+ // worth retrying with the same input. The cloud gets the same step log.
54
+ if (isAbort(error))
55
+ throw error;
56
+ if (isGuardrail(error)) {
57
+ // Apple's guardrail objects to text in the prompt or the reply. On a
58
+ // step that carries tool output that is usually a false positive on a
59
+ // calendar title, not an attack; either way, escalating would ship the
60
+ // very text it flagged. Stop here and let the app tell the user.
61
+ emit({ type: "escalate", reason: "guardrail", error: describeError(error) });
62
+ return { text: "", handledBy: "device", steps, trace };
63
+ }
64
+ return escalate("model-error", describeError(error));
65
+ }
66
+ async function stones() {
67
+ let planRetried = false;
68
+ let retryHint;
69
+ for (;;) {
70
+ const stall = judgeStep({ steps, maxSteps: input.maxSteps });
71
+ if (stall)
72
+ return escalate(stall);
73
+ const outcome = hasTools
74
+ ? await plan({
75
+ model: input.model,
76
+ instructions,
77
+ tools: input.tools,
78
+ ask: input.ask,
79
+ summary: state.summary,
80
+ steps,
81
+ maxSteps: input.maxSteps,
82
+ window: input.window,
83
+ emit,
84
+ ...(input.facts ? { facts: input.facts } : {}),
85
+ ...(input.examples ? { examples: input.examples } : {}),
86
+ ...(retryHint ? { retryHint } : {}),
87
+ ...(input.signal ? { signal: input.signal } : {}),
88
+ })
89
+ : { ok: true, plan: { kind: "answer", brief: "reply" } };
90
+ if (!outcome.ok) {
91
+ // Small models drop a brace now and then. One more try, told what was
92
+ // wrong (Claude Code feeds the validation error back), is cheap and
93
+ // usually enough; two failures in a row is a real signal.
94
+ if (planRetried)
95
+ return escalate("plan-invalid");
96
+ planRetried = true;
97
+ retryHint = outcome.hint;
98
+ continue;
99
+ }
100
+ retryHint = undefined;
101
+ const next = outcome.plan;
102
+ emit({
103
+ type: "plan",
104
+ index: steps.length,
105
+ kind: next.kind === "escalate" ? "answer" : next.kind,
106
+ ...(next.tool ? { tool: next.tool } : {}),
107
+ ...(next.why ? { why: next.why } : {}),
108
+ brief: next.brief,
109
+ });
110
+ if (next.kind === "escalate")
111
+ return escalate("conductor-asked");
112
+ if (next.kind === "answer") {
113
+ const answerInput = {
114
+ model: input.model,
115
+ instructions,
116
+ ask: input.ask,
117
+ summary: state.summary,
118
+ steps,
119
+ ...(input.signal ? { signal: input.signal } : {}),
120
+ };
121
+ let text = await runAnswerStep(answerInput);
122
+ if (judgeAnswer(text)) {
123
+ // eve, OpenClaw, and Hermes all reissue once with a nudge before giving up.
124
+ text = await runAnswerStep({ ...answerInput, nudge: EMPTY_ANSWER_NUDGE });
125
+ }
126
+ const empty = judgeAnswer(text);
127
+ if (empty)
128
+ return escalate(empty);
129
+ steps.push({ index: steps.length, kind: "answer", brief: next.brief, text });
130
+ emit({ type: "answer", text });
131
+ await commit(input, state, { ask: input.ask, answer: text, at: Date.now() }, emit);
132
+ return { text, handledBy: "device", steps, trace };
133
+ }
134
+ const tool = input.tools[next.tool ?? ""];
135
+ if (!tool)
136
+ return escalate("plan-invalid");
137
+ const started = Date.now();
138
+ // Claude Code answers an identical re-read with a stub instead of the
139
+ // file. A first repeat of a read-only tool gets the earlier result back
140
+ // with no model call and no execution; a second repeat is a real loop.
141
+ const cached = !tool.writes ? findRepeatOfReadOnly(steps, tool.name) : undefined;
142
+ if (cached) {
143
+ const record = {
144
+ index: steps.length,
145
+ kind: "tool",
146
+ brief: next.brief,
147
+ tool: tool.name,
148
+ input: cached.input,
149
+ result: `same as step ${cached.index + 1}`,
150
+ cached: true,
151
+ };
152
+ steps.push(record);
153
+ emit({
154
+ type: "tool",
155
+ tool: tool.name,
156
+ input: cached.input,
157
+ result: record.result ?? "",
158
+ ms: 0,
159
+ });
160
+ continue;
161
+ }
162
+ const done = await runToolStep({
163
+ model: input.model,
164
+ instructions,
165
+ tool,
166
+ brief: next.brief,
167
+ ask: input.ask,
168
+ confirm: async (request) => {
169
+ const decision = await input.confirm(request);
170
+ const approved = typeof decision === "boolean" ? decision : decision.approved;
171
+ const reason = typeof decision === "object" ? decision.reason : undefined;
172
+ emit({ type: "confirm", tool: request.tool, approved, ...(reason ? { reason } : {}) });
173
+ return decision;
174
+ },
175
+ ...(input.signal ? { signal: input.signal } : {}),
176
+ });
177
+ if (!done.ok)
178
+ return escalate(done.reason);
179
+ const repeat = judgeStep({
180
+ steps,
181
+ maxSteps: input.maxSteps,
182
+ next: { tool: tool.name, input: done.input },
183
+ });
184
+ if (repeat === "repeated-tool-call")
185
+ return escalate(repeat);
186
+ steps.push({
187
+ index: steps.length,
188
+ kind: "tool",
189
+ brief: next.brief,
190
+ tool: tool.name,
191
+ input: done.input,
192
+ result: done.result,
193
+ skipped: done.skipped,
194
+ ...(done.failed ? { failed: true } : {}),
195
+ });
196
+ emit({
197
+ type: "tool",
198
+ tool: tool.name,
199
+ input: done.input,
200
+ result: done.result,
201
+ ms: Date.now() - started,
202
+ });
203
+ const failing = judgeToolFailures(steps);
204
+ if (failing)
205
+ return escalate(failing, steps.at(-1)?.result);
206
+ }
207
+ }
208
+ };
209
+ /**
210
+ * The most recent step with this read-only tool, when no step has already
211
+ * been served from cache for it. Argument equality is checked by the judge
212
+ * after the worker runs; here the tool name alone is the signal, because a
213
+ * no-argument tool never reaches the worker.
214
+ */
215
+ const findRepeatOfReadOnly = (steps, toolName) => {
216
+ const same = steps.filter((s) => s.kind === "tool" && s.tool === toolName);
217
+ const last = same.at(-1);
218
+ if (!last || last.cached)
219
+ return undefined;
220
+ if (last.input !== undefined && Object.keys(last.input).length > 0)
221
+ return undefined;
222
+ return last;
223
+ };
224
+ /** One answer call from the step log; an empty or failing call yields "". */
225
+ const bestEffortAnswer = async (input, instructions, summary, steps) => {
226
+ try {
227
+ return await runAnswerStep({
228
+ model: input.model,
229
+ instructions,
230
+ ask: input.ask,
231
+ summary,
232
+ steps,
233
+ bestEffort: true,
234
+ ...(input.signal ? { signal: input.signal } : {}),
235
+ });
236
+ }
237
+ catch (error) {
238
+ if (isAbort(error))
239
+ throw error;
240
+ return "";
241
+ }
242
+ };
243
+ const EMPTY_ANSWER_NUDGE = "Your previous reply was empty. Answer now from what you found above; do not mention this notice.";
244
+ const isAbort = (error) => error?.name === "AbortError";
245
+ /** Apple's GenerationError.guardrailViolation, however the provider spells it. */
246
+ const isGuardrail = (error) => /guardrail|content.?filter|safety/i.test(describeError(error));
247
+ const describeError = (error) => error instanceof Error ? `${error.name}: ${error.message}` : String(error);
248
+ const commit = async (input, state, exchange, emit) => {
249
+ const next = await remember({
250
+ model: input.model,
251
+ state,
252
+ exchange,
253
+ summaryBudget: Math.floor(input.window / 8),
254
+ ...(input.signal ? { signal: input.signal } : {}),
255
+ });
256
+ await input.memory.save(next);
257
+ emit({ type: "remember", summary: next.summary });
258
+ };
259
+ export { runTurn };
260
+ //# sourceMappingURL=run-turn.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"run-turn.js","sourceRoot":"","sources":["../src/run-turn.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,17 @@
1
+ import { type LanguageModel } from "ai";
2
+ import type { Exchange, MemoryState } from "./types.js";
3
+ declare const RECENT_KEEP = 3;
4
+ /**
5
+ * Remember the turn. The last few exchanges stay verbatim; when one falls off
6
+ * the end it is folded into the brief with one model call. So most turns cost
7
+ * nothing, and the brief never grows past its budget.
8
+ */
9
+ declare const remember: (input: {
10
+ model: LanguageModel;
11
+ state: MemoryState;
12
+ exchange: Exchange;
13
+ summaryBudget: number;
14
+ signal?: AbortSignal;
15
+ }) => Promise<MemoryState>;
16
+ export { RECENT_KEEP, remember };
17
+ //# sourceMappingURL=scribe.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scribe.d.ts","sourceRoot":"","sources":["../src/scribe.ts"],"names":[],"mappings":"AAAA,OAAO,EAAgB,KAAK,aAAa,EAAE,MAAM,IAAI,CAAC;AAGtD,OAAO,KAAK,EAAE,QAAQ,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAExD,QAAA,MAAM,WAAW,IAAI,CAAC;AAEtB;;;;GAIG;AACH,QAAA,MAAM,QAAQ,GAAU,OAAO;IAC7B,KAAK,EAAE,aAAa,CAAC;IACrB,KAAK,EAAE,WAAW,CAAC;IACnB,QAAQ,EAAE,QAAQ,CAAC;IACnB,aAAa,EAAE,MAAM,CAAC;IACtB,MAAM,CAAC,EAAE,WAAW,CAAC;CACtB,KAAG,OAAO,CAAC,WAAW,CAoBtB,CAAC;AAsBF,OAAO,EAAE,WAAW,EAAE,QAAQ,EAAE,CAAC"}
package/dist/scribe.js ADDED
@@ -0,0 +1,44 @@
1
+ import { generateText } from "ai";
2
+ import { estimateTokens } from "./budget.js";
3
+ import { scribeSystem, scribeUser } from "./prompts.js";
4
+ const RECENT_KEEP = 3;
5
+ /**
6
+ * Remember the turn. The last few exchanges stay verbatim; when one falls off
7
+ * the end it is folded into the brief with one model call. So most turns cost
8
+ * nothing, and the brief never grows past its budget.
9
+ */
10
+ const remember = async (input) => {
11
+ const recent = [...input.state.recent, input.exchange];
12
+ let summary = input.state.summary;
13
+ while (recent.length > RECENT_KEEP) {
14
+ const evicted = recent.shift();
15
+ if (!evicted)
16
+ break;
17
+ summary = await fold({
18
+ model: input.model,
19
+ summary,
20
+ exchange: evicted,
21
+ ...(input.signal ? { signal: input.signal } : {}),
22
+ });
23
+ }
24
+ if (estimateTokens(summary) > input.summaryBudget) {
25
+ summary = summary.slice(0, input.summaryBudget * 4);
26
+ }
27
+ return { summary, recent };
28
+ };
29
+ const fold = async (input) => {
30
+ const result = await generateText({
31
+ model: input.model,
32
+ system: scribeSystem,
33
+ prompt: scribeUser({
34
+ summary: input.summary,
35
+ ask: input.exchange.ask,
36
+ answer: input.exchange.answer,
37
+ }),
38
+ ...(input.signal ? { abortSignal: input.signal } : {}),
39
+ });
40
+ const text = result.text.trim();
41
+ return text || input.summary;
42
+ };
43
+ export { RECENT_KEEP, remember };
44
+ //# sourceMappingURL=scribe.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scribe.js","sourceRoot":"","sources":["../src/scribe.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAsB,MAAM,IAAI,CAAC;AACtD,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAC7C,OAAO,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAGxD,MAAM,WAAW,GAAG,CAAC,CAAC;AAEtB;;;;GAIG;AACH,MAAM,QAAQ,GAAG,KAAK,EAAE,KAMvB,EAAwB,EAAE;IACzB,MAAM,MAAM,GAAG,CAAC,GAAG,KAAK,CAAC,KAAK,CAAC,MAAM,EAAE,KAAK,CAAC,QAAQ,CAAC,CAAC;IACvD,IAAI,OAAO,GAAG,KAAK,CAAC,KAAK,CAAC,OAAO,CAAC;IAElC,OAAO,MAAM,CAAC,MAAM,GAAG,WAAW,EAAE,CAAC;QACnC,MAAM,OAAO,GAAG,MAAM,CAAC,KAAK,EAAE,CAAC;QAC/B,IAAI,CAAC,OAAO;YAAE,MAAM;QACpB,OAAO,GAAG,MAAM,IAAI,CAAC;YACnB,KAAK,EAAE,KAAK,CAAC,KAAK;YAClB,OAAO;YACP,QAAQ,EAAE,OAAO;YACjB,GAAG,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,KAAK,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;SAClD,CAAC,CAAC;IACL,CAAC;IAED,IAAI,cAAc,CAAC,OAAO,CAAC,GAAG,KAAK,CAAC,aAAa,EAAE,CAAC;QAClD,OAAO,GAAG,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,KAAK,CAAC,aAAa,GAAG,CAAC,CAAC,CAAC;IACtD,CAAC;IAED,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,CAAC;AAC7B,CAAC,CAAC;AAEF,MAAM,IAAI,GAAG,KAAK,EAAE,KAKnB,EAAmB,EAAE;IACpB,MAAM,MAAM,GAAG,MAAM,YAAY,CAAC;QAChC,KAAK,EAAE,KAAK,CAAC,KAAK;QAClB,MAAM,EAAE,YAAY;QACpB,MAAM,EAAE,UAAU,CAAC;YACjB,OAAO,EAAE,KAAK,CAAC,OAAO;YACtB,GAAG,EAAE,KAAK,CAAC,QAAQ,CAAC,GAAG;YACvB,MAAM,EAAE,KAAK,CAAC,QAAQ,CAAC,MAAM;SAC9B,CAAC;QACF,GAAG,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,WAAW,EAAE,KAAK,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;KACvD,CAAC,CAAC;IACH,MAAM,IAAI,GAAG,MAAM,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC;IAChC,OAAO,IAAI,IAAI,KAAK,CAAC,OAAO,CAAC;AAC/B,CAAC,CAAC;AAEF,OAAO,EAAE,WAAW,EAAE,QAAQ,EAAE,CAAC"}
@@ -0,0 +1,27 @@
1
+ import { MockLanguageModelV4 } from "ai/test";
2
+ import type { LanguageModelV4CallOptions } from "@ai-sdk/provider";
3
+ /** One scripted reply. `json` is for structured output, `toolCall` for a worker step. */
4
+ type ScriptedReply = {
5
+ text: string;
6
+ } | {
7
+ json: unknown;
8
+ } | {
9
+ toolCall: {
10
+ name: string;
11
+ input: unknown;
12
+ };
13
+ };
14
+ type FakeModel = MockLanguageModelV4 & {
15
+ /** Every prompt the harness sent, in order, for assertions. */
16
+ calls: LanguageModelV4CallOptions[];
17
+ /** Replies not yet consumed. Empty at the end of a well-scripted test. */
18
+ remaining: () => number;
19
+ };
20
+ /**
21
+ * A language model that reads from a script. Each `doGenerate` consumes the
22
+ * next reply; running out throws, so a test cannot silently pass on air.
23
+ */
24
+ declare const fakeModel: (script: ScriptedReply[]) => FakeModel;
25
+ export { fakeModel };
26
+ export type { FakeModel, ScriptedReply };
27
+ //# sourceMappingURL=fake-model.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fake-model.d.ts","sourceRoot":"","sources":["../../src/testing/fake-model.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAE,MAAM,SAAS,CAAC;AAC9C,OAAO,KAAK,EAAE,0BAA0B,EAAiC,MAAM,kBAAkB,CAAC;AAElG,yFAAyF;AACzF,KAAK,aAAa,GAChB;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,GAAG;IAAE,IAAI,EAAE,OAAO,CAAA;CAAE,GAAG;IAAE,QAAQ,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,OAAO,CAAA;KAAE,CAAA;CAAE,CAAC;AAExF,KAAK,SAAS,GAAG,mBAAmB,GAAG;IACrC,+DAA+D;IAC/D,KAAK,EAAE,0BAA0B,EAAE,CAAC;IACpC,0EAA0E;IAC1E,SAAS,EAAE,MAAM,MAAM,CAAC;CACzB,CAAC;AAwCF;;;GAGG;AACH,QAAA,MAAM,SAAS,GAAI,QAAQ,aAAa,EAAE,KAAG,SAkB5C,CAAC;AAEF,OAAO,EAAE,SAAS,EAAE,CAAC;AACrB,YAAY,EAAE,SAAS,EAAE,aAAa,EAAE,CAAC"}
@@ -0,0 +1,57 @@
1
+ import { MockLanguageModelV4 } from "ai/test";
2
+ const usage = (input, output) => ({
3
+ inputTokens: { total: input, noCache: input, cacheRead: 0, cacheWrite: 0 },
4
+ outputTokens: { total: output, text: output, reasoning: 0 },
5
+ });
6
+ const promptChars = (options) => JSON.stringify(options.prompt).length;
7
+ const toResult = (reply, options) => {
8
+ const inputTokens = Math.ceil(promptChars(options) / 4);
9
+ if ("toolCall" in reply) {
10
+ const input = JSON.stringify(reply.toolCall.input ?? {});
11
+ return {
12
+ content: [
13
+ {
14
+ type: "tool-call",
15
+ toolCallId: `call_${Math.random().toString(36).slice(2, 8)}`,
16
+ toolName: reply.toolCall.name,
17
+ input,
18
+ },
19
+ ],
20
+ finishReason: { unified: "tool-calls", raw: "tool-calls" },
21
+ usage: usage(inputTokens, Math.ceil(input.length / 4)),
22
+ warnings: [],
23
+ };
24
+ }
25
+ const text = "json" in reply ? JSON.stringify(reply.json) : reply.text;
26
+ return {
27
+ content: [{ type: "text", text }],
28
+ finishReason: { unified: "stop", raw: "stop" },
29
+ usage: usage(inputTokens, Math.ceil(text.length / 4)),
30
+ warnings: [],
31
+ };
32
+ };
33
+ /**
34
+ * A language model that reads from a script. Each `doGenerate` consumes the
35
+ * next reply; running out throws, so a test cannot silently pass on air.
36
+ */
37
+ const fakeModel = (script) => {
38
+ const queue = [...script];
39
+ const calls = [];
40
+ const model = new MockLanguageModelV4({
41
+ provider: "goliath.fake",
42
+ modelId: "scripted",
43
+ doGenerate: async (options) => {
44
+ calls.push(options);
45
+ const reply = queue.shift();
46
+ if (!reply) {
47
+ throw new Error(`fakeModel: script exhausted after ${calls.length} call(s)`);
48
+ }
49
+ return toResult(reply, options);
50
+ },
51
+ });
52
+ model.calls = calls;
53
+ model.remaining = () => queue.length;
54
+ return model;
55
+ };
56
+ export { fakeModel };
57
+ //# sourceMappingURL=fake-model.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fake-model.js","sourceRoot":"","sources":["../../src/testing/fake-model.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ export { fakeModel } from "./fake-model.js";
2
+ export type { FakeModel, ScriptedReply } from "./fake-model.js";
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/testing/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAC5C,YAAY,EAAE,SAAS,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC"}
@@ -0,0 +1,2 @@
1
+ export { fakeModel } from "./fake-model.js";
2
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/testing/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC"}
@@ -0,0 +1,21 @@
1
+ import type { z } from "zod";
2
+ import type { GoliathTool, ToolContext } from "../types.js";
3
+ type DefineToolArgs<INPUT, OUTPUT> = {
4
+ name: string;
5
+ description: string;
6
+ parameters: z.ZodType<INPUT>;
7
+ writes?: boolean;
8
+ execute: (input: INPUT, context: ToolContext) => Promise<OUTPUT> | OUTPUT;
9
+ toModelOutput?: (output: OUTPUT) => string;
10
+ requires?: string[];
11
+ };
12
+ /**
13
+ * Declare a tool. A thin identity function that pins the types.
14
+ *
15
+ * Keep `parameters` flat: primitives and enums, no nested objects, no unions.
16
+ * That is what a 3B model fills in reliably and what Apple's guided
17
+ * generation accepts (docs/research/rn-providers-and-ai-sdk.md § 1.5).
18
+ */
19
+ declare const defineTool: <INPUT, OUTPUT>(args: DefineToolArgs<INPUT, OUTPUT>) => GoliathTool<INPUT, OUTPUT>;
20
+ export { defineTool };
21
+ //# sourceMappingURL=define-tool.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"define-tool.d.ts","sourceRoot":"","sources":["../../src/tools/define-tool.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAC7B,OAAO,KAAK,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAE5D,KAAK,cAAc,CAAC,KAAK,EAAE,MAAM,IAAI;IACnC,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,CAAC,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;IAC7B,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,OAAO,EAAE,CAAC,KAAK,EAAE,KAAK,EAAE,OAAO,EAAE,WAAW,KAAK,OAAO,CAAC,MAAM,CAAC,GAAG,MAAM,CAAC;IAC1E,aAAa,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,MAAM,CAAC;IAC3C,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;CACrB,CAAC;AAEF;;;;;;GAMG;AACH,QAAA,MAAM,UAAU,GAAI,KAAK,EAAE,MAAM,EAC/B,MAAM,cAAc,CAAC,KAAK,EAAE,MAAM,CAAC,KAClC,WAAW,CAAC,KAAK,EAAE,MAAM,CAAS,CAAC;AAEtC,OAAO,EAAE,UAAU,EAAE,CAAC"}
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Declare a tool. A thin identity function that pins the types.
3
+ *
4
+ * Keep `parameters` flat: primitives and enums, no nested objects, no unions.
5
+ * That is what a 3B model fills in reliably and what Apple's guided
6
+ * generation accepts (docs/research/rn-providers-and-ai-sdk.md § 1.5).
7
+ */
8
+ const defineTool = (args) => args;
9
+ export { defineTool };
10
+ //# sourceMappingURL=define-tool.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"define-tool.js","sourceRoot":"","sources":["../../src/tools/define-tool.ts"],"names":[],"mappings":"AAaA;;;;;;GAMG;AACH,MAAM,UAAU,GAAG,CACjB,IAAmC,EACP,EAAE,CAAC,IAAI,CAAC;AAEtC,OAAO,EAAE,UAAU,EAAE,CAAC"}