@womp/spark-agent 5.8.0 → 5.9.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.
- package/dist/context-budget.d.ts +9 -0
- package/dist/context-budget.js +54 -6
- package/dist/prompt.d.ts +1 -1
- package/dist/prompt.js +3 -1
- package/dist/server/service.js +16 -1
- package/package.json +1 -1
package/dist/context-budget.d.ts
CHANGED
|
@@ -12,6 +12,15 @@ export declare const TRIM_TRIGGER_RATIO = 0.9;
|
|
|
12
12
|
export declare const TRIM_TARGET_RATIO = 0.75;
|
|
13
13
|
/** A tool result at or below this is left whole: not worth the detail it loses. */
|
|
14
14
|
export declare const SMALL_TOOL_OUTPUT_TOKENS = 500;
|
|
15
|
+
/**
|
|
16
|
+
* Steps of the CURRENT turn kept whole, however long that turn runs.
|
|
17
|
+
*
|
|
18
|
+
* The model is mid-task and reasons from what it just did, so the recent steps
|
|
19
|
+
* have to survive; the ones before them are history like any other and are worth
|
|
20
|
+
* more as window than as detail. Five is small enough that a long run always
|
|
21
|
+
* recovers room and large enough to hold a read-edit-verify cycle.
|
|
22
|
+
*/
|
|
23
|
+
export declare const PROTECTED_CURRENT_STEPS = 5;
|
|
15
24
|
export declare const OMITTED_HISTORY_TEXT = "[Earlier messages in this conversation were left out of this request to fit the model's context window.]";
|
|
16
25
|
export declare const OMITTED_HISTORY_IMAGE_TEXT = "[An earlier image was left out of this request to fit the model's context window.]";
|
|
17
26
|
export declare const OMITTED_TOOL_OUTPUT_TEXT = "[This earlier tool result was left out of this request to fit the model's context window.]";
|
package/dist/context-budget.js
CHANGED
|
@@ -22,9 +22,22 @@
|
|
|
22
22
|
// still paired with its result. A note in their place tells the model that
|
|
23
23
|
// earlier messages were left out.
|
|
24
24
|
//
|
|
25
|
-
//
|
|
26
|
-
//
|
|
27
|
-
//
|
|
25
|
+
// THE CURRENT TURN is the latest user message and everything after it. The
|
|
26
|
+
// message itself -- the question and any image the user just attached -- is never
|
|
27
|
+
// changed, and neither are the last PROTECTED_CURRENT_STEPS steps after it: the
|
|
28
|
+
// model has to be able to see what it just did.
|
|
29
|
+
//
|
|
30
|
+
// Its EARLIER steps are trimmed like any other history, by stages 1 and 2 above.
|
|
31
|
+
// They were exempt until 2026-10-01, on the assumption that a turn is small. An
|
|
32
|
+
// agentic run's turn is not: every step it takes lands after that user message,
|
|
33
|
+
// and `maxSteps` is 100. dev0 thread 142417 reached the window that way -- ten
|
|
34
|
+
// minutes of work, earlier history already trimmed to nothing, and what was left
|
|
35
|
+
// over the limit was the run's own tool results, which nothing was allowed to
|
|
36
|
+
// touch. Stage 3 still stops at the current turn, because dropping whole messages
|
|
37
|
+
// inside it cannot keep calls paired with their results.
|
|
38
|
+
//
|
|
39
|
+
// When the protected part alone is over the window the request still goes out as
|
|
40
|
+
// it is, and the provider's own error says so.
|
|
28
41
|
//
|
|
29
42
|
// ESTIMATES, NOT COUNTS. Counting needs the provider's tokenizer or a request.
|
|
30
43
|
// The estimate is deliberately high (CHARS_PER_TOKEN is under the ~3.5-4 of prose)
|
|
@@ -50,6 +63,15 @@ export const TRIM_TRIGGER_RATIO = 0.9;
|
|
|
50
63
|
export const TRIM_TARGET_RATIO = 0.75;
|
|
51
64
|
/** A tool result at or below this is left whole: not worth the detail it loses. */
|
|
52
65
|
export const SMALL_TOOL_OUTPUT_TOKENS = 500;
|
|
66
|
+
/**
|
|
67
|
+
* Steps of the CURRENT turn kept whole, however long that turn runs.
|
|
68
|
+
*
|
|
69
|
+
* The model is mid-task and reasons from what it just did, so the recent steps
|
|
70
|
+
* have to survive; the ones before them are history like any other and are worth
|
|
71
|
+
* more as window than as detail. Five is small enough that a long run always
|
|
72
|
+
* recovers room and large enough to hold a read-edit-verify cycle.
|
|
73
|
+
*/
|
|
74
|
+
export const PROTECTED_CURRENT_STEPS = 5;
|
|
53
75
|
export const OMITTED_HISTORY_TEXT = "[Earlier messages in this conversation were left out of this request to fit the model's context window.]";
|
|
54
76
|
export const OMITTED_HISTORY_IMAGE_TEXT = "[An earlier image was left out of this request to fit the model's context window.]";
|
|
55
77
|
export const OMITTED_TOOL_OUTPUT_TEXT = "[This earlier tool result was left out of this request to fit the model's context window.]";
|
|
@@ -76,6 +98,25 @@ export function contextWindowFor(modelId) {
|
|
|
76
98
|
return 1_048_576;
|
|
77
99
|
return undefined;
|
|
78
100
|
}
|
|
101
|
+
/**
|
|
102
|
+
* Where the current turn's protected tail begins: the Nth-from-last step.
|
|
103
|
+
*
|
|
104
|
+
* Counted by ASSISTANT messages, because that is what starts a step -- the tool
|
|
105
|
+
* messages that answer it follow and are protected with it, so no call is ever
|
|
106
|
+
* separated from its result. A turn with fewer steps than that protects all of
|
|
107
|
+
* them, which is what this did for every turn before it could trim one.
|
|
108
|
+
*/
|
|
109
|
+
function protectedTailStart(messages, current) {
|
|
110
|
+
let steps = 0;
|
|
111
|
+
for (let i = messages.length - 1; i > current; i -= 1) {
|
|
112
|
+
if (messages[i]?.role !== "assistant")
|
|
113
|
+
continue;
|
|
114
|
+
steps += 1;
|
|
115
|
+
if (steps >= PROTECTED_CURRENT_STEPS)
|
|
116
|
+
return i;
|
|
117
|
+
}
|
|
118
|
+
return current + 1;
|
|
119
|
+
}
|
|
79
120
|
export function fitMessagesToContext(messages, options) {
|
|
80
121
|
if (options.contextWindow === undefined)
|
|
81
122
|
return { messages };
|
|
@@ -96,10 +137,15 @@ export function fitMessagesToContext(messages, options) {
|
|
|
96
137
|
};
|
|
97
138
|
if (originalTokens <= budgetTokens * TRIM_TRIGGER_RATIO)
|
|
98
139
|
return { messages, report };
|
|
99
|
-
// The current turn starts at the latest user message
|
|
140
|
+
// The current turn starts at the latest user message. With no user message at
|
|
141
|
+
// all there is no turn to reason about and nothing is touched.
|
|
100
142
|
const current = lastIndex(messages, ({ role }) => role === "user");
|
|
101
|
-
if (current
|
|
143
|
+
if (current < 0)
|
|
102
144
|
return { messages, report };
|
|
145
|
+
// Stages 1 and 2 rewrite everything before this: all earlier turns, and the
|
|
146
|
+
// current turn's own earlier steps. `current` itself is skipped -- the user's
|
|
147
|
+
// question and the image they just attached are not history.
|
|
148
|
+
const protectedFrom = protectedTailStart(messages, current);
|
|
103
149
|
const target = budgetTokens * TRIM_TARGET_RATIO;
|
|
104
150
|
const out = messages.slice();
|
|
105
151
|
let total = originalTokens;
|
|
@@ -110,7 +156,9 @@ export function fitMessagesToContext(messages, options) {
|
|
|
110
156
|
* message, which is then measured again.
|
|
111
157
|
*/
|
|
112
158
|
const rewriteEarlier = (rewrite) => {
|
|
113
|
-
for (let i = 0; i <
|
|
159
|
+
for (let i = 0; i < protectedFrom && total > target; i += 1) {
|
|
160
|
+
if (i === current)
|
|
161
|
+
continue;
|
|
114
162
|
const message = out[i];
|
|
115
163
|
if (message === undefined || !Array.isArray(message.content))
|
|
116
164
|
continue;
|
package/dist/prompt.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
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. 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 ordinary or underspecified creations directly with editable scene shapes, including\n \"a car\" and \"make X in 3D\". Proceed without an image preview or method-selection menu.\n Organic appearance alone does not require mesh generation or 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- Generate/edit images or generate meshes when requested. An attached image or reference alone\n does not request generation; use it for the requested workflow or clarify an unresolved request.\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 completing explicitly requested generation, you may briefly suggest a relevant next step\n 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. A sculpted appearance does not by itself 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
|
+
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 ordinary or underspecified creations directly with editable scene shapes, including\n \"a car\" and \"make X in 3D\". Proceed without an image preview or method-selection menu.\n Organic appearance alone does not require mesh generation or 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- Generate/edit images or generate meshes when requested. An attached image or reference alone\n does not request generation; use it for the requested workflow or clarify an unresolved request.\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 completing explicitly requested generation, you may briefly suggest a relevant next step\n 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. A sculpted appearance does not by itself 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.";
|
package/dist/prompt.js
CHANGED
|
@@ -19,7 +19,9 @@ when a write requires one. Capabilities own transactions: an entire script is no
|
|
|
19
19
|
If an operation fails after earlier effects, use its receipts to inspect and retry only unresolved work.
|
|
20
20
|
Never claim an uncertain commit rolled back. Never resubmit a job just because waiting timed out.
|
|
21
21
|
Complete related work in this conversation without delegating to another agent. Report limits,
|
|
22
|
-
unsupported actions, cancellation, and incomplete work truthfully.
|
|
22
|
+
unsupported actions, cancellation, and incomplete work truthfully. You cannot see what your work
|
|
23
|
+
costs: model use can be billed, not only generations. Never estimate credits used or call work free;
|
|
24
|
+
report only credit figures a host message or tool provides. Finish with a concise explanation
|
|
23
25
|
of the result in the user's language; keep implementation details out of that explanation.
|
|
24
26
|
|
|
25
27
|
Creation intent
|
package/dist/server/service.js
CHANGED
|
@@ -75,6 +75,21 @@ function normalizeUserMessage(message) {
|
|
|
75
75
|
},
|
|
76
76
|
};
|
|
77
77
|
}
|
|
78
|
+
/** Credits as the host charged them, to the cent, without trailing zeros. */
|
|
79
|
+
const formatCredits = (credits) => String(Math.round(credits * 100) / 100);
|
|
80
|
+
/**
|
|
81
|
+
* What a goal's continuation turn is told. A credit-capped goal carries the credits
|
|
82
|
+
* the host has charged before this turn (just refreshed by the caller), so the model
|
|
83
|
+
* can report a real figure instead of guessing what its own work cost. Labelled as
|
|
84
|
+
* "before this turn": the turn's own usage is not in it, and a model reporting it
|
|
85
|
+
* as the goal's total at the end of the turn would under-report.
|
|
86
|
+
*/
|
|
87
|
+
function goalContinuationText(goal) {
|
|
88
|
+
const credits = goal.creditBudget === undefined
|
|
89
|
+
? ""
|
|
90
|
+
: `\nCredits used before this turn: ${formatCredits(goal.creditsUsed ?? 0)} of ${formatCredits(goal.creditBudget)}`;
|
|
91
|
+
return `Continue working toward the goal: ${goal.objective}${credits}\nProgress check: ${goal.lastReason}`;
|
|
92
|
+
}
|
|
78
93
|
export class SparkAgentService {
|
|
79
94
|
store;
|
|
80
95
|
executeRun;
|
|
@@ -623,7 +638,7 @@ export class SparkAgentService {
|
|
|
623
638
|
await this.submitInTurn({
|
|
624
639
|
ownerId: conversation.ownerId, conversationId: conversation.id,
|
|
625
640
|
trigger: "submit-message", settings: this.continuationSettings?.(next.settings) ?? next.settings,
|
|
626
|
-
message: { id: crypto.randomUUID(), role: "user", parts: [{ type: "text", text:
|
|
641
|
+
message: { id: crypto.randomUUID(), role: "user", parts: [{ type: "text", text: goalContinuationText(next) }] },
|
|
627
642
|
}, true);
|
|
628
643
|
}
|
|
629
644
|
catch (error) {
|