@stigmer/runner 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 (125) hide show
  1. package/dist/.build-fingerprint +1 -1
  2. package/dist/activities/call-http.js +12 -0
  3. package/dist/activities/call-http.js.map +1 -1
  4. package/dist/activities/call-llm.d.ts +18 -0
  5. package/dist/activities/call-llm.js +56 -2
  6. package/dist/activities/call-llm.js.map +1 -1
  7. package/dist/activities/execute-cursor/agent-session-cache.d.ts +72 -0
  8. package/dist/activities/execute-cursor/agent-session-cache.js +186 -0
  9. package/dist/activities/execute-cursor/agent-session-cache.js.map +1 -0
  10. package/dist/activities/execute-cursor/index.js +61 -28
  11. package/dist/activities/execute-cursor/index.js.map +1 -1
  12. package/dist/activities/execute-cursor/service-tier.d.ts +5 -15
  13. package/dist/activities/execute-cursor/service-tier.js +5 -21
  14. package/dist/activities/execute-cursor/service-tier.js.map +1 -1
  15. package/dist/activities/execute-cursor/skill-resolver.d.ts +15 -0
  16. package/dist/activities/execute-cursor/skill-resolver.js +45 -7
  17. package/dist/activities/execute-cursor/skill-resolver.js.map +1 -1
  18. package/dist/activities/execute-deep-agent/__test-utils__/scripted-model.d.ts +9 -1
  19. package/dist/activities/execute-deep-agent/__test-utils__/scripted-model.js +13 -2
  20. package/dist/activities/execute-deep-agent/__test-utils__/scripted-model.js.map +1 -1
  21. package/dist/activities/execute-deep-agent/setup.js +14 -0
  22. package/dist/activities/execute-deep-agent/setup.js.map +1 -1
  23. package/dist/activities/execute-deep-agent/shell-env.d.ts +8 -5
  24. package/dist/activities/execute-deep-agent/shell-env.js +10 -7
  25. package/dist/activities/execute-deep-agent/shell-env.js.map +1 -1
  26. package/dist/activities/execute-deep-agent/subagent-wiring.d.ts +5 -1
  27. package/dist/activities/execute-deep-agent/subagent-wiring.js +9 -1
  28. package/dist/activities/execute-deep-agent/subagent-wiring.js.map +1 -1
  29. package/dist/client/stigmer-client.d.ts +9 -1
  30. package/dist/client/stigmer-client.js +10 -0
  31. package/dist/client/stigmer-client.js.map +1 -1
  32. package/dist/config.js +10 -5
  33. package/dist/config.js.map +1 -1
  34. package/dist/encryption/config.js +7 -2
  35. package/dist/encryption/config.js.map +1 -1
  36. package/dist/main.js +12 -6
  37. package/dist/main.js.map +1 -1
  38. package/dist/middleware/index.d.ts +6 -5
  39. package/dist/middleware/index.js +8 -5
  40. package/dist/middleware/index.js.map +1 -1
  41. package/dist/middleware/tool-intent.d.ts +57 -0
  42. package/dist/middleware/tool-intent.js +152 -0
  43. package/dist/middleware/tool-intent.js.map +1 -0
  44. package/dist/payload-codecs.js +2 -1
  45. package/dist/payload-codecs.js.map +1 -1
  46. package/dist/runner-manager.js +20 -7
  47. package/dist/runner-manager.js.map +1 -1
  48. package/dist/runner.js +19 -6
  49. package/dist/runner.js.map +1 -1
  50. package/dist/shared/fingerprint-secret.d.ts +3 -2
  51. package/dist/shared/fingerprint-secret.js +5 -3
  52. package/dist/shared/fingerprint-secret.js.map +1 -1
  53. package/dist/shared/llm-backend.js +8 -1
  54. package/dist/shared/llm-backend.js.map +1 -1
  55. package/dist/shared/model-client.d.ts +15 -0
  56. package/dist/shared/model-client.js +57 -13
  57. package/dist/shared/model-client.js.map +1 -1
  58. package/dist/shared/registry-endpoint.d.ts +5 -0
  59. package/dist/shared/registry-endpoint.js +7 -1
  60. package/dist/shared/registry-endpoint.js.map +1 -1
  61. package/dist/shared/runner-credential-keys.d.ts +26 -1
  62. package/dist/shared/runner-credential-keys.js +34 -1
  63. package/dist/shared/runner-credential-keys.js.map +1 -1
  64. package/dist/shared/runner-credential-store.d.ts +77 -0
  65. package/dist/shared/runner-credential-store.js +111 -0
  66. package/dist/shared/runner-credential-store.js.map +1 -0
  67. package/dist/shared/service-tier.d.ts +55 -0
  68. package/dist/shared/service-tier.js +67 -0
  69. package/dist/shared/service-tier.js.map +1 -0
  70. package/dist/shared/skill-writer.js +2 -2
  71. package/dist/shared/skill-writer.js.map +1 -1
  72. package/dist/shared/zip-extract.d.ts +10 -3
  73. package/dist/shared/zip-extract.js +10 -3
  74. package/dist/shared/zip-extract.js.map +1 -1
  75. package/dist/workflow-engine/tasks/call-function.d.ts +14 -0
  76. package/dist/workflow-engine/tasks/call-function.js +49 -5
  77. package/dist/workflow-engine/tasks/call-function.js.map +1 -1
  78. package/dist/workflow-engine/types.d.ts +6 -0
  79. package/dist/workflow-engine/types.js.map +1 -1
  80. package/dist/workflows/engine-core.js +36 -8
  81. package/dist/workflows/engine-core.js.map +1 -1
  82. package/package.json +2 -2
  83. package/src/activities/__tests__/call-http.test.ts +36 -0
  84. package/src/activities/__tests__/call-llm.test.ts +77 -0
  85. package/src/activities/call-http.ts +17 -0
  86. package/src/activities/call-llm.ts +78 -2
  87. package/src/activities/execute-cursor/__tests__/agent-session-cache.test.ts +220 -0
  88. package/src/activities/execute-cursor/__tests__/service-tier.test.ts +1 -1
  89. package/src/activities/execute-cursor/__tests__/skill-resolver.test.ts +104 -1
  90. package/src/activities/execute-cursor/agent-session-cache.ts +229 -0
  91. package/src/activities/execute-cursor/index.ts +66 -20
  92. package/src/activities/execute-cursor/service-tier.ts +5 -29
  93. package/src/activities/execute-cursor/skill-resolver.ts +52 -8
  94. package/src/activities/execute-deep-agent/__test-utils__/scripted-model.ts +13 -2
  95. package/src/activities/execute-deep-agent/__tests__/subagent-wiring.test.ts +21 -20
  96. package/src/activities/execute-deep-agent/setup.ts +15 -0
  97. package/src/activities/execute-deep-agent/shell-env.ts +10 -7
  98. package/src/activities/execute-deep-agent/subagent-wiring.ts +10 -1
  99. package/src/client/stigmer-client.ts +12 -1
  100. package/src/config.ts +10 -5
  101. package/src/encryption/config.ts +8 -2
  102. package/src/main.ts +16 -6
  103. package/src/middleware/__tests__/tool-intent.test.ts +266 -0
  104. package/src/middleware/index.ts +9 -5
  105. package/src/middleware/tool-intent.ts +174 -0
  106. package/src/payload-codecs.ts +2 -1
  107. package/src/runner-manager.ts +29 -6
  108. package/src/runner.ts +25 -6
  109. package/src/shared/__tests__/model-client.test.ts +99 -0
  110. package/src/shared/__tests__/runner-credential-store.test.ts +155 -0
  111. package/src/shared/__tests__/zip-extract.test.ts +46 -11
  112. package/src/shared/fingerprint-secret.ts +5 -3
  113. package/src/shared/llm-backend.ts +7 -1
  114. package/src/shared/model-client.ts +76 -13
  115. package/src/shared/registry-endpoint.ts +9 -1
  116. package/src/shared/runner-credential-keys.ts +36 -1
  117. package/src/shared/runner-credential-store.ts +115 -0
  118. package/src/shared/service-tier.ts +78 -0
  119. package/src/shared/skill-writer.ts +2 -2
  120. package/src/shared/zip-extract.ts +14 -7
  121. package/src/workflow-engine/__tests__/golden-execution.test.ts +20 -1
  122. package/src/workflow-engine/__tests__/tasks/call-function.test.ts +105 -0
  123. package/src/workflow-engine/tasks/call-function.ts +74 -13
  124. package/src/workflow-engine/types.ts +6 -0
  125. package/src/workflows/engine-core.ts +39 -8
