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.
Files changed (36) hide show
  1. package/CHANGELOG.md +32 -1
  2. package/README.md +31 -67
  3. package/package.json +1 -1
  4. package/src/agent/runtime-session-contracts.ts +2 -29
  5. package/src/agent/runtime-session.ts +13 -67
  6. package/src/cli/config.ts +0 -29
  7. package/src/cli/model-profiles.ts +0 -80
  8. package/src/cli/public-config-contract.ts +1 -82
  9. package/src/cli/runner-dependencies.ts +35 -35
  10. package/src/cli/tui-runner.tsx +2 -41
  11. package/src/events/observation-text-log.ts +7 -0
  12. package/src/memory/contracts.ts +0 -256
  13. package/src/memory/memory-create-tool.ts +3 -5
  14. package/src/memory/memory-files.ts +145 -0
  15. package/src/memory/memory-search-output.ts +23 -0
  16. package/src/memory/memory-search-tool.ts +79 -175
  17. package/src/memory/memory-search.ts +115 -0
  18. package/src/observation/observation-builder.ts +5 -1
  19. package/src/session/session-store-contracts.ts +0 -23
  20. package/src/session/session-store.ts +2 -103
  21. package/src/tools/bash.ts +9 -1
  22. package/src/tools/bounded-output-preview.ts +2 -2
  23. package/src/tools/registry.ts +15 -18
  24. package/src/tools/types.ts +12 -0
  25. package/src/tui/app.tsx +6 -4
  26. package/src/tui/event-store.ts +3 -1
  27. package/src/cli/tui-memory.ts +0 -69
  28. package/src/memory/embedding-client.ts +0 -105
  29. package/src/memory/memory-coordinator.ts +0 -1274
  30. package/src/memory/memory-delete-tool.ts +0 -88
  31. package/src/memory/memory-extractor.ts +0 -252
  32. package/src/memory/memory-get-tool.ts +0 -86
  33. package/src/memory/memory-log.ts +0 -88
  34. package/src/memory/memory-store.ts +0 -1133
  35. package/src/memory/memory-update-tool.ts +0 -142
  36. 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.11.0...HEAD
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 Memory result's `sourceSessionId`:
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, event logs, and observation
574
- logs (`sessions/<id>/`), background Bash task logs (`bash/`), imported image
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tinker-agent",
3
- "version": "2.11.0",
3
+ "version": "2.13.0",
4
4
  "description": "A personal coding agent with an interactive TUI and one-shot CLI.",
5
5
  "license": "Apache-2.0",
6
6
  "type": "module",
@@ -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, TurnId } from "../ids/runtime-id";
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, type TurnId } from "../ids/runtime-id";
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
- path.join(sessionDirectory, "observations.md"),
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: "memory-config" as const }),
385
+ Object.freeze({ name: "memory", valueKind: "ignored-legacy-config" as const }),
467
386
  ]);
468
387
 
469
388
  export type PublicToolingConfig = {