@deepstrike/wasm 0.2.26 → 0.2.28

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.
@@ -186,9 +186,25 @@ export declare class RuntimeRunner {
186
186
  * through the syscall trap; this driver runs each kernel-emitted batch of nodes in parallel,
187
187
  * feeds their results back, and loops until the kernel reports the workflow complete.
188
188
  */
189
+ /**
190
+ * Lower the declarative governance / attention / scheduler-budget / resource-quota / memory policies
191
+ * into a freshly-created kernel. Shared by `execute()` (full run) and `bootstrapWorkflowKernel()`
192
+ * (standalone workflow) so a DAG's node spawns are gated and quota'd exactly as a mid-run spawn.
193
+ * Must run BEFORE `start_run`. No config ⇒ native-profile defaults.
194
+ */
195
+ private applyKernelPolicies;
196
+ /**
197
+ * Bootstrap a standalone kernel for a host-driven workflow with no active parent run — the path a
198
+ * stateless handler (browser/edge worker) takes when it calls `runWorkflow(spec)` directly. Mirrors
199
+ * `execute()`'s pre-run setup (policies via `applyKernelPolicies`, then `start_run`) and records a
200
+ * best-effort `run_started` so the run is resumable. `runWorkflow` tears the kernel down afterward.
201
+ */
202
+ private bootstrapWorkflowKernel;
189
203
  runWorkflow(spec: WorkflowSpec, opts?: {
190
204
  resumedCompleted?: string[];
191
205
  resumedSubmissions?: Record<string, unknown>[][];
206
+ /** Standalone session id when bootstrapping (no active parent run). Defaults to a fresh uuid. */
207
+ sessionId?: string;
192
208
  }): Promise<{
193
209
  completed: string[];
194
210
  failed: string[];
@@ -233,7 +249,9 @@ export declare class RuntimeRunner {
233
249
  * Reads the session log, extracts completed workflow node agent_ids, and
234
250
  * calls runWorkflow with resumedCompleted so the kernel skips those nodes.
235
251
  */
236
- resumeWorkflow(spec: WorkflowSpec): Promise<{
252
+ resumeWorkflow(spec: WorkflowSpec, opts?: {
253
+ sessionId?: string;
254
+ }): Promise<{
237
255
  completed: string[];
238
256
  failed: string[];
239
257
  }>;
@@ -385,56 +385,7 @@ export class RuntimeRunner {
385
385
  : baseSpec;
386
386
  startPayload.run_spec = agentRunSpecToKernel(spec);
387
387
  }
388
- const osProfile = assertNativeProfile(this.opts.osProfile ?? "native");
389
- const attentionPolicy = this.opts.attentionPolicy ?? osProfile.attentionPolicy;
390
- const governancePolicy = this.opts.governancePolicy ?? osProfile.governancePolicy;
391
- kernelApply(runtime, this.pendingObservations, governancePolicyToKernelEvent(governancePolicy));
392
- kernelApply(runtime, this.pendingObservations, {
393
- kind: "set_attention_policy",
394
- ...(attentionPolicy.maxQueueSize !== undefined
395
- ? { max_queue_size: attentionPolicy.maxQueueSize }
396
- : {}),
397
- });
398
- if (this.opts.schedulerBudget) {
399
- kernelApply(runtime, this.pendingObservations, {
400
- kind: "set_scheduler_budget",
401
- ...(this.opts.schedulerBudget.maxWallMs !== undefined
402
- ? { max_wall_ms: this.opts.schedulerBudget.maxWallMs }
403
- : {}),
404
- });
405
- }
406
- if (this.opts.resourceQuota) {
407
- const q = this.opts.resourceQuota;
408
- kernelApply(runtime, this.pendingObservations, {
409
- kind: "set_resource_quota",
410
- quota: {
411
- ...(q.maxConcurrentSubagents !== undefined
412
- ? { max_concurrent_subagents: q.maxConcurrentSubagents }
413
- : {}),
414
- ...(q.maxSpawnDepth !== undefined ? { max_spawn_depth: q.maxSpawnDepth } : {}),
415
- ...(q.memoryWritesPerWindow !== undefined
416
- ? {
417
- memory_writes_per_window: [
418
- q.memoryWritesPerWindow.maxWrites,
419
- q.memoryWritesPerWindow.windowMs,
420
- ],
421
- }
422
- : {}),
423
- },
424
- });
425
- }
426
- if (this.opts.memoryPolicy) {
427
- const m = this.opts.memoryPolicy;
428
- kernelApply(runtime, this.pendingObservations, {
429
- kind: "set_memory_policy",
430
- ...(m.memoryPath !== undefined ? { memory_path: m.memoryPath } : {}),
431
- ...(m.staleWarningDays !== undefined ? { stale_warning_days: m.staleWarningDays } : {}),
432
- ...(m.retrievalTopK !== undefined ? { retrieval_top_k: m.retrievalTopK } : {}),
433
- ...(m.validationEnabled !== undefined ? { validation_enabled: m.validationEnabled } : {}),
434
- ...(m.maxContentBytes !== undefined ? { max_content_bytes: m.maxContentBytes } : {}),
435
- ...(m.maxNameLength !== undefined ? { max_name_length: m.maxNameLength } : {}),
436
- });
437
- }
388
+ this.applyKernelPolicies(runtime);
438
389
  // I4: pre-fetch memory into the knowledge partition before the first LLM turn (mirrors Node).
439
390
  if (!resumeMidRun && this.opts.preQueryMemory && this.opts.dreamStore && this.opts.agentId) {
440
391
  try {
@@ -1007,22 +958,124 @@ export class RuntimeRunner {
1007
958
  * through the syscall trap; this driver runs each kernel-emitted batch of nodes in parallel,
1008
959
  * feeds their results back, and loops until the kernel reports the workflow complete.
1009
960
  */
961
+ /**
962
+ * Lower the declarative governance / attention / scheduler-budget / resource-quota / memory policies
963
+ * into a freshly-created kernel. Shared by `execute()` (full run) and `bootstrapWorkflowKernel()`
964
+ * (standalone workflow) so a DAG's node spawns are gated and quota'd exactly as a mid-run spawn.
965
+ * Must run BEFORE `start_run`. No config ⇒ native-profile defaults.
966
+ */
967
+ applyKernelPolicies(runtime) {
968
+ const osProfile = assertNativeProfile(this.opts.osProfile ?? "native");
969
+ const attentionPolicy = this.opts.attentionPolicy ?? osProfile.attentionPolicy;
970
+ const governancePolicy = this.opts.governancePolicy ?? osProfile.governancePolicy;
971
+ kernelApply(runtime, this.pendingObservations, governancePolicyToKernelEvent(governancePolicy));
972
+ kernelApply(runtime, this.pendingObservations, {
973
+ kind: "set_attention_policy",
974
+ ...(attentionPolicy.maxQueueSize !== undefined
975
+ ? { max_queue_size: attentionPolicy.maxQueueSize }
976
+ : {}),
977
+ });
978
+ if (this.opts.schedulerBudget) {
979
+ kernelApply(runtime, this.pendingObservations, {
980
+ kind: "set_scheduler_budget",
981
+ ...(this.opts.schedulerBudget.maxWallMs !== undefined
982
+ ? { max_wall_ms: this.opts.schedulerBudget.maxWallMs }
983
+ : {}),
984
+ });
985
+ }
986
+ if (this.opts.resourceQuota) {
987
+ const q = this.opts.resourceQuota;
988
+ kernelApply(runtime, this.pendingObservations, {
989
+ kind: "set_resource_quota",
990
+ quota: {
991
+ ...(q.maxConcurrentSubagents !== undefined
992
+ ? { max_concurrent_subagents: q.maxConcurrentSubagents }
993
+ : {}),
994
+ ...(q.maxSpawnDepth !== undefined ? { max_spawn_depth: q.maxSpawnDepth } : {}),
995
+ ...(q.memoryWritesPerWindow !== undefined
996
+ ? {
997
+ memory_writes_per_window: [
998
+ q.memoryWritesPerWindow.maxWrites,
999
+ q.memoryWritesPerWindow.windowMs,
1000
+ ],
1001
+ }
1002
+ : {}),
1003
+ },
1004
+ });
1005
+ }
1006
+ if (this.opts.memoryPolicy) {
1007
+ const m = this.opts.memoryPolicy;
1008
+ kernelApply(runtime, this.pendingObservations, {
1009
+ kind: "set_memory_policy",
1010
+ ...(m.memoryPath !== undefined ? { memory_path: m.memoryPath } : {}),
1011
+ ...(m.staleWarningDays !== undefined ? { stale_warning_days: m.staleWarningDays } : {}),
1012
+ ...(m.retrievalTopK !== undefined ? { retrieval_top_k: m.retrievalTopK } : {}),
1013
+ ...(m.validationEnabled !== undefined ? { validation_enabled: m.validationEnabled } : {}),
1014
+ ...(m.maxContentBytes !== undefined ? { max_content_bytes: m.maxContentBytes } : {}),
1015
+ ...(m.maxNameLength !== undefined ? { max_name_length: m.maxNameLength } : {}),
1016
+ });
1017
+ }
1018
+ }
1019
+ /**
1020
+ * Bootstrap a standalone kernel for a host-driven workflow with no active parent run — the path a
1021
+ * stateless handler (browser/edge worker) takes when it calls `runWorkflow(spec)` directly. Mirrors
1022
+ * `execute()`'s pre-run setup (policies via `applyKernelPolicies`, then `start_run`) and records a
1023
+ * best-effort `run_started` so the run is resumable. `runWorkflow` tears the kernel down afterward.
1024
+ */
1025
+ async bootstrapWorkflowKernel(sessionId, spec) {
1026
+ this.interrupted = false;
1027
+ this.pendingObservations = [];
1028
+ this.pendingSpoolOutputs.clear();
1029
+ this.currentSessionId = sessionId;
1030
+ const kernel = await getKernel();
1031
+ const runtime = new kernel.KernelRuntime({
1032
+ maxTokens: this.opts.maxTokens,
1033
+ maxTurns: this.opts.maxTurns ?? 25,
1034
+ ...(this.opts.maxTotalTokens !== undefined ? { maxTotalTokens: this.opts.maxTotalTokens } : {}),
1035
+ timeoutMs: this.opts.timeoutMs !== undefined ? BigInt(this.opts.timeoutMs) : undefined,
1036
+ });
1037
+ this.activeKernel = runtime;
1038
+ const goal = `workflow:${spec.nodes.length} nodes`;
1039
+ void Promise.resolve(this.opts.sessionLog.append(sessionId, {
1040
+ kind: "run_started",
1041
+ run_id: crypto.randomUUID(),
1042
+ goal,
1043
+ criteria: [],
1044
+ ...(this.opts.agentId ? { agent_id: this.opts.agentId } : {}),
1045
+ })).catch(() => { });
1046
+ this.applyKernelPolicies(runtime);
1047
+ kernelApply(runtime, this.pendingObservations, { kind: "start_run", task: { goal, criteria: [] } });
1048
+ return runtime;
1049
+ }
1010
1050
  async runWorkflow(spec, opts) {
1011
- if (!this.activeKernel || !this.currentSessionId) {
1012
- throw new Error("runWorkflow requires an active parent run");
1051
+ // Standalone entry: with no active parent run, auto-bootstrap a kernel that owns the DAG (same
1052
+ // governance/quota policies a full run gets), drive it, then tear it down so the runner is reusable.
1053
+ // Mid-run callers keep the original in-place behavior with no teardown.
1054
+ const bootstrapped = !this.activeKernel || !this.currentSessionId;
1055
+ if (bootstrapped) {
1056
+ await this.bootstrapWorkflowKernel(opts?.sessionId ?? `wf-${crypto.randomUUID()}`, spec);
1013
1057
  }
1014
1058
  const parentSessionId = this.currentSessionId;
1015
1059
  const runtime = this.activeKernel;
1016
- const observations = kernelApply(runtime, this.pendingObservations, {
1017
- kind: "load_workflow",
1018
- spec: workflowSpecToKernel(spec),
1019
- parent_session_id: parentSessionId,
1020
- // W0-ABI resume: skip nodes already completed before an interruption.
1021
- ...(opts?.resumedCompleted && opts.resumedCompleted.length ? { resumed_completed: opts.resumedCompleted } : {}),
1022
- // R3-1: re-apply recorded runtime submissions so dynamically-appended nodes are reconstructed.
1023
- ...(opts?.resumedSubmissions && opts.resumedSubmissions.length ? { resumed_submissions: opts.resumedSubmissions } : {}),
1024
- });
1025
- return this.driveWorkflow(observations, parentSessionId, runtime);
1060
+ try {
1061
+ const observations = kernelApply(runtime, this.pendingObservations, {
1062
+ kind: "load_workflow",
1063
+ spec: workflowSpecToKernel(spec),
1064
+ parent_session_id: parentSessionId,
1065
+ // W0-ABI resume: skip nodes already completed before an interruption.
1066
+ ...(opts?.resumedCompleted && opts.resumedCompleted.length ? { resumed_completed: opts.resumedCompleted } : {}),
1067
+ // R3-1: re-apply recorded runtime submissions so dynamically-appended nodes are reconstructed.
1068
+ ...(opts?.resumedSubmissions && opts.resumedSubmissions.length ? { resumed_submissions: opts.resumedSubmissions } : {}),
1069
+ });
1070
+ return await this.driveWorkflow(observations, parentSessionId, runtime);
1071
+ }
1072
+ finally {
1073
+ if (bootstrapped) {
1074
+ this.activeKernel = null;
1075
+ this.currentSessionId = null;
1076
+ this.pendingObservations = [];
1077
+ }
1078
+ }
1026
1079
  }
1027
1080
  /**
1028
1081
  * M5/G1: bootstrap an **agent-authored** workflow ("the model writes its own harness"). Routes the
@@ -1169,14 +1222,16 @@ export class RuntimeRunner {
1169
1222
  * Reads the session log, extracts completed workflow node agent_ids, and
1170
1223
  * calls runWorkflow with resumedCompleted so the kernel skips those nodes.
1171
1224
  */
1172
- async resumeWorkflow(spec) {
1173
- if (!this.currentSessionId) {
1174
- throw new Error("resumeWorkflow requires an active parent run");
1225
+ async resumeWorkflow(spec, opts) {
1226
+ // Standalone resume: a stateless handler passes the prior `sessionId`; mid-run callers omit it.
1227
+ const sessionId = opts?.sessionId ?? this.currentSessionId;
1228
+ if (!sessionId) {
1229
+ throw new Error("resumeWorkflow requires an active parent run or an explicit sessionId");
1175
1230
  }
1176
- const events = await this.opts.sessionLog.read(this.currentSessionId);
1231
+ const events = await this.opts.sessionLog.read(sessionId);
1177
1232
  const resumedCompleted = recoverCompletedWorkflowNodes(events);
1178
1233
  const resumedSubmissions = recoverSubmittedWorkflowNodes(events);
1179
- return this.runWorkflow(spec, { resumedCompleted, resumedSubmissions });
1234
+ return this.runWorkflow(spec, { resumedCompleted, resumedSubmissions, sessionId });
1180
1235
  }
1181
1236
  async appendObservations(sessionId, runtime, nextArchiveStart) {
1182
1237
  const turn = runtime.turn();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@deepstrike/wasm",
3
- "version": "0.2.26",
3
+ "version": "0.2.28",
4
4
  "description": "DeepStrike WASM SDK — browser, Cloudflare Workers, Deno Deploy",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -15,7 +15,7 @@
15
15
  "test": "node --experimental-vm-modules node_modules/.bin/jest"
16
16
  },
17
17
  "dependencies": {
18
- "@deepstrike/wasm-kernel": "0.2.26"
18
+ "@deepstrike/wasm-kernel": "0.2.28"
19
19
  },
20
20
  "devDependencies": {
21
21
  "@types/jest": "^30.0.0",