@@ -1,7 +1,14 @@
1
1
  /**
2
- * Skill-artifact ZIP extraction: text-decoded entries with non-fatal
2
+ * Skill-artifact ZIP extraction: byte-preserving entries with non-fatal
3
3
  * structural failure.
4
4
  *
5
+ * Entries are returned as raw bytes, never decoded: skill artifacts carry
6
+ * binary assets (images, fonts, archives) alongside text, and a UTF-8
7
+ * decode/encode round-trip silently corrupts anything that isn't valid
8
+ * UTF-8 (issue #683). No consumer parses entry content as text — SKILL.md
9
+ * is excluded from extraction on every path and written from the Skill
10
+ * spec instead — so there is deliberately no text accessor to misuse.
11
+ *
5
12
  * Structural parsing lives in zip-structure.ts (central-directory-based —
6
13
  * see that module's doc for why local-header walks are never acceptable,
7
14
  * issue #450). This module owns the skill-artifact *policy* on top of it:
@@ -33,8 +40,8 @@ import { EOCD_MIN_SIZE, parseZipStructure, type ZipStructuralEntry } from "./zip
33
40
  export interface ZipFileEntry {
34
41
  /** Relative path within the archive (forward-slash separated). */
35
42
  readonly path: string;
36
- /** Decoded text content of the file. */
37
- readonly content: string;
43
+ /** Raw file bytes, exactly as stored in the archive. */
44
+ readonly content: Uint8Array;
38
45
  }
