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.
Files changed (208) hide show
  1. package/CHANGELOG.md +36 -0
  2. package/LICENSE +201 -0
  3. package/NOTICE +6 -0
  4. package/README.md +90 -9
  5. package/bin/tinker.js +39 -0
  6. package/node_modules/ansi-regex/index.d.ts +33 -0
  7. package/node_modules/ansi-regex/index.js +14 -0
  8. package/node_modules/ansi-regex/license +9 -0
  9. package/node_modules/ansi-regex/package.json +61 -0
  10. package/node_modules/ansi-regex/readme.md +66 -0
  11. package/node_modules/ansi-styles/index.d.ts +236 -0
  12. package/node_modules/ansi-styles/index.js +223 -0
  13. package/node_modules/ansi-styles/license +9 -0
  14. package/node_modules/ansi-styles/package.json +54 -0
  15. package/node_modules/ansi-styles/readme.md +173 -0
  16. package/node_modules/chalk/license +9 -0
  17. package/node_modules/chalk/package.json +83 -0
  18. package/node_modules/chalk/readme.md +297 -0
  19. package/node_modules/chalk/source/index.d.ts +325 -0
  20. package/node_modules/chalk/source/index.js +225 -0
  21. package/node_modules/chalk/source/utilities.js +33 -0
  22. package/node_modules/chalk/source/vendor/ansi-styles/index.d.ts +236 -0
  23. package/node_modules/chalk/source/vendor/ansi-styles/index.js +223 -0
  24. package/node_modules/chalk/source/vendor/supports-color/browser.d.ts +1 -0
  25. package/node_modules/chalk/source/vendor/supports-color/browser.js +34 -0
  26. package/node_modules/chalk/source/vendor/supports-color/index.d.ts +55 -0
  27. package/node_modules/chalk/source/vendor/supports-color/index.js +190 -0
  28. package/node_modules/character-entities/index.d.ts +6 -0
  29. package/node_modules/character-entities/index.js +2132 -0
  30. package/node_modules/character-entities/license +22 -0
  31. package/node_modules/character-entities/package.json +78 -0
  32. package/node_modules/character-entities/readme.md +152 -0
  33. package/node_modules/decode-named-character-reference/index.d.ts +13 -0
  34. package/node_modules/decode-named-character-reference/index.d.ts.map +1 -0
  35. package/node_modules/decode-named-character-reference/index.dom.d.ts +6 -0
  36. package/node_modules/decode-named-character-reference/index.dom.d.ts.map +1 -0
  37. package/node_modules/decode-named-character-reference/index.dom.js +32 -0
  38. package/node_modules/decode-named-character-reference/index.js +19 -0
  39. package/node_modules/decode-named-character-reference/license +22 -0
  40. package/node_modules/decode-named-character-reference/package.json +90 -0
  41. package/node_modules/decode-named-character-reference/readme.md +136 -0
  42. package/node_modules/get-east-asian-width/index.d.ts +60 -0
  43. package/node_modules/get-east-asian-width/index.js +30 -0
  44. package/node_modules/get-east-asian-width/license +9 -0
  45. package/node_modules/get-east-asian-width/lookup-data.js +21 -0
  46. package/node_modules/get-east-asian-width/lookup.js +138 -0
  47. package/node_modules/get-east-asian-width/package.json +71 -0
  48. package/node_modules/get-east-asian-width/readme.md +65 -0
  49. package/node_modules/get-east-asian-width/utilities.js +24 -0
  50. package/node_modules/has-flag/index.d.ts +39 -0
  51. package/node_modules/has-flag/index.js +8 -0
  52. package/node_modules/has-flag/license +9 -0
  53. package/node_modules/has-flag/package.json +49 -0
  54. package/node_modules/has-flag/readme.md +74 -0
  55. package/node_modules/is-fullwidth-code-point/index.d.ts +17 -0
  56. package/node_modules/is-fullwidth-code-point/index.js +12 -0
  57. package/node_modules/is-fullwidth-code-point/license +9 -0
  58. package/node_modules/is-fullwidth-code-point/package.json +53 -0
  59. package/node_modules/is-fullwidth-code-point/readme.md +31 -0
  60. package/node_modules/markdansi/LICENSE +21 -0
  61. package/node_modules/markdansi/README.md +167 -0
  62. package/node_modules/markdansi/dist/ast.d.ts +99 -0
  63. package/node_modules/markdansi/dist/ast.js +1 -0
  64. package/node_modules/markdansi/dist/cli.d.ts +17 -0
  65. package/node_modules/markdansi/dist/cli.js +208 -0
  66. package/node_modules/markdansi/dist/hyperlink.d.ts +9 -0
  67. package/node_modules/markdansi/dist/hyperlink.js +26 -0
  68. package/node_modules/markdansi/dist/index.d.ts +10 -0
  69. package/node_modules/markdansi/dist/index.js +16 -0
  70. package/node_modules/markdansi/dist/parser.d.ts +2 -0
  71. package/node_modules/markdansi/dist/parser.js +205 -0
  72. package/node_modules/markdansi/dist/render.d.ts +9 -0
  73. package/node_modules/markdansi/dist/render.js +680 -0
  74. package/node_modules/markdansi/dist/stream.d.ts +39 -0
  75. package/node_modules/markdansi/dist/stream.js +192 -0
  76. package/node_modules/markdansi/dist/theme.d.ts +18 -0
  77. package/node_modules/markdansi/dist/theme.js +105 -0
  78. package/node_modules/markdansi/dist/types.d.ts +58 -0
  79. package/node_modules/markdansi/dist/types.js +1 -0
  80. package/node_modules/markdansi/dist/wrap.d.ts +10 -0
  81. package/node_modules/markdansi/dist/wrap.js +73 -0
  82. package/node_modules/markdansi/docs/spec.md +110 -0
  83. package/node_modules/markdansi/package.json +78 -0
  84. package/node_modules/markdansi/tsconfig.json +18 -0
  85. package/node_modules/marked/LICENSE +44 -0
  86. package/node_modules/marked/README.md +115 -0
  87. package/node_modules/marked/bin/main.js +284 -0
  88. package/node_modules/marked/bin/marked.js +16 -0
  89. package/node_modules/marked/lib/marked.d.ts +760 -0
  90. package/node_modules/marked/lib/marked.esm.js +77 -0
  91. package/node_modules/marked/lib/marked.esm.js.map +7 -0
  92. package/node_modules/marked/lib/marked.umd.js +79 -0
  93. package/node_modules/marked/lib/marked.umd.js.map +7 -0
  94. package/node_modules/marked/man/marked.1 +113 -0
  95. package/node_modules/marked/man/marked.1.md +93 -0
  96. package/node_modules/marked/package.json +103 -0
  97. package/node_modules/slice-ansi/index.d.ts +19 -0
  98. package/node_modules/slice-ansi/index.js +317 -0
  99. package/node_modules/slice-ansi/license +10 -0
  100. package/node_modules/slice-ansi/package.json +59 -0
  101. package/node_modules/slice-ansi/readme.md +55 -0
  102. package/node_modules/slice-ansi/tokenize-ansi.js +752 -0
  103. package/node_modules/string-width/index.d.ts +39 -0
  104. package/node_modules/string-width/index.js +203 -0
  105. package/node_modules/string-width/license +9 -0
  106. package/node_modules/string-width/package.json +65 -0
  107. package/node_modules/string-width/readme.md +66 -0
  108. package/node_modules/strip-ansi/index.d.ts +15 -0
  109. package/node_modules/strip-ansi/index.js +19 -0
  110. package/node_modules/strip-ansi/license +9 -0
  111. package/node_modules/strip-ansi/package.json +59 -0
  112. package/node_modules/strip-ansi/readme.md +37 -0
  113. package/node_modules/supports-color/browser.d.ts +1 -0
  114. package/node_modules/supports-color/browser.js +35 -0
  115. package/node_modules/supports-color/index.d.ts +55 -0
  116. package/node_modules/supports-color/index.js +202 -0
  117. package/node_modules/supports-color/license +9 -0
  118. package/node_modules/supports-color/package.json +64 -0
  119. package/node_modules/supports-color/readme.md +75 -0
  120. package/node_modules/supports-hyperlinks/browser.js +10 -0
  121. package/node_modules/supports-hyperlinks/index.d.ts +25 -0
  122. package/node_modules/supports-hyperlinks/index.js +163 -0
  123. package/node_modules/supports-hyperlinks/license +10 -0
  124. package/node_modules/supports-hyperlinks/package.json +47 -0
  125. package/node_modules/supports-hyperlinks/readme.md +45 -0
  126. package/package.json +41 -10
  127. package/patches/markdansi@0.3.2.patch +22 -2
  128. package/src/agent/context-builder.ts +24 -10
  129. package/src/agent/context-meter.ts +89 -5
  130. package/src/agent/loop.ts +107 -10
  131. package/src/agent/runtime-session.ts +1437 -82
  132. package/src/agent/session-ledger.ts +169 -29
  133. package/src/agent/types.ts +12 -1
  134. package/src/agent/user-prompt-projection.ts +62 -0
  135. package/src/cli/config.ts +57 -14
  136. package/src/cli/model-profiles.ts +124 -0
  137. package/src/cli/run-runner.ts +4 -1
  138. package/src/cli/tui-runner.tsx +68 -30
  139. package/src/context/compiled-context-hash.ts +1 -1
  140. package/src/context/compiled-context-validator.ts +92 -15
  141. package/src/context/context-automation-policy.ts +121 -0
  142. package/src/context/context-manager.ts +390 -11
  143. package/src/context/context-policy.ts +8 -0
  144. package/src/context/context-protocol-validator.ts +11 -1
  145. package/src/context/context-revision-compiler.ts +190 -27
  146. package/src/context/context-revision.ts +105 -19
  147. package/src/context/context-surface.ts +329 -0
  148. package/src/context/prefix-retirement-planner.ts +570 -0
  149. package/src/context/protocol-frame.ts +30 -3
  150. package/src/context/recall-retirement-contract.ts +27 -0
  151. package/src/context/swap-planner.ts +34 -10
  152. package/src/events/observation-text-log.ts +3 -3
  153. package/src/events/stdout-event-printer.ts +39 -3
  154. package/src/events/types.ts +172 -35
  155. package/src/ids/runtime-id.ts +6 -0
  156. package/src/ids/uuid-v7.ts +7 -0
  157. package/src/image/image-asset-store.ts +319 -0
  158. package/src/image/image-input-policy.ts +14 -0
  159. package/src/image/image-probe.ts +233 -0
  160. package/src/image/image-types.ts +262 -0
  161. package/src/instructions/project-instructions.ts +4 -1
  162. package/src/mcp/mcp-manager.ts +83 -19
  163. package/src/model/fake-model-client.ts +9 -1
  164. package/src/model/input-token-estimator.ts +25 -0
  165. package/src/model/model-client.ts +60 -0
  166. package/src/model/model-request-preflight.ts +1 -0
  167. package/src/model/moonshot-input-token-estimator.ts +107 -0
  168. package/src/model/openai-chat-mapping.ts +77 -1
  169. package/src/model/openai-chat-model-client.ts +389 -59
  170. package/src/model/openai-chat-stream.ts +272 -0
  171. package/src/model/prompt-prefix-hash.ts +2 -4
  172. package/src/model/token-estimator.ts +4 -0
  173. package/src/observation/observation-builder.ts +29 -0
  174. package/src/session/resume-projection.ts +94 -34
  175. package/src/session/session-catalog.ts +4 -4
  176. package/src/session/session-errors.ts +1 -1
  177. package/src/session/session-history-reader.ts +151 -4
  178. package/src/session/session-last-response-reader.ts +189 -0
  179. package/src/session/session-schema.ts +349 -35
  180. package/src/session/session-store.ts +3266 -255
  181. package/src/session/sqlite-session-ledger.ts +9 -4
  182. package/src/skills/skill-catalog.ts +182 -0
  183. package/src/skills/skill-context.ts +364 -0
  184. package/src/skills/skill-loader.ts +779 -0
  185. package/src/skills/skill-tool.ts +297 -0
  186. package/src/tools/edit.ts +51 -64
  187. package/src/tools/ensure-parent-directory.ts +6 -0
  188. package/src/tools/read.ts +2 -2
  189. package/src/tools/recall.ts +43 -40
  190. package/src/tools/registry.ts +24 -3
  191. package/src/tools/ripgrep.ts +3 -2
  192. package/src/tools/types.ts +43 -4
  193. package/src/tools/write.ts +20 -31
  194. package/src/tui/app.tsx +333 -96
  195. package/src/tui/clipboard.ts +5 -0
  196. package/src/tui/components/mcp-panel.tsx +28 -0
  197. package/src/tui/components/prompt-input.tsx +790 -257
  198. package/src/tui/components/skills-panel.tsx +31 -0
  199. package/src/tui/components/timeline.tsx +30 -1
  200. package/src/tui/event-store.ts +85 -2
  201. package/src/tui/file-mention.ts +234 -0
  202. package/src/tui/line-editor.ts +1 -0
  203. package/src/tui/project-slash-commands.ts +229 -0
  204. package/src/tui/prompt-draft.ts +510 -0
  205. package/src/tui/prompt-history.ts +294 -22
  206. package/src/tui/slash-commands.ts +46 -2
  207. package/src/tui/tui-session-controller.ts +110 -48
  208. package/src/tui/workspace-file-search.ts +94 -0
