tinker-agent 2.10.0 → 2.12.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 (52) hide show
  1. package/CHANGELOG.md +47 -1
  2. package/README.md +44 -67
  3. package/package.json +4 -3
  4. package/src/agent/loop.ts +50 -13
  5. package/src/agent/runtime-provider-retry.ts +115 -0
  6. package/src/agent/runtime-session-contracts.ts +13 -29
  7. package/src/agent/runtime-session.ts +42 -67
  8. package/src/cli/config.ts +0 -29
  9. package/src/cli/model-profiles.ts +0 -80
  10. package/src/cli/public-config-contract.ts +1 -82
  11. package/src/cli/tui-runner.tsx +3 -41
  12. package/src/events/observation-text-log.ts +7 -0
  13. package/src/events/types.ts +8 -0
  14. package/src/image/abortable-file-open.ts +54 -0
  15. package/src/image/image-asset-store.ts +7 -1
  16. package/src/memory/contracts.ts +0 -256
  17. package/src/memory/memory-create-tool.ts +3 -5
  18. package/src/memory/memory-files.ts +145 -0
  19. package/src/memory/memory-search-output.ts +23 -0
  20. package/src/memory/memory-search-tool.ts +79 -175
  21. package/src/memory/memory-search.ts +115 -0
  22. package/src/model/fake-model-client.ts +20 -1
  23. package/src/model/openai-model-utils.ts +45 -1
  24. package/src/model/openai-responses-mapping.ts +11 -0
  25. package/src/model/openai-responses-stream.ts +14 -0
  26. package/src/observation/observation-builder.ts +5 -1
  27. package/src/session/scoped-query-database.ts +27 -0
  28. package/src/session/session-history-access.ts +4 -3
  29. package/src/session/session-store-contracts.ts +0 -23
  30. package/src/session/session-store.ts +9 -106
  31. package/src/tools/grep.ts +12 -4
  32. package/src/tools/registry.ts +15 -18
  33. package/src/tools/types.ts +12 -0
  34. package/src/tui/app.tsx +51 -7
  35. package/src/tui/components/ask-user.tsx +15 -8
  36. package/src/tui/components/prompt-input.tsx +14 -6
  37. package/src/tui/components/timeline.tsx +17 -9
  38. package/src/tui/event-store.ts +13 -1
  39. package/src/tui/file-mention.ts +29 -5
  40. package/src/tui/tui-projection-store.ts +5 -2
  41. package/src/tui/tui-session-controller.ts +8 -0
  42. package/src/tui/workspace-file-search.ts +21 -0
  43. package/src/cli/tui-memory.ts +0 -69
  44. package/src/memory/embedding-client.ts +0 -105
  45. package/src/memory/memory-coordinator.ts +0 -1274
  46. package/src/memory/memory-delete-tool.ts +0 -88
  47. package/src/memory/memory-extractor.ts +0 -252
  48. package/src/memory/memory-get-tool.ts +0 -86
  49. package/src/memory/memory-log.ts +0 -88
  50. package/src/memory/memory-store.ts +0 -1133
  51. package/src/memory/memory-update-tool.ts +0 -142
  52. package/src/memory/vector.ts +0 -153
