@womp/spark-agent 5.12.0 → 6.0.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.
package/README.md CHANGED
@@ -88,6 +88,8 @@ judges the reconstruction sufficient; insufficient credits or a blocker stop it.
88
88
  Reaching the cap completes the pass without a run error. Keep refining
89
89
  asks for a new pass budget, then continues in the same conversation with the
90
90
  same reference and detail level. The budget travels as structured goal data.
91
+ `wompifyPrompt` writes the reference name prefix into the request message, which is
92
+ also the goal objective, so every pass names the same reference.
91
93
 
92
94
  Ordinary goals support `{ action: "update_budget", goalId, creditBudget }` on
93
95
  the goal endpoint. This edits the total credit cap without resetting usage,
@@ -95,6 +97,14 @@ restarting the run, or adding a model message. Crow reads the current cap before
95
97
  worker and evaluator calls; an in-flight call may finish. Raising an exhausted
96
98
  cap leaves the goal paused until Resume. Wompify's tier caps cannot be edited.
97
99
 
100
+ ## System prompt and editor selection
101
+
102
+ Every host sends `sparkSystemPrompt(skills)`: the shared agent prompt and the skills
103
+ directory, with nothing host-specific, so it stays identical across a conversation.
104
+ Hosts attach the editor selection to the user message it was sent with as a
105
+ `data-selection` part (`{ nodeIds }`). It is not rendered as chat text; the model reads
106
+ it as a short note on that message.
107
+
98
108
  ## Package entry points
99
109
 
100
110
  - `@womp/spark-agent` — browser-safe model loop, two tools, persistence hooks, and shared contracts
@@ -348,7 +358,8 @@ Prompt caching is enabled for every listed model: Claude Opus 5 and Fable 5 use
348
358
  automatic 5-minute caching, and GPT-6 Astra uses implicit caching with a 30-minute TTL.
349
359
  The providers manage the cache as message history grows across tool steps; no local
350
360
  response cache is used. Cache hits require matching prefixes, sufficient prompt length,
351
- and an unexpired entry. Cache writes can cost more than uncached input, while subsequent
361
+ and an unexpired entry. Goal state stays out of the system prompt: objectives,
362
+ goal rules and evaluator feedback arrive in appended goal messages. Cache writes can cost more than uncached input, while subsequent
352
363
  cache reads are cheaper. The UI displays actual reported reads/writes, not assumed savings.