@@ -0,0 +1,229 @@
1
+ import { constants } from "node:fs";
2
+ import { lstat, open, realpath, stat } from "node:fs/promises";
3
+ import path from "node:path";
4
+ import { SLASH_COMMANDS, SlashCommandError, type SlashCommand } from "./slash-commands";
5
+
6
+ export const PROJECT_CONFIG_FILE_NAME = ".tinker.json";
7
+ export const PROJECT_CONFIG_MAX_BYTES = 1024 * 1024;
8
+
9
+ export type ProjectSlashCommand = SlashCommand & {
10
+ readonly prompt: string;
11
+ };
12
+
13
+ const COMMAND_NAME_PATTERN = /^[a-z0-9][a-z0-9-]{0,63}$/;
14
+ const BUILT_IN_NAMES = new Set(SLASH_COMMANDS.map((command) => command.name));
15
+
16
+ export async function loadProjectSlashCommands(
17
+ workspaceRoot: string,
18
+ ): Promise<readonly ProjectSlashCommand[]> {
19
+ const canonicalRoot = await realpath(workspaceRoot);
20
+ const configPath = path.join(canonicalRoot, PROJECT_CONFIG_FILE_NAME);
21
+ let handle;
22
+
23
+ try {
24
+ handle = await open(configPath, constants.O_RDONLY | constants.O_NONBLOCK);
25
+ } catch (error) {
26
+ if (!isErrno(error, "ENOENT")) {
27
+ throw loadError(configPath, error);
28
+ }
29
+ try {
30
+ await lstat(configPath);
31
+ } catch (lstatError) {
32
+ if (isErrno(lstatError, "ENOENT")) {
33
+ return [];
34
+ }
35
+ throw loadError(configPath, lstatError);
36
+ }
37
+ throw loadError(configPath, error);
38
+ }
39
+
40
+ try {
41
+ const targetPath = await realpath(configPath);
42
+ if (!isWithinWorkspace(canonicalRoot, targetPath)) {
43
+ throw new Error(`${PROJECT_CONFIG_FILE_NAME} resolves outside the workspace.`);
44
+ }
45
+
46
+ const stats = await handle.stat();
47
+ if (!stats.isFile()) {
48
+ throw new Error(`${PROJECT_CONFIG_FILE_NAME} must be a regular file.`);
49
+ }
50
+ const targetStats = await stat(targetPath);
51
+ if (targetStats.dev !== stats.dev || targetStats.ino !== stats.ino) {
52
+ throw new Error(`${PROJECT_CONFIG_FILE_NAME} changed while being opened.`);
53
+ }
54
+ if (stats.size > PROJECT_CONFIG_MAX_BYTES) {
55
+ throw tooLargeError(stats.size);
56
+ }
57
+
58
+ const bytes = await readBounded(handle, PROJECT_CONFIG_MAX_BYTES);
59
+ if (bytes.byteLength > PROJECT_CONFIG_MAX_BYTES) {
60
+ throw tooLargeError(bytes.byteLength);
61
+ }
62
+ if (bytes.includes(0)) {
63
+ throw new Error(`${PROJECT_CONFIG_FILE_NAME} contains a NUL byte.`);
64
+ }
65
+
66
+ let raw: string;
67
+ try {
68
+ raw = new TextDecoder("utf-8", { fatal: true, ignoreBOM: true }).decode(bytes);
69
+ } catch (error) {
70
+ throw new Error(`${PROJECT_CONFIG_FILE_NAME} is not valid UTF-8.`, {
71
+ cause: error,
72
+ });
73
+ }
74
+ return parseProjectSlashCommands(raw, configPath);
75
+ } catch (error) {
76
+ throw loadError(configPath, error);
77
+ } finally {
78
+ await handle.close().catch((error: unknown) => {
79
+ throw loadError(configPath, error);
80
+ });
81
+ }
82
+ }
83
+
84
+ export function parseProjectSlashCommands(
85
+ raw: string,
86
+ sourcePath: string,
87
+ ): readonly ProjectSlashCommand[] {
88
+ let value: unknown;
89
+ try {
90
+ value = JSON.parse(raw);
91
+ } catch (error) {
92
+ throw new Error(`Invalid JSON: ${errorMessage(error)}`, { cause: error });
93
+ }
94
+
95
+ const config = requireRecord(value, "configuration");
96
+ requireExactKeys(config, ["version", "slashCommands"], "configuration");
97
+ if (config.version !== 1) {
98
+ throw new Error(`${sourcePath}: "version" must be 1.`);
99
+ }
100
+ if (!Array.isArray(config.slashCommands)) {
101
+ throw new Error(`${sourcePath}: "slashCommands" must be an array.`);
102
+ }
103
+
104
+ const names = new Set<string>();
105
+ return Object.freeze(
106
+ config.slashCommands.map((entry, index) => {
107
+ const where = `${sourcePath}: slashCommands[${index}]`;
108
+ const command = requireRecord(entry, where);
109
+ requireExactKeys(command, ["name", "description", "prompt"], where);
110
+ const name = requireNonEmptyString(command.name, `${where}.name`);
111
+ if (!COMMAND_NAME_PATTERN.test(name)) {
112
+ throw new Error(`${where}.name must match ${COMMAND_NAME_PATTERN.toString()}.`);
113
+ }
114
+ if (BUILT_IN_NAMES.has(name)) {
115
+ throw new Error(`${where}.name conflicts with built-in command /${name}.`);
116
+ }
117
+ if (names.has(name)) {
118
+ throw new Error(`${where}.name duplicates /${name}.`);
119
+ }
120
+ names.add(name);
121
+
122
+ return Object.freeze({
123
+ name,
124
+ description: requireNonEmptyString(command.description, `${where}.description`),
125
+ prompt: requireNonEmptyString(command.prompt, `${where}.prompt`),
126
+ });
127
+ }),
128
+ );
129
+ }
130
+
131
+ export function resolveProjectSlashCommand(
132
+ input: string,
133
+ commands: readonly ProjectSlashCommand[],
134
+ ): ProjectSlashCommand | undefined {
135
+ const trimmed = input.trim();
136
+ const tokens = trimmed.split(/\s+/);
137
+ const name = tokens[0]?.startsWith("/") ? tokens[0].slice(1) : "";
138
+ const command = commands.find((candidate) => candidate.name === name);
139
+ if (command === undefined) {
140
+ return undefined;
141
+ }
142
+ if (tokens.length !== 1) {
143
+ throw new SlashCommandError(`Usage: /${command.name}`);
144
+ }
145
+ return command;
146
+ }
147
+
148
+ async function readBounded(
149
+ handle: Awaited<ReturnType<typeof open>>,
150
+ maxBytes: number,
151
+ ): Promise<Buffer> {
152
+ const chunks: Buffer[] = [];
153
+ let total = 0;
154
+ while (total <= maxBytes) {
155
+ const chunk = Buffer.allocUnsafe(Math.min(8192, maxBytes + 1 - total));
156
+ const { bytesRead } = await handle.read(chunk, 0, chunk.byteLength, null);
157
+ if (bytesRead === 0) {
158
+ break;
159
+ }
160
+ chunks.push(chunk.subarray(0, bytesRead));
161
+ total += bytesRead;
162
+ }
163
+ return Buffer.concat(chunks, total);
164
+ }
165
+
166
+ function requireRecord(value: unknown, where: string): Record<string, unknown> {
167
+ if (typeof value !== "object" || value === null || Array.isArray(value)) {
168
+ throw new Error(`${where} must be an object.`);
169
+ }
170
+ return value as Record<string, unknown>;
171
+ }
172
+
173
+ function requireExactKeys(
174
+ value: Record<string, unknown>,
175
+ expected: readonly string[],
176
+ where: string,
177
+ ): void {
178
+ const expectedKeys = new Set(expected);
179
+ const unknown = Object.keys(value).filter((key) => !expectedKeys.has(key));
180
+ if (unknown.length > 0) {
181
+ throw new Error(`${where} has unknown field ${JSON.stringify(unknown[0])}.`);
182
+ }
183
+ const missing = expected.find((key) => !(key in value));
184
+ if (missing !== undefined) {
185
+ throw new Error(`${where} is missing field ${JSON.stringify(missing)}.`);
186
+ }
187
+ }
188
+
189
+ function requireNonEmptyString(value: unknown, where: string): string {
190
+ if (typeof value !== "string" || value.trim() === "") {
191
+ throw new Error(`${where} must be a non-empty string.`);
192
+ }
193
+ return value;
194
+ }
195
+
196
+ function isWithinWorkspace(workspaceRoot: string, targetPath: string): boolean {
197
+ const relative = path.relative(workspaceRoot, targetPath);
198
+ return (
199
+ relative !== ".." &&
200
+ !relative.startsWith(`..${path.sep}`) &&
201
+ !path.isAbsolute(relative)
202
+ );
203
+ }
204
+
205
+ function tooLargeError(actual: number): Error {
206
+ return new Error(
207
+ `${PROJECT_CONFIG_FILE_NAME} is ${actual} bytes; the limit is ${PROJECT_CONFIG_MAX_BYTES} bytes.`,
208
+ );
209
+ }
210
+
211
+ function loadError(configPath: string, cause: unknown): Error {
212
+ return new Error(
213
+ `Failed to load project config ${configPath}: ${errorMessage(cause)}`,
214
+ { cause },
215
+ );
216
+ }
217
+
218
+ function isErrno(error: unknown, code: string): boolean {
219
+ return (
220
+ typeof error === "object" &&
221
+ error !== null &&
222
+ "code" in error &&
223
+ error.code === code
224
+ );
225
+ }
226
+
227
+ function errorMessage(error: unknown): string {
228
+ return error instanceof Error ? error.message : String(error);
229
+ }