@stigmer/runner-slim 3.12.2 → 3.12.4

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.
Files changed (3) hide show
  1. package/main.js +41 -41
  2. package/package.json +6 -6
  3. package/workflow-bundle.js +85 -13
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@stigmer/runner-slim",
3
- "version": "3.12.2",
3
+ "version": "3.12.4",
4
4
  "description": "Self-contained Stigmer runner build for embedding in desktop apps — the bundle-friendly @stigmer/runner",
5
5
  "license": "Apache-2.0",
6
6
  "engines": {
@@ -16,11 +16,11 @@
16
16
  "jq-wasm": "^1.1.0-jq-1.8.1"
17
17
  },
18
18
  "optionalDependencies": {
19
- "@stigmer/runner-slim-darwin-arm64": "3.12.2",
20
- "@stigmer/runner-slim-darwin-x64": "3.12.2",
21
- "@stigmer/runner-slim-linux-x64": "3.12.2",
22
- "@stigmer/runner-slim-linux-arm64": "3.12.2",
23
- "@stigmer/runner-slim-win32-x64": "3.12.2"
19
+ "@stigmer/runner-slim-darwin-arm64": "3.12.4",
20
+ "@stigmer/runner-slim-darwin-x64": "3.12.4",
21
+ "@stigmer/runner-slim-linux-x64": "3.12.4",
22
+ "@stigmer/runner-slim-linux-arm64": "3.12.4",
23
+ "@stigmer/runner-slim-win32-x64": "3.12.4"
24
24
  },
25
25
  "keywords": [
26
26
  "stigmer",
@@ -26416,15 +26416,59 @@ class CallFunctionTaskBuilder {
26416
26416
  resolved.input = input;
26417
26417
  }
26418
26418
  const executionId = state.env.__stigmer_execution_id || undefined;
26419
- const result = await ctx.callFunction(callType, resolved, state.env, {
26420
- workflowExecutionId: executionId,
26421
- });
26419
+ const fnMeta = { workflowExecutionId: executionId };
26422
26420
  if (callType === "llm") {
26423
- return normalizeLlmOutput(result, !!resolved.response_schema);
26421
+ return this.executeLlmCall(resolved, state.env, fnMeta, ctx);
26424
26422
  }
26425
- return result;
26423
+ return ctx.callFunction(callType, resolved, state.env, fnMeta);
26426
26424
  };
26427
26425
  }
26426
+ /**
26427
+ * Executes a `call: llm` with schema-validation policy orchestration —
26428
+ * the llm twin of call-agent's output-contract loop (#686).
26429
+ *
26430
+ * With ON_INVALID_RETRY / ON_INVALID_FALLBACK the activity reports a
26431
+ * validation miss as a `parse_error` result instead of throwing; each
26432
+ * retry is its own activity invocation (visible in Temporal history)
26433
+ * re-prompting with the validation errors so the model can self-correct.
26434
+ * Exhausted retries (or immediate FALLBACK) branch to `fallback_task`
26435
+ * via the engine's flow directive; without one, the task fails — the
26436
+ * proto contract. ON_INVALID_FAIL (default) keeps the activity's
26437
+ * throwing path: one attempt, LLM_SCHEMA_VALIDATION on miss.
26438
+ */
26439
+ async executeLlmCall(resolved, env, fnMeta, ctx) {
26440
+ const hasSchema = !!resolved.response_schema;
26441
+ const onInvalid = resolved.on_invalid ?? "ON_INVALID_FAIL";
26442
+ const softHandling = hasSchema && (onInvalid === "ON_INVALID_RETRY" || onInvalid === "ON_INVALID_FALLBACK");
26443
+ // Proto: max_retries "Default: 1", meaningful only for ON_INVALID_RETRY.
26444
+ const maxRetries = onInvalid === "ON_INVALID_RETRY" ? resolved.max_retries ?? 1 : 0;
26445
+ let attempts = 0;
26446
+ let lastParseError = "";
26447
+ do {
26448
+ const attemptConfig = attempts === 0
26449
+ ? resolved
26450
+ : {
26451
+ ...resolved,
26452
+ prompt: `${resolved.prompt}\n\n[RETRY — Your previous response did not match ` +
26453
+ `the required output schema. Validation errors: ${lastParseError}. ` +
26454
+ `Please ensure your response strictly matches the JSON schema provided.]`,
26455
+ };
26456
+ const result = (await ctx.callFunction("llm", attemptConfig, env, fnMeta));
26457
+ if (!softHandling || !result.parse_error) {
26458
+ return normalizeLlmOutput(result, hasSchema);
26459
+ }
26460
+ lastParseError = result.parse_error;
26461
+ attempts++;
26462
+ } while (onInvalid === "ON_INVALID_RETRY" && attempts <= maxRetries);
26463
+ if (typeof resolved.fallback_task === "string" && resolved.fallback_task !== "") {
26464
+ return {
26465
+ __flow_directive__: resolved.fallback_task,
26466
+ validation_errors: [lastParseError],
26467
+ };
26468
+ }
26469
+ throw new Error(`LLM output validation failed after ${attempts} attempt(s) ` +
26470
+ `for task '${this.taskName}': ${lastParseError}`);
26471
+ }
26428
26472
  async shouldRun() {
26429
26473
  return true;
26430
26474
  }
