tinker-agent 1.0.65 → 1.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +36 -0
- package/LICENSE +201 -0
- package/NOTICE +6 -0
- package/README.md +90 -9
- package/bin/tinker.js +39 -0
- package/node_modules/ansi-regex/index.d.ts +33 -0
- package/node_modules/ansi-regex/index.js +14 -0
- package/node_modules/ansi-regex/license +9 -0
- package/node_modules/ansi-regex/package.json +61 -0
- package/node_modules/ansi-regex/readme.md +66 -0
- package/node_modules/ansi-styles/index.d.ts +236 -0
- package/node_modules/ansi-styles/index.js +223 -0
- package/node_modules/ansi-styles/license +9 -0
- package/node_modules/ansi-styles/package.json +54 -0
- package/node_modules/ansi-styles/readme.md +173 -0
- package/node_modules/chalk/license +9 -0
- package/node_modules/chalk/package.json +83 -0
- package/node_modules/chalk/readme.md +297 -0
- package/node_modules/chalk/source/index.d.ts +325 -0
- package/node_modules/chalk/source/index.js +225 -0
- package/node_modules/chalk/source/utilities.js +33 -0
- package/node_modules/chalk/source/vendor/ansi-styles/index.d.ts +236 -0
- package/node_modules/chalk/source/vendor/ansi-styles/index.js +223 -0
- package/node_modules/chalk/source/vendor/supports-color/browser.d.ts +1 -0
- package/node_modules/chalk/source/vendor/supports-color/browser.js +34 -0
- package/node_modules/chalk/source/vendor/supports-color/index.d.ts +55 -0
- package/node_modules/chalk/source/vendor/supports-color/index.js +190 -0
- package/node_modules/character-entities/index.d.ts +6 -0
- package/node_modules/character-entities/index.js +2132 -0
- package/node_modules/character-entities/license +22 -0
- package/node_modules/character-entities/package.json +78 -0
- package/node_modules/character-entities/readme.md +152 -0
- package/node_modules/decode-named-character-reference/index.d.ts +13 -0
- package/node_modules/decode-named-character-reference/index.d.ts.map +1 -0
- package/node_modules/decode-named-character-reference/index.dom.d.ts +6 -0
- package/node_modules/decode-named-character-reference/index.dom.d.ts.map +1 -0
- package/node_modules/decode-named-character-reference/index.dom.js +32 -0
- package/node_modules/decode-named-character-reference/index.js +19 -0
- package/node_modules/decode-named-character-reference/license +22 -0
- package/node_modules/decode-named-character-reference/package.json +90 -0
- package/node_modules/decode-named-character-reference/readme.md +136 -0
- package/node_modules/get-east-asian-width/index.d.ts +60 -0
- package/node_modules/get-east-asian-width/index.js +30 -0
- package/node_modules/get-east-asian-width/license +9 -0
- package/node_modules/get-east-asian-width/lookup-data.js +21 -0
- package/node_modules/get-east-asian-width/lookup.js +138 -0
- package/node_modules/get-east-asian-width/package.json +71 -0
- package/node_modules/get-east-asian-width/readme.md +65 -0
- package/node_modules/get-east-asian-width/utilities.js +24 -0
- package/node_modules/has-flag/index.d.ts +39 -0
- package/node_modules/has-flag/index.js +8 -0
- package/node_modules/has-flag/license +9 -0
- package/node_modules/has-flag/package.json +49 -0
- package/node_modules/has-flag/readme.md +74 -0
- package/node_modules/is-fullwidth-code-point/index.d.ts +17 -0
- package/node_modules/is-fullwidth-code-point/index.js +12 -0
- package/node_modules/is-fullwidth-code-point/license +9 -0
- package/node_modules/is-fullwidth-code-point/package.json +53 -0
- package/node_modules/is-fullwidth-code-point/readme.md +31 -0
- package/node_modules/markdansi/LICENSE +21 -0
- package/node_modules/markdansi/README.md +167 -0
- package/node_modules/markdansi/dist/ast.d.ts +99 -0
- package/node_modules/markdansi/dist/ast.js +1 -0
- package/node_modules/markdansi/dist/cli.d.ts +17 -0
- package/node_modules/markdansi/dist/cli.js +208 -0
- package/node_modules/markdansi/dist/hyperlink.d.ts +9 -0
- package/node_modules/markdansi/dist/hyperlink.js +26 -0
- package/node_modules/markdansi/dist/index.d.ts +10 -0
- package/node_modules/markdansi/dist/index.js +16 -0
- package/node_modules/markdansi/dist/parser.d.ts +2 -0
- package/node_modules/markdansi/dist/parser.js +205 -0
- package/node_modules/markdansi/dist/render.d.ts +9 -0
- package/node_modules/markdansi/dist/render.js +680 -0
- package/node_modules/markdansi/dist/stream.d.ts +39 -0
- package/node_modules/markdansi/dist/stream.js +192 -0
- package/node_modules/markdansi/dist/theme.d.ts +18 -0
- package/node_modules/markdansi/dist/theme.js +105 -0
- package/node_modules/markdansi/dist/types.d.ts +58 -0
- package/node_modules/markdansi/dist/types.js +1 -0
- package/node_modules/markdansi/dist/wrap.d.ts +10 -0
- package/node_modules/markdansi/dist/wrap.js +73 -0
- package/node_modules/markdansi/docs/spec.md +110 -0
- package/node_modules/markdansi/package.json +78 -0
- package/node_modules/markdansi/tsconfig.json +18 -0
- package/node_modules/marked/LICENSE +44 -0
- package/node_modules/marked/README.md +115 -0
- package/node_modules/marked/bin/main.js +284 -0
- package/node_modules/marked/bin/marked.js +16 -0
- package/node_modules/marked/lib/marked.d.ts +760 -0
- package/node_modules/marked/lib/marked.esm.js +77 -0
- package/node_modules/marked/lib/marked.esm.js.map +7 -0
- package/node_modules/marked/lib/marked.umd.js +79 -0
- package/node_modules/marked/lib/marked.umd.js.map +7 -0
- package/node_modules/marked/man/marked.1 +113 -0
- package/node_modules/marked/man/marked.1.md +93 -0
- package/node_modules/marked/package.json +103 -0
- package/node_modules/slice-ansi/index.d.ts +19 -0
- package/node_modules/slice-ansi/index.js +317 -0
- package/node_modules/slice-ansi/license +10 -0
- package/node_modules/slice-ansi/package.json +59 -0
- package/node_modules/slice-ansi/readme.md +55 -0
- package/node_modules/slice-ansi/tokenize-ansi.js +752 -0
- package/node_modules/string-width/index.d.ts +39 -0
- package/node_modules/string-width/index.js +203 -0
- package/node_modules/string-width/license +9 -0
- package/node_modules/string-width/package.json +65 -0
- package/node_modules/string-width/readme.md +66 -0
- package/node_modules/strip-ansi/index.d.ts +15 -0
- package/node_modules/strip-ansi/index.js +19 -0
- package/node_modules/strip-ansi/license +9 -0
- package/node_modules/strip-ansi/package.json +59 -0
- package/node_modules/strip-ansi/readme.md +37 -0
- package/node_modules/supports-color/browser.d.ts +1 -0
- package/node_modules/supports-color/browser.js +35 -0
- package/node_modules/supports-color/index.d.ts +55 -0
- package/node_modules/supports-color/index.js +202 -0
- package/node_modules/supports-color/license +9 -0
- package/node_modules/supports-color/package.json +64 -0
- package/node_modules/supports-color/readme.md +75 -0
- package/node_modules/supports-hyperlinks/browser.js +10 -0
- package/node_modules/supports-hyperlinks/index.d.ts +25 -0
- package/node_modules/supports-hyperlinks/index.js +163 -0
- package/node_modules/supports-hyperlinks/license +10 -0
- package/node_modules/supports-hyperlinks/package.json +47 -0
- package/node_modules/supports-hyperlinks/readme.md +45 -0
- package/package.json +41 -10
- package/patches/markdansi@0.3.2.patch +22 -2
- package/src/agent/context-builder.ts +24 -10
- package/src/agent/context-meter.ts +89 -5
- package/src/agent/loop.ts +107 -10
- package/src/agent/runtime-session.ts +1437 -82
- package/src/agent/session-ledger.ts +169 -29
- package/src/agent/types.ts +12 -1
- package/src/agent/user-prompt-projection.ts +62 -0
- package/src/cli/config.ts +57 -14
- package/src/cli/model-profiles.ts +124 -0
- package/src/cli/run-runner.ts +4 -1
- package/src/cli/tui-runner.tsx +68 -30
- package/src/context/compiled-context-hash.ts +1 -1
- package/src/context/compiled-context-validator.ts +92 -15
- package/src/context/context-automation-policy.ts +121 -0
- package/src/context/context-manager.ts +390 -11
- package/src/context/context-policy.ts +8 -0
- package/src/context/context-protocol-validator.ts +11 -1
- package/src/context/context-revision-compiler.ts +190 -27
- package/src/context/context-revision.ts +105 -19
- package/src/context/context-surface.ts +329 -0
- package/src/context/prefix-retirement-planner.ts +570 -0
- package/src/context/protocol-frame.ts +30 -3
- package/src/context/recall-retirement-contract.ts +27 -0
- package/src/context/swap-planner.ts +34 -10
- package/src/events/observation-text-log.ts +3 -3
- package/src/events/stdout-event-printer.ts +39 -3
- package/src/events/types.ts +172 -35
- package/src/ids/runtime-id.ts +6 -0
- package/src/ids/uuid-v7.ts +7 -0
- package/src/image/image-asset-store.ts +319 -0
- package/src/image/image-input-policy.ts +14 -0
- package/src/image/image-probe.ts +233 -0
- package/src/image/image-types.ts +262 -0
- package/src/instructions/project-instructions.ts +4 -1
- package/src/mcp/mcp-manager.ts +83 -19
- package/src/model/fake-model-client.ts +9 -1
- package/src/model/input-token-estimator.ts +25 -0
- package/src/model/model-client.ts +60 -0
- package/src/model/model-request-preflight.ts +1 -0
- package/src/model/moonshot-input-token-estimator.ts +107 -0
- package/src/model/openai-chat-mapping.ts +77 -1
- package/src/model/openai-chat-model-client.ts +389 -59
- package/src/model/openai-chat-stream.ts +272 -0
- package/src/model/prompt-prefix-hash.ts +2 -4
- package/src/model/token-estimator.ts +4 -0
- package/src/observation/observation-builder.ts +29 -0
- package/src/session/resume-projection.ts +94 -34
- package/src/session/session-catalog.ts +4 -4
- package/src/session/session-errors.ts +1 -1
- package/src/session/session-history-reader.ts +151 -4
- package/src/session/session-last-response-reader.ts +189 -0
- package/src/session/session-schema.ts +349 -35
- package/src/session/session-store.ts +3266 -255
- package/src/session/sqlite-session-ledger.ts +9 -4
- package/src/skills/skill-catalog.ts +182 -0
- package/src/skills/skill-context.ts +364 -0
- package/src/skills/skill-loader.ts +779 -0
- package/src/skills/skill-tool.ts +297 -0
- package/src/tools/edit.ts +51 -64
- package/src/tools/ensure-parent-directory.ts +6 -0
- package/src/tools/read.ts +2 -2
- package/src/tools/recall.ts +43 -40
- package/src/tools/registry.ts +24 -3
- package/src/tools/ripgrep.ts +3 -2
- package/src/tools/types.ts +43 -4
- package/src/tools/write.ts +20 -31
- package/src/tui/app.tsx +333 -96
- package/src/tui/clipboard.ts +5 -0
- package/src/tui/components/mcp-panel.tsx +28 -0
- package/src/tui/components/prompt-input.tsx +790 -257
- package/src/tui/components/skills-panel.tsx +31 -0
- package/src/tui/components/timeline.tsx +30 -1
- package/src/tui/event-store.ts +85 -2
- package/src/tui/file-mention.ts +234 -0
- package/src/tui/line-editor.ts +1 -0
- package/src/tui/project-slash-commands.ts +229 -0
- package/src/tui/prompt-draft.ts +510 -0
- package/src/tui/prompt-history.ts +294 -22
- package/src/tui/slash-commands.ts +46 -2
- package/src/tui/tui-session-controller.ts +110 -48
- package/src/tui/workspace-file-search.ts +94 -0
|
@@ -1,11 +1,21 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type {
|
|
2
|
+
ContextRevisionId,
|
|
3
|
+
MessageId,
|
|
4
|
+
RuntimeIdFactory,
|
|
5
|
+
SessionId,
|
|
6
|
+
ToolCallId,
|
|
7
|
+
} from "../ids/runtime-id";
|
|
2
8
|
import type { ToolDefinition } from "../tools/types";
|
|
3
9
|
import type {
|
|
4
10
|
BuiltContextRequest,
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
11
|
+
StoredContextRevisionV8,
|
|
12
|
+
StoredContextSnapshotV8,
|
|
13
|
+
StoredContextOverrideV8,
|
|
8
14
|
} from "../context/context-revision";
|
|
15
|
+
import {
|
|
16
|
+
createContextSurface,
|
|
17
|
+
type StoredContextSurfaceV8,
|
|
18
|
+
} from "../context/context-surface";
|
|
9
19
|
import {
|
|
10
20
|
ContextRevisionCompiler,
|
|
11
21
|
createInitialContextRevision,
|
|
@@ -15,12 +25,13 @@ import {
|
|
|
15
25
|
ContextProtocolValidator,
|
|
16
26
|
} from "../context/context-protocol-validator";
|
|
17
27
|
import {
|
|
18
|
-
|
|
28
|
+
CURRENT_TOOL_OBSERVATION_FORMAT,
|
|
19
29
|
contentHash,
|
|
20
30
|
immutableCanonicalClone,
|
|
21
31
|
immutableRecord,
|
|
22
32
|
observationForCompletion,
|
|
23
33
|
rawResultHash,
|
|
34
|
+
userMessageHash,
|
|
24
35
|
type CanonicalMessageRecord,
|
|
25
36
|
type ProtocolContextView,
|
|
26
37
|
type ProtocolFrame,
|
|
@@ -29,19 +40,27 @@ import {
|
|
|
29
40
|
type ToolResultRecord,
|
|
30
41
|
} from "../context/protocol-frame";
|
|
31
42
|
import { ContextBuilder } from "./context-builder";
|
|
43
|
+
import { CURRENT_RECALL_RETIREMENT_CONTRACT_VERSION } from "../context/recall-retirement-contract";
|
|
32
44
|
import type {
|
|
33
45
|
AssistantMessage,
|
|
34
46
|
IterationIdentity,
|
|
35
47
|
RunAgentResult,
|
|
36
48
|
ToolCall,
|
|
37
49
|
TurnIdentity,
|
|
50
|
+
UserMessage,
|
|
38
51
|
} from "./types";
|
|
52
|
+
import { sha256, stableJsonStringify } from "../model/model-request-preflight";
|
|
53
|
+
import { validateUserMessage } from "../image/image-types";
|
|
39
54
|
|
|
40
55
|
export type SessionLedger = {
|
|
41
|
-
beginTurn(input: {
|
|
56
|
+
beginTurn(input: {
|
|
57
|
+
turn: TurnIdentity;
|
|
58
|
+
userMessage: UserMessage;
|
|
59
|
+
admissionBase?: AdmissionBaseToken;
|
|
60
|
+
}): PendingLedgerTurn;
|
|
42
61
|
buildCommittedModelRequest(tools: readonly ToolDefinition[]): BuiltContextRequest;
|
|
43
62
|
buildCandidateModelRequest(
|
|
44
|
-
|
|
63
|
+
userMessage: UserMessage,
|
|
45
64
|
tools: readonly ToolDefinition[],
|
|
46
65
|
): BuiltContextRequest;
|
|
47
66
|
committedMessageCount(): number;
|
|
@@ -53,6 +72,26 @@ export type SessionLedger = {
|
|
|
53
72
|
fault(error: unknown): void;
|
|
54
73
|
};
|
|
55
74
|
|
|
75
|
+
export type AdmissionBaseToken = {
|
|
76
|
+
readonly canonicalMessageCount: number;
|
|
77
|
+
readonly canonicalHeadMessageId: MessageId;
|
|
78
|
+
readonly canonicalHeadContentSha256: string;
|
|
79
|
+
readonly activeRevisionId: ContextRevisionId;
|
|
80
|
+
readonly activeRevisionNumber: number;
|
|
81
|
+
readonly surfaceSha256: string;
|
|
82
|
+
readonly sessionCompatibilitySha256: string;
|
|
83
|
+
readonly nextTurnNumber: number;
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
export class AdmissionStaleError extends Error {
|
|
87
|
+
readonly code = "ADMISSION_STALE";
|
|
88
|
+
|
|
89
|
+
constructor() {
|
|
90
|
+
super("Turn admission became stale before commit; the Prompt was not accepted.");
|
|
91
|
+
this.name = "AdmissionStaleError";
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
56
95
|
export type PendingLedgerTurn = {
|
|
57
96
|
readonly agent: AgentTurnLedger;
|
|
58
97
|
projectedMessageCount(): number;
|
|
@@ -68,16 +107,25 @@ export type AgentTurnLedger = {
|
|
|
68
107
|
model: string;
|
|
69
108
|
}): void;
|
|
70
109
|
assertCanExecuteTool(call: ToolCall): void;
|
|
71
|
-
commitToolCompletions(
|
|
110
|
+
commitToolCompletions(
|
|
111
|
+
completions: readonly ToolCompletionInput[],
|
|
112
|
+
): readonly CommittedToolCompletion[];
|
|
72
113
|
buildModelRequest(tools: readonly ToolDefinition[]): BuiltContextRequest;
|
|
73
114
|
};
|
|
74
115
|
|
|
116
|
+
export type CommittedToolCompletion = {
|
|
117
|
+
readonly toolCallId: ToolCallId;
|
|
118
|
+
readonly toolMessageId: MessageId;
|
|
119
|
+
readonly ordinal: number;
|
|
120
|
+
};
|
|
121
|
+
|
|
75
122
|
export type LedgerMutation =
|
|
76
123
|
| {
|
|
77
124
|
kind: "begin_turn";
|
|
78
125
|
turn: TurnIdentity;
|
|
79
126
|
frame: ProtocolFrame;
|
|
80
127
|
message: CanonicalMessageRecord;
|
|
128
|
+
admissionBase?: AdmissionBaseToken;
|
|
81
129
|
next: ProtocolContextView;
|
|
82
130
|
}
|
|
83
131
|
| {
|
|
@@ -122,7 +170,9 @@ export type CreateInMemorySessionLedgerInput = {
|
|
|
122
170
|
idFactory: RuntimeIdFactory;
|
|
123
171
|
systemPrompt?: string;
|
|
124
172
|
initialView?: ProtocolContextView;
|
|
125
|
-
initialSnapshot?:
|
|
173
|
+
initialSnapshot?: StoredContextSnapshotV8;
|
|
174
|
+
initialSurface?: StoredContextSurfaceV8;
|
|
175
|
+
initialToolDefinitions?: readonly ToolDefinition[];
|
|
126
176
|
initialRevisionId?: ContextRevisionId;
|
|
127
177
|
contextBuilder?: ContextBuilder;
|
|
128
178
|
revisionCompiler?: ContextRevisionCompiler;
|
|
@@ -138,8 +188,9 @@ export class InMemorySessionLedger implements SessionLedger {
|
|
|
138
188
|
private readonly validator = new ContextProtocolValidator();
|
|
139
189
|
private readonly contextBuilder: ContextBuilder;
|
|
140
190
|
private readonly revisionCompiler: ContextRevisionCompiler;
|
|
141
|
-
private readonly revision:
|
|
142
|
-
private readonly
|
|
191
|
+
private readonly revision: StoredContextRevisionV8;
|
|
192
|
+
private readonly surface: StoredContextSurfaceV8;
|
|
193
|
+
private readonly activeOverrides: readonly StoredContextOverrideV8[];
|
|
143
194
|
private readonly clock: () => string;
|
|
144
195
|
|
|
145
196
|
constructor(private readonly input: CreateInMemorySessionLedgerInput) {
|
|
@@ -162,6 +213,7 @@ export class InMemorySessionLedger implements SessionLedger {
|
|
|
162
213
|
}
|
|
163
214
|
this.view = immutableView(input.initialSnapshot.canonical);
|
|
164
215
|
this.revision = input.initialSnapshot.revision;
|
|
216
|
+
this.surface = input.initialSnapshot.surface;
|
|
165
217
|
this.activeOverrides = input.initialSnapshot.activeOverrides;
|
|
166
218
|
} else {
|
|
167
219
|
this.view =
|
|
@@ -173,17 +225,32 @@ export class InMemorySessionLedger implements SessionLedger {
|
|
|
173
225
|
this.clock,
|
|
174
226
|
)
|
|
175
227
|
: immutableView(input.initialView);
|
|
228
|
+
const systemMessage = this.view.messages[0];
|
|
229
|
+
if (systemMessage?.role !== "system") {
|
|
230
|
+
throw new Error("Session ledger canonical history has no system message.");
|
|
231
|
+
}
|
|
232
|
+
this.surface =
|
|
233
|
+
input.initialSurface ??
|
|
234
|
+
createInMemoryContextSurface({
|
|
235
|
+
sessionId: input.sessionId,
|
|
236
|
+
systemPrompt: systemMessage.content,
|
|
237
|
+
tools: input.initialToolDefinitions ?? [],
|
|
238
|
+
idFactory: input.idFactory,
|
|
239
|
+
createdAt: this.view.frames[0]?.createdAt ?? this.clock(),
|
|
240
|
+
});
|
|
176
241
|
this.revision = createInitialContextRevision({
|
|
177
242
|
revisionId:
|
|
178
243
|
input.initialRevisionId ?? input.idFactory.createContextRevisionId(),
|
|
179
244
|
canonical: this.view,
|
|
245
|
+
surface: this.surface,
|
|
180
246
|
createdAt: this.view.frames[0]?.createdAt ?? this.clock(),
|
|
181
247
|
});
|
|
182
248
|
this.activeOverrides = Object.freeze([]);
|
|
183
249
|
}
|
|
184
250
|
if (
|
|
185
251
|
this.view.sessionId !== input.sessionId ||
|
|
186
|
-
this.revision.sessionId !== input.sessionId
|
|
252
|
+
this.revision.sessionId !== input.sessionId ||
|
|
253
|
+
this.surface.sessionId !== input.sessionId
|
|
187
254
|
) {
|
|
188
255
|
throw new Error("Session ledger history or revision belongs to another session.");
|
|
189
256
|
}
|
|
@@ -195,11 +262,13 @@ export class InMemorySessionLedger implements SessionLedger {
|
|
|
195
262
|
}
|
|
196
263
|
}
|
|
197
264
|
|
|
198
|
-
beginTurn(input: {
|
|
265
|
+
beginTurn(input: {
|
|
266
|
+
turn: TurnIdentity;
|
|
267
|
+
userMessage: UserMessage;
|
|
268
|
+
admissionBase?: AdmissionBaseToken;
|
|
269
|
+
}): PendingLedgerTurn {
|
|
199
270
|
this.requireHealthy("begin a turn");
|
|
200
|
-
|
|
201
|
-
throw new Error("Cannot begin a ledger turn with an empty prompt.");
|
|
202
|
-
}
|
|
271
|
+
validateUserMessage(input.userMessage);
|
|
203
272
|
if (input.turn.sessionId !== this.input.sessionId) {
|
|
204
273
|
throw new Error(`Turn ${input.turn.turnId} belongs to another session.`);
|
|
205
274
|
}
|
|
@@ -207,6 +276,9 @@ export class InMemorySessionLedger implements SessionLedger {
|
|
|
207
276
|
throw new Error("Cannot begin a ledger turn while another turn is open.");
|
|
208
277
|
}
|
|
209
278
|
this.assertNoOpenFrame();
|
|
279
|
+
if (input.admissionBase !== undefined) {
|
|
280
|
+
this.assertAdmissionBase(input.admissionBase, input.turn.turnNumber);
|
|
281
|
+
}
|
|
210
282
|
|
|
211
283
|
const createdAt = this.clock();
|
|
212
284
|
const ordinal = this.view.messages.length + 1;
|
|
@@ -216,11 +288,18 @@ export class InMemorySessionLedger implements SessionLedger {
|
|
|
216
288
|
sessionId: this.input.sessionId,
|
|
217
289
|
frameId,
|
|
218
290
|
ordinal,
|
|
219
|
-
contentSha256:
|
|
291
|
+
contentSha256: userMessageHash(input.userMessage),
|
|
220
292
|
createdAt,
|
|
221
293
|
role: "user",
|
|
222
294
|
turnId: input.turn.turnId,
|
|
223
|
-
content: input.
|
|
295
|
+
content: input.userMessage.content,
|
|
296
|
+
...(input.userMessage.attachments === undefined
|
|
297
|
+
? {}
|
|
298
|
+
: {
|
|
299
|
+
attachments: Object.freeze(
|
|
300
|
+
immutableCanonicalClone(input.userMessage.attachments),
|
|
301
|
+
),
|
|
302
|
+
}),
|
|
224
303
|
origin: "user",
|
|
225
304
|
});
|
|
226
305
|
const frame = immutableRecord<ProtocolFrame>({
|
|
@@ -236,7 +315,16 @@ export class InMemorySessionLedger implements SessionLedger {
|
|
|
236
315
|
});
|
|
237
316
|
const next = appendView(this.view, [frame], [message], []);
|
|
238
317
|
this.validator.validate(next, { fullIntegrity: true });
|
|
239
|
-
this.commit({
|
|
318
|
+
this.commit({
|
|
319
|
+
kind: "begin_turn",
|
|
320
|
+
turn: input.turn,
|
|
321
|
+
frame,
|
|
322
|
+
message,
|
|
323
|
+
...(input.admissionBase === undefined
|
|
324
|
+
? {}
|
|
325
|
+
: { admissionBase: input.admissionBase }),
|
|
326
|
+
next,
|
|
327
|
+
});
|
|
240
328
|
|
|
241
329
|
const pending = new InMemoryPendingLedgerTurn(this, input.turn);
|
|
242
330
|
this.pending = pending;
|
|
@@ -252,14 +340,14 @@ export class InMemorySessionLedger implements SessionLedger {
|
|
|
252
340
|
}
|
|
253
341
|
|
|
254
342
|
buildCandidateModelRequest(
|
|
255
|
-
|
|
343
|
+
userMessage: UserMessage,
|
|
256
344
|
tools: readonly ToolDefinition[],
|
|
257
345
|
): BuiltContextRequest {
|
|
258
346
|
this.requireHealthy("build candidate context");
|
|
259
347
|
if (this.pending !== undefined) {
|
|
260
348
|
throw new Error("Cannot build candidate context while a turn is open.");
|
|
261
349
|
}
|
|
262
|
-
return this.buildRequest(tools,
|
|
350
|
+
return this.buildRequest(tools, userMessage);
|
|
263
351
|
}
|
|
264
352
|
|
|
265
353
|
committedMessageCount(): number {
|
|
@@ -375,7 +463,7 @@ export class InMemorySessionLedger implements SessionLedger {
|
|
|
375
463
|
commitToolCompletions(
|
|
376
464
|
pending: InMemoryPendingLedgerTurn,
|
|
377
465
|
completions: readonly ToolCompletionInput[],
|
|
378
|
-
):
|
|
466
|
+
): readonly CommittedToolCompletion[] {
|
|
379
467
|
this.requirePending(pending, "commit tool completions");
|
|
380
468
|
if (completions.length === 0) {
|
|
381
469
|
throw new Error("Tool completion batch must not be empty.");
|
|
@@ -459,6 +547,13 @@ export class InMemorySessionLedger implements SessionLedger {
|
|
|
459
547
|
toolResults: Object.freeze(toolResults),
|
|
460
548
|
next,
|
|
461
549
|
});
|
|
550
|
+
return Object.freeze(
|
|
551
|
+
toolResults.map((result, index) => ({
|
|
552
|
+
toolCallId: result.toolCallId,
|
|
553
|
+
toolMessageId: result.toolMessageId,
|
|
554
|
+
ordinal: requireItem(messages, index, "committed tool message").ordinal,
|
|
555
|
+
})),
|
|
556
|
+
);
|
|
462
557
|
}
|
|
463
558
|
|
|
464
559
|
buildTurnModelRequest(
|
|
@@ -508,20 +603,21 @@ export class InMemorySessionLedger implements SessionLedger {
|
|
|
508
603
|
|
|
509
604
|
private buildRequest(
|
|
510
605
|
tools: readonly ToolDefinition[],
|
|
511
|
-
|
|
606
|
+
candidateUserMessage?: UserMessage,
|
|
512
607
|
): BuiltContextRequest {
|
|
513
608
|
try {
|
|
514
609
|
const canonical = this.view;
|
|
515
610
|
const compiled = this.revisionCompiler.compileActive(
|
|
516
|
-
snapshotFor(canonical, this.revision, this.activeOverrides),
|
|
611
|
+
snapshotFor(canonical, this.revision, this.surface, this.activeOverrides),
|
|
517
612
|
);
|
|
518
613
|
return this.contextBuilder.build({
|
|
519
614
|
canonical,
|
|
520
615
|
revision: this.revision,
|
|
616
|
+
surface: this.surface,
|
|
521
617
|
activeOverrides: this.activeOverrides,
|
|
522
618
|
compiled,
|
|
523
619
|
tools,
|
|
524
|
-
...(
|
|
620
|
+
...(candidateUserMessage === undefined ? {} : { candidateUserMessage }),
|
|
525
621
|
});
|
|
526
622
|
} catch (error) {
|
|
527
623
|
if (error instanceof ContextProtocolError && error.code !== "open_frame") {
|
|
@@ -535,12 +631,31 @@ export class InMemorySessionLedger implements SessionLedger {
|
|
|
535
631
|
try {
|
|
536
632
|
this.input.committer?.commit(mutation);
|
|
537
633
|
} catch (error) {
|
|
634
|
+
if (error instanceof AdmissionStaleError) {
|
|
635
|
+
throw error;
|
|
636
|
+
}
|
|
538
637
|
this.view = immutableView({ ...this.view, faulted: true });
|
|
539
638
|
throw new SessionLedgerWriteError(mutation.kind, { cause: error });
|
|
540
639
|
}
|
|
541
640
|
this.view = mutation.next;
|
|
542
641
|
}
|
|
543
642
|
|
|
643
|
+
private assertAdmissionBase(token: AdmissionBaseToken, turnNumber: number): void {
|
|
644
|
+
const head = this.view.messages.at(-1);
|
|
645
|
+
if (
|
|
646
|
+
head === undefined ||
|
|
647
|
+
token.canonicalMessageCount !== this.view.messages.length ||
|
|
648
|
+
token.canonicalHeadMessageId !== head.messageId ||
|
|
649
|
+
token.canonicalHeadContentSha256 !== head.contentSha256 ||
|
|
650
|
+
token.activeRevisionId !== this.revision.revisionId ||
|
|
651
|
+
token.activeRevisionNumber !== this.revision.revisionNumber ||
|
|
652
|
+
token.surfaceSha256 !== this.surface.surfaceSha256 ||
|
|
653
|
+
token.nextTurnNumber !== turnNumber
|
|
654
|
+
) {
|
|
655
|
+
throw new AdmissionStaleError();
|
|
656
|
+
}
|
|
657
|
+
}
|
|
658
|
+
|
|
544
659
|
private requirePending(pending: InMemoryPendingLedgerTurn, action: string): void {
|
|
545
660
|
this.requireHealthy(action);
|
|
546
661
|
if (this.pending !== pending) {
|
|
@@ -598,20 +713,45 @@ export class InMemorySessionLedger implements SessionLedger {
|
|
|
598
713
|
|
|
599
714
|
function snapshotFor(
|
|
600
715
|
canonical: ProtocolContextView,
|
|
601
|
-
revision:
|
|
602
|
-
|
|
603
|
-
|
|
716
|
+
revision: StoredContextRevisionV8,
|
|
717
|
+
surface: StoredContextSurfaceV8,
|
|
718
|
+
activeOverrides: readonly StoredContextOverrideV8[],
|
|
719
|
+
): StoredContextSnapshotV8 {
|
|
604
720
|
return Object.freeze({
|
|
605
721
|
meta: Object.freeze({
|
|
606
722
|
sessionId: canonical.sessionId,
|
|
607
723
|
activeRevisionId: revision.revisionId,
|
|
608
724
|
}),
|
|
609
725
|
revision,
|
|
726
|
+
surface,
|
|
610
727
|
activeOverrides,
|
|
611
728
|
canonical,
|
|
612
729
|
});
|
|
613
730
|
}
|
|
614
731
|
|
|
732
|
+
function createInMemoryContextSurface(input: {
|
|
733
|
+
sessionId: SessionId;
|
|
734
|
+
systemPrompt: string;
|
|
735
|
+
tools: readonly ToolDefinition[];
|
|
736
|
+
idFactory: RuntimeIdFactory;
|
|
737
|
+
createdAt: string;
|
|
738
|
+
}): StoredContextSurfaceV8 {
|
|
739
|
+
const serializedTools = input.tools.map((tool) => stableJsonStringify(tool));
|
|
740
|
+
return createContextSurface({
|
|
741
|
+
surfaceId: input.idFactory.createContextSurfaceId(),
|
|
742
|
+
sessionId: input.sessionId,
|
|
743
|
+
systemPrompt: input.systemPrompt,
|
|
744
|
+
recallContractVersion: CURRENT_RECALL_RETIREMENT_CONTRACT_VERSION,
|
|
745
|
+
toolDefinitions: input.tools,
|
|
746
|
+
prepared: {
|
|
747
|
+
requestConfigHash: sha256("in-memory-model-request-v1"),
|
|
748
|
+
requestMaxOutputTokens: 1,
|
|
749
|
+
toolSchemaHash: sha256(serializedTools.join("\n")),
|
|
750
|
+
},
|
|
751
|
+
createdAt: input.createdAt,
|
|
752
|
+
});
|
|
753
|
+
}
|
|
754
|
+
|
|
615
755
|
class InMemoryPendingLedgerTurn implements PendingLedgerTurn {
|
|
616
756
|
readonly agent: AgentTurnLedger;
|
|
617
757
|
private state: PendingState = "open";
|
|
@@ -763,7 +903,7 @@ function canonicalCompletion(input: ToolCompletionInput): ToolCompletion {
|
|
|
763
903
|
kind: "returned",
|
|
764
904
|
raw,
|
|
765
905
|
rawSha256: rawResultHash(raw),
|
|
766
|
-
observationFormat:
|
|
906
|
+
observationFormat: CURRENT_TOOL_OBSERVATION_FORMAT,
|
|
767
907
|
});
|
|
768
908
|
}
|
|
769
909
|
|
package/src/agent/types.ts
CHANGED
|
@@ -1,4 +1,15 @@
|
|
|
1
1
|
import type { IterationId, SessionId, ToolCallId, TurnId } from "../ids/runtime-id";
|
|
2
|
+
import type { UserMessage } from "../image/image-types";
|
|
3
|
+
|
|
4
|
+
export type {
|
|
5
|
+
CodePointRange,
|
|
6
|
+
ImageAssetId,
|
|
7
|
+
ImageAssetRef,
|
|
8
|
+
ImageAttachmentId,
|
|
9
|
+
ImageMimeType,
|
|
10
|
+
UserImageAttachment,
|
|
11
|
+
UserMessage,
|
|
12
|
+
} from "../image/image-types";
|
|
2
13
|
|
|
3
14
|
export type SessionIdentity = {
|
|
4
15
|
sessionId: SessionId;
|
|
@@ -44,7 +55,7 @@ export type ToolMessage = {
|
|
|
44
55
|
|
|
45
56
|
export type AgentMessage =
|
|
46
57
|
| { role: "system"; content: string }
|
|
47
|
-
|
|
|
58
|
+
| UserMessage
|
|
48
59
|
| AssistantMessage
|
|
49
60
|
| ToolMessage;
|
|
50
61
|
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import type { UserMessage } from "./types";
|
|
2
|
+
import type { CodePointRange } from "../image/image-types";
|
|
3
|
+
import { validateUserMessage } from "../image/image-types";
|
|
4
|
+
|
|
5
|
+
export const MAX_TIMELINE_PROMPT_CODE_POINTS = 4_000;
|
|
6
|
+
|
|
7
|
+
export type UserPromptProjection = {
|
|
8
|
+
readonly version: 1;
|
|
9
|
+
readonly text: string;
|
|
10
|
+
readonly images: readonly {
|
|
11
|
+
readonly label: string;
|
|
12
|
+
readonly range: CodePointRange;
|
|
13
|
+
readonly originalName: string;
|
|
14
|
+
}[];
|
|
15
|
+
readonly omittedImageCount: number;
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
export function projectUserMessage(message: UserMessage): UserPromptProjection {
|
|
19
|
+
validateUserMessage(message);
|
|
20
|
+
return Object.freeze({
|
|
21
|
+
version: 1,
|
|
22
|
+
text: message.content,
|
|
23
|
+
images: Object.freeze(
|
|
24
|
+
(message.attachments ?? []).map((attachment) =>
|
|
25
|
+
Object.freeze({
|
|
26
|
+
label: attachment.label,
|
|
27
|
+
range: Object.freeze({ ...attachment.range }),
|
|
28
|
+
originalName: attachment.originalName,
|
|
29
|
+
}),
|
|
30
|
+
),
|
|
31
|
+
),
|
|
32
|
+
omittedImageCount: 0,
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export function truncateUserPromptProjection(
|
|
37
|
+
projection: UserPromptProjection,
|
|
38
|
+
maxCodePoints: number,
|
|
39
|
+
): UserPromptProjection {
|
|
40
|
+
if (!Number.isSafeInteger(maxCodePoints) || maxCodePoints < 0) {
|
|
41
|
+
throw new Error("Prompt projection limit must be a non-negative safe integer.");
|
|
42
|
+
}
|
|
43
|
+
const chars = [...projection.text];
|
|
44
|
+
if (chars.length <= maxCodePoints) {
|
|
45
|
+
return projection;
|
|
46
|
+
}
|
|
47
|
+
let end = maxCodePoints;
|
|
48
|
+
const splitImage = projection.images.find(
|
|
49
|
+
(image) => image.range.start < end && end < image.range.end,
|
|
50
|
+
);
|
|
51
|
+
if (splitImage !== undefined) {
|
|
52
|
+
end = splitImage.range.start;
|
|
53
|
+
}
|
|
54
|
+
const images = projection.images.filter((image) => image.range.end <= end);
|
|
55
|
+
return Object.freeze({
|
|
56
|
+
version: 1,
|
|
57
|
+
text: `${chars.slice(0, end).join("")}\n…`,
|
|
58
|
+
images: Object.freeze(images),
|
|
59
|
+
omittedImageCount:
|
|
60
|
+
projection.omittedImageCount + projection.images.length - images.length,
|
|
61
|
+
});
|
|
62
|
+
}
|
package/src/cli/config.ts
CHANGED
|
@@ -11,15 +11,19 @@ import {
|
|
|
11
11
|
import { createModelRefiner, type Refiner } from "../tools/web-fetch/refiner";
|
|
12
12
|
import { createUuidV7 } from "../ids/uuid-v7";
|
|
13
13
|
import type { SessionId } from "../ids/runtime-id";
|
|
14
|
+
import { renderRecallRetirementContract } from "../context/recall-retirement-contract";
|
|
14
15
|
import {
|
|
15
16
|
profileToContextProfile,
|
|
16
17
|
type ModelProfile,
|
|
18
|
+
type ModelInputModality,
|
|
17
19
|
type ModelProfiles,
|
|
20
|
+
type ModelTokenEstimatorProfile,
|
|
18
21
|
unknownProfileError,
|
|
19
22
|
} from "./model-profiles";
|
|
20
23
|
|
|
21
24
|
export const DEFAULT_MAX_ITERATIONS = 512;
|
|
22
25
|
export const DEFAULT_INCLUDE_REASONING_CONTENT = false;
|
|
26
|
+
export const DEFAULT_STREAM = true;
|
|
23
27
|
|
|
24
28
|
export const RUNTIME_INSTRUCTIONS = (
|
|
25
29
|
workspaceRoot: string,
|
|
@@ -37,10 +41,11 @@ Use Grep to search file contents. Do not use Bash with grep or rg for routine co
|
|
|
37
41
|
With Grep, start with output_mode="files_with_matches" to narrow scope, then use output_mode="content" when you need matching lines.
|
|
38
42
|
Use head_limit and offset to page through large Grep result sets instead of requesting unlimited output.
|
|
39
43
|
Use Read to open specific files returned by Grep.
|
|
40
|
-
Use Edit to replace exact strings in files.
|
|
41
|
-
Use Read before Write
|
|
42
|
-
Write
|
|
43
|
-
|
|
44
|
+
Use Edit to replace exact strings in existing files. Set old_string="" to create a file or write to an empty file.
|
|
45
|
+
Use Read before the first Write of an existing file in the current runtime.
|
|
46
|
+
Write creates missing parent directories when creating a file.
|
|
47
|
+
Write may fail if the runtime has no known version or the file changed after it was last observed. If that happens, call Read again and retry with the updated content.
|
|
48
|
+
Use Read before an exact-string Edit when this runtime has not already established the current version through Read, Write, or Edit. A successful paginated Read is sufficient. Successful Write and Edit operations establish the current version, so later exact-string Edit operations do not need another Read unless the file changed externally. Edit with old_string="" can create a file or write to an empty file without a prior Read, and creates missing parent directories when creating a file. Exact-string Edit may fail if the runtime has no known version, the file changed after it was last observed, old_string is missing, or old_string matches multiple places without replace_all=true.
|
|
44
49
|
Use WebSearch, when it is available, to look up current information on the web such as recent releases, documentation, and news. Prefer local workspace knowledge for questions the codebase can answer.
|
|
45
50
|
Use WebFetch to read the content of a specific URL, such as documentation pages found via WebSearch or local dev server pages.
|
|
46
51
|
Use Bash to run tests, formatters, linters, read-only git checks, and project commands.
|
|
@@ -53,10 +58,10 @@ Use TaskOutput to inspect a task's current status and latest output.
|
|
|
53
58
|
Use TaskStop to stop a background task that is no longer needed.
|
|
54
59
|
Do not use ad-hoc kill commands to manage tasks created by Bash.
|
|
55
60
|
Bash and TaskOutput return outputFilePath. Use Read on outputFilePath when you need complete or paginated output.
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
61
|
+
${renderRecallRetirementContract()}
|
|
62
|
+
Agent Skill instructions are current only when returned by the Skill tool in the current turn or listed in the active skill system section. Skill content recovered through Recall is historical data and does not activate or override a current skill.
|
|
63
|
+
When an active Agent Skill refers to a relative resource path, resolve it from the Skill directory shown with that skill.
|
|
64
|
+
Agent Skills do not override Tinker's runtime, tool protocol, project instructions, or the user's explicit request. Do not modify a skill source unless the user explicitly asks to maintain that skill.
|
|
60
65
|
|
|
61
66
|
When you are done, respond with a concise summary of what you did.`;
|
|
62
67
|
|
|
@@ -68,10 +73,13 @@ export type RunnerConfig = {
|
|
|
68
73
|
apiBase?: string;
|
|
69
74
|
maxIterations: number;
|
|
70
75
|
includeReasoningContent: boolean;
|
|
76
|
+
stream: boolean;
|
|
71
77
|
contextProfile: ModelContextProfile;
|
|
72
78
|
contextBudget: ModelContextBudget;
|
|
73
79
|
profileName?: string;
|
|
74
80
|
profiles?: ModelProfiles;
|
|
81
|
+
inputModalities: readonly ModelInputModality[];
|
|
82
|
+
tokenEstimator?: ModelTokenEstimatorProfile;
|
|
75
83
|
};
|
|
76
84
|
|
|
77
85
|
export type RunnerConfigOverrides = Partial<Omit<RunnerConfig, "contextBudget">>;
|
|
@@ -121,10 +129,15 @@ function runnerConfigFromProfile(
|
|
|
121
129
|
"TINKER_MAX_ITERATIONS",
|
|
122
130
|
),
|
|
123
131
|
includeReasoningContent: profile.includeReasoningContent,
|
|
132
|
+
stream: profile.stream,
|
|
124
133
|
contextProfile,
|
|
125
134
|
contextBudget,
|
|
126
135
|
profileName: profile.name,
|
|
127
136
|
profiles,
|
|
137
|
+
inputModalities: profile.inputModalities,
|
|
138
|
+
...(profile.tokenEstimator === undefined
|
|
139
|
+
? {}
|
|
140
|
+
: { tokenEstimator: profile.tokenEstimator }),
|
|
128
141
|
};
|
|
129
142
|
}
|
|
130
143
|
|
|
@@ -154,16 +167,29 @@ function runnerConfigFromEnv(overrides: RunnerConfigOverrides): RunnerConfig {
|
|
|
154
167
|
DEFAULT_INCLUDE_REASONING_CONTENT,
|
|
155
168
|
"TINKER_INCLUDE_REASONING_CONTENT",
|
|
156
169
|
),
|
|
170
|
+
stream:
|
|
171
|
+
overrides.stream ??
|
|
172
|
+
parseBoolean(process.env.TINKER_STREAM, DEFAULT_STREAM, "TINKER_STREAM"),
|
|
157
173
|
contextProfile,
|
|
158
174
|
contextBudget,
|
|
175
|
+
inputModalities: overrides.inputModalities ?? Object.freeze(["text"]),
|
|
176
|
+
...(overrides.tokenEstimator === undefined
|
|
177
|
+
? {}
|
|
178
|
+
: { tokenEstimator: overrides.tokenEstimator }),
|
|
159
179
|
};
|
|
160
180
|
}
|
|
161
181
|
|
|
162
182
|
export function createModelClientFromEnv(
|
|
163
183
|
config: Pick<
|
|
164
184
|
RunnerConfig,
|
|
165
|
-
|
|
166
|
-
|
|
185
|
+
| "modelName"
|
|
186
|
+
| "includeReasoningContent"
|
|
187
|
+
| "stream"
|
|
188
|
+
| "contextBudget"
|
|
189
|
+
| "apiKey"
|
|
190
|
+
| "apiBase"
|
|
191
|
+
> &
|
|
192
|
+
Partial<Pick<RunnerConfig, "inputModalities" | "tokenEstimator">>,
|
|
167
193
|
): ModelClient {
|
|
168
194
|
const fakeMode = process.env.TINKER_TEST_FAKE_MODEL;
|
|
169
195
|
if (fakeMode !== undefined && fakeMode !== "") {
|
|
@@ -181,15 +207,26 @@ export function createModelClientFromEnv(
|
|
|
181
207
|
baseURL,
|
|
182
208
|
includeReasoningContent: config.includeReasoningContent,
|
|
183
209
|
model: config.modelName,
|
|
210
|
+
stream: config.stream,
|
|
184
211
|
contextBudget: config.contextBudget,
|
|
212
|
+
inputModalities: config.inputModalities ?? Object.freeze(["text"]),
|
|
213
|
+
...(config.tokenEstimator === undefined
|
|
214
|
+
? {}
|
|
215
|
+
: { tokenEstimator: config.tokenEstimator }),
|
|
185
216
|
});
|
|
186
217
|
}
|
|
187
218
|
|
|
188
219
|
export function createRunnerModelClient(
|
|
189
220
|
config: Pick<
|
|
190
221
|
RunnerConfig,
|
|
191
|
-
|
|
192
|
-
|
|
222
|
+
| "modelName"
|
|
223
|
+
| "includeReasoningContent"
|
|
224
|
+
| "stream"
|
|
225
|
+
| "contextBudget"
|
|
226
|
+
| "apiKey"
|
|
227
|
+
| "apiBase"
|
|
228
|
+
> &
|
|
229
|
+
Partial<Pick<RunnerConfig, "inputModalities" | "tokenEstimator">>,
|
|
193
230
|
injected?: ModelClient,
|
|
194
231
|
): ModelClient {
|
|
195
232
|
return injected ?? createModelClientFromEnv(config);
|
|
@@ -198,8 +235,14 @@ export function createRunnerModelClient(
|
|
|
198
235
|
export function createWebFetchRefinerFromEnv(
|
|
199
236
|
config: Pick<
|
|
200
237
|
RunnerConfig,
|
|
201
|
-
|
|
202
|
-
|
|
238
|
+
| "modelName"
|
|
239
|
+
| "includeReasoningContent"
|
|
240
|
+
| "stream"
|
|
241
|
+
| "contextBudget"
|
|
242
|
+
| "apiKey"
|
|
243
|
+
| "apiBase"
|
|
244
|
+
> &
|
|
245
|
+
Partial<Pick<RunnerConfig, "inputModalities" | "tokenEstimator">>,
|
|
203
246
|
): Refiner {
|
|
204
247
|
return createModelRefiner({
|
|
205
248
|
createModelClient: () => createModelClientFromEnv(config),
|