@rowan-agent/agent 0.5.6 → 0.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.
Files changed (4) hide show
  1. package/README.md +338 -326
  2. package/dist/index.d.ts +758 -914
  3. package/dist/index.js +2551 -557
  4. package/package.json +4 -5
package/dist/index.js CHANGED
@@ -380,9 +380,9 @@ function createExtensionAPI(hooks, _extensionPath, options, runtime, eventBus) {
380
380
  }
381
381
 
382
382
  // src/harness/phases/loader.ts
383
- import { existsSync as existsSync2, statSync } from "fs";
383
+ import { existsSync, statSync } from "fs";
384
384
  import { readdir } from "fs/promises";
385
- import { dirname as dirname2, join as join2, resolve } from "path";
385
+ import { dirname as dirname2, join, resolve } from "path";
386
386
 
387
387
  // src/harness/loader.ts
388
388
  import { readFile } from "fs/promises";
@@ -447,181 +447,8 @@ function inferResourceName(path, markerFile) {
447
447
  return extension ? file.slice(0, -extension.length) : file;
448
448
  }
449
449
 
450
- // src/harness/config.ts
451
- import { existsSync } from "fs";
452
- import { readFile as readFile2 } from "fs/promises";
453
- import { join } from "path";
454
- import { parse as parseYaml } from "yaml";
455
- import { registerModel } from "@rowan-agent/models";
456
- var DEFAULT_REASONING = false;
457
- var DEFAULT_INPUT = ["text"];
458
- var DEFAULT_CONTEXT_WINDOW = 128e3;
459
- var DEFAULT_MAX_TOKENS = 16384;
460
- var DEFAULT_COST = { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 };
461
- var DEFAULT_TIMEOUT_MS = 6e4;
462
- var ENV_VAR_PATTERN = /\$\{([^}]+)\}/g;
463
- function interpolateEnvVars(value) {
464
- return value.replace(ENV_VAR_PATTERN, (match, varName) => {
465
- const resolved = process.env[varName];
466
- if (resolved === void 0 || resolved === "") {
467
- throw new Error(
468
- `Environment variable "${varName}" is not set but is referenced in config.yaml via "${match}".`
469
- );
470
- }
471
- return resolved;
472
- });
473
- }
474
- var CONFIG_CANDIDATES = ["config.yaml", "config.yml"];
475
- async function loadConfigFile(workspace) {
476
- let configPath;
477
- let configFilename;
478
- for (const candidate of CONFIG_CANDIDATES) {
479
- const p = join(workspace.rowanDir, candidate);
480
- if (existsSync(p)) {
481
- configPath = p;
482
- configFilename = candidate;
483
- break;
484
- }
485
- }
486
- if (!configPath || !configFilename) {
487
- return void 0;
488
- }
489
- const raw = await readFile2(configPath, "utf8");
490
- let parsed;
491
- try {
492
- parsed = parseYaml(raw);
493
- } catch (error) {
494
- const message = error instanceof Error ? error.message : String(error);
495
- throw new Error(`Failed to parse .rowan/${configFilename}: ${message}`);
496
- }
497
- if (parsed === null || parsed === void 0) {
498
- return void 0;
499
- }
500
- return validateConfigFile(parsed, configFilename);
501
- }
502
- function validateConfigFile(parsed, configFilename) {
503
- if (typeof parsed !== "object" || parsed === null) {
504
- throw new Error(`.rowan/${configFilename} must be a YAML object at the top level.`);
505
- }
506
- const obj = parsed;
507
- const providers = obj.providers;
508
- if (!Array.isArray(providers)) {
509
- throw new Error(`.rowan/${configFilename} must have a "providers" array.`);
510
- }
511
- const validatedProviders = providers.map((p, i) => {
512
- if (typeof p !== "object" || p === null) {
513
- throw new Error(`providers[${i}] must be an object.`);
514
- }
515
- const provider = p;
516
- if (typeof provider.id !== "string" || !provider.id) {
517
- throw new Error(`providers[${i}].id is required and must be a non-empty string.`);
518
- }
519
- if (typeof provider.baseUrl !== "string" || !provider.baseUrl) {
520
- throw new Error(`providers[${i}].baseUrl is required and must be a non-empty string.`);
521
- }
522
- if (typeof provider.apiKey !== "string" || !provider.apiKey) {
523
- throw new Error(`providers[${i}].apiKey is required and must be a non-empty string.`);
524
- }
525
- if (typeof provider.protocol !== "string" || !provider.protocol) {
526
- throw new Error(`providers[${i}].protocol is required and must be a non-empty string.`);
527
- }
528
- if (!Array.isArray(provider.models) || provider.models.length === 0) {
529
- throw new Error(`providers[${i}].models must be a non-empty array.`);
530
- }
531
- const interpolatedApiKey = interpolateEnvVars(provider.apiKey);
532
- const validatedModels = provider.models.map((m, j) => {
533
- if (typeof m !== "object" || m === null) {
534
- throw new Error(`providers[${i}].models[${j}] must be an object.`);
535
- }
536
- const model = m;
537
- if (typeof model.id !== "string" || !model.id) {
538
- throw new Error(`providers[${i}].models[${j}].id is required and must be a non-empty string.`);
539
- }
540
- return model;
541
- });
542
- return {
543
- id: provider.id,
544
- baseUrl: provider.baseUrl,
545
- apiKey: interpolatedApiKey,
546
- protocol: provider.protocol,
547
- ...typeof provider.name === "string" ? { name: provider.name } : {},
548
- ...typeof provider.timeoutMs === "number" ? { timeoutMs: provider.timeoutMs } : {},
549
- ...typeof provider.maxRetries === "number" ? { maxRetries: provider.maxRetries } : {},
550
- ...typeof provider.retryDelayMs === "number" ? { retryDelayMs: provider.retryDelayMs } : {},
551
- ...provider.headers && typeof provider.headers === "object" ? { headers: provider.headers } : {},
552
- models: validatedModels
553
- };
554
- });
555
- const result = { providers: validatedProviders };
556
- if (obj.model && typeof obj.model === "object") {
557
- const modelRef = obj.model;
558
- if (typeof modelRef.provider !== "string" || typeof modelRef.id !== "string") {
559
- throw new Error(`.rowan/${configFilename} "model" must have "provider" and "id" string fields.`);
560
- }
561
- result.model = { provider: modelRef.provider, id: modelRef.id };
562
- }
563
- if (obj.logLevel !== void 0) {
564
- const valid = ["debug", "info", "warn", "error", "silent"];
565
- if (typeof obj.logLevel !== "string" || !valid.includes(obj.logLevel)) {
566
- throw new Error(`.rowan/${configFilename} "logLevel" must be one of: ${valid.join(", ")}.`);
567
- }
568
- result.logLevel = obj.logLevel;
569
- }
570
- return result;
571
- }
572
- function resolveDefaultModel(config) {
573
- if (config.model) {
574
- return { provider: config.model.provider, id: config.model.id };
575
- }
576
- for (const provider of config.providers) {
577
- for (const model of provider.models) {
578
- if (model.primary) {
579
- return { provider: provider.id, id: model.id };
580
- }
581
- }
582
- }
583
- for (const provider of config.providers) {
584
- if (provider.models.length > 0) {
585
- return { provider: provider.id, id: provider.models[0].id };
586
- }
587
- }
588
- return void 0;
589
- }
590
- function registerConfigModels(config) {
591
- for (const provider of config.providers) {
592
- for (const fileModel of provider.models) {
593
- const model = {
594
- id: fileModel.id,
595
- name: fileModel.name ?? fileModel.id,
596
- protocol: provider.protocol,
597
- provider: provider.id,
598
- baseUrl: provider.baseUrl,
599
- reasoning: fileModel.reasoning ?? DEFAULT_REASONING,
600
- input: fileModel.input ?? DEFAULT_INPUT,
601
- cost: { ...DEFAULT_COST, ...fileModel.cost },
602
- contextWindow: fileModel.contextWindow ?? DEFAULT_CONTEXT_WINDOW,
603
- maxTokens: fileModel.maxTokens ?? DEFAULT_MAX_TOKENS,
604
- apiKey: provider.apiKey,
605
- timeoutMs: provider.timeoutMs ?? DEFAULT_TIMEOUT_MS,
606
- ...provider.maxRetries !== void 0 ? { maxRetries: provider.maxRetries } : {},
607
- ...provider.retryDelayMs !== void 0 ? { retryDelayMs: provider.retryDelayMs } : {},
608
- ...provider.headers ? { headers: provider.headers } : {}
609
- };
610
- registerModel(model);
611
- }
612
- }
613
- }
614
- function parseModelRef(input) {
615
- if (!input) return void 0;
616
- const slashIndex = input.indexOf("/");
617
- if (slashIndex === -1) {
618
- return { provider: "*", id: input };
619
- }
620
- return {
621
- provider: input.slice(0, slashIndex),
622
- id: input.slice(slashIndex + 1)
623
- };
624
- }
450
+ // src/harness/phases/loader.ts
451
+ import { parseModelRef } from "@rowan-agent/models";
625
452
 
626
453
  // src/harness/context/resource-formatter.ts
627
454
  function escapeXml(value) {
@@ -694,10 +521,10 @@ ${indent}</${tag}>`;
694
521
  return `${indent}<${tag}>${escapeXml(String(val))}</${tag}>`;
695
522
  }).join("\n");
696
523
  }
697
- function buildPhaseDirectiveMessage(phase, output, toolUseId) {
524
+ function buildPhaseDirectiveMessage(phase, output) {
698
525
  const parts = [];
699
- parts.push(`<phase name="${escapeXml(phase.name)}">`);
700
- parts.push(` <content>${phase.content}</content>`);
526
+ parts.push(`<phase_content name="${escapeXml(phase.name)}">`);
527
+ parts.push(phase.content);
701
528
  if (output.results && output.results.length > 0) {
702
529
  parts.push(` <prev_phase_outputs>`);
703
530
  if (output.instruction) {
@@ -712,20 +539,15 @@ function buildPhaseDirectiveMessage(phase, output, toolUseId) {
712
539
  }
713
540
  parts.push(` </prev_phase_outputs>`);
714
541
  }
715
- parts.push(`</phase>`);
716
- return [{
717
- type: "tool_result",
718
- toolUseId,
719
- content: parts.join("\n"),
720
- isError: false
721
- }];
542
+ parts.push(`</phase_content>`);
543
+ return parts.join("\n");
722
544
  }
723
545
 
724
546
  // src/harness/phases/loader.ts
725
547
  var PHASE_MARKER = "PHASE.md";
726
548
  function resolvePhasePath(input) {
727
549
  const resolved = resolve(input);
728
- return existsSync2(resolved) && statSync(resolved).isDirectory() ? join2(resolved, PHASE_MARKER) : resolved;
550
+ return existsSync(resolved) && statSync(resolved).isDirectory() ? join(resolved, PHASE_MARKER) : resolved;
729
551
  }
730
552
  async function loadPhase(targetPath) {
731
553
  const resolved = resolvePhasePath(targetPath);
@@ -772,18 +594,15 @@ function readPhaseContent(phase) {
772
594
  baseDir: phase.baseDir
773
595
  });
774
596
  }
775
- function resolvePhaseLoadTarget(targetPath) {
776
- return typeof targetPath === "string" ? targetPath : join2(targetPath.rowanDir, "phases");
777
- }
778
597
  async function loadPhases(targetPath) {
779
598
  const phases = /* @__PURE__ */ new Map();
780
- const phasesDir = resolve(resolvePhaseLoadTarget(targetPath));
781
- if (existsSync2(phasesDir) && statSync(phasesDir).isFile()) {
599
+ const phasesDir = resolve(targetPath);
600
+ if (existsSync(phasesDir) && statSync(phasesDir).isFile()) {
782
601
  const phase = await loadPhase(phasesDir);
783
602
  phases.set(phase.id, phase);
784
603
  return { phases, entryPhaseId: null };
785
604
  }
786
- if (existsSync2(join2(phasesDir, PHASE_MARKER))) {
605
+ if (existsSync(join(phasesDir, PHASE_MARKER))) {
787
606
  const phase = await loadPhase(phasesDir);
788
607
  phases.set(phase.id, phase);
789
608
  return { phases, entryPhaseId: null };
@@ -791,8 +610,8 @@ async function loadPhases(targetPath) {
791
610
  const entries = await readdir(phasesDir, { withFileTypes: true });
792
611
  for (const entry of [...entries].sort((a, b) => a.name.localeCompare(b.name))) {
793
612
  if (!entry.isDirectory()) continue;
794
- const phaseFile = join2(phasesDir, entry.name, PHASE_MARKER);
795
- if (!existsSync2(phaseFile)) continue;
613
+ const phaseFile = join(phasesDir, entry.name, PHASE_MARKER);
614
+ if (!existsSync(phaseFile)) continue;
796
615
  try {
797
616
  const phase = await loadPhase(phaseFile);
798
617
  phases.set(phase.id, phase);
@@ -805,8 +624,8 @@ async function loadPhases(targetPath) {
805
624
  async function discoverPhaseCode(baseDir) {
806
625
  const candidates = ["index.ts", "index.js"];
807
626
  for (const candidate of candidates) {
808
- const fullPath = join2(baseDir, candidate);
809
- if (existsSync2(fullPath)) {
627
+ const fullPath = join(baseDir, candidate);
628
+ if (existsSync(fullPath)) {
810
629
  return fullPath;
811
630
  }
812
631
  }
@@ -815,7 +634,9 @@ async function discoverPhaseCode(baseDir) {
815
634
  async function loadPhaseCode(codePath) {
816
635
  const { createJiti: createJiti2 } = await import("jiti");
817
636
  const jiti = createJiti2(import.meta.url, {
818
- moduleCache: false
637
+ fsCache: false,
638
+ moduleCache: false,
639
+ tryNative: false
819
640
  });
820
641
  const mod = await jiti.import(codePath, { default: true });
821
642
  const fn = typeof mod === "function" ? mod : mod?.default;
@@ -827,19 +648,6 @@ async function loadPhaseCode(codePath) {
827
648
  }
828
649
  throw new Error(`Phase code at "${codePath}" must export a default function or a run() function.`);
829
650
  }
830
- async function reloadPhases(registry) {
831
- for (const [id, phase] of registry.phases) {
832
- if (!phase.filePath) {
833
- continue;
834
- }
835
- try {
836
- const fresh = await loadPhase(phase.filePath);
837
- registry.phases.set(id, fresh);
838
- } catch (error) {
839
- console.warn(`Failed to reload phase "${id}":`, error);
840
- }
841
- }
842
- }
843
651
 
844
652
  // src/harness/phases/default.ts
845
653
  var DEFAULT_PHASE_ID = "default";
@@ -855,119 +663,16 @@ function createDefaultPhase() {
855
663
  }
856
664
 
857
665
  // src/harness/tools/index.ts
858
- import { mkdir, readFile as readFile3, stat, writeFile } from "fs/promises";
859
- import { basename as basename2, dirname as dirname4, isAbsolute as isAbsolute2, relative as relative2, resolve as resolve3, sep as sep2 } from "path";
666
+ import { mkdir, readFile as readFile2, stat, writeFile } from "fs/promises";
667
+ import { basename as basename2, dirname as dirname3, isAbsolute as isAbsolute2, relative as relative2, resolve as resolve3, sep as sep2 } from "path";
860
668
  import Type2 from "typebox";
861
669
  import Schema from "typebox/schema";
862
670
 
863
- // src/harness/env/path.ts
864
- import { existsSync as existsSync3, readFileSync } from "fs";
865
- import { homedir } from "os";
866
- import { dirname as dirname3, isAbsolute, join as join3, parse, relative, resolve as resolve2, sep } from "path";
867
- var WORKSPACE_ENV = "ROWAN_WORKSPACE";
868
- var BINARY_WORKSPACE_DIR = ".rowan";
869
- function nonEmptyEnv(env, key) {
870
- const value = env[key]?.trim();
871
- return value ? value : void 0;
872
- }
873
- function resolveUserPath(path, homeDir) {
874
- if (path === "~") {
875
- return homeDir;
876
- }
877
- if (path.startsWith("~/") || path.startsWith("~\\")) {
878
- return join3(homeDir, path.slice(2));
879
- }
880
- return resolve2(path);
881
- }
882
- function isSourceWorkspaceRoot(path) {
883
- const packagePath = join3(path, "package.json");
884
- if (!existsSync3(packagePath)) {
885
- return false;
886
- }
887
- try {
888
- const manifest = JSON.parse(readFileSync(packagePath, "utf8"));
889
- return manifest.name === "rowan-agent" || Array.isArray(manifest.workspaces);
890
- } catch {
891
- return false;
892
- }
893
- }
894
- function findSourceWorkspaceRoot(startDir = process.cwd()) {
895
- let current = resolve2(startDir);
896
- const { root } = parse(current);
897
- while (true) {
898
- if (isSourceWorkspaceRoot(current)) {
899
- return current;
900
- }
901
- if (current === root) {
902
- return resolve2(startDir);
903
- }
904
- current = dirname3(current);
905
- }
906
- }
907
- function defaultWorkspaceStartDir(options) {
908
- if (options.cwd) {
909
- return options.cwd;
910
- }
911
- return process.cwd();
912
- }
913
- function resolveWorkspaceRoot(options = {}) {
914
- const env = options.env ?? process.env;
915
- const homeDir = options.homeDir ?? homedir();
916
- const override = nonEmptyEnv(env, WORKSPACE_ENV);
917
- if (override) {
918
- return resolveUserPath(override, homeDir);
919
- }
920
- return findSourceWorkspaceRoot(defaultWorkspaceStartDir(options));
921
- }
922
- function resolveWorkspacePaths(options = {}) {
923
- const cwd = resolveWorkspaceRoot(options);
924
- return {
925
- cwd,
926
- rowanDir: resolveProjectRowanDir(cwd, options.rowanDir)
927
- };
928
- }
929
- function resolveProjectRowanDir(cwd, rowanDir = BINARY_WORKSPACE_DIR) {
930
- const inputPath = rowanDir.trim() || BINARY_WORKSPACE_DIR;
931
- if (isAbsolute(inputPath)) {
932
- throw new Error(`Project Rowan dir must be a relative path: ${rowanDir}`);
933
- }
934
- return resolveWorkspacePath({ root: cwd }, inputPath).absolutePath;
935
- }
936
- function resolveInWorkspace(path, rootOrPaths) {
937
- if (path === "~" || path.startsWith("~/") || path.startsWith("~\\")) {
938
- return resolveUserPath(path, homedir());
939
- }
940
- if (isAbsolute(path)) {
941
- return path;
942
- }
943
- const root = typeof rootOrPaths === "string" ? rootOrPaths : rootOrPaths.cwd;
944
- return resolve2(root, path);
945
- }
671
+ // src/harness/path.ts
672
+ import { isAbsolute, relative, resolve as resolve2, sep } from "path";
946
673
  function normalizeRelativePath(path) {
947
674
  return path.split(sep).join("/");
948
675
  }
949
- function normalizeWorkspaceInputPath(path = ".") {
950
- const trimmed = path.trim();
951
- if (!trimmed || trimmed === "/" || trimmed === "\\") {
952
- return ".";
953
- }
954
- return path;
955
- }
956
- function resolveWorkspacePath(context, path = ".") {
957
- const root = resolve2(context.root);
958
- const inputPath = normalizeWorkspaceInputPath(path);
959
- const absolutePath = resolve2(root, inputPath);
960
- const relativePath = relative(root, absolutePath);
961
- if (relativePath === ".." || relativePath.startsWith(`..${sep}`) || isAbsolute(relativePath)) {
962
- throw new Error(`Path escapes workspace root: ${path}`);
963
- }
964
- return {
965
- root,
966
- inputPath,
967
- absolutePath,
968
- relativePath: normalizeRelativePath(relativePath || ".")
969
- };
970
- }
971
676
 
972
677
  // src/harness/tools/route-tool.ts
973
678
  import Type from "typebox";
@@ -1310,7 +1015,7 @@ function createReadTool(context) {
1310
1015
  error: `Not a file: ${resolved.relativePath}`
1311
1016
  });
1312
1017
  }
1313
- const bytes = await readFile3(resolved.absolutePath);
1018
+ const bytes = await readFile2(resolved.absolutePath);
1314
1019
  const sliced = bytes.subarray(0, maxBytes);
1315
1020
  const text = new TextDecoder().decode(sliced);
1316
1021
  const resourceType = parsed.type ?? detectResourceType(resolved.absolutePath);
@@ -1320,7 +1025,7 @@ function createReadTool(context) {
1320
1025
  const { frontmatter } = parseFrontmatter(text);
1321
1026
  const marker = resourceType === "skill" ? "SKILL.md" : "PHASE.md";
1322
1027
  name = frontmatter.name ?? inferResourceName(resolved.absolutePath, marker);
1323
- baseDir = dirname4(resolved.absolutePath);
1028
+ baseDir = dirname3(resolved.absolutePath);
1324
1029
  } else {
1325
1030
  name = inferResourceName(resolved.absolutePath, basename2(resolved.absolutePath));
1326
1031
  }
@@ -1351,7 +1056,7 @@ function createWriteTool(context) {
1351
1056
  async execute(args, toolContext) {
1352
1057
  const parsed = WriteArgsValidator.Parse(args);
1353
1058
  const resolved = resolveCoreToolPath(context, parsed.path);
1354
- await mkdir(dirname4(resolved.absolutePath), { recursive: true });
1059
+ await mkdir(dirname3(resolved.absolutePath), { recursive: true });
1355
1060
  await writeFile(resolved.absolutePath, parsed.content, "utf8");
1356
1061
  return toolResult({
1357
1062
  context: toolContext,
@@ -1388,7 +1093,7 @@ function createEditTool(context) {
1388
1093
  });
1389
1094
  }
1390
1095
  const resolved = resolveCoreToolPath(context, parsed.path);
1391
- const current = await readFile3(resolved.absolutePath, "utf8");
1096
+ const current = await readFile2(resolved.absolutePath, "utf8");
1392
1097
  if (!current.includes(parsed.oldText)) {
1393
1098
  return toolResult({
1394
1099
  context: toolContext,
@@ -1576,15 +1281,6 @@ function serializeSkills(skills) {
1576
1281
  disableModelInvocation: skill.disableModelInvocation
1577
1282
  }));
1578
1283
  }
1579
- function latestUserInput(messages) {
1580
- for (let index = messages.length - 1; index >= 0; index -= 1) {
1581
- const message = messages[index];
1582
- if (message.role === "user") {
1583
- return messageContentText(message.content);
1584
- }
1585
- }
1586
- return "";
1587
- }
1588
1284
  function conversationMessages(messages) {
1589
1285
  return messages.flatMap((message) => {
1590
1286
  if (message.role === "user") {
@@ -1665,11 +1361,17 @@ function compactMessages(messages, options = {}) {
1665
1361
  );
1666
1362
  const recentStart = Math.max(messages.length - keepRecent, firstUserIdx + 1);
1667
1363
  const oldMessages = messages.slice(firstUserIdx >= 0 ? firstUserIdx + 1 : 0, recentStart);
1668
- if (oldMessages.length < minCompact) {
1364
+ const protectedPhaseMessages = oldMessages.filter(
1365
+ (message) => message.metadata?.kind === "phase_prompt"
1366
+ );
1367
+ const summarizableMessages = oldMessages.filter(
1368
+ (message) => message.metadata?.kind !== "phase_prompt"
1369
+ );
1370
+ if (summarizableMessages.length < minCompact) {
1669
1371
  return { compacted: false, messages };
1670
1372
  }
1671
1373
  const recentMessages = messages.slice(recentStart);
1672
- const summary = buildSummary(oldMessages);
1374
+ const summary = buildSummary(summarizableMessages);
1673
1375
  const result = [];
1674
1376
  if (firstUserIdx >= 0) {
1675
1377
  result.push(messages[firstUserIdx]);
@@ -1679,14 +1381,15 @@ function compactMessages(messages, options = {}) {
1679
1381
 
1680
1382
  ${summary}`, {
1681
1383
  type: "compaction_summary",
1682
- compactedCount: oldMessages.length
1384
+ compactedCount: summarizableMessages.length
1683
1385
  })
