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,329 @@
1
+ import type { ProjectInstructionManifest } from "../instructions/project-instructions";
2
+ import type { ContextSurfaceId, SessionId } from "../ids/runtime-id";
3
+ import type { PreparedModelRequest } from "../model/model-client";
4
+ import { sha256, stableJsonStringify } from "../model/model-request-preflight";
5
+ import type { ToolDefinition } from "../tools/types";
6
+ import {
7
+ activeSkillManifestSha256,
8
+ skillManifestSha256,
9
+ type ActiveSkillManifestEntry,
10
+ type SkillCatalogManifestEntry,
11
+ } from "../skills/skill-catalog";
12
+ import { immutableCanonicalClone } from "./protocol-frame";
13
+ import {
14
+ CURRENT_RECALL_RETIREMENT_CONTRACT_VERSION,
15
+ isSupportedRecallRetirementContractVersion,
16
+ type RecallRetirementContractVersion,
17
+ } from "./recall-retirement-contract";
18
+
19
+ export type ContextSurfaceComponent =
20
+ | "system_prompt"
21
+ | "project_instruction"
22
+ | "skill_catalog"
23
+ | "active_skills"
24
+ | "tool_definitions"
25
+ | "request_config";
26
+
27
+ export type ContextSurfaceChanges = {
28
+ readonly systemPrompt: boolean;
29
+ readonly projectInstruction: boolean;
30
+ readonly skillCatalog: boolean;
31
+ readonly activeSkills: boolean;
32
+ readonly toolDefinitions: boolean;
33
+ readonly requestConfig: boolean;
34
+ };
35
+
36
+ export type StoredContextSurfaceV8 = {
37
+ readonly surfaceId: ContextSurfaceId;
38
+ readonly sessionId: SessionId;
39
+ readonly systemPrompt: string;
40
+ readonly systemPromptSha256: string;
41
+ readonly recallContractVersion: RecallRetirementContractVersion;
42
+ readonly projectInstruction?: ProjectInstructionManifest;
43
+ readonly skillCatalog: readonly SkillCatalogManifestEntry[];
44
+ readonly skillCatalogSha256: string;
45
+ readonly activeSkills: readonly ActiveSkillManifestEntry[];
46
+ readonly activeSkillsSha256: string;
47
+ readonly toolDefinitions: readonly ToolDefinition[];
48
+ readonly toolDefinitionsSha256: string;
49
+ readonly toolSchemaSha256: string;
50
+ readonly requestConfigSha256: string;
51
+ readonly requestMaxOutputTokens: number;
52
+ readonly surfaceSha256: string;
53
+ readonly createdAt: string;
54
+ };
55
+
56
+ export function createContextSurface(input: {
57
+ surfaceId: ContextSurfaceId;
58
+ sessionId: SessionId;
59
+ systemPrompt: string;
60
+ recallContractVersion: RecallRetirementContractVersion;
61
+ projectInstruction?: ProjectInstructionManifest;
62
+ skillCatalog?: readonly SkillCatalogManifestEntry[];
63
+ activeSkills?: readonly ActiveSkillManifestEntry[];
64
+ toolDefinitions: readonly ToolDefinition[];
65
+ prepared: Pick<
66
+ PreparedModelRequest,
67
+ "requestConfigHash" | "requestMaxOutputTokens" | "toolSchemaHash"
68
+ >;
69
+ createdAt: string;
70
+ }): StoredContextSurfaceV8 {
71
+ if (input.surfaceId.trim() === "" || input.sessionId.trim() === "") {
72
+ throw new Error("Context surface identity must not be empty.");
73
+ }
74
+ if (input.systemPrompt.trim() === "") {
75
+ throw new Error("Context surface system prompt must not be empty.");
76
+ }
77
+ if (input.recallContractVersion !== CURRENT_RECALL_RETIREMENT_CONTRACT_VERSION) {
78
+ throw new Error("New context surfaces must use the current Recall contract.");
79
+ }
80
+ if (
81
+ !Number.isSafeInteger(input.prepared.requestMaxOutputTokens) ||
82
+ input.prepared.requestMaxOutputTokens < 1
83
+ ) {
84
+ throw new Error("Context surface output token limit must be positive.");
85
+ }
86
+ assertSha256(input.prepared.requestConfigHash, "requestConfigHash");
87
+ assertSha256(input.prepared.toolSchemaHash, "toolSchemaHash");
88
+ validateProjectInstruction(input.projectInstruction);
89
+ validateToolDefinitions(input.toolDefinitions);
90
+
91
+ const skillCatalog = Object.freeze(
92
+ immutableCanonicalClone([...(input.skillCatalog ?? [])]),
93
+ );
94
+ const activeSkills = Object.freeze(
95
+ immutableCanonicalClone([...(input.activeSkills ?? [])]),
96
+ );
97
+ validateSkillManifests(skillCatalog, activeSkills);
98
+
99
+ const toolDefinitions = Object.freeze(
100
+ immutableCanonicalClone([...input.toolDefinitions]),
101
+ );
102
+ const projectInstruction =
103
+ input.projectInstruction === undefined
104
+ ? undefined
105
+ : Object.freeze(immutableCanonicalClone(input.projectInstruction));
106
+ const systemPromptSha256 = sha256(input.systemPrompt);
107
+ const toolDefinitionsSha256 = sha256(stableJsonStringify(toolDefinitions));
108
+ const skillCatalogSha256 = skillManifestSha256(skillCatalog);
109
+ const activeSkillsSha256 = activeSkillManifestSha256(activeSkills);
110
+ const manifest = {
111
+ systemPromptSha256,
112
+ recallContractVersion: input.recallContractVersion,
113
+ ...(projectInstruction === undefined ? {} : { projectInstruction }),
114
+ skillCatalogSha256,
115
+ activeSkillsSha256,
116
+ toolDefinitionsSha256,
117
+ toolSchemaSha256: input.prepared.toolSchemaHash,
118
+ requestConfigSha256: input.prepared.requestConfigHash,
119
+ requestMaxOutputTokens: input.prepared.requestMaxOutputTokens,
120
+ };
121
+
122
+ return Object.freeze({
123
+ surfaceId: input.surfaceId,
124
+ sessionId: input.sessionId,
125
+ systemPrompt: input.systemPrompt,
126
+ systemPromptSha256,
127
+ recallContractVersion: input.recallContractVersion,
128
+ ...(projectInstruction === undefined ? {} : { projectInstruction }),
129
+ skillCatalog,
130
+ skillCatalogSha256,
131
+ activeSkills,
132
+ activeSkillsSha256,
133
+ toolDefinitions,
134
+ toolDefinitionsSha256,
135
+ toolSchemaSha256: input.prepared.toolSchemaHash,
136
+ requestConfigSha256: input.prepared.requestConfigHash,
137
+ requestMaxOutputTokens: input.prepared.requestMaxOutputTokens,
138
+ surfaceSha256: sha256(stableJsonStringify(manifest)),
139
+ createdAt: input.createdAt,
140
+ });
141
+ }
142
+
143
+ export function contextSurfaceChanges(
144
+ previous: StoredContextSurfaceV8,
145
+ current: StoredContextSurfaceV8,
146
+ ): ContextSurfaceChanges {
147
+ return Object.freeze({
148
+ systemPrompt:
149
+ previous.systemPromptSha256 !== current.systemPromptSha256 ||
150
+ previous.recallContractVersion !== current.recallContractVersion,
151
+ projectInstruction:
152
+ stableJsonStringify(previous.projectInstruction ?? null) !==
153
+ stableJsonStringify(current.projectInstruction ?? null),
154
+ skillCatalog: previous.skillCatalogSha256 !== current.skillCatalogSha256,
155
+ activeSkills: previous.activeSkillsSha256 !== current.activeSkillsSha256,
156
+ toolDefinitions:
157
+ previous.toolDefinitionsSha256 !== current.toolDefinitionsSha256 ||
158
+ previous.toolSchemaSha256 !== current.toolSchemaSha256,
159
+ requestConfig:
160
+ previous.requestConfigSha256 !== current.requestConfigSha256 ||
161
+ previous.requestMaxOutputTokens !== current.requestMaxOutputTokens,
162
+ });
163
+ }
164
+
165
+ export function changedContextSurfaceComponents(
166
+ changes: ContextSurfaceChanges,
167
+ ): readonly ContextSurfaceComponent[] {
168
+ const changed: ContextSurfaceComponent[] = [];
169
+ if (changes.systemPrompt) changed.push("system_prompt");
170
+ if (changes.projectInstruction) changed.push("project_instruction");
171
+ if (changes.skillCatalog) changed.push("skill_catalog");
172
+ if (changes.activeSkills) changed.push("active_skills");
173
+ if (changes.toolDefinitions) changed.push("tool_definitions");
174
+ if (changes.requestConfig) changed.push("request_config");
175
+ return Object.freeze(changed);
176
+ }
177
+
178
+ export function contextSurfaceChangeManifestHash(
179
+ changes: ContextSurfaceChanges,
180
+ ): string {
181
+ return sha256(stableJsonStringify(changes));
182
+ }
183
+
184
+ export function sameContextSurface(
185
+ previous: StoredContextSurfaceV8,
186
+ current: StoredContextSurfaceV8,
187
+ ): boolean {
188
+ return previous.surfaceSha256 === current.surfaceSha256;
189
+ }
190
+
191
+ export function validateStoredContextSurface(surface: StoredContextSurfaceV8): void {
192
+ if (surface.systemPromptSha256 !== sha256(surface.systemPrompt)) {
193
+ throw new Error("Context surface system prompt hash does not match.");
194
+ }
195
+ if (!isSupportedRecallRetirementContractVersion(surface.recallContractVersion)) {
196
+ throw new Error("Context surface Recall contract version is unsupported.");
197
+ }
198
+ validateToolDefinitions(surface.toolDefinitions);
199
+ validateSkillManifests(surface.skillCatalog, surface.activeSkills);
200
+ if (surface.skillCatalogSha256 !== skillManifestSha256(surface.skillCatalog)) {
201
+ throw new Error("Context surface Agent Skill catalog hash does not match.");
202
+ }
203
+ if (surface.activeSkillsSha256 !== activeSkillManifestSha256(surface.activeSkills)) {
204
+ throw new Error("Context surface active Agent Skills hash does not match.");
205
+ }
206
+ if (
207
+ surface.toolDefinitionsSha256 !==
208
+ sha256(stableJsonStringify(surface.toolDefinitions))
209
+ ) {
210
+ throw new Error("Context surface tool definitions hash does not match.");
211
+ }
212
+ for (const [name, value] of [
213
+ ["toolSchemaSha256", surface.toolSchemaSha256],
214
+ ["requestConfigSha256", surface.requestConfigSha256],
215
+ ["surfaceSha256", surface.surfaceSha256],
216
+ ] as const) {
217
+ assertSha256(value, name);
218
+ }
219
+ const expectedManifest = {
220
+ systemPromptSha256: surface.systemPromptSha256,
221
+ recallContractVersion: surface.recallContractVersion,
222
+ ...(surface.projectInstruction === undefined
223
+ ? {}
224
+ : { projectInstruction: surface.projectInstruction }),
225
+ skillCatalogSha256: surface.skillCatalogSha256,
226
+ activeSkillsSha256: surface.activeSkillsSha256,
227
+ toolDefinitionsSha256: surface.toolDefinitionsSha256,
228
+ toolSchemaSha256: surface.toolSchemaSha256,
229
+ requestConfigSha256: surface.requestConfigSha256,
230
+ requestMaxOutputTokens: surface.requestMaxOutputTokens,
231
+ };
232
+ if (sha256(stableJsonStringify(expectedManifest)) !== surface.surfaceSha256) {
233
+ throw new Error("Context surface manifest hash does not match.");
234
+ }
235
+ }
236
+
237
+ function validateSkillManifests(
238
+ catalog: readonly SkillCatalogManifestEntry[],
239
+ active: readonly ActiveSkillManifestEntry[],
240
+ ): void {
241
+ const catalogNames = new Set<string>();
242
+ let previousName: string | undefined;
243
+ for (const entry of catalog) {
244
+ validateSkillManifestEntry(entry);
245
+ if (
246
+ catalogNames.has(entry.name) ||
247
+ (previousName !== undefined && previousName >= entry.name)
248
+ ) {
249
+ throw new Error("Context surface Agent Skill catalog order is invalid.");
250
+ }
251
+ catalogNames.add(entry.name);
252
+ previousName = entry.name;
253
+ }
254
+ const activeNames = new Set<string>();
255
+ previousName = undefined;
256
+ for (const entry of active) {
257
+ validateSkillManifestEntry(entry);
258
+ const catalogEntry = catalog.find((candidate) => candidate.name === entry.name);
259
+ if (
260
+ catalogEntry === undefined ||
261
+ activeNames.has(entry.name) ||
262
+ (previousName !== undefined && previousName >= entry.name) ||
263
+ entry.activationMessageId.trim() === "" ||
264
+ stableJsonStringify(catalogEntry) !==
265
+ stableJsonStringify({
266
+ name: entry.name,
267
+ scope: entry.scope,
268
+ directorySha256: entry.directorySha256,
269
+ descriptionSha256: entry.descriptionSha256,
270
+ skillFileSha256: entry.skillFileSha256,
271
+ byteLength: entry.byteLength,
272
+ })
273
+ ) {
274
+ throw new Error("Context surface active Agent Skills manifest is invalid.");
275
+ }
276
+ activeNames.add(entry.name);
277
+ previousName = entry.name;
278
+ }
279
+ }
280
+
281
+ function validateSkillManifestEntry(entry: SkillCatalogManifestEntry): void {
282
+ if (
283
+ entry.name.trim() === "" ||
284
+ (entry.scope !== "project" && entry.scope !== "user") ||
285
+ !Number.isSafeInteger(entry.byteLength) ||
286
+ entry.byteLength < 1
287
+ ) {
288
+ throw new Error("Context surface Agent Skill manifest entry is invalid.");
289
+ }
290
+ assertSha256(entry.directorySha256, "skill.directorySha256");
291
+ assertSha256(entry.descriptionSha256, "skill.descriptionSha256");
292
+ assertSha256(entry.skillFileSha256, "skill.skillFileSha256");
293
+ }
294
+
295
+ function validateToolDefinitions(definitions: readonly ToolDefinition[]): void {
296
+ const names = new Set<string>();
297
+ for (const definition of definitions) {
298
+ if (definition.name.trim() === "") {
299
+ throw new Error("Context surface tool name must not be empty.");
300
+ }
301
+ if (names.has(definition.name)) {
302
+ throw new Error(`Context surface contains duplicate tool ${definition.name}.`);
303
+ }
304
+ names.add(definition.name);
305
+ stableJsonStringify(definition);
306
+ }
307
+ }
308
+
309
+ function validateProjectInstruction(
310
+ instruction: ProjectInstructionManifest | undefined,
311
+ ): void {
312
+ if (instruction === undefined) {
313
+ return;
314
+ }
315
+ if (
316
+ (instruction.path !== "AGENTS.md" && instruction.path !== "CLAUDE.md") ||
317
+ !Number.isSafeInteger(instruction.byteLength) ||
318
+ instruction.byteLength < 1
319
+ ) {
320
+ throw new Error("Context surface project instruction manifest is invalid.");
321
+ }
322
+ assertSha256(instruction.sha256, "projectInstruction.sha256");
323
+ }
324
+
325
+ function assertSha256(value: string, name: string): void {
326
+ if (!/^[0-9a-f]{64}$/.test(value)) {
327
+ throw new Error(`Context surface ${name} must be a SHA-256 hash.`);
328
+ }
329
+ }