39
46
 
40
47
  /**
@@ -91,17 +98,17 @@ function isExcluded(name: string, excludeSet: ReadonlySet<string>): boolean {
91
98
 
92
99
  // ─── Decompression ───────────────────────────────────────────────────────
93
100
 
94
- async function decompressEntry(entry: ZipStructuralEntry): Promise<string> {
101
+ async function decompressEntry(entry: ZipStructuralEntry): Promise<Uint8Array> {
95
102
  if (entry.compressionMethod === 0) {
96
- return new TextDecoder().decode(entry.compressedData);
103
+ return entry.compressedData;
97
104
  }
98
105
 
99
106
  if (entry.compressionMethod === 8) {
100
- return new Promise<string>((resolve, reject) => {
107
+ return new Promise<Uint8Array>((resolve, reject) => {
101
108
  const inflate = createInflateRaw();
102
109
  const chunks: Buffer[] = [];
103
110
  inflate.on("data", (chunk: Buffer) => chunks.push(chunk));
104
- inflate.on("end", () => resolve(Buffer.concat(chunks).toString("utf-8")));
111
+ inflate.on("end", () => resolve(Buffer.concat(chunks)));
105
112
  inflate.on("error", reject);
106
113
  inflate.end(Buffer.from(entry.compressedData));
107
114
  });
@@ -523,8 +523,27 @@ describe("Golden Execution — Tier 1d: Advanced Tasks", () => {
523
523
 
524
524
  await executeDoTasks(model.do, null, state, model, evaluateExpressionBatch, ctx);
525
525
 
526
- expect(mockCallFunction).toHaveBeenCalledTimes(2);
526
+ expect(mockCallFunction).toHaveBeenCalledTimes(3);
527
527
  expect(state.data.events_emitted).toBe(true);
528
+
529
+ // Delivery targets authored in the DSL (oss#530) must reach the
530
+ // function-call boundary verbatim: the runner's emit activity
531
+ // discriminates each entry structurally on its webhook/signal key.
532
+ const deliveryConfig = mockCallFunction.mock.calls[2][1] as Record<string, unknown>;
533
+ expect(deliveryConfig.delivery).toEqual([
534
+ {
535
+ webhook: {
536
+ url: "https://hooks.acme.com/orders",
537
+ headers: { "X-Source": "stigmer" },
538
+ },
539
+ },
540
+ {
541
+ signal: {
542
+ execution_id: "wfx_shipping_001",
543
+ signal_name: "order-fulfilled",
544
+ },
545
+ },
546
+ ]);
528
547
  });
529
548
 
530
549
  it("#20 human-input — HITL approval gates with timeout policies", async () => {
@@ -208,6 +208,111 @@ describe("CallFunctionTaskBuilder", () => {
208
208
  // The config resolver must never pre-evaluate them — the pre-fix
209
209
  // behavior substituted the evaluated boolean back into the config and
210
210
  // crashed the validate activity with `expr.includes is not a function`.
211
+ describe("llm on_invalid policy orchestration (#686)", () => {
212
+ const schemaTaskDef = (extra: Record<string, unknown>): CallFunctionTaskDef => ({
213
+ kind: "call:function",
214
+ call: "llm",
215
+ with: {
216
+ model: "gpt-4o-mini",
217
+ prompt: "Classify",
218
+ response_schema: { type: "object", properties: { answer: { type: "number" } } },
219
+ ...extra,
220
+ },
221
+ });
222
+
223
+ const invalidResult = {
224
+ result: undefined, model: "gpt-4o-mini", provider: "openai",
225
+ input_tokens: 0, output_tokens: 0, parse_error: "answer: Required",
226
+ };
227
+ const validResult = {
228
+ result: { answer: 42 }, model: "gpt-4o-mini", provider: "openai",
229
+ input_tokens: 10, output_tokens: 5,
230
+ };
231
+
232
+ it("ON_INVALID_RETRY re-prompts with the validation errors and succeeds", async () => {
233
+ mockCallFunction
234
+ .mockResolvedValueOnce(invalidResult)
235
+ .mockResolvedValueOnce(validResult);
236
+
237
+ const builder = new CallFunctionTaskBuilder(
238
+ "classify", schemaTaskDef({ on_invalid: "ON_INVALID_RETRY", max_retries: 2 }),
239
+ );
240
+ const result = await builder.build()(null, createState(), makeCtx());
241
+
242
+ expect(result).toMatchObject({ structured: { answer: 42 } });
243
+ expect(mockCallFunction).toHaveBeenCalledTimes(2);
244
+ const retryConfig = mockCallFunction.mock.calls[1][1] as Record<string, unknown>;
245
+ expect(retryConfig.prompt).toContain("Classify");
246
+ expect(retryConfig.prompt).toContain("answer: Required");
247
+ expect(retryConfig.prompt).toContain("RETRY");
248
+ });
249
+
250
+ it("ON_INVALID_RETRY exhausts max_retries then fails without a fallback_task", async () => {
251
+ mockCallFunction.mockResolvedValue(invalidResult);
252
+
253
+ const builder = new CallFunctionTaskBuilder(
254
+ "classify", schemaTaskDef({ on_invalid: "ON_INVALID_RETRY", max_retries: 2 }),
255
+ );
256
+
257
+ await expect(builder.build()(null, createState(), makeCtx()))
258
+ .rejects.toThrow(/validation failed after 3 attempt\(s\).*answer: Required/);
259
+ // first attempt + max_retries retries
260
+ expect(mockCallFunction).toHaveBeenCalledTimes(3);
261
+ });
262
+
263
+ it("ON_INVALID_RETRY defaults max_retries to 1 (proto contract)", async () => {
264
+ mockCallFunction.mockResolvedValue(invalidResult);
265
+
266
+ const builder = new CallFunctionTaskBuilder(
267
+ "classify", schemaTaskDef({ on_invalid: "ON_INVALID_RETRY" }),
268
+ );
269
+
270
+ await expect(builder.build()(null, createState(), makeCtx())).rejects.toThrow();
271
+ expect(mockCallFunction).toHaveBeenCalledTimes(2);
272
+ });
273
+
274
+ it("exhausted retries branch to fallback_task when set", async () => {
275
+ mockCallFunction.mockResolvedValue(invalidResult);
276
+
277
+ const builder = new CallFunctionTaskBuilder(
278
+ "classify",
279
+ schemaTaskDef({ on_invalid: "ON_INVALID_RETRY", max_retries: 1, fallback_task: "human_review" }),
280
+ );
281
+ const result = await builder.build()(null, createState(), makeCtx());
282
+
283
+ expect(result).toEqual({
284
+ __flow_directive__: "human_review",
285
+ validation_errors: ["answer: Required"],
286
+ });
287
+ expect(mockCallFunction).toHaveBeenCalledTimes(2);
288
+ });
289
+
290
+ it("ON_INVALID_FALLBACK branches immediately without retrying", async () => {
291
+ mockCallFunction.mockResolvedValue(invalidResult);
292
+
293
+ const builder = new CallFunctionTaskBuilder(
294
+ "classify",
295
+ schemaTaskDef({ on_invalid: "ON_INVALID_FALLBACK", fallback_task: "human_review" }),
296
+ );
297
+ const result = await builder.build()(null, createState(), makeCtx());
298
+
299
+ expect(result).toMatchObject({ __flow_directive__: "human_review" });
300
+ expect(mockCallFunction).toHaveBeenCalledTimes(1);
301
+ });
302
+
303
+ it("a parse_error without a soft policy passes through as normal output (activity owns the failure)", async () => {
304
+ // Default policy: the activity throws LLM_SCHEMA_VALIDATION itself and
305
+ // never returns parse_error — the engine must not add a second layer.
306
+ mockCallFunction.mockResolvedValue(validResult);
307
+
308
+ const builder = new CallFunctionTaskBuilder("classify", schemaTaskDef({}));
309
+ const result = await builder.build()(null, createState(), makeCtx());
310
+
311
+ expect(result).toMatchObject({ structured: { answer: 42 } });
312
+ expect(mockCallFunction).toHaveBeenCalledTimes(1);
313
+ });
314
+ });
315
+
211
316
  describe("deferred expression fields", () => {
212
317
  it("passes validate rules[].expression through unresolved while input and message interpolate", async () => {
213
318
  mockCallFunction.mockResolvedValue({ valid: true, errors: [] });
@@ -162,26 +162,87 @@ export class CallFunctionTaskBuilder implements TaskBuilder {
162
162
  const executionId =
163
163
  (state.env.__stigmer_execution_id as string | undefined) || undefined;
164
164
 
165
- const result = await ctx.callFunction(
166
- callType,
167
- resolved,
168
- state.env,
169
- {
170
- workflowExecutionId: executionId,
171
- },
172
- );
165
+ const fnMeta = { workflowExecutionId: executionId };
173
166
 
174
167
  if (callType === "llm") {
175
- return normalizeLlmOutput(
176
- result as LlmActivityResult,
177
- !!resolved.response_schema,
178
- );
168
+ return this.executeLlmCall(resolved, state.env, fnMeta, ctx);
179
169
  }
180
170
 
181
- return result;
171
+ return ctx.callFunction(callType, resolved, state.env, fnMeta);
182
172
  };
183
173
  }
184
174
 
175
+ /**
176
+ * Executes a `call: llm` with schema-validation policy orchestration —
177
+ * the llm twin of call-agent's output-contract loop (#686).
178
+ *
179
+ * With ON_INVALID_RETRY / ON_INVALID_FALLBACK the activity reports a
180
+ * validation miss as a `parse_error` result instead of throwing; each
181
+ * retry is its own activity invocation (visible in Temporal history)
182
+ * re-prompting with the validation errors so the model can self-correct.
183
+ * Exhausted retries (or immediate FALLBACK) branch to `fallback_task`
184
+ * via the engine's flow directive; without one, the task fails — the
185
+ * proto contract. ON_INVALID_FAIL (default) keeps the activity's
186
+ * throwing path: one attempt, LLM_SCHEMA_VALIDATION on miss.
187
+ */
188
+ private async executeLlmCall(
189
+ resolved: Record<string, unknown>,
190
+ env: Record<string, unknown>,
191
+ fnMeta: { workflowExecutionId?: string },
192
+ ctx: Parameters<TaskExecutorFn>[2],
193
+ ): Promise<unknown> {
194
+ const hasSchema = !!resolved.response_schema;
195
+ const onInvalid = (resolved.on_invalid as string | undefined) ?? "ON_INVALID_FAIL";
196
+ const softHandling =
197
+ hasSchema && (onInvalid === "ON_INVALID_RETRY" || onInvalid === "ON_INVALID_FALLBACK");
198
+
199
+ // Proto: max_retries "Default: 1", meaningful only for ON_INVALID_RETRY.
200
+ const maxRetries =
201
+ onInvalid === "ON_INVALID_RETRY" ? (resolved.max_retries as number | undefined) ?? 1 : 0;
202
+
203
+ let attempts = 0;
204
+ let lastParseError = "";
205
+
206
+ do {
207
+ const attemptConfig =
208
+ attempts === 0
209
+ ? resolved
210
+ : {
211
+ ...resolved,
212
+ prompt:
213
+ `${resolved.prompt}\n\n[RETRY — Your previous response did not match ` +
214
+ `the required output schema. Validation errors: ${lastParseError}. ` +
215
+ `Please ensure your response strictly matches the JSON schema provided.]`,
216
+ };
217
+
218
+ const result = (await ctx.callFunction(
219
+ "llm",
220
+ attemptConfig,
221
+ env,
222
+ fnMeta,
223
+ )) as LlmActivityResult;
224
+
225
+ if (!softHandling || !result.parse_error) {
226
+ return normalizeLlmOutput(result, hasSchema);
227
+ }
228
+
229
+ lastParseError = result.parse_error;
230
+ attempts++;
231
+ } while (onInvalid === "ON_INVALID_RETRY" && attempts <= maxRetries);
232
+
233
+ if (typeof resolved.fallback_task === "string" && resolved.fallback_task !== "") {
234
+ return {
235
+ __flow_directive__: resolved.fallback_task,
236
+ validation_errors: [lastParseError],
237
+ };
238
+ }
239
+
240
+ throw new Error(
241
+ `LLM output validation failed after ${attempts} attempt(s) ` +
242
+ `for task '${this.taskName}': ${lastParseError}`,
243
+ );
244
+ }
245
+
185
246
  async shouldRun(): Promise<boolean> {
186
247
  return true;
187
248
  }
