@stigmer/runner 3.6.0 → 3.8.0

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 (100) hide show
  1. package/dist/.build-fingerprint +1 -1
  2. package/dist/activities/call-agent.js +85 -10
  3. package/dist/activities/call-agent.js.map +1 -1
  4. package/dist/activities/execute-cursor/index.d.ts +12 -0
  5. package/dist/activities/execute-cursor/index.js +80 -10
  6. package/dist/activities/execute-cursor/index.js.map +1 -1
  7. package/dist/activities/execute-cursor/model-pricing.d.ts +9 -0
  8. package/dist/activities/execute-cursor/model-pricing.js +19 -0
  9. package/dist/activities/execute-cursor/model-pricing.js.map +1 -1
  10. package/dist/activities/execute-cursor/prompt-builder.d.ts +11 -0
  11. package/dist/activities/execute-cursor/prompt-builder.js +11 -0
  12. package/dist/activities/execute-cursor/prompt-builder.js.map +1 -1
  13. package/dist/activities/execute-cursor/service-tier.d.ts +68 -0
  14. package/dist/activities/execute-cursor/service-tier.js +187 -0
  15. package/dist/activities/execute-cursor/service-tier.js.map +1 -0
  16. package/dist/activities/execute-cursor/session-lifecycle.d.ts +16 -1
  17. package/dist/activities/execute-cursor/session-lifecycle.js +12 -4
  18. package/dist/activities/execute-cursor/session-lifecycle.js.map +1 -1
  19. package/dist/activities/execute-cursor/usage-accumulator.d.ts +21 -1
  20. package/dist/activities/execute-cursor/usage-accumulator.js +23 -3
  21. package/dist/activities/execute-cursor/usage-accumulator.js.map +1 -1
  22. package/dist/activities/execute-deep-agent/mcp-gate.d.ts +28 -0
  23. package/dist/activities/execute-deep-agent/mcp-gate.js +22 -0
  24. package/dist/activities/execute-deep-agent/mcp-gate.js.map +1 -0
  25. package/dist/activities/execute-deep-agent/prompt-builder.d.ts +11 -0
  26. package/dist/activities/execute-deep-agent/prompt-builder.js +16 -0
  27. package/dist/activities/execute-deep-agent/prompt-builder.js.map +1 -1
  28. package/dist/activities/execute-deep-agent/setup.js +30 -4
  29. package/dist/activities/execute-deep-agent/setup.js.map +1 -1
  30. package/dist/client/stigmer-client.d.ts +6 -1
  31. package/dist/client/stigmer-client.js +5 -2
  32. package/dist/client/stigmer-client.js.map +1 -1
  33. package/dist/main.js +18 -0
  34. package/dist/main.js.map +1 -1
  35. package/dist/runner.js +48 -0
  36. package/dist/runner.js.map +1 -1
  37. package/dist/sandbox-token-renewal.d.ts +65 -0
  38. package/dist/sandbox-token-renewal.js +169 -0
  39. package/dist/sandbox-token-renewal.js.map +1 -0
  40. package/dist/shared/artifact-storage.d.ts +17 -3
  41. package/dist/shared/artifact-storage.js +22 -4
  42. package/dist/shared/artifact-storage.js.map +1 -1
  43. package/dist/shared/channel-attachment.d.ts +3 -1
  44. package/dist/shared/channel-attachment.js +3 -1
  45. package/dist/shared/channel-attachment.js.map +1 -1
  46. package/dist/shared/conversation-attachment.d.ts +81 -0
  47. package/dist/shared/conversation-attachment.js +102 -0
  48. package/dist/shared/conversation-attachment.js.map +1 -0
  49. package/dist/shared/conversation-catchup.d.ts +33 -0
  50. package/dist/shared/conversation-catchup.js +53 -0
  51. package/dist/shared/conversation-catchup.js.map +1 -0
  52. package/dist/workflow-engine/loader.js +99 -2
  53. package/dist/workflow-engine/loader.js.map +1 -1
  54. package/dist/workflow-engine/tasks/call-agent.d.ts +0 -2
  55. package/dist/workflow-engine/tasks/call-agent.js +0 -2
  56. package/dist/workflow-engine/tasks/call-agent.js.map +1 -1
  57. package/dist/workflow-engine/types.d.ts +39 -7
  58. package/dist/workflow-engine/types.js.map +1 -1
  59. package/dist/workflows/call-agent-orchestrator.d.ts +3 -2
  60. package/dist/workflows/call-agent-orchestrator.js +8 -2
  61. package/dist/workflows/call-agent-orchestrator.js.map +1 -1
  62. package/package.json +2 -2
  63. package/src/__tests__/sandbox-token-renewal.test.ts +174 -0
  64. package/src/activities/__tests__/call-agent-contracts.test.ts +4 -4
  65. package/src/activities/__tests__/call-agent.test.ts +219 -4
  66. package/src/activities/call-agent.ts +94 -10
  67. package/src/activities/execute-cursor/__tests__/build-prompt.test.ts +79 -0
  68. package/src/activities/execute-cursor/__tests__/model-pricing.test.ts +20 -0
  69. package/src/activities/execute-cursor/__tests__/service-tier.test.ts +170 -0
  70. package/src/activities/execute-cursor/__tests__/usage-accumulator.test.ts +87 -1
  71. package/src/activities/execute-cursor/index.ts +111 -11
  72. package/src/activities/execute-cursor/model-pricing.ts +23 -0
  73. package/src/activities/execute-cursor/prompt-builder.ts +23 -0
  74. package/src/activities/execute-cursor/service-tier.ts +244 -0
  75. package/src/activities/execute-cursor/session-lifecycle.ts +33 -5
  76. package/src/activities/execute-cursor/usage-accumulator.ts +35 -3
  77. package/src/activities/execute-deep-agent/__tests__/mcp-gate.test.ts +42 -0
  78. package/src/activities/execute-deep-agent/__tests__/prompt-builder.test.ts +39 -1
  79. package/src/activities/execute-deep-agent/mcp-gate.ts +37 -0
  80. package/src/activities/execute-deep-agent/prompt-builder.ts +22 -2
  81. package/src/activities/execute-deep-agent/setup.ts +40 -4
  82. package/src/client/stigmer-client.ts +11 -4
  83. package/src/main.ts +20 -0
  84. package/src/runner.ts +62 -0
  85. package/src/sandbox-token-renewal.ts +212 -0
  86. package/src/shared/__tests__/channel-attachment.test.ts +3 -3
  87. package/src/shared/__tests__/conversation-attachment.test.ts +138 -0
  88. package/src/shared/__tests__/conversation-catchup.test.ts +70 -0
  89. package/src/shared/__tests__/synthesized-attachment.test.ts +120 -0
  90. package/src/shared/artifact-storage.ts +32 -7
  91. package/src/shared/channel-attachment.ts +3 -1
  92. package/src/shared/conversation-attachment.ts +115 -0
  93. package/src/shared/conversation-catchup.ts +60 -0
  94. package/src/workflow-engine/__tests__/golden-execution.test.ts +8 -8
  95. package/src/workflow-engine/__tests__/loader.test.ts +192 -7
  96. package/src/workflow-engine/__tests__/tasks/call-agent.test.ts +9 -9
  97. package/src/workflow-engine/loader.ts +113 -2
  98. package/src/workflow-engine/tasks/call-agent.ts +0 -2
  99. package/src/workflow-engine/types.ts +40 -7
  100. package/src/workflows/call-agent-orchestrator.ts +8 -2