@@ -28440,16 +28484,42 @@ __webpack_require__.r(__webpack_exports__);
28440
28484
  const evalProxy = (0,_temporalio_workflow__WEBPACK_IMPORTED_MODULE_0__.proxyLocalActivities)({
28441
28485
  startToCloseTimeout: "10s",
28442
28486
  });
28487
+ const CALL_PROXY_RETRY = {
28488
+ maximumAttempts: 5,
28489
+ initialInterval: "1s",
28490
+ backoffCoefficient: 2,
28491
+ maximumInterval: "1m",
28492
+ };
28443
28493
  const callProxy = (0,_temporalio_workflow__WEBPACK_IMPORTED_MODULE_0__.proxyActivities)({
28444
28494
  startToCloseTimeout: "5m",
28445
28495
  heartbeatTimeout: "30s",
28446
- retry: {
28447
- maximumAttempts: 5,
28448
- initialInterval: "1s",
28449
- backoffCoefficient: 2,
28450
- maximumInterval: "1m",
28451
- },
28496
+ retry: CALL_PROXY_RETRY,
28452
28497
  });
28498
+ /**
28499
+ * Task-config timeouts (llm_call.timeout ≤600s, http_call.timeout_seconds
28500
+ * ≤300s) can exceed or crowd the default 5m startToClose, which would kill
28501
+ * the activity before its own well-typed LLM_TIMEOUT / HTTP_CALL_TIMEOUT
28502
+ * failure fires. When a task declares a budget, proxy its call with
28503
+ * startToClose = budget + 30s so the in-activity bound always wins (#686).
28504
+ *
28505
+ * proxyActivities in workflow code is a deterministic proxy construction
28506
+ * (no Temporal commands); the memo just avoids rebuilding per call.
28507
+ */
28508
+ const timeoutAwareProxies = new Map();
28509
+ function callProxyFor(timeoutSeconds) {
28510
+ if (!timeoutSeconds || !Number.isFinite(timeoutSeconds))
28511
+ return callProxy;
28512
+ let proxy = timeoutAwareProxies.get(timeoutSeconds);
28513
+ if (!proxy) {
28514
+ proxy = (0,_temporalio_workflow__WEBPACK_IMPORTED_MODULE_0__.proxyActivities)({
28515
+ startToCloseTimeout: `${timeoutSeconds + 30}s`,
28516
+ heartbeatTimeout: "30s",
28517
+ retry: CALL_PROXY_RETRY,
28518
+ });
28519
+ timeoutAwareProxies.set(timeoutSeconds, proxy);
28520
+ }
28521
+ return proxy;
28522
+ }
28453
28523
  const runProxy = (0,_temporalio_workflow__WEBPACK_IMPORTED_MODULE_0__.proxyActivities)({
28454
28524
  startToCloseTimeout: "5m",
28455
28525
  heartbeatTimeout: "30s",
@@ -28589,9 +28659,11 @@ async function runWorkflowEngine(input, options) {
28589
28659
  : runProxy.RunShell(config),
28590
28660
  runWorkflow: (config) => (0,_run_orchestrator_js__WEBPACK_IMPORTED_MODULE_4__.orchestrateRunWorkflow)(config),
28591
28661
  awaitHumanInput: (config) => (0,_human_input_orchestrator_js__WEBPACK_IMPORTED_MODULE_5__.orchestrateHumanInput)(config),
28592
- callHttp: (config, runtimeEnv) => callProxy.CallHttp(config, runtimeEnv),
28662
+ callHttp: (config, runtimeEnv) => callProxyFor(config.timeout_seconds).CallHttp(config, runtimeEnv),
28593
28663
  callGrpc: (config, runtimeEnv) => callProxy.CallGrpc(config, runtimeEnv),
28594
- callFunction: (call, config, runtimeEnv, fnMeta) => callProxy.CallFunction(call, config, runtimeEnv, fnMeta.workflowExecutionId ?? executionId),
28664
+ callFunction: (call, config, runtimeEnv, fnMeta) => callProxyFor(call === "llm" && typeof config.timeout === "number"
28665
+ ? config.timeout
28666
+ : undefined).CallFunction(call, config, runtimeEnv, fnMeta.workflowExecutionId ?? executionId),
28595
28667
  callAgent: (config, runtimeEnv, agentMeta) => (0,_call_agent_orchestrator_js__WEBPACK_IMPORTED_MODULE_2__.orchestrateAgentCall)({
28596
28668
  config,
28597
28669
  runtimeEnv,