@@ -336,6 +336,12 @@ export interface HttpCallConfig {
336
336
  readonly query?: Record<string, string>;
337
337
  readonly output?: string;
338
338
  readonly redirect?: string;
339
+ /**
340
+ * Request timeout in seconds (proto: HttpCallTaskConfig.timeout_seconds,
341
+ * 1-300). Aborts the request and fails the task with HTTP_CALL_TIMEOUT;
342
+ * the engine also widens the activity's startToClose to fit it (#686).
343
+ */
344
+ readonly timeout_seconds?: number;
339
345
  }
340
346
 
341
347
  export type EndpointDef = string | { readonly uri: string; readonly authentication?: Record<string, unknown> };
@@ -67,17 +67,44 @@ const evalProxy = proxyLocalActivities<EvalActivities>({
67
67
  startToCloseTimeout: "10s",
68
68
  });
69
69
 
70
+ const CALL_PROXY_RETRY = {
71
+ maximumAttempts: 5,
72
+ initialInterval: "1s",
73
+ backoffCoefficient: 2,
74
+ maximumInterval: "1m",
75
+ } as const;
76
+
70
77
  const callProxy = proxyActivities<HttpActivities & GrpcActivities & FunctionActivities>({
71
78
  startToCloseTimeout: "5m",
72
79
  heartbeatTimeout: "30s",
73
- retry: {
74
- maximumAttempts: 5,
75
- initialInterval: "1s",
76
- backoffCoefficient: 2,
77
- maximumInterval: "1m",
78
- },
80
+ retry: CALL_PROXY_RETRY,
79
81
  });