@@ -420,12 +420,13 @@ do:
420
420
  with:
421
421
  agent: "acme/support-triage"
422
422
  message: "Triage this ticket"
423
- org: acme
424
423
  env:
425
424
  API_KEY: "\${.secrets.KEY}"
426
- config:
427
- model: claude-3-5-sonnet
428
- timeout: 300
425
+ run_config:
426
+ model_name: claude-3-5-sonnet
427
+ max_cost_usd: 0.5
428
+ max_tool_rounds: 15
429
+ service_tier: fast
429
430
  output:
430
431
  schema:
431
432
  type: object
@@ -444,10 +445,11 @@ do:
444
445
  if (task.kind === "call:agent") {
445
446
  expect(task.with.agent).toBe("acme/support-triage");
446
447
  expect(task.with.message).toBe("Triage this ticket");
447
- expect(task.with.org).toBe("acme");
448
448
  expect(task.with.env?.API_KEY).toBe("${.secrets.KEY}");
449
- expect(task.with.config?.model).toBe("claude-3-5-sonnet");
450
- expect(task.with.config?.timeout).toBe(300);
449
+ expect(task.with.run_config?.model_name).toBe("claude-3-5-sonnet");
450
+ expect(task.with.run_config?.max_cost_usd).toBe(0.5);
451
+ expect(task.with.run_config?.max_tool_rounds).toBe(15);
452
+ expect(task.with.run_config?.service_tier).toBe("SERVICE_TIER_FAST");
451
453
  expect(task.with.output?.schema.type).toBe("object");
452
454
  expect(task.with.output?.on_invalid).toBe("ON_INVALID_RETRY");
453
455
  expect(task.with.output?.max_retries).toBe(2);
@@ -455,6 +457,189 @@ do:
455
457
  }
