@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.
- package/LICENSE +21 -0
- package/README.md +142 -0
- package/dist/budget.d.ts +16 -0
- package/dist/budget.d.ts.map +1 -0
- package/dist/budget.js +36 -0
- package/dist/budget.js.map +1 -0
- package/dist/compress/structural.d.ts +4 -0
- package/dist/compress/structural.d.ts.map +1 -0
- package/dist/compress/structural.js +68 -0
- package/dist/compress/structural.js.map +1 -0
- package/dist/conductor.d.ts +75 -0
- package/dist/conductor.d.ts.map +1 -0
- package/dist/conductor.js +102 -0
- package/dist/conductor.js.map +1 -0
- package/dist/create-goliath.d.ts +22 -0
- package/dist/create-goliath.d.ts.map +1 -0
- package/dist/create-goliath.js +68 -0
- package/dist/create-goliath.js.map +1 -0
- package/dist/fallback/http-fallback.d.ts +20 -0
- package/dist/fallback/http-fallback.d.ts.map +1 -0
- package/dist/fallback/http-fallback.js +34 -0
- package/dist/fallback/http-fallback.js.map +1 -0
- package/dist/index.d.ts +14 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +9 -0
- package/dist/index.js.map +1 -0
- package/dist/judge.d.ts +22 -0
- package/dist/judge.d.ts.map +1 -0
- package/dist/judge.js +23 -0
- package/dist/judge.js.map +1 -0
- package/dist/memory/in-memory.d.ts +6 -0
- package/dist/memory/in-memory.d.ts.map +1 -0
- package/dist/memory/in-memory.js +13 -0
- package/dist/memory/in-memory.js.map +1 -0
- package/dist/memory/key-value.d.ts +14 -0
- package/dist/memory/key-value.d.ts.map +1 -0
- package/dist/memory/key-value.js +25 -0
- package/dist/memory/key-value.js.map +1 -0
- package/dist/prompts.d.ts +41 -0
- package/dist/prompts.d.ts.map +1 -0
- package/dist/prompts.js +104 -0
- package/dist/prompts.js.map +1 -0
- package/dist/run-turn.d.ts +26 -0
- package/dist/run-turn.d.ts.map +1 -0
- package/dist/run-turn.js +260 -0
- package/dist/run-turn.js.map +1 -0
- package/dist/scribe.d.ts +17 -0
- package/dist/scribe.d.ts.map +1 -0
- package/dist/scribe.js +44 -0
- package/dist/scribe.js.map +1 -0
- package/dist/testing/fake-model.d.ts +27 -0
- package/dist/testing/fake-model.d.ts.map +1 -0
- package/dist/testing/fake-model.js +57 -0
- package/dist/testing/fake-model.js.map +1 -0
- package/dist/testing/index.d.ts +3 -0
- package/dist/testing/index.d.ts.map +1 -0
- package/dist/testing/index.js +2 -0
- package/dist/testing/index.js.map +1 -0
- package/dist/tools/define-tool.d.ts +21 -0
- package/dist/tools/define-tool.d.ts.map +1 -0
- package/dist/tools/define-tool.js +10 -0
- package/dist/tools/define-tool.js.map +1 -0
- package/dist/types.d.ts +182 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/dist/worker.d.ts +53 -0
- package/dist/worker.d.ts.map +1 -0
- package/dist/worker.js +124 -0
- package/dist/worker.js.map +1 -0
- package/package.json +69 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 hellohelen-ai / David Hanlon
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
# Goliath
|
|
2
|
+
|
|
3
|
+
**An agent harness for the phone's own model.**
|
|
4
|
+
|
|
5
|
+
Apple ships a ~3B language model on every iPhone with Apple Intelligence. It is free, private, and
|
|
6
|
+
fast. It also has a 4,096-token window and gets lost after a few tool calls. Goliath is the loop
|
|
7
|
+
that makes it useful anyway: it plans one step at a time, runs each step in a fresh context, keeps
|
|
8
|
+
tool output small, asks before it changes anything, and hands the turn to a cloud agent when the
|
|
9
|
+
phone cannot finish.
|
|
10
|
+
|
|
11
|
+
It only needs one stone.
|
|
12
|
+
|
|
13
|
+
```ts
|
|
14
|
+
import { createGoliath, defineTool } from "@hellohelen-ai/goliath";
|
|
15
|
+
import { apple } from "@react-native-ai/apple";
|
|
16
|
+
import { z } from "zod";
|
|
17
|
+
|
|
18
|
+
const listTasks = defineTool({
|
|
19
|
+
name: "listTasks",
|
|
20
|
+
description: "The user's open tasks.",
|
|
21
|
+
parameters: z.object({}),
|
|
22
|
+
execute: () => convex.query(api.tasks.list, {}),
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
const createTask = defineTool({
|
|
26
|
+
name: "createTask",
|
|
27
|
+
description: "Add a task.",
|
|
28
|
+
parameters: z.object({ title: z.string() }),
|
|
29
|
+
writes: true, // Goliath asks before running it
|
|
30
|
+
execute: ({ title }) => convex.mutation(api.tasks.create, { title }),
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
const goliath = createGoliath({
|
|
34
|
+
model: apple(),
|
|
35
|
+
tools: { listTasks, createTask },
|
|
36
|
+
confirm: async ({ tool, input }) => askTheUser(tool, input),
|
|
37
|
+
fallback: async ({ ask, summary, steps }) => cloudAgent.turn({ ask, summary, steps }),
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
const result = await goliath.run("if I don't already have it, add call the dentist");
|
|
41
|
+
result.text; // "Added Call the dentist. You now have three open tasks."
|
|
42
|
+
result.handledBy; // "device" | "cloud"
|
|
43
|
+
result.steps; // what it did, one line each
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
Any [AI SDK](https://ai-sdk.dev) language model works. On a phone that is
|
|
47
|
+
[`@react-native-ai/apple`](https://ai-sdk.dev/providers/community-providers/react-native-apple);
|
|
48
|
+
in a test it is the scripted model from `@hellohelen-ai/goliath/testing`.
|
|
49
|
+
|
|
50
|
+
## How a turn runs
|
|
51
|
+
|
|
52
|
+
```
|
|
53
|
+
ask ──► recall ──► conductor ──► worker ──► judge ──► … ──► answer ──► remember
|
|
54
|
+
│ │ │ │
|
|
55
|
+
memory next stone fresh ctx stalled?
|
|
56
|
+
brief (JSON, 3 one tool → fallback
|
|
57
|
+
fields) ≤600 chars
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
| Stage | What it sees | What it returns |
|
|
61
|
+
| ------------- | ------------------------------------------------------- | ------------------------------------------------------------------------------------ |
|
|
62
|
+
| **Conductor** | The ask, the memory brief, and one line per step so far | `{ kind: "tool" \| "answer" \| "escalate", tool?, brief }` |
|
|
63
|
+
| **Worker** | A one-line brief and one tool's schema | The arguments, as structured output. Goliath runs the tool and compresses the result |
|
|
64
|
+
| **Judge** | The step log | Escalate on a repeated call, an empty answer, or the step cap |
|
|
65
|
+
| **Answer** | The ask, the brief, the step log | Two or three sentences |
|
|
66
|
+
| **Scribe** | The last three exchanges | A rolling brief of at most 60 words, updated only when an exchange falls off |
|
|
67
|
+
|
|
68
|
+
Nothing a worker saw survives the step. The conductor never sees raw JSON. That is the whole trick.
|
|
69
|
+
|
|
70
|
+
## Why not just call the model in a loop
|
|
71
|
+
|
|
72
|
+
You can. The AI SDK's `generateText` with `stopWhen` is that loop, and Apple's own session runs a
|
|
73
|
+
tool loop natively. Both fall over on a phone for the same reasons:
|
|
74
|
+
|
|
75
|
+
- **The window fills.** One JSON tool result can be a thousand tokens. Three of them and the model
|
|
76
|
+
has forgotten the ask.
|
|
77
|
+
- **Many tools confuse a small model.** Past about five definitions, it picks wrong or invents
|
|
78
|
+
arguments. Goliath gives each worker one.
|
|
79
|
+
- **Apple runs its loop out of sight.** Under the Callstack provider, tools are pre-registered and
|
|
80
|
+
executed inside Apple's own session; the AI SDK sees one step and `stopWhen` never fires. Goliath
|
|
81
|
+
never hands tools to the provider. It asks for the arguments as structured output, which Apple's
|
|
82
|
+
guided generation constrains at decode time, then runs the tool itself.
|
|
83
|
+
- **There is no confidence signal.** No logprobs on device. Goliath watches for the things a lost
|
|
84
|
+
3B model does: repeats itself, answers with nothing, runs past the cap.
|
|
85
|
+
|
|
86
|
+
## What you supply
|
|
87
|
+
|
|
88
|
+
| Option | Default | Notes |
|
|
89
|
+
| -------------- | ------------------- | -------------------------------------------------------------------------- |
|
|
90
|
+
| `model` | required | Any AI SDK `LanguageModel` |
|
|
91
|
+
| `tools` | `{}` | Keep to five or fewer per Goliath. Flat schemas. One-sentence descriptions |
|
|
92
|
+
| `memory` | in-process | `{ load, save }` over `{ summary, recent }`. Persist it however you like |
|
|
93
|
+
| `fallback` | none | Receives the ask, the brief, the step log, and the reason. Returns text |
|
|
94
|
+
| `confirm` | approve all | Asked before any `writes: true` tool runs |
|
|
95
|
+
| `window` | `4096` | Apple Foundation Models. The brief is budgeted at one eighth of it |
|
|
96
|
+
| `maxSteps` | `5` | Five stones |
|
|
97
|
+
| `instructions` | a careful assistant | One or two sentences. Every prompt starts with it |
|
|
98
|
+
| `onEvent` | none | Every trace event as it happens: plan, tool, confirm, escalate, remember |
|
|
99
|
+
|
|
100
|
+
## Testing without a phone
|
|
101
|
+
|
|
102
|
+
```ts
|
|
103
|
+
import { fakeModel } from "@hellohelen-ai/goliath/testing";
|
|
104
|
+
|
|
105
|
+
const model = fakeModel([
|
|
106
|
+
{ json: { kind: "tool", tool: "listTasks", brief: "see what is open" } },
|
|
107
|
+
{ toolCall: { name: "listTasks", input: {} } },
|
|
108
|
+
{ json: { kind: "answer", brief: "reply" } },
|
|
109
|
+
{ text: "You have two tasks: buy milk and call mom." },
|
|
110
|
+
]);
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
The script is consumed in order and a test fails if it runs out, so a passing test proves the
|
|
114
|
+
harness sent exactly the prompts you expected. `model.calls` holds every prompt for assertions.
|
|
115
|
+
|
|
116
|
+
## Evals
|
|
117
|
+
|
|
118
|
+
`evals/fixtures.ts` holds the asks a personal assistant hears every day, with the tool calls a good
|
|
119
|
+
run makes and where it should finish. `runEvals` scores any model against them and prints the
|
|
120
|
+
split:
|
|
121
|
+
|
|
122
|
+
```
|
|
123
|
+
PASS list-today device 412ms
|
|
124
|
+
PASS add-task device 655ms
|
|
125
|
+
PASS add-after-check device 1203ms
|
|
126
|
+
PASS small-talk device 198ms
|
|
127
|
+
PASS plan-week cloud 301ms
|
|
128
|
+
|
|
129
|
+
5/5 passed · 4 on device · 1 escalated
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
That last line is the number this project exists for.
|
|
133
|
+
|
|
134
|
+
## Status
|
|
135
|
+
|
|
136
|
+
Early. The core loop, compression, memory, judge, and eval runner are here and tested against a
|
|
137
|
+
scripted model. On-device runs and the Apple provider adapter are next. See `docs/` for the
|
|
138
|
+
research behind the design.
|
|
139
|
+
|
|
140
|
+
## License
|
|
141
|
+
|
|
142
|
+
MIT
|
package/dist/budget.d.ts
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { ModelMessage } from "ai";
|
|
2
|
+
/**
|
|
3
|
+
* Tokens, estimated without a tokenizer. English runs near four characters
|
|
4
|
+
* per token; the margin covers code, names, and JSON, which run denser.
|
|
5
|
+
*/
|
|
6
|
+
declare const estimateTokens: (text: string) => number;
|
|
7
|
+
declare const textOf: (message: ModelMessage) => string;
|
|
8
|
+
declare const messageTokens: (message: ModelMessage) => number;
|
|
9
|
+
declare const transcriptTokens: (messages: ModelMessage[]) => number;
|
|
10
|
+
/**
|
|
11
|
+
* Drop the oldest non-system messages until the transcript fits. The first
|
|
12
|
+
* message survives when it is the system prompt; the last always survives.
|
|
13
|
+
*/
|
|
14
|
+
declare const fitWithin: (messages: ModelMessage[], budget: number) => ModelMessage[];
|
|
15
|
+
export { estimateTokens, fitWithin, messageTokens, textOf, transcriptTokens };
|
|
16
|
+
//# sourceMappingURL=budget.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"budget.d.ts","sourceRoot":"","sources":["../src/budget.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,IAAI,CAAC;AAEvC;;;GAGG;AACH,QAAA,MAAM,cAAc,GAAI,MAAM,MAAM,KAAG,MAA6C,CAAC;AAErF,QAAA,MAAM,MAAM,GAAI,SAAS,YAAY,KAAG,MAUvC,CAAC;AAEF,QAAA,MAAM,aAAa,GAAI,SAAS,YAAY,KAAG,MAA6C,CAAC;AAE7F,QAAA,MAAM,gBAAgB,GAAI,UAAU,YAAY,EAAE,KAAG,MACe,CAAC;AAErE;;;GAGG;AACH,QAAA,MAAM,SAAS,GAAI,UAAU,YAAY,EAAE,EAAE,QAAQ,MAAM,KAAG,YAAY,EAOzE,CAAC;AAEF,OAAO,EAAE,cAAc,EAAE,SAAS,EAAE,aAAa,EAAE,MAAM,EAAE,gBAAgB,EAAE,CAAC"}
|
package/dist/budget.js
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tokens, estimated without a tokenizer. English runs near four characters
|
|
3
|
+
* per token; the margin covers code, names, and JSON, which run denser.
|
|
4
|
+
*/
|
|
5
|
+
const estimateTokens = (text) => Math.ceil((text.length / 4) * 1.15);
|
|
6
|
+
const textOf = (message) => {
|
|
7
|
+
if (typeof message.content === "string")
|
|
8
|
+
return message.content;
|
|
9
|
+
return message.content
|
|
10
|
+
.map((part) => {
|
|
11
|
+
if (part.type === "text")
|
|
12
|
+
return part.text;
|
|
13
|
+
if (part.type === "tool-call")
|
|
14
|
+
return JSON.stringify(part.input ?? {});
|
|
15
|
+
if (part.type === "tool-result")
|
|
16
|
+
return JSON.stringify(part.output ?? "");
|
|
17
|
+
return "";
|
|
18
|
+
})
|
|
19
|
+
.join(" ");
|
|
20
|
+
};
|
|
21
|
+
const messageTokens = (message) => estimateTokens(textOf(message)) + 4;
|
|
22
|
+
const transcriptTokens = (messages) => messages.reduce((sum, message) => sum + messageTokens(message), 0);
|
|
23
|
+
/**
|
|
24
|
+
* Drop the oldest non-system messages until the transcript fits. The first
|
|
25
|
+
* message survives when it is the system prompt; the last always survives.
|
|
26
|
+
*/
|
|
27
|
+
const fitWithin = (messages, budget) => {
|
|
28
|
+
const kept = [...messages];
|
|
29
|
+
const protectedHead = kept[0]?.role === "system" ? 1 : 0;
|
|
30
|
+
while (transcriptTokens(kept) > budget && kept.length > protectedHead + 1) {
|
|
31
|
+
kept.splice(protectedHead, 1);
|
|
32
|
+
}
|
|
33
|
+
return kept;
|
|
34
|
+
};
|
|
35
|
+
export { estimateTokens, fitWithin, messageTokens, textOf, transcriptTokens };
|
|
36
|
+
//# sourceMappingURL=budget.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"budget.js","sourceRoot":"","sources":["../src/budget.ts"],"names":[],"mappings":"AAEA;;;GAGG;AACH,MAAM,cAAc,GAAG,CAAC,IAAY,EAAU,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC;AAErF,MAAM,MAAM,GAAG,CAAC,OAAqB,EAAU,EAAE;IAC/C,IAAI,OAAO,OAAO,CAAC,OAAO,KAAK,QAAQ;QAAE,OAAO,OAAO,CAAC,OAAO,CAAC;IAChE,OAAO,OAAO,CAAC,OAAO;SACnB,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE;QACZ,IAAI,IAAI,CAAC,IAAI,KAAK,MAAM;YAAE,OAAO,IAAI,CAAC,IAAI,CAAC;QAC3C,IAAI,IAAI,CAAC,IAAI,KAAK,WAAW;YAAE,OAAO,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,KAAK,IAAI,EAAE,CAAC,CAAC;QACvE,IAAI,IAAI,CAAC,IAAI,KAAK,aAAa;YAAE,OAAO,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,MAAM,IAAI,EAAE,CAAC,CAAC;QAC1E,OAAO,EAAE,CAAC;IACZ,CAAC,CAAC;SACD,IAAI,CAAC,GAAG,CAAC,CAAC;AACf,CAAC,CAAC;AAEF,MAAM,aAAa,GAAG,CAAC,OAAqB,EAAU,EAAE,CAAC,cAAc,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,GAAG,CAAC,CAAC;AAE7F,MAAM,gBAAgB,GAAG,CAAC,QAAwB,EAAU,EAAE,CAC5D,QAAQ,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,OAAO,EAAE,EAAE,CAAC,GAAG,GAAG,aAAa,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC,CAAC;AAErE;;;GAGG;AACH,MAAM,SAAS,GAAG,CAAC,QAAwB,EAAE,MAAc,EAAkB,EAAE;IAC7E,MAAM,IAAI,GAAG,CAAC,GAAG,QAAQ,CAAC,CAAC;IAC3B,MAAM,aAAa,GAAG,IAAI,CAAC,CAAC,CAAC,EAAE,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IACzD,OAAO,gBAAgB,CAAC,IAAI,CAAC,GAAG,MAAM,IAAI,IAAI,CAAC,MAAM,GAAG,aAAa,GAAG,CAAC,EAAE,CAAC;QAC1E,IAAI,CAAC,MAAM,CAAC,aAAa,EAAE,CAAC,CAAC,CAAC;IAChC,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC,CAAC;AAEF,OAAO,EAAE,cAAc,EAAE,SAAS,EAAE,aAAa,EAAE,MAAM,EAAE,gBAAgB,EAAE,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"structural.d.ts","sourceRoot":"","sources":["../../src/compress/structural.ts"],"names":[],"mappings":"AAcA,QAAA,MAAM,IAAI,GAAI,MAAM,MAAM,EAAE,KAAK,MAAM,KAAG,MAC0B,CAAC;AAyBrE,QAAA,MAAM,mBAAmB,GAAI,OAAO,OAAO,EAAE,iBAA4B,KAAG,MA2B3E,CAAC;AAEF,OAAO,EAAE,mBAAmB,EAAE,IAAI,EAAE,CAAC"}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Turn a tool result into the fewest characters the model still needs.
|
|
3
|
+
* JSON becomes `key: value` lines, arrays become numbered lines, long
|
|
4
|
+
* strings get cut. No model call, so this runs on every result.
|
|
5
|
+
*/
|
|
6
|
+
const DEFAULT_MAX_CHARS = 600;
|
|
7
|
+
/** Long lists show the first items, a count, and the last items (deepagents, Grok Bot). */
|
|
8
|
+
const HEAD_ITEMS = 5;
|
|
9
|
+
const TAIL_ITEMS = 2;
|
|
10
|
+
const MAX_ITEMS = HEAD_ITEMS + TAIL_ITEMS;
|
|
11
|
+
const MAX_VALUE_CHARS = 80;
|
|
12
|
+
/** A line that looks like a failure survives any cut (OpenClaw's diagnostic tail). */
|
|
13
|
+
const ERROR_LINE = /\b(error|exception|failed|fatal|denied|timed?\s?out)\b/i;
|
|
14
|
+
const clip = (text, max) => text.length <= max ? text : `${text.slice(0, max - 1).trimEnd()}…`;
|
|
15
|
+
const scalar = (value) => {
|
|
16
|
+
if (value === null || value === undefined)
|
|
17
|
+
return "";
|
|
18
|
+
if (typeof value === "string")
|
|
19
|
+
return clip(value, MAX_VALUE_CHARS);
|
|
20
|
+
if (typeof value === "number" || typeof value === "boolean")
|
|
21
|
+
return String(value);
|
|
22
|
+
return "";
|
|
23
|
+
};
|
|
24
|
+
const isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
25
|
+
const lineFor = (value) => {
|
|
26
|
+
if (isRecord(value)) {
|
|
27
|
+
return Object.entries(value)
|
|
28
|
+
.map(([key, item]) => {
|
|
29
|
+
const text = scalar(item);
|
|
30
|
+
return text ? `${key}: ${text}` : "";
|
|
31
|
+
})
|
|
32
|
+
.filter(Boolean)
|
|
33
|
+
.join(", ");
|
|
34
|
+
}
|
|
35
|
+
return scalar(value);
|
|
36
|
+
};
|
|
37
|
+
const summarizeToolResult = (value, maxChars = DEFAULT_MAX_CHARS) => {
|
|
38
|
+
if (value === undefined || value === null)
|
|
39
|
+
return "done";
|
|
40
|
+
if (typeof value === "string")
|
|
41
|
+
return value.trim() ? clip(value, maxChars) : "(no output)";
|
|
42
|
+
if (Array.isArray(value)) {
|
|
43
|
+
if (value.length === 0)
|
|
44
|
+
return "none";
|
|
45
|
+
const line = (item, i) => `${i + 1}. ${lineFor(item)}`;
|
|
46
|
+
if (value.length <= MAX_ITEMS)
|
|
47
|
+
return clip(value.map(line).join("\n"), maxChars);
|
|
48
|
+
const head = value.slice(0, HEAD_ITEMS).map(line);
|
|
49
|
+
const tail = value
|
|
50
|
+
.slice(-TAIL_ITEMS)
|
|
51
|
+
.map((item, i) => line(item, value.length - TAIL_ITEMS + i));
|
|
52
|
+
const omitted = value
|
|
53
|
+
.slice(HEAD_ITEMS, -TAIL_ITEMS)
|
|
54
|
+
.map((item, i) => line(item, HEAD_ITEMS + i))
|
|
55
|
+
.filter((text) => ERROR_LINE.test(text));
|
|
56
|
+
const shown = [...head, `… ${value.length - MAX_ITEMS} omitted`, ...omitted, ...tail];
|
|
57
|
+
return clip(shown.join("\n"), maxChars);
|
|
58
|
+
}
|
|
59
|
+
if (isRecord(value)) {
|
|
60
|
+
const lines = Object.entries(value).map(([key, item]) => Array.isArray(item) || isRecord(item)
|
|
61
|
+
? `${key}: ${lineFor(item) || summarizeToolResult(item, MAX_VALUE_CHARS)}`
|
|
62
|
+
: `${key}: ${scalar(item)}`);
|
|
63
|
+
return clip(lines.join("\n"), maxChars);
|
|
64
|
+
}
|
|
65
|
+
return clip(String(value), maxChars);
|
|
66
|
+
};
|
|
67
|
+
export { summarizeToolResult, clip };
|
|
68
|
+
//# sourceMappingURL=structural.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"structural.js","sourceRoot":"","sources":["../../src/compress/structural.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { type LanguageModel } from "ai";
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
import type { PlanExample, StepRecord, ToolMap, TraceEvent } from "./types.js";
|
|
4
|
+
declare const planSchema: z.ZodObject<{
|
|
5
|
+
why: z.ZodOptional<z.ZodString>;
|
|
6
|
+
kind: z.ZodEnum<{
|
|
7
|
+
tool: "tool";
|
|
8
|
+
answer: "answer";
|
|
9
|
+
escalate: "escalate";
|
|
10
|
+
}>;
|
|
11
|
+
tool: z.ZodOptional<z.ZodString>;
|
|
12
|
+
brief: z.ZodString;
|
|
13
|
+
}, z.core.$strip>;
|
|
14
|
+
type Plan = z.infer<typeof planSchema>;
|
|
15
|
+
/**
|
|
16
|
+
* The same shape with `tool` as an enum of the registered names. Under
|
|
17
|
+
* constrained decoding an enum cannot be misspelled or invented (Apple's
|
|
18
|
+
* guarantee only holds when the name is in the schema).
|
|
19
|
+
*/
|
|
20
|
+
declare const planSchemaFor: (toolNames: string[]) => z.ZodObject<{
|
|
21
|
+
why: z.ZodOptional<z.ZodString>;
|
|
22
|
+
kind: z.ZodEnum<{
|
|
23
|
+
tool: "tool";
|
|
24
|
+
answer: "answer";
|
|
25
|
+
escalate: "escalate";
|
|
26
|
+
}>;
|
|
27
|
+
tool: z.ZodOptional<z.ZodString>;
|
|
28
|
+
brief: z.ZodString;
|
|
29
|
+
}, z.core.$strip> | z.ZodObject<{
|
|
30
|
+
why: z.ZodOptional<z.ZodString>;
|
|
31
|
+
kind: z.ZodEnum<{
|
|
32
|
+
tool: "tool";
|
|
33
|
+
answer: "answer";
|
|
34
|
+
escalate: "escalate";
|
|
35
|
+
}>;
|
|
36
|
+
tool: z.ZodOptional<z.ZodEnum<{
|
|
37
|
+
[x: string]: string;
|
|
38
|
+
}>>;
|
|
39
|
+
brief: z.ZodString;
|
|
40
|
+
}, z.core.$strip>;
|
|
41
|
+
type PlanOutcome = {
|
|
42
|
+
ok: true;
|
|
43
|
+
plan: Plan;
|
|
44
|
+
} | {
|
|
45
|
+
ok: false;
|
|
46
|
+
reason: "plan-invalid" | "no-such-tool";
|
|
47
|
+
hint: string;
|
|
48
|
+
};
|
|
49
|
+
/**
|
|
50
|
+
* Apple's window is input plus output, and the overflow error kills the
|
|
51
|
+
* session, so the conductor's prompt must stay well under it. Past this share
|
|
52
|
+
* of the window, older step results are clipped to a line before prompting.
|
|
53
|
+
*/
|
|
54
|
+
declare const PROMPT_SHARE = 0.7;
|
|
55
|
+
declare const trimSteps: (steps: StepRecord[]) => StepRecord[];
|
|
56
|
+
/** Ask the model what to do next, with only the ask, the brief, and the step log in view. */
|
|
57
|
+
declare const plan: (input: {
|
|
58
|
+
model: LanguageModel;
|
|
59
|
+
instructions: string;
|
|
60
|
+
tools: ToolMap;
|
|
61
|
+
ask: string;
|
|
62
|
+
summary: string;
|
|
63
|
+
steps: StepRecord[];
|
|
64
|
+
maxSteps: number;
|
|
65
|
+
window: number;
|
|
66
|
+
emit?: (event: TraceEvent) => void;
|
|
67
|
+
/** Why the previous plan was rejected, fed back on the one retry. */
|
|
68
|
+
retryHint?: string;
|
|
69
|
+
facts?: Record<string, string>;
|
|
70
|
+
examples?: PlanExample[];
|
|
71
|
+
signal?: AbortSignal;
|
|
72
|
+
}) => Promise<PlanOutcome>;
|
|
73
|
+
export { plan, planSchema, planSchemaFor, trimSteps, PROMPT_SHARE };
|
|
74
|
+
export type { Plan, PlanOutcome };
|
|
75
|
+
//# sourceMappingURL=conductor.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"conductor.d.ts","sourceRoot":"","sources":["../src/conductor.ts"],"names":[],"mappings":"AAAA,OAAO,EAAgD,KAAK,aAAa,EAAE,MAAM,IAAI,CAAC;AACtF,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAIxB,OAAO,KAAK,EAAE,WAAW,EAAE,UAAU,EAAE,OAAO,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAc/E,QAAA,MAAM,UAAU;;;;;;;;;iBAKd,CAAC;AAEH,KAAK,IAAI,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,UAAU,CAAC,CAAC;AAEvC;;;;GAIG;AACH,QAAA,MAAM,aAAa,GAAI,WAAW,MAAM,EAAE;;;;;;;;;;;;;;;;;;;;iBAQ1B,CAAC;AAEjB,KAAK,WAAW,GACd;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,IAAI,EAAE,IAAI,CAAA;CAAE,GAAG;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,MAAM,EAAE,cAAc,GAAG,cAAc,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC;AAUlG;;;;GAIG;AACH,QAAA,MAAM,YAAY,MAAM,CAAC;AAGzB,QAAA,MAAM,SAAS,GAAI,OAAO,UAAU,EAAE,KAAG,UAAU,EAKhD,CAAC;AAEJ,6FAA6F;AAC7F,QAAA,MAAM,IAAI,GAAU,OAAO;IACzB,KAAK,EAAE,aAAa,CAAC;IACrB,YAAY,EAAE,MAAM,CAAC;IACrB,KAAK,EAAE,OAAO,CAAC;IACf,GAAG,EAAE,MAAM,CAAC;IACZ,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,UAAU,EAAE,CAAC;IACpB,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,CAAC,KAAK,EAAE,UAAU,KAAK,IAAI,CAAC;IACnC,qEAAqE;IACrE,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC/B,QAAQ,CAAC,EAAE,WAAW,EAAE,CAAC;IACzB,MAAM,CAAC,EAAE,WAAW,CAAC;CACtB,KAAG,OAAO,CAAC,WAAW,CA+CtB,CAAC;AAEF,OAAO,EAAE,IAAI,EAAE,UAAU,EAAE,aAAa,EAAE,SAAS,EAAE,YAAY,EAAE,CAAC;AACpE,YAAY,EAAE,IAAI,EAAE,WAAW,EAAE,CAAC"}
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import { NoObjectGeneratedError, Output, generateText } from "ai";
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
import { estimateTokens } from "./budget.js";
|
|
4
|
+
import { clip } from "./compress/structural.js";
|
|
5
|
+
import { conductorSystem, conductorUser } from "./prompts.js";
|
|
6
|
+
/**
|
|
7
|
+
* The next stone. Flat on purpose: three fields is what a 3B model fills in
|
|
8
|
+
* reliably under constrained decoding.
|
|
9
|
+
*/
|
|
10
|
+
/**
|
|
11
|
+
* Field order is generation order under guided generation, so `why` comes
|
|
12
|
+
* first: a rationale before the decision is worth ~10 points at this model
|
|
13
|
+
* size (docs/research/round2/small-model-prompting.md § 2.1). It is one
|
|
14
|
+
* short sentence naming the fact that decides the step, not deliberation.
|
|
15
|
+
*/
|
|
16
|
+
const WHY = "One short sentence naming the fact that decides this step.";
|
|
17
|
+
const planSchema = z.object({
|
|
18
|
+
why: z.string().optional().describe(WHY),
|
|
19
|
+
kind: z.enum(["tool", "answer", "escalate"]),
|
|
20
|
+
tool: z.string().optional(),
|
|
21
|
+
brief: z.string(),
|
|
22
|
+
});
|
|
23
|
+
/**
|
|
24
|
+
* The same shape with `tool` as an enum of the registered names. Under
|
|
25
|
+
* constrained decoding an enum cannot be misspelled or invented (Apple's
|
|
26
|
+
* guarantee only holds when the name is in the schema).
|
|
27
|
+
*/
|
|
28
|
+
const planSchemaFor = (toolNames) => toolNames.length
|
|
29
|
+
? z.object({
|
|
30
|
+
why: z.string().optional().describe(WHY),
|
|
31
|
+
kind: z.enum(["tool", "answer", "escalate"]),
|
|
32
|
+
tool: z.enum(toolNames).optional(),
|
|
33
|
+
brief: z.string(),
|
|
34
|
+
})
|
|
35
|
+
: planSchema;
|
|
36
|
+
const planInvalidHint = (toolNames) => toolNames.length
|
|
37
|
+
? `Your last reply was not valid. Reply with JSON fields kind, tool, brief. tool must be one of: ${toolNames.join(", ")}, or leave it out and answer.`
|
|
38
|
+
: "Your last reply was not valid. Reply with JSON fields kind, tool, brief.";
|
|
39
|
+
const noSuchToolHint = (name, toolNames) => `No such tool available: ${name ?? "(none)"}. Pick one of: ${toolNames.join(", ")}, or answer.`;
|
|
40
|
+
/**
|
|
41
|
+
* Apple's window is input plus output, and the overflow error kills the
|
|
42
|
+
* session, so the conductor's prompt must stay well under it. Past this share
|
|
43
|
+
* of the window, older step results are clipped to a line before prompting.
|
|
44
|
+
*/
|
|
45
|
+
const PROMPT_SHARE = 0.7;
|
|
46
|
+
const CLIPPED_RESULT_CHARS = 100;
|
|
47
|
+
const trimSteps = (steps) => steps.map((step, i) => step.kind === "tool" && step.result && i < steps.length - 1
|
|
48
|
+
? { ...step, result: clip(step.result.split("\n")[0] ?? "", CLIPPED_RESULT_CHARS) }
|
|
49
|
+
: step);
|
|
50
|
+
/** Ask the model what to do next, with only the ask, the brief, and the step log in view. */
|
|
51
|
+
const plan = async (input) => {
|
|
52
|
+
const toolNames = Object.keys(input.tools);
|
|
53
|
+
const system = conductorSystem(input.instructions, input.tools, input.maxSteps, {
|
|
54
|
+
...(input.facts ? { facts: input.facts } : {}),
|
|
55
|
+
...(input.examples ? { examples: input.examples } : {}),
|
|
56
|
+
});
|
|
57
|
+
const limit = Math.floor(input.window * PROMPT_SHARE);
|
|
58
|
+
let steps = input.steps;
|
|
59
|
+
const render = (log) => {
|
|
60
|
+
const base = conductorUser({
|
|
61
|
+
ask: input.ask,
|
|
62
|
+
summary: input.summary,
|
|
63
|
+
steps: log,
|
|
64
|
+
maxSteps: input.maxSteps,
|
|
65
|
+
});
|
|
66
|
+
return input.retryHint ? `${base}\n\n${input.retryHint}` : base;
|
|
67
|
+
};
|
|
68
|
+
let prompt = render(steps);
|
|
69
|
+
let tokens = estimateTokens(system) + estimateTokens(prompt);
|
|
70
|
+
if (tokens > limit) {
|
|
71
|
+
steps = trimSteps(steps);
|
|
72
|
+
prompt = render(steps);
|
|
73
|
+
tokens = estimateTokens(system) + estimateTokens(prompt);
|
|
74
|
+
input.emit?.({ type: "budget", label: "conductor", tokens, limit });
|
|
75
|
+
}
|
|
76
|
+
try {
|
|
77
|
+
const result = await generateText({
|
|
78
|
+
model: input.model,
|
|
79
|
+
output: Output.object({ schema: planSchemaFor(toolNames) }),
|
|
80
|
+
system,
|
|
81
|
+
prompt,
|
|
82
|
+
...(input.signal ? { abortSignal: input.signal } : {}),
|
|
83
|
+
});
|
|
84
|
+
const next = result.output;
|
|
85
|
+
if (!next)
|
|
86
|
+
return { ok: false, reason: "plan-invalid", hint: planInvalidHint(toolNames) };
|
|
87
|
+
if (next.kind === "tool" && (!next.tool || !toolNames.includes(next.tool))) {
|
|
88
|
+
return { ok: false, reason: "no-such-tool", hint: noSuchToolHint(next.tool, toolNames) };
|
|
89
|
+
}
|
|
90
|
+
return { ok: true, plan: next };
|
|
91
|
+
}
|
|
92
|
+
catch (error) {
|
|
93
|
+
// Only a malformed plan is the model's mistake to retry. Guardrails, a dead
|
|
94
|
+
// session, or an unavailable model propagate so the turn escalates as model-error.
|
|
95
|
+
if (NoObjectGeneratedError.isInstance(error)) {
|
|
96
|
+
return { ok: false, reason: "plan-invalid", hint: planInvalidHint(toolNames) };
|
|
97
|
+
}
|
|
98
|
+
throw error;
|
|
99
|
+
}
|
|
100
|
+
};
|
|
101
|
+
export { plan, planSchema, planSchemaFor, trimSteps, PROMPT_SHARE };
|
|
102
|
+
//# sourceMappingURL=conductor.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"conductor.js","sourceRoot":"","sources":["../src/conductor.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,sBAAsB,EAAE,MAAM,EAAE,YAAY,EAAsB,MAAM,IAAI,CAAC;AACtF,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAC7C,OAAO,EAAE,IAAI,EAAE,MAAM,0BAA0B,CAAC;AAChD,OAAO,EAAE,eAAe,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAG9D;;;GAGG;AACH;;;;;GAKG;AACH,MAAM,GAAG,GAAG,4DAA4D,CAAC;AAEzE,MAAM,UAAU,GAAG,CAAC,CAAC,MAAM,CAAC;IAC1B,GAAG,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,GAAG,CAAC;IACxC,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,MAAM,EAAE,QAAQ,EAAE,UAAU,CAAC,CAAC;IAC5C,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IAC3B,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE;CAClB,CAAC,CAAC;AAIH;;;;GAIG;AACH,MAAM,aAAa,GAAG,CAAC,SAAmB,EAAE,EAAE,CAC5C,SAAS,CAAC,MAAM;IACd,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC;QACP,GAAG,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,GAAG,CAAC;QACxC,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,MAAM,EAAE,QAAQ,EAAE,UAAU,CAAC,CAAC;QAC5C,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,SAAkC,CAAC,CAAC,QAAQ,EAAE;QAC3D,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE;KAClB,CAAC;IACJ,CAAC,CAAC,UAAU,CAAC;AAKjB,MAAM,eAAe,GAAG,CAAC,SAAmB,EAAU,EAAE,CACtD,SAAS,CAAC,MAAM;IACd,CAAC,CAAC,iGAAiG,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,+BAA+B;IACtJ,CAAC,CAAC,0EAA0E,CAAC;AAEjF,MAAM,cAAc,GAAG,CAAC,IAAwB,EAAE,SAAmB,EAAU,EAAE,CAC/E,2BAA2B,IAAI,IAAI,QAAQ,kBAAkB,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,cAAc,CAAC;AAElG;;;;GAIG;AACH,MAAM,YAAY,GAAG,GAAG,CAAC;AACzB,MAAM,oBAAoB,GAAG,GAAG,CAAC;AAEjC,MAAM,SAAS,GAAG,CAAC,KAAmB,EAAgB,EAAE,CACtD,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,CAAC,EAAE,EAAE,CACpB,IAAI,CAAC,IAAI,KAAK,MAAM,IAAI,IAAI,CAAC,MAAM,IAAI,CAAC,GAAG,KAAK,CAAC,MAAM,GAAG,CAAC;IACzD,CAAC,CAAC,EAAE,GAAG,IAAI,EAAE,MAAM,EAAE,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,EAAE,oBAAoB,CAAC,EAAE;IACnF,CAAC,CAAC,IAAI,CACT,CAAC;AAEJ,6FAA6F;AAC7F,MAAM,IAAI,GAAG,KAAK,EAAE,KAenB,EAAwB,EAAE;IACzB,MAAM,SAAS,GAAG,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;IAC3C,MAAM,MAAM,GAAG,eAAe,CAAC,KAAK,CAAC,YAAY,EAAE,KAAK,CAAC,KAAK,EAAE,KAAK,CAAC,QAAQ,EAAE;QAC9E,GAAG,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,KAAK,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QAC9C,GAAG,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE,QAAQ,EAAE,KAAK,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;KACxD,CAAC,CAAC;IACH,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,MAAM,GAAG,YAAY,CAAC,CAAC;IACtD,IAAI,KAAK,GAAG,KAAK,CAAC,KAAK,CAAC;IACxB,MAAM,MAAM,GAAG,CAAC,GAAiB,EAAE,EAAE;QACnC,MAAM,IAAI,GAAG,aAAa,CAAC;YACzB,GAAG,EAAE,KAAK,CAAC,GAAG;YACd,OAAO,EAAE,KAAK,CAAC,OAAO;YACtB,KAAK,EAAE,GAAG;YACV,QAAQ,EAAE,KAAK,CAAC,QAAQ;SACzB,CAAC,CAAC;QACH,OAAO,KAAK,CAAC,SAAS,CAAC,CAAC,CAAC,GAAG,IAAI,OAAO,KAAK,CAAC,SAAS,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC;IAClE,CAAC,CAAC;IACF,IAAI,MAAM,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC;IAC3B,IAAI,MAAM,GAAG,cAAc,CAAC,MAAM,CAAC,GAAG,cAAc,CAAC,MAAM,CAAC,CAAC;IAC7D,IAAI,MAAM,GAAG,KAAK,EAAE,CAAC;QACnB,KAAK,GAAG,SAAS,CAAC,KAAK,CAAC,CAAC;QACzB,MAAM,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC;QACvB,MAAM,GAAG,cAAc,CAAC,MAAM,CAAC,GAAG,cAAc,CAAC,MAAM,CAAC,CAAC;QACzD,KAAK,CAAC,IAAI,EAAE,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,KAAK,EAAE,WAAW,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,CAAC;IACtE,CAAC;IACD,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,MAAM,YAAY,CAAC;YAChC,KAAK,EAAE,KAAK,CAAC,KAAK;YAClB,MAAM,EAAE,MAAM,CAAC,MAAM,CAAC,EAAE,MAAM,EAAE,aAAa,CAAC,SAAS,CAAC,EAAE,CAAC;YAC3D,MAAM;YACN,MAAM;YACN,GAAG,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,WAAW,EAAE,KAAK,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;SACvD,CAAC,CAAC;QACH,MAAM,IAAI,GAAG,MAAM,CAAC,MAAM,CAAC;QAC3B,IAAI,CAAC,IAAI;YAAE,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,cAAc,EAAE,IAAI,EAAE,eAAe,CAAC,SAAS,CAAC,EAAE,CAAC;QAC1F,IAAI,IAAI,CAAC,IAAI,KAAK,MAAM,IAAI,CAAC,CAAC,IAAI,CAAC,IAAI,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,EAAE,CAAC;YAC3E,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,cAAc,EAAE,IAAI,EAAE,cAAc,CAAC,IAAI,CAAC,IAAI,EAAE,SAAS,CAAC,EAAE,CAAC;QAC3F,CAAC;QACD,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC;IAClC,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,4EAA4E;QAC5E,mFAAmF;QACnF,IAAI,sBAAsB,CAAC,UAAU,CAAC,KAAK,CAAC,EAAE,CAAC;YAC7C,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,cAAc,EAAE,IAAI,EAAE,eAAe,CAAC,SAAS,CAAC,EAAE,CAAC;QACjF,CAAC;QACD,MAAM,KAAK,CAAC;IACd,CAAC;AACH,CAAC,CAAC;AAEF,OAAO,EAAE,IAAI,EAAE,UAAU,EAAE,aAAa,EAAE,SAAS,EAAE,YAAY,EAAE,CAAC"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { GoliathConfig, RunResult, TraceEvent } from "./types.js";
|
|
2
|
+
declare const DEFAULT_WINDOW = 4096;
|
|
3
|
+
declare const DEFAULT_MAX_STEPS = 5;
|
|
4
|
+
/** Consecutive model errors before the rest of the session skips the device. */
|
|
5
|
+
declare const SESSION_FALLBACK_AFTER = 3;
|
|
6
|
+
type RunOptions = {
|
|
7
|
+
signal?: AbortSignal;
|
|
8
|
+
onEvent?: (event: TraceEvent) => void;
|
|
9
|
+
};
|
|
10
|
+
type Goliath = {
|
|
11
|
+
run: (ask: string, options?: RunOptions) => Promise<RunResult>;
|
|
12
|
+
/** True once three turns in a row died on the device; later turns go straight to the cloud. */
|
|
13
|
+
readonly sessionFallback: boolean;
|
|
14
|
+
};
|
|
15
|
+
/**
|
|
16
|
+
* Build a Goliath. Give it a model, the tools the app allows, somewhere to
|
|
17
|
+
* remember, and somewhere to send what the phone cannot finish.
|
|
18
|
+
*/
|
|
19
|
+
declare const createGoliath: (config: GoliathConfig) => Goliath;
|
|
20
|
+
export { createGoliath, DEFAULT_MAX_STEPS, DEFAULT_WINDOW, SESSION_FALLBACK_AFTER };
|
|
21
|
+
export type { Goliath, RunOptions };
|
|
22
|
+
//# sourceMappingURL=create-goliath.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"create-goliath.d.ts","sourceRoot":"","sources":["../src/create-goliath.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,aAAa,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAEvE,QAAA,MAAM,cAAc,OAAO,CAAC;AAC5B,QAAA,MAAM,iBAAiB,IAAI,CAAC;AAC5B,gFAAgF;AAChF,QAAA,MAAM,sBAAsB,IAAI,CAAC;AAEjC,KAAK,UAAU,GAAG;IAChB,MAAM,CAAC,EAAE,WAAW,CAAC;IACrB,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,UAAU,KAAK,IAAI,CAAC;CACvC,CAAC;AAEF,KAAK,OAAO,GAAG;IACb,GAAG,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,UAAU,KAAK,OAAO,CAAC,SAAS,CAAC,CAAC;IAC/D,+FAA+F;IAC/F,QAAQ,CAAC,eAAe,EAAE,OAAO,CAAC;CACnC,CAAC;AAEF;;;GAGG;AACH,QAAA,MAAM,aAAa,GAAI,QAAQ,aAAa,KAAG,OA6D9C,CAAC;AAEF,OAAO,EAAE,aAAa,EAAE,iBAAiB,EAAE,cAAc,EAAE,sBAAsB,EAAE,CAAC;AACpF,YAAY,EAAE,OAAO,EAAE,UAAU,EAAE,CAAC"}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { inMemory } from "./memory/in-memory.js";
|
|
2
|
+
import { runTurn } from "./run-turn.js";
|
|
3
|
+
const DEFAULT_WINDOW = 4096;
|
|
4
|
+
const DEFAULT_MAX_STEPS = 5;
|
|
5
|
+
/** Consecutive model errors before the rest of the session skips the device. */
|
|
6
|
+
const SESSION_FALLBACK_AFTER = 3;
|
|
7
|
+
/**
|
|
8
|
+
* Build a Goliath. Give it a model, the tools the app allows, somewhere to
|
|
9
|
+
* remember, and somewhere to send what the phone cannot finish.
|
|
10
|
+
*/
|
|
11
|
+
const createGoliath = (config) => {
|
|
12
|
+
const memory = config.memory ?? inMemory();
|
|
13
|
+
const confirm = config.confirm ?? (async () => true);
|
|
14
|
+
// The conductor picks by name, so the map is keyed by each tool's own name,
|
|
15
|
+
// whatever the app called the property.
|
|
16
|
+
const tools = Object.fromEntries(Object.values(config.tools ?? {}).map((tool) => [tool.name, tool]));
|
|
17
|
+
let consecutiveModelErrors = 0;
|
|
18
|
+
const goliath = {
|
|
19
|
+
get sessionFallback() {
|
|
20
|
+
return consecutiveModelErrors >= SESSION_FALLBACK_AFTER;
|
|
21
|
+
},
|
|
22
|
+
run: async (ask, options = {}) => {
|
|
23
|
+
const emit = (event) => {
|
|
24
|
+
config.onEvent?.(event);
|
|
25
|
+
options.onEvent?.(event);
|
|
26
|
+
};
|
|
27
|
+
if (goliath.sessionFallback && config.fallback) {
|
|
28
|
+
// Claude Code switches models after three consecutive overloads. A
|
|
29
|
+
// device that has failed three turns running is not coming back this
|
|
30
|
+
// session; stop paying the on-device latency to find out.
|
|
31
|
+
const state = await memory.load();
|
|
32
|
+
emit({ type: "escalate", reason: "model-error", error: "session fallback" });
|
|
33
|
+
const { text } = await config.fallback({
|
|
34
|
+
ask,
|
|
35
|
+
summary: state.summary,
|
|
36
|
+
recent: state.recent,
|
|
37
|
+
steps: [],
|
|
38
|
+
reason: "model-error",
|
|
39
|
+
error: "session fallback",
|
|
40
|
+
...(options.signal ? { signal: options.signal } : {}),
|
|
41
|
+
});
|
|
42
|
+
return { text, handledBy: "cloud", steps: [], trace: [] };
|
|
43
|
+
}
|
|
44
|
+
const facts = typeof config.facts === "function" ? config.facts() : config.facts;
|
|
45
|
+
const result = await runTurn({
|
|
46
|
+
ask,
|
|
47
|
+
model: config.model,
|
|
48
|
+
tools,
|
|
49
|
+
memory,
|
|
50
|
+
confirm,
|
|
51
|
+
...(facts ? { facts } : {}),
|
|
52
|
+
...(config.examples ? { examples: config.examples } : {}),
|
|
53
|
+
...(config.fallback ? { fallback: config.fallback } : {}),
|
|
54
|
+
...(config.instructions ? { instructions: config.instructions } : {}),
|
|
55
|
+
maxSteps: config.maxSteps ?? DEFAULT_MAX_STEPS,
|
|
56
|
+
window: config.window ?? DEFAULT_WINDOW,
|
|
57
|
+
onEvent: emit,
|
|
58
|
+
...(options.signal ? { signal: options.signal } : {}),
|
|
59
|
+
});
|
|
60
|
+
const diedOnDevice = result.trace.some((e) => e.type === "escalate" && e.reason === "model-error");
|
|
61
|
+
consecutiveModelErrors = diedOnDevice ? consecutiveModelErrors + 1 : 0;
|
|
62
|
+
return result;
|
|
63
|
+
},
|
|
64
|
+
};
|
|
65
|
+
return goliath;
|
|
66
|
+
};
|
|
67
|
+
export { createGoliath, DEFAULT_MAX_STEPS, DEFAULT_WINDOW, SESSION_FALLBACK_AFTER };
|
|
68
|
+
//# sourceMappingURL=create-goliath.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"create-goliath.js","sourceRoot":"","sources":["../src/create-goliath.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { Fallback, FallbackRequest } from "../types.js";
|
|
2
|
+
type HttpFallbackOptions = {
|
|
3
|
+
url: string;
|
|
4
|
+
/** Static headers, or a function when the token rotates. */
|
|
5
|
+
headers?: Record<string, string> | (() => Promise<Record<string, string>>);
|
|
6
|
+
/** Override for tests. Defaults to the global `fetch`. */
|
|
7
|
+
fetch?: typeof fetch;
|
|
8
|
+
/** Pull the answer text out of the response body. Default reads `{ text }`. */
|
|
9
|
+
readText?: (body: unknown) => string;
|
|
10
|
+
};
|
|
11
|
+
type FallbackPayload = Omit<FallbackRequest, "signal">;
|
|
12
|
+
/**
|
|
13
|
+
* Send the turn to a server as JSON and read back `{ text }`. The payload is
|
|
14
|
+
* exactly what the conductor saw, so the cloud agent starts where the phone
|
|
15
|
+
* stopped instead of from scratch.
|
|
16
|
+
*/
|
|
17
|
+
declare const httpFallback: (options: HttpFallbackOptions) => Fallback;
|
|
18
|
+
export { httpFallback };
|
|
19
|
+
export type { FallbackPayload, HttpFallbackOptions };
|
|
20
|
+
//# sourceMappingURL=http-fallback.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"http-fallback.d.ts","sourceRoot":"","sources":["../../src/fallback/http-fallback.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAE7D,KAAK,mBAAmB,GAAG;IACzB,GAAG,EAAE,MAAM,CAAC;IACZ,4DAA4D;IAC5D,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,CAAC,MAAM,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC;IAC3E,0DAA0D;IAC1D,KAAK,CAAC,EAAE,OAAO,KAAK,CAAC;IACrB,+EAA+E;IAC/E,QAAQ,CAAC,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,MAAM,CAAC;CACtC,CAAC;AAEF,KAAK,eAAe,GAAG,IAAI,CAAC,eAAe,EAAE,QAAQ,CAAC,CAAC;AAEvD;;;;GAIG;AACH,QAAA,MAAM,YAAY,GAAI,SAAS,mBAAmB,KAAG,QAoBpD,CAAC;AAUF,OAAO,EAAE,YAAY,EAAE,CAAC;AACxB,YAAY,EAAE,eAAe,EAAE,mBAAmB,EAAE,CAAC"}
|