1684
1386
  );
1387
+ result.push(...protectedPhaseMessages);
1685
1388
  result.push(...recentMessages);
1686
1389
  return {
1687
1390
  compacted: true,
1688
1391
  messages: result,
1689
- summarizedCount: oldMessages.length,
1392
+ summarizedCount: summarizableMessages.length,
1690
1393
  summary
1691
1394
  };
1692
1395
  }
@@ -1716,17 +1419,20 @@ function applyFirstDecision(route, output) {
1716
1419
  if (first.reason) output.routeReason = first.reason;
1717
1420
  if (first.payload !== void 0) output.payload = normalizePayload(first.payload);
1718
1421
  }
1719
- function injectPhaseContent(phase, output, messageManager) {
1422
+ function injectPhaseContent(phase, output, messages, mirror) {
1720
1423
  try {
1721
1424
  const phaseContent = phase.filePath ? readPhaseContent(phase) : phase.content ?? phase.description ?? "";
1722
1425
  const content = buildPhaseDirectiveMessage(
1723
1426
  { name: phase.id, content: phaseContent },
1724
- output,
1725
- `phase_${phase.id}`
1427
+ output
1726
1428
  );
1727
- const msgId = messageManager.start("tool", content, { phase: phase.id });
1728
- messageManager.end(msgId);
1729
- return msgId;
1429
+ const message = createMessage("user", content, {
1430
+ kind: "phase_prompt",
1431
+ phase: phase.id
1432
+ });
1433
+ messages.push(message);
1434
+ if (mirror && mirror !== messages) mirror.push(message);
1435
+ return message.id;
1730
1436
  } catch {
1731
1437
  return void 0;
1732
1438
  }
@@ -1889,12 +1595,16 @@ async function startPhaseLoop(config, state) {
1889
1595
  return runPhaseLoop(config, state, registry);
1890
1596
  }
1891
1597
  async function runPhaseLoop(config, state, registry) {
1892
- let currentPhaseId = registry.entryPhaseId;
1893
- let isContinuing = false;
1894
- let previousPayload = void 0;
1895
- let previousPhaseMsgId = void 0;
1896
- let previousResults = [];
1897
- let pendingInstruction = void 0;
1598
+ const resumingSuspendedRun = state.status === "suspended" && Boolean(state.currentPhase);
1599
+ let currentPhaseId = resumingSuspendedRun ? state.currentPhase : registry.entryPhaseId;
1600
+ let isContinuing = resumingSuspendedRun ? state.continuation?.isContinuing ?? true : false;
1601
+ if (resumingSuspendedRun) {
1602
+ state.status = "running";
1603
+ }
1604
+ let previousPayload = resumingSuspendedRun ? state.continuation?.previousPayload : void 0;
1605
+ let previousPhaseMsgId = resumingSuspendedRun ? state.continuation?.previousPhaseMessageId : void 0;
1606
+ let previousResults = resumingSuspendedRun ? state.continuation?.previousResults?.map((result) => ({ ...result })) ?? [] : [];
1607
+ let pendingInstruction = resumingSuspendedRun ? state.continuation?.pendingInstruction : void 0;
1898
1608
  while (currentPhaseId) {
1899
1609
  const availablePhases = [];
1900
1610
  for (const [, phase2] of registry.phases) {
@@ -1902,6 +1612,8 @@ async function runPhaseLoop(config, state, registry) {
1902
1612
  }
1903
1613
  const abortResult = LoopGuard.checkAbort(config.signal);
1904
1614
  if (abortResult.stopReason !== "none") {
1615
+ removePhaseMessage(config.context.messages, previousPhaseMsgId);
1616
+ previousPhaseMsgId = void 0;
1905
1617
  return completeRun(config, state, createOutcome.aborted());
1906
1618
  }
1907
1619
  state.metrics.iterations++;
@@ -1939,6 +1651,10 @@ async function runPhaseLoop(config, state, registry) {
1939
1651
  messages: messageManager.visible(),
1940
1652
  tools: phaseTools,
1941
1653
  skills: phaseSkills,
1654
+ invocation: {
1655
+ mode: "serial",
1656
+ instanceId: currentPhaseId
1657
+ },
1942
1658
  state: {
1943
1659
  current: currentPhaseId,
1944
1660
  available: Array.from(registry.phases.keys()),
@@ -1955,10 +1671,14 @@ async function runPhaseLoop(config, state, registry) {
1955
1671
  const extBefore = await config.beforePhase(currentPhaseId, phaseContext);
1956
1672
  if (extBefore.abort) {
1957
1673
  config.emit?.({ type: "phase_end", phase: currentPhaseId, ts: createTimestamp() });
1674
+ removePhaseMessage(config.context.messages, previousPhaseMsgId);
1675
+ previousPhaseMsgId = void 0;
1958
1676
  return completeRun(config, state, extBefore.abort);
1959
1677
  }
1960
1678
  if (extBefore.skip) {
1961
1679
  config.emit?.({ type: "phase_end", phase: currentPhaseId, ts: createTimestamp() });
1680
+ removePhaseMessage(config.context.messages, previousPhaseMsgId);
1681
+ previousPhaseMsgId = void 0;
1962
1682
  if (extBefore.skip.route === "stop") {
1963
1683
  return completeRun(config, state, {
1964
1684
  id: "skip",
@@ -1972,21 +1692,26 @@ async function runPhaseLoop(config, state, registry) {
1972
1692
  phaseContext = extBefore.input;
1973
1693
  }
1974
1694
  }
1975
- removePhaseMessage(config.context.messages, previousPhaseMsgId);
1976
- previousPhaseMsgId = void 0;
1977
- if (enteringNewPhase) {
1978
- previousPhaseMsgId = injectPhaseContent(phase, { results: previousResults, instruction: pendingInstruction }, messageManager);
1695
+ const phaseMessageIsPresent = previousPhaseMsgId !== void 0 && config.context.messages.some((message) => message.id === previousPhaseMsgId);
1696
+ if (enteringNewPhase || !phaseMessageIsPresent) {
1697
+ removePhaseMessage(config.context.messages, previousPhaseMsgId);
1698
+ removePhaseMessage(phaseContext.messages, previousPhaseMsgId);
1699
+ previousPhaseMsgId = void 0;
1700
+ previousPhaseMsgId = injectPhaseContent(
1701
+ phase,
1702
+ { results: previousResults, instruction: pendingInstruction },
1703
+ config.context.messages,
1704
+ phaseContext.messages
1705
+ );
1979
1706
  previousResults = [];
1980
1707
  pendingInstruction = void 0;
1981
1708
  }
1982
1709
  const runtime = { phase, config, state, execution, messageManager, registry, context: phaseContext };
1983
1710
  let output = await executePhase(runtime);
1984
- let routeToolCalled = false;
1985
1711
  let routeDecision;
1986
1712
  if (output.toolCalls && output.toolCalls.length > 0) {
1987
1713
  routeDecision = extractRouteCall(output.toolCalls);
1988
1714
  if (routeDecision) {
1989
- routeToolCalled = true;
1990
1715
  applyFirstDecision(routeDecision, output);
1991
1716
  }
1992
1717
  }
@@ -1994,6 +1719,8 @@ async function runPhaseLoop(config, state, registry) {
1994
1719
  const extAfter = await config.afterPhase(currentPhaseId, output);
1995
1720
  if (extAfter.abort) {
1996
1721
  config.emit?.({ type: "phase_end", phase: currentPhaseId, ts: createTimestamp() });
1722
+ removePhaseMessage(config.context.messages, previousPhaseMsgId);
1723
+ previousPhaseMsgId = void 0;
1997
1724
  return completeRun(config, state, extAfter.abort);
1998
1725
  }
1999
1726
  if (extAfter.retry && (phase.run || phase.factory)) {
@@ -2016,8 +1743,27 @@ async function runPhaseLoop(config, state, registry) {
2016
1743
  previousPhaseMsgId = void 0;
2017
1744
  return completeRun(config, state, createOutcome.aborted());
2018
1745
  }
2019
- config.emit?.({ type: "user_prompt_requested", phase: currentPhaseId, ts: createTimestamp() });
2020
- const userMessages = await config.waitForInput();
1746
+ const requestedAt = createTimestamp();
1747
+ const inputRequest = {
1748
+ phase: currentPhaseId,
1749
+ prompt: output.message,
1750
+ requestedAt
1751
+ };
1752
+ config.emit?.({
1753
+ type: "user_prompt_requested",
1754
+ phase: currentPhaseId,
1755
+ ts: requestedAt
1756
+ });
1757
+ state.status = "suspended";
1758
+ state.continuation = {
1759
+ isContinuing,
1760
+ previousPayload,
1761
+ previousResults: previousResults.map((result) => ({ ...result })),
1762
+ pendingInstruction,
1763
+ previousPhaseMessageId: previousPhaseMsgId
1764
+ };
1765
+ const userMessages = await config.waitForInput(state, inputRequest);
1766
+ state.status = "running";
2021
1767
  const abortResult2 = LoopGuard.checkAbort(config.signal);
2022
1768
  if (abortResult2.stopReason !== "none") {
2023
1769
  config.emit?.({ type: "phase_end", phase: currentPhaseId, ts: createTimestamp() });
@@ -2028,13 +1774,12 @@ async function runPhaseLoop(config, state, registry) {
2028
1774
  for (const message of userMessages) {
2029
1775
  config.context.messages.push(message);
2030
1776
  }
2031
- removePhaseMessage(config.context.messages, previousPhaseMsgId);
2032
- previousPhaseMsgId = void 0;
2033
1777
  isContinuing = true;
2034
1778
  continue;
2035
1779
  }
2036
1780
  config.emit?.({ type: "phase_end", phase: currentPhaseId, ts: createTimestamp() });
2037
1781
  removePhaseMessage(config.context.messages, previousPhaseMsgId);
1782
+ previousPhaseMsgId = void 0;
2038
1783
  return completeRun(config, state, createOutcome.default(output, config.context.messages));
2039
1784
  }
2040
1785
  if (output.route === "continue") {
@@ -2046,6 +1791,8 @@ async function runPhaseLoop(config, state, registry) {
2046
1791
  const contextSnapshot = snapshotMessages(config.context.messages);
2047
1792
  const parallelTasks = /* @__PURE__ */ new Map();
2048
1793
  const instanceIds = buildInstanceIds(routeDecision.decision.map((t) => t.phase));
1794
+ const groupId = createId("phase-group");
1795
+ const count = routeDecision.decision.length;
2049
1796
  for (let i = 0; i < routeDecision.decision.length; i++) {
2050
1797
  const target = routeDecision.decision[i];
2051
1798
  const pt = registry.phases.get(target.phase);
@@ -2053,7 +1800,20 @@ async function runPhaseLoop(config, state, registry) {
2053
1800
  const instanceId = instanceIds[i];
2054
1801
  const context = pt.isolated ? [] : contextSnapshot;
2055
1802
  const payload = target.payload !== void 0 ? normalizePayload(target.payload) : void 0;
2056
- const promise = executeParallelPhase(config, state, registry, pt, payload, context, availablePhases, currentPhaseId);
1803
+ const promise = executeParallelPhase(
1804
+ config,
1805
+ state,
1806
+ registry,
1807
+ pt,
1808
+ payload,
1809
+ context,
1810
+ availablePhases,
1811
+ instanceId,
1812
+ groupId,
1813
+ i,
1814
+ count,
1815
+ currentPhaseId
1816
+ );
2057
1817
  parallelTasks.set(instanceId, { promise, phaseId: target.phase });
2058
1818
  }
2059
1819
  const successfulResults = await waitForBackgroundTasks(parallelTasks);
@@ -2061,6 +1821,8 @@ async function runPhaseLoop(config, state, registry) {
2061
1821
  pendingInstruction = routeDecision.instruction;
2062
1822
  const entryPhaseId = phase.target ?? registry.entryPhaseId;
2063
1823
  if (entryPhaseId === "stop") {
1824
+ removePhaseMessage(config.context.messages, previousPhaseMsgId);
1825
+ previousPhaseMsgId = void 0;
2064
1826
  return completeRun(config, state, createTerminalOutcome(phase, output, config.context.messages));
2065
1827
  }
2066
1828
  currentPhaseId = entryPhaseId;
@@ -2076,12 +1838,14 @@ async function runPhaseLoop(config, state, registry) {
2076
1838
  nextRoute = "stop";
2077
1839
  }
2078
1840
  if (nextRoute === "stop") {
2079
- if (routeToolCalled) removePhaseMessage(config.context.messages, previousPhaseMsgId);
1841
+ removePhaseMessage(config.context.messages, previousPhaseMsgId);
1842
+ previousPhaseMsgId = void 0;
2080
1843
  return completeRun(config, state, createTerminalOutcome(phase, output, config.context.messages));
2081
1844
  }
2082
1845
  const targetPhaseId = nextRoute;
2083
1846
  if (!registry.phases.has(targetPhaseId)) {
2084
1847
  removePhaseMessage(config.context.messages, previousPhaseMsgId);
1848
+ previousPhaseMsgId = void 0;
2085
1849
  return completeRun(config, state, createOutcome.phaseNotFound(output));
2086
1850
  }
2087
1851
  state.metrics.phaseTransitions.push({
@@ -2298,17 +2062,25 @@ function createPhaseExecution(config, state, allTools, phase, messageManager, to
2298
2062
  }
2299
2063
  };
2300
2064
  }
2301
- async function executeParallelPhase(config, state, registry, phase, payload, context, availablePhases, currentPhaseId) {
2065
+ async function executeParallelPhase(config, state, registry, phase, payload, context, availablePhases, instanceId, groupId, index, count, sourcePhaseId) {
2302
2066
  const messages = [...context];
2303
2067
  const allTools = buildToolsWithRouting(config, availablePhases);
2304
2068
  const phaseTools = phase.tools ? allTools.filter((t) => t.name === PhaseRouteTool || phase.tools.includes(t.name)) : allTools;
2305
2069
  const phaseSkills = phase.skills ? config.context.skills.filter((s) => phase.skills.includes(s.name)) : config.context.skills;
2306
- const systemPrompt = phase.isolated ? readPhaseContent(phase) : config.context.systemPrompt;
2070
+ const systemPrompt = config.context.systemPrompt;
2307
2071
  const phaseContext = {
2308
2072
  systemPrompt,
2309
2073
  messages,
2310
2074
  tools: phaseTools,
2311
2075
  skills: phaseSkills,
2076
+ invocation: {
2077
+ mode: "parallel",
2078
+ instanceId,
2079
+ groupId,
2080
+ index,
2081
+ count,
2082
+ sourcePhaseId
2083
+ },
2312
2084
  state: {
2313
2085
  current: phase.id,
2314
2086
  available: Array.from(registry.phases.keys()),
@@ -2317,10 +2089,10 @@ async function executeParallelPhase(config, state, registry, phase, payload, con
2317
2089
  }
2318
2090
  };
2319
2091
  const messageManager = createMessageManager({ messages }, config.emit, config.onMessage);
2320
- const phaseMsgId = phase.isolated ? void 0 : injectPhaseContent(
2092
+ const phaseMsgId = injectPhaseContent(
2321
2093
  phase,
2322
- { results: payload !== void 0 ? [{ name: currentPhaseId, output: payload }] : [] },
2323
- messageManager
2094
+ { results: payload !== void 0 ? [{ name: sourcePhaseId, output: payload }] : [] },
2095
+ messages
2324
2096
  );
2325
2097
  const toolExecutionManager = createToolExecutionManager(config.emit);
2326
2098
  const execution = createPhaseExecution(config, state, phaseTools, phase, messageManager, toolExecutionManager, registry);
@@ -2329,7 +2101,7 @@ async function executeParallelPhase(config, state, registry, phase, payload, con
2329
2101
  if (phaseMsgId) removePhaseMessage(messages, phaseMsgId);
2330
2102
  const decision = output.toolCalls ? extractRouteCall(output.toolCalls) : void 0;
2331
2103
  const resultPayload = decision?.decision[0]?.payload !== void 0 ? normalizePayload(decision.decision[0].payload) : output.payload;
2332
- return { instanceId: "", phaseId: phase.id, payload: resultPayload, content: output.message };
2104
+ return { instanceId, phaseId: phase.id, payload: resultPayload, content: output.message };
2333
2105
  }
2334
2106
  async function waitForBackgroundTasks(backgroundTasks) {
2335
2107
  const entries = Array.from(backgroundTasks.entries());
@@ -2398,13 +2170,13 @@ async function completeRun2(config, state, outcome) {
2398
2170
  }
2399
2171
 
2400
2172
  // src/harness/skills.ts
2401
- import { existsSync as existsSync4, statSync as statSync2 } from "fs";
2173
+ import { existsSync as existsSync2, statSync as statSync2 } from "fs";
2402
2174
  import { readdir as readdir2 } from "fs/promises";
2403
- import { dirname as dirname5, join as join4, resolve as resolve4 } from "path";
2175
+ import { dirname as dirname4, join as join2, resolve as resolve4 } from "path";
2404
2176
  var SKILL_MARKER = "SKILL.md";
2405
2177
  function resolveSkillPath(input) {
2406
2178
  const resolved = resolve4(input);
2407
- return existsSync4(resolved) && statSync2(resolved).isDirectory() ? join4(resolved, SKILL_MARKER) : resolved;
2179
+ return existsSync2(resolved) && statSync2(resolved).isDirectory() ? join2(resolved, SKILL_MARKER) : resolved;
2408
2180
  }
2409
2181
  function readSkillContent(skill) {
2410
2182
  return formatResourceOutput({
@@ -2422,25 +2194,25 @@ async function loadSkill(path) {
2422
2194
  name: frontmatter.name ?? inferResourceName(resolved, SKILL_MARKER),
2423
2195
  description: frontmatter.description ?? "",
2424
2196
  filePath: resolved,
2425
- baseDir: dirname5(resolved),
2197
+ baseDir: dirname4(resolved),
2426
2198
  content: body,
2427
2199
  disableModelInvocation: frontmatter["disable-model-invocation"] === true
2428
2200
  };
2429
2201
  }
2430
2202
  async function loadSkills(targetPath) {
2431
2203
  const skillsDir = resolve4(targetPath);
2432
- if (existsSync4(skillsDir) && statSync2(skillsDir).isFile()) {
2204
+ if (existsSync2(skillsDir) && statSync2(skillsDir).isFile()) {
2433
2205
  return [await loadSkill(skillsDir)];
2434
2206
  }
2435
- if (existsSync4(join4(skillsDir, SKILL_MARKER))) {
2207
+ if (existsSync2(join2(skillsDir, SKILL_MARKER))) {
2436
2208
  return [await loadSkill(skillsDir)];
2437
2209
  }
2438
2210
  const entries = await readdir2(skillsDir, { withFileTypes: true });
2439
2211
  const skills = [];
2440
2212
  for (const entry of [...entries].sort((a, b) => a.name.localeCompare(b.name))) {
2441
2213
  if (!entry.isDirectory()) continue;
2442
- const skillFile = join4(skillsDir, entry.name, SKILL_MARKER);
2443
- if (!existsSync4(skillFile)) continue;
2214
+ const skillFile = join2(skillsDir, entry.name, SKILL_MARKER);
2215
+ if (!existsSync2(skillFile)) continue;
2444
2216
  try {
2445
2217
  const skill = await loadSkill(skillFile);
2446
2218
  skills.push(skill);
@@ -2561,19 +2333,11 @@ var HooksManager = class {
2561
2333
  return void 0;
2562
2334
  }
2563
2335
  };
2564
- var _globalHooks;
2565
- function getGlobalHooks() {
2566
- _globalHooks ??= new HooksManager();
2567
- return _globalHooks;
2568
- }
2569
- function resetGlobalHooks() {
2570
- _globalHooks = void 0;
2571
- }
2572
2336
 
2573
2337
  // src/extensions/runner.ts
2574
2338
  import { execFile } from "child_process";
2575
2339
  import {
2576
- registerModel as registerModel2,
2340
+ registerModel,
2577
2341
  unregisterProviderModels,
2578
2342
  registerApiProvider
2579
2343
  } from "@rowan-agent/models";
@@ -2624,6 +2388,9 @@ function createExtensionRuntime() {
2624
2388
  return runtime;
2625
2389
  }
2626
2390
 
2391
+ // src/extensions/runner.ts
2392
+ import { parseModelRef as parseModelRef2 } from "@rowan-agent/models";
2393
+
2627
2394
  // src/extensions/event-bus.ts
2628
2395
  function createEventBus() {
2629
2396
  const listeners = /* @__PURE__ */ new Map();
@@ -2705,7 +2472,7 @@ function applyProviderRegistration(config) {
2705
2472
  registerApiProvider({ protocol: config.protocol, stream: config.streamSimple });
2706
2473
  }
2707
2474
  for (const modelConfig of config.models) {
2708
- registerModel2({
2475
+ registerModel({
2709
2476
  id: modelConfig.id,
2710
2477
  name: modelConfig.name,
2711
2478
  protocol: config.protocol,
@@ -2977,7 +2744,7 @@ var ExtensionRunner = class {
2977
2744
  filePath: "",
2978
2745
  baseDir: "",
2979
2746
  content: "",
2980
- ...def.model ? { model: parseModelRef(def.model) } : {}
2747
+ ...def.model ? { model: parseModelRef2(def.model) } : {}
2981
2748
  };
2982
2749
  }
2983
2750
  // ---------------------------------------------------------------------------
@@ -3300,10 +3067,10 @@ function createExtensionRunner(options) {
3300
3067
  }
3301
3068
 
3302
3069
  // src/extensions/loader.ts
3303
- import { existsSync as existsSync5, readFileSync as readFileSync2 } from "fs";
3304
- import { readdir as readdir3, readFile as readFile4, stat as stat2 } from "fs/promises";
3070
+ import { existsSync as existsSync3, readFileSync } from "fs";
3071
+ import { readdir as readdir3, readFile as readFile3, stat as stat2 } from "fs/promises";
3305
3072
  import { fileURLToPath } from "url";
3306
- import { dirname as dirname6, extname as extname2, join as join5, resolve as resolve5 } from "path";
3073
+ import { basename as basename3, dirname as dirname5, extname as extname2, join as join3, resolve as resolve5 } from "path";
3307
3074
  import { createJiti } from "jiti";
3308
3075
  function isExtensionFile(path) {
3309
3076
  const extension = extname2(path).toLowerCase();
@@ -3313,12 +3080,12 @@ function isSyntheticPath(path) {
3313
3080
  return path.startsWith("<") && path.endsWith(">");
3314
3081
  }
3315
3082
  function readManifestSync(dir) {
3316
- const manifestPath = join5(dir, "package.json");
3317
- if (!existsSync5(manifestPath)) {
3083
+ const manifestPath = join3(dir, "package.json");
3084
+ if (!existsSync3(manifestPath)) {
3318
3085
  return void 0;
3319
3086
  }
3320
3087
  try {
3321
- const content = readFileSync2(manifestPath, "utf8");
3088
+ const content = readFileSync(manifestPath, "utf8");
3322
3089
  const pkg = JSON.parse(content);
3323
3090
  const rowan = pkg.rowan;
3324
3091
  if (!rowan) return void 0;
@@ -3336,7 +3103,7 @@ function jitiAliases() {
3336
3103
  return {};
3337
3104
  }
3338
3105
  const agentSourcePath = fileURLToPath(new URL("../index.ts", moduleUrl));
3339
- if (!existsSync5(agentSourcePath)) {
3106
+ if (!existsSync3(agentSourcePath)) {
3340
3107
  return {};
3341
3108
  }
3342
3109
  return { "@rowan-agent/agent": agentSourcePath };
@@ -3344,7 +3111,9 @@ function jitiAliases() {
3344
3111
  var sharedJiti;
3345
3112
  function getJiti() {
3346
3113
  sharedJiti ??= createJiti(import.meta.url || process.cwd(), {
3114
+ fsCache: false,
3347
3115
  moduleCache: false,
3116
+ tryNative: false,
3348
3117
  alias: jitiAliases()
3349
3118
  });
3350
3119
  return sharedJiti;
@@ -3355,21 +3124,21 @@ async function loadExtensionModule(extensionPath) {
3355
3124
  return typeof module === "function" ? module : void 0;
3356
3125
  }
3357
3126
  async function readPackageManifest(path) {
3358
- if (!existsSync5(path)) {
3127
+ if (!existsSync3(path)) {
3359
3128
  return void 0;
3360
3129
  }
3361
- const content = await readFile4(path, "utf8");
3130
+ const content = await readFile3(path, "utf8");
3362
3131
  return JSON.parse(content);
3363
3132
  }
3364
3133
  async function resolveExtensionEntries(dir) {
3365
- const manifest = await readPackageManifest(join5(dir, "package.json"));
3134
+ const manifest = await readPackageManifest(join3(dir, "package.json"));
3366
3135
  const declared = manifest?.rowan?.extensions;
3367
3136
  if (declared && declared.length > 0) {
3368
3137
  return declared.map((entry) => resolve5(dir, entry));
3369
3138
  }
3370
3139
  for (const name of ["index.ts", "index.js"]) {
3371
- const entry = join5(dir, name);
3372
- if (existsSync5(entry)) {
3140
+ const entry = join3(dir, name);
3141
+ if (existsSync3(entry)) {
3373
3142
  return [entry];
3374
3143
  }
3375
3144
  }
@@ -3384,7 +3153,7 @@ async function discoverExtensionsInDir(dir) {
3384
3153
  });
3385
3154
  const paths = [];
3386
3155
  for (const entry of [...entries].sort((a, b) => a.name.localeCompare(b.name))) {
3387
- const entryPath = join5(dir, entry.name);
3156
+ const entryPath = join3(dir, entry.name);
3388
3157
  if ((entry.isFile() || entry.isSymbolicLink()) && isExtensionFile(entry.name)) {
3389
3158
  paths.push(entryPath);
3390
3159
  continue;
@@ -3404,8 +3173,8 @@ async function discoverExtensionsInDir(dir) {
3404
3173
  function loadExtensionFromFactory(factory, cwd, extensionPath = "<inline>") {
3405
3174
  const resolvedCwd = resolve5(cwd);
3406
3175
  const resolvedPath = isSyntheticPath(extensionPath) ? extensionPath : resolve5(resolvedCwd, extensionPath);
3407
- const name = isSyntheticPath(extensionPath) ? extensionPath : dirname6(resolvedPath).split("/").pop() ?? "unknown";
3408
- const manifestDir = isSyntheticPath(extensionPath) ? resolvedCwd : dirname6(resolvedPath);
3176
+ const name = isSyntheticPath(extensionPath) ? extensionPath : basename3(dirname5(resolvedPath)) || "unknown";
3177
+ const manifestDir = isSyntheticPath(extensionPath) ? resolvedCwd : dirname5(resolvedPath);
3409
3178
  const manifest = readManifestSync(manifestDir);
3410
3179
  return {
3411
3180
  path: resolvedPath,
@@ -3443,14 +3212,14 @@ async function loadExtensionsFromPath(path) {
3443
3212
  const resolvedPath = resolve5(path);
3444
3213
  const info = await stat2(resolvedPath);
3445
3214
  if (!info.isDirectory()) {
3446
- return loadExtensions([resolvedPath], dirname6(resolvedPath));
3215
+ return loadExtensions([resolvedPath], dirname5(resolvedPath));
3447
3216
  }
3448
3217
  const entries = await resolveExtensionEntries(resolvedPath) ?? await discoverExtensionsInDir(resolvedPath);
3449
3218
  return loadExtensions(entries, resolvedPath);
3450
3219
  }
3451
3220
 
3452
- // src/agent.ts
3453
- var Agent = class {
3221
+ // src/agent-execution.ts
3222
+ var AgentExecution = class {
3454
3223
  state;
3455
3224
  options;
3456
3225
  listeners = /* @__PURE__ */ new Set();
@@ -3460,6 +3229,17 @@ var Agent = class {
3460
3229
  extensionRunner;
3461
3230
  loadedExtensions;
3462
3231
  loadedExtensionsCwd;
3232
+ runtimeRunId;
3233
+ get id() {
3234
+ const agentId = this.state.agentId;
3235
+ if (!agentId) throw new Error("Agent has no durable identity.");
3236
+ return agentId;
3237
+ }
3238
+ get sessionId() {
3239
+ const sessionId = this.state.sessionId;
3240
+ if (!sessionId) throw new Error("Agent has no Session.");
3241
+ return sessionId;
3242
+ }
3463
3243
  static loadSkills(targetPath) {
3464
3244
  return loadSkills(targetPath);
3465
3245
  }
@@ -3476,6 +3256,7 @@ var Agent = class {
3476
3256
  context
3477
3257
  };
3478
3258
  this.state = {
3259
+ ...this.options.agentId ? { agentId: this.options.agentId } : {},
3479
3260
  ...this.options.sessionId ? { sessionId: this.options.sessionId } : {},
3480
3261
  context: prepareAgentContext(context),
3481
3262
  model: this.options.model,
@@ -3511,17 +3292,37 @@ var Agent = class {
3511
3292
  this.appendUserMessage(input);
3512
3293
  return this.run(options);
3513
3294
  }
3514
- runWithMessage(message, options) {
3295
+ runWithMessage(message, options, internalHooks = {}) {
3515
3296
  const activeRun = this.activeRun;
3516
3297
  if (activeRun?.resume) {
3298
+ if (internalHooks.onSuspend) activeRun.onSuspend = internalHooks.onSuspend;
3517
3299
  activeRun.resume([message]);
3518
3300
  return activeRun.promise;
3519
3301
  }
3520
3302
  if (activeRun) {
3521
3303
  return Promise.reject(new Error("Agent is already running."));
3522
3304
  }
3523
- this.appendMessage(message);
3524
- return this.run(options);
3305
+ if (!this.options.context.messages.some((candidate) => candidate.id === message.id)) {
3306
+ this.appendMessage(message);
3307
+ }
3308
+ return this.run(options, internalHooks);
3309
+ }
3310
+ /** Internal Runtime seam: executes one leased Agent Input. */
3311
+ async executeAgentInput(input, runId, control, executionState) {
3312
+ this.runtimeRunId = runId;
3313
+ this.options.sessionState = executionState ? restoreSessionState(executionState) : void 0;
3314
+ const internal = {
3315
+ onSuspend: (reason, state, inputRequest) => control.suspend(
3316
+ reason,
3317
+ state ? snapshotExecutionState(state) : void 0,
3318
+ inputRequest
3319
+ )
3320
+ };
3321
+ try {
3322
+ return (await this.runWithMessage(input, void 0, internal)).outcome;
3323
+ } finally {
3324
+ if (!this.activeRun) this.runtimeRunId = void 0;
3325
+ }
3525
3326
  }
3526
3327
  resetInitialization() {
3527
3328
  this.state.initialized = false;
@@ -3593,6 +3394,13 @@ var Agent = class {
3593
3394
  getSessionId() {
3594
3395
  return this.state.sessionId;
3595
3396
  }
3397
+ getAgentId() {
3398
+ return this.state.agentId;
3399
+ }
3400
+ /** Internal Runtime hook for associating Tool Calls with the leased Run. */
3401
+ getRuntimeRunId() {
3402
+ return this.runtimeRunId;
3403
+ }
3596
3404
  setModel(model) {
3597
3405
  this.options = {
3598
3406
  ...this.options,
@@ -3740,25 +3548,34 @@ var Agent = class {
3740
3548
  const message = error instanceof Error ? error.message : "Agent run failed.";
3741
3549
  this.state.error = message;
3742
3550
  }
3743
- runWithLifecycle(executor) {
3551
+ runWithLifecycle(executor, hooks = {}) {
3744
3552
  if (this.activeRun) {
3745
3553
  return Promise.reject(new Error("Agent is already running."));
3746
3554
  }
3747
3555
  const abortController = new AbortController();
3748
- const waitForInput = () => new Promise((resolve7) => {
3749
- const activeRun = this.activeRun;
3750
- activeRun.resume = (messages) => {
3751
- activeRun.resume = void 0;
3752
- resolve7(messages);
3753
- };
3754
- });
3556
+ let suspensionRequested = false;
3557
+ const waitForInput = (state, inputRequest) => {
3558
+ const waiting = new Promise((resolve7) => {
3559
+ const activeRun = this.activeRun;
3560
+ activeRun.resume = (messages) => {
3561
+ activeRun.resume = void 0;
3562
+ suspensionRequested = false;
3563
+ resolve7(messages);
3564
+ };
3565
+ });
3566
+ if (!suspensionRequested) {
3567
+ suspensionRequested = true;
3568
+ void this.activeRun?.onSuspend?.("Agent requested input.", state, inputRequest);
3569
+ }
3570
+ return waiting;
3571
+ };
3755
3572
  const promise = Promise.resolve().then(() => executor({ signal: abortController.signal, waitForInput })).catch((error) => {
3756
3573
  this.handleRunFailure(error, abortController.signal.aborted);
3757
3574
  throw error;
3758
3575
  }).finally(() => {
3759
3576
  this.finishRun();
3760
3577
  });
3761
- this.activeRun = { promise, abortController };
3578
+ this.activeRun = { promise, abortController, onSuspend: hooks.onSuspend };
3762
3579
  this.state.running = true;
3763
3580
  return promise;
3764
3581
  }
@@ -3785,13 +3602,14 @@ var Agent = class {
3785
3602
  this.loadedExtensions = extensions;
3786
3603
  this.loadedExtensionsCwd = cwd;
3787
3604
  }
3788
- run(config) {
3605
+ run(config, internalHooks = {}) {
3789
3606
  return this.runWithLifecycle(async ({ signal, waitForInput }) => {
3790
3607
  const resolved = this.resolveRunConfig(config);
3791
3608
  const extensions = resolved.extensions ?? [];
3792
3609
  await this.loadExtensions(extensions, resolved.cwd);
3793
3610
  const runContext = prepareAgentContext(resolved.context, this.extensionRunner);
3794
- const entryPhaseId = this.state.initialized ? DEFAULT_PHASE_ID : runContext.phases?.entryPhaseId ?? DEFAULT_PHASE_ID;
3611
+ const resumingSuspendedRun = resolved.sessionState?.status === "suspended";
3612
+ const entryPhaseId = resumingSuspendedRun ? resolved.sessionState?.currentPhase || runContext.phases?.entryPhaseId || DEFAULT_PHASE_ID : this.state.initialized ? DEFAULT_PHASE_ID : runContext.phases?.entryPhaseId ?? DEFAULT_PHASE_ID;
3795
3613
  const loopContext = {
3796
3614
  ...runContext,
3797
3615
  phases: {
@@ -3836,6 +3654,7 @@ var Agent = class {
3836
3654
  model: resolved.model,
3837
3655
  stream: resolved.stream,
3838
3656
  maxAttempts: resolved.maxAttempts,
3657
+ sessionState: resolved.sessionState,
3839
3658
  signal,
3840
3659
  beforeToolCall,
3841
3660
  afterToolCall,
@@ -3846,7 +3665,8 @@ var Agent = class {
3846
3665
  onMessage: resolved.onMessage,
3847
3666
  onOutcome: resolved.onOutcome,
3848
3667
  onModelTranscript: resolved.onModelTranscript,
3849
- waitForInput
3668
+ waitForInput,
3669
+ runtime: resolved.runtime
3850
3670
  });
3851
3671
  const nextContext = {
3852
3672
  ...prepareAgentContext(resolved.context),
@@ -3859,16 +3679,27 @@ var Agent = class {
3859
3679
  this.options = {
3860
3680
  ...resolved,
3861
3681
  sessionId: result.sessionId,
3862
- context: nextContext
3682
+ context: nextContext,
3683
+ sessionState: void 0
3863
3684
  };
3864
3685
  return result;
3865
- });
3686
+ }, internalHooks);
3866
3687
  }
3867
3688
  abort(reason = "Aborted by caller.") {
3689
+ this.abortLocal(reason);
3690
+ }
3691
+ isSuspended() {
3692
+ return Boolean(this.activeRun?.resume);
3693
+ }
3694
+ abortLocal(reason) {
3868
3695
  const activeRun = this.activeRun;
3869
3696
  activeRun?.resume?.([]);
3870
3697
  activeRun?.abortController.abort(reason);
3871
3698
  }
3699
+ /** Internal Runtime hook; public callers abort a precise AgentRun. */
3700
+ abortFromRuntime(reason = "Agent Runtime aborted the Run.") {
3701
+ this.abortLocal(reason);
3702
+ }
3872
3703
  /**
3873
3704
  * Get formatted skill content for LLM consumption.
3874
3705
  *
@@ -3934,6 +3765,50 @@ ${additionalInstructions}` : content;
3934
3765
  };
3935
3766
  }
3936
3767
  };
3768
+ function restoreSessionState(state) {
3769
+ const persisted = parsePersistedExecutionState(state);
3770
+ return {
3771
+ ...persisted,
3772
+ status: "suspended",
3773
+ metrics: {
3774
+ ...persisted.metrics,
3775
+ phaseTransitions: persisted.metrics.phaseTransitions.map((transition) => ({ ...transition }))
3776
+ },
3777
+ ...persisted.continuation ? {
3778
+ continuation: {
3779
+ ...persisted.continuation,
3780
+ previousResults: persisted.continuation.previousResults.map((result) => ({ ...result }))
3781
+ }
3782
+ } : {}
3783
+ };
3784
+ }
3785
+ function snapshotExecutionState(state) {
3786
+ return {
3787
+ version: 1,
3788
+ currentPhase: state.currentPhase,
3789
+ attempt: state.attempt,
3790
+ metrics: {
3791
+ ...state.metrics,
3792
+ phaseTransitions: state.metrics.phaseTransitions.map((transition) => ({ ...transition }))
3793
+ },
3794
+ ...state.continuation ? {
3795
+ continuation: {
3796
+ ...state.continuation,
3797
+ previousResults: state.continuation.previousResults.map((result) => ({ ...result }))
3798
+ }
3799
+ } : {}
3800
+ };
3801
+ }
3802
+ function parsePersistedExecutionState(state) {
3803
+ if (!state || state.version !== 1 || typeof state.currentPhase !== "string" || typeof state.attempt !== "number") {
3804
+ throw new Error("Invalid Agent Run execution checkpoint.");
3805
+ }
3806
+ const metrics = state.metrics;
3807
+ if (!metrics || !Array.isArray(metrics.phaseTransitions)) {
3808
+ throw new Error("Invalid Agent Run execution checkpoint metrics.");
3809
+ }
3810
+ return state;
3811
+ }
3937
3812
  function clonePhaseRegistry(registry) {
3938
3813
  return {
3939
3814
  phases: new Map(registry.phases),
@@ -3962,9 +3837,130 @@ function prepareAgentContext(context, extensionRunner) {
3962
3837
  };
3963
3838
  }
3964
3839
 
3840
+ // src/agent.ts
3841
+ var AGENT_CONSTRUCTION = /* @__PURE__ */ Symbol("rowan.agent.construction");
3842
+ var ATTACH_AGENT = /* @__PURE__ */ Symbol("rowan.agent.attach");
3843
+ var Agent = class _Agent {
3844
+ constructor(token, id, sessionId, execution, submitInput) {
3845
+ this.id = id;
3846
+ this.sessionId = sessionId;
3847
+ this.execution = execution;
3848
+ this.submitInput = submitInput;
3849
+ if (token !== AGENT_CONSTRUCTION) {
3850
+ throw new Error("Agent lifecycle is owned by AgentRuntime.");
3851
+ }
3852
+ }
3853
+ id;
3854
+ sessionId;
3855
+ execution;
3856
+ submitInput;
3857
+ static [ATTACH_AGENT](id, sessionId, execution, submitInput) {
3858
+ return new _Agent(AGENT_CONSTRUCTION, id, sessionId, execution, submitInput);
3859
+ }
3860
+ static loadSkills(targetPath) {
3861
+ return AgentExecution.loadSkills(targetPath);
3862
+ }
3863
+ static loadPhases(targetPath) {
3864
+ return AgentExecution.loadPhases(targetPath);
3865
+ }
3866
+ static loadExtensions(targetPath) {
3867
+ return AgentExecution.loadExtensions(targetPath);
3868
+ }
3869
+ subscribe(listener) {
3870
+ return this.execution.subscribe(listener);
3871
+ }
3872
+ flushEvents() {
3873
+ return this.execution.flushEvents();
3874
+ }
3875
+ send(input) {
3876
+ const message = typeof input === "string" ? createMessage("user", input) : input;
3877
+ return this.submitInput(message);
3878
+ }
3879
+ };
3880
+ async function attachAgent(input) {
3881
+ const restoredContext = await input.manager.buildAgentContext({
3882
+ tools: input.options.context.tools,
3883
+ skills: input.options.context.skills
3884
+ });
3885
+ const context = {
3886
+ ...restoredContext,
3887
+ systemPrompt: input.options.context.systemPrompt || restoredContext.systemPrompt,
3888
+ phases: input.options.context.phases
3889
+ };
3890
+ const persistInput = async (message) => {
3891
+ const context2 = await input.manager.buildAgentContext();
3892
+ if (context2.messages.some((candidate) => candidate.id === message.id)) return;
3893
+ await input.manager.appendMessage(message);
3894
+ };
3895
+ let execution;
3896
+ execution = new AgentExecution({
3897
+ ...input.options,
3898
+ context,
3899
+ agentId: input.agentId,
3900
+ sessionId: input.sessionId,
3901
+ runtime: {
3902
+ tools: async ({ config, toolCall }) => {
3903
+ const tool = config.context.tools.find((candidate) => candidate.name === toolCall.name);
3904
+ const runId = execution.getRuntimeRunId();
3905
+ if (!tool || !runId) {
3906
+ const message = `Tool ${toolCall.name} is not available to this Agent Run.`;
3907
+ return {
3908
+ toolCallId: toolCall.id,
3909
+ toolName: toolCall.name,
3910
+ ok: false,
3911
+ content: message,
3912
+ error: message
3913
+ };
3914
+ }
3915
+ return input.executeTool({
3916
+ agentId: input.agentId,
3917
+ runId,
3918
+ tool,
3919
+ toolCall,
3920
+ context: config.context,
3921
+ beforeToolCall: config.beforeToolCall,
3922
+ afterToolCall: config.afterToolCall,
3923
+ signal: config.signal
3924
+ });
3925
+ }
3926
+ },
3927
+ onMessage: async (message) => {
3928
+ await input.manager.appendMessage(message);
3929
+ await input.options.onMessage?.(message);
3930
+ },
3931
+ onInput: persistInput,
3932
+ onOutcome: async (outcome) => {
3933
+ await input.manager.appendOutcome(outcome);
3934
+ await input.options.onOutcome?.(outcome);
3935
+ },
3936
+ onModelTranscript: async (transcript, meta) => {
3937
+ await input.manager.appendModelTranscript(transcript, meta);
3938
+ await input.options.onModelTranscript?.(transcript, meta);
3939
+ }
3940
+ });
3941
+ const agent = Agent[ATTACH_AGENT](
3942
+ input.agentId,
3943
+ input.sessionId,
3944
+ execution,
3945
+ (message) => input.submit(message, () => persistInput(message))
3946
+ );
3947
+ return {
3948
+ agent,
3949
+ binding: {
3950
+ abort: (reason) => execution.abortFromRuntime(reason),
3951
+ isSuspended: () => execution.isSuspended(),
3952
+ persistInput,
3953
+ execute: (message, runId, control, executionState) => execution.executeAgentInput(message, runId, control, executionState)
3954
+ }
3955
+ };
3956
+ }
3957
+
3958
+ // src/harness/session/jsonl.ts
3959
+ import { appendFile, mkdir as mkdir2, readFile as readFile4, readdir as readdir4, stat as stat3, unlink, writeFile as writeFile2 } from "fs/promises";
3960
+ import { join as join4, relative as relative3, resolve as resolve6, sep as sep3 } from "path";
3961
+
3965
3962
  // src/harness/session/session.ts
3966
3963
  import Type3 from "typebox";
3967
- var SESSION_SCHEMA_VERSION = "0.4.4";
3968
3964
  var AgentMessageSchema = Type3.Object({
3969
3965
  id: Type3.String(),
3970
3966
  role: Type3.Union([
@@ -3985,39 +3981,6 @@ var SkillSchema = Type3.Object({
3985
3981
  content: Type3.String(),
3986
3982
  disableModelInvocation: Type3.Boolean()
3987
3983
  });
3988
- function createMessage2(role, content, metadata) {
3989
- return {
3990
- id: createId("msg"),
3991
- role,
3992
- content,
3993
- createdAt: createTimestamp(),
3994
- ...metadata ? { metadata } : {}
3995
- };
3996
- }
3997
- function createSession(input) {
3998
- const createdAt = createTimestamp();
3999
- const messages = [
4000
- createMessage2("user", input.input)
4001
- ];
4002
- return {
4003
- version: SESSION_SCHEMA_VERSION,
4004
- id: input.id ?? createId("ses"),
4005
- ...input.parentSessionId ? { parentSessionId: input.parentSessionId } : {},
4006
- systemPrompt: input.systemPrompt,
4007
- input: input.input,
4008
- messages,
4009
- log: [],
4010
- skills: input.skills ?? [],
4011
- createdAt,
4012
- updatedAt: createdAt,
4013
- ...input.title ? { title: input.title } : {}
4014
- };
4015
- }
4016
- function appendUserTurn(session, input) {
4017
- session.messages.push(createMessage2("user", input));
4018
- session.updatedAt = createTimestamp();
4019
- return session;
4020
- }
4021
3984
 
4022
3985
  // src/harness/session/session-manager.ts
4023
3986
  var SESSION_MANAGER_SCHEMA_VERSION = "0.4.4";
@@ -4214,8 +4177,6 @@ function summarizeSessionManagerRecords(records) {
4214
4177
  }
4215
4178
 
4216
4179
  // src/harness/session/jsonl.ts
4217
- import { appendFile, mkdir as mkdir2, readFile as readFile5, readdir as readdir4, stat as stat3, unlink, writeFile as writeFile2 } from "fs/promises";
4218
- import { join as join6, relative as relative3, resolve as resolve6, sep as sep3 } from "path";
4219
4180
  var SESSION_ID_PATTERN = /^ses_[A-Za-z0-9_-]+$/;
4220
4181
  function isPathInside(parent, child) {
4221
4182
  const relativePath = relative3(parent, child);
@@ -4237,7 +4198,7 @@ function parseRecord(line) {
4237
4198
  return value;
4238
4199
  }
4239
4200
  async function readRecords(path) {
4240
- const text = await readFile5(path, "utf8").catch((error) => {
4201
+ const text = await readFile4(path, "utf8").catch((error) => {
4241
4202
  if (error.code === "ENOENT") {
4242
4203
  return void 0;
4243
4204
  }
@@ -4326,7 +4287,7 @@ var LocalJsonlSessionManager = class _LocalJsonlSessionManager {
4326
4287
  });
4327
4288
  const sessions = await Promise.all(
4328
4289
  entries.filter((entry) => entry.isFile() && entry.name.endsWith(".jsonl")).map(async (entry) => {
4329
- const path = join6(sessionsDir, entry.name);
4290
+ const path = join4(sessionsDir, entry.name);
4330
4291
  const records = await readRecords(path);
4331
4292
  return records ? summarizeSessionManagerRecords(records) : void 0;
4332
4293
  })
@@ -4416,114 +4377,2147 @@ var LocalJsonlSessionManager = class _LocalJsonlSessionManager {
4416
4377
  }
4417
4378
  };
4418
4379
 
4419
- // src/event-stream.ts
4420
- var EventStream = class {
4421
- queue = [];
4422
- waiting = [];
4423
- done = false;
4424
- finalResultPromise;
4425
- resolveFinalResult;
4426
- isComplete;
4427
- extractResult;
4428
- constructor(isComplete, extractResult) {
4429
- this.isComplete = isComplete;
4430
- this.extractResult = extractResult;
4431
- this.finalResultPromise = new Promise((resolve7) => {
4432
- this.resolveFinalResult = resolve7;
4433
- });
4380
+ // src/harness/session/store.ts
4381
+ var InMemorySessionStore = class {
4382
+ sessions = /* @__PURE__ */ new Map();
4383
+ async create(input) {
4384
+ const session = InMemorySessionManager.create(input);
4385
+ this.sessions.set(session.getSessionId(), session);
4386
+ return session;
4434
4387
  }
4435
- push(event) {
4436
- if (this.done) return;
4437
- if (this.isComplete(event)) {
4438
- this.done = true;
4439
- this.resolveFinalResult(this.extractResult(event));
4440
- }
4441
- const waiter = this.waiting.shift();
4442
- if (waiter) {
4443
- waiter({ value: event, done: false });
4444
- } else {
4445
- this.queue.push(event);
4446
- }
4388
+ async open(sessionId) {
4389
+ return this.sessions.get(sessionId);
4447
4390
  }
4448
- end(result) {
4449
- this.done = true;
4450
- if (result !== void 0) {
4451
- this.resolveFinalResult(result);
4452
- }
4453
- while (this.waiting.length > 0) {
4454
- const waiter = this.waiting.shift();
4455
- waiter({ value: void 0, done: true });
4456
- }
4391
+ };
4392
+ var JsonlSessionStore = class {
4393
+ constructor(sessionsDir) {
4394
+ this.sessionsDir = sessionsDir;
4457
4395
  }
4458
- async *[Symbol.asyncIterator]() {
4459
- while (true) {
4460
- if (this.queue.length > 0) {
4461
- yield this.queue.shift();
4462
- } else if (this.done) {
4463
- return;
4464
- } else {
4465
- const result = await new Promise((resolve7) => this.waiting.push(resolve7));
4466
- if (result.done) return;
4467
- yield result.value;
4468
- }
4469
- }
4396
+ sessionsDir;
4397
+ create(input) {
4398
+ return LocalJsonlSessionManager.create(this.sessionsDir, input);
4470
4399
  }
4471
- result() {
4472
- return this.finalResultPromise;
4400
+ open(sessionId) {
4401
+ return LocalJsonlSessionManager.open(this.sessionsDir, sessionId);
4473
4402
  }
4474
- };
4475
- var AgentEventStream = class extends EventStream {
4476
- constructor() {
4477
- super(
4478
- (event) => event.type === "agent_end",
4479
- (event) => event.type === "agent_end" ? event.messages : []
4480
- );
4403
+ list() {
4404
+ return LocalJsonlSessionManager.list(this.sessionsDir);
4481
4405
  }
4482
4406
  };
4483
4407
 
4484
- // src/index.ts
4485
- import {
4486
- createDispatchStream,
4487
- registerBuiltInApiProviders
4488
- } from "@rowan-agent/models";
4408
+ // src/runtime/agent-runtime.ts
4409
+ import { createModelStream } from "@rowan-agent/models";
4410
+
4411
+ // src/runtime/tool-runtime.ts
4412
+ function failed(toolCall, message) {
4413
+ return {
4414
+ toolCallId: toolCall.id,
4415
+ toolName: toolCall.name,
4416
+ ok: false,
4417
+ content: message,
4418
+ error: message
4419
+ };
4420
+ }
4421
+ function positiveLimit(value, fallback) {
4422
+ if (value === void 0) return fallback;
4423
+ if (!Number.isFinite(value) || value <= 0) {
4424
+ throw new Error("Tool Runtime concurrency limits must be positive finite numbers.");
4425
+ }
4426
+ return Math.floor(value);
4427
+ }
4428
+ var ToolRuntime = class {
4429
+ constructor(stateStore, policy = {}, onTransition = () => void 0) {
4430
+ this.stateStore = stateStore;
4431
+ this.onTransition = onTransition;
4432
+ this.maxConcurrent = positiveLimit(policy.maxConcurrent, Number.MAX_SAFE_INTEGER);
4433
+ this.perToolMaxConcurrent = Object.fromEntries(
4434
+ Object.entries(policy.perToolMaxConcurrent ?? {}).map(([name, value]) => [name, positiveLimit(value, 1)])
4435
+ );
4436
+ this.allowedTools = policy.allowedTools ? new Set(policy.allowedTools) : void 0;
4437
+ }
4438
+ stateStore;
4439
+ onTransition;
4440
+ maxConcurrent;
4441
+ perToolMaxConcurrent;
4442
+ running = 0;
4443
+ runningByTool = /* @__PURE__ */ new Map();
4444
+ waiters = [];
4445
+ activeToolCalls = /* @__PURE__ */ new Map();
4446
+ allowedTools;
4447
+ async execute(input) {
4448
+ const call = await this.stateStore.createToolCall({
4449
+ agentId: input.agentId,
4450
+ runId: input.runId,
4451
+ name: input.toolCall.name,
4452
+ args: input.toolCall.args
4453
+ });
4454
+ await this.onTransition();
4455
+ if (!input.tool || this.allowedTools && !this.allowedTools.has(input.tool.name)) {
4456
+ const result = failed(input.toolCall, `Tool ${input.toolCall.name} is not permitted by Runtime policy.`);
4457
+ await this.stateStore.completeToolCall({ toolCallId: call.id, result, state: "failed" });
4458
+ await this.onTransition();
4459
+ return result;
4460
+ }
4461
+ const before = await input.beforeToolCall?.({ tool: input.tool, args: input.toolCall.args });
4462
+ if (before && !before.allow) {
4463
+ const result = failed(input.toolCall, before.reason);
4464
+ await this.stateStore.completeToolCall({ toolCallId: call.id, result, state: "failed" });
4465
+ await this.onTransition();
4466
+ return result;
4467
+ }
4468
+ let release;
4469
+ const controller = new AbortController();
4470
+ const forwardAbort = () => controller.abort(input.signal?.reason);
4471
+ input.signal?.addEventListener("abort", forwardAbort, { once: true });
4472
+ this.activeToolCalls.set(call.id, { runId: input.runId, controller });
4473
+ try {
4474
+ try {
4475
+ release = await this.acquire(input.tool.name, controller.signal);
4476
+ } catch (error) {
4477
+ const result2 = failed(input.toolCall, error instanceof Error ? error.message : "Tool execution was cancelled.");
4478
+ await this.stateStore.completeToolCall({ toolCallId: call.id, result: result2, state: "failed" });
4479
+ await this.onTransition();
4480
+ return result2;
4481
+ }
4482
+ if (controller.signal.aborted) {
4483
+ const result2 = failed(input.toolCall, "Tool execution was aborted before it started.");
4484
+ await this.stateStore.completeToolCall({ toolCallId: call.id, result: result2, state: "failed" });
4485
+ await this.onTransition();
4486
+ return result2;
4487
+ }
4488
+ await this.stateStore.startToolCall(call.id);
4489
+ await this.onTransition();
4490
+ let result;
4491
+ try {
4492
+ result = await input.tool.execute(input.toolCall.args, {
4493
+ skills: input.context.skills,
4494
+ toolCallId: call.id
4495
+ }, controller.signal);
4496
+ } catch (error) {
4497
+ if (controller.signal.aborted) {
4498
+ await this.stateStore.markToolCallIndeterminate({
4499
+ toolCallId: call.id,
4500
+ reason: error instanceof Error ? error.message : "Tool execution was interrupted."
4501
+ });
4502
+ await this.onTransition();
4503
+ return failed(input.toolCall, "Tool Call became indeterminate after abort.");
4504
+ }
4505
+ result = failed(input.toolCall, error instanceof Error ? error.message : "Tool execution failed.");
4506
+ }
4507
+ if (controller.signal.aborted) {
4508
+ await this.stateStore.markToolCallIndeterminate({ toolCallId: call.id, reason: "Tool execution was interrupted after it may have had an external effect." });
4509
+ await this.onTransition();
4510
+ return failed(input.toolCall, "Tool Call became indeterminate after abort.");
4511
+ }
4512
+ try {
4513
+ result = await input.afterToolCall?.({ tool: input.tool, result }) ?? result;
4514
+ } catch (error) {
4515
+ result = failed(input.toolCall, error instanceof Error ? error.message : "Tool result review failed.");
4516
+ }
4517
+ await this.stateStore.completeToolCall({
4518
+ toolCallId: call.id,
4519
+ result,
4520
+ state: result.ok ? "completed" : "failed"
4521
+ });
4522
+ await this.onTransition();
4523
+ return result;
4524
+ } finally {
4525
+ input.signal?.removeEventListener("abort", forwardAbort);
4526
+ this.activeToolCalls.delete(call.id);
4527
+ release?.();
4528
+ }
4529
+ }
4530
+ abortRun(runId) {
4531
+ for (const active of this.activeToolCalls.values()) {
4532
+ if (active.runId === runId) active.controller.abort("Agent Run aborted.");
4533
+ }
4534
+ }
4535
+ async acquire(toolName, signal) {
4536
+ if (signal.aborted) throw new Error("Tool execution aborted while waiting for capacity.");
4537
+ const limit = this.perToolMaxConcurrent[toolName] ?? this.maxConcurrent;
4538
+ if (this.running < this.maxConcurrent && (this.runningByTool.get(toolName) ?? 0) < limit) {
4539
+ return this.reserve(toolName);
4540
+ }
4541
+ return new Promise((resolve7, reject) => {
4542
+ let waiter;
4543
+ const onAbort = () => {
4544
+ const index = this.waiters.indexOf(waiter);
4545
+ if (index >= 0) this.waiters.splice(index, 1);
4546
+ reject(new Error("Tool execution aborted while waiting for capacity."));
4547
+ };
4548
+ waiter = { toolName, signal, onAbort, resolve: resolve7, reject };
4549
+ this.waiters.push(waiter);
4550
+ signal.addEventListener("abort", onAbort, { once: true });
4551
+ });
4552
+ }
4553
+ reserve(toolName) {
4554
+ this.running += 1;
4555
+ this.runningByTool.set(toolName, (this.runningByTool.get(toolName) ?? 0) + 1);
4556
+ let released = false;
4557
+ return () => {
4558
+ if (released) return;
4559
+ released = true;
4560
+ this.running -= 1;
4561
+ const count = (this.runningByTool.get(toolName) ?? 1) - 1;
4562
+ if (count <= 0) this.runningByTool.delete(toolName);
4563
+ else this.runningByTool.set(toolName, count);
4564
+ this.pumpWaiters();
4565
+ };
4566
+ }
4567
+ pumpWaiters() {
4568
+ for (let index = 0; index < this.waiters.length; index += 1) {
4569
+ const waiter = this.waiters[index];
4570
+ const limit = this.perToolMaxConcurrent[waiter.toolName] ?? this.maxConcurrent;
4571
+ if (this.running >= this.maxConcurrent || (this.runningByTool.get(waiter.toolName) ?? 0) >= limit) continue;
4572
+ this.waiters.splice(index, 1);
4573
+ waiter.signal.removeEventListener("abort", waiter.onAbort);
4574
+ waiter.resolve(this.reserve(waiter.toolName));
4575
+ index -= 1;
4576
+ }
4577
+ }
4578
+ };
4579
+
4580
+ // src/runtime/agent-run.ts
4581
+ var AGENT_RUN_CONSTRUCTION = /* @__PURE__ */ Symbol("rowan.agentRun.construction");
4582
+ var CREATE_AGENT_RUN = /* @__PURE__ */ Symbol("rowan.agentRun.create");
4583
+ var AgentRun = class _AgentRun {
4584
+ constructor(token, host, initial, publicMessageId) {
4585
+ this.host = host;
4586
+ this.publicMessageId = publicMessageId;
4587
+ if (token !== AGENT_RUN_CONSTRUCTION) {
4588
+ throw new Error("AgentRun lifecycle is owned by AgentRuntime.");
4589
+ }
4590
+ this.current = structuredClone(initial);
4591
+ host.register(this);
4592
+ }
4593
+ host;
4594
+ publicMessageId;
4595
+ current;
4596
+ listeners = /* @__PURE__ */ new Set();
4597
+ static [CREATE_AGENT_RUN](host, initial, publicMessageId) {
4598
+ return new _AgentRun(AGENT_RUN_CONSTRUCTION, host, initial, publicMessageId);
4599
+ }
4600
+ get id() {
4601
+ return this.current.id;
4602
+ }
4603
+ get messageId() {
4604
+ return this.publicMessageId;
4605
+ }
4606
+ get status() {
4607
+ return this.current.state;
4608
+ }
4609
+ get state() {
4610
+ return this.current.state;
4611
+ }
4612
+ get inputRequest() {
4613
+ return this.current.inputRequest ? structuredClone(this.current.inputRequest) : void 0;
4614
+ }
4615
+ async getStatus() {
4616
+ const run = await this.host.getRun(this.id);
4617
+ if (run) this.notify(run);
4618
+ return this.current.state;
4619
+ }
4620
+ subscribe(listener) {
4621
+ this.listeners.add(listener);
4622
+ return () => this.listeners.delete(listener);
4623
+ }
4624
+ consumeRuntimeEvents(consumerId, listener) {
4625
+ return this.host.consumeEvents(consumerId, (event) => {
4626
+ if (event.runId === this.id) return listener(event);
4627
+ });
4628
+ }
4629
+ async result() {
4630
+ while (true) {
4631
+ const run = await this.host.getRun(this.id);
4632
+ if (!run) throw new Error(`Agent Run not found: ${this.id}.`);
4633
+ this.notify(run);
4634
+ if (["completed", "failed", "cancelled"].includes(run.state)) {
4635
+ if (!run.outcome) {
4636
+ throw new Error(`Agent Run ${this.id} reached ${run.state} without an Outcome.`);
4637
+ }
4638
+ return structuredClone(run.outcome);
4639
+ }
4640
+ await this.host.waitForRunChange(this.id, run.state, run.updatedAt);
4641
+ }
4642
+ }
4643
+ async abort(reason = "Agent Run aborted by caller.") {
4644
+ await this.host.abortRun(this.id, reason);
4645
+ }
4646
+ notify(run) {
4647
+ this.current = structuredClone(run);
4648
+ for (const listener of this.listeners) {
4649
+ try {
4650
+ const result = listener(run.state);
4651
+ if (result && typeof result.then === "function") {
4652
+ void Promise.resolve(result).catch(() => void 0);
4653
+ }
4654
+ } catch {
4655
+ }
4656
+ }
4657
+ }
4658
+ };
4659
+ function createAgentRun(host, initial, publicMessageId = initial.messageId) {
4660
+ return AgentRun[CREATE_AGENT_RUN](host, initial, publicMessageId);
4661
+ }
4662
+
4663
+ // src/runtime/agent-runtime.ts
4664
+ var InfrastructureFailureError = class extends Error {
4665
+ constructor(message, options) {
4666
+ super(message, options);
4667
+ this.name = "InfrastructureFailureError";
4668
+ }
4669
+ };
4670
+ var activeRuntime;
4671
+ var AgentRuntime = class _AgentRuntime {
4672
+ stateStore;
4673
+ sessionProvider;
4674
+ maxConcurrentRuns;
4675
+ maxInfrastructureAttempts;
4676
+ leaseDurationMs;
4677
+ leaseRenewalIntervalMs;
4678
+ toolRuntime;
4679
+ bindings = /* @__PURE__ */ new Map();
4680
+ agents = /* @__PURE__ */ new Map();
4681
+ runHandles = /* @__PURE__ */ new Map();
4682
+ runWaiters = /* @__PURE__ */ new Map();
4683
+ pendingRuns = [];
4684
+ scheduledRunIds = /* @__PURE__ */ new Set();
4685
+ runningAgents = /* @__PURE__ */ new Set();
4686
+ pausedAgents = /* @__PURE__ */ new Set();
4687
+ eventSubscriptions = /* @__PURE__ */ new Map();
4688
+ activeDispatches = /* @__PURE__ */ new Set();
4689
+ runningRuns = 0;
4690
+ pumping = false;
4691
+ nextWorkerId = 0;
4692
+ stopped = false;
4693
+ constructor(options) {
4694
+ this.stateStore = options.stateStore;
4695
+ this.sessionProvider = options.sessionProvider;
4696
+ this.maxConcurrentRuns = options.maxConcurrentRuns ?? Number.POSITIVE_INFINITY;
4697
+ this.maxInfrastructureAttempts = options.maxInfrastructureAttempts ?? 3;
4698
+ this.leaseDurationMs = options.leaseDurationMs ?? 6e4;
4699
+ this.leaseRenewalIntervalMs = options.leaseRenewalIntervalMs ?? Math.max(1, Math.floor(this.leaseDurationMs / 2));
4700
+ this.toolRuntime = new ToolRuntime(options.stateStore, options.toolPolicy, () => this.publishEvents());
4701
+ }
4702
+ static async start(options) {
4703
+ if (activeRuntime) {
4704
+ throw new Error("Agent Runtime is already started in this process.");
4705
+ }
4706
+ if (!options.stateStore) {
4707
+ throw new Error("Agent Runtime requires a RuntimeStateStore.");
4708
+ }
4709
+ if (!Number.isFinite(options.maxConcurrentRuns ?? 1) || (options.maxConcurrentRuns ?? 1) <= 0) {
4710
+ throw new Error("Agent Runtime maxConcurrentRuns must be a positive finite number.");
4711
+ }
4712
+ if (!Number.isInteger(options.maxInfrastructureAttempts ?? 3) || (options.maxInfrastructureAttempts ?? 3) <= 0) {
4713
+ throw new Error("Agent Runtime maxInfrastructureAttempts must be a positive integer.");
4714
+ }
4715
+ if (!Number.isFinite(options.leaseDurationMs ?? 6e4) || (options.leaseDurationMs ?? 6e4) <= 0) {
4716
+ throw new Error("Agent Runtime leaseDurationMs must be a positive finite number.");
4717
+ }
4718
+ const leaseDurationMs = options.leaseDurationMs ?? 6e4;
4719
+ const renewalIntervalMs = options.leaseRenewalIntervalMs ?? Math.max(1, Math.floor(leaseDurationMs / 2));
4720
+ if (!Number.isFinite(renewalIntervalMs) || renewalIntervalMs <= 0 || renewalIntervalMs >= leaseDurationMs) {
4721
+ throw new Error("Agent Runtime leaseRenewalIntervalMs must be positive and shorter than leaseDurationMs.");
4722
+ }
4723
+ const runtime = new _AgentRuntime(options);
4724
+ activeRuntime = runtime;
4725
+ try {
4726
+ await runtime.recover();
4727
+ return runtime;
4728
+ } catch (error) {
4729
+ await runtime.stop().catch(() => void 0);
4730
+ throw error;
4731
+ }
4732
+ }
4733
+ async createAgent(options) {
4734
+ this.assertRunning();
4735
+ assertAgentOptions(options);
4736
+ const provider = this.sessionProvider;
4737
+ if (!provider) throw new Error("Agent Runtime requires a SessionManager provider to create an Agent.");
4738
+ const manager = await provider.create({
4739
+ systemPrompt: options.context.systemPrompt,
4740
+ input: "",
4741
+ skills: options.context.skills
4742
+ });
4743
+ const record = await this.stateStore.createAgent({
4744
+ sessionId: manager.getSessionId()
4745
+ });
4746
+ this.pausedAgents.delete(record.id);
4747
+ const agent = await this.attachAgent(record, manager, options);
4748
+ this.publishEvents();
4749
+ return agent;
4750
+ }
4751
+ async reconstructAgent(agentId, options) {
4752
+ this.assertRunning();
4753
+ assertAgentOptions(options);
4754
+ const record = await this.stateStore.getAgent(agentId);
4755
+ if (!record) throw new Error(`Agent not found: ${agentId}.`);
4756
+ if (record.state === "paused") this.pausedAgents.add(record.id);
4757
+ else this.pausedAgents.delete(record.id);
4758
+ const provider = this.sessionProvider;
4759
+ if (!provider) throw new Error("Agent Runtime requires a SessionManager provider to reconstruct an Agent.");
4760
+ const manager = await provider.open(record.sessionId);
4761
+ if (!manager) throw new Error(`Session not found: ${record.sessionId}.`);
4762
+ const agent = await this.attachAgent(record, manager, options);
4763
+ this.publishEvents();
4764
+ return agent;
4765
+ }
4766
+ async stop() {
4767
+ if (this.stopped) return;
4768
+ this.stopped = true;
4769
+ let suspendedAgents;
4770
+ try {
4771
+ suspendedAgents = new Set(
4772
+ (await this.stateStore.listRuns({ states: ["suspended"] })).map((run) => run.agentId)
4773
+ );
4774
+ } catch {
4775
+ suspendedAgents = /* @__PURE__ */ new Set();
4776
+ }
4777
+ for (const [agentId, binding] of this.bindings) {
4778
+ if (!suspendedAgents.has(agentId) && !binding.isSuspended()) binding.abort("Agent Runtime stopped.");
4779
+ }
4780
+ this.bindings.clear();
4781
+ this.agents.clear();
4782
+ this.pendingRuns.length = 0;
4783
+ this.scheduledRunIds.clear();
4784
+ this.pausedAgents.clear();
4785
+ const activeDispatches = [...this.activeDispatches];
4786
+ this.runHandles.clear();
4787
+ for (const waiters of this.runWaiters.values()) {
4788
+ for (const resolve7 of waiters) resolve7();
4789
+ }
4790
+ this.runWaiters.clear();
4791
+ this.eventSubscriptions.clear();
4792
+ await Promise.allSettled(activeDispatches);
4793
+ if (activeRuntime === this) activeRuntime = void 0;
4794
+ }
4795
+ bindAgent(agentId, agent) {
4796
+ this.assertRunning();
4797
+ if (this.bindings.has(agentId)) {
4798
+ throw new Error(`Agent ${agentId} is already bound to a live Agent.`);
4799
+ }
4800
+ this.bindings.set(agentId, agent);
4801
+ void this.pump();
4802
+ if (!this.pausedAgents.has(agentId)) void this.scheduleQueuedRuns(agentId);
4803
+ }
4804
+ registerRunHandle(runId, handle) {
4805
+ this.runHandles.set(runId, handle);
4806
+ }
4807
+ waitForRunChange(runId, state, updatedAt) {
4808
+ return new Promise((resolve7) => {
4809
+ const waiters = this.runWaiters.get(runId) ?? /* @__PURE__ */ new Set();
4810
+ let settled = false;
4811
+ const finish = () => {
4812
+ if (settled) return;
4813
+ settled = true;
4814
+ waiters.delete(finish);
4815
+ if (waiters.size === 0) this.runWaiters.delete(runId);
4816
+ resolve7();
4817
+ };
4818
+ waiters.add(finish);
4819
+ this.runWaiters.set(runId, waiters);
4820
+ void this.stateStore.getRun(runId).then((current) => {
4821
+ if (!current || current.state !== state || current.updatedAt !== updatedAt) finish();
4822
+ }, finish);
4823
+ });
4824
+ }
4825
+ scheduleRun(agentId, runId) {
4826
+ this.assertRunning();
4827
+ if (this.scheduledRunIds.has(runId)) return;
4828
+ this.scheduledRunIds.add(runId);
4829
+ this.pendingRuns.push({ agentId, runId });
4830
+ void this.pump();
4831
+ }
4832
+ async pauseAgent(agentId) {
4833
+ this.assertRunning();
4834
+ this.pausedAgents.add(agentId);
4835
+ try {
4836
+ await this.stateStore.setAgentState(agentId, "paused");
4837
+ } catch (error) {
4838
+ this.pausedAgents.delete(agentId);
4839
+ throw error;
4840
+ }
4841
+ this.publishEvents();
4842
+ }
4843
+ async resumeAgent(agentId) {
4844
+ this.assertRunning();
4845
+ await this.stateStore.setAgentState(agentId, "active");
4846
+ this.pausedAgents.delete(agentId);
4847
+ await this.scheduleQueuedRuns(agentId);
4848
+ void this.pump();
4849
+ this.publishEvents();
4850
+ }
4851
+ async getRun(runId) {
4852
+ return this.stateStore.getRun(runId);
4853
+ }
4854
+ async listActiveRuns() {
4855
+ return this.stateStore.listActiveRuns();
4856
+ }
4857
+ async listRuns(input) {
4858
+ return this.stateStore.listRuns(input);
4859
+ }
4860
+ getAgent(agentId) {
4861
+ return this.agents.get(agentId);
4862
+ }
4863
+ async getMessage(messageId) {
4864
+ return this.stateStore.getMessage(messageId);
4865
+ }
4866
+ async getToolCall(toolCallId) {
4867
+ return this.stateStore.getToolCall(toolCallId);
4868
+ }
4869
+ async abortRun(runId, reason = "Agent Run aborted by caller.") {
4870
+ const run = await this.stateStore.getRun(runId);
4871
+ if (!run || ["completed", "failed", "cancelled"].includes(run.state)) return;
4872
+ const aborted = await this.stateStore.abortRun({
4873
+ runId,
4874
+ outcome: { id: createId("out"), message: reason }
4875
+ });
4876
+ this.notifyRun(aborted);
4877
+ if (run.state === "queued") {
4878
+ const pendingIndex = this.pendingRuns.findIndex((job) => job.runId === runId);
4879
+ if (pendingIndex >= 0) this.pendingRuns.splice(pendingIndex, 1);
4880
+ this.scheduledRunIds.delete(runId);
4881
+ }
4882
+ this.toolRuntime.abortRun(runId);
4883
+ if (run.state === "running" || run.state === "suspended") {
4884
+ this.bindings.get(aborted.agentId)?.abort(reason);
4885
+ }
4886
+ this.publishEvents();
4887
+ }
4888
+ consumeEvents(consumerId, listener) {
4889
+ const subscription = this.registerEventConsumer(consumerId, listener);
4890
+ void subscription.caughtUp.catch(() => void 0);
4891
+ return subscription;
4892
+ }
4893
+ registerEventConsumer(consumerId, listener) {
4894
+ this.assertRunning();
4895
+ if (consumerId.trim().length === 0) {
4896
+ throw new Error("Runtime Event Consumer ID must not be empty.");
4897
+ }
4898
+ if (this.eventSubscriptions.has(consumerId)) {
4899
+ throw new Error(`Runtime Event Consumer is already active: ${consumerId}.`);
4900
+ }
4901
+ const subscription = {
4902
+ consumerId,
4903
+ listener,
4904
+ delivering: false,
4905
+ redeliver: false
4906
+ };
4907
+ this.eventSubscriptions.set(consumerId, subscription);
4908
+ let caughtUpSettled = false;
4909
+ let resolveCaughtUp;
4910
+ let rejectCaughtUp;
4911
+ const caughtUp = new Promise((resolve7, reject) => {
4912
+ resolveCaughtUp = resolve7;
4913
+ rejectCaughtUp = reject;
4914
+ });
4915
+ const stop = () => {
4916
+ if (this.eventSubscriptions.get(consumerId) === subscription) {
4917
+ this.eventSubscriptions.delete(consumerId);
4918
+ }
4919
+ if (!caughtUpSettled) {
4920
+ caughtUpSettled = true;
4921
+ const error = new Error("Runtime Event Consumer stopped before catch-up completed.");
4922
+ error.name = "AbortError";
4923
+ rejectCaughtUp(error);
4924
+ }
4925
+ };
4926
+ void this.deliverEvents(subscription).then(
4927
+ () => {
4928
+ if (!caughtUpSettled) {
4929
+ caughtUpSettled = true;
4930
+ resolveCaughtUp();
4931
+ }
4932
+ },
4933
+ (error) => {
4934
+ if (!caughtUpSettled) {
4935
+ caughtUpSettled = true;
4936
+ rejectCaughtUp(error);
4937
+ }
4938
+ }
4939
+ );
4940
+ return { caughtUp, stop };
4941
+ }
4942
+ async listEvents(cursor) {
4943
+ return this.stateStore.listEvents(cursor);
4944
+ }
4945
+ async recover() {
4946
+ await this.stateStore.recoverLeases();
4947
+ const agents = await this.stateStore.listAgents();
4948
+ for (const record of agents) {
4949
+ if (record.state === "paused") this.pausedAgents.add(record.id);
4950
+ }
4951
+ this.publishEvents();
4952
+ }
4953
+ async scheduleQueuedRuns(agentId) {
4954
+ if (!this.bindings.has(agentId)) return;
4955
+ const runs = await this.stateStore.listRuns({ agentId, states: ["queued"] });
4956
+ for (const run of runs) this.scheduleRun(agentId, run.id);
4957
+ }
4958
+ async attachAgent(record, manager, options) {
4959
+ const model = { provider: options.model.provider, id: options.model.id };
4960
+ const resolvedOptions = {
4961
+ ...options,
4962
+ model,
4963
+ stream: options.stream ?? createModelStream(options.model)
4964
+ };
4965
+ const attached = await attachAgent({
4966
+ options: resolvedOptions,
4967
+ agentId: record.id,
4968
+ sessionId: record.sessionId,
4969
+ manager,
4970
+ submit: (message, persist) => this.submitAgentInput(record.id, message, persist),
4971
+ executeTool: (input) => this.toolRuntime.execute(input)
4972
+ });
4973
+ this.bindAgent(record.id, attached.binding);
4974
+ this.agents.set(record.id, attached.agent);
4975
+ return attached.agent;
4976
+ }
4977
+ async submitAgentInput(agentId, input, persist) {
4978
+ this.assertRunning();
4979
+ if (!this.bindings.has(agentId)) throw new Error(`Agent ${agentId} has no live Agent Binding.`);
4980
+ const enqueued = await this.stateStore.enqueueAgentInput({ agentId, input });
4981
+ await persist();
4982
+ if (enqueued.resumed) this.notifyRun(enqueued.run);
4983
+ const run = createAgentRun({
4984
+ register: (handle) => this.registerRunHandle(enqueued.run.id, handle),
4985
+ getRun: (runId) => this.stateStore.getRun(runId),
4986
+ waitForRunChange: (runId, state, updatedAt) => this.waitForRunChange(runId, state, updatedAt),
4987
+ abortRun: (runId, reason) => this.abortRun(runId, reason),
4988
+ consumeEvents: (consumerId, listener) => this.consumeEvents(consumerId, listener)
4989
+ }, enqueued.run, input.id);
4990
+ this.scheduleRun(agentId, enqueued.run.id);
4991
+ this.publishEvents();
4992
+ return run;
4993
+ }
4994
+ async pump() {
4995
+ if (this.pumping || this.stopped) return;
4996
+ this.pumping = true;
4997
+ try {
4998
+ while (this.runningRuns < this.maxConcurrentRuns) {
4999
+ const index = this.pendingRuns.findIndex((job2) => this.bindings.has(job2.agentId) && !this.pausedAgents.has(job2.agentId) && !this.runningAgents.has(job2.agentId));
5000
+ if (index < 0) return;
5001
+ const [job] = this.pendingRuns.splice(index, 1);
5002
+ if (!job) return;
5003
+ this.runningRuns += 1;
5004
+ this.runningAgents.add(job.agentId);
5005
+ let dispatch;
5006
+ dispatch = this.dispatchRun(job).finally(() => {
5007
+ this.runningRuns -= 1;
5008
+ this.runningAgents.delete(job.agentId);
5009
+ this.scheduledRunIds.delete(job.runId);
5010
+ this.activeDispatches.delete(dispatch);
5011
+ void this.pump();
5012
+ });
5013
+ this.activeDispatches.add(dispatch);
5014
+ void dispatch;
5015
+ }
5016
+ } finally {
5017
+ this.pumping = false;
5018
+ }
5019
+ }
5020
+ async dispatchRun(job) {
5021
+ const binding = this.bindings.get(job.agentId);
5022
+ if (!binding) return;
5023
+ const workerId = `runtime-worker-${++this.nextWorkerId}`;
5024
+ let suspendedResolve;
5025
+ let suspended = false;
5026
+ const suspendedSignal = new Promise((resolve7) => {
5027
+ suspendedResolve = resolve7;
5028
+ });
5029
+ const control = {
5030
+ suspend: async (reason, executionState, inputRequest) => {
5031
+ if (suspended) return;
5032
+ suspended = true;
5033
+ const current = await this.stateStore.suspendRun({ runId: job.runId, reason, executionState, inputRequest });
5034
+ this.notifyRun(current);
5035
+ this.publishEvents();
5036
+ suspendedResolve?.();
5037
+ }
5038
+ };
5039
+ let renewalTimer;
5040
+ let renewalFailed = false;
5041
+ let failRenewal;
5042
+ const renewalFailureSignal = new Promise((resolve7) => {
5043
+ failRenewal = resolve7;
5044
+ });
5045
+ try {
5046
+ const leased = await this.stateStore.leaseRun({
5047
+ runId: job.runId,
5048
+ workerId,
5049
+ leaseDurationMs: this.leaseDurationMs
5050
+ });
5051
+ this.notifyRun(leased.run);
5052
+ try {
5053
+ await binding.persistInput(leased.message.input);
5054
+ } catch (error) {
5055
+ throw new InfrastructureFailureError("Agent Input persistence failed.", { cause: error });
5056
+ }
5057
+ renewalTimer = setInterval(() => {
5058
+ void this.stateStore.renewLease({
5059
+ runId: job.runId,
5060
+ leaseId: leased.lease.id,
5061
+ leaseDurationMs: this.leaseDurationMs
5062
+ }).catch((error) => {
5063
+ if (renewalFailed) return;
5064
+ renewalFailed = true;
5065
+ const failure = new InfrastructureFailureError("Agent Run Lease renewal failed.", { cause: error });
5066
+ binding.abort(failure.message);
5067
+ failRenewal(failure);
5068
+ });
5069
+ }, this.leaseRenewalIntervalMs);
5070
+ const execution = binding.execute(leased.message.input, job.runId, control, leased.run.executionState);
5071
+ const outcome = await Promise.race([
5072
+ execution.then((value) => ({ kind: "terminal", value })).catch((error) => ({ kind: "error", error })),
5073
+ suspendedSignal.then(() => ({ kind: "suspended" })),
5074
+ renewalFailureSignal.then((error) => ({ kind: "renewal_failure", error }))
5075
+ ]);
5076
+ if (outcome.kind === "suspended") {
5077
+ void execution.catch(() => void 0);
5078
+ return;
5079
+ }
5080
+ if (outcome.kind === "error") {
5081
+ await this.finishFailedRun(job, outcome.error);
5082
+ return;
5083
+ }
5084
+ if (outcome.kind === "renewal_failure") {
5085
+ await execution.catch(() => void 0);
5086
+ await this.finishFailedRun(job, outcome.error);
5087
+ return;
5088
+ }
5089
+ await this.finishRun(job, outcome.value);
5090
+ } catch (error) {
5091
+ const current = await this.stateStore.getRun(job.runId);
5092
+ if (!current || ["completed", "failed", "cancelled", "suspended"].includes(current.state)) {
5093
+ if (current) this.notifyRun(current);
5094
+ return;
5095
+ }
5096
+ if (current.state !== "running") {
5097
+ this.notifyRun(current);
5098
+ return;
5099
+ }
5100
+ await this.finishFailedRun(job, error);
5101
+ } finally {
5102
+ if (renewalTimer) clearInterval(renewalTimer);
5103
+ }
5104
+ }
5105
+ async finishRun(job, outcome) {
5106
+ const current = await this.stateStore.getRun(job.runId);
5107
+ if (!current || ["completed", "failed", "cancelled"].includes(current.state)) {
5108
+ if (current) this.notifyRun(current);
5109
+ return;
5110
+ }
5111
+ const completed = await this.stateStore.completeRun({ runId: current.id, outcome });
5112
+ this.notifyRun(completed);
5113
+ this.publishEvents();
5114
+ }
5115
+ async finishFailedRun(job, error) {
5116
+ const current = await this.stateStore.getRun(job.runId);
5117
+ if (!current || ["completed", "failed", "cancelled"].includes(current.state)) {
5118
+ if (current) this.notifyRun(current);
5119
+ return;
5120
+ }
5121
+ const message = error instanceof Error ? error.message : "Agent Run failed.";
5122
+ const outcome = { id: createId("out"), message };
5123
+ const retryable = isRetryableInfrastructureError(error);
5124
+ if (retryable && current.attempt < this.maxInfrastructureAttempts) {
5125
+ const retried = await this.stateStore.retryRun({ runId: current.id, reason: message });
5126
+ this.notifyRun(retried);
5127
+ this.pendingRuns.push(job);
5128
+ this.publishEvents();
5129
+ return;
5130
+ }
5131
+ const failed2 = retryable ? await this.stateStore.exhaustRun({ runId: current.id, outcome, reason: message }) : await this.stateStore.completeRun({ runId: current.id, state: "failed", outcome });
5132
+ this.notifyRun(failed2);
5133
+ this.publishEvents();
5134
+ }
5135
+ notifyRun(run) {
5136
+ this.runHandles.get(run.id)?.notify(run);
5137
+ const waiters = this.runWaiters.get(run.id);
5138
+ if (!waiters) return;
5139
+ this.runWaiters.delete(run.id);
5140
+ for (const resolve7 of waiters) resolve7();
5141
+ }
5142
+ publishEvents() {
5143
+ for (const subscription of this.eventSubscriptions.values()) {
5144
+ void this.deliverEvents(subscription).catch(() => void 0);
5145
+ }
5146
+ }
5147
+ async deliverEvents(subscription) {
5148
+ if (subscription.delivering) {
5149
+ subscription.redeliver = true;
5150
+ return;
5151
+ }
5152
+ subscription.delivering = true;
5153
+ try {
5154
+ do {
5155
+ subscription.redeliver = false;
5156
+ const checkpoint = await this.stateStore.getEventCheckpoint(subscription.consumerId);
5157
+ const events = await this.stateStore.listEvents(
5158
+ checkpoint.eventId ? { after: checkpoint.eventId } : void 0
5159
+ );
5160
+ for (const event of events) {
5161
+ if (this.eventSubscriptions.get(subscription.consumerId) !== subscription) return;
5162
+ const disposition = await subscription.listener(structuredClone(event));
5163
+ if (disposition?.type === "enqueue") {
5164
+ const delivered = await this.stateStore.acknowledgeEventAndEnqueueAgentInput({
5165
+ consumerId: subscription.consumerId,
5166
+ eventId: event.id,
5167
+ agentId: disposition.agentId,
5168
+ input: disposition.input
5169
+ });
5170
+ if (delivered.enqueued) {
5171
+ this.scheduleRun(delivered.enqueued.run.agentId, delivered.enqueued.run.id);
5172
+ this.publishEvents();
5173
+ }
5174
+ } else {
5175
+ await this.stateStore.acknowledgeEvent(subscription.consumerId, event.id);
5176
+ }
5177
+ }
5178
+ } while (subscription.redeliver);
5179
+ } finally {
5180
+ subscription.delivering = false;
5181
+ }
5182
+ }
5183
+ assertRunning() {
5184
+ if (this.stopped || activeRuntime !== this) throw new Error("Agent Runtime is stopped.");
5185
+ }
5186
+ };
5187
+ function isModelConfig(value) {
5188
+ if (typeof value !== "object" || value === null) return false;
5189
+ return "protocol" in value && "baseUrl" in value && "apiKey" in value;
5190
+ }
5191
+ function assertAgentOptions(options) {
5192
+ if (options.stream && isModelConfig(options.model)) {
5193
+ throw new Error("AgentOptions accepts either a complete model config or a custom stream, not both.");
5194
+ }
5195
+ }
5196
+ function isRetryableInfrastructureError(error) {
5197
+ if (error instanceof InfrastructureFailureError) return true;
5198
+ return typeof error === "object" && error !== null && "retryable" in error && error.retryable === true;
5199
+ }
5200
+
5201
+ // src/runtime/domain.ts
5202
+ function runEventPayload(metadata, payload = {}) {
5203
+ return metadata ? { ...payload, metadata: structuredClone(metadata) } : payload;
5204
+ }
5205
+
5206
+ // src/runtime/memory-store.ts
5207
+ function clone2(value) {
5208
+ return structuredClone(value);
5209
+ }
5210
+ function transitionError(entity, id, state, action) {
5211
+ return new Error(`Cannot ${action} ${entity} ${id} from state "${state}".`);
5212
+ }
5213
+ function requireTransition(entity, id, state, action, allowedStates) {
5214
+ if (!allowedStates.includes(state)) {
5215
+ throw transitionError(entity, id, state, action);
5216
+ }
5217
+ }
5218
+ function now(input) {
5219
+ const date = input ? new Date(input) : /* @__PURE__ */ new Date();
5220
+ return { date, timestamp: createTimestamp(date) };
5221
+ }
5222
+ var InMemoryRuntimeStateStore = class {
5223
+ agents = /* @__PURE__ */ new Map();
5224
+ messages = /* @__PURE__ */ new Map();
5225
+ runs = /* @__PURE__ */ new Map();
5226
+ toolCalls = /* @__PURE__ */ new Map();
5227
+ events = [];
5228
+ eventCheckpoints = /* @__PURE__ */ new Map();
5229
+ async createAgent(input) {
5230
+ const { timestamp } = now();
5231
+ const agent = {
5232
+ id: createId("agt"),
5233
+ sessionId: input.sessionId,
5234
+ state: "active",
5235
+ createdAt: timestamp,
5236
+ updatedAt: timestamp
5237
+ };
5238
+ this.agents.set(agent.id, agent);
5239
+ this.recordEvent("agent_created", { agentId: agent.id });
5240
+ return clone2(agent);
5241
+ }
5242
+ async getAgent(agentId) {
5243
+ const agent = this.agents.get(agentId);
5244
+ return agent ? clone2(agent) : void 0;
5245
+ }
5246
+ async listAgents() {
5247
+ return clone2([...this.agents.values()]);
5248
+ }
5249
+ async setAgentState(agentId, state) {
5250
+ const agent = this.requireAgent(agentId);
5251
+ if (agent.state === state) return clone2(agent);
5252
+ const { timestamp } = now();
5253
+ agent.state = state;
5254
+ agent.updatedAt = timestamp;
5255
+ this.recordEvent(state === "paused" ? "agent_paused" : "agent_resumed", { agentId });
5256
+ return clone2(agent);
5257
+ }
5258
+ async enqueueAgentInput(input) {
5259
+ return this.enqueueAgentInputState(input);
5260
+ }
5261
+ enqueueAgentInputState(input) {
5262
+ this.requireAgent(input.agentId);
5263
+ const suspended = [...this.runs.values()].filter((run2) => run2.agentId === input.agentId && run2.state === "suspended").sort((a, b) => a.createdAt.localeCompare(b.createdAt))[0];
5264
+ const { timestamp } = now();
5265
+ const messageId = createId("rmsg");
5266
+ const runId = suspended?.id ?? createId("run");
5267
+ const message = {
5268
+ id: messageId,
5269
+ agentId: input.agentId,
5270
+ kind: "agent_input",
5271
+ input: clone2(input.input),
5272
+ state: "queued",
5273
+ attempts: 0,
5274
+ runId,
5275
+ createdAt: timestamp,
5276
+ updatedAt: timestamp
5277
+ };
5278
+ if (suspended) {
5279
+ suspended.state = "queued";
5280
+ suspended.messageId = messageId;
5281
+ delete suspended.suspensionReason;
5282
+ delete suspended.inputRequest;
5283
+ suspended.updatedAt = timestamp;
5284
+ message.runId = suspended.id;
5285
+ }
5286
+ const run = {
5287
+ id: runId,
5288
+ agentId: input.agentId,
5289
+ messageId,
5290
+ state: "queued",
5291
+ attempt: 0,
5292
+ ...suspended?.metadata ? { metadata: clone2(suspended.metadata) } : input.input.metadata ? { metadata: clone2(input.input.metadata) } : {},
5293
+ createdAt: timestamp,
5294
+ updatedAt: timestamp
5295
+ };
5296
+ this.messages.set(message.id, message);
5297
+ this.runs.set(run.id, suspended ?? run);
5298
+ this.recordEvent("message_enqueued", { agentId: input.agentId, messageId });
5299
+ this.recordEvent("run_enqueued", { agentId: input.agentId, messageId, runId }, runEventPayload(suspended?.metadata ?? input.input.metadata));
5300
+ return { message: clone2(message), run: clone2(suspended ?? run), resumed: Boolean(suspended) };
5301
+ }
5302
+ async getMessage(messageId) {
5303
+ const message = this.messages.get(messageId);
5304
+ return message ? clone2(message) : void 0;
5305
+ }
5306
+ async getRun(runId) {
5307
+ const run = this.runs.get(runId);
5308
+ return run ? clone2(run) : void 0;
5309
+ }
5310
+ async listRuns(input = {}) {
5311
+ return clone2([...this.runs.values()].filter((run) => input.agentId === void 0 || run.agentId === input.agentId).filter((run) => !input.states || input.states.includes(run.state)).sort((a, b) => a.createdAt.localeCompare(b.createdAt) || String(a.id).localeCompare(String(b.id))));
5312
+ }
5313
+ async listActiveRuns() {
5314
+ return this.listRuns({ states: ["queued", "running", "suspended"] });
5315
+ }
5316
+ async leaseRun(input) {
5317
+ const run = this.requireRun(input.runId);
5318
+ requireTransition("Agent Run", run.id, run.state, "lease", ["queued"]);
5319
+ const message = this.requireMessage(run.messageId);
5320
+ requireTransition("Runtime Message", message.id, message.state, "lease", ["queued"]);
5321
+ const agent = this.requireAgent(run.agentId);
5322
+ if (agent.state !== "active") {
5323
+ throw transitionError("Agent", agent.id, agent.state, "lease a Run for");
5324
+ }
5325
+ if ([...this.runs.values()].some(
5326
+ (candidate) => candidate.agentId === run.agentId && candidate.id !== run.id && (candidate.state === "running" || candidate.state === "suspended")
5327
+ )) {
5328
+ throw new Error(`Agent ${run.agentId} already has an active Run.`);
5329
+ }
5330
+ if (!Number.isFinite(input.leaseDurationMs) || input.leaseDurationMs <= 0) {
5331
+ throw new Error("Lease duration must be a positive finite number.");
5332
+ }
5333
+ const { date, timestamp } = now(input.now);
5334
+ const lease = {
5335
+ id: createId("lease"),
5336
+ runId: run.id,
5337
+ workerId: input.workerId,
5338
+ leasedAt: timestamp,
5339
+ expiresAt: createTimestamp(new Date(date.getTime() + input.leaseDurationMs))
5340
+ };
5341
+ run.state = "running";
5342
+ run.attempt += 1;
5343
+ run.leaseId = lease.id;
5344
+ run.updatedAt = timestamp;
5345
+ message.state = "leased";
5346
+ message.attempts += 1;
5347
+ message.lease = lease;
5348
+ message.updatedAt = timestamp;
5349
+ this.recordEvent("run_leased", { agentId: run.agentId, messageId: message.id, runId: run.id }, lease);
5350
+ return { run: clone2(run), message: clone2(message), lease: clone2(lease) };
5351
+ }
5352
+ async renewLease(input) {
5353
+ const run = this.requireRun(input.runId);
5354
+ requireTransition("Agent Run", run.id, run.state, "renew Lease", ["running"]);
5355
+ const message = this.requireMessage(run.messageId);
5356
+ requireTransition("Runtime Message", message.id, message.state, "renew Lease", ["leased"]);
5357
+ if (run.leaseId !== input.leaseId || message.lease?.id !== input.leaseId) {
5358
+ throw new Error(`Lease ${input.leaseId} does not own Agent Run ${run.id}.`);
5359
+ }
5360
+ if (!Number.isFinite(input.leaseDurationMs) || input.leaseDurationMs <= 0) {
5361
+ throw new Error("Lease duration must be a positive finite number.");
5362
+ }
5363
+ const { date, timestamp } = now(input.now);
5364
+ const lease = {
5365
+ ...message.lease,
5366
+ expiresAt: createTimestamp(new Date(date.getTime() + input.leaseDurationMs))
5367
+ };
5368
+ message.lease = lease;
5369
+ message.updatedAt = timestamp;
5370
+ return clone2(lease);
5371
+ }
5372
+ async suspendRun(input) {
5373
+ const run = this.requireRun(input.runId);
5374
+ requireTransition("Agent Run", run.id, run.state, "suspend", ["running"]);
5375
+ const message = this.requireMessage(run.messageId);
5376
+ const { timestamp } = now();
5377
+ run.state = "suspended";
5378
+ run.suspensionReason = input.reason;
5379
+ if (input.inputRequest) run.inputRequest = clone2(input.inputRequest);
5380
+ if (input.executionState) run.executionState = clone2(input.executionState);
5381
+ delete run.leaseId;
5382
+ run.updatedAt = timestamp;
5383
+ requireTransition("Runtime Message", message.id, message.state, "acknowledge", ["leased"]);
5384
+ message.state = "acknowledged";
5385
+ delete message.lease;
5386
+ message.updatedAt = timestamp;
5387
+ this.recordEvent("run_suspended", { agentId: run.agentId, messageId: message.id, runId: run.id }, runEventPayload(run.metadata, {
5388
+ reason: input.reason
5389
+ }));
5390
+ return clone2(run);
5391
+ }
5392
+ async completeRun(input) {
5393
+ const run = this.requireRun(input.runId);
5394
+ requireTransition("Agent Run", run.id, run.state, "complete", ["running"]);
5395
+ const message = this.requireMessage(run.messageId);
5396
+ requireTransition("Runtime Message", message.id, message.state, "acknowledge", ["leased"]);
5397
+ const { timestamp } = now();
5398
+ const state = input.state ?? "completed";
5399
+ run.state = state;
5400
+ run.outcome = clone2(input.outcome);
5401
+ delete run.inputRequest;
5402
+ delete run.executionState;
5403
+ delete run.leaseId;
5404
+ run.updatedAt = timestamp;
5405
+ message.state = "acknowledged";
5406
+ delete message.lease;
5407
+ message.updatedAt = timestamp;
5408
+ this.recordEvent("run_completed", { agentId: run.agentId, messageId: run.messageId, runId: run.id }, runEventPayload(run.metadata, {
5409
+ state,
5410
+ outcome: input.outcome
5411
+ }));
5412
+ this.recordEvent("message_acknowledged", { agentId: message.agentId, messageId: message.id });
5413
+ return clone2(run);
5414
+ }
5415
+ async retryRun(input) {
5416
+ const run = this.requireRun(input.runId);
5417
+ requireTransition("Agent Run", run.id, run.state, "retry", ["running"]);
5418
+ const message = this.requireMessage(run.messageId);
5419
+ requireTransition("Runtime Message", message.id, message.state, "retry", ["leased"]);
5420
+ const { timestamp } = now();
5421
+ run.state = "queued";
5422
+ delete run.leaseId;
5423
+ run.updatedAt = timestamp;
5424
+ message.state = "queued";
5425
+ delete message.lease;
5426
+ message.updatedAt = timestamp;
5427
+ this.recordEvent("run_retry_scheduled", {
5428
+ agentId: run.agentId,
5429
+ messageId: message.id,
5430
+ runId: run.id
5431
+ }, { reason: input.reason, attempt: run.attempt });
5432
+ return clone2(run);
5433
+ }
5434
+ async exhaustRun(input) {
5435
+ const run = this.requireRun(input.runId);
5436
+ requireTransition("Agent Run", run.id, run.state, "exhaust retries", ["running"]);
5437
+ const message = this.requireMessage(run.messageId);
5438
+ requireTransition("Runtime Message", message.id, message.state, "dead-letter", ["leased"]);
5439
+ const { timestamp } = now();
5440
+ run.state = "failed";
5441
+ run.outcome = clone2(input.outcome);
5442
+ delete run.inputRequest;
5443
+ delete run.executionState;
5444
+ delete run.leaseId;
5445
+ run.updatedAt = timestamp;
5446
+ message.state = "dead_lettered";
5447
+ message.deadLetterReason = input.reason;
5448
+ delete message.lease;
5449
+ message.updatedAt = timestamp;
5450
+ this.recordEvent("run_completed", { agentId: run.agentId, messageId: message.id, runId: run.id }, runEventPayload(run.metadata, {
5451
+ state: "failed",
5452
+ outcome: input.outcome
5453
+ }));
5454
+ this.recordEvent("message_dead_lettered", { agentId: run.agentId, messageId: message.id }, {
5455
+ reason: input.reason
5456
+ });
5457
+ return clone2(run);
5458
+ }
5459
+ async abortRun(input) {
5460
+ const run = this.requireRun(input.runId);
5461
+ requireTransition("Agent Run", run.id, run.state, "abort", ["queued", "running", "suspended"]);
5462
+ const message = this.requireMessage(run.messageId);
5463
+ const { timestamp } = now();
5464
+ run.state = "cancelled";
5465
+ run.outcome = clone2(input.outcome);
5466
+ delete run.inputRequest;
5467
+ delete run.executionState;
5468
+ delete run.leaseId;
5469
+ run.updatedAt = timestamp;
5470
+ if (message.state === "queued" || message.state === "leased") {
5471
+ message.state = "acknowledged";
5472
+ delete message.lease;
5473
+ message.updatedAt = timestamp;
5474
+ }
5475
+ this.recordEvent("run_aborted", { agentId: run.agentId, messageId: message.id, runId: run.id }, runEventPayload(run.metadata, {
5476
+ state: "cancelled",
5477
+ outcome: input.outcome
5478
+ }));
5479
+ return clone2(run);
5480
+ }
5481
+ async recoverExpiredLeases(input) {
5482
+ const { date, timestamp } = now(input);
5483
+ const recovered = [];
5484
+ for (const run of this.runs.values()) {
5485
+ if (run.state !== "running" || !run.leaseId) continue;
5486
+ const message = this.requireMessage(run.messageId);
5487
+ if (message.state !== "leased" || !message.lease || Date.parse(message.lease.expiresAt) > date.getTime()) continue;
5488
+ run.state = "queued";
5489
+ delete run.leaseId;
5490
+ run.updatedAt = timestamp;
5491
+ message.state = "queued";
5492
+ delete message.lease;
5493
+ message.updatedAt = timestamp;
5494
+ this.recordEvent("lease_expired", { agentId: run.agentId, messageId: message.id, runId: run.id });
5495
+ recovered.push(clone2(run));
5496
+ }
5497
+ return recovered;
5498
+ }
5499
+ async recoverLeases() {
5500
+ const { timestamp } = now();
5501
+ const recovered = [];
5502
+ for (const run of this.runs.values()) {
5503
+ if (run.state !== "running" || !run.leaseId) continue;
5504
+ const message = this.requireMessage(run.messageId);
5505
+ if (message.state !== "leased" || !message.lease) continue;
5506
+ run.state = "queued";
5507
+ delete run.leaseId;
5508
+ run.updatedAt = timestamp;
5509
+ message.state = "queued";
5510
+ delete message.lease;
5511
+ message.updatedAt = timestamp;
5512
+ this.recordEvent("lease_recovered", { agentId: run.agentId, messageId: message.id, runId: run.id });
5513
+ recovered.push(clone2(run));
5514
+ }
5515
+ return recovered;
5516
+ }
5517
+ async createToolCall(input) {
5518
+ this.requireAgent(input.agentId);
5519
+ this.requireRun(input.runId);
5520
+ const { timestamp } = now();
5521
+ const toolCall = {
5522
+ id: createId("call"),
5523
+ agentId: input.agentId,
5524
+ runId: input.runId,
5525
+ name: input.name,
5526
+ args: clone2(input.args),
5527
+ state: "queued",
5528
+ createdAt: timestamp,
5529
+ updatedAt: timestamp
5530
+ };
5531
+ this.toolCalls.set(toolCall.id, toolCall);
5532
+ this.recordEvent("tool_call_created", { agentId: input.agentId, runId: input.runId, toolCallId: toolCall.id });
5533
+ return clone2(toolCall);
5534
+ }
5535
+ async startToolCall(toolCallId) {
5536
+ const toolCall = this.requireToolCall(toolCallId);
5537
+ requireTransition("Tool Call", toolCall.id, toolCall.state, "start", ["queued"]);
5538
+ const { timestamp } = now();
5539
+ toolCall.state = "running";
5540
+ toolCall.updatedAt = timestamp;
5541
+ this.recordEvent("tool_call_started", { agentId: toolCall.agentId, runId: toolCall.runId, toolCallId: toolCall.id });
5542
+ return clone2(toolCall);
5543
+ }
5544
+ async completeToolCall(input) {
5545
+ const toolCall = this.requireToolCall(input.toolCallId);
5546
+ const state = input.state ?? (input.result.ok ? "completed" : "failed");
5547
+ requireTransition(
5548
+ "Tool Call",
5549
+ toolCall.id,
5550
+ toolCall.state,
5551
+ "complete",
5552
+ state === "failed" ? ["queued", "running"] : ["running"]
5553
+ );
5554
+ const { timestamp } = now();
5555
+ toolCall.state = state;
5556
+ toolCall.result = clone2(input.result);
5557
+ toolCall.updatedAt = timestamp;
5558
+ this.recordEvent(state === "failed" ? "tool_call_failed" : "tool_call_completed", {
5559
+ agentId: toolCall.agentId,
5560
+ runId: toolCall.runId,
5561
+ toolCallId: toolCall.id
5562
+ }, { state: toolCall.state });
5563
+ return clone2(toolCall);
5564
+ }
5565
+ async markToolCallIndeterminate(input) {
5566
+ const toolCall = this.requireToolCall(input.toolCallId);
5567
+ requireTransition("Tool Call", toolCall.id, toolCall.state, "mark indeterminate", ["running"]);
5568
+ const { timestamp } = now();
5569
+ toolCall.state = "indeterminate";
5570
+ toolCall.indeterminateReason = input.reason;
5571
+ toolCall.updatedAt = timestamp;
5572
+ this.recordEvent("tool_call_indeterminate", {
5573
+ agentId: toolCall.agentId,
5574
+ runId: toolCall.runId,
5575
+ toolCallId: toolCall.id
5576
+ }, { reason: input.reason });
5577
+ return clone2(toolCall);
5578
+ }
5579
+ async getToolCall(toolCallId) {
5580
+ const toolCall = this.toolCalls.get(toolCallId);
5581
+ return toolCall ? clone2(toolCall) : void 0;
5582
+ }
5583
+ async listEvents(cursor = {}) {
5584
+ let events = this.events;
5585
+ if (cursor.after) {
5586
+ const index = events.findIndex((event) => event.id === cursor.after);
5587
+ if (index >= 0) events = events.slice(index + 1);
5588
+ }
5589
+ return clone2(cursor.limit === void 0 ? events : events.slice(0, cursor.limit));
5590
+ }
5591
+ async recordEvent(input, related = {}, payload) {
5592
+ this.recordEventInternal(typeof input === "string" ? input : input.kind, typeof input === "string" ? related : input.related ?? {}, typeof input === "string" ? payload : input.payload);
5593
+ return clone2(this.events.at(-1));
5594
+ }
5595
+ async getEventCheckpoint(consumerId) {
5596
+ return clone2(this.eventCheckpoints.get(consumerId) ?? {
5597
+ consumerId,
5598
+ sequence: 0,
5599
+ updatedAt: createTimestamp(/* @__PURE__ */ new Date(0))
5600
+ });
5601
+ }
5602
+ async acknowledgeEvent(consumerId, eventId) {
5603
+ const event = this.events.find((candidate) => candidate.id === eventId);
5604
+ if (!event) throw new Error(`Runtime Event not found: ${eventId}.`);
5605
+ const current = await this.getEventCheckpoint(consumerId);
5606
+ if (event.sequence <= current.sequence) return current;
5607
+ if (event.sequence !== current.sequence + 1) {
5608
+ throw new Error(`Runtime Event Consumer Checkpoint cannot advance from sequence ${current.sequence} to ${event.sequence}.`);
5609
+ }
5610
+ const checkpoint = {
5611
+ consumerId,
5612
+ sequence: event.sequence,
5613
+ eventId: event.id,
5614
+ updatedAt: createTimestamp()
5615
+ };
5616
+ this.eventCheckpoints.set(consumerId, checkpoint);
5617
+ return clone2(checkpoint);
5618
+ }
5619
+ async acknowledgeEventAndEnqueueAgentInput(input) {
5620
+ const event = this.events.find((candidate) => candidate.id === input.eventId);
5621
+ if (!event) throw new Error(`Runtime Event not found: ${input.eventId}.`);
5622
+ const current = this.eventCheckpoints.get(input.consumerId) ?? {
5623
+ consumerId: input.consumerId,
5624
+ sequence: 0,
5625
+ updatedAt: createTimestamp(/* @__PURE__ */ new Date(0))
5626
+ };
5627
+ if (event.sequence <= current.sequence) return { checkpoint: clone2(current) };
5628
+ if (event.sequence !== current.sequence + 1) {
5629
+ throw new Error(`Runtime Event Consumer Checkpoint cannot advance from sequence ${current.sequence} to ${event.sequence}.`);
5630
+ }
5631
+ this.requireAgent(input.agentId);
5632
+ const enqueued = this.enqueueAgentInputState(input);
5633
+ const checkpoint = {
5634
+ consumerId: input.consumerId,
5635
+ sequence: event.sequence,
5636
+ eventId: event.id,
5637
+ updatedAt: createTimestamp()
5638
+ };
5639
+ this.eventCheckpoints.set(input.consumerId, checkpoint);
5640
+ return { checkpoint: clone2(checkpoint), enqueued };
5641
+ }
5642
+ requireAgent(agentId) {
5643
+ const agent = this.agents.get(agentId);
5644
+ if (!agent) {
5645
+ throw new Error(`Agent not found: ${agentId}.`);
5646
+ }
5647
+ return agent;
5648
+ }
5649
+ requireMessage(messageId) {
5650
+ const message = this.messages.get(messageId);
5651
+ if (!message) {
5652
+ throw new Error(`Runtime Message not found: ${messageId}.`);
5653
+ }
5654
+ return message;
5655
+ }
5656
+ requireRun(runId) {
5657
+ const run = this.runs.get(runId);
5658
+ if (!run) {
5659
+ throw new Error(`Agent Run not found: ${runId}.`);
5660
+ }
5661
+ return run;
5662
+ }
5663
+ requireToolCall(toolCallId) {
5664
+ const toolCall = this.toolCalls.get(toolCallId);
5665
+ if (!toolCall) {
5666
+ throw new Error(`Tool Call not found: ${toolCallId}.`);
5667
+ }
5668
+ return toolCall;
5669
+ }
5670
+ recordEventInternal(kind, related, payload) {
5671
+ this.events.push({
5672
+ id: createId("evt"),
5673
+ sequence: this.events.length + 1,
5674
+ kind,
5675
+ ...related,
5676
+ ...payload !== void 0 ? { payload: clone2(payload) } : {},
5677
+ createdAt: createTimestamp()
5678
+ });
5679
+ }
5680
+ };
5681
+
5682
+ // src/runtime/sqlite-store.ts
5683
+ import { Database } from "bun:sqlite";
5684
+
5685
+ // src/runtime/runtime-schema.ts
5686
+ var RUNTIME_SCHEMA_SQL = `
5687
+ CREATE TABLE IF NOT EXISTS agents (
5688
+ id TEXT PRIMARY KEY NOT NULL,
5689
+ session_id TEXT NOT NULL,
5690
+ state TEXT NOT NULL CHECK (state IN ('active', 'paused')),
5691
+ created_at TEXT NOT NULL,
5692
+ updated_at TEXT NOT NULL
5693
+ );
5694
+
5695
+ CREATE TABLE IF NOT EXISTS runtime_messages (
5696
+ id TEXT PRIMARY KEY NOT NULL,
5697
+ agent_id TEXT NOT NULL REFERENCES agents(id),
5698
+ kind TEXT NOT NULL CHECK (kind IN ('agent_input')),
5699
+ payload_json TEXT NOT NULL,
5700
+ state TEXT NOT NULL CHECK (state IN ('queued', 'leased', 'acknowledged', 'dead_lettered')),
5701
+ attempts INTEGER NOT NULL DEFAULT 0,
5702
+ run_id TEXT,
5703
+ dead_letter_reason TEXT,
5704
+ created_at TEXT NOT NULL,
5705
+ updated_at TEXT NOT NULL
5706
+ );
5707
+
5708
+ CREATE TABLE IF NOT EXISTS agent_runs (
5709
+ id TEXT PRIMARY KEY NOT NULL,
5710
+ agent_id TEXT NOT NULL REFERENCES agents(id),
5711
+ message_id TEXT NOT NULL REFERENCES runtime_messages(id),
5712
+ state TEXT NOT NULL CHECK (state IN ('queued', 'running', 'suspended', 'completed', 'failed', 'cancelled')),
5713
+ attempt INTEGER NOT NULL DEFAULT 0,
5714
+ lease_id TEXT,
5715
+ outcome_json TEXT,
5716
+ suspension_reason TEXT,
5717
+ input_request_json TEXT,
5718
+ execution_state_json TEXT,
5719
+ metadata_json TEXT,
5720
+ created_at TEXT NOT NULL,
5721
+ updated_at TEXT NOT NULL
5722
+ );
5723
+
5724
+ CREATE TABLE IF NOT EXISTS runtime_leases (
5725
+ id TEXT PRIMARY KEY NOT NULL,
5726
+ run_id TEXT NOT NULL UNIQUE REFERENCES agent_runs(id),
5727
+ worker_id TEXT NOT NULL,
5728
+ leased_at TEXT NOT NULL,
5729
+ expires_at TEXT NOT NULL
5730
+ );
5731
+
5732
+ CREATE TABLE IF NOT EXISTS runtime_events (
5733
+ id TEXT PRIMARY KEY NOT NULL,
5734
+ sequence INTEGER NOT NULL DEFAULT 0,
5735
+ kind TEXT NOT NULL,
5736
+ agent_id TEXT,
5737
+ message_id TEXT,
5738
+ run_id TEXT,
5739
+ tool_call_id TEXT,
5740
+ payload_json TEXT,
5741
+ created_at TEXT NOT NULL
5742
+ );
5743
+
5744
+ CREATE TABLE IF NOT EXISTS runtime_tool_calls (
5745
+ id TEXT PRIMARY KEY NOT NULL,
5746
+ agent_id TEXT NOT NULL REFERENCES agents(id),
5747
+ run_id TEXT NOT NULL REFERENCES agent_runs(id),
5748
+ name TEXT NOT NULL,
5749
+ args_json TEXT NOT NULL,
5750
+ state TEXT NOT NULL CHECK (state IN ('queued', 'running', 'completed', 'failed', 'indeterminate')),
5751
+ result_json TEXT,
5752
+ indeterminate_reason TEXT,
5753
+ created_at TEXT NOT NULL,
5754
+ updated_at TEXT NOT NULL
5755
+ );
5756
+
5757
+ CREATE TABLE IF NOT EXISTS runtime_event_consumers (
5758
+ consumer_id TEXT PRIMARY KEY NOT NULL,
5759
+ sequence INTEGER NOT NULL DEFAULT 0,
5760
+ event_id TEXT,
5761
+ updated_at TEXT NOT NULL
5762
+ );
5763
+
5764
+ CREATE INDEX IF NOT EXISTS runtime_messages_runnable_idx
5765
+ ON runtime_messages (agent_id, state, created_at);
5766
+ CREATE INDEX IF NOT EXISTS agent_runs_agent_state_idx
5767
+ ON agent_runs (agent_id, state, created_at);
5768
+ CREATE INDEX IF NOT EXISTS runtime_events_delivery_idx
5769
+ ON runtime_events (sequence);
5770
+
5771
+ CREATE INDEX IF NOT EXISTS agent_runs_runnable_idx
5772
+ ON agent_runs (state, created_at);
5773
+ `;
5774
+ function initializeRuntimeSchema(database) {
5775
+ const initialize = database.transaction(() => {
5776
+ for (const statement of RUNTIME_SCHEMA_SQL.split(";")) {
5777
+ const sql = statement.trim();
5778
+ if (sql.length > 0) {
5779
+ database.run(sql);
5780
+ }
5781
+ }
5782
+ try {
5783
+ database.run("ALTER TABLE agent_runs ADD COLUMN metadata_json TEXT");
5784
+ } catch (error) {
5785
+ if (!(error instanceof Error) || !/duplicate column name/i.test(error.message)) throw error;
5786
+ }
5787
+ });
5788
+ initialize();
5789
+ }
5790
+
5791
+ // src/runtime/sqlite-store.ts
5792
+ function clone3(value) {
5793
+ return structuredClone(value);
5794
+ }
5795
+ function json(value) {
5796
+ return JSON.stringify(value);
5797
+ }
5798
+ function parse(value) {
5799
+ return JSON.parse(value);
5800
+ }
5801
+ function optionalJson(value) {
5802
+ return value === null ? void 0 : parse(value);
5803
+ }
5804
+ function transitionError2(entity, id, state, action) {
5805
+ return new Error(`Cannot ${action} ${entity} ${id} from state "${state}".`);
5806
+ }
5807
+ function requireTransition2(entity, id, state, action, allowedStates) {
5808
+ if (!allowedStates.includes(state)) {
5809
+ throw transitionError2(entity, id, state, action);
5810
+ }
5811
+ }
5812
+ function timestampFor(input) {
5813
+ const date = input ? new Date(input) : /* @__PURE__ */ new Date();
5814
+ return { date, timestamp: createTimestamp(date) };
5815
+ }
5816
+ var SqliteRuntimeStateStore = class {
5817
+ database;
5818
+ constructor(filename = ":memory:") {
5819
+ this.database = new Database(filename, { create: true, readwrite: true, strict: true });
5820
+ this.database.run("PRAGMA foreign_keys = ON");
5821
+ initializeRuntimeSchema(this.database);
5822
+ }
5823
+ close() {
5824
+ this.database.close();
5825
+ }
5826
+ async createAgent(input) {
5827
+ const { timestamp } = timestampFor();
5828
+ const agent = {
5829
+ id: createId("agt"),
5830
+ sessionId: input.sessionId,
5831
+ state: "active",
5832
+ createdAt: timestamp,
5833
+ updatedAt: timestamp
5834
+ };
5835
+ const create = this.database.transaction(() => {
5836
+ this.database.run(
5837
+ "INSERT INTO agents (id, session_id, state, created_at, updated_at) VALUES (?, ?, ?, ?, ?)",
5838
+ [agent.id, agent.sessionId, agent.state, agent.createdAt, agent.updatedAt]
5839
+ );
5840
+ this.recordEvent("agent_created", { agentId: agent.id });
5841
+ });
5842
+ create();
5843
+ return clone3(agent);
5844
+ }
5845
+ async getAgent(agentId) {
5846
+ const row = this.database.query("SELECT * FROM agents WHERE id = ?").get(agentId);
5847
+ return row ? this.agentFromRow(row) : void 0;
5848
+ }
5849
+ async listAgents() {
5850
+ const rows = this.database.query("SELECT * FROM agents ORDER BY created_at, id").all();
5851
+ return rows.map((row) => this.agentFromRow(row));
5852
+ }
5853
+ async setAgentState(agentId, state) {
5854
+ const current = this.requireAgent(agentId);
5855
+ if (current.state === state) return clone3(current);
5856
+ const { timestamp } = timestampFor();
5857
+ const updated = this.database.transaction(() => {
5858
+ this.database.run("UPDATE agents SET state = ?, updated_at = ? WHERE id = ?", [state, timestamp, agentId]);
5859
+ this.recordEvent(state === "paused" ? "agent_paused" : "agent_resumed", { agentId });
5860
+ return this.requireAgent(agentId);
5861
+ })();
5862
+ return clone3(updated);
5863
+ }
5864
+ async enqueueAgentInput(input) {
5865
+ const enqueue = this.database.transaction(() => this.enqueueAgentInputState(input));
5866
+ return clone3(enqueue());
5867
+ }
5868
+ enqueueAgentInputState(input) {
5869
+ this.requireAgent(input.agentId);
5870
+ const { timestamp } = timestampFor();
5871
+ const messageId = createId("rmsg");
5872
+ const suspendedRow = this.database.query(
5873
+ "SELECT * FROM agent_runs WHERE agent_id = ? AND state = 'suspended' ORDER BY created_at, id LIMIT 1"
5874
+ ).get(input.agentId);
5875
+ const runId = suspendedRow?.id ?? createId("run");
5876
+ const message = {
5877
+ id: messageId,
5878
+ agentId: input.agentId,
5879
+ kind: "agent_input",
5880
+ input: clone3(input.input),
5881
+ state: "queued",
5882
+ attempts: 0,
5883
+ runId,
5884
+ createdAt: timestamp,
5885
+ updatedAt: timestamp
5886
+ };
5887
+ const run = {
5888
+ id: runId,
5889
+ agentId: input.agentId,
5890
+ messageId,
5891
+ state: "queued",
5892
+ attempt: 0,
5893
+ ...suspendedRow?.metadata_json ? { metadata: parse(suspendedRow.metadata_json) } : input.input.metadata ? { metadata: clone3(input.input.metadata) } : {},
5894
+ createdAt: timestamp,
5895
+ updatedAt: timestamp
5896
+ };
5897
+ if (suspendedRow) {
5898
+ message.runId = runId;
5899
+ }
5900
+ this.insertMessage(message);
5901
+ if (suspendedRow) {
5902
+ this.database.run(
5903
+ "UPDATE agent_runs SET message_id = ?, state = 'queued', lease_id = NULL, suspension_reason = NULL, input_request_json = NULL, updated_at = ? WHERE id = ?",
5904
+ [messageId, timestamp, runId]
5905
+ );
5906
+ } else {
5907
+ this.database.run(
5908
+ "INSERT INTO agent_runs (id, agent_id, message_id, state, attempt, lease_id, outcome_json, suspension_reason, input_request_json, execution_state_json, metadata_json, created_at, updated_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",
5909
+ [run.id, run.agentId, run.messageId, run.state, run.attempt, null, null, null, null, null, run.metadata ? json(run.metadata) : null, run.createdAt, run.updatedAt]
5910
+ );
5911
+ }
5912
+ this.recordEventInternal("message_enqueued", { agentId: input.agentId, messageId });
5913
+ const storedRun = this.requireRun(runId);
5914
+ this.recordEventInternal("run_enqueued", { agentId: input.agentId, messageId, runId }, runEventPayload(storedRun.metadata));
5915
+ return { message: clone3(message), run: clone3(storedRun), resumed: Boolean(suspendedRow) };
5916
+ }
5917
+ async getMessage(messageId) {
5918
+ const row = this.messageRow(messageId);
5919
+ return row ? this.messageFromRow(row) : void 0;
5920
+ }
5921
+ async getRun(runId) {
5922
+ const row = this.runRow(runId);
5923
+ return row ? this.runFromRow(row) : void 0;
5924
+ }
5925
+ async listRuns(input = {}) {
5926
+ const clauses = [];
5927
+ const params = [];
5928
+ if (input.agentId) {
5929
+ clauses.push("agent_id = ?");
5930
+ params.push(input.agentId);
5931
+ }
5932
+ if (input.states && input.states.length > 0) {
5933
+ clauses.push(`state IN (${input.states.map(() => "?").join(", ")})`);
5934
+ params.push(...input.states);
5935
+ }
5936
+ const where = clauses.length > 0 ? `WHERE ${clauses.join(" AND ")}` : "";
5937
+ const rows = this.database.query(`SELECT * FROM agent_runs ${where} ORDER BY created_at, id`).all(...params);
5938
+ return rows.map((row) => this.runFromRow(row));
5939
+ }
5940
+ async listActiveRuns() {
5941
+ const rows = this.database.query(
5942
+ "SELECT * FROM agent_runs WHERE state IN ('queued', 'running', 'suspended') ORDER BY created_at, id"
5943
+ ).all();
5944
+ return rows.map((row) => this.runFromRow(row));
5945
+ }
5946
+ async leaseRun(input) {
5947
+ const lease = this.database.transaction(() => {
5948
+ const run2 = this.requireRun(input.runId);
5949
+ requireTransition2("Agent Run", run2.id, run2.state, "lease", ["queued"]);
5950
+ const message2 = this.requireMessage(run2.messageId);
5951
+ requireTransition2("Runtime Message", message2.id, message2.state, "lease", ["queued"]);
5952
+ const agent = this.requireAgent(run2.agentId);
5953
+ if (agent.state !== "active") {
5954
+ throw transitionError2("Agent", agent.id, agent.state, "lease a Run for");
5955
+ }
5956
+ const activeRun = this.database.query(
5957
+ "SELECT id FROM agent_runs WHERE agent_id = ? AND id <> ? AND state IN ('running', 'suspended') LIMIT 1"
5958
+ ).get(run2.agentId, run2.id);
5959
+ if (activeRun) {
5960
+ throw new Error(`Agent ${run2.agentId} already has an active Run.`);
5961
+ }
5962
+ if (!Number.isFinite(input.leaseDurationMs) || input.leaseDurationMs <= 0) {
5963
+ throw new Error("Lease duration must be a positive finite number.");
5964
+ }
5965
+ const { date, timestamp } = timestampFor(input.now);
5966
+ const runtimeLease = {
5967
+ id: createId("lease"),
5968
+ runId: run2.id,
5969
+ workerId: input.workerId,
5970
+ leasedAt: timestamp,
5971
+ expiresAt: createTimestamp(new Date(date.getTime() + input.leaseDurationMs))
5972
+ };
5973
+ this.database.run(
5974
+ "INSERT INTO runtime_leases (id, run_id, worker_id, leased_at, expires_at) VALUES (?, ?, ?, ?, ?)",
5975
+ [runtimeLease.id, runtimeLease.runId, runtimeLease.workerId, runtimeLease.leasedAt, runtimeLease.expiresAt]
5976
+ );
5977
+ this.database.run(
5978
+ "UPDATE agent_runs SET state = ?, attempt = attempt + 1, lease_id = ?, updated_at = ? WHERE id = ?",
5979
+ ["running", runtimeLease.id, timestamp, run2.id]
5980
+ );
5981
+ this.database.run(
5982
+ "UPDATE runtime_messages SET state = ?, attempts = attempts + 1, updated_at = ? WHERE id = ?",
5983
+ ["leased", timestamp, message2.id]
5984
+ );
5985
+ this.recordEvent("run_leased", { agentId: run2.agentId, messageId: message2.id, runId: run2.id }, runtimeLease);
5986
+ return runtimeLease;
5987
+ })();
5988
+ const run = this.requireRun(input.runId);
5989
+ const message = this.requireMessage(run.messageId);
5990
+ return { run, message, lease: clone3(lease) };
5991
+ }
5992
+ async renewLease(input) {
5993
+ const lease = this.database.transaction(() => {
5994
+ const run = this.requireRun(input.runId);
5995
+ requireTransition2("Agent Run", run.id, run.state, "renew Lease", ["running"]);
5996
+ const message = this.requireMessage(run.messageId);
5997
+ requireTransition2("Runtime Message", message.id, message.state, "renew Lease", ["leased"]);
5998
+ if (run.leaseId !== input.leaseId || message.lease?.id !== input.leaseId) {
5999
+ throw new Error(`Lease ${input.leaseId} does not own Agent Run ${run.id}.`);
6000
+ }
6001
+ if (!Number.isFinite(input.leaseDurationMs) || input.leaseDurationMs <= 0) {
6002
+ throw new Error("Lease duration must be a positive finite number.");
6003
+ }
6004
+ const { date, timestamp } = timestampFor(input.now);
6005
+ const expiresAt = createTimestamp(new Date(date.getTime() + input.leaseDurationMs));
6006
+ this.database.run("UPDATE runtime_leases SET expires_at = ? WHERE id = ?", [expiresAt, input.leaseId]);
6007
+ this.database.run("UPDATE runtime_messages SET updated_at = ? WHERE id = ?", [timestamp, message.id]);
6008
+ return { ...message.lease, expiresAt };
6009
+ })();
6010
+ return clone3(lease);
6011
+ }
6012
+ async suspendRun(input) {
6013
+ const run = this.database.transaction(() => {
6014
+ const current = this.requireRun(input.runId);
6015
+ requireTransition2("Agent Run", current.id, current.state, "suspend", ["running"]);
6016
+ const message = this.requireMessage(current.messageId);
6017
+ requireTransition2("Runtime Message", message.id, message.state, "acknowledge", ["leased"]);
6018
+ const { timestamp } = timestampFor();
6019
+ this.database.run(
6020
+ "UPDATE agent_runs SET state = ?, lease_id = NULL, suspension_reason = ?, input_request_json = ?, execution_state_json = ?, updated_at = ? WHERE id = ?",
6021
+ ["suspended", input.reason ?? null, input.inputRequest ? json(input.inputRequest) : null, input.executionState ? json(input.executionState) : null, timestamp, current.id]
6022
+ );
6023
+ this.database.run(
6024
+ "UPDATE runtime_messages SET state = ?, updated_at = ? WHERE id = ?",
6025
+ ["acknowledged", timestamp, message.id]
6026
+ );
6027
+ this.database.run("DELETE FROM runtime_leases WHERE run_id = ?", [current.id]);
6028
+ this.recordEvent("run_suspended", { agentId: current.agentId, messageId: message.id, runId: current.id }, runEventPayload(current.metadata, {
6029
+ reason: input.reason
6030
+ }));
6031
+ return this.requireRun(current.id);
6032
+ })();
6033
+ return clone3(run);
6034
+ }
6035
+ async completeRun(input) {
6036
+ const run = this.database.transaction(() => {
6037
+ const current = this.requireRun(input.runId);
6038
+ requireTransition2("Agent Run", current.id, current.state, "complete", ["running"]);
6039
+ const message = this.requireMessage(current.messageId);
6040
+ requireTransition2("Runtime Message", message.id, message.state, "acknowledge", ["leased"]);
6041
+ const { timestamp } = timestampFor();
6042
+ const state = input.state ?? "completed";
6043
+ this.database.run(
6044
+ "UPDATE agent_runs SET state = ?, lease_id = NULL, outcome_json = ?, input_request_json = NULL, execution_state_json = NULL, updated_at = ? WHERE id = ?",
6045
+ [state, json(input.outcome), timestamp, current.id]
6046
+ );
6047
+ this.database.run(
6048
+ "UPDATE runtime_messages SET state = 'acknowledged', updated_at = ? WHERE id = ?",
6049
+ [timestamp, message.id]
6050
+ );
6051
+ this.database.run("DELETE FROM runtime_leases WHERE run_id = ?", [current.id]);
6052
+ this.recordEvent("run_completed", { agentId: current.agentId, messageId: current.messageId, runId: current.id }, runEventPayload(current.metadata, {
6053
+ state,
6054
+ outcome: input.outcome
6055
+ }));
6056
+ this.recordEvent("message_acknowledged", { agentId: message.agentId, messageId: message.id });
6057
+ return this.requireRun(current.id);
6058
+ })();
6059
+ return clone3(run);
6060
+ }
6061
+ async retryRun(input) {
6062
+ const run = this.database.transaction(() => {
6063
+ const current = this.requireRun(input.runId);
6064
+ requireTransition2("Agent Run", current.id, current.state, "retry", ["running"]);
6065
+ const message = this.requireMessage(current.messageId);
6066
+ requireTransition2("Runtime Message", message.id, message.state, "retry", ["leased"]);
6067
+ const { timestamp } = timestampFor();
6068
+ this.database.run(
6069
+ "UPDATE agent_runs SET state = 'queued', lease_id = NULL, updated_at = ? WHERE id = ?",
6070
+ [timestamp, current.id]
6071
+ );
6072
+ this.database.run(
6073
+ "UPDATE runtime_messages SET state = 'queued', updated_at = ? WHERE id = ?",
6074
+ [timestamp, message.id]
6075
+ );
6076
+ this.database.run("DELETE FROM runtime_leases WHERE run_id = ?", [current.id]);
6077
+ this.recordEvent("run_retry_scheduled", {
6078
+ agentId: current.agentId,
6079
+ messageId: message.id,
6080
+ runId: current.id
6081
+ }, { reason: input.reason, attempt: current.attempt });
6082
+ return this.requireRun(current.id);
6083
+ })();
6084
+ return clone3(run);
6085
+ }
6086
+ async exhaustRun(input) {
6087
+ const run = this.database.transaction(() => {
6088
+ const current = this.requireRun(input.runId);
6089
+ requireTransition2("Agent Run", current.id, current.state, "exhaust retries", ["running"]);
6090
+ const message = this.requireMessage(current.messageId);
6091
+ requireTransition2("Runtime Message", message.id, message.state, "dead-letter", ["leased"]);
6092
+ const { timestamp } = timestampFor();
6093
+ this.database.run(
6094
+ "UPDATE agent_runs SET state = 'failed', lease_id = NULL, outcome_json = ?, input_request_json = NULL, execution_state_json = NULL, updated_at = ? WHERE id = ?",
6095
+ [json(input.outcome), timestamp, current.id]
6096
+ );
6097
+ this.database.run(
6098
+ "UPDATE runtime_messages SET state = 'dead_lettered', dead_letter_reason = ?, updated_at = ? WHERE id = ?",
6099
+ [input.reason, timestamp, message.id]
6100
+ );
6101
+ this.database.run("DELETE FROM runtime_leases WHERE run_id = ?", [current.id]);
6102
+ this.recordEvent("run_completed", { agentId: current.agentId, messageId: message.id, runId: current.id }, runEventPayload(current.metadata, {
6103
+ state: "failed",
6104
+ outcome: input.outcome
6105
+ }));
6106
+ this.recordEvent("message_dead_lettered", { agentId: current.agentId, messageId: message.id }, {
6107
+ reason: input.reason
6108
+ });
6109
+ return this.requireRun(current.id);
6110
+ })();
6111
+ return clone3(run);
6112
+ }
6113
+ async abortRun(input) {
6114
+ const run = this.database.transaction(() => {
6115
+ const current = this.requireRun(input.runId);
6116
+ requireTransition2("Agent Run", current.id, current.state, "abort", ["queued", "running", "suspended"]);
6117
+ const message = this.requireMessage(current.messageId);
6118
+ const { timestamp } = timestampFor();
6119
+ this.database.run(
6120
+ "UPDATE agent_runs SET state = 'cancelled', lease_id = NULL, outcome_json = ?, input_request_json = NULL, execution_state_json = NULL, updated_at = ? WHERE id = ?",
6121
+ [json(input.outcome), timestamp, current.id]
6122
+ );
6123
+ if (message.state === "queued" || message.state === "leased") {
6124
+ this.database.run("UPDATE runtime_messages SET state = 'acknowledged', updated_at = ? WHERE id = ?", [
6125
+ timestamp,
6126
+ message.id
6127
+ ]);
6128
+ }
6129
+ this.database.run("DELETE FROM runtime_leases WHERE run_id = ?", [current.id]);
6130
+ this.recordEvent("run_aborted", { agentId: current.agentId, messageId: message.id, runId: current.id }, runEventPayload(current.metadata, {
6131
+ state: "cancelled",
6132
+ outcome: input.outcome
6133
+ }));
6134
+ return this.requireRun(current.id);
6135
+ })();
6136
+ return clone3(run);
6137
+ }
6138
+ async recoverExpiredLeases(input) {
6139
+ const { timestamp } = timestampFor(input);
6140
+ const rows = this.database.query(`
6141
+ SELECT r.id
6142
+ FROM agent_runs r
6143
+ JOIN runtime_leases l ON l.id = r.lease_id
6144
+ WHERE r.state = 'running' AND l.expires_at <= ?
6145
+ ORDER BY l.expires_at, r.created_at
6146
+ `).all(timestamp);
6147
+ if (rows.length === 0) return [];
6148
+ const recover = this.database.transaction(() => {
6149
+ const recovered = [];
6150
+ for (const row of rows) {
6151
+ const run = this.requireRun(row.id);
6152
+ if (run.state !== "running") continue;
6153
+ const message = this.requireMessage(run.messageId);
6154
+ this.database.run("UPDATE agent_runs SET state = 'queued', lease_id = NULL, updated_at = ? WHERE id = ?", [
6155
+ timestamp,
6156
+ run.id
6157
+ ]);
6158
+ this.database.run("UPDATE runtime_messages SET state = 'queued', updated_at = ? WHERE id = ?", [
6159
+ timestamp,
6160
+ message.id
6161
+ ]);
6162
+ this.database.run("DELETE FROM runtime_leases WHERE run_id = ?", [run.id]);
6163
+ this.recordEvent("lease_expired", { agentId: run.agentId, messageId: message.id, runId: run.id });
6164
+ recovered.push(this.requireRun(run.id));
6165
+ }
6166
+ return recovered;
6167
+ })();
6168
+ return clone3(recover);
6169
+ }
6170
+ async recoverLeases() {
6171
+ const { timestamp } = timestampFor();
6172
+ const rows = this.database.query(`
6173
+ SELECT r.id
6174
+ FROM agent_runs r
6175
+ JOIN runtime_leases l ON l.id = r.lease_id
6176
+ WHERE r.state = 'running'
6177
+ ORDER BY l.leased_at, r.created_at
6178
+ `).all();
6179
+ if (rows.length === 0) return [];
6180
+ const recovered = this.database.transaction(() => {
6181
+ const runs = [];
6182
+ for (const row of rows) {
6183
+ const run = this.requireRun(row.id);
6184
+ if (run.state !== "running") continue;
6185
+ const message = this.requireMessage(run.messageId);
6186
+ this.database.run("UPDATE agent_runs SET state = 'queued', lease_id = NULL, updated_at = ? WHERE id = ?", [
6187
+ timestamp,
6188
+ run.id
6189
+ ]);
6190
+ this.database.run("UPDATE runtime_messages SET state = 'queued', updated_at = ? WHERE id = ?", [
6191
+ timestamp,
6192
+ message.id
6193
+ ]);
6194
+ this.database.run("DELETE FROM runtime_leases WHERE run_id = ?", [run.id]);
6195
+ this.recordEvent("lease_recovered", { agentId: run.agentId, messageId: message.id, runId: run.id });
6196
+ runs.push(this.requireRun(run.id));
6197
+ }
6198
+ return runs;
6199
+ })();
6200
+ return clone3(recovered);
6201
+ }
6202
+ async createToolCall(input) {
6203
+ this.requireAgent(input.agentId);
6204
+ this.requireRun(input.runId);
6205
+ const { timestamp } = timestampFor();
6206
+ const toolCall = {
6207
+ id: createId("call"),
6208
+ agentId: input.agentId,
6209
+ runId: input.runId,
6210
+ name: input.name,
6211
+ args: clone3(input.args),
6212
+ state: "queued",
6213
+ createdAt: timestamp,
6214
+ updatedAt: timestamp
6215
+ };
6216
+ const create = this.database.transaction(() => {
6217
+ this.database.run(
6218
+ "INSERT INTO runtime_tool_calls (id, agent_id, run_id, name, args_json, state, result_json, indeterminate_reason, created_at, updated_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",
6219
+ [toolCall.id, toolCall.agentId, toolCall.runId, toolCall.name, json(toolCall.args), toolCall.state, null, null, toolCall.createdAt, toolCall.updatedAt]
6220
+ );
6221
+ this.recordEvent("tool_call_created", { agentId: toolCall.agentId, runId: toolCall.runId, toolCallId: toolCall.id });
6222
+ });
6223
+ create();
6224
+ return clone3(toolCall);
6225
+ }
6226
+ async startToolCall(toolCallId) {
6227
+ const toolCall = this.database.transaction(() => {
6228
+ const current = this.requireToolCall(toolCallId);
6229
+ requireTransition2("Tool Call", current.id, current.state, "start", ["queued"]);
6230
+ const { timestamp } = timestampFor();
6231
+ this.database.run("UPDATE runtime_tool_calls SET state = ?, updated_at = ? WHERE id = ?", [
6232
+ "running",
6233
+ timestamp,
6234
+ current.id
6235
+ ]);
6236
+ this.recordEvent("tool_call_started", { agentId: current.agentId, runId: current.runId, toolCallId: current.id });
6237
+ return this.requireToolCall(current.id);
6238
+ })();
6239
+ return clone3(toolCall);
6240
+ }
6241
+ async completeToolCall(input) {
6242
+ const toolCall = this.database.transaction(() => {
6243
+ const current = this.requireToolCall(input.toolCallId);
6244
+ const state = input.state ?? (input.result.ok ? "completed" : "failed");
6245
+ requireTransition2(
6246
+ "Tool Call",
6247
+ current.id,
6248
+ current.state,
6249
+ "complete",
6250
+ state === "failed" ? ["queued", "running"] : ["running"]
6251
+ );
6252
+ const { timestamp } = timestampFor();
6253
+ this.database.run(
6254
+ "UPDATE runtime_tool_calls SET state = ?, result_json = ?, updated_at = ? WHERE id = ?",
6255
+ [state, json(input.result), timestamp, current.id]
6256
+ );
6257
+ this.recordEvent(state === "failed" ? "tool_call_failed" : "tool_call_completed", { agentId: current.agentId, runId: current.runId, toolCallId: current.id }, {
6258
+ state
6259
+ });
6260
+ return this.requireToolCall(current.id);
6261
+ })();
6262
+ return clone3(toolCall);
6263
+ }
6264
+ async markToolCallIndeterminate(input) {
6265
+ const toolCall = this.database.transaction(() => {
6266
+ const current = this.requireToolCall(input.toolCallId);
6267
+ requireTransition2("Tool Call", current.id, current.state, "mark indeterminate", ["running"]);
6268
+ const { timestamp } = timestampFor();
6269
+ this.database.run(
6270
+ "UPDATE runtime_tool_calls SET state = ?, indeterminate_reason = ?, updated_at = ? WHERE id = ?",
6271
+ ["indeterminate", input.reason, timestamp, current.id]
6272
+ );
6273
+ this.recordEvent("tool_call_indeterminate", { agentId: current.agentId, runId: current.runId, toolCallId: current.id }, {
6274
+ reason: input.reason
6275
+ });
6276
+ return this.requireToolCall(current.id);
6277
+ })();
6278
+ return clone3(toolCall);
6279
+ }
6280
+ async getToolCall(toolCallId) {
6281
+ const row = this.database.query("SELECT * FROM runtime_tool_calls WHERE id = ?").get(toolCallId);
6282
+ return row ? this.toolCallFromRow(row) : void 0;
6283
+ }
6284
+ async listEvents(cursor = {}) {
6285
+ const after = cursor.after ? this.database.query("SELECT sequence FROM runtime_events WHERE id = ?").get(cursor.after) : null;
6286
+ const limit = cursor.limit === void 0 ? "" : ` LIMIT ${Math.max(0, Math.floor(cursor.limit))}`;
6287
+ const rows = this.database.query(`SELECT * FROM runtime_events ${after ? "WHERE sequence > ?" : ""} ORDER BY sequence${limit}`).all(...after ? [after.sequence] : []);
6288
+ return rows.map((row) => this.eventFromRow(row));
6289
+ }
6290
+ async recordEvent(input, related = {}, payload) {
6291
+ if (typeof input === "string") {
6292
+ this.recordEventInternal(input, related, payload);
6293
+ const row = this.database.query("SELECT * FROM runtime_events ORDER BY sequence DESC LIMIT 1").get();
6294
+ return clone3(this.eventFromRow(row));
6295
+ }
6296
+ const event = this.database.transaction(() => {
6297
+ this.recordEventInternal(input.kind, input.related ?? {}, input.payload);
6298
+ const row = this.database.query("SELECT * FROM runtime_events ORDER BY sequence DESC LIMIT 1").get();
6299
+ return this.eventFromRow(row);
6300
+ })();
6301
+ return clone3(event);
6302
+ }
6303
+ async getEventCheckpoint(consumerId) {
6304
+ return clone3(this.eventCheckpoint(consumerId));
6305
+ }
6306
+ async acknowledgeEvent(consumerId, eventId) {
6307
+ const acknowledge = this.database.transaction(() => this.acknowledgeEventState(consumerId, eventId));
6308
+ const checkpoint = acknowledge();
6309
+ return clone3(checkpoint);
6310
+ }
6311
+ async acknowledgeEventAndEnqueueAgentInput(input) {
6312
+ const deliver = this.database.transaction(() => {
6313
+ const event = this.requireEvent(input.eventId);
6314
+ const current = this.eventCheckpoint(input.consumerId);
6315
+ if (event.sequence <= current.sequence) return { checkpoint: current };
6316
+ this.requireAgent(input.agentId);
6317
+ const enqueued = this.enqueueAgentInputState(input);
6318
+ const checkpoint = this.acknowledgeEventState(input.consumerId, input.eventId);
6319
+ return { checkpoint, enqueued };
6320
+ });
6321
+ return clone3(deliver());
6322
+ }
6323
+ acknowledgeEventState(consumerId, eventId) {
6324
+ const event = this.requireEvent(eventId);
6325
+ const current = this.eventCheckpoint(consumerId);
6326
+ if (event.sequence <= current.sequence) return current;
6327
+ if (event.sequence !== current.sequence + 1) {
6328
+ throw new Error(`Runtime Event Consumer Checkpoint cannot advance from sequence ${current.sequence} to ${event.sequence}.`);
6329
+ }
6330
+ const updatedAt = createTimestamp();
6331
+ this.database.run(
6332
+ `INSERT INTO runtime_event_consumers (consumer_id, sequence, event_id, updated_at)
6333
+ VALUES (?, ?, ?, ?)
6334
+ ON CONFLICT(consumer_id) DO UPDATE SET
6335
+ sequence = excluded.sequence,
6336
+ event_id = excluded.event_id,
6337
+ updated_at = excluded.updated_at`,
6338
+ [consumerId, event.sequence, event.id, updatedAt]
6339
+ );
6340
+ return this.eventCheckpoint(consumerId);
6341
+ }
6342
+ insertMessage(message) {
6343
+ this.database.run(
6344
+ "INSERT INTO runtime_messages (id, agent_id, kind, payload_json, state, attempts, run_id, dead_letter_reason, created_at, updated_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",
6345
+ [message.id, message.agentId, message.kind, json(message.input), message.state, message.attempts, message.runId ?? null, null, message.createdAt, message.updatedAt]
6346
+ );
6347
+ }
6348
+ agentFromRow(row) {
6349
+ return {
6350
+ id: row.id,
6351
+ sessionId: row.session_id,
6352
+ state: row.state,
6353
+ createdAt: row.created_at,
6354
+ updatedAt: row.updated_at
6355
+ };
6356
+ }
6357
+ messageFromRow(row) {
6358
+ const message = {
6359
+ id: row.id,
6360
+ agentId: row.agent_id,
6361
+ kind: row.kind,
6362
+ input: parse(row.payload_json),
6363
+ state: row.state,
6364
+ attempts: row.attempts,
6365
+ ...row.run_id ? { runId: row.run_id } : {},
6366
+ ...row.dead_letter_reason ? { deadLetterReason: row.dead_letter_reason } : {},
6367
+ createdAt: row.created_at,
6368
+ updatedAt: row.updated_at
6369
+ };
6370
+ if (row.lease_id && row.lease_run_id && row.worker_id && row.leased_at && row.expires_at) {
6371
+ message.lease = {
6372
+ id: row.lease_id,
6373
+ runId: row.lease_run_id,
6374
+ workerId: row.worker_id,
6375
+ leasedAt: row.leased_at,
6376
+ expiresAt: row.expires_at
6377
+ };
6378
+ }
6379
+ return message;
6380
+ }
6381
+ runFromRow(row) {
6382
+ return {
6383
+ id: row.id,
6384
+ agentId: row.agent_id,
6385
+ messageId: row.message_id,
6386
+ state: row.state,
6387
+ attempt: row.attempt,
6388
+ ...row.lease_id ? { leaseId: row.lease_id } : {},
6389
+ ...row.outcome_json ? { outcome: parse(row.outcome_json) } : {},
6390
+ ...row.suspension_reason ? { suspensionReason: row.suspension_reason } : {},
6391
+ ...row.input_request_json ? { inputRequest: parse(row.input_request_json) } : {},
6392
+ ...row.execution_state_json ? { executionState: parse(row.execution_state_json) } : {},
6393
+ ...row.metadata_json ? { metadata: parse(row.metadata_json) } : {},
6394
+ createdAt: row.created_at,
6395
+ updatedAt: row.updated_at
6396
+ };
6397
+ }
6398
+ eventFromRow(row) {
6399
+ return {
6400
+ id: row.id,
6401
+ sequence: row.sequence,
6402
+ kind: row.kind,
6403
+ ...row.agent_id ? { agentId: row.agent_id } : {},
6404
+ ...row.message_id ? { messageId: row.message_id } : {},
6405
+ ...row.run_id ? { runId: row.run_id } : {},
6406
+ ...row.tool_call_id ? { toolCallId: row.tool_call_id } : {},
6407
+ ...row.payload_json ? { payload: optionalJson(row.payload_json) } : {},
6408
+ createdAt: row.created_at
6409
+ };
6410
+ }
6411
+ toolCallFromRow(row) {
6412
+ return {
6413
+ id: row.id,
6414
+ agentId: row.agent_id,
6415
+ runId: row.run_id,
6416
+ name: row.name,
6417
+ args: parse(row.args_json),
6418
+ state: row.state,
6419
+ ...row.result_json ? { result: parse(row.result_json) } : {},
6420
+ ...row.indeterminate_reason ? { indeterminateReason: row.indeterminate_reason } : {},
6421
+ createdAt: row.created_at,
6422
+ updatedAt: row.updated_at
6423
+ };
6424
+ }
6425
+ messageRow(messageId) {
6426
+ return this.database.query(`
6427
+ SELECT
6428
+ m.*,
6429
+ l.id AS lease_id,
6430
+ l.run_id AS lease_run_id,
6431
+ l.worker_id,
6432
+ l.leased_at,
6433
+ l.expires_at
6434
+ FROM runtime_messages m
6435
+ LEFT JOIN agent_runs r ON r.id = m.run_id
6436
+ LEFT JOIN runtime_leases l ON l.id = r.lease_id
6437
+ WHERE m.id = ?
6438
+ `).get(messageId);
6439
+ }
6440
+ runRow(runId) {
6441
+ return this.database.query("SELECT * FROM agent_runs WHERE id = ?").get(runId);
6442
+ }
6443
+ requireAgent(agentId) {
6444
+ const row = this.database.query("SELECT * FROM agents WHERE id = ?").get(agentId);
6445
+ if (!row) {
6446
+ throw new Error(`Agent not found: ${agentId}.`);
6447
+ }
6448
+ return this.agentFromRow(row);
6449
+ }
6450
+ requireMessage(messageId) {
6451
+ const row = this.messageRow(messageId);
6452
+ if (!row) {
6453
+ throw new Error(`Runtime Message not found: ${messageId}.`);
6454
+ }
6455
+ return this.messageFromRow(row);
6456
+ }
6457
+ requireRun(runId) {
6458
+ const row = this.runRow(runId);
6459
+ if (!row) {
6460
+ throw new Error(`Agent Run not found: ${runId}.`);
6461
+ }
6462
+ return this.runFromRow(row);
6463
+ }
6464
+ requireToolCall(toolCallId) {
6465
+ const row = this.database.query("SELECT * FROM runtime_tool_calls WHERE id = ?").get(toolCallId);
6466
+ if (!row) {
6467
+ throw new Error(`Tool Call not found: ${toolCallId}.`);
6468
+ }
6469
+ return this.toolCallFromRow(row);
6470
+ }
6471
+ requireEvent(eventId) {
6472
+ const row = this.database.query("SELECT * FROM runtime_events WHERE id = ?").get(eventId);
6473
+ if (!row) {
6474
+ throw new Error(`Runtime Event not found: ${eventId}.`);
6475
+ }
6476
+ return this.eventFromRow(row);
6477
+ }
6478
+ eventCheckpoint(consumerId) {
6479
+ const row = this.database.query(
6480
+ "SELECT consumer_id, sequence, event_id, updated_at FROM runtime_event_consumers WHERE consumer_id = ?"
6481
+ ).get(consumerId);
6482
+ if (!row) {
6483
+ return {
6484
+ consumerId,
6485
+ sequence: 0,
6486
+ updatedAt: createTimestamp(/* @__PURE__ */ new Date(0))
6487
+ };
6488
+ }
6489
+ return {
6490
+ consumerId: row.consumer_id,
6491
+ sequence: row.sequence,
6492
+ ...row.event_id ? { eventId: row.event_id } : {},
6493
+ updatedAt: row.updated_at
6494
+ };
6495
+ }
6496
+ recordEventInternal(kind, related, payload) {
6497
+ this.database.run(
6498
+ "INSERT INTO runtime_events (id, sequence, kind, agent_id, message_id, run_id, tool_call_id, payload_json, created_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)",
6499
+ [
6500
+ createId("evt"),
6501
+ (this.database.query("SELECT COALESCE(MAX(sequence), 0) AS sequence FROM runtime_events").get().sequence ?? 0) + 1,
6502
+ kind,
6503
+ related.agentId ?? null,
6504
+ related.messageId ?? null,
6505
+ related.runId ?? null,
6506
+ related.toolCallId ?? null,
6507
+ payload === void 0 ? null : json(payload),
6508
+ createTimestamp()
6509
+ ]
6510
+ );
6511
+ }
6512
+ };
4489
6513
  export {
4490
6514
  Agent,
4491
- AgentEventStream,
4492
- EmptyResponseError,
4493
- EventStream,
4494
- ExtensionRunner,
4495
- HooksManager,
4496
- LocalJsonlSessionManager,
4497
- appendUserTurn,
4498
- buildModelRequest,
4499
- buildSystemPrompt,
4500
- conversationMessages,
6515
+ AgentRun,
6516
+ AgentRuntime,
6517
+ InMemoryRuntimeStateStore,
6518
+ InMemorySessionStore,
6519
+ JsonlSessionStore,
6520
+ SqliteRuntimeStateStore,
4501
6521
  createCoreTools,
4502
- createDispatchStream,
4503
- createEventBus,
4504
- createExtension,
4505
- createExtensionAPI,
4506
- createExtensionRunner,
4507
- createExtensionRuntime,
4508
- createId,
4509
- createMessage,
4510
- createSession,
4511
- createSourceInfo,
4512
- createTimestamp,
4513
- getGlobalHooks,
4514
- interpolateEnvVars,
4515
- latestUserInput,
4516
- loadConfigFile,
4517
- loadPhase,
4518
- loadPhases,
4519
- messageContentText,
4520
- parseModelRef,
4521
- registerBuiltInApiProviders,
4522
- registerConfigModels,
4523
- reloadPhases,
4524
- resetGlobalHooks,
4525
- resolveDefaultModel,
4526
- resolveInWorkspace,
4527
- resolveWorkspacePaths,
4528
- serializeSkills
6522
+ createMessage
4529
6523
  };