456
458
  });
457
459
 
460
+ it("rejects unknown run_config keys with a named error", () => {
461
+ const yaml = `
462
+ document:
463
+ dsl: '1.0.0'
464
+ name: test
465
+ do:
466
+ - invoke:
467
+ call: agent
468
+ with:
469
+ agent: reviewer
470
+ message: Review
471
+ run_config:
472
+ model: claude-sonnet-4-6
473
+ `;
474
+ // "model" is the pre-#358 key; the shared RunConfig field is
475
+ // "model_name". A typo'd knob must fail parsing, not silently no-op.
476
+ expect(() => loadWorkflowFromYaml(yaml)).toThrow(
477
+ "call:agent 'run_config' has unknown field 'model'",
478
+ );
479
+ });
480
+
481
+ it("maps service_tier shorthands and canonical names to the enum name", () => {
482
+ for (const [written, expected] of [
483
+ ["standard", "SERVICE_TIER_STANDARD"],
484
+ ["fast", "SERVICE_TIER_FAST"],
485
+ ["FAST", "SERVICE_TIER_FAST"],
486
+ ["SERVICE_TIER_STANDARD", "SERVICE_TIER_STANDARD"],
487
+ ] as const) {
488
+ const yaml = `
489
+ document:
490
+ dsl: '1.0.0'
491
+ name: test
492
+ do:
493
+ - invoke:
494
+ call: agent
495
+ with:
496
+ agent: reviewer
497
+ message: Review
498
+ run_config:
499
+ service_tier: "${written}"
500
+ `;
501
+ const model = loadWorkflowFromYaml(yaml);
502
+ const task = model.do[0].task;
503
+ if (task.kind === "call:agent") {
504
+ expect(task.with.run_config?.service_tier).toBe(expected);
505
+ } else {
506
+ throw new Error("expected call:agent task");
507
+ }
508
+ }
509
+ });
510
+
511
+ it("rejects an unknown service_tier value with a named error", () => {
512
+ const yaml = `
513
+ document:
514
+ dsl: '1.0.0'
515
+ name: test
516
+ do:
517
+ - invoke:
518
+ call: agent
519
+ with:
520
+ agent: reviewer
521
+ message: Review
522
+ run_config:
523
+ service_tier: turbo
524
+ `;
525
+ // The tier exists to make pricing deterministic — a typo must fail
526
+ // parsing, never silently fall back to the default tier.
527
+ expect(() => loadWorkflowFromYaml(yaml)).toThrow(
528
+ "call:agent 'run_config.service_tier' has unknown value 'turbo'",
529
+ );
530
+ });
531
+
532
+ it("rejects negative run_config bounds", () => {
533
+ const yaml = `
534
+ document:
535
+ dsl: '1.0.0'
536
+ name: test
537
+ do:
538
+ - invoke:
539
+ call: agent
540
+ with:
541
+ agent: reviewer
542
+ message: Review
543
+ run_config:
544
+ max_cost_usd: -1
545
+ `;
546
+ expect(() => loadWorkflowFromYaml(yaml)).toThrow(
547
+ "call:agent 'run_config.max_cost_usd' must be a number >= 0",
548
+ );
549
+ });
550
+
551
+ it("parses git workspace entries", () => {
552
+ const yaml = `
553
+ document:
554
+ dsl: '1.0.0'
555
+ name: test
556
+ do:
557
+ - invoke:
558
+ call: agent
559
+ with:
560
+ agent: reviewer
561
+ message: Review
562
+ workspace_entries:
563
+ - name: app
564
+ source:
565
+ git_repo:
566
+ url: https://github.com/acme/app
567
+ branch: main
568
+ - source:
569
+ git_repo:
570
+ url: https://github.com/acme/lib
571
+ `;
572
+ const model = loadWorkflowFromYaml(yaml);
573
+ const task = model.do[0].task;
574
+ expect(task.kind).toBe("call:agent");
575
+ if (task.kind === "call:agent") {
576
+ const entries = task.with.workspace_entries;
577
+ expect(entries).toHaveLength(2);
578
+ expect(entries?.[0].name).toBe("app");
579
+ expect(entries?.[0].source.git_repo.url).toBe("https://github.com/acme/app");
580
+ expect(entries?.[0].source.git_repo.branch).toBe("main");
581
+ expect(entries?.[1].name).toBeUndefined();
582
+ expect(entries?.[1].source.git_repo.branch).toBeUndefined();
583
+ }
584
+ });
585
+
586
+ it("rejects a workspace entry without a git url", () => {
587
+ const yaml = `
588
+ document:
589
+ dsl: '1.0.0'
590
+ name: test
591
+ do:
592
+ - invoke:
593
+ call: agent
594
+ with:
595
+ agent: reviewer
596
+ message: Review
597
+ workspace_entries:
598
+ - source:
599
+ local_path:
600
+ path: /home/me/repo
601
+ `;
602
+ expect(() => loadWorkflowFromYaml(yaml)).toThrow(
603
+ "call:agent 'workspace_entries[0]' requires 'source.git_repo.url'",
604
+ );
605
+ });
606
+
607
+ it("rejects a non-list workspace_entries", () => {
608
+ const yaml = `
609
+ document:
610
+ dsl: '1.0.0'
611
+ name: test
612
+ do:
613
+ - invoke:
614
+ call: agent
615
+ with:
616
+ agent: reviewer
617
+ message: Review
618
+ workspace_entries: "https://github.com/acme/app"
619
+ `;
620
+ expect(() => loadWorkflowFromYaml(yaml)).toThrow(
621
+ "call:agent 'workspace_entries' must be a list",
622
+ );
623
+ });
624
+
625
+ it("rejects a non-mapping run_config", () => {
626
+ const yaml = `
627
+ document:
628
+ dsl: '1.0.0'
629
+ name: test
630
+ do:
631
+ - invoke:
632
+ call: agent
633
+ with:
634
+ agent: reviewer
635
+ message: Review
636
+ run_config: "cheap"
637
+ `;
638
+ expect(() => loadWorkflowFromYaml(yaml)).toThrow(
639
+ "call:agent 'run_config' must be a mapping",
640
+ );
641
+ });
642
+
458
643
  it("normalizes harness shorthand 'native' to HARNESS_NATIVE", () => {
459
644
  const yaml = `
460
645
  document:
@@ -108,16 +108,15 @@ describe("CallAgentTaskBuilder", () => {
108
108
  expect(config.env.GITHUB_TOKEN).toBe("${.secrets.GH_TOKEN}");
109
109
  });
110
110
 
111
- it("passes org from config when specified", async () => {
111
+ it("passes org-prefixed agent references through untouched", async () => {
112
112
  mockCallAgent.mockResolvedValue({ final_text: "done" });
113
113
 
114
114
  const taskDef: CallAgentTaskDef = {
115
115
  kind: "call:agent",
116
116
  call: "agent",
117
117
  with: {
118
- agent: "my-agent",
118
+ agent: "acme/my-agent",
119
119
  message: "Hello",
120
- org: "acme",
121
120
  },
122
121
  };
123
122
 
@@ -127,7 +126,7 @@ describe("CallAgentTaskBuilder", () => {
127
126
  await executor({}, createState(), makeCtx());
128
127
 
129
128
  const [config] = mockCallAgent.mock.calls[0];
130
- expect(config.org).toBe("acme");
129
+ expect(config.agent).toBe("acme/my-agent");
131
130
  });
132
131
 
133
132
  it("shouldRun returns true", async () => {
@@ -495,7 +494,7 @@ describe("CallAgentTaskBuilder — output.schema propagation", () => {
495
494
  agent: "notification-analyst",
496
495
  message: "Generate the daily cohort analysis report.",
497
496
  output: { schema: cohortSchema, on_invalid: "ON_INVALID_FAIL" },
498
- config: { model: "claude-sonnet-4", timeout: 300 },
497
+ run_config: { model_name: "claude-sonnet-4" },
499
498
  harness: "HARNESS_CURSOR",
500
499
  },
501
500
  };
@@ -525,7 +524,7 @@ describe("CallAgentTaskBuilder — output.schema propagation", () => {
525
524
  "Date: ${ $env.NOTIFICATION_DATE }\n" +
526
525
  "Data source: decor schema.",
527
526
  output: { schema: cohortSchema, on_invalid: "ON_INVALID_FAIL" },
528
- config: { model: "claude-sonnet-4" },
527
+ run_config: { model_name: "claude-sonnet-4" },
529
528
  },
530
529
  };
531
530
 
@@ -596,7 +595,7 @@ describe("CallAgentTaskBuilder — output.schema propagation", () => {
596
595
  expect(config.output.schema.required).toEqual(["executive_summary", "cohorts", "anomalies"]);
597
596
  });
598
597
 
599
- it("preserves output.schema alongside config, harness, and env fields", async () => {
598
+ it("preserves output.schema alongside run_config, harness, and env fields", async () => {
600
599
  mockCallAgent.mockResolvedValue({ structured: { executive_summary: "ok", cohorts: [], anomalies: [] } });
601
600
 
602
601
  const taskDef: CallAgentTaskDef = {
@@ -606,7 +605,7 @@ describe("CallAgentTaskBuilder — output.schema propagation", () => {
606
605
  agent: "notification-analyst",
607
606
  message: "Analyze data.",
608
607
  env: { POSTGRES_CONNECTION_URL: "${.secrets.PG_URL}" },
609
- config: { model: "claude-sonnet-4", timeout: 300 },
608
+ run_config: { model_name: "claude-sonnet-4", max_cost_usd: 0.5 },
610
609
  output: { schema: cohortSchema, on_invalid: "ON_INVALID_FAIL" },
611
610
  harness: "HARNESS_CURSOR",
612
611
  },
@@ -618,7 +617,8 @@ describe("CallAgentTaskBuilder — output.schema propagation", () => {
618
617
 
619
618
  const [config] = mockCallAgent.mock.calls[0];
620
619
  expect(config.agent).toBe("notification-analyst");
621
- expect(config.config?.model).toBe("claude-sonnet-4");
620
+ expect(config.run_config?.model_name).toBe("claude-sonnet-4");
621
+ expect(config.run_config?.max_cost_usd).toBe(0.5);
622
622
  expect(config.env?.POSTGRES_CONNECTION_URL).toBe("${.secrets.PG_URL}");
623
623
  expect(config.harness).toBe("HARNESS_CURSOR");
624
624
  expect(config.output).toBeDefined();
@@ -489,14 +489,125 @@ function parseAgentCallConfig(raw: unknown): AgentCallConfig {
489
489
  return {
490
490
  agent: obj.agent,
491
491
  message: obj.message,
492
- org: typeof obj.org === "string" ? obj.org : undefined,
493
492
  env: obj.env as Record<string, string> | undefined,
494
- config: obj.config as AgentCallConfig["config"],
493
+ run_config: parseAgentCallRunConfig(obj.run_config),
495
494
  output: obj.output as AgentCallConfig["output"],
496
495
  harness,
496
+ workspace_entries: parseAgentCallWorkspaceEntries(obj.workspace_entries),
497
497
  };
498
498
  }
499
499
 
500
+ /**
501
+ * Parses workspace entries with structural validation. Write-time
502
+ * validation already enforces the git-only constraint, so a malformed
503
+ * entry here means a converter defect or hand-edited YAML — either way
504
+ * an error the author must hear about, not a silent skip.
505
+ */
506
+ function parseAgentCallWorkspaceEntries(
507
+ raw: unknown,
508
+ ): AgentCallConfig["workspace_entries"] {
509
+ if (raw === undefined || raw === null) return undefined;
510
+ if (!Array.isArray(raw)) {
511
+ throw new Error("call:agent 'workspace_entries' must be a list");
512
+ }
513
+
514
+ return raw.map((item, i) => {
515
+ if (!item || typeof item !== "object" || Array.isArray(item)) {
516
+ throw new Error(`call:agent 'workspace_entries[${i}]' must be a mapping`);
517
+ }
518
+ const entry = item as Record<string, unknown>;
519
+ const source = entry.source as Record<string, unknown> | undefined;
520
+ const gitRepo = source?.git_repo as Record<string, unknown> | undefined;
521
+ if (!gitRepo || typeof gitRepo.url !== "string" || !gitRepo.url) {
522
+ throw new Error(
523
+ `call:agent 'workspace_entries[${i}]' requires 'source.git_repo.url' ` +
524
+ `(the workflow surface supports git sources only)`,
525
+ );
526
+ }
527
+ return {
528
+ name: typeof entry.name === "string" && entry.name ? entry.name : undefined,
529
+ source: {
530
+ git_repo: {
531
+ url: gitRepo.url,
532
+ branch: typeof gitRepo.branch === "string" && gitRepo.branch ? gitRepo.branch : undefined,
533
+ },
534
+ },
535
+ };
536
+ });
537
+ }
538
+
539
+ /**
540
+ * Parses the shared RunConfig block with per-key validation rather than a
541
+ * bare cast: a typo'd key or mistyped value is an authoring error the
542
+ * author must hear about, not something to silently carry along.
543
+ */
544
+ function parseAgentCallRunConfig(raw: unknown): AgentCallConfig["run_config"] {
545
+ if (raw === undefined || raw === null) return undefined;
546
+ if (typeof raw !== "object" || Array.isArray(raw)) {
547
+ throw new Error("call:agent 'run_config' must be a mapping");
548
+ }
549
+ const obj = raw as Record<string, unknown>;
550
+
551
+ const known = new Set(["model_name", "max_cost_usd", "max_tool_rounds", "service_tier"]);
552
+ for (const key of Object.keys(obj)) {
553
+ if (!known.has(key)) {
554
+ throw new Error(
555
+ `call:agent 'run_config' has unknown field '${key}' ` +
556
+ `(expected: model_name, max_cost_usd, max_tool_rounds, service_tier)`,
557
+ );
558
+ }
559
+ }
560
+
561
+ const modelName = obj.model_name;
562
+ if (modelName !== undefined && typeof modelName !== "string") {
563
+ throw new Error("call:agent 'run_config.model_name' must be a string");
564
+ }
565
+ const maxCostUsd = obj.max_cost_usd;
566
+ if (maxCostUsd !== undefined && (typeof maxCostUsd !== "number" || maxCostUsd < 0)) {
567
+ throw new Error("call:agent 'run_config.max_cost_usd' must be a number >= 0");
568
+ }
569
+ const maxToolRounds = obj.max_tool_rounds;
570
+ if (maxToolRounds !== undefined && (typeof maxToolRounds !== "number" || maxToolRounds < 0)) {
571
+ throw new Error("call:agent 'run_config.max_tool_rounds' must be a number >= 0");
572
+ }
573
+ const serviceTier = parseServiceTier(obj.service_tier);
574
+
575
+ return {
576
+ model_name: modelName,
577
+ max_cost_usd: maxCostUsd,
578
+ max_tool_rounds: maxToolRounds,
579
+ service_tier: serviceTier,
580
+ };
581
+ }
582
+
583
+ /**
584
+ * Maps YAML service-tier shorthands to canonical enum names, mirroring
585
+ * HARNESS_SHORTHANDS. Unknown values are authoring errors: the tier
586
+ * exists to make pricing deterministic, so a typo must never silently
587
+ * fall back to the default.
588
+ */
589
+ const SERVICE_TIER_SHORTHANDS: Record<string, string> = {
590
+ standard: "SERVICE_TIER_STANDARD",
591
+ fast: "SERVICE_TIER_FAST",
592
+ };
593
+
594
+ function parseServiceTier(raw: unknown): string | undefined {
595
+ if (raw === undefined || raw === null) return undefined;
596
+ if (typeof raw !== "string") {
597
+ throw new Error("call:agent 'run_config.service_tier' must be a string");
598
+ }
599
+ const canonical =
600
+ SERVICE_TIER_SHORTHANDS[raw.toLowerCase()] ??
601
+ (Object.values(SERVICE_TIER_SHORTHANDS).includes(raw) ? raw : undefined);
602
+ if (!canonical) {
603
+ throw new Error(
604
+ `call:agent 'run_config.service_tier' has unknown value '${raw}' ` +
605
+ `(expected: standard, fast)`,
606
+ );
607
+ }
608
+ return canonical;
609
+ }
610
+
500
611
  function parseHumanInputConfig(taskName: string, raw: unknown): import("./types.js").HumanInputConfig {
501
612
  if (!raw || typeof raw !== "object") {
502
613
  throw new Error(`human_input task '${taskName}' requires a 'with' configuration block`);
@@ -10,8 +10,6 @@
10
10
  * This builder is sandbox-safe — it evaluates jq expressions and
11
11
  * delegates to `ctx.callAgent()`. The async completion and signal
12
12
  * logic live in the workflow-side orchestrator, not here.
13
- *
14
- * Mirrors Go's `CallAgentTaskBuilder` in `task_builder_call_agent.go`.
15
13
  */
16
14
 
17
15
  import type {
@@ -694,22 +694,55 @@ export type CallAgentFn = (
694
694
  /**
695
695
  * Agent call configuration — mirrors the proto `AgentCallTaskConfig`.
696
696
  * Parsed from the YAML `with:` block of a `call: agent` task.
697
+ *
698
+ * The agent may be referenced as "slug" (workflow's org) or "org/slug"
699
+ * (explicit org); there is no separate org field.
697
700
  */
698
701
  export interface AgentCallConfig {
699
702
  readonly agent: string;
700
703
  readonly message: string;
701
- readonly org?: string;
702
704
  readonly env?: Record<string, string>;
703
- readonly config?: AgentExecutionCallConfig;
705
+ readonly run_config?: AgentCallRunConfig;
704
706
  readonly output?: AgentCallOutputContract;
705
707
  readonly harness?: string;
708
+ readonly workspace_entries?: readonly AgentCallWorkspaceEntry[];
706
709
  }
707
710
 
708
- export interface AgentExecutionCallConfig {
709
- readonly model?: string;
710
- readonly timeout?: number;
711
- readonly temperature?: number;
712
- readonly max_cost_micros?: number;
711
+ /**
712
+ * Mirrors the shared `ai.stigmer.agentic.session.v1.WorkspaceEntry` in its
713
+ * git-only form — the workflow surface rejects local_path sources at write
714
+ * time (no client is connected to serve one when a task fires), so by the
715
+ * time YAML reaches the runner only git_repo entries exist.
716
+ *
717
+ * The task's `environment_refs` sibling never appears in execution YAML:
718
+ * the server resolves it from the Workflow row at execution create.
719
+ */
720
+ export interface AgentCallWorkspaceEntry {
721
+ readonly name?: string;
722
+ readonly source: {
723
+ readonly git_repo: {
724
+ readonly url: string;
725
+ readonly branch?: string;
726
+ };
727
+ };
728
+ }
729
+
730
+ /**
731
+ * Mirrors the shared `ai.stigmer.agentic.agentexecution.v1.RunConfig`
732
+ * (issue #358) — the same run-bound vocabulary schedules embed. Every
733
+ * field maps onto its `ExecutionConfig` namesake and is enforced by the
734
+ * runner's generic cost/tool-round guards; nothing here is decorative.
735
+ */
736
+ export interface AgentCallRunConfig {
737
+ readonly model_name?: string;
738
+ readonly max_cost_usd?: number;
739
+ readonly max_tool_rounds?: number;
740
+ /**
741
+ * Canonical ServiceTier enum name ("SERVICE_TIER_STANDARD" /
742
+ * "SERVICE_TIER_FAST"); the loader maps the YAML shorthands
743
+ * ("standard"/"fast") to these, mirroring harness.
744
+ */
745
+ readonly service_tier?: string;
713
746
  }
714
747
 
715
748
  export interface AgentCallOutputContract {
@@ -8,8 +8,9 @@
8
8
  * 4. Updating workflow approval status via local activities
9
9
  * 5. Returning the agent result when the activity completes
10
10
  *
11
- * The kernel's CallAgentTaskBuilder calls `ctx.callAgent()`, which
12
- * is wired to `orchestrateAgentCall()` in execute-serverless-workflow.ts.
11
+ * The workflow engine's CallAgentTaskBuilder (workflow-engine/tasks/
12
+ * call-agent.ts) calls `ctx.callAgent()`, which is wired to
13
+ * `orchestrateAgentCall()` in execute-serverless-workflow.ts.
13
14
  *
14
15
  * SANDBOX RULES: Only @temporalio/workflow imports, type-only imports,
15
16
  * and pure logic. No Node.js built-ins.
@@ -54,6 +55,11 @@ type AgentActivities = ReturnType<typeof createCallAgentActivities>;
54
55
  type StatusActivities = ReturnType<typeof createCallAgentStatusActivities>;
55
56
  type EventActivities = ReturnType<typeof createWorkflowEventActivities>;
56
57
 
58
+ // The 1h ceiling is a deliberate platform constant, not a user knob: the
59
+ // old AgentCallTaskConfig declared a per-task `timeout` that nothing ever
60
+ // read, and #358 deleted it rather than declaring what isn't honored. The
61
+ // real per-run bound is run_config.max_cost_usd, enforced inside the
62
+ // agent execution itself.
57
63
  const agentProxy = proxyActivities<AgentActivities>({
58
64
  startToCloseTimeout: "1h",
59
65
  retry: {