@womp/spark-agent 3.0.3 → 4.0.1

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 (78) hide show
  1. package/README.md +59 -33
  2. package/dist/agent-run-cli.d.ts +2 -0
  3. package/dist/agent-run-cli.js +30 -0
  4. package/dist/agent-run-input.d.ts +11 -0
  5. package/dist/agent-run-input.js +59 -0
  6. package/dist/agent-run.d.ts +81 -0
  7. package/dist/agent-run.js +234 -0
  8. package/dist/agent-tools.js +3 -2
  9. package/dist/agent.d.ts +0 -2
  10. package/dist/agent.js +4 -36
  11. package/dist/capabilities/account.js +15 -15
  12. package/dist/capabilities/asset-imports.js +50 -50
  13. package/dist/capabilities/assets.js +10 -10
  14. package/dist/capabilities/flow.js +15 -15
  15. package/dist/capabilities/geometry.js +13 -14
  16. package/dist/capabilities/knowledge.js +4 -4
  17. package/dist/capabilities/openscad-instructions.d.ts +1 -1
  18. package/dist/capabilities/openscad-instructions.js +80 -80
  19. package/dist/capabilities/openscad.js +2 -2
  20. package/dist/capabilities/scene.d.ts +11 -3
  21. package/dist/capabilities/scene.js +93 -27
  22. package/dist/capabilities.d.ts +1 -2
  23. package/dist/capabilities.js +0 -1
  24. package/dist/client.d.ts +5 -0
  25. package/dist/client.js +22 -0
  26. package/dist/coordinates.d.ts +0 -7
  27. package/dist/coordinates.js +8 -47
  28. package/dist/generated/scene-declarations.d.ts +1 -1
  29. package/dist/generated/scene-declarations.js +1 -1
  30. package/dist/geometry/schemas/flowir-dialect.json +2 -2
  31. package/dist/index.d.ts +0 -1
  32. package/dist/index.js +0 -1
  33. package/dist/models.d.ts +7 -0
  34. package/dist/models.js +10 -0
  35. package/dist/prompt.d.ts +1 -1
  36. package/dist/prompt.js +53 -1
  37. package/dist/request-limits.d.ts +1 -0
  38. package/dist/request-limits.js +1 -0
  39. package/dist/run-metrics.d.ts +15 -0
  40. package/dist/run-metrics.js +30 -0
  41. package/dist/runtime.d.ts +3 -5
  42. package/dist/scene-metadata.js +1 -1
  43. package/dist/sdk-scene-host.d.ts +11 -13
  44. package/dist/sdk-scene-host.js +1 -0
  45. package/dist/sdk-scene-screenshot.d.ts +4 -6
  46. package/dist/sdk-scene-screenshot.js +14 -15
  47. package/dist/server/artifacts.js +1 -5
  48. package/dist/server/executor.d.ts +0 -1
  49. package/dist/server/executor.js +0 -1
  50. package/dist/server/express.js +17 -5
  51. package/dist/server/scene-actions.d.ts +2 -0
  52. package/dist/server/scene-actions.js +20 -0
  53. package/dist/server/service.d.ts +2 -0
  54. package/dist/server/service.js +10 -0
  55. package/dist/server/sqlite-store.d.ts +3 -1
  56. package/dist/server/sqlite-store.js +26 -3
  57. package/dist/server/standalone.d.ts +2 -0
  58. package/dist/server/standalone.js +54 -3
  59. package/dist/server/store.d.ts +3 -1
  60. package/dist/server/store.js +21 -1
  61. package/dist/server/types.d.ts +5 -0
  62. package/dist/simple-bench-cli.d.ts +2 -0
  63. package/dist/simple-bench-cli.js +64 -0
  64. package/dist/simple-bench-prompts.d.ts +28 -0
  65. package/dist/simple-bench-prompts.js +11 -0
  66. package/dist/simple-bench.d.ts +15 -0
  67. package/dist/simple-bench.js +175 -0
  68. package/dist/types.d.ts +4 -13
  69. package/dist/ui.d.ts +3 -8
  70. package/dist/womp-scene-api.d.ts +14 -3
  71. package/dist/womp-scene-api.js +98 -75
  72. package/package.json +7 -3
  73. package/dist/capabilities/health.d.ts +0 -9
  74. package/dist/capabilities/health.js +0 -27
  75. package/dist/context.d.ts +0 -15
  76. package/dist/context.js +0 -266
  77. package/dist/generated/health-declarations.d.ts +0 -1
  78. package/dist/generated/health-declarations.js +0 -3
