@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.
- package/README.md +59 -33
- package/dist/agent-run-cli.d.ts +2 -0
- package/dist/agent-run-cli.js +30 -0
- package/dist/agent-run-input.d.ts +11 -0
- package/dist/agent-run-input.js +59 -0
- package/dist/agent-run.d.ts +81 -0
- package/dist/agent-run.js +234 -0
- package/dist/agent-tools.js +3 -2
- package/dist/agent.d.ts +0 -2
- package/dist/agent.js +4 -36
- package/dist/capabilities/account.js +15 -15
- package/dist/capabilities/asset-imports.js +50 -50
- package/dist/capabilities/assets.js +10 -10
- package/dist/capabilities/flow.js +15 -15
- package/dist/capabilities/geometry.js +13 -14
- package/dist/capabilities/knowledge.js +4 -4
- package/dist/capabilities/openscad-instructions.d.ts +1 -1
- package/dist/capabilities/openscad-instructions.js +80 -80
- package/dist/capabilities/openscad.js +2 -2
- package/dist/capabilities/scene.d.ts +11 -3
- package/dist/capabilities/scene.js +93 -27
- package/dist/capabilities.d.ts +1 -2
- package/dist/capabilities.js +0 -1
- package/dist/client.d.ts +5 -0
- package/dist/client.js +22 -0
- package/dist/coordinates.d.ts +0 -7
- package/dist/coordinates.js +8 -47
- package/dist/generated/scene-declarations.d.ts +1 -1
- package/dist/generated/scene-declarations.js +1 -1
- package/dist/geometry/schemas/flowir-dialect.json +2 -2
- package/dist/index.d.ts +0 -1
- package/dist/index.js +0 -1
- package/dist/models.d.ts +7 -0
- package/dist/models.js +10 -0
- package/dist/prompt.d.ts +1 -1
- package/dist/prompt.js +53 -1
- package/dist/request-limits.d.ts +1 -0
- package/dist/request-limits.js +1 -0
- package/dist/run-metrics.d.ts +15 -0
- package/dist/run-metrics.js +30 -0
- package/dist/runtime.d.ts +3 -5
- package/dist/scene-metadata.js +1 -1
- package/dist/sdk-scene-host.d.ts +11 -13
- package/dist/sdk-scene-host.js +1 -0
- package/dist/sdk-scene-screenshot.d.ts +4 -6
- package/dist/sdk-scene-screenshot.js +14 -15
- package/dist/server/artifacts.js +1 -5
- package/dist/server/executor.d.ts +0 -1
- package/dist/server/executor.js +0 -1
- package/dist/server/express.js +17 -5
- package/dist/server/scene-actions.d.ts +2 -0
- package/dist/server/scene-actions.js +20 -0
- package/dist/server/service.d.ts +2 -0
- package/dist/server/service.js +10 -0
- package/dist/server/sqlite-store.d.ts +3 -1
- package/dist/server/sqlite-store.js +26 -3
- package/dist/server/standalone.d.ts +2 -0
- package/dist/server/standalone.js +54 -3
- package/dist/server/store.d.ts +3 -1
- package/dist/server/store.js +21 -1
- package/dist/server/types.d.ts +5 -0
- package/dist/simple-bench-cli.d.ts +2 -0
- package/dist/simple-bench-cli.js +64 -0
- package/dist/simple-bench-prompts.d.ts +28 -0
- package/dist/simple-bench-prompts.js +11 -0
- package/dist/simple-bench.d.ts +15 -0
- package/dist/simple-bench.js +175 -0
- package/dist/types.d.ts +4 -13
- package/dist/ui.d.ts +3 -8
- package/dist/womp-scene-api.d.ts +14 -3
- package/dist/womp-scene-api.js +98 -75
- package/package.json +7 -3
- package/dist/capabilities/health.d.ts +0 -9
- package/dist/capabilities/health.js +0 -27
- package/dist/context.d.ts +0 -15
- package/dist/context.js +0 -266
- package/dist/generated/health-declarations.d.ts +0 -1
- package/dist/generated/health-declarations.js +0 -3
package/dist/index.js
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
export { runSparkAgent, } from "./agent.js";
|
|
2
2
|
export { createSparkTools } from "./agent-tools.js";
|
|
3
3
|
export { convertSparkMessages } from "./model-history.js";
|
|
4
|
-
export { prepareSparkContext, estimateSparkContextTokens, SparkContextLimitError, } from "./context.js";
|
|
5
4
|
export * from "./capabilities.js";
|
|
6
5
|
export { createSdkSceneEditor, createSdkSceneHost, } from "./sdk-scene-host.js";
|
|
7
6
|
export { recoverSceneOperation } from "./scene-operations.js";
|
package/dist/models.d.ts
CHANGED
|
@@ -7,33 +7,40 @@ export type SparkModel = {
|
|
|
7
7
|
note: string;
|
|
8
8
|
provider: ModelProvider;
|
|
9
9
|
reasoningEfforts: readonly ReasoningEffort[];
|
|
10
|
+
/** Published capacity for display only; providers enforce their own limits. */
|
|
11
|
+
contextWindow?: number;
|
|
10
12
|
};
|
|
11
13
|
export declare const SPARK_MODELS: readonly [{
|
|
12
14
|
readonly id: "gpt-6-astra";
|
|
15
|
+
readonly contextWindow: 1050000;
|
|
13
16
|
readonly label: "GPT-6 Astra";
|
|
14
17
|
readonly note: "Best OpenAI quality";
|
|
15
18
|
readonly provider: "openai";
|
|
16
19
|
readonly reasoningEfforts: readonly ["low", "medium", "high", "xhigh", "max"];
|
|
17
20
|
}, {
|
|
18
21
|
readonly id: "gpt-5.6-luna";
|
|
22
|
+
readonly contextWindow: 1050000;
|
|
19
23
|
readonly label: "GPT-5.6 Luna";
|
|
20
24
|
readonly note: "Fast, cost-efficient OpenAI model";
|
|
21
25
|
readonly provider: "openai";
|
|
22
26
|
readonly reasoningEfforts: readonly ["low", "medium", "high", "xhigh", "max"];
|
|
23
27
|
}, {
|
|
24
28
|
readonly id: "gemini-3.8-flash";
|
|
29
|
+
readonly contextWindow: 1048576;
|
|
25
30
|
readonly label: "Gemini 3.8 Flash";
|
|
26
31
|
readonly note: "Fast Google model";
|
|
27
32
|
readonly provider: "google";
|
|
28
33
|
readonly reasoningEfforts: readonly ["low", "medium", "high"];
|
|
29
34
|
}, {
|
|
30
35
|
readonly id: "claude-opus-5";
|
|
36
|
+
readonly contextWindow: 1000000;
|
|
31
37
|
readonly label: "Opus 5";
|
|
32
38
|
readonly note: "Best Anthropic quality";
|
|
33
39
|
readonly provider: "anthropic";
|
|
34
40
|
readonly reasoningEfforts: readonly ["low", "medium", "high", "xhigh", "max"];
|
|
35
41
|
}, {
|
|
36
42
|
readonly id: "claude-fable-5";
|
|
43
|
+
readonly contextWindow: 1000000;
|
|
37
44
|
readonly label: "Fable 5";
|
|
38
45
|
readonly note: "Fast Anthropic model";
|
|
39
46
|
readonly provider: "anthropic";
|
package/dist/models.js
CHANGED
|
@@ -15,6 +15,8 @@ const EXTENDED_REASONING_EFFORTS = [
|
|
|
15
15
|
export const SPARK_MODELS = [
|
|
16
16
|
{
|
|
17
17
|
id: "gpt-6-astra",
|
|
18
|
+
// https://developers.openai.com/api/docs/models/gpt-6-astra
|
|
19
|
+
contextWindow: 1_050_000,
|
|
18
20
|
label: "GPT-6 Astra",
|
|
19
21
|
note: "Best OpenAI quality",
|
|
20
22
|
provider: "openai",
|
|
@@ -22,6 +24,8 @@ export const SPARK_MODELS = [
|
|
|
22
24
|
},
|
|
23
25
|
{
|
|
24
26
|
id: "gpt-5.6-luna",
|
|
27
|
+
// https://developers.openai.com/api/docs/models/gpt-5.6-luna
|
|
28
|
+
contextWindow: 1_050_000,
|
|
25
29
|
label: "GPT-5.6 Luna",
|
|
26
30
|
note: "Fast, cost-efficient OpenAI model",
|
|
27
31
|
provider: "openai",
|
|
@@ -29,6 +33,8 @@ export const SPARK_MODELS = [
|
|
|
29
33
|
},
|
|
30
34
|
{
|
|
31
35
|
id: "gemini-3.8-flash",
|
|
36
|
+
// https://ai.google.dev/gemini-api/docs/models/gemini-3.8-flash
|
|
37
|
+
contextWindow: 1_048_576,
|
|
32
38
|
label: "Gemini 3.8 Flash",
|
|
33
39
|
note: "Fast Google model",
|
|
34
40
|
provider: "google",
|
|
@@ -36,6 +42,8 @@ export const SPARK_MODELS = [
|
|
|
36
42
|
},
|
|
37
43
|
{
|
|
38
44
|
id: "claude-opus-5",
|
|
45
|
+
// https://platform.claude.com/docs/en/models/opus-5/whats-new-opus-5
|
|
46
|
+
contextWindow: 1_000_000,
|
|
39
47
|
label: "Opus 5",
|
|
40
48
|
note: "Best Anthropic quality",
|
|
41
49
|
provider: "anthropic",
|
|
@@ -43,6 +51,8 @@ export const SPARK_MODELS = [
|
|
|
43
51
|
},
|
|
44
52
|
{
|
|
45
53
|
id: "claude-fable-5",
|
|
54
|
+
// https://platform.claude.com/docs/en/models/fable-5/introducing-claude-fable-5-and-claude-mythos-5
|
|
55
|
+
contextWindow: 1_000_000,
|
|
46
56
|
label: "Fable 5",
|
|
47
57
|
note: "Fast Anthropic model",
|
|
48
58
|
provider: "anthropic",
|
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 to discover the APIs needed for the user's request. 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.\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. Inspect 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.";
|
|
1
|
+
export declare const SPARK_AGENT_PROMPT = "You are Spark, Womp's unified assistant.\nUse read_skill to discover the APIs needed for the user's request. 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.\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. Inspect 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\nChoose the workflow from the user's intended result and the conversation.\nDescribe choices by their outcome, not internal tools.\n\n- Use editable CAD for printable, measured, mechanical, or real-world-fit\n objects. Ask about missing dimensions only when a sensible default would\n likely make the result wrong. CAD is the preferred printing workflow;\n do not claim print readiness without supporting validation.\n\n- Build directly in the scene for ordinary 3D requests, including \u201C3D X\u201D\n and \u201Cmake X in 3D,\u201D and when the user wants individually editable shapes\n or geometry that blends with existing objects. Printable, measured,\n or mechanical intent takes precedence and routes to CAD.\n\n- Use mesh generation when the user explicitly requests a mesh, asks to\n turn an existing image into 3D, or chooses a sculpted result rather than\n individually editable shapes.\n\n- Use image generation for pictures, logos, concept art, wallpapers,\n and other clearly visual output.\n\n- For an ambiguous creative request without explicit 3D or printing intent,\n generate one image preview first. Then offer one short, outcome-based\n choice: refine the image, turn it into a sculpted 3D piece, or build it\n from individually editable shapes. Offer adjustable CAD instead of\n editable shapes when the subject is measured or must fit something real.\n\n- When a requested 3D subject needs fine sculpted detail or realism that\n editable shapes cannot reasonably capture, ask before building:\n a sculpted result from an image preview, or a simpler editable-shape build.\n Respect an explicit choice already made by the user.\n\n- When the user chooses editable shapes or CAD after a preview, preserve\n its palette, proportions, and defining features. When they choose a\n sculpted result, use that preview as the image-to-mesh input rather\n than starting again from a text prompt.\n\n- If the user wants repeated generation variations and Flow is available,\n offer a reusable Flow workflow.\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.";
|
package/dist/prompt.js
CHANGED
|
@@ -16,4 +16,56 @@ If an operation fails after earlier effects, use its receipts to inspect and ret
|
|
|
16
16
|
Never claim an uncertain commit rolled back. Never resubmit a job just because waiting timed out.
|
|
17
17
|
Complete related work in this conversation without delegating to another agent. Report limits,
|
|
18
18
|
unsupported actions, cancellation, and incomplete work truthfully. Finish with a concise explanation
|
|
19
|
-
of the result in the user's language; keep implementation details out of that explanation
|
|
19
|
+
of the result in the user's language; keep implementation details out of that explanation.
|
|
20
|
+
|
|
21
|
+
Creation intent
|
|
22
|
+
|
|
23
|
+
Choose the workflow from the user's intended result and the conversation.
|
|
24
|
+
Describe choices by their outcome, not internal tools.
|
|
25
|
+
|
|
26
|
+
- Use editable CAD for printable, measured, mechanical, or real-world-fit
|
|
27
|
+
objects. Ask about missing dimensions only when a sensible default would
|
|
28
|
+
likely make the result wrong. CAD is the preferred printing workflow;
|
|
29
|
+
do not claim print readiness without supporting validation.
|
|
30
|
+
|
|
31
|
+
- Build directly in the scene for ordinary 3D requests, including “3D X”
|
|
32
|
+
and “make X in 3D,” and when the user wants individually editable shapes
|
|
33
|
+
or geometry that blends with existing objects. Printable, measured,
|
|
34
|
+
or mechanical intent takes precedence and routes to CAD.
|
|
35
|
+
|
|
36
|
+
- Use mesh generation when the user explicitly requests a mesh, asks to
|
|
37
|
+
turn an existing image into 3D, or chooses a sculpted result rather than
|
|
38
|
+
individually editable shapes.
|
|
39
|
+
|
|
40
|
+
- Use image generation for pictures, logos, concept art, wallpapers,
|
|
41
|
+
and other clearly visual output.
|
|
42
|
+
|
|
43
|
+
- For an ambiguous creative request without explicit 3D or printing intent,
|
|
44
|
+
generate one image preview first. Then offer one short, outcome-based
|
|
45
|
+
choice: refine the image, turn it into a sculpted 3D piece, or build it
|
|
46
|
+
from individually editable shapes. Offer adjustable CAD instead of
|
|
47
|
+
editable shapes when the subject is measured or must fit something real.
|
|
48
|
+
|
|
49
|
+
- When a requested 3D subject needs fine sculpted detail or realism that
|
|
50
|
+
editable shapes cannot reasonably capture, ask before building:
|
|
51
|
+
a sculpted result from an image preview, or a simpler editable-shape build.
|
|
52
|
+
Respect an explicit choice already made by the user.
|
|
53
|
+
|
|
54
|
+
- When the user chooses editable shapes or CAD after a preview, preserve
|
|
55
|
+
its palette, proportions, and defining features. When they choose a
|
|
56
|
+
sculpted result, use that preview as the image-to-mesh input rather
|
|
57
|
+
than starting again from a text prompt.
|
|
58
|
+
|
|
59
|
+
- If the user wants repeated generation variations and Flow is available,
|
|
60
|
+
offer a reusable Flow workflow.
|
|
61
|
+
|
|
62
|
+
- Ask at most one focused question at a time, only when a sensible default
|
|
63
|
+
would likely produce the wrong outcome. Do not repeat resolved questions.
|
|
64
|
+
Interpret replies using the preceding offer; if several options were
|
|
65
|
+
offered and “yes” does not identify one, clarify briefly.
|
|
66
|
+
|
|
67
|
+
- Treat a clear answer to your question as an instruction to proceed.
|
|
68
|
+
Continue from existing results rather than restarting the workflow.
|
|
69
|
+
|
|
70
|
+
Offer only workflows available in this host. Follow their authorization
|
|
71
|
+
and confirmation requirements before generation or scene changes.`;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const DEFAULT_JSON_LIMIT_BYTES: number;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export const DEFAULT_JSON_LIMIT_BYTES = 25 * 1024 * 1024;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { SparkStepMetricsData, SparkUIMessageChunk } from "./ui.js";
|
|
2
|
+
export declare function createBenchMetrics(): {
|
|
3
|
+
accept(chunk: SparkUIMessageChunk): void;
|
|
4
|
+
details(): {
|
|
5
|
+
toolCounts: Record<string, number>;
|
|
6
|
+
steps: SparkStepMetricsData[];
|
|
7
|
+
};
|
|
8
|
+
snapshot(): {
|
|
9
|
+
steps: number;
|
|
10
|
+
readSkill: number;
|
|
11
|
+
executeJavaScript: number;
|
|
12
|
+
costUsd: number | null;
|
|
13
|
+
costStatus: "unavailable" | "partial" | "estimated";
|
|
14
|
+
};
|
|
15
|
+
};
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
export function createBenchMetrics() {
|
|
2
|
+
const steps = new Map();
|
|
3
|
+
const calls = new Map();
|
|
4
|
+
return {
|
|
5
|
+
accept(chunk) {
|
|
6
|
+
if (chunk.type === "data-step-metrics")
|
|
7
|
+
steps.set(chunk.data.step, chunk.data);
|
|
8
|
+
if (chunk.type === "tool-input-available")
|
|
9
|
+
calls.set(chunk.toolCallId, chunk.toolName);
|
|
10
|
+
},
|
|
11
|
+
details() {
|
|
12
|
+
const toolCounts = {};
|
|
13
|
+
for (const name of calls.values())
|
|
14
|
+
toolCounts[name] = (toolCounts[name] ?? 0) + 1;
|
|
15
|
+
return { toolCounts, steps: Array.from(steps.values()) };
|
|
16
|
+
},
|
|
17
|
+
snapshot() {
|
|
18
|
+
const names = Array.from(calls.values());
|
|
19
|
+
const priced = Array.from(steps.values()).map((step) => step.cost?.totalUsd ?? null)
|
|
20
|
+
.filter((cost) => cost !== null);
|
|
21
|
+
return {
|
|
22
|
+
steps: steps.size,
|
|
23
|
+
readSkill: names.filter((name) => name === "read_skill").length,
|
|
24
|
+
executeJavaScript: names.filter((name) => name === "execute_javascript").length,
|
|
25
|
+
costUsd: priced.length ? priced.reduce((sum, cost) => sum + cost, 0) : null,
|
|
26
|
+
costStatus: (priced.length === 0 ? "unavailable" : priced.length < steps.size ? "partial" : "estimated"),
|
|
27
|
+
};
|
|
28
|
+
},
|
|
29
|
+
};
|
|
30
|
+
}
|
package/dist/runtime.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import { KakapoAPI
|
|
1
|
+
import { KakapoAPI } from "@womp/kakapo-sdk";
|
|
2
|
+
import type { SceneScreenshotOptions } from "./capabilities/scene.js";
|
|
2
3
|
import type { JavaScriptExecutor } from "./executor.js";
|
|
3
4
|
import type { ExecutionResult, ExecutionScreenshot, SparkRuntimeEventSink, SparkRuntime } from "./types.js";
|
|
4
5
|
import { type WompSceneApi } from "./womp-scene-api.js";
|
|
@@ -31,9 +32,6 @@ export declare class KakapoRuntime implements SparkRuntime {
|
|
|
31
32
|
scene: import("@womp/kakapo-sdk").Scene;
|
|
32
33
|
}>;
|
|
33
34
|
edit<T>(callback: (draft: WompSceneApi) => Promise<T> | T, expectedRevision: string, context: SparkInvocationContext): Promise<T>;
|
|
34
|
-
screenshot(input:
|
|
35
|
-
targetId?: number;
|
|
36
|
-
view?: ScreenshotView;
|
|
37
|
-
}): Promise<ExecutionScreenshot>;
|
|
35
|
+
screenshot(input: SceneScreenshotOptions): Promise<ExecutionScreenshot>;
|
|
38
36
|
execute(code: string, purpose: string): Promise<ExecutionResult>;
|
|
39
37
|
}
|
package/dist/scene-metadata.js
CHANGED
|
@@ -41,7 +41,7 @@ export function shiftCurvePointMetadata(scene, curveId, index, direction) {
|
|
|
41
41
|
if (direction === 1) {
|
|
42
42
|
points[index] = {
|
|
43
43
|
locked: false,
|
|
44
|
-
libraryId:
|
|
44
|
+
libraryId: -1,
|
|
45
45
|
name: `Point ${index + 1}`,
|
|
46
46
|
};
|
|
47
47
|
}
|
package/dist/sdk-scene-host.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { FontFamily, KakapoAPI, SceneEdit, MaterialResource } from "@womp/kakapo-sdk";
|
|
2
2
|
import type { ArtifactRef, SparkInvocationContext } from "./capabilities.js";
|
|
3
|
-
import type { SceneHost, SceneScreenshotOptions } from "./capabilities/scene.js";
|
|
3
|
+
import type { SceneHost, SceneHealthHost, SceneScreenshotOptions } from "./capabilities/scene.js";
|
|
4
4
|
import { type SceneOperation, type SceneOperationStore } from "./scene-operations.js";
|
|
5
5
|
import type { SceneHdr } from "./scene-environment.js";
|
|
6
6
|
export type SdkSceneServices = {
|
|
@@ -82,6 +82,11 @@ export declare function createSdkSceneDraft(api: KakapoAPI, scene: SceneEdit, se
|
|
|
82
82
|
nodeCreate: (type: number, parentId?: number) => number;
|
|
83
83
|
nodeDelete: (id: number) => void;
|
|
84
84
|
nodeClone: (id: number) => number;
|
|
85
|
+
cloneSubtree: (id: number, options?: {
|
|
86
|
+
parentId?: number;
|
|
87
|
+
name?: string;
|
|
88
|
+
index?: number;
|
|
89
|
+
}) => Promise<number>;
|
|
85
90
|
nodeList: () => number[];
|
|
86
91
|
nodeChildren: (parentId: number) => number[];
|
|
87
92
|
nodeSetParent: (id: number, parentId: number, index?: number) => void;
|
|
@@ -118,7 +123,7 @@ export declare function createSdkSceneDraft(api: KakapoAPI, scene: SceneEdit, se
|
|
|
118
123
|
nodeGetMaterialId: (id: number) => number | null;
|
|
119
124
|
nodeSetMaterialId: (id: number, materialId: number) => void;
|
|
120
125
|
nodeGetResolution: (id: number) => number;
|
|
121
|
-
|
|
126
|
+
nodeSetUnionResolution: (unionId: number, resolution: number) => void;
|
|
122
127
|
nodeGetLightType: (id: number) => number;
|
|
123
128
|
nodeSetLightType: (id: number, lightType: number) => void;
|
|
124
129
|
nodeGetPower: (id: number) => number;
|
|
@@ -164,16 +169,7 @@ export declare function createSdkSceneDraft(api: KakapoAPI, scene: SceneEdit, se
|
|
|
164
169
|
curvePointSetHandleIn: (curveId: number, pointIndex: number, value: import("@womp/kakapo-sdk").Vec3) => void;
|
|
165
170
|
curvePointSetHandleOut: (curveId: number, pointIndex: number, value: import("@womp/kakapo-sdk").Vec3) => void;
|
|
166
171
|
curvePointCount: (curveId: number) => number;
|
|
167
|
-
curvePointAdd: (curveId: number, point
|
|
168
|
-
name?: string;
|
|
169
|
-
pos?: import("@womp/kakapo-sdk").Vec3;
|
|
170
|
-
rot?: import("@womp/kakapo-sdk").Vec3;
|
|
171
|
-
size?: import("@womp/kakapo-sdk").Vec3;
|
|
172
|
-
fixed?: boolean;
|
|
173
|
-
round?: number;
|
|
174
|
-
materialId?: number;
|
|
175
|
-
}) => number;
|
|
176
|
-
curvePointInsert: (curveId: number, pointIndex: number, point?: {
|
|
172
|
+
curvePointAdd: (curveId: number, point: {
|
|
177
173
|
name?: string;
|
|
178
174
|
pos?: import("@womp/kakapo-sdk").Vec3;
|
|
179
175
|
rot?: import("@womp/kakapo-sdk").Vec3;
|
|
@@ -181,7 +177,7 @@ export declare function createSdkSceneDraft(api: KakapoAPI, scene: SceneEdit, se
|
|
|
181
177
|
fixed?: boolean;
|
|
182
178
|
round?: number;
|
|
183
179
|
materialId?: number;
|
|
184
|
-
}) => number;
|
|
180
|
+
}, pointIndex?: number) => number;
|
|
185
181
|
curvePointRemove: (curveId: number, pointIndex: number) => void;
|
|
186
182
|
curvePointGetName: (curveId: number, pointIndex: number) => string;
|
|
187
183
|
curvePointSetName: (curveId: number, pointIndex: number, name: string) => void;
|
|
@@ -285,6 +281,7 @@ export declare function createSdkSceneDraft(api: KakapoAPI, scene: SceneEdit, se
|
|
|
285
281
|
max: import("@womp/kakapo-sdk").Vec3;
|
|
286
282
|
}) => number;
|
|
287
283
|
lookAt: (origin: import("@womp/kakapo-sdk").Vec3, target: import("@womp/kakapo-sdk").Vec3) => import("@womp/kakapo-sdk").Vec3;
|
|
284
|
+
alignAxis: (direction: import("@womp/kakapo-sdk").Vec3, axis: "x" | "y" | "z") => import("@womp/kakapo-sdk").Vec3;
|
|
288
285
|
scenePropertiesRead: () => {
|
|
289
286
|
hdr_name: string | undefined;
|
|
290
287
|
hdr_multiplier: number | undefined;
|
|
@@ -306,6 +303,7 @@ export declare function createSdkSceneDraft(api: KakapoAPI, scene: SceneEdit, se
|
|
|
306
303
|
export declare function createSdkSceneEditor(options: SdkSceneEditorOptions): SdkSceneEditor;
|
|
307
304
|
export declare function createSdkSceneHost(options: SdkSceneEditorOptions & {
|
|
308
305
|
editor?: SdkSceneEditor;
|
|
306
|
+
health?: SceneHealthHost;
|
|
309
307
|
services(api: KakapoAPI, context: SparkInvocationContext): Promise<SdkSceneServices>;
|
|
310
308
|
screenshot(options: SceneScreenshotOptions, context: SparkInvocationContext): Promise<ArtifactRef>;
|
|
311
309
|
onSceneCommitted?(operation: SceneOperation, api: KakapoAPI): Promise<Record<string, unknown> | void>;
|
package/dist/sdk-scene-host.js
CHANGED
|
@@ -193,6 +193,7 @@ export function createSdkSceneEditor(options) {
|
|
|
193
193
|
export function createSdkSceneHost(options) {
|
|
194
194
|
const editor = options.editor ?? createSdkSceneEditor(options);
|
|
195
195
|
return {
|
|
196
|
+
health: options.health?.read.bind(options.health),
|
|
196
197
|
read: (context) => editor.read(context, async (api) => {
|
|
197
198
|
const scene = api.getScene();
|
|
198
199
|
const revision = await api.getSceneRevision();
|
|
@@ -1,7 +1,5 @@
|
|
|
1
|
-
import type { KakapoAPI
|
|
1
|
+
import type { KakapoAPI } from "@womp/kakapo-sdk";
|
|
2
|
+
import type { SceneScreenshotOptions } from "./capabilities/scene.js";
|
|
2
3
|
import type { ExecutionScreenshot } from "./types.js";
|
|
3
|
-
/**
|
|
4
|
-
export declare function captureSdkSceneScreenshot(api: KakapoAPI, input:
|
|
5
|
-
targetId?: number;
|
|
6
|
-
view?: ScreenshotView;
|
|
7
|
-
}): Promise<ExecutionScreenshot>;
|
|
4
|
+
/** Convert Womp scene coordinates at the host boundary; never fit a camera implicitly. */
|
|
5
|
+
export declare function captureSdkSceneScreenshot(api: KakapoAPI, input: SceneScreenshotOptions): Promise<ExecutionScreenshot>;
|
|
@@ -1,17 +1,16 @@
|
|
|
1
|
-
|
|
1
|
+
import { INTERNAL_UNITS_PER_MM, sceneUnitMultiplier, toEnginePosition } from "./coordinates.js";
|
|
2
|
+
/** Convert Womp scene coordinates at the host boundary; never fit a camera implicitly. */
|
|
2
3
|
export async function captureSdkSceneScreenshot(api, input) {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
}
|
|
16
|
-
return api.captureScreenshot({ targetId, view, timeoutMs: 30_000 });
|
|
4
|
+
if (!input.camera)
|
|
5
|
+
return api.captureScreenshot({ timeoutMs: 30_000 });
|
|
6
|
+
const scene = await api.refreshScene();
|
|
7
|
+
const units = sceneUnitMultiplier(scene.properties.unit_multiplier);
|
|
8
|
+
const camera = {
|
|
9
|
+
...input.camera,
|
|
10
|
+
position: toEnginePosition(input.camera.position, units),
|
|
11
|
+
target: toEnginePosition(input.camera.target, units),
|
|
12
|
+
};
|
|
13
|
+
if (camera.orthographicWidth !== undefined)
|
|
14
|
+
camera.orthographicWidth *= INTERNAL_UNITS_PER_MM / units;
|
|
15
|
+
return api.captureScreenshot({ camera, timeoutMs: 30_000 });
|
|
17
16
|
}
|
package/dist/server/artifacts.js
CHANGED
|
@@ -11,13 +11,9 @@ export class MemorySparkArtifactStore {
|
|
|
11
11
|
ownerId,
|
|
12
12
|
url: `${this.publicBaseUrl}/${id}`,
|
|
13
13
|
mediaType: screenshot.mediaType,
|
|
14
|
-
name:
|
|
14
|
+
name: "spark-screenshot.jpg",
|
|
15
15
|
width: screenshot.width,
|
|
16
16
|
height: screenshot.height,
|
|
17
|
-
metadata: {
|
|
18
|
-
view: screenshot.view,
|
|
19
|
-
targetId: screenshot.targetId,
|
|
20
|
-
},
|
|
21
17
|
data: screenshot.data.slice(),
|
|
22
18
|
};
|
|
23
19
|
this.artifacts.set(id, artifact);
|
|
@@ -6,7 +6,6 @@ export type ResolvedSparkModel = {
|
|
|
6
6
|
modelId: string;
|
|
7
7
|
providerOptions?: SparkAgentOptions["providerOptions"];
|
|
8
8
|
maxOutputTokens?: number;
|
|
9
|
-
maxInputTokens?: number;
|
|
10
9
|
};
|
|
11
10
|
export type SparkRunExecutorOptions = {
|
|
12
11
|
resolveModel(settings: SparkRunSettings): ResolvedSparkModel | Promise<ResolvedSparkModel>;
|
package/dist/server/executor.js
CHANGED
|
@@ -20,7 +20,6 @@ export function createSparkRunExecutor(options) {
|
|
|
20
20
|
modelId: resolved.modelId,
|
|
21
21
|
providerOptions: resolved.providerOptions,
|
|
22
22
|
maxOutputTokens: resolved.maxOutputTokens,
|
|
23
|
-
maxInputTokens: resolved.maxInputTokens,
|
|
24
23
|
runId: context.runId,
|
|
25
24
|
messageId: context.assistantMessageId,
|
|
26
25
|
signal: context.signal,
|
package/dist/server/express.js
CHANGED
|
@@ -3,7 +3,12 @@ import express from "express";
|
|
|
3
3
|
import { z } from "zod";
|
|
4
4
|
import { SPARK_RUN_STATUSES } from "../ui.js";
|
|
5
5
|
import { SparkServiceError } from "./service.js";
|
|
6
|
+
import { DEFAULT_JSON_LIMIT_BYTES } from "../request-limits.js";
|
|
6
7
|
const reasoningEffortSchema = z.enum(["low", "medium", "high", "xhigh", "max"]);
|
|
8
|
+
const settingsSchema = z.object({
|
|
9
|
+
modelId: z.string().min(1),
|
|
10
|
+
reasoningEffort: reasoningEffortSchema,
|
|
11
|
+
});
|
|
7
12
|
const messageSchema = z.object({
|
|
8
13
|
id: z.string().min(1),
|
|
9
14
|
role: z.enum(["system", "user", "assistant"]),
|
|
@@ -14,10 +19,7 @@ const submitSchema = z.object({
|
|
|
14
19
|
trigger: z.enum(["submit-message", "regenerate-message"]),
|
|
15
20
|
message: messageSchema,
|
|
16
21
|
messageId: z.string().optional(),
|
|
17
|
-
settings:
|
|
18
|
-
modelId: z.string().min(1),
|
|
19
|
-
reasoningEffort: reasoningEffortSchema,
|
|
20
|
-
}),
|
|
22
|
+
settings: settingsSchema,
|
|
21
23
|
});
|
|
22
24
|
function parseSequence(value) {
|
|
23
25
|
if (typeof value !== "string" || /^\d+$/u.test(value) === false)
|
|
@@ -37,7 +39,7 @@ export function createSparkAgentRouter(options) {
|
|
|
37
39
|
const router = express.Router();
|
|
38
40
|
const resolveOwnerId = options.resolveOwnerId ?? (() => "local");
|
|
39
41
|
const heartbeatMs = options.heartbeatMs ?? 15_000;
|
|
40
|
-
router.use(express.json({ limit: options.jsonLimit ??
|
|
42
|
+
router.use(express.json({ limit: options.jsonLimit ?? DEFAULT_JSON_LIMIT_BYTES }));
|
|
41
43
|
router.get("/conversations", async (request, response) => {
|
|
42
44
|
const ownerId = await resolveOwnerId(request);
|
|
43
45
|
response.json({ conversations: await options.service.listConversations(ownerId) });
|
|
@@ -56,6 +58,16 @@ export function createSparkAgentRouter(options) {
|
|
|
56
58
|
const conversation = await options.service.getConversation(ownerId, request.params.conversationId);
|
|
57
59
|
response.json({ conversation });
|
|
58
60
|
});
|
|
61
|
+
router.patch("/conversations/:conversationId/settings", async (request, response) => {
|
|
62
|
+
const ownerId = await resolveOwnerId(request);
|
|
63
|
+
await options.service.saveConversationSettings(ownerId, request.params.conversationId, settingsSchema.parse(request.body));
|
|
64
|
+
response.sendStatus(204);
|
|
65
|
+
});
|
|
66
|
+
router.delete("/conversations/:conversationId", async (request, response) => {
|
|
67
|
+
const ownerId = await resolveOwnerId(request);
|
|
68
|
+
await options.service.deleteConversation(ownerId, request.params.conversationId);
|
|
69
|
+
response.status(204).end();
|
|
70
|
+
});
|
|
59
71
|
router.post("/conversations/:conversationId/messages", async (request, response) => {
|
|
60
72
|
const ownerId = await resolveOwnerId(request);
|
|
61
73
|
const body = submitSchema.parse(request.body);
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
function nodeDepth(node, nodes) {
|
|
2
|
+
let depth = 0;
|
|
3
|
+
let parentId = node.parentId;
|
|
4
|
+
while (parentId !== null && parentId !== 0) {
|
|
5
|
+
depth += 1;
|
|
6
|
+
parentId = nodes.get(parentId)?.parentId ?? null;
|
|
7
|
+
}
|
|
8
|
+
return depth;
|
|
9
|
+
}
|
|
10
|
+
export function clearSceneContents(scene) {
|
|
11
|
+
const nodes = scene.listNodes().filter(({ id }) => id !== 0);
|
|
12
|
+
const nodesById = new Map(nodes.map((node) => [node.id, node]));
|
|
13
|
+
nodes.sort((a, b) => nodeDepth(b, nodesById) - nodeDepth(a, nodesById));
|
|
14
|
+
for (const node of nodes)
|
|
15
|
+
scene.deleteNode(node.id);
|
|
16
|
+
const materials = scene.listMaterials();
|
|
17
|
+
for (const material of materials)
|
|
18
|
+
scene.deleteMaterial(material.id);
|
|
19
|
+
return nodes.length + materials.length;
|
|
20
|
+
}
|
package/dist/server/service.d.ts
CHANGED
|
@@ -28,6 +28,8 @@ export declare class SparkAgentService {
|
|
|
28
28
|
}): Promise<SparkConversation>;
|
|
29
29
|
listConversations(ownerId: string): Promise<SparkConversationSummary[]>;
|
|
30
30
|
getConversation(ownerId: string, id: string): Promise<SparkConversation>;
|
|
31
|
+
saveConversationSettings(ownerId: string, id: string, settings: SparkRunSettings): Promise<void>;
|
|
32
|
+
deleteConversation(ownerId: string, id: string): Promise<void>;
|
|
31
33
|
getRun(ownerId: string, id: string): Promise<SparkRun>;
|
|
32
34
|
getActiveRun(ownerId: string, conversationId: string): Promise<SparkRun | undefined>;
|
|
33
35
|
/** The host must establish exclusive ownership before recovering abandoned runs. */
|
package/dist/server/service.js
CHANGED
|
@@ -96,6 +96,16 @@ export class SparkAgentService {
|
|
|
96
96
|
}
|
|
97
97
|
return conversation;
|
|
98
98
|
}
|
|
99
|
+
async saveConversationSettings(ownerId, id, settings) {
|
|
100
|
+
await this.getConversation(ownerId, id);
|
|
101
|
+
await this.store.saveConversationSettings(id, settings);
|
|
102
|
+
}
|
|
103
|
+
async deleteConversation(ownerId, id) {
|
|
104
|
+
await this.getConversation(ownerId, id);
|
|
105
|
+
if (await this.store.deleteConversation(id) === false) {
|
|
106
|
+
throw new SparkServiceError(409, "Stop the active run before deleting this conversation.");
|
|
107
|
+
}
|
|
108
|
+
}
|
|
99
109
|
async getRun(ownerId, id) {
|
|
100
110
|
const run = await this.store.getRun(id);
|
|
101
111
|
if (run === undefined)
|
|
@@ -3,7 +3,7 @@ import type { SparkArtifact, StoredSparkChunk } from "../ui.js";
|
|
|
3
3
|
import type { SparkArtifactStore, StoredSparkArtifact } from "./artifacts.js";
|
|
4
4
|
import type { SceneOperation, SceneOperationStore } from "../scene-operations.js";
|
|
5
5
|
import { type RepairStateStore } from "../repair-state.js";
|
|
6
|
-
import type { SparkAgentStore, SparkConversation, SparkConversationSummary, SparkRun } from "./types.js";
|
|
6
|
+
import type { SparkAgentStore, SparkConversation, SparkConversationSummary, SparkRun, SparkRunSettings } from "./types.js";
|
|
7
7
|
/** Local development storage. Execution ownership belongs to the host service. */
|
|
8
8
|
export declare class SqliteSparkStore implements SparkAgentStore, SparkArtifactStore, SceneOperationStore {
|
|
9
9
|
private readonly artifactBaseUrl;
|
|
@@ -21,6 +21,8 @@ export declare class SqliteSparkStore implements SparkAgentStore, SparkArtifactS
|
|
|
21
21
|
getConversation(id: string): Promise<SparkConversation | undefined>;
|
|
22
22
|
listConversations(ownerId: string): Promise<SparkConversationSummary[]>;
|
|
23
23
|
saveConversation(value: SparkConversation): Promise<void>;
|
|
24
|
+
saveConversationSettings(id: string, settings: SparkRunSettings): Promise<void>;
|
|
25
|
+
deleteConversation(id: string): Promise<boolean>;
|
|
24
26
|
createRun(value: SparkRun): Promise<void>;
|
|
25
27
|
getRun(id: string): Promise<SparkRun | undefined>;
|
|
26
28
|
saveRun(value: SparkRun): Promise<void>;
|
|
@@ -159,10 +159,34 @@ export class SqliteSparkStore {
|
|
|
159
159
|
});
|
|
160
160
|
}
|
|
161
161
|
async saveConversation(value) {
|
|
162
|
-
const result = this.database.prepare("UPDATE conversations SET owner_id = ?, updated_at = ?, data =
|
|
162
|
+
const result = this.database.prepare("UPDATE conversations SET owner_id = ?, updated_at = ?, data = json_patch(json_remove(?, '$.settings'), json_object('settings', json_extract(data, '$.settings'))) WHERE id = ?").run(value.ownerId, value.updatedAt, JSON.stringify(value), value.id);
|
|
163
163
|
if (result.changes === 0)
|
|
164
164
|
throw new Error(`Conversation ${value.id} does not exist.`);
|
|
165
165
|
}
|
|
166
|
+
async saveConversationSettings(id, settings) {
|
|
167
|
+
const result = this.database.prepare("UPDATE conversations SET data = json_set(data, '$.settings', json(?)) WHERE id = ?").run(JSON.stringify(settings), id);
|
|
168
|
+
if (result.changes === 0)
|
|
169
|
+
throw new Error(`Conversation ${id} does not exist.`);
|
|
170
|
+
}
|
|
171
|
+
async deleteConversation(id) {
|
|
172
|
+
this.database.exec("BEGIN IMMEDIATE");
|
|
173
|
+
try {
|
|
174
|
+
const active = this.database.prepare("SELECT id FROM conversations WHERE id = ? AND json_extract(data, '$.activeRunId') IS NOT NULL").get(id);
|
|
175
|
+
if (active) {
|
|
176
|
+
this.database.exec("ROLLBACK");
|
|
177
|
+
return false;
|
|
178
|
+
}
|
|
179
|
+
this.database.prepare("DELETE FROM chunks WHERE run_id IN (SELECT id FROM runs WHERE conversation_id = ?)").run(id);
|
|
180
|
+
this.database.prepare("DELETE FROM runs WHERE conversation_id = ?").run(id);
|
|
181
|
+
this.database.prepare("DELETE FROM conversations WHERE id = ?").run(id);
|
|
182
|
+
this.database.exec("COMMIT");
|
|
183
|
+
return true;
|
|
184
|
+
}
|
|
185
|
+
catch (error) {
|
|
186
|
+
this.database.exec("ROLLBACK");
|
|
187
|
+
throw error;
|
|
188
|
+
}
|
|
189
|
+
}
|
|
166
190
|
async createRun(value) {
|
|
167
191
|
this.database.prepare("INSERT INTO runs VALUES (?, ?, ?)")
|
|
168
192
|
.run(value.id, value.conversationId, JSON.stringify(value));
|
|
@@ -205,9 +229,8 @@ export class SqliteSparkStore {
|
|
|
205
229
|
const id = crypto.randomUUID();
|
|
206
230
|
const artifact = {
|
|
207
231
|
id, url: `${this.artifactBaseUrl.replace(/\/$/u, "")}/${id}`,
|
|
208
|
-
mediaType: image.mediaType, name:
|
|
232
|
+
mediaType: image.mediaType, name: "spark-screenshot.jpg",
|
|
209
233
|
width: image.width, height: image.height,
|
|
210
|
-
metadata: { view: image.view, targetId: image.targetId },
|
|
211
234
|
};
|
|
212
235
|
this.database.prepare("INSERT INTO artifacts VALUES (?, ?, ?, ?)")
|
|
213
236
|
.run(id, ownerId, JSON.stringify(artifact), image.data);
|
|
@@ -11,6 +11,8 @@ export type StandaloneSparkAgentOptions = {
|
|
|
11
11
|
/** Stable host-verified identity for the scene behind kakapoUrl. */
|
|
12
12
|
sceneIdentity?: string;
|
|
13
13
|
logger?: Pick<Console, "info" | "warn">;
|
|
14
|
+
/** Observe successful renderer captures, including captures not attached to a reply. */
|
|
15
|
+
onScreenshot?: (runId: string) => void;
|
|
14
16
|
};
|
|
15
17
|
export type StandaloneSparkAgentApp = Express & {
|
|
16
18
|
dispose(): Promise<void>;
|