@womp/spark-agent 5.5.0 → 5.6.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 CHANGED
@@ -395,7 +395,9 @@ yarn web:build
395
395
 
396
396
  ## Release procedure
397
397
 
398
- Releases use a manual package publication; no separate release service is required.
398
+ Pushes to `main` run `.github/workflows/publish.yml`. Publication is driven by
399
+ `package.json`, not Git tags: already-published versions skip; unpublished stable versions
400
+ newer than npm's `latest` run release checks and publish automatically.
399
401
 
400
402
  Versions follow **MAJOR.MINOR.PATCH** (for example, `4.4.0`):
401
403
 
@@ -405,22 +407,19 @@ Versions follow **MAJOR.MINOR.PATCH** (for example, `4.4.0`):
405
407
 
406
408
  ### Publishing
407
409
 
408
- 1. Choose an unused package version after checking `npm view @womp/spark-agent versions --json`.
409
- Update `package.json` and record public API changes and migration notes. Use a new minor version
410
- for incompatible changes while the package is below 1.0.
411
- 2. On a supported Node version, run `yarn install --frozen-lockfile`, `yarn typecheck`, `yarn test`,
412
- and `yarn web:build`. Do not bypass dependency engine checks. Commit the release changes and
413
- record the commit used to build the package.
414
- 3. Run `yarn pack --filename spark-agent-release.tgz`. Inspect the archive contents and install
415
- that archive in the consuming application. Verify packaged declarations and skills, browser
416
- import boundaries, the production build, and the applicable integration cases without source
417
- aliases. Keep the archive outside Git and do not rebuild or change it after verification.
418
- 4. Publish the verified archive with `npm publish ./spark-agent-release.tgz --access public`.
419
- If the response is uncertain, inspect the registry's version and integrity before retrying.
420
- Verify the published version and archive integrity, then tag the recorded commit as `v<version>`
421
- and push the release commit and tag. An already published version cannot be replaced; corrections
422
- need a new version.
423
- 5. In the consuming application, use `yarn add @womp/spark-agent@<version> --exact`. Commit its
410
+ 1. Configure npm's trusted publisher once for `@womp/spark-agent`: organization `wompxyz`,
411
+ repository `spark-agent`, workflow filename `publish.yml`, no environment, and allow direct
412
+ `npm publish`. GitHub-hosted runners use OIDC; no publishing token is required.
413
+ 2. Deliberately bump `package.json` to an unused stable MAJOR.MINOR.PATCH version and push
414
+ the release commit to `main`. Prereleases/build metadata are not published by this workflow.
415
+ 3. CI installs the frozen Yarn lockfile, runs typechecks, tests and the UI build, then checks
416
+ package entry files and imports every exported module from a fresh tarball installation.
417
+ It rechecks npm before publishing that same archive and verifies registry integrity and `latest`.
418
+ Releases are serialized; newer pending pushes may supersede older pending runs.
419
+ Registry failures stop publication rather than being treated as missing versions.
420
+ 4. If publication reports an uncertain result, inspect npm before rerunning the failed workflow.
421
+ Published versions cannot be replaced; corrections need a new version. No tags are required.
422
+ 5. In the consuming application, use `yarn add @womp/spark-agent@<version> --exact`. Commit its
424
423
  manifest and lockfile together, and verify a clean frozen-lockfile installation and production
425
424
  build. Remove the temporary archive dependency. Application deployment and merge requirements
426
425
  remain separate from package publication.