package/dist/context.js DELETED
@@ -1,266 +0,0 @@
1
- export class SparkContextLimitError extends Error {
2
- constructor() {
3
- super("This conversation is too large to continue without losing required context. Completed work and full history are saved. Start a new conversation with the current target references and remaining task; inspect existing results before retrying any changes.");
4
- this.name = "SparkContextLimitError";
5
- }
6
- }
7
- function record(value) {
8
- return typeof value === "object" && value !== null && !Array.isArray(value)
9
- ? value
10
- : undefined;
11
- }
12
- /** Heuristic, not provider tokenization. Encoded media bytes are not text tokens. */
13
- export function estimateSparkContextTokens(value) {
14
- let mediaTokens = 0;
15
- const json = JSON.stringify(value, (_key, item) => {
16
- const part = record(item);
17
- if (part &&
18
- (part.type === "image" || part.type === "file" || part.type === "reasoning-file")) {
19
- const isImage = part.type === "image" ||
20
- (typeof part.mediaType === "string" && part.mediaType.startsWith("image/"));
21
- mediaTokens += isImage ? 512 : 8192;
22
- return { type: part.type, mediaType: part.mediaType };
23
- }
24
- return item;
25
- }) ?? "";
26
- return Math.ceil(new TextEncoder().encode(json).byteLength / 3) + mediaTokens;
27
- }
28
- function executionOutput(part) {
29
- if (part.toolName !== "execute_javascript" || part.output.type !== "content")
30
- return;
31
- const first = part.output.value[0];
32
- if (first?.type !== "text")
33
- return;
34
- try {
35
- const value = record(JSON.parse(first.text));
36
- if (value?.version === 2 && typeof value.ok === "boolean")
37
- return value;
38
- }
39
- catch {
40
- // Legacy and unrecognized results remain verbatim.
41
- }
42
- }
43
- function shortenSkills(messages, keepLatest) {
44
- let retained = 0;
45
- const result = [...messages];
46
- for (let index = result.length - 1; index >= 0; index--) {
47
- const message = result[index];
48
- if (message.role !== "tool")
49
- continue;
50
- result[index] = {
51
- ...message,
52
- content: message.content.map((part) => {
53
- if (part.type !== "tool-result" ||
54
- part.toolName !== "read_skill" ||
55
- part.output.type !== "json")
56
- return part;
57
- const skill = record(part.output.value);
58
- if (skill?.ok !== true ||
59
- typeof skill.name !== "string" ||
60
- (typeof skill.instructions !== "string" && typeof skill.declarations !== "string"))
61
- return part;
62
- if (retained++ < keepLatest)
63
- return part;
64
- const summary = {
65
- ok: true,
66
- name: skill.name,
67
- version: typeof skill.version === "string" ? skill.version : "",
68
- contextNote: "This older skill body was removed from model context. Call read_skill again before using its API; do not guess its declarations.",
69
- };
70
- if (JSON.stringify(summary).length >= JSON.stringify(skill).length)
71
- return part;
72
- return { ...part, output: { type: "json", value: summary } };
73
- }),
74
- };
75
- }
76
- return result;
77
- }
78
- /**
79
- * Project model input only. Never mutate persisted messages, remove call/result
80
- * pairs, summarize user requests, or discard receipts, references, and failures.
81
- */
82
- export function prepareSparkContext(messages, system, policy) {
83
- if (!Number.isSafeInteger(policy.maxInputTokens) || policy.maxInputTokens < 1)
84
- throw new Error("maxInputTokens must be a positive integer.");
85
- // Reserve room for the two tool schemas and provider message framing.
86
- const fits = (input) => estimateSparkContextTokens({ system, messages: input }) + 2048 <= policy.maxInputTokens;
87
- if (fits(messages))
88
- return messages;
89
- const seenSkills = new Map();
90
- const seenOutputs = new Map();
91
- const compactableCalls = new Set();
92
- const lastUser = messages.reduce((last, message, index) => (message.role === "user" ? index : last), -1);
93
- const prepared = messages.map((message) => ({ ...message }));
94
- for (let index = prepared.length - 1; index >= 0; index--) {
95
- const message = prepared[index];
96
- if (message.role !== "tool")
97
- continue;
98
- prepared[index] = {
99
- ...message,
100
- content: message.content.map((part) => {
101
- if (part.type !== "tool-result")
102
- return part;
103
- if (part.toolName === "read_skill" && part.output.type === "json") {
104
- const skill = record(part.output.value);
105
- if (skill?.ok !== true || typeof skill.name !== "string")
106
- return part;
107
- const key = JSON.stringify(skill);
108
- const later = seenSkills.get(key);
109
- if (later === undefined) {
110
- seenSkills.set(key, part.toolCallId);
111
- return part;
112
- }
113
- const summary = {
114
- ok: true,
115
- name: skill.name,
116
- version: typeof skill.version === "string" ? skill.version : "",
117
- contextNote: `Identical skill content is retained in tool result ${later}. Read the skill again if needed.`,
118
- };
119
- if (JSON.stringify(summary).length >= key.length)
120
- return part;
121
- return {
122
- ...part,
123
- output: { type: "json", value: summary },
124
- };
125
- }
126
- const output = executionOutput(part);
127
- if (!output || part.output.type !== "content")
128
- return part;
129
- const receipts = Array.isArray(output.receipts) ? output.receipts : [];
130
- const settled = output.ok === true &&
131
- receipts.every((receipt) => record(receipt)?.status === "committed");
132
- // Preserve code for unresolved work and for the current user turn.
133
- if (index < lastUser && settled && receipts.length > 0)
134
- compactableCalls.add(part.toolCallId);
135
- if (!settled)
136
- return part;
137
- const key = JSON.stringify({ value: output.value, logs: output.logs });
138
- const later = seenOutputs.get(key);
139
- if (later === undefined) {
140
- seenOutputs.set(key, part.toolCallId);
141
- return part;
142
- }
143
- const abbreviated = {
144
- ...output,
145
- value: null,
146
- logs: [`Identical returned value and logs are retained in tool result ${later}.`],
147
- contextNote: "Only duplicate observations were omitted. This call's own receipts and artifacts remain below.",
148
- };
149
- const text = JSON.stringify(abbreviated);
150
- if (text.length >= JSON.stringify(output).length)
151
- return part;
152
- return {
153
- ...part,
154
- output: {
155
- ...part.output,
156
- value: [{ type: "text", text }, ...part.output.value.slice(1)],
157
- },
158
- };
159
- }),
160
- };
161
- }
162
- if (fits(prepared))
163
- return prepared;
164
- // Prefer keeping the latest workflow and reference together while there is room.
165
- const withRecentSkills = shortenSkills(prepared, 2);
166
- if (fits(withRecentSkills))
167
- return withRecentSkills;
168
- const withoutOldCode = withRecentSkills.map((message) => {
169
- if (message.role !== "assistant" ||
170
- typeof message.content === "string" ||
171
- message.providerOptions)
172
- return message;
173
- if (message.content.some((part) => part.type === "reasoning" ||
174
- part.type === "reasoning-file" ||
175
- ("providerOptions" in part && part.providerOptions)))
176
- return message;
177
- return {
178
- ...message,
179
- content: message.content.map((part) => {
180
- // Signed/provider-owned tool inputs must remain byte-for-byte intact.
181
- if (part.type !== "tool-call" ||
182
- part.toolName !== "execute_javascript" ||
183
- part.providerOptions ||
184
- !compactableCalls.has(part.toolCallId))
185
- return part;
186
- const input = record(part.input);
187
- if (typeof input?.code !== "string")
188
- return part;
189
- const code = "// Historical code omitted from model context. This completed call must not be replayed; inspect its preserved result and receipts.";
190
- if (input.code.length <= code.length)
191
- return part;
192
- return {
193
- ...part,
194
- input: { ...input, code },
195
- };
196
- }),
197
- };
198
- });
199
- if (fits(withoutOldCode))
200
- return withoutOldCode;
201
- // Older visual observations can be recovered from their retained artifact references.
202
- // Keep the latest visual result and all user/provider-owned media verbatim.
203
- // SDK call metadata can also appear on local tool results; preserve that metadata
204
- // without treating the host's subsequently produced output as signed model content.
205
- let latestVisualCall;
206
- for (const message of withoutOldCode) {
207
- if (message.role !== "tool")
208
- continue;
209
- for (const part of message.content) {
210
- if (part.type !== "tool-result" || part.output.type !== "content")
211
- continue;
212
- if (part.output.value.some((item) => item.type === "file"))
213
- latestVisualCall = part.toolCallId;
214
- }
215
- }
216
- for (let index = 0; index < withoutOldCode.length; index++) {
217
- const message = withoutOldCode[index];
218
- if (message.role !== "tool" || message.providerOptions)
219
- continue;
220
- withoutOldCode[index] = {
221
- ...message,
222
- content: message.content.map((part) => {
223
- if (part.type !== "tool-result" ||
224
- part.toolCallId === latestVisualCall ||
225
- part.output.type !== "content")
226
- return part;
227
- const output = executionOutput(part);
228
- if (output?.ok !== true ||
229
- !Array.isArray(output.artifacts) ||
230
- output.artifacts.length === 0)
231
- return part;
232
- if (output.artifacts.some((item) => typeof record(record(item)?.artifact)?.id !== "string"))
233
- return part;
234
- const receipts = Array.isArray(output.receipts) ? output.receipts : [];
235
- if (receipts.some((receipt) => record(receipt)?.status !== "committed"))
236
- return part;
237
- if (!part.output.value.some((item) => item.type === "file"))
238
- return part;
239
- if (part.output.value.some((item) => "providerOptions" in item && item.providerOptions))
240
- return part;
241
- const abbreviated = {
242
- ...output,
243
- contextNote: "Older image/file bytes were omitted from model context. The original artifact references remain in this result and full stored history. Use emitImage or emitFile with those references when you need to inspect them again.",
244
- };
245
- return {
246
- ...part,
247
- output: {
248
- type: "content",
249
- value: [
250
- { type: "text", text: JSON.stringify(abbreviated) },
251
- ...part.output.value.slice(1).filter((item) => item.type !== "file"),
252
- ],
253
- },
254
- };
255
- }),
256
- };
257
- if (fits(withoutOldCode))
258
- return withoutOldCode;
259
- }
260
- // Documentation can be reread; keep current observations and receipts instead
261
- // of stopping solely to retain a second skill body.
262
- const withLatestSkill = shortenSkills(withoutOldCode, 1);
263
- if (fits(withLatestSkill))
264
- return withLatestSkill;
265
- throw new SparkContextLimitError();
266
- }
@@ -1 +0,0 @@
1
- export declare const WOMP_HEALTH_DECLARATIONS = "declare const womp: { health: {\n\"read\": ((objectIds: Array<number>) => Promise<{\n\"status\": \"within_limits\" | \"overflow\" | \"unverified\";\n\"freshness\": \"unverified\" | \"snapshot\" | \"post_ack_event\";\n\"revisionVerified\": false;\n\"affectedAreaIds\": Array<string>;\n\"emptyAreaIds\"?: undefined | Array<string>;\n\"thermometer\"?: undefined | {\n\"objects\": {\n[key: string]: {\n\"num_prims\": number;\n\"flag_prim_overflow\": number;\n\"flag_depth_overflow\": number;\n\"flag_vox_overflow\": number;\n\"voxels\": number;\n};\n};\n\"max_num_prims\": number;\n\"max_total_depth\": number;\n\"max_per_object\": number;\n\"total\": number;\n\"max_total\": number;\n\"assets_budget_used\": number;\n\"assets_budget_total\": number;\n\"mesh_instances_used\"?: undefined | number;\n\"mesh_instances_total\"?: undefined | number;\n\"decal_instances_used\"?: undefined | number;\n\"decal_instances_total\"?: undefined | number;\n\"field_instances_used\"?: undefined | number;\n\"field_instances_total\"?: undefined | number;\n};\n\"reason\"?: undefined | string;\n}>);\n} };\ndeclare function print(...values: unknown[]): void;\ndeclare function emitImage(ref: { id: string; mediaType: string; name?: string }): Promise<void>;\ndeclare function emitFile(ref: { id: string; mediaType: string; name?: string }): Promise<void>;\n";
@@ -1,3 +0,0 @@
1
- // Generated by scripts/generate-skill-declarations.mjs.
2
- // prettier-ignore
3
- export const WOMP_HEALTH_DECLARATIONS = "declare const womp: { health: {\n\"read\": ((objectIds: Array<number>) => Promise<{\n\"status\": \"within_limits\" | \"overflow\" | \"unverified\";\n\"freshness\": \"unverified\" | \"snapshot\" | \"post_ack_event\";\n\"revisionVerified\": false;\n\"affectedAreaIds\": Array<string>;\n\"emptyAreaIds\"?: undefined | Array<string>;\n\"thermometer\"?: undefined | {\n\"objects\": {\n[key: string]: {\n\"num_prims\": number;\n\"flag_prim_overflow\": number;\n\"flag_depth_overflow\": number;\n\"flag_vox_overflow\": number;\n\"voxels\": number;\n};\n};\n\"max_num_prims\": number;\n\"max_total_depth\": number;\n\"max_per_object\": number;\n\"total\": number;\n\"max_total\": number;\n\"assets_budget_used\": number;\n\"assets_budget_total\": number;\n\"mesh_instances_used\"?: undefined | number;\n\"mesh_instances_total\"?: undefined | number;\n\"decal_instances_used\"?: undefined | number;\n\"decal_instances_total\"?: undefined | number;\n\"field_instances_used\"?: undefined | number;\n\"field_instances_total\"?: undefined | number;\n};\n\"reason\"?: undefined | string;\n}>);\n} };\ndeclare function print(...values: unknown[]): void;\ndeclare function emitImage(ref: { id: string; mediaType: string; name?: string }): Promise<void>;\ndeclare function emitFile(ref: { id: string; mediaType: string; name?: string }): Promise<void>;\n";