353
364
  See [Claude caching](https://platform.claude.com/docs/en/build-with-claude/prompt-caching)
354
365
  and [OpenAI caching](https://developers.openai.com/api/docs/guides/prompt-caching).
package/dist/agent.d.ts CHANGED
@@ -22,7 +22,6 @@ export type SparkAgentOptions = {
22
22
  runId: string;
23
23
  messageId: string;
24
24
  signal?: AbortSignal;
25
- context?: string;
26
25
  instructions?: string;
27
26
  maxSteps?: number;
28
27
  maxOutputTokens?: number;
package/dist/agent.js CHANGED
@@ -2,7 +2,7 @@ import { sparkErrorDiagnostics } from "./error-diagnostics.js";
2
2
  import { WompifyCreditCapReachedError } from "./wompify.js";
3
3
  import { createUIMessageStream, stepCountIs, streamText, } from "ai";
4
4
  import { createSparkTools } from "./agent-tools.js";
5
- import { SPARK_AGENT_PROMPT } from "./prompt.js";
5
+ import { sparkSystemPrompt } from "./prompt.js";
6
6
  import { calculateStepCost } from "./pricing.js";
7
7
  import { contextWindowFor, fitMessagesToContext } from "./context-budget.js";
8
8
  function errorMessage(error) {
@@ -124,14 +124,7 @@ export function runSparkAgent(options) {
124
124
  controller,
125
125
  write: (chunk) => writer.write(chunk),
126
126
  });
127
- const readSystem = () => [
128
- options.instructions ?? SPARK_AGENT_PROMPT,
129
- options.context,
130
- "Available skills (read before using their APIs):",
131
- ...options.capabilities.skills.map(({ name, version, description }) => `- ${name} (${version}): ${description}`),
132
- ]
133
- .filter(Boolean)
134
- .join("\n\n");
127
+ const readSystem = () => sparkSystemPrompt(options.capabilities.skills, options.instructions);
135
128
  const generation = streamText({
136
129
  model: options.model,
137
130
  system: readSystem(),
package/dist/index.d.ts CHANGED
@@ -8,5 +8,5 @@ export { recoverSceneOperation, type SceneOperation, type SceneOperationStore }
8
8
  export type { JavaScriptExecutor, JavaScriptGlobals, JavaScriptUserWait } from "./executor.js";
9
9
  export { DEFAULT_MODEL_ID, getSparkModel, REASONING_EFFORT_LABELS, resolveReasoningEffort, SPARK_MODELS, } from "./models.js";
10
10
  export type { ModelProvider, ReasoningEffort, SparkModel } from "./models.js";
11
- export { SPARK_AGENT_PROMPT } from "./prompt.js";
11
+ export { SPARK_AGENT_PROMPT, sparkSystemPrompt } from "./prompt.js";
12
12
  export type { ExecutionResult, ExecutionScreenshot, SparkRuntimeEvent, SparkRuntimeEventSink, SparkContextMessage, SparkImage, SparkRunInput, SparkRuntime, } from "./types.js";
package/dist/index.js CHANGED
@@ -6,4 +6,4 @@ export * from "./capabilities.js";
6
6
  export { createSdkSceneEditor, createSdkSceneHost, } from "./sdk-scene-host.js";
7
7
  export { recoverSceneOperation } from "./scene-operations.js";
8
8
  export { DEFAULT_MODEL_ID, getSparkModel, REASONING_EFFORT_LABELS, resolveReasoningEffort, SPARK_MODELS, } from "./models.js";
9
- export { SPARK_AGENT_PROMPT } from "./prompt.js";
9
+ export { SPARK_AGENT_PROMPT, sparkSystemPrompt } from "./prompt.js";
@@ -1,5 +1,5 @@
1
1
  import { type ModelMessage } from "ai";
2
2
  import type { createSparkTools } from "./agent-tools.js";
3
- import type { SparkUIMessage } from "./ui.js";
3
+ import { type SparkUIMessage } from "./ui.js";
4
4
  /** UI artifact previews already represented by a tool result are not model output. */
5
5
  export declare function convertSparkMessages(messages: SparkUIMessage[], tools: ReturnType<typeof createSparkTools>): Promise<ModelMessage[]>;
@@ -1,4 +1,5 @@
1
1
  import { convertToModelMessages } from "ai";
2
+ import { sparkDataPartSchemas } from "./ui.js";
2
3
  function record(value) {
3
4
  return typeof value === "object" && value !== null && !Array.isArray(value)
4
5
  ? value
@@ -30,5 +31,15 @@ export function convertSparkMessages(messages, tools) {
30
31
  parts: message.parts.filter((part) => part.type !== "file" || (!previews.has(part.url) && part.providerMetadata?.spark?.inspect !== false)),
31
32
  };
32
33
  });
33
- return convertToModelMessages(history, { tools });
34
+ return convertToModelMessages(history, {
35
+ tools,
36
+ convertDataPart: (part) => {
37
+ if (part.type !== "data-selection")
38
+ return undefined;
39
+ const selection = sparkDataPartSchemas.selection.safeParse(part.data);
40
+ return selection.success
41
+ ? { type: "text", text: `[Scene objects selected when this message was sent (node IDs): ${selection.data.nodeIds.join(", ")}]` }
42
+ : undefined;
43
+ },
44
+ });
34
45
  }
package/dist/prompt.d.ts CHANGED
@@ -1 +1,8 @@
1
- export declare const SPARK_AGENT_PROMPT = "You are Spark, Womp's unified assistant.\nUse read_skill({names:[...]}) to load the required APIs together. Skills provide TypeScript\ndeclarations for documentation; execute_javascript runs JavaScript, with top-level await.\nOnly the listed capabilities are available. Reading a skill does not grant additional permissions.\nUse only the supplied womp APIs and output helpers to access application services and state.\nIf a capability is unavailable, explain what is missing and ask the user to enable it when applicable.\nDo not work around missing capabilities by inspecting page globals, DOM or framework internals,\nor making direct network requests. Native execution is not a security sandbox; it does not expand\nthe supported API or the user's granted capabilities.\nGive each execute_javascript call a concise present-progressive purpose, such as \"Generating a car image\", \"Inspecting the scene\", or \"Importing the model\". Write purpose before code.\nEach execution has fresh local variables. Keep stable object, job, and artifact references.\nUse print for concise, filtered observations; await emitImage or emitFile with a host-issued artifact\nreference to show actual output. emitImage inspects by default; use { inspect: false } to display without adding image bytes to model context.\nCompleted generations are shown automatically to the user; image bytes are intentionally omitted from your context. Acknowledge completion without listing, re-emitting, or inspecting outputs unless the task requires it. Use returned artifact references for follow-up work.\nDo not claim visual verification without inspecting the image. Missing artifact references must be recovered or reported; omitted image bytes alone are not a missing result.\nInspect the scene before editing and verify the committed result.\nPreserve existing content unless the user asks to replace or delete it. Use the latest target revision\nwhen a write requires one. Capabilities own transactions: an entire script is not atomic.\nIf an operation fails after earlier effects, use its receipts to inspect and retry only unresolved work.\nNever claim an uncertain commit rolled back. Never resubmit a job just because waiting timed out.\nComplete related work in this conversation without delegating to another agent. Report limits,\nunsupported actions, cancellation, and incomplete work truthfully. You cannot see what your work\ncosts: model use can be billed, not only generations. Never estimate credits used or call work free;\nreport only credit figures a host message or tool provides. Finish with a concise explanation\nof the result in the user's language; keep implementation details out of that explanation.\n\nCreation intent\n\nResolve the requested outcome from the current message and preceding dialogue, not keyword matches.\nExplicit method choices take precedence. Continue edits in the existing representation unless the user\nasks to change it. Creation defaults do not apply to help, discussion, imports, or account questions.\n\n- Build directly with editable scene shapes for ordinary 3D requests, including \"3D X\" and\n \"make X in 3D\", and when the user wants individually editable shapes or geometry that blends\n with existing objects. Organic appearance alone does not require Geometry Nodes.\n\n- Use OpenSCAD for parametric, measured, mechanical, printable, or real-world-fit objects,\n except when another method is explicit (including primitives with dimensions), or when\n parametric organic/procedural geometry, animation, or Geometry Nodes is requested:\n use Geometry Nodes for those, not Workbench Flow. Do not claim print readiness without validation.\n\n- For mesh wompification, read mesh-wompify and scene-editing together and reconstruct only the specified reference using primitives and curves.\n\n- Use mesh generation when the user explicitly requests a mesh, asks to turn an existing or\n attached image into 3D, or chooses a sculpted result rather than individually editable shapes.\n\n- Use image generation for pictures, logos, concept art, wallpapers, and other clearly visual output.\n\n- For an ambiguous creative request without explicit 3D or printing intent, generate one image\n preview first. Then offer one short, outcome-based choice: refine the image, turn it into a\n sculpted 3D piece, or build it from individually editable shapes. Offer adjustable CAD instead\n of editable shapes when the subject is measured or must fit something real.\n\n- Also preview first when a 3D request without a named method depends on likeness or surface\n detail that shapes cannot carry (characters, creatures, people, food, sculptures, specific\n vehicles or branded items). Plain functional objects still build directly.\n\n- Generating a result does not authorize importing, converting, or building it in the scene.\n\n- Use Workbench Flow for image/mesh generation explicitly requested through Flow/nodes or\n continued edits to that Flow workflow, unless the user asks for an inline result. Flow must be\n open; explain that prerequisite if closed instead of silently generating inline. Merely opening\n Flow does not redirect ordinary scene requests or inline generation to it.\n\n- After a preview, offer the choice above. After other completed generation, you may briefly\n suggest a relevant next step using available capabilities. Describe its outcome, not internal tools. Suggestions are optional,\n not a mandatory menu, and never authorize actions. Wait for a clear choice and follow existing\n confirmations. Reuse the existing artifact/reference for the chosen follow-up, including as the\n image-to-mesh input, instead of regenerating the source. Preserve its defining features when building it.\n\n- Choose construction methods by the user's requested workflow and the\n available capabilities, not by assuming editable shapes require lower\n fidelity. Outside the preview defaults above, a sculpted appearance does not by itself\n require mesh generation.\n Respect an explicit method choice and pursue the reference's visible shape\n and detail with that method. Offer alternatives only for a specific\n demonstrated limitation; ask before switching or simplifying the requested\n result. No method guarantees exact reconstruction from a single image.\n\n- Ask at most one focused question at a time, only when a sensible default\n would likely produce the wrong outcome. Do not repeat resolved questions.\n Interpret replies using the preceding offer; if several options were\n offered and \u201Cyes\u201D does not identify one, clarify briefly.\n\n- Treat a clear answer to your question as an instruction to proceed.\n Continue from existing results rather than restarting the workflow.\n\nOffer only workflows available in this host. Follow their authorization\nand confirmation requirements before generation or scene changes. If a requested workflow is unavailable,\nexplain the limitation rather than silently substituting another.";
1
+ import type { SparkSkill } from "./capabilities.js";
2
+ export declare const SPARK_AGENT_PROMPT = "You are Spark, Womp's unified assistant.\nUse read_skill({names:[...]}) to load the required APIs together. Skills provide TypeScript\ndeclarations for documentation; execute_javascript runs JavaScript, with top-level await.\nOnly the listed capabilities are available. Reading a skill does not grant additional permissions.\nUse only the supplied womp APIs and output helpers to access application services and state.\nIf a capability is unavailable, explain what is missing and ask the user to enable it when applicable.\nDo not work around missing capabilities by inspecting page globals, DOM or framework internals,\nor making direct network requests. Native execution is not a security sandbox; it does not expand\nthe supported API or the user's granted capabilities.\nGive each execute_javascript call a concise present-progressive purpose, such as \"Generating a car image\", \"Inspecting the scene\", or \"Importing the model\". Write purpose before code.\nEach execution has fresh local variables. Keep stable object, job, and artifact references.\nUse print for concise, filtered observations; await emitImage or emitFile with a host-issued artifact\nreference to show actual output. emitImage inspects by default; use { inspect: false } to display without adding image bytes to model context.\nCompleted generations are shown automatically to the user; image bytes are intentionally omitted from your context. Acknowledge completion without listing, re-emitting, or inspecting outputs unless the task requires it. Use returned artifact references for follow-up work.\nDo not claim visual verification without inspecting the image. Missing artifact references must be recovered or reported; omitted image bytes alone are not a missing result.\nInspect the scene before editing and verify the committed result.\nPreserve existing content unless the user asks to replace or delete it. Use the latest target revision\nwhen a write requires one. Capabilities own transactions: an entire script is not atomic.\nRecover ordinary tool errors yourself: use confirmed receipts and fresh reads, retry only unresolved work,\npreserve successful changes, and never replay an interrupted script. Do not narrate internal failures\nunless recovery cannot continue. Never claim an uncertain commit rolled back. Never resubmit a job just because waiting timed out.\nComplete related work in this conversation without delegating to another agent. Report limits,\nunsupported actions, cancellation, and incomplete work truthfully. You cannot see what your work\ncosts: model use can be billed, not only generations. Never estimate credits used or call work free;\nreport only credit figures a host message or tool provides. Finish with a concise explanation\nof the result in the user's language; keep implementation details out of that explanation.\n\nCreation intent\n\nResolve the requested outcome from the current message and preceding dialogue, not keyword matches.\nExplicit method choices take precedence. Continue edits in the existing representation unless the user\nasks to change it. Creation defaults do not apply to help, discussion, imports, or account questions.\n\n- Build directly with editable scene shapes for ordinary 3D requests, including \"3D X\" and\n \"make X in 3D\", and when the user wants individually editable shapes or geometry that blends\n with existing objects. Organic appearance alone does not require Geometry Nodes.\n\n- Use OpenSCAD for parametric, measured, mechanical, printable, or real-world-fit objects,\n except when another method is explicit (including primitives with dimensions), or when\n parametric organic/procedural geometry, animation, or Geometry Nodes is requested:\n use Geometry Nodes for those, not Workbench Flow. Do not claim print readiness without validation.\n\n- For mesh wompification, read mesh-wompify and scene-editing together and reconstruct only the specified reference using primitives and curves.\n\n- Use mesh generation when the user explicitly requests a mesh, asks to turn an existing or\n attached image into 3D, or chooses a sculpted result rather than individually editable shapes.\n\n- Use image generation for pictures, logos, concept art, wallpapers, and other clearly visual output.\n\n- For an ambiguous creative request without explicit 3D or printing intent, generate one image\n preview first. Then offer one short, outcome-based choice: refine the image, turn it into a\n sculpted 3D piece, or build it from individually editable shapes. Offer adjustable CAD instead\n of editable shapes when the subject is measured or must fit something real.\n\n- Also preview first when a 3D request without a named method depends on likeness or surface\n detail that shapes cannot carry (characters, creatures, people, food, sculptures, specific\n vehicles or branded items). Plain functional objects still build directly.\n\n- Generating a result does not authorize importing, converting, or building it in the scene.\n\n- Use Workbench Flow for image/mesh generation explicitly requested through Flow/nodes or\n continued edits to that Flow workflow, unless the user asks for an inline result. Flow must be\n open; explain that prerequisite if closed instead of silently generating inline. Merely opening\n Flow does not redirect ordinary scene requests or inline generation to it.\n\n- After a preview, offer the choice above. After other completed generation, you may briefly\n suggest a relevant next step using available capabilities. Describe its outcome, not internal tools. Suggestions are optional,\n not a mandatory menu, and never authorize actions. Wait for a clear choice and follow existing\n confirmations. Reuse the existing artifact/reference for the chosen follow-up, including as the\n image-to-mesh input, instead of regenerating the source. Preserve its defining features when building it.\n\n- Choose construction methods by the user's requested workflow and the\n available capabilities, not by assuming editable shapes require lower\n fidelity. Outside the preview defaults above, a sculpted appearance does not by itself\n require mesh generation.\n Respect an explicit method choice and pursue the reference's visible shape\n and detail with that method. Offer alternatives only for a specific\n demonstrated limitation; ask before switching or simplifying the requested\n result. No method guarantees exact reconstruction from a single image.\n\n- Ask at most one focused question at a time, only when a sensible default\n would likely produce the wrong outcome. Do not repeat resolved questions.\n Interpret replies using the preceding offer; if several options were\n offered and \u201Cyes\u201D does not identify one, clarify briefly.\n\n- Treat a clear answer to your question as an instruction to proceed.\n Continue from existing results rather than restarting the workflow.\n\nOffer only workflows available in this host. Follow their authorization\nand confirmation requirements before generation or scene changes. If a requested workflow is unavailable,\nexplain the limitation rather than silently substituting another.";
3
+ /**
4
+ * The system prompt every host sends: the agent prompt and the skills directory.
5
+ * It must stay identical across the turns and steps of a conversation, or the
6
+ * provider's prompt cache misses; per-turn state travels in messages instead.
7
+ */
8
+ export declare function sparkSystemPrompt(skills: readonly Pick<SparkSkill, "name" | "version" | "description">[], instructions?: string): string;
package/dist/prompt.js CHANGED
@@ -16,8 +16,9 @@ Do not claim visual verification without inspecting the image. Missing artifact
16
16
  Inspect the scene before editing and verify the committed result.
17
17
  Preserve existing content unless the user asks to replace or delete it. Use the latest target revision
18
18
  when a write requires one. Capabilities own transactions: an entire script is not atomic.
19
- If an operation fails after earlier effects, use its receipts to inspect and retry only unresolved work.
20
- Never claim an uncertain commit rolled back. Never resubmit a job just because waiting timed out.
19
+ Recover ordinary tool errors yourself: use confirmed receipts and fresh reads, retry only unresolved work,
20
+ preserve successful changes, and never replay an interrupted script. Do not narrate internal failures
21
+ unless recovery cannot continue. Never claim an uncertain commit rolled back. Never resubmit a job just because waiting timed out.
21
22
  Complete related work in this conversation without delegating to another agent. Report limits,
22
23
  unsupported actions, cancellation, and incomplete work truthfully. You cannot see what your work
23
24
  costs: model use can be billed, not only generations. Never estimate credits used or call work free;
@@ -88,3 +89,15 @@ asks to change it. Creation defaults do not apply to help, discussion, imports,
88
89
  Offer only workflows available in this host. Follow their authorization
89
90
  and confirmation requirements before generation or scene changes. If a requested workflow is unavailable,
90
91
  explain the limitation rather than silently substituting another.`;
92
+ /**
93
+ * The system prompt every host sends: the agent prompt and the skills directory.
94
+ * It must stay identical across the turns and steps of a conversation, or the
95
+ * provider's prompt cache misses; per-turn state travels in messages instead.
96
+ */
97
+ export function sparkSystemPrompt(skills, instructions = SPARK_AGENT_PROMPT) {
98
+ return [
99
+ instructions,
100
+ "Available skills (read before using their APIs):",
101
+ ...skills.map(({ name, version, description }) => `- ${name} (${version}): ${description}`),
102
+ ].join("\n\n");
103
+ }
@@ -9,7 +9,7 @@ export type ResolvedSparkModel = {
9
9
  };
10
10
  export type SparkRunExecutorOptions = {
11
11
  resolveModel(settings: SparkRunSettings): ResolvedSparkModel | Promise<ResolvedSparkModel>;
12
- createHost(context: SparkRunExecutionContext): Promise<Pick<SparkAgentOptions, "capabilities" | "executor" | "artifacts" | "persistence" | "context"> & {
12
+ createHost(context: SparkRunExecutionContext): Promise<Pick<SparkAgentOptions, "capabilities" | "executor" | "artifacts" | "persistence"> & {
13
13
  dispose(): void;
14
14
  }>;
15
15
  };
@@ -23,7 +23,6 @@ export function createSparkRunExecutor(options) {
23
23
  runId: context.runId,
24
24
  messageId: context.assistantMessageId,
25
25
  signal: context.signal,
26
- context: [host.context, context.goal ? `Active goal: ${JSON.stringify(context.goal.objective)}\nKeep working toward this objective and verify it with concrete tool results before claiming completion. Do not ask whether to continue. If you need user input or access, explain exactly what is missing. Latest evaluation: ${context.goal.lastReason ?? "Not evaluated yet."}` : undefined].filter(Boolean).join("\n\n"),
27
26
  });
28
27
  return { stream: run.stream, result: run.result.finally(() => host.dispose()) };
29
28
  }
@@ -57,7 +57,8 @@ function normalizeUserMessage(message) {
57
57
  throw new SparkServiceError(400, "A new chat message must have the user role.");
58
58
  }
59
59
  for (const part of message.parts) {
60
- if (part.type === "text")
60
+ // The data schemas are checked with the rest of the message (validateUIMessages).
61
+ if (part.type === "text" || part.type === "data-selection")
61
62
  continue;
62
63
  if (part.type === "file" &&
63
64
  part.mediaType.startsWith("image/") &&
@@ -76,12 +77,20 @@ function normalizeUserMessage(message) {
76
77
  },
77
78
  };
78
79
  }
79
- /** Budget accounting stays in the host; the worker receives the task and progress. */
80
+ const GOAL_WORK_RULES = "Keep working toward this objective and verify it with concrete tool results before claiming completion. Do not ask whether to continue. If you need user input or access, explain exactly what is missing.";
81
+ /**
82
+ * Budget accounting stays in the host; the worker receives the task and progress.
83
+ * Goal state travels in these appended messages, not the system prompt, so each
84
+ * turn reuses the provider's cached prefix.
85
+ */
80
86
  function goalContinuationText(goal) {
81
- return `Continue working toward the goal: ${goal.objective}\nProgress check: ${goal.lastReason}`
87
+ return `Continue working toward the goal: ${goal.objective}\n${GOAL_WORK_RULES}\nProgress check: ${goal.lastReason}`
82
88
  + (wompifyContextSchema.safeParse(goal.settings.wompify).success
83
89
  ? "\nKeep refining the existing reconstruction at the same detail level. Compare it with the reference and improve the remaining shape and color discrepancies." : "");
84
90
  }
91
+ function goalStartText(goal, action) {
92
+ return `${action === "set" ? "Goal" : "Resume the goal"}: ${goal.objective}\n${GOAL_WORK_RULES}`;
93
+ }
85
94
  export class SparkAgentService {
86
95
  store;
87
96
  executeRun;
@@ -222,7 +231,7 @@ export class SparkAgentService {
222
231
  } : { ...current, settings: runSettings, status: "active", updatedAt: now(),
223
232
  idleTurns: 0, };
224
233
  const inputMessage = normalizeUserMessage(message ?? {
225
- id: crypto.randomUUID(), role: "user", parts: [{ type: "text", text: command.action === "set" ? goal.objective : `Resume the goal: ${goal.objective}` }],
234
+ id: crypto.randomUUID(), role: "user", parts: [{ type: "text", text: goalStartText(goal, command.action) }],
226
235
  });
227
236
  await validateUIMessages({ messages: [inputMessage], metadataSchema: sparkMessageMetadataSchema, dataSchemas: sparkDataPartSchemas });
228
237
  await this.store.saveConversationGoal(conversationId, goal);
package/dist/ui.d.ts CHANGED
@@ -89,6 +89,11 @@ export type SparkDataParts = {
89
89
  toolCallId: string;
90
90
  receipt: ExecutionReceipt;
91
91
  };
92
+ selection: SparkSelectionData;
93
+ };
94
+ /** Editor node IDs selected when a user message was sent; never rendered as chat text. */
95
+ export type SparkSelectionData = {
96
+ nodeIds: string[];
92
97
  };
93
98
  export type ExecuteJavaScriptInput = {
94
99
  purpose: string;
@@ -321,6 +326,13 @@ export declare const sparkDataPartSchemas: {
321
326
  }, z.core.$strip>>;
322
327
  usage: z.ZodOptional<z.ZodUnknown>;
323
328
  }, z.core.$strip>;
329
+ /**
330
+ * The editor selection when a user message was sent. Hosts attach it to that
331
+ * message and do not render it; the model reads it as text (model-history.ts).
332
+ */
333
+ readonly selection: z.ZodObject<{
334
+ nodeIds: z.ZodArray<z.ZodString>;
335
+ }, z.core.$strip>;
324
336
  };
325
- export { parseWompifyCommand, WOMPIFY_PRESETS, wompifyPrompt, wompifyContext, wompifyContextSchema, type WompifyRequest, type WompifyContext } from "./wompify.js";
337
+ export { parseWompifyCommand, WOMPIFY_PRESETS, wompifyPrompt, wompifyContextSchema, type WompifyRequest, type WompifyContext } from "./wompify.js";
326
338
  export { parseSparkGoalCommand, DEFAULT_GOAL_CREDITS } from "./goals.js";
package/dist/ui.js CHANGED
@@ -79,6 +79,12 @@ export const sparkDataPartSchemas = {
79
79
  cost: sparkStepCostSchema.optional(),
80
80
  usage: z.unknown().optional(),
81
81
  }),
82
+ /**
83
+ * The editor selection when a user message was sent. Hosts attach it to that
84
+ * message and do not render it; the model reads it as text (model-history.ts).
85
+ */
86
+ // Editor ids are the engine's decimal integers; 8192 covers a select-all at the engine's budgets.
87
+ selection: z.object({ nodeIds: z.array(z.string().min(1).max(64)).min(1).max(8192) }),
82
88
  };
83
- export { parseWompifyCommand, WOMPIFY_PRESETS, wompifyPrompt, wompifyContext, wompifyContextSchema } from "./wompify.js";
89
+ export { parseWompifyCommand, WOMPIFY_PRESETS, wompifyPrompt, wompifyContextSchema } from "./wompify.js";
84
90
  export { parseSparkGoalCommand, DEFAULT_GOAL_CREDITS } from "./goals.js";
package/dist/wompify.d.ts CHANGED
@@ -29,8 +29,11 @@ export declare const wompifyContextSchema: z.ZodObject<{
29
29
  }>;
30
30
  }, z.core.$strip>;
31
31
  export type WompifyContext = z.infer<typeof wompifyContextSchema>;
32
- export declare function wompifyContext(context?: WompifyContext): string | undefined;
33
- export declare function wompifyPrompt(preset: WompifyRequest["preset"], referenceName: string, note?: string): string;
32
+ /** The Wompify request message, which is also the goal objective every continuation repeats. */
33
+ export declare function wompifyPrompt(request: WompifyContext & {
34
+ referenceName: string;
35
+ note?: string;
36
+ }): string;
34
37
  export declare function parseWompifyCommand(text: string): {
35
38
  preset: WompifyRequest["preset"];
36
39
  note: string;
package/dist/wompify.js CHANGED
@@ -15,13 +15,12 @@ export const wompifyContextSchema = z.object({
15
15
  referenceNamePrefix: z.string().trim().min(1).max(200),
16
16
  preset: z.enum(["simple", "high", "ultra"]),
17
17
  });
18
- export function wompifyContext(context) {
19
- if (!context)
20
- return undefined;
21
- return `Use the mesh-wompify skill for reference name prefix ${JSON.stringify(context.referenceNamePrefix)} at tier ${WOMPIFY_PRESETS[context.preset].label}. Stop if that skill is unavailable.`;
22
- }
23
- export function wompifyPrompt(preset, referenceName, note = "") {
24
- return `Wompify ${JSON.stringify(referenceName)} at tier ${WOMPIFY_PRESETS[preset].label}.${note.trim() ? `\n${note.trim()}` : ""}`;
18
+ /** The Wompify request message, which is also the goal objective every continuation repeats. */
19
+ export function wompifyPrompt(request) {
20
+ const note = request.note?.trim();
21
+ return `Wompify ${JSON.stringify(request.referenceName)} at tier ${WOMPIFY_PRESETS[request.preset].label}.`
22
+ + `\nUse the mesh-wompify skill for reference name prefix ${JSON.stringify(request.referenceNamePrefix)}. Stop if that skill is unavailable.`
23
+ + (note ? `\n${note}` : "");
25
24
  }
26
25
  export function parseWompifyCommand(text) {
27
26
  const match = /^\/wompify (simple|high|ultra)(?:\n([\s\S]*))?$/u.exec(text.trim());
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@womp/spark-agent",
3
- "version": "5.12.0",
3
+ "version": "6.0.0",
4
4
  "description": "Portable skills-based AI agent with local and durable chat transports for Womp",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",