tinker-agent 2.11.0 → 2.13.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 +32 -1
- package/README.md +31 -67
- package/package.json +1 -1
- package/src/agent/runtime-session-contracts.ts +2 -29
- package/src/agent/runtime-session.ts +13 -67
- package/src/cli/config.ts +0 -29
- package/src/cli/model-profiles.ts +0 -80
- package/src/cli/public-config-contract.ts +1 -82
- package/src/cli/runner-dependencies.ts +35 -35
- package/src/cli/tui-runner.tsx +2 -41
- package/src/events/observation-text-log.ts +7 -0
- package/src/memory/contracts.ts +0 -256
- package/src/memory/memory-create-tool.ts +3 -5
- package/src/memory/memory-files.ts +145 -0
- package/src/memory/memory-search-output.ts +23 -0
- package/src/memory/memory-search-tool.ts +79 -175
- package/src/memory/memory-search.ts +115 -0
- package/src/observation/observation-builder.ts +5 -1
- package/src/session/session-store-contracts.ts +0 -23
- package/src/session/session-store.ts +2 -103
- package/src/tools/bash.ts +9 -1
- package/src/tools/bounded-output-preview.ts +2 -2
- package/src/tools/registry.ts +15 -18
- package/src/tools/types.ts +12 -0
- package/src/tui/app.tsx +6 -4
- package/src/tui/event-store.ts +3 -1
- package/src/cli/tui-memory.ts +0 -69
- package/src/memory/embedding-client.ts +0 -105
- package/src/memory/memory-coordinator.ts +0 -1274
- package/src/memory/memory-delete-tool.ts +0 -88
- package/src/memory/memory-extractor.ts +0 -252
- package/src/memory/memory-get-tool.ts +0 -86
- package/src/memory/memory-log.ts +0 -88
- package/src/memory/memory-store.ts +0 -1133
- package/src/memory/memory-update-tool.ts +0 -142
- package/src/memory/vector.ts +0 -153
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,35 @@ All notable user-facing changes to Tinker are documented here. The project follo
|
|
|
5
5
|
|
|
6
6
|
## [Unreleased]
|
|
7
7
|
|
|
8
|
+
## [2.13.0] - 2026-09-12
|
|
9
|
+
|
|
10
|
+
### Changed
|
|
11
|
+
|
|
12
|
+
- Tighten the Bash output preview window from 200 lines to 50 lines (keeping
|
|
13
|
+
the first and last 25 lines). Model observations for shell commands now stay
|
|
14
|
+
within a smaller context budget, while the complete output remains available
|
|
15
|
+
through the reported `outputFilePath` and `TaskOutput` pagination.
|
|
16
|
+
- Streamline the runtime system instructions into a concise sectioned format.
|
|
17
|
+
Detailed tool-usage guidance moves into the tool descriptions themselves,
|
|
18
|
+
reducing the static system prompt sent with every model request.
|
|
19
|
+
|
|
20
|
+
## [2.12.0] - 2026-09-11
|
|
21
|
+
|
|
22
|
+
### Changed
|
|
23
|
+
|
|
24
|
+
- Replace the vector memory system with plain-text Markdown records. Session
|
|
25
|
+
transcripts are appended to `memory/records/<sessionId>.md` in the global
|
|
26
|
+
Tinker home, and `MemoryCreate` saves explicit Markdown notes. `MemorySearch`
|
|
27
|
+
searches all memory files with literal, case-insensitive keywords and
|
|
28
|
+
paginated, grouped passages; the `MemoryGet`, `MemoryUpdate`, and
|
|
29
|
+
`MemoryDelete` tools and the `memory` model-profile configuration are
|
|
30
|
+
removed. Old memory configuration is ignored, and the existing memory
|
|
31
|
+
database and diagnostic files are moved to `memory-legacy/` on first use,
|
|
32
|
+
preserved but no longer searched. Memory now works in both the TUI and the
|
|
33
|
+
one-shot CLI without any model profile or embedding service.
|
|
34
|
+
- Simplify `MemoryCreate` to a Markdown note with the `text` heading and an
|
|
35
|
+
optional `summary` body, recording creation time and source workspace.
|
|
36
|
+
|
|
8
37
|
## [2.11.0] - 2026-09-08
|
|
9
38
|
|
|
10
39
|
### Added
|
|
@@ -464,7 +493,9 @@ All notable user-facing changes to Tinker are documented here. The project follo
|
|
|
464
493
|
- First formal npm release under the `tinker-agent` package name with the `tinker`
|
|
465
494
|
executable.
|
|
466
495
|
|
|
467
|
-
[Unreleased]: https://github.com/ishowshao/tinker/compare/v2.
|
|
496
|
+
[Unreleased]: https://github.com/ishowshao/tinker/compare/v2.13.0...HEAD
|
|
497
|
+
[2.13.0]: https://github.com/ishowshao/tinker/releases/tag/v2.13.0
|
|
498
|
+
[2.12.0]: https://github.com/ishowshao/tinker/releases/tag/v2.12.0
|
|
468
499
|
[2.11.0]: https://github.com/ishowshao/tinker/releases/tag/v2.11.0
|
|
469
500
|
[2.10.0]: https://github.com/ishowshao/tinker/releases/tag/v2.10.0
|
|
470
501
|
[2.9.0]: https://github.com/ishowshao/tinker/releases/tag/v2.9.0
|
package/README.md
CHANGED
|
@@ -65,7 +65,7 @@ creation; running tasks cannot be resized through these tools.
|
|
|
65
65
|
|
|
66
66
|
Both Recall tools accept an optional `sessionId` (a canonical session UUID).
|
|
67
67
|
Omitting it keeps the current-session behavior. In the TUI or one-shot CLI, the
|
|
68
|
-
agent can use a session ID you supply or a
|
|
68
|
+
agent can use a session ID you supply or find in a memory record's filename/header:
|
|
69
69
|
|
|
70
70
|
```ts
|
|
71
71
|
RecallSearch({ sessionId: sourceSessionId, query: "distinctive-anchor" })
|
|
@@ -380,70 +380,6 @@ Image-capable profile example:
|
|
|
380
380
|
}
|
|
381
381
|
```
|
|
382
382
|
|
|
383
|
-
The top-level `memory` object is optional. When present, every field below is required. It enables completed-turn extraction and `MemorySearch` only in the TUI; one-shot runs do not load memory.
|
|
384
|
-
|
|
385
|
-
| Field | Required | Type / constraint | Secret | Description |
|
|
386
|
-
| --- | --- | --- | --- | --- |
|
|
387
|
-
| `profile` | Yes | Non-empty string | No | Existing model profile used for completed-turn atomic-memory extraction. |
|
|
388
|
-
| `embedding` | Yes | Object | Yes | Single embedding profile for the global memory database. |
|
|
389
|
-
|
|
390
|
-
`memory.embedding` fields:
|
|
391
|
-
|
|
392
|
-
| Field | Required | Type / constraint | Secret | Description |
|
|
393
|
-
| --- | --- | --- | --- | --- |
|
|
394
|
-
| `name` | Yes | Non-empty string | No | Stable identity for the embedding space. |
|
|
395
|
-
| `kind` | Yes | Literal `"openai-compatible"` | No | Embedding transport kind. |
|
|
396
|
-
| `model` | Yes | Non-empty string | No | Embedding provider model name. |
|
|
397
|
-
| `apiBase` | Yes | Non-empty string | No | OpenAI-compatible API base URL. |
|
|
398
|
-
| `apiKey` | Yes | Non-empty string | Yes | Embedding provider credential. |
|
|
399
|
-
| `dimensions` | Yes | Positive integer | No | Fixed vector dimensions for the global memory database. |
|
|
400
|
-
|
|
401
|
-
Enabling memory sends completed-turn text (not image bytes) to `memory.profile`, and sends extracted candidates plus search queries to the embedding endpoint. Derived memories are stored in `~/.tinker/memory/memory.sqlite`; newly inserted memory text is appended to the private development log `~/.tinker/memory/extracted-memories.log`.
|
|
402
|
-
|
|
403
|
-
Atomic-memory profile example:
|
|
404
|
-
|
|
405
|
-
```json
|
|
406
|
-
{
|
|
407
|
-
"default": "text",
|
|
408
|
-
"profiles": {
|
|
409
|
-
"text": {
|
|
410
|
-
"model": "example-text-model",
|
|
411
|
-
"api": "chat-completions",
|
|
412
|
-
"apiBase": "https://api.example.com/v1",
|
|
413
|
-
"apiKey": "your-model-api-key",
|
|
414
|
-
"contextWindowTokens": 128000,
|
|
415
|
-
"maxSupportedOutputTokens": 8192,
|
|
416
|
-
"reasoning": {
|
|
417
|
-
"supportedEfforts": [
|
|
418
|
-
"low",
|
|
419
|
-
"medium",
|
|
420
|
-
"high"
|
|
421
|
-
],
|
|
422
|
-
"defaultEffort": "medium"
|
|
423
|
-
},
|
|
424
|
-
"includeReasoningContent": false,
|
|
425
|
-
"stream": true,
|
|
426
|
-
"inputModalities": [
|
|
427
|
-
"text"
|
|
428
|
-
],
|
|
429
|
-
"toolResultModalities": [
|
|
430
|
-
"text"
|
|
431
|
-
]
|
|
432
|
-
}
|
|
433
|
-
},
|
|
434
|
-
"memory": {
|
|
435
|
-
"profile": "text",
|
|
436
|
-
"embedding": {
|
|
437
|
-
"name": "example-embedding-space",
|
|
438
|
-
"kind": "openai-compatible",
|
|
439
|
-
"model": "example-embedding-model",
|
|
440
|
-
"apiBase": "https://embeddings.example.com/v1",
|
|
441
|
-
"apiKey": "your-embedding-api-key",
|
|
442
|
-
"dimensions": 1024
|
|
443
|
-
}
|
|
444
|
-
}
|
|
445
|
-
}
|
|
446
|
-
```
|
|
447
383
|
<!-- END GENERATED: MODEL PROFILE FIELDS -->
|
|
448
384
|
|
|
449
385
|
Set `api` to `"responses"` to use the standard Responses API. Keep `apiBase`
|
|
@@ -563,6 +499,34 @@ one-shot `tinker run` command. See the
|
|
|
563
499
|
[project command loader](src/tui/project-slash-commands.ts) for configuration
|
|
564
500
|
validation and loading behavior.
|
|
565
501
|
|
|
502
|
+
### Text memory
|
|
503
|
+
|
|
504
|
+
Memory works without a model profile or embedding service in both the TUI and
|
|
505
|
+
one-shot CLI. Session transcripts are appended to
|
|
506
|
+
`<home>/.tinker/memory/records/<sessionId>.md`. `MemoryCreate` saves explicit notes
|
|
507
|
+
to `memory/notes/<id>.md`, using `text` as the heading and optional `summary` as
|
|
508
|
+
the body, with creation time and source workspace.
|
|
509
|
+
|
|
510
|
+
`MemorySearch` searches Markdown under `<home>/.tinker/memory/` using literal,
|
|
511
|
+
case-insensitive keywords. Supply `keywords` (any keyword may match), optionally
|
|
512
|
+
`context` (default 3), `limit` (default 20 matching lines), and `offset` (default 0).
|
|
513
|
+
Each line counts once, even if several keywords match. Keywords cannot span lines.
|
|
514
|
+
Passages are grouped by file with line numbers; `>` marks matching lines and `…`
|
|
515
|
+
separates passages. Long lines retain up to 500 code points around the earliest
|
|
516
|
+
match. Use `Read` with `offset` and `limit` to expand a passage, or `nextOffset` to
|
|
517
|
+
continue searching. There are no regex, glob, output-mode, or multiline parameters.
|
|
518
|
+
Results follow file-path order; changes between calls can affect pagination.
|
|
519
|
+
There are no MemoryGet, MemoryUpdate, or
|
|
520
|
+
MemoryDelete tools. Resume appends to the same record; cloning copies historical
|
|
521
|
+
content; deleting a session leaves its memory record in place. `/memory` browses
|
|
522
|
+
previews of records and notes.
|
|
523
|
+
|
|
524
|
+
Old `models.json` memory configuration is ignored. Existing database and memory
|
|
525
|
+
diagnostic files are moved to `<home>/.tinker/memory-legacy/<id>/` on first use;
|
|
526
|
+
they are preserved but not searched or converted. Older session-local
|
|
527
|
+
`observations.md` files move to records when those sessions are resumed.
|
|
528
|
+
See [the memory design](docs/global-memory/global-memory-design.md).
|
|
529
|
+
|
|
566
530
|
### Global Runtime Data
|
|
567
531
|
|
|
568
532
|
Tinker keeps private runtime state in a global home directory instead of inside
|
|
@@ -570,8 +534,8 @@ your projects. Each workspace maps to
|
|
|
570
534
|
`~/.tinker/projects/<workspace-slug-hash>/` — the slug comes from the workspace
|
|
571
535
|
directory name and the hash from its canonical absolute path, so one project
|
|
572
536
|
always resolves to one storage directory even when reached through symlinks.
|
|
573
|
-
That directory holds per-session SQLite databases
|
|
574
|
-
|
|
537
|
+
That directory holds per-session SQLite databases and event logs
|
|
538
|
+
(`sessions/<id>/`), background Bash task logs (`bash/`), imported image
|
|
575
539
|
assets (`assets/images/`), and prompt history (`prompt-history.jsonl`).
|
|
576
540
|
Workspaces stay clean: no `.tinker/` directory is created inside them.
|
|
577
541
|
|
package/package.json
CHANGED
|
@@ -15,7 +15,7 @@ import type { ContextSurfaceComponent } from "../context/context-surface";
|
|
|
15
15
|
import type { ToolCompletionInput } from "../context/protocol-frame";
|
|
16
16
|
import type { EventSink } from "../events/event-sink";
|
|
17
17
|
import type { AgentEventInput, AgentEventType } from "../events/types";
|
|
18
|
-
import type { RuntimeIdFactory, SessionId
|
|
18
|
+
import type { RuntimeIdFactory, SessionId } from "../ids/runtime-id";
|
|
19
19
|
import type { ImportedImageAsset } from "../image/image-asset-store";
|
|
20
20
|
import type { ImageAssetRef, UserMessage } from "../image/image-types";
|
|
21
21
|
import type { ProjectInstructionManifest } from "../instructions/project-instructions";
|
|
@@ -28,11 +28,7 @@ import type {
|
|
|
28
28
|
} from "../model/model-context-profile";
|
|
29
29
|
import type { ReasoningEffortSnapshot } from "../model/reasoning-effort";
|
|
30
30
|
import type { ObservationBuilder } from "../observation/observation-builder";
|
|
31
|
-
import type {
|
|
32
|
-
CompletedTurnSnapshot,
|
|
33
|
-
SessionRecoveryResult,
|
|
34
|
-
SessionStore,
|
|
35
|
-
} from "../session/session-store";
|
|
31
|
+
import type { SessionRecoveryResult, SessionStore } from "../session/session-store";
|
|
36
32
|
import type { SkillCatalogSnapshot } from "../skills/skill-loader";
|
|
37
33
|
import type { createDefaultTooling } from "../tools/registry";
|
|
38
34
|
import type { TurnUndoResult } from "../tools/turn-undo-manager";
|
|
@@ -211,25 +207,6 @@ export type SkillsUpdateSummary = {
|
|
|
211
207
|
readonly addedOverrideCount: number;
|
|
212
208
|
};
|
|
213
209
|
|
|
214
|
-
export type CompletedTurnHookInput = {
|
|
215
|
-
readonly workspaceRoot: string;
|
|
216
|
-
readonly sessionId: SessionId;
|
|
217
|
-
readonly turnId: TurnId;
|
|
218
|
-
readonly snapshot: CompletedTurnSnapshot;
|
|
219
|
-
};
|
|
220
|
-
|
|
221
|
-
export type CompletedTurnHookFailure = {
|
|
222
|
-
readonly workspaceRoot: string;
|
|
223
|
-
readonly sessionId: SessionId;
|
|
224
|
-
readonly turnId: TurnId;
|
|
225
|
-
readonly reason: "completed_turn_snapshot_failed" | "completed_turn_enqueue_failed";
|
|
226
|
-
};
|
|
227
|
-
|
|
228
|
-
export type CompletedTurnHook = {
|
|
229
|
-
enqueue(input: CompletedTurnHookInput): void;
|
|
230
|
-
recordFailure(input: CompletedTurnHookFailure): void;
|
|
231
|
-
};
|
|
232
|
-
|
|
233
210
|
export type CommonRuntimeSessionInput = {
|
|
234
211
|
workspaceRoot: string;
|
|
235
212
|
homeRoot?: string;
|
|
@@ -254,11 +231,7 @@ export type CommonRuntimeSessionInput = {
|
|
|
254
231
|
webFetchRefiner?: Refiner;
|
|
255
232
|
toolingConfig?: PublicToolingConfig;
|
|
256
233
|
memorySearch?: ToolExecutor;
|
|
257
|
-
memoryGet?: ToolExecutor;
|
|
258
234
|
memoryCreate?: ToolExecutor;
|
|
259
|
-
memoryUpdate?: ToolExecutor;
|
|
260
|
-
memoryDelete?: ToolExecutor;
|
|
261
|
-
completedTurnHook?: CompletedTurnHook;
|
|
262
235
|
enableTurnUndo?: boolean;
|
|
263
236
|
enableAskUser?: boolean;
|
|
264
237
|
enableProviderRetryPrompt?: boolean;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { prepareSessionMemory, sessionMemoryPath } from "../memory/memory-files";
|
|
1
2
|
import {
|
|
2
3
|
RuntimeProviderRetry,
|
|
3
4
|
type ProviderRetryDecision,
|
|
@@ -25,7 +26,7 @@ import type { EventSink } from "../events/event-sink";
|
|
|
25
26
|
import { JsonlEventLog } from "../events/jsonl-event-log";
|
|
26
27
|
import { ObservationTextLog } from "../events/observation-text-log";
|
|
27
28
|
import type { AgentEvent, AgentEventInput } from "../events/types";
|
|
28
|
-
import { runtimeIdFactory, type SessionId
|
|
29
|
+
import { runtimeIdFactory, type SessionId } from "../ids/runtime-id";
|
|
29
30
|
import { ImageAssetStore, type ImportedImageAsset } from "../image/image-asset-store";
|
|
30
31
|
import { IMAGE_INPUT_POLICY } from "../image/image-input-policy";
|
|
31
32
|
import {
|
|
@@ -52,7 +53,6 @@ import { SessionError } from "../session/session-errors";
|
|
|
52
53
|
import {
|
|
53
54
|
createSessionCompatibilityContract,
|
|
54
55
|
SessionStore,
|
|
55
|
-
type CompletedTurnSnapshot,
|
|
56
56
|
type SessionRecoveryResult,
|
|
57
57
|
type StoredSkillActivation,
|
|
58
58
|
} from "../session/session-store";
|
|
@@ -83,8 +83,6 @@ import {
|
|
|
83
83
|
type AskUserResolution,
|
|
84
84
|
type AskUserSnapshot,
|
|
85
85
|
type BashGuardSnapshot,
|
|
86
|
-
type CompletedTurnHook,
|
|
87
|
-
type CompletedTurnHookFailure,
|
|
88
86
|
type CreateNewRuntimeSessionInput,
|
|
89
87
|
type CreateRuntimeSessionInput,
|
|
90
88
|
type ExecuteTurnInput,
|
|
@@ -123,9 +121,6 @@ export {
|
|
|
123
121
|
type AskUserSnapshot,
|
|
124
122
|
type BashGuardSnapshot,
|
|
125
123
|
type BashGuardSource,
|
|
126
|
-
type CompletedTurnHook,
|
|
127
|
-
type CompletedTurnHookFailure,
|
|
128
|
-
type CompletedTurnHookInput,
|
|
129
124
|
type ContextSurfaceRefreshSummary,
|
|
130
125
|
type CreateRuntimeSessionInput,
|
|
131
126
|
type ExecuteTurnInput,
|
|
@@ -347,6 +342,16 @@ class DefaultRuntimeSession implements RuntimeSession {
|
|
|
347
342
|
}
|
|
348
343
|
let session: DefaultRuntimeSession;
|
|
349
344
|
try {
|
|
345
|
+
if (
|
|
346
|
+
input.persistence !== false &&
|
|
347
|
+
input.persistence?.observationLogPath === undefined
|
|
348
|
+
) {
|
|
349
|
+
await prepareSessionMemory(
|
|
350
|
+
store.sessionDirectory,
|
|
351
|
+
input.selection.sessionId,
|
|
352
|
+
input.homeRoot,
|
|
353
|
+
);
|
|
354
|
+
}
|
|
350
355
|
session = new DefaultRuntimeSession(
|
|
351
356
|
input,
|
|
352
357
|
dependencies,
|
|
@@ -474,16 +479,9 @@ class DefaultRuntimeSession implements RuntimeSession {
|
|
|
474
479
|
...(input.memorySearch === undefined
|
|
475
480
|
? {}
|
|
476
481
|
: { memorySearch: input.memorySearch }),
|
|
477
|
-
...(input.memoryGet === undefined ? {} : { memoryGet: input.memoryGet }),
|
|
478
482
|
...(input.memoryCreate === undefined
|
|
479
483
|
? {}
|
|
480
484
|
: { memoryCreate: input.memoryCreate }),
|
|
481
|
-
...(input.memoryUpdate === undefined
|
|
482
|
-
? {}
|
|
483
|
-
: { memoryUpdate: input.memoryUpdate }),
|
|
484
|
-
...(input.memoryDelete === undefined
|
|
485
|
-
? {}
|
|
486
|
-
: { memoryDelete: input.memoryDelete }),
|
|
487
485
|
...(session.skillCatalog.skills.size === 0
|
|
488
486
|
? {}
|
|
489
487
|
: {
|
|
@@ -1244,9 +1242,6 @@ class DefaultRuntimeSession implements RuntimeSession {
|
|
|
1244
1242
|
pendingLedgerTurn.finish(result);
|
|
1245
1243
|
settled = true;
|
|
1246
1244
|
this.requireTooling().turnUndoManager?.completeTurn(turn);
|
|
1247
|
-
if (result.status === "completed") {
|
|
1248
|
-
this.notifyCompletedTurn(turn);
|
|
1249
|
-
}
|
|
1250
1245
|
await this.runtimeSkills.settleClosedTurnSkills();
|
|
1251
1246
|
if (result.status === "completed") {
|
|
1252
1247
|
await this.contextMaintenance.evaluateClosedTurnContextPressure();
|
|
@@ -1270,55 +1265,6 @@ class DefaultRuntimeSession implements RuntimeSession {
|
|
|
1270
1265
|
}
|
|
1271
1266
|
}
|
|
1272
1267
|
|
|
1273
|
-
private notifyCompletedTurn(turn: TurnIdentity): void {
|
|
1274
|
-
const hook = this.input.completedTurnHook;
|
|
1275
|
-
if (hook === undefined) {
|
|
1276
|
-
return;
|
|
1277
|
-
}
|
|
1278
|
-
let snapshot: CompletedTurnSnapshot;
|
|
1279
|
-
try {
|
|
1280
|
-
snapshot = this.store.readCompletedTurnSnapshot(turn.turnId);
|
|
1281
|
-
} catch {
|
|
1282
|
-
this.recordCompletedTurnHookFailure(
|
|
1283
|
-
hook,
|
|
1284
|
-
turn.turnId,
|
|
1285
|
-
"completed_turn_snapshot_failed",
|
|
1286
|
-
);
|
|
1287
|
-
return;
|
|
1288
|
-
}
|
|
1289
|
-
try {
|
|
1290
|
-
hook.enqueue({
|
|
1291
|
-
workspaceRoot: this.input.workspaceRoot,
|
|
1292
|
-
sessionId: this.sessionId,
|
|
1293
|
-
turnId: turn.turnId,
|
|
1294
|
-
snapshot,
|
|
1295
|
-
});
|
|
1296
|
-
} catch {
|
|
1297
|
-
this.recordCompletedTurnHookFailure(
|
|
1298
|
-
hook,
|
|
1299
|
-
turn.turnId,
|
|
1300
|
-
"completed_turn_enqueue_failed",
|
|
1301
|
-
);
|
|
1302
|
-
}
|
|
1303
|
-
}
|
|
1304
|
-
|
|
1305
|
-
private recordCompletedTurnHookFailure(
|
|
1306
|
-
hook: CompletedTurnHook,
|
|
1307
|
-
turnId: TurnId,
|
|
1308
|
-
reason: CompletedTurnHookFailure["reason"],
|
|
1309
|
-
): void {
|
|
1310
|
-
try {
|
|
1311
|
-
hook.recordFailure({
|
|
1312
|
-
workspaceRoot: this.input.workspaceRoot,
|
|
1313
|
-
sessionId: this.sessionId,
|
|
1314
|
-
turnId,
|
|
1315
|
-
reason,
|
|
1316
|
-
});
|
|
1317
|
-
} catch {
|
|
1318
|
-
// Optional completed-turn integrations never fault a committed turn.
|
|
1319
|
-
}
|
|
1320
|
-
}
|
|
1321
|
-
|
|
1322
1268
|
private async appendTerminalEvent(
|
|
1323
1269
|
turn: TurnIdentity,
|
|
1324
1270
|
result: RunAgentResult,
|
|
@@ -1744,7 +1690,7 @@ function createEventSink(
|
|
|
1744
1690
|
),
|
|
1745
1691
|
new ObservationTextLog(
|
|
1746
1692
|
input.persistence?.observationLogPath ??
|
|
1747
|
-
|
|
1693
|
+
sessionMemoryPath(input.selection.sessionId, input.homeRoot),
|
|
1748
1694
|
),
|
|
1749
1695
|
);
|
|
1750
1696
|
}
|
package/src/cli/config.ts
CHANGED
|
@@ -17,7 +17,6 @@ import {
|
|
|
17
17
|
type ModelProfiles,
|
|
18
18
|
unknownProfileError,
|
|
19
19
|
} from "./model-profiles";
|
|
20
|
-
import type { MemoryEmbeddingConfig } from "../memory/contracts";
|
|
21
20
|
import type { ReasoningEffortConfig } from "../model/reasoning-effort";
|
|
22
21
|
import {
|
|
23
22
|
parsePublicEnvironment,
|
|
@@ -54,12 +53,6 @@ export type RunnerConfigSelection = {
|
|
|
54
53
|
|
|
55
54
|
type RunnerConfigTemplate = Omit<RunnerConfig, "sessionId">;
|
|
56
55
|
|
|
57
|
-
export type ResolvedMemoryConfig = {
|
|
58
|
-
readonly profile: ModelProfile;
|
|
59
|
-
readonly contextBudget: ModelContextBudget;
|
|
60
|
-
readonly embedding: MemoryEmbeddingConfig;
|
|
61
|
-
};
|
|
62
|
-
|
|
63
56
|
export type ResolvedPublicConfig =
|
|
64
57
|
| {
|
|
65
58
|
readonly mode: "env";
|
|
@@ -71,7 +64,6 @@ export type ResolvedPublicConfig =
|
|
|
71
64
|
readonly tooling: PublicToolingConfig;
|
|
72
65
|
readonly profiles: ModelProfiles;
|
|
73
66
|
readonly templates: ReadonlyMap<string, RunnerConfigTemplate>;
|
|
74
|
-
readonly memory?: ResolvedMemoryConfig;
|
|
75
67
|
readonly persistDefaultProfile: (profileName: string) => Promise<void>;
|
|
76
68
|
};
|
|
77
69
|
|
|
@@ -101,16 +93,11 @@ export function createResolvedPublicConfig(
|
|
|
101
93
|
runnerConfigTemplateFromProfile(environment, profile),
|
|
102
94
|
]),
|
|
103
95
|
);
|
|
104
|
-
const memory =
|
|
105
|
-
profiles.memory === undefined
|
|
106
|
-
? undefined
|
|
107
|
-
: resolveMemoryConfig(profiles, profiles.memory.profile);
|
|
108
96
|
return Object.freeze({
|
|
109
97
|
mode: "profile",
|
|
110
98
|
tooling: environment.tooling,
|
|
111
99
|
profiles,
|
|
112
100
|
templates,
|
|
113
|
-
...(memory === undefined ? {} : { memory }),
|
|
114
101
|
persistDefaultProfile: (profileName: string) =>
|
|
115
102
|
persistDefaultProfile(profileName, environment.modelsPath),
|
|
116
103
|
});
|
|
@@ -126,22 +113,6 @@ export function createResolvedPublicConfig(
|
|
|
126
113
|
});
|
|
127
114
|
}
|
|
128
115
|
|
|
129
|
-
function resolveMemoryConfig(
|
|
130
|
-
profiles: ModelProfiles,
|
|
131
|
-
profileName: string,
|
|
132
|
-
): ResolvedMemoryConfig {
|
|
133
|
-
const profile = profiles.profiles.get(profileName);
|
|
134
|
-
if (profile === undefined || profiles.memory === undefined) {
|
|
135
|
-
throw unknownProfileError(profileName, profiles);
|
|
136
|
-
}
|
|
137
|
-
const contextProfile = profileToContextProfile(profile);
|
|
138
|
-
return Object.freeze({
|
|
139
|
-
profile,
|
|
140
|
-
contextBudget: deriveModelContextBudget(contextProfile),
|
|
141
|
-
embedding: profiles.memory.embedding,
|
|
142
|
-
});
|
|
143
|
-
}
|
|
144
|
-
|
|
145
116
|
export function deriveRunnerConfig(
|
|
146
117
|
snapshot: ResolvedPublicConfig,
|
|
147
118
|
selection: RunnerConfigSelection,
|
|
@@ -5,13 +5,10 @@ import {
|
|
|
5
5
|
} from "../model/model-context-profile";
|
|
6
6
|
import { parseModelApi, type ModelApi } from "../model/model-api";
|
|
7
7
|
import {
|
|
8
|
-
MEMORY_CONFIG_FIELDS,
|
|
9
|
-
MEMORY_EMBEDDING_FIELDS,
|
|
10
8
|
MODEL_PROFILE_FIELDS,
|
|
11
9
|
MODEL_PROFILES_DOCUMENT_FIELDS,
|
|
12
10
|
MODEL_REASONING_FIELDS,
|
|
13
11
|
} from "./public-config-contract";
|
|
14
|
-
import type { MemoryEmbeddingConfig } from "../memory/contracts";
|
|
15
12
|
import type { ReasoningEffortConfig } from "../model/reasoning-effort";
|
|
16
13
|
import type { ModelInputModality, ToolResultModality } from "../model/model-client";
|
|
17
14
|
|
|
@@ -34,12 +31,6 @@ export type { ModelInputModality, ToolResultModality } from "../model/model-clie
|
|
|
34
31
|
export type ModelProfiles = {
|
|
35
32
|
readonly defaultProfile: string;
|
|
36
33
|
readonly profiles: ReadonlyMap<string, ModelProfile>;
|
|
37
|
-
readonly memory?: MemoryConfig;
|
|
38
|
-
};
|
|
39
|
-
|
|
40
|
-
export type MemoryConfig = {
|
|
41
|
-
readonly profile: string;
|
|
42
|
-
readonly embedding: MemoryEmbeddingConfig;
|
|
43
34
|
};
|
|
44
35
|
|
|
45
36
|
export async function loadModelProfiles(configPath: string): Promise<ModelProfiles> {
|
|
@@ -146,14 +137,9 @@ export function parseModelProfiles(raw: string, sourcePath: string): ModelProfil
|
|
|
146
137
|
profiles.set(profileName, parseProfile(profileName, profileValue, sourcePath));
|
|
147
138
|
}
|
|
148
139
|
|
|
149
|
-
const memory =
|
|
150
|
-
json.memory === undefined
|
|
151
|
-
? undefined
|
|
152
|
-
: parseMemoryConfig(json.memory, profiles, sourcePath);
|
|
153
140
|
return Object.freeze({
|
|
154
141
|
defaultProfile,
|
|
155
142
|
profiles,
|
|
156
|
-
...(memory === undefined ? {} : { memory }),
|
|
157
143
|
});
|
|
158
144
|
}
|
|
159
145
|
|
|
@@ -370,56 +356,6 @@ function parseReasoning(value: unknown, where: string): ReasoningEffortConfig {
|
|
|
370
356
|
});
|
|
371
357
|
}
|
|
372
358
|
|
|
373
|
-
function parseMemoryConfig(
|
|
374
|
-
value: unknown,
|
|
375
|
-
profiles: ReadonlyMap<string, ModelProfile>,
|
|
376
|
-
sourcePath: string,
|
|
377
|
-
): MemoryConfig {
|
|
378
|
-
const where = `Model profiles ${sourcePath}: "memory"`;
|
|
379
|
-
if (!isRecord(value)) {
|
|
380
|
-
throw new Error(`${where} must be an object.`);
|
|
381
|
-
}
|
|
382
|
-
assertKnownKeys(
|
|
383
|
-
value,
|
|
384
|
-
MEMORY_CONFIG_FIELDS.map((field) => field.name),
|
|
385
|
-
where,
|
|
386
|
-
);
|
|
387
|
-
const profile = requireString(value.profile, `${where}.profile`);
|
|
388
|
-
if (!profiles.has(profile)) {
|
|
389
|
-
throw unknownProfileNamesError(profile, [...profiles.keys()]);
|
|
390
|
-
}
|
|
391
|
-
const embedding = parseMemoryEmbedding(value.embedding, `${where}.embedding`);
|
|
392
|
-
return Object.freeze({ profile, embedding });
|
|
393
|
-
}
|
|
394
|
-
|
|
395
|
-
function parseMemoryEmbedding(value: unknown, where: string): MemoryEmbeddingConfig {
|
|
396
|
-
if (!isRecord(value)) {
|
|
397
|
-
throw new Error(`${where} must be an object.`);
|
|
398
|
-
}
|
|
399
|
-
assertKnownKeys(
|
|
400
|
-
value,
|
|
401
|
-
MEMORY_EMBEDDING_FIELDS.map((field) => field.name),
|
|
402
|
-
where,
|
|
403
|
-
);
|
|
404
|
-
const name = requireString(value.name, `${where}.name`);
|
|
405
|
-
if (value.kind !== "openai-compatible") {
|
|
406
|
-
throw new Error(`${where}.kind must be "openai-compatible".`);
|
|
407
|
-
}
|
|
408
|
-
const model = requireString(value.model, `${where}.model`);
|
|
409
|
-
const apiBase = requireString(value.apiBase, `${where}.apiBase`);
|
|
410
|
-
requireHttpUrl(apiBase, `${where}.apiBase`);
|
|
411
|
-
const apiKey = requireString(value.apiKey, `${where}.apiKey`);
|
|
412
|
-
const dimensions = requirePositiveInteger(value.dimensions, `${where}.dimensions`);
|
|
413
|
-
return Object.freeze({
|
|
414
|
-
name,
|
|
415
|
-
kind: "openai-compatible",
|
|
416
|
-
model,
|
|
417
|
-
apiBase,
|
|
418
|
-
apiKey,
|
|
419
|
-
dimensions,
|
|
420
|
-
});
|
|
421
|
-
}
|
|
422
|
-
|
|
423
359
|
function parseInputModalities(
|
|
424
360
|
value: unknown,
|
|
425
361
|
name: string,
|
|
@@ -538,22 +474,6 @@ function requirePositiveInteger(value: unknown, name: string): number {
|
|
|
538
474
|
return value;
|
|
539
475
|
}
|
|
540
476
|
|
|
541
|
-
function requireHttpUrl(value: string, name: string): void {
|
|
542
|
-
let parsed: URL;
|
|
543
|
-
try {
|
|
544
|
-
parsed = new URL(value);
|
|
545
|
-
} catch {
|
|
546
|
-
throw new Error(`${name} must be a valid HTTP(S) URL.`);
|
|
547
|
-
}
|
|
548
|
-
if (
|
|
549
|
-
(parsed.protocol !== "http:" && parsed.protocol !== "https:") ||
|
|
550
|
-
parsed.username !== "" ||
|
|
551
|
-
parsed.password !== ""
|
|
552
|
-
) {
|
|
553
|
-
throw new Error(`${name} must be a valid HTTP(S) URL.`);
|
|
554
|
-
}
|
|
555
|
-
}
|
|
556
|
-
|
|
557
477
|
function parseBoolean(value: unknown, name: string): boolean {
|
|
558
478
|
if (typeof value === "boolean") {
|
|
559
479
|
return value;
|
|
@@ -379,91 +379,10 @@ export const MODEL_REASONING_FIELDS = Object.freeze([
|
|
|
379
379
|
}),
|
|
380
380
|
]);
|
|
381
381
|
|
|
382
|
-
export type MemoryConfigField = {
|
|
383
|
-
readonly name: "profile" | "embedding";
|
|
384
|
-
readonly valueKind: "non-empty-string" | "embedding-profile";
|
|
385
|
-
readonly required: true;
|
|
386
|
-
readonly secret: boolean;
|
|
387
|
-
readonly description: string;
|
|
388
|
-
};
|
|
389
|
-
|
|
390
|
-
export const MEMORY_CONFIG_FIELDS = Object.freeze([
|
|
391
|
-
Object.freeze({
|
|
392
|
-
name: "profile",
|
|
393
|
-
valueKind: "non-empty-string",
|
|
394
|
-
required: true,
|
|
395
|
-
secret: false,
|
|
396
|
-
description:
|
|
397
|
-
"Existing model profile used for completed-turn atomic-memory extraction.",
|
|
398
|
-
}),
|
|
399
|
-
Object.freeze({
|
|
400
|
-
name: "embedding",
|
|
401
|
-
valueKind: "embedding-profile",
|
|
402
|
-
required: true,
|
|
403
|
-
secret: true,
|
|
404
|
-
description: "Single embedding profile for the global memory database.",
|
|
405
|
-
}),
|
|
406
|
-
] satisfies readonly MemoryConfigField[]);
|
|
407
|
-
|
|
408
|
-
export type MemoryEmbeddingField = {
|
|
409
|
-
readonly name: "name" | "kind" | "model" | "apiBase" | "apiKey" | "dimensions";
|
|
410
|
-
readonly valueKind: "non-empty-string" | "positive-integer" | "literal-string";
|
|
411
|
-
readonly required: true;
|
|
412
|
-
readonly secret: boolean;
|
|
413
|
-
readonly literalValue?: "openai-compatible";
|
|
414
|
-
readonly description: string;
|
|
415
|
-
};
|
|
416
|
-
|
|
417
|
-
export const MEMORY_EMBEDDING_FIELDS = Object.freeze([
|
|
418
|
-
Object.freeze({
|
|
419
|
-
name: "name",
|
|
420
|
-
valueKind: "non-empty-string",
|
|
421
|
-
required: true,
|
|
422
|
-
secret: false,
|
|
423
|
-
description: "Stable identity for the embedding space.",
|
|
424
|
-
}),
|
|
425
|
-
Object.freeze({
|
|
426
|
-
name: "kind",
|
|
427
|
-
valueKind: "literal-string",
|
|
428
|
-
required: true,
|
|
429
|
-
secret: false,
|
|
430
|
-
literalValue: "openai-compatible",
|
|
431
|
-
description: "Embedding transport kind.",
|
|
432
|
-
}),
|
|
433
|
-
Object.freeze({
|
|
434
|
-
name: "model",
|
|
435
|
-
valueKind: "non-empty-string",
|
|
436
|
-
required: true,
|
|
437
|
-
secret: false,
|
|
438
|
-
description: "Embedding provider model name.",
|
|
439
|
-
}),
|
|
440
|
-
Object.freeze({
|
|
441
|
-
name: "apiBase",
|
|
442
|
-
valueKind: "non-empty-string",
|
|
443
|
-
required: true,
|
|
444
|
-
secret: false,
|
|
445
|
-
description: "OpenAI-compatible API base URL.",
|
|
446
|
-
}),
|
|
447
|
-
Object.freeze({
|
|
448
|
-
name: "apiKey",
|
|
449
|
-
valueKind: "non-empty-string",
|
|
450
|
-
required: true,
|
|
451
|
-
secret: true,
|
|
452
|
-
description: "Embedding provider credential.",
|
|
453
|
-
}),
|
|
454
|
-
Object.freeze({
|
|
455
|
-
name: "dimensions",
|
|
456
|
-
valueKind: "positive-integer",
|
|
457
|
-
required: true,
|
|
458
|
-
secret: false,
|
|
459
|
-
description: "Fixed vector dimensions for the global memory database.",
|
|
460
|
-
}),
|
|
461
|
-
] satisfies readonly MemoryEmbeddingField[]);
|
|
462
|
-
|
|
463
382
|
export const MODEL_PROFILES_DOCUMENT_FIELDS = Object.freeze([
|
|
464
383
|
Object.freeze({ name: "default", valueKind: "non-empty-string" as const }),
|
|
465
384
|
Object.freeze({ name: "profiles", valueKind: "profiles-map" as const }),
|
|
466
|
-
Object.freeze({ name: "memory", valueKind: "
|
|
385
|
+
Object.freeze({ name: "memory", valueKind: "ignored-legacy-config" as const }),
|
|
467
386
|
]);
|
|
468
387
|
|
|
469
388
|
export type PublicToolingConfig = {
|