@@ -1,142 +0,0 @@
1
- import { throwIfTurnCancelled } from "../agent/turn-cancellation";
2
- import type { ToolCall } from "../agent/types";
3
- import {
4
- defineToolExecutor,
5
- type MemoryUpdateRawResult,
6
- type ToolDefinition,
7
- type ToolExecutor,
8
- } from "../tools/types";
9
- import {
10
- MAX_MEMORY_ID_BYTES,
11
- MAX_MEMORY_SUMMARY_BYTES,
12
- MAX_MEMORY_TEXT_BYTES,
13
- MEMORY_UPDATE_TOOL_NAME,
14
- } from "./contracts";
15
-
16
- export const MEMORY_UPDATE_TOOL_DEFINITION: ToolDefinition = Object.freeze({
17
- name: MEMORY_UPDATE_TOOL_NAME,
18
- description: "Replace one global memory shared across sessions and workspaces.",
19
- parameters: {
20
- type: "object",
21
- additionalProperties: false,
22
- properties: {
23
- id: {
24
- type: "string",
25
- minLength: 1,
26
- maxLength: MAX_MEMORY_ID_BYTES,
27
- description: "The memoryId returned by MemorySearch or MemoryGet.",
28
- },
29
- text: {
30
- type: "string",
31
- minLength: 1,
32
- maxLength: MAX_MEMORY_TEXT_BYTES,
33
- description: "The complete replacement one-line searchable index.",
34
- },
35
- summary: {
36
- type: "string",
37
- maxLength: MAX_MEMORY_SUMMARY_BYTES,
38
- description:
39
- "The complete replacement details, reasons, constraints, and scope.",
40
- },
41
- },
42
- required: ["id", "text", "summary"],
43
- },
44
- });
45
-
46
- export function createMemoryUpdateToolExecutor(options: {
47
- readonly update: (
48
- memoryId: string,
49
- text: string,
50
- summary: string,
51
- call: ToolCall,
52
- signal: AbortSignal,
53
- ) => Promise<MemoryUpdateRawResult>;
54
- readonly recordInvalidCall: (call: ToolCall) => Promise<void>;
55
- }): ToolExecutor {
56
- return defineToolExecutor("memory_update", {
57
- definition: MEMORY_UPDATE_TOOL_DEFINITION,
58
- async execute(args, call, context): Promise<MemoryUpdateRawResult> {
59
- const parsed = parseMemoryUpdateArgs(args);
60
- if (!parsed.ok) {
61
- throwIfTurnCancelled(context.signal);
62
- await options.recordInvalidCall(call);
63
- throwIfTurnCancelled(context.signal);
64
- return { ok: false, error: parsed.error };
65
- }
66
- const result = await options.update(
67
- parsed.memoryId,
68
- parsed.text,
69
- parsed.summary,
70
- call,
71
- context.signal,
72
- );
73
- throwIfTurnCancelled(context.signal);
74
- return result;
75
- },
76
- });
77
- }
78
-
79
- type ParsedMemoryUpdateArgs =
80
- | {
81
- readonly ok: true;
82
- readonly memoryId: string;
83
- readonly text: string;
84
- readonly summary: string;
85
- }
86
- | { readonly ok: false; readonly error: string };
87
-
88
- function parseMemoryUpdateArgs(args: unknown): ParsedMemoryUpdateArgs {
89
- if (!isRecord(args)) {
90
- return {
91
- ok: false,
92
- error:
93
- "MemoryUpdate arguments must be an object containing only id, text, and summary.",
94
- };
95
- }
96
- const unexpected = Object.keys(args).find(
97
- (key) => key !== "id" && key !== "text" && key !== "summary",
98
- );
99
- if (unexpected !== undefined) {
100
- return {
101
- ok: false,
102
- error: `MemoryUpdate received unexpected field: ${unexpected}.`,
103
- };
104
- }
105
- if (typeof args.id !== "string") {
106
- return { ok: false, error: "MemoryUpdate.id must be a string." };
107
- }
108
- const memoryId = args.id.trim();
109
- const idBytes = Buffer.byteLength(memoryId, "utf8");
110
- if (idBytes < 1 || idBytes > MAX_MEMORY_ID_BYTES) {
111
- return {
112
- ok: false,
113
- error: `MemoryUpdate.id must be 1 to ${MAX_MEMORY_ID_BYTES} UTF-8 bytes after trimming.`,
114
- };
115
- }
116
- if (typeof args.text !== "string") {
117
- return { ok: false, error: "MemoryUpdate.text must be a string." };
118
- }
119
- const text = args.text.trim();
120
- const textBytes = Buffer.byteLength(text, "utf8");
121
- if (textBytes < 1 || textBytes > MAX_MEMORY_TEXT_BYTES) {
122
- return {
123
- ok: false,
124
- error: `MemoryUpdate.text must be 1 to ${MAX_MEMORY_TEXT_BYTES} UTF-8 bytes after trimming.`,
125
- };
126
- }
127
- if (typeof args.summary !== "string") {
128
- return { ok: false, error: "MemoryUpdate.summary must be a string." };
129
- }
130
- const summary = args.summary.trim();
131
- if (Buffer.byteLength(summary, "utf8") > MAX_MEMORY_SUMMARY_BYTES) {
132
- return {
133
- ok: false,
134
- error: `MemoryUpdate.summary must be at most ${MAX_MEMORY_SUMMARY_BYTES} UTF-8 bytes after trimming.`,
135
- };
136
- }
137
- return { ok: true, memoryId, text, summary };
138
- }
139
-
140
- function isRecord(value: unknown): value is Record<string, unknown> {
141
- return typeof value === "object" && value !== null && !Array.isArray(value);
142
- }
@@ -1,153 +0,0 @@
1
- import { MemoryError } from "./contracts";
2
-
3
- export function normalizeEmbedding(
4
- values: readonly number[],
5
- dimensions: number,
6
- ): Float32Array {
7
- requireDimensions(dimensions);
8
- if (values.length !== dimensions) {
9
- throw new MemoryError(
10
- "memory_embedding_dimensions_invalid",
11
- `Embedding has ${values.length} dimensions; expected ${dimensions}.`,
12
- );
13
- }
14
- if (values.some((value) => !Number.isFinite(value))) {
15
- throw new MemoryError(
16
- "memory_embedding_values_invalid",
17
- "Embedding contains a non-finite value.",
18
- );
19
- }
20
-
21
- const rawNorm = vectorNorm(values);
22
- if (!Number.isFinite(rawNorm) || rawNorm === 0) {
23
- throw new MemoryError(
24
- "memory_embedding_norm_invalid",
25
- "Embedding norm must be finite and non-zero.",
26
- );
27
- }
28
-
29
- const normalized = new Float32Array(
30
- values.map((value) => Math.fround(value / rawNorm)),
31
- );
32
- const floatNorm = vectorNorm(normalized);
33
- if (!Number.isFinite(floatNorm) || floatNorm === 0) {
34
- throw new MemoryError(
35
- "memory_embedding_norm_invalid",
36
- "Embedding became zero or non-finite after Float32 conversion.",
37
- );
38
- }
39
- for (let index = 0; index < normalized.length; index += 1) {
40
- normalized[index] = Math.fround(normalized[index] / floatNorm);
41
- }
42
- return normalized;
43
- }
44
-
45
- export function encodeEmbedding(vector: Float32Array): Uint8Array {
46
- const bytes = new Uint8Array(vector.length * Float32Array.BYTES_PER_ELEMENT);
47
- const view = new DataView(bytes.buffer);
48
- for (let index = 0; index < vector.length; index += 1) {
49
- const value = vector[index];
50
- if (!Number.isFinite(value)) {
51
- throw new MemoryError(
52
- "memory_embedding_values_invalid",
53
- "Embedding contains a non-finite Float32 value.",
54
- );
55
- }
56
- view.setFloat32(index * Float32Array.BYTES_PER_ELEMENT, value, true);
57
- }
58
- return bytes;
59
- }
60
-
61
- export function decodeEmbedding(
62
- value: unknown,
63
- expectedBlobBytes: number,
64
- ): Float32Array {
65
- if (!(value instanceof Uint8Array)) {
66
- throw new MemoryError(
67
- "memory_embedding_blob_invalid",
68
- "Stored memory embedding is not a SQLite BLOB.",
69
- );
70
- }
71
- if (value.byteLength !== expectedBlobBytes) {
72
- throw new MemoryError(
73
- "memory_embedding_blob_invalid",
74
- `Stored memory embedding has ${value.byteLength} bytes; expected ${expectedBlobBytes}.`,
75
- );
76
- }
77
-
78
- const view = new DataView(value.buffer, value.byteOffset, value.byteLength);
79
- const dimensions = expectedBlobBytes / Float32Array.BYTES_PER_ELEMENT;
80
- const vector = new Float32Array(dimensions);
81
- for (let index = 0; index < dimensions; index += 1) {
82
- const decoded = view.getFloat32(index * Float32Array.BYTES_PER_ELEMENT, true);
83
- if (!Number.isFinite(decoded)) {
84
- throw new MemoryError(
85
- "memory_embedding_blob_invalid",
86
- "Stored memory embedding contains a non-finite value.",
87
- );
88
- }
89
- vector[index] = decoded;
90
- }
91
- return vector;
92
- }
93
-
94
- export function cosineFromNormalized(left: Float32Array, right: Float32Array): number {
95
- if (left.length !== right.length || left.length === 0) {
96
- throw new MemoryError(
97
- "memory_embedding_dimensions_invalid",
98
- "Cosine vectors must have the same non-zero dimensions.",
99
- );
100
- }
101
- let score = 0;
102
- for (let index = 0; index < left.length; index += 1) {
103
- score += left[index] * right[index];
104
- }
105
- if (!Number.isFinite(score)) {
106
- throw new MemoryError(
107
- "memory_embedding_values_invalid",
108
- "Cosine score is not finite.",
109
- );
110
- }
111
- return score;
112
- }
113
-
114
- export function expectedEmbeddingBlobBytes(dimensions: number): number {
115
- requireDimensions(dimensions);
116
- const bytes = dimensions * Float32Array.BYTES_PER_ELEMENT;
117
- if (!Number.isSafeInteger(bytes) || bytes < 1) {
118
- throw new MemoryError(
119
- "memory_embedding_dimensions_invalid",
120
- "Embedding byte length is not a positive safe integer.",
121
- );
122
- }
123
- return bytes;
124
- }
125
-
126
- function requireDimensions(dimensions: number): void {
127
- if (!Number.isSafeInteger(dimensions) || dimensions < 1) {
128
- throw new MemoryError(
129
- "memory_embedding_dimensions_invalid",
130
- "Embedding dimensions must be a positive safe integer.",
131
- );
132
- }
133
- }
134
-
135
- function vectorNorm(values: ArrayLike<number>): number {
136
- let scale = 0;
137
- let sumSquares = 1;
138
- for (let index = 0; index < values.length; index += 1) {
139
- const absolute = Math.abs(values[index]);
140
- if (absolute === 0) {
141
- continue;
142
- }
143
- if (scale < absolute) {
144
- const ratio = scale / absolute;
145
- sumSquares = 1 + sumSquares * ratio * ratio;
146
- scale = absolute;
147
- } else {
148
- const ratio = absolute / scale;
149
- sumSquares += ratio * ratio;
150
- }
151
- }
152
- return scale === 0 ? 0 : scale * Math.sqrt(sumSquares);
153
- }