80
82
 
83
+ /**
84
+ * Task-config timeouts (llm_call.timeout ≤600s, http_call.timeout_seconds
85
+ * ≤300s) can exceed or crowd the default 5m startToClose, which would kill
86
+ * the activity before its own well-typed LLM_TIMEOUT / HTTP_CALL_TIMEOUT
87
+ * failure fires. When a task declares a budget, proxy its call with
88
+ * startToClose = budget + 30s so the in-activity bound always wins (#686).
89
+ *
90
+ * proxyActivities in workflow code is a deterministic proxy construction
91
+ * (no Temporal commands); the memo just avoids rebuilding per call.
92
+ */
93
+ const timeoutAwareProxies = new Map<number, typeof callProxy>();
94
+ function callProxyFor(timeoutSeconds: number | undefined): typeof callProxy {
95
+ if (!timeoutSeconds || !Number.isFinite(timeoutSeconds)) return callProxy;
96
+ let proxy = timeoutAwareProxies.get(timeoutSeconds);
97
+ if (!proxy) {
98
+ proxy = proxyActivities<HttpActivities & GrpcActivities & FunctionActivities>({
99
+ startToCloseTimeout: `${timeoutSeconds + 30}s`,
100
+ heartbeatTimeout: "30s",
101
+ retry: CALL_PROXY_RETRY,
102
+ });
103
+ timeoutAwareProxies.set(timeoutSeconds, proxy);
104
+ }
105
+ return proxy;
106
+ }
107
+
81
108
  const runProxy = proxyActivities<RunActivities>({
82
109
  startToCloseTimeout: "5m",
83
110
  heartbeatTimeout: "30s",
@@ -240,7 +267,7 @@ export async function runWorkflowEngine(
240
267
  runWorkflow: (config: RunWorkflowExecutionConfig) => orchestrateRunWorkflow(config),
241
268
  awaitHumanInput: (config: HumanInputExecutionConfig) => orchestrateHumanInput(config),
242
269
  callHttp: (config: HttpCallConfig, runtimeEnv: Record<string, unknown>) =>
243
- callProxy.CallHttp(config, runtimeEnv),
270
+ callProxyFor(config.timeout_seconds).CallHttp(config, runtimeEnv),
244
271
  callGrpc: (config: GrpcCallConfig, runtimeEnv: Record<string, unknown>) =>
245
272
  callProxy.CallGrpc(config, runtimeEnv),
246
273
  callFunction: (
@@ -249,7 +276,11 @@ export async function runWorkflowEngine(
249
276
  runtimeEnv: Record<string, unknown>,
250
277
  fnMeta: CallFunctionMetadata,
251
278
  ) =>
252
- callProxy.CallFunction(
279
+ callProxyFor(
280
+ call === "llm" && typeof config.timeout === "number"
281
+ ? config.timeout
282
+ : undefined,
283
+ ).CallFunction(
253
284
  call,
254
285
  config,
255
286
  runtimeEnv,