@@ -79,11 +79,10 @@ export function createSparkTools(options) {
79
79
  return {
80
80
  read_skill: tool({
81
81
  ...inputLabels("name"),
82
- description: "Read required skills together with names, or one skill with name. Returns instructions, TypeScript API declarations, and JavaScript examples from the available directory.",
83
- inputSchema: z.union([
84
- z.object({ names: z.array(z.string().min(1).max(160)).min(1) }).strict(),
85
- z.object({ name: z.string().min(1).max(160) }).strict(),
86
- ]),
82
+ description: "Read the skills listed in names. Returns instructions, TypeScript API declarations, and JavaScript examples from the available directory.",
83
+ inputSchema: z.object({
84
+ names: z.array(z.string().min(1).max(160)).min(1),
85
+ }).strict(),
87
86
  execute: (input, { toolCallId }) => execute({ id: toolCallId, name: "read_skill", input }, async () => {
88
87
  const read = (requested) => {
89
88
  const skill = options.capabilities.skills.find(({ name }) => name === requested);
@@ -96,8 +95,6 @@ export function createSparkTools(options) {
96
95
  }
97
96
  return { ok: true, ...skill };
98
97
  };
99
- if ("name" in input)
100
- return read(input.name);
101
98
  const skills = [...new Set(input.names)].map(read);
102
99
  return { ok: skills.every(skill => skill.ok), skills };
103
100
  }),
package/dist/ui.d.ts CHANGED
@@ -115,13 +115,9 @@ export type SparkUITools = {
115
115
  };
116
116
  read_skill: {
117
117
  input: {
118
- name: string;
119
- names?: never;
120
- } | {
121
118
  names: string[];
122
- name?: never;
123
119
  };
124
- output: SkillReadResult | {
120
+ output: {
125
121
  ok: boolean;
126
122
  skills: SkillReadResult[];
127
123
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@womp/spark-agent",
3
- "version": "5.5.0",
3
+ "version": "5.6.1",
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",
@@ -102,7 +102,8 @@
102
102
  "web:build": "yarn build && tsc --noEmit -p web/tsconfig.json && vite build --config web/vite.config.ts",
103
103
  "test": "yarn build && tsx --tsconfig tsconfig.json --test test/**/*.test.ts && node --input-type=module -e \"await Promise.all([import('@womp/spark-agent'), import('@womp/spark-agent/ui'), import('@womp/spark-agent/client'), import('@womp/spark-agent/server'), import('@womp/spark-agent/express'), import('@womp/spark-agent/kakapo'), import('@womp/spark-agent/geometry'), import('@womp/spark-agent/openscad')])\"",
104
104
  "test:live": "tsx --tsconfig tsconfig.json test/womp-scene-api.live.ts",
105
- "prepack": "yarn build"
105
+ "prepack": "yarn build",
106
+ "release:pack": "node scripts/npm-release.mjs pack"
106
107
  },
107
108
  "dependencies": {
108
109
  "@ai-sdk/anthropic": "4.0.50",
@@ -1,11 +0,0 @@
1
- import type { KakapoAPI } from "@womp/kakapo-sdk";
2
- import type { OpenScadBoundingBox } from "./openscad.js";
3
- export declare function waitForOpenScadRenderer(options: {
4
- api: KakapoAPI;
5
- objectId: number;
6
- expectedBounds: OpenScadBoundingBox;
7
- baselineBounds?: OpenScadBoundingBox;
8
- signal: AbortSignal;
9
- timeoutMs?: number;
10
- pollIntervalMs?: number;
11
- }): Promise<boolean>;
@@ -1,71 +0,0 @@
1
- const OPENSCAD_RENDERER_WAIT_MS = 15_000;
2
- const OPENSCAD_RENDERER_POLL_MS = 250;
3
- function boundsMatch(actual, expected) {
4
- const size = Math.max(expected.max.x - expected.min.x, expected.max.y - expected.min.y, expected.max.z - expected.min.z);
5
- const tolerance = Math.max(size * 0.0001, 0.001);
6
- return ["min", "max"].every((edge) => ["x", "y", "z"].every((axis) => Math.abs(actual[edge][axis] - expected[edge][axis]) <= tolerance));
7
- }
8
- function getBoundsBeforeDeadline(options) {
9
- const remaining = options.deadline - Date.now();
10
- if (remaining <= 0)
11
- return Promise.resolve(undefined);
12
- options.signal.throwIfAborted();
13
- return new Promise((resolve, reject) => {
14
- let settled = false;
15
- const finish = (action) => {
16
- if (settled)
17
- return;
18
- settled = true;
19
- clearTimeout(timeout);
20
- options.signal.removeEventListener("abort", onAbort);
21
- action();
22
- };
23
- const onAbort = () => finish(() => reject(options.signal.reason ?? new Error("The operation was aborted.")));
24
- const timeout = setTimeout(() => finish(() => resolve(undefined)), remaining);
25
- options.signal.addEventListener("abort", onAbort, { once: true });
26
- if (options.signal.aborted) {
27
- onAbort();
28
- return;
29
- }
30
- void options.api.getNodeBoundingBox(options.objectId).then((bounds) => finish(() => resolve(bounds)), () => finish(() => resolve(undefined)));
31
- });
32
- }
33
- function waitForPoll(signal, milliseconds) {
34
- signal.throwIfAborted();
35
- return new Promise((resolve, reject) => {
36
- const onAbort = () => {
37
- clearTimeout(timeout);
38
- reject(signal.reason ?? new Error("The operation was aborted."));
39
- };
40
- const timeout = setTimeout(() => {
41
- signal.removeEventListener("abort", onAbort);
42
- resolve();
43
- }, milliseconds);
44
- signal.addEventListener("abort", onAbort, { once: true });
45
- if (signal.aborted)
46
- onAbort();
47
- });
48
- }
49
- export async function waitForOpenScadRenderer(options) {
50
- const deadline = Date.now() + (options.timeoutMs ?? OPENSCAD_RENDERER_WAIT_MS);
51
- if (options.baselineBounds !== undefined &&
52
- boundsMatch(options.baselineBounds, options.expectedBounds))
53
- return false;
54
- do {
55
- const actual = await getBoundsBeforeDeadline({
56
- api: options.api,
57
- objectId: options.objectId,
58
- signal: options.signal,
59
- deadline,
60
- });
61
- options.signal.throwIfAborted();
62
- if (Date.now() >= deadline)
63
- return false;
64
- if (actual !== undefined && boundsMatch(actual, options.expectedBounds))
65
- return true;
66
- const remaining = deadline - Date.now();
67
- if (remaining <= 0)
68
- return false;
69
- await waitForPoll(options.signal, Math.min(options.pollIntervalMs ?? OPENSCAD_RENDERER_POLL_MS, remaining));
70
- } while (true);
71
- }