@earendil-works/pi-agent-core 0.83.0 → 0.84.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (140) hide show
  1. package/README.md +9 -3
  2. package/dist/agent.d.ts +3 -1
  3. package/dist/agent.d.ts.map +1 -1
  4. package/dist/agent.js +6 -0
  5. package/dist/agent.js.map +1 -1
  6. package/dist/harness/agent-harness.d.ts +452 -85
  7. package/dist/harness/agent-harness.d.ts.map +1 -1
  8. package/dist/harness/agent-harness.js +204 -917
  9. package/dist/harness/agent-harness.js.map +1 -1
  10. package/dist/harness/compaction/branch-summarization.d.ts +13 -6
  11. package/dist/harness/compaction/branch-summarization.d.ts.map +1 -1
  12. package/dist/harness/compaction/branch-summarization.js +11 -15
  13. package/dist/harness/compaction/branch-summarization.js.map +1 -1
  14. package/dist/harness/compaction/compaction.d.ts +14 -17
  15. package/dist/harness/compaction/compaction.d.ts.map +1 -1
  16. package/dist/harness/compaction/compaction.js +23 -36
  17. package/dist/harness/compaction/compaction.js.map +1 -1
  18. package/dist/harness/env/nodejs.d.ts +1 -0
  19. package/dist/harness/env/nodejs.d.ts.map +1 -1
  20. package/dist/harness/env/nodejs.js +23 -7
  21. package/dist/harness/env/nodejs.js.map +1 -1
  22. package/dist/harness/messages.d.ts +3 -3
  23. package/dist/harness/messages.d.ts.map +1 -1
  24. package/dist/harness/messages.js +3 -3
  25. package/dist/harness/messages.js.map +1 -1
  26. package/dist/harness/prompt-templates.d.ts.map +1 -1
  27. package/dist/harness/prompt-templates.js +4 -9
  28. package/dist/harness/prompt-templates.js.map +1 -1
  29. package/dist/harness/reducer.d.ts +100 -0
  30. package/dist/harness/reducer.d.ts.map +1 -0
  31. package/dist/harness/reducer.js +415 -0
  32. package/dist/harness/reducer.js.map +1 -0
  33. package/dist/harness/result.d.ts +40 -0
  34. package/dist/harness/result.d.ts.map +1 -0
  35. package/dist/harness/result.js +41 -0
  36. package/dist/harness/result.js.map +1 -0
  37. package/dist/harness/session/context.d.ts +22 -0
  38. package/dist/harness/session/context.d.ts.map +1 -0
  39. package/dist/harness/session/context.js +67 -0
  40. package/dist/harness/session/context.js.map +1 -0
  41. package/dist/harness/session/index.d.ts +7 -0
  42. package/dist/harness/session/index.d.ts.map +1 -0
  43. package/dist/harness/session/index.js +6 -0
  44. package/dist/harness/session/index.js.map +1 -0
  45. package/dist/harness/session/jsonl/codec.d.ts +8 -0
  46. package/dist/harness/session/jsonl/codec.d.ts.map +1 -0
  47. package/dist/harness/session/jsonl/codec.js +190 -0
  48. package/dist/harness/session/jsonl/codec.js.map +1 -0
  49. package/dist/harness/session/jsonl/errors.d.ts +5 -0
  50. package/dist/harness/session/jsonl/errors.d.ts.map +1 -0
  51. package/dist/harness/session/jsonl/errors.js +11 -0
  52. package/dist/harness/session/jsonl/errors.js.map +1 -0
  53. package/dist/harness/session/jsonl/repo.d.ts +23 -0
  54. package/dist/harness/session/jsonl/repo.d.ts.map +1 -0
  55. package/dist/harness/session/jsonl/repo.js +132 -0
  56. package/dist/harness/session/jsonl/repo.js.map +1 -0
  57. package/dist/harness/session/jsonl/storage.d.ts +43 -0
  58. package/dist/harness/session/jsonl/storage.d.ts.map +1 -0
  59. package/dist/harness/session/jsonl/storage.js +208 -0
  60. package/dist/harness/session/jsonl/storage.js.map +1 -0
  61. package/dist/harness/session/jsonl/types.d.ts +38 -0
  62. package/dist/harness/session/jsonl/types.d.ts.map +1 -0
  63. package/dist/harness/session/jsonl/types.js +2 -0
  64. package/dist/harness/session/jsonl/types.js.map +1 -0
  65. package/dist/harness/session/jsonl.d.ts +3 -0
  66. package/dist/harness/session/jsonl.d.ts.map +1 -0
  67. package/dist/harness/session/jsonl.js +2 -0
  68. package/dist/harness/session/jsonl.js.map +1 -0
  69. package/dist/harness/session/memory.d.ts +44 -0
  70. package/dist/harness/session/memory.d.ts.map +1 -0
  71. package/dist/harness/session/memory.js +141 -0
  72. package/dist/harness/session/memory.js.map +1 -0
  73. package/dist/harness/session/search.d.ts +19 -0
  74. package/dist/harness/session/search.d.ts.map +1 -0
  75. package/dist/harness/session/search.js +42 -0
  76. package/dist/harness/session/search.js.map +1 -0
  77. package/dist/harness/session/session.d.ts +43 -42
  78. package/dist/harness/session/session.d.ts.map +1 -1
  79. package/dist/harness/session/session.js +187 -228
  80. package/dist/harness/session/session.js.map +1 -1
  81. package/dist/harness/session/state.d.ts +65 -0
  82. package/dist/harness/session/state.d.ts.map +1 -0
  83. package/dist/harness/session/state.js +319 -0
  84. package/dist/harness/session/state.js.map +1 -0
  85. package/dist/harness/session/testing/conformance.d.ts +4 -0
  86. package/dist/harness/session/testing/conformance.d.ts.map +1 -0
  87. package/dist/harness/session/testing/conformance.js +743 -0
  88. package/dist/harness/session/testing/conformance.js.map +1 -0
  89. package/dist/harness/session/testing/index.d.ts +3 -0
  90. package/dist/harness/session/testing/index.d.ts.map +1 -0
  91. package/dist/harness/session/testing/index.js +2 -0
  92. package/dist/harness/session/testing/index.js.map +1 -0
  93. package/dist/harness/session/testing/types.d.ts +14 -0
  94. package/dist/harness/session/testing/types.d.ts.map +1 -0
  95. package/dist/harness/session/testing/types.js +2 -0
  96. package/dist/harness/session/testing/types.js.map +1 -0
  97. package/dist/harness/session/types.d.ts +333 -0
  98. package/dist/harness/session/types.d.ts.map +1 -0
  99. package/dist/harness/session/types.js +10 -0
  100. package/dist/harness/session/types.js.map +1 -0
  101. package/dist/harness/skills.d.ts.map +1 -1
  102. package/dist/harness/skills.js +21 -19
  103. package/dist/harness/skills.js.map +1 -1
  104. package/dist/harness/telemetry.d.ts +1221 -0
  105. package/dist/harness/telemetry.d.ts.map +1 -0
  106. package/dist/harness/telemetry.js +508 -0
  107. package/dist/harness/telemetry.js.map +1 -0
  108. package/dist/harness/types.d.ts +6 -459
  109. package/dist/harness/types.d.ts.map +1 -1
  110. package/dist/harness/types.js +0 -19
  111. package/dist/harness/types.js.map +1 -1
  112. package/dist/index.d.ts +10 -9
  113. package/dist/index.d.ts.map +1 -1
  114. package/dist/index.js +7 -8
  115. package/dist/index.js.map +1 -1
  116. package/dist/proxy.d.ts +1 -1
  117. package/dist/proxy.d.ts.map +1 -1
  118. package/dist/proxy.js +1 -0
  119. package/dist/proxy.js.map +1 -1
  120. package/package.json +9 -2
  121. package/dist/harness/session/jsonl-repo.d.ts +0 -26
  122. package/dist/harness/session/jsonl-repo.d.ts.map +0 -1
  123. package/dist/harness/session/jsonl-repo.js +0 -103
  124. package/dist/harness/session/jsonl-repo.js.map +0 -1
  125. package/dist/harness/session/jsonl-storage.d.ts +0 -42
  126. package/dist/harness/session/jsonl-storage.d.ts.map +0 -1
  127. package/dist/harness/session/jsonl-storage.js +0 -293
  128. package/dist/harness/session/jsonl-storage.js.map +0 -1
  129. package/dist/harness/session/memory-repo.d.ts +0 -18
  130. package/dist/harness/session/memory-repo.d.ts.map +0 -1
  131. package/dist/harness/session/memory-repo.js +0 -42
  132. package/dist/harness/session/memory-repo.js.map +0 -1
  133. package/dist/harness/session/memory-storage.d.ts +0 -33
  134. package/dist/harness/session/memory-storage.d.ts.map +0 -1
  135. package/dist/harness/session/memory-storage.js +0 -168
  136. package/dist/harness/session/memory-storage.js.map +0 -1
  137. package/dist/harness/session/repo-utils.d.ts +0 -11
  138. package/dist/harness/session/repo-utils.d.ts.map +0 -1
  139. package/dist/harness/session/repo-utils.js +0 -39
  140. package/dist/harness/session/repo-utils.js.map +0 -1
@@ -30,7 +30,7 @@ export async function loadPromptTemplates(env, paths) {
30
30
  diagnostics.push(...result.diagnostics);
31
31
  }
32
32
  else if (kind === "file" && info.name.endsWith(".md")) {
33
- const result = await loadTemplateFromFile(env, info.path);
33
+ const result = await loadTemplateFromFile(env, info.path, info.name);
34
34
  if (result.promptTemplate)
35
35
  promptTemplates.push(result.promptTemplate);
36
36
  diagnostics.push(...result.diagnostics);
@@ -80,14 +80,14 @@ async function loadTemplatesFromDir(env, dir) {
80
80
  const kind = await resolveKind(env, entry, diagnostics);
81
81
  if (kind !== "file" || !entry.name.endsWith(".md"))
82
82
  continue;
83
- const result = await loadTemplateFromFile(env, entry.path);
83
+ const result = await loadTemplateFromFile(env, entry.path, entry.name);
84
84
  if (result.promptTemplate)
85
85
  promptTemplates.push(result.promptTemplate);
86
86
  diagnostics.push(...result.diagnostics);
87
87
  }
88
88
  return { promptTemplates, diagnostics };
89
89
  }
90
- async function loadTemplateFromFile(env, filePath) {
90
+ async function loadTemplateFromFile(env, filePath, fileName) {
91
91
  const diagnostics = [];
92
92
  const rawContent = await env.readTextFile(filePath);
93
93
  if (!rawContent.ok) {
@@ -119,7 +119,7 @@ async function loadTemplateFromFile(env, filePath) {
119
119
  }
120
120
  return {
121
121
  promptTemplate: {
122
- name: basenameEnvPath(filePath).replace(/\.md$/i, ""),
122
+ name: fileName.replace(/\.md$/i, ""),
123
123
  description,
124
124
  content: body,
125
125
  },
@@ -171,11 +171,6 @@ function parseFrontmatter(content) {
171
171
  return { ok: false, error: toError(error) };
172
172
  }
173
173
  }
174
- function basenameEnvPath(path) {
175
- const normalized = path.replace(/\/+$/, "");
176
- const slashIndex = normalized.lastIndexOf("/");
177
- return slashIndex === -1 ? normalized : normalized.slice(slashIndex + 1);
178
- }
179
174
  /** Parse an argument string using simple shell-style single and double quotes. */
180
175
  export function parseCommandArgs(argsString) {
181
176
  const args = [];
@@ -1 +1 @@
1
- {"version":3,"file":"prompt-templates.js","sourceRoot":"","sources":["../../src/harness/prompt-templates.ts"],"names":[],"mappings":"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","sourcesContent":["import { parse } from \"yaml\";\nimport { type ExecutionEnv, type FileInfo, type PromptTemplate, type Result, toError } from \"./types.ts\";\n\nexport type PromptTemplateDiagnosticCode = \"file_info_failed\" | \"list_failed\" | \"read_failed\" | \"parse_failed\";\n\n/** Warning produced while loading prompt templates. */\nexport interface PromptTemplateDiagnostic {\n\t/** Diagnostic severity. Currently only warnings are emitted. */\n\ttype: \"warning\";\n\t/** Stable diagnostic code. */\n\tcode: PromptTemplateDiagnosticCode;\n\t/** Human-readable diagnostic message. */\n\tmessage: string;\n\t/** Path associated with the diagnostic. */\n\tpath: string;\n}\n\ninterface PromptTemplateFrontmatter {\n\tdescription?: string;\n\t\"argument-hint\"?: string;\n\t[key: string]: unknown;\n}\n\n/**\n * Load prompt templates from one or more paths.\n *\n * Directory inputs load direct `.md` children non-recursively. File inputs load explicit `.md` files. Missing paths and\n * non-markdown files are skipped. Read and parse failures are returned as diagnostics.\n */\nexport async function loadPromptTemplates(\n\tenv: ExecutionEnv,\n\tpaths: string | string[],\n): Promise<{ promptTemplates: PromptTemplate[]; diagnostics: PromptTemplateDiagnostic[] }> {\n\tconst promptTemplates: PromptTemplate[] = [];\n\tconst diagnostics: PromptTemplateDiagnostic[] = [];\n\tfor (const path of Array.isArray(paths) ? paths : [paths]) {\n\t\tconst infoResult = await env.fileInfo(path);\n\t\tif (!infoResult.ok) {\n\t\t\tif (infoResult.error.code !== \"not_found\") {\n\t\t\t\tdiagnostics.push({\n\t\t\t\t\ttype: \"warning\",\n\t\t\t\t\tcode: \"file_info_failed\",\n\t\t\t\t\tmessage: infoResult.error.message,\n\t\t\t\t\tpath,\n\t\t\t\t});\n\t\t\t}\n\t\t\tcontinue;\n\t\t}\n\t\tconst info = infoResult.value;\n\t\tconst kind = await resolveKind(env, info, diagnostics);\n\t\tif (kind === \"directory\") {\n\t\t\tconst result = await loadTemplatesFromDir(env, info.path);\n\t\t\tpromptTemplates.push(...result.promptTemplates);\n\t\t\tdiagnostics.push(...result.diagnostics);\n\t\t} else if (kind === \"file\" && info.name.endsWith(\".md\")) {\n\t\t\tconst result = await loadTemplateFromFile(env, info.path);\n\t\t\tif (result.promptTemplate) promptTemplates.push(result.promptTemplate);\n\t\t\tdiagnostics.push(...result.diagnostics);\n\t\t}\n\t}\n\treturn { promptTemplates, diagnostics };\n}\n\n/**\n * Load prompt templates from source-tagged paths.\n *\n * Source values are preserved exactly and attached to every loaded prompt template and diagnostic. The agent package does\n * not interpret source values; applications define their own provenance shape.\n */\nexport async function loadSourcedPromptTemplates<TSource, TPromptTemplate extends PromptTemplate = PromptTemplate>(\n\tenv: ExecutionEnv,\n\tinputs: Array<{ path: string; source: TSource }>,\n\tmapPromptTemplate?: (promptTemplate: PromptTemplate, source: TSource) => TPromptTemplate,\n): Promise<{\n\tpromptTemplates: Array<{ promptTemplate: TPromptTemplate; source: TSource }>;\n\tdiagnostics: Array<PromptTemplateDiagnostic & { source: TSource }>;\n}> {\n\tconst promptTemplates: Array<{ promptTemplate: TPromptTemplate; source: TSource }> = [];\n\tconst diagnostics: Array<PromptTemplateDiagnostic & { source: TSource }> = [];\n\tfor (const input of inputs) {\n\t\tconst result = await loadPromptTemplates(env, input.path);\n\t\tfor (const promptTemplate of result.promptTemplates) {\n\t\t\tpromptTemplates.push({\n\t\t\t\tpromptTemplate: mapPromptTemplate\n\t\t\t\t\t? mapPromptTemplate(promptTemplate, input.source)\n\t\t\t\t\t: (promptTemplate as TPromptTemplate),\n\t\t\t\tsource: input.source,\n\t\t\t});\n\t\t}\n\t\tfor (const diagnostic of result.diagnostics) diagnostics.push({ ...diagnostic, source: input.source });\n\t}\n\treturn { promptTemplates, diagnostics };\n}\n\nasync function loadTemplatesFromDir(\n\tenv: ExecutionEnv,\n\tdir: string,\n): Promise<{ promptTemplates: PromptTemplate[]; diagnostics: PromptTemplateDiagnostic[] }> {\n\tconst promptTemplates: PromptTemplate[] = [];\n\tconst diagnostics: PromptTemplateDiagnostic[] = [];\n\tconst entriesResult = await env.listDir(dir);\n\tif (!entriesResult.ok) {\n\t\tdiagnostics.push({\n\t\t\ttype: \"warning\",\n\t\t\tcode: \"list_failed\",\n\t\t\tmessage: entriesResult.error.message,\n\t\t\tpath: dir,\n\t\t});\n\t\treturn { promptTemplates, diagnostics };\n\t}\n\tconst entries = entriesResult.value;\n\n\tfor (const entry of entries.sort((a, b) => a.name.localeCompare(b.name))) {\n\t\tconst kind = await resolveKind(env, entry, diagnostics);\n\t\tif (kind !== \"file\" || !entry.name.endsWith(\".md\")) continue;\n\t\tconst result = await loadTemplateFromFile(env, entry.path);\n\t\tif (result.promptTemplate) promptTemplates.push(result.promptTemplate);\n\t\tdiagnostics.push(...result.diagnostics);\n\t}\n\treturn { promptTemplates, diagnostics };\n}\n\nasync function loadTemplateFromFile(\n\tenv: ExecutionEnv,\n\tfilePath: string,\n): Promise<{ promptTemplate: PromptTemplate | null; diagnostics: PromptTemplateDiagnostic[] }> {\n\tconst diagnostics: PromptTemplateDiagnostic[] = [];\n\tconst rawContent = await env.readTextFile(filePath);\n\tif (!rawContent.ok) {\n\t\tdiagnostics.push({\n\t\t\ttype: \"warning\",\n\t\t\tcode: \"read_failed\",\n\t\t\tmessage: rawContent.error.message,\n\t\t\tpath: filePath,\n\t\t});\n\t\treturn { promptTemplate: null, diagnostics };\n\t}\n\n\tconst parsed = parseFrontmatter<PromptTemplateFrontmatter>(rawContent.value);\n\tif (!parsed.ok) {\n\t\tdiagnostics.push({\n\t\t\ttype: \"warning\",\n\t\t\tcode: \"parse_failed\",\n\t\t\tmessage: parsed.error.message,\n\t\t\tpath: filePath,\n\t\t});\n\t\treturn { promptTemplate: null, diagnostics };\n\t}\n\n\tconst { frontmatter, body } = parsed.value;\n\tconst firstLine = body.split(\"\\n\").find((line) => line.trim());\n\tlet description = typeof frontmatter.description === \"string\" ? frontmatter.description : \"\";\n\tif (!description && firstLine) {\n\t\tdescription = firstLine.slice(0, 60);\n\t\tif (firstLine.length > 60) description += \"...\";\n\t}\n\treturn {\n\t\tpromptTemplate: {\n\t\t\tname: basenameEnvPath(filePath).replace(/\\.md$/i, \"\"),\n\t\t\tdescription,\n\t\t\tcontent: body,\n\t\t},\n\t\tdiagnostics,\n\t};\n}\n\nasync function resolveKind(\n\tenv: ExecutionEnv,\n\tinfo: FileInfo,\n\tdiagnostics: PromptTemplateDiagnostic[],\n): Promise<\"file\" | \"directory\" | undefined> {\n\tif (info.kind === \"file\" || info.kind === \"directory\") return info.kind;\n\tconst canonicalPath = await env.canonicalPath(info.path);\n\tif (!canonicalPath.ok) {\n\t\tif (canonicalPath.error.code !== \"not_found\") {\n\t\t\tdiagnostics.push({\n\t\t\t\ttype: \"warning\",\n\t\t\t\tcode: \"file_info_failed\",\n\t\t\t\tmessage: canonicalPath.error.message,\n\t\t\t\tpath: info.path,\n\t\t\t});\n\t\t}\n\t\treturn undefined;\n\t}\n\tconst target = await env.fileInfo(canonicalPath.value);\n\tif (!target.ok) {\n\t\tif (target.error.code !== \"not_found\") {\n\t\t\tdiagnostics.push({\n\t\t\t\ttype: \"warning\",\n\t\t\t\tcode: \"file_info_failed\",\n\t\t\t\tmessage: target.error.message,\n\t\t\t\tpath: info.path,\n\t\t\t});\n\t\t}\n\t\treturn undefined;\n\t}\n\treturn target.value.kind === \"file\" || target.value.kind === \"directory\" ? target.value.kind : undefined;\n}\n\nfunction parseFrontmatter<T extends Record<string, unknown>>(\n\tcontent: string,\n): Result<{ frontmatter: T; body: string }, Error> {\n\ttry {\n\t\tconst normalized = content.replace(/\\r\\n/g, \"\\n\").replace(/\\r/g, \"\\n\");\n\t\tif (!normalized.startsWith(\"---\")) return { ok: true, value: { frontmatter: {} as T, body: normalized } };\n\t\tconst endIndex = normalized.indexOf(\"\\n---\", 3);\n\t\tif (endIndex === -1) return { ok: true, value: { frontmatter: {} as T, body: normalized } };\n\t\tconst yamlString = normalized.slice(4, endIndex);\n\t\tconst body = normalized.slice(endIndex + 4).trim();\n\t\treturn { ok: true, value: { frontmatter: (parse(yamlString) ?? {}) as T, body } };\n\t} catch (error) {\n\t\treturn { ok: false, error: toError(error) };\n\t}\n}\n\nfunction basenameEnvPath(path: string): string {\n\tconst normalized = path.replace(/\\/+$/, \"\");\n\tconst slashIndex = normalized.lastIndexOf(\"/\");\n\treturn slashIndex === -1 ? normalized : normalized.slice(slashIndex + 1);\n}\n\n/** Parse an argument string using simple shell-style single and double quotes. */\nexport function parseCommandArgs(argsString: string): string[] {\n\tconst args: string[] = [];\n\tlet current = \"\";\n\tlet inQuote: string | null = null;\n\n\tfor (let i = 0; i < argsString.length; i++) {\n\t\tconst char = argsString[i]!;\n\t\tif (inQuote) {\n\t\t\tif (char === inQuote) inQuote = null;\n\t\t\telse current += char;\n\t\t} else if (char === '\"' || char === \"'\") {\n\t\t\tinQuote = char;\n\t\t} else if (char === \" \" || char === \"\\t\") {\n\t\t\tif (current) {\n\t\t\t\targs.push(current);\n\t\t\t\tcurrent = \"\";\n\t\t\t}\n\t\t} else {\n\t\t\tcurrent += char;\n\t\t}\n\t}\n\tif (current) args.push(current);\n\treturn args;\n}\n\n/** Substitute prompt template placeholders (`$1`, `$@`, `$ARGUMENTS`, `${@:N}`, `${@:N:L}`) with command arguments. */\nexport function substituteArgs(content: string, args: string[]): string {\n\tlet result = content;\n\tresult = result.replace(/\\$(\\d+)/g, (_, num: string) => args[parseInt(num, 10) - 1] ?? \"\");\n\tresult = result.replace(/\\$\\{@:(\\d+)(?::(\\d+))?\\}/g, (_, startStr: string, lengthStr?: string) => {\n\t\tlet start = parseInt(startStr, 10) - 1;\n\t\tif (start < 0) start = 0;\n\t\tif (lengthStr) return args.slice(start, start + parseInt(lengthStr, 10)).join(\" \");\n\t\treturn args.slice(start).join(\" \");\n\t});\n\tconst allArgs = args.join(\" \");\n\tresult = result.replace(/\\$ARGUMENTS/g, allArgs);\n\tresult = result.replace(/\\$@/g, allArgs);\n\treturn result;\n}\n\n/** Format a prompt template invocation with positional arguments. */\nexport function formatPromptTemplateInvocation(template: PromptTemplate, args: string[] = []): string {\n\treturn substituteArgs(template.content, args);\n}\n"]}
1
+ {"version":3,"file":"prompt-templates.js","sourceRoot":"","sources":["../../src/harness/prompt-templates.ts"],"names":[],"mappings":"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","sourcesContent":["import { parse } from \"yaml\";\nimport { type ExecutionEnv, type FileInfo, type PromptTemplate, type Result, toError } from \"./types.ts\";\n\nexport type PromptTemplateDiagnosticCode = \"file_info_failed\" | \"list_failed\" | \"read_failed\" | \"parse_failed\";\n\n/** Warning produced while loading prompt templates. */\nexport interface PromptTemplateDiagnostic {\n\t/** Diagnostic severity. Currently only warnings are emitted. */\n\ttype: \"warning\";\n\t/** Stable diagnostic code. */\n\tcode: PromptTemplateDiagnosticCode;\n\t/** Human-readable diagnostic message. */\n\tmessage: string;\n\t/** Path associated with the diagnostic. */\n\tpath: string;\n}\n\ninterface PromptTemplateFrontmatter {\n\tdescription?: string;\n\t\"argument-hint\"?: string;\n\t[key: string]: unknown;\n}\n\n/**\n * Load prompt templates from one or more paths.\n *\n * Directory inputs load direct `.md` children non-recursively. File inputs load explicit `.md` files. Missing paths and\n * non-markdown files are skipped. Read and parse failures are returned as diagnostics.\n */\nexport async function loadPromptTemplates(\n\tenv: ExecutionEnv,\n\tpaths: string | string[],\n): Promise<{ promptTemplates: PromptTemplate[]; diagnostics: PromptTemplateDiagnostic[] }> {\n\tconst promptTemplates: PromptTemplate[] = [];\n\tconst diagnostics: PromptTemplateDiagnostic[] = [];\n\tfor (const path of Array.isArray(paths) ? paths : [paths]) {\n\t\tconst infoResult = await env.fileInfo(path);\n\t\tif (!infoResult.ok) {\n\t\t\tif (infoResult.error.code !== \"not_found\") {\n\t\t\t\tdiagnostics.push({\n\t\t\t\t\ttype: \"warning\",\n\t\t\t\t\tcode: \"file_info_failed\",\n\t\t\t\t\tmessage: infoResult.error.message,\n\t\t\t\t\tpath,\n\t\t\t\t});\n\t\t\t}\n\t\t\tcontinue;\n\t\t}\n\t\tconst info = infoResult.value;\n\t\tconst kind = await resolveKind(env, info, diagnostics);\n\t\tif (kind === \"directory\") {\n\t\t\tconst result = await loadTemplatesFromDir(env, info.path);\n\t\t\tpromptTemplates.push(...result.promptTemplates);\n\t\t\tdiagnostics.push(...result.diagnostics);\n\t\t} else if (kind === \"file\" && info.name.endsWith(\".md\")) {\n\t\t\tconst result = await loadTemplateFromFile(env, info.path, info.name);\n\t\t\tif (result.promptTemplate) promptTemplates.push(result.promptTemplate);\n\t\t\tdiagnostics.push(...result.diagnostics);\n\t\t}\n\t}\n\treturn { promptTemplates, diagnostics };\n}\n\n/**\n * Load prompt templates from source-tagged paths.\n *\n * Source values are preserved exactly and attached to every loaded prompt template and diagnostic. The agent package does\n * not interpret source values; applications define their own provenance shape.\n */\nexport async function loadSourcedPromptTemplates<TSource, TPromptTemplate extends PromptTemplate = PromptTemplate>(\n\tenv: ExecutionEnv,\n\tinputs: Array<{ path: string; source: TSource }>,\n\tmapPromptTemplate?: (promptTemplate: PromptTemplate, source: TSource) => TPromptTemplate,\n): Promise<{\n\tpromptTemplates: Array<{ promptTemplate: TPromptTemplate; source: TSource }>;\n\tdiagnostics: Array<PromptTemplateDiagnostic & { source: TSource }>;\n}> {\n\tconst promptTemplates: Array<{ promptTemplate: TPromptTemplate; source: TSource }> = [];\n\tconst diagnostics: Array<PromptTemplateDiagnostic & { source: TSource }> = [];\n\tfor (const input of inputs) {\n\t\tconst result = await loadPromptTemplates(env, input.path);\n\t\tfor (const promptTemplate of result.promptTemplates) {\n\t\t\tpromptTemplates.push({\n\t\t\t\tpromptTemplate: mapPromptTemplate\n\t\t\t\t\t? mapPromptTemplate(promptTemplate, input.source)\n\t\t\t\t\t: (promptTemplate as TPromptTemplate),\n\t\t\t\tsource: input.source,\n\t\t\t});\n\t\t}\n\t\tfor (const diagnostic of result.diagnostics) diagnostics.push({ ...diagnostic, source: input.source });\n\t}\n\treturn { promptTemplates, diagnostics };\n}\n\nasync function loadTemplatesFromDir(\n\tenv: ExecutionEnv,\n\tdir: string,\n): Promise<{ promptTemplates: PromptTemplate[]; diagnostics: PromptTemplateDiagnostic[] }> {\n\tconst promptTemplates: PromptTemplate[] = [];\n\tconst diagnostics: PromptTemplateDiagnostic[] = [];\n\tconst entriesResult = await env.listDir(dir);\n\tif (!entriesResult.ok) {\n\t\tdiagnostics.push({\n\t\t\ttype: \"warning\",\n\t\t\tcode: \"list_failed\",\n\t\t\tmessage: entriesResult.error.message,\n\t\t\tpath: dir,\n\t\t});\n\t\treturn { promptTemplates, diagnostics };\n\t}\n\tconst entries = entriesResult.value;\n\n\tfor (const entry of entries.sort((a, b) => a.name.localeCompare(b.name))) {\n\t\tconst kind = await resolveKind(env, entry, diagnostics);\n\t\tif (kind !== \"file\" || !entry.name.endsWith(\".md\")) continue;\n\t\tconst result = await loadTemplateFromFile(env, entry.path, entry.name);\n\t\tif (result.promptTemplate) promptTemplates.push(result.promptTemplate);\n\t\tdiagnostics.push(...result.diagnostics);\n\t}\n\treturn { promptTemplates, diagnostics };\n}\n\nasync function loadTemplateFromFile(\n\tenv: ExecutionEnv,\n\tfilePath: string,\n\tfileName: string,\n): Promise<{ promptTemplate: PromptTemplate | null; diagnostics: PromptTemplateDiagnostic[] }> {\n\tconst diagnostics: PromptTemplateDiagnostic[] = [];\n\tconst rawContent = await env.readTextFile(filePath);\n\tif (!rawContent.ok) {\n\t\tdiagnostics.push({\n\t\t\ttype: \"warning\",\n\t\t\tcode: \"read_failed\",\n\t\t\tmessage: rawContent.error.message,\n\t\t\tpath: filePath,\n\t\t});\n\t\treturn { promptTemplate: null, diagnostics };\n\t}\n\n\tconst parsed = parseFrontmatter<PromptTemplateFrontmatter>(rawContent.value);\n\tif (!parsed.ok) {\n\t\tdiagnostics.push({\n\t\t\ttype: \"warning\",\n\t\t\tcode: \"parse_failed\",\n\t\t\tmessage: parsed.error.message,\n\t\t\tpath: filePath,\n\t\t});\n\t\treturn { promptTemplate: null, diagnostics };\n\t}\n\n\tconst { frontmatter, body } = parsed.value;\n\tconst firstLine = body.split(\"\\n\").find((line) => line.trim());\n\tlet description = typeof frontmatter.description === \"string\" ? frontmatter.description : \"\";\n\tif (!description && firstLine) {\n\t\tdescription = firstLine.slice(0, 60);\n\t\tif (firstLine.length > 60) description += \"...\";\n\t}\n\treturn {\n\t\tpromptTemplate: {\n\t\t\tname: fileName.replace(/\\.md$/i, \"\"),\n\t\t\tdescription,\n\t\t\tcontent: body,\n\t\t},\n\t\tdiagnostics,\n\t};\n}\n\nasync function resolveKind(\n\tenv: ExecutionEnv,\n\tinfo: FileInfo,\n\tdiagnostics: PromptTemplateDiagnostic[],\n): Promise<\"file\" | \"directory\" | undefined> {\n\tif (info.kind === \"file\" || info.kind === \"directory\") return info.kind;\n\tconst canonicalPath = await env.canonicalPath(info.path);\n\tif (!canonicalPath.ok) {\n\t\tif (canonicalPath.error.code !== \"not_found\") {\n\t\t\tdiagnostics.push({\n\t\t\t\ttype: \"warning\",\n\t\t\t\tcode: \"file_info_failed\",\n\t\t\t\tmessage: canonicalPath.error.message,\n\t\t\t\tpath: info.path,\n\t\t\t});\n\t\t}\n\t\treturn undefined;\n\t}\n\tconst target = await env.fileInfo(canonicalPath.value);\n\tif (!target.ok) {\n\t\tif (target.error.code !== \"not_found\") {\n\t\t\tdiagnostics.push({\n\t\t\t\ttype: \"warning\",\n\t\t\t\tcode: \"file_info_failed\",\n\t\t\t\tmessage: target.error.message,\n\t\t\t\tpath: info.path,\n\t\t\t});\n\t\t}\n\t\treturn undefined;\n\t}\n\treturn target.value.kind === \"file\" || target.value.kind === \"directory\" ? target.value.kind : undefined;\n}\n\nfunction parseFrontmatter<T extends Record<string, unknown>>(\n\tcontent: string,\n): Result<{ frontmatter: T; body: string }, Error> {\n\ttry {\n\t\tconst normalized = content.replace(/\\r\\n/g, \"\\n\").replace(/\\r/g, \"\\n\");\n\t\tif (!normalized.startsWith(\"---\")) return { ok: true, value: { frontmatter: {} as T, body: normalized } };\n\t\tconst endIndex = normalized.indexOf(\"\\n---\", 3);\n\t\tif (endIndex === -1) return { ok: true, value: { frontmatter: {} as T, body: normalized } };\n\t\tconst yamlString = normalized.slice(4, endIndex);\n\t\tconst body = normalized.slice(endIndex + 4).trim();\n\t\treturn { ok: true, value: { frontmatter: (parse(yamlString) ?? {}) as T, body } };\n\t} catch (error) {\n\t\treturn { ok: false, error: toError(error) };\n\t}\n}\n\n/** Parse an argument string using simple shell-style single and double quotes. */\nexport function parseCommandArgs(argsString: string): string[] {\n\tconst args: string[] = [];\n\tlet current = \"\";\n\tlet inQuote: string | null = null;\n\n\tfor (let i = 0; i < argsString.length; i++) {\n\t\tconst char = argsString[i]!;\n\t\tif (inQuote) {\n\t\t\tif (char === inQuote) inQuote = null;\n\t\t\telse current += char;\n\t\t} else if (char === '\"' || char === \"'\") {\n\t\t\tinQuote = char;\n\t\t} else if (char === \" \" || char === \"\\t\") {\n\t\t\tif (current) {\n\t\t\t\targs.push(current);\n\t\t\t\tcurrent = \"\";\n\t\t\t}\n\t\t} else {\n\t\t\tcurrent += char;\n\t\t}\n\t}\n\tif (current) args.push(current);\n\treturn args;\n}\n\n/** Substitute prompt template placeholders (`$1`, `$@`, `$ARGUMENTS`, `${@:N}`, `${@:N:L}`) with command arguments. */\nexport function substituteArgs(content: string, args: string[]): string {\n\tlet result = content;\n\tresult = result.replace(/\\$(\\d+)/g, (_, num: string) => args[parseInt(num, 10) - 1] ?? \"\");\n\tresult = result.replace(/\\$\\{@:(\\d+)(?::(\\d+))?\\}/g, (_, startStr: string, lengthStr?: string) => {\n\t\tlet start = parseInt(startStr, 10) - 1;\n\t\tif (start < 0) start = 0;\n\t\tif (lengthStr) return args.slice(start, start + parseInt(lengthStr, 10)).join(\" \");\n\t\treturn args.slice(start).join(\" \");\n\t});\n\tconst allArgs = args.join(\" \");\n\tresult = result.replace(/\\$ARGUMENTS/g, allArgs);\n\tresult = result.replace(/\\$@/g, allArgs);\n\treturn result;\n}\n\n/** Format a prompt template invocation with positional arguments. */\nexport function formatPromptTemplateInvocation(template: PromptTemplate, args: string[] = []): string {\n\treturn substituteArgs(template.content, args);\n}\n"]}
@@ -0,0 +1,100 @@
1
+ import type { AssistantMessage, DeferredHandle, StopReason } from "@earendil-works/pi-ai";
2
+ import type { AgentMessage, AgentToolCall, ThinkingLevel } from "../types.ts";
3
+ import type { Entry, LaneRecord, OperationStartedRecord, ProvisionedEntry, ToolStartedRecord } from "./session/types.ts";
4
+ /**
5
+ * Machine-readable category for a contradiction in a lane's durable recovery
6
+ * slice. These indicate states the single-writer record protocol cannot
7
+ * produce, not ordinary operation failures or incomplete-but-recoverable
8
+ * intent/result prefixes. Restore must reject such states rather than repair or
9
+ * continue it; the accompanying error message supplies human-readable detail.
10
+ */
11
+ export type RecordLogCorruptionReason = "multiple_open_operations" | "unknown_operation" | "record_after_finish" | "non_consecutive_attempt" | "invalid_compaction_reason" | "queue_after_abort" | "invalid_queue_cancellation" | "inconsistent_step" | "tool_call_mismatch" | "duplicate_tool_invocation" | "provisioned_entry_mismatch" | "invalid_deferred_handle";
12
+ export declare class RecordLogCorruption extends Error {
13
+ readonly reason: RecordLogCorruptionReason;
14
+ constructor(reason: RecordLogCorruptionReason, message: string);
15
+ }
16
+ export interface RecordLogSlice {
17
+ lane: string;
18
+ openOperations: readonly OperationStartedRecord[];
19
+ records: readonly LaneRecord[];
20
+ /** Operation-owned entries plus entries fetched directly by provisioned or referenced ids. */
21
+ entries: readonly Entry[];
22
+ }
23
+ export interface EffectiveLaneConfiguration {
24
+ model: {
25
+ provider: string;
26
+ modelId: string;
27
+ };
28
+ thinkingLevel: ThinkingLevel;
29
+ activeToolNames: string[];
30
+ }
31
+ export interface TerminalFailureState {
32
+ entryId: string;
33
+ source: "step" | "deferred_fetch";
34
+ message: AssistantMessage;
35
+ }
36
+ export interface ToolBatchState {
37
+ assistantEntryId: string;
38
+ calls: {
39
+ toolIndex: number;
40
+ toolCall: AgentToolCall;
41
+ started?: ToolStartedRecord;
42
+ resultExists: boolean;
43
+ terminate?: boolean;
44
+ }[];
45
+ truncated: boolean;
46
+ unresolved: boolean;
47
+ }
48
+ export interface LaneState {
49
+ lane: string;
50
+ leafId: string | null;
51
+ operation: null | {
52
+ id: string;
53
+ kind: "run" | "compaction" | "navigation";
54
+ intent: OperationStartedRecord["intent"];
55
+ aborting: boolean;
56
+ step: null | {
57
+ kind: "assistant" | "compaction" | "branch_summary";
58
+ attempts: number;
59
+ resultEntryId: string;
60
+ compactionReason?: "manual" | "threshold" | "overflow";
61
+ };
62
+ toolBatch: ToolBatchState | null;
63
+ missingInitialMessages: ProvisionedEntry[];
64
+ pendingSteer: ProvisionedEntry[];
65
+ pendingFollowUp: ProvisionedEntry[];
66
+ pendingWrites: ProvisionedEntry[];
67
+ deferred: DeferredHandle | null;
68
+ overflowRecoveryUsed: boolean;
69
+ newestOwn: null | {
70
+ entryId: string;
71
+ type: Entry["type"];
72
+ role?: AgentMessage["role"];
73
+ stopReason?: StopReason;
74
+ };
75
+ targets: {
76
+ result?: boolean;
77
+ summary?: boolean;
78
+ };
79
+ };
80
+ pendingNextRun: ProvisionedEntry[];
81
+ }
82
+ export interface LaneReductionInput extends RecordLogSlice {
83
+ leafId: string | null;
84
+ /** Entries appended by the open operation, oldest first. Empty when idle. */
85
+ ownEntries: readonly Entry[];
86
+ /** Bounded effective-state lookups at the operation anchor or idle leaf, oldest first. */
87
+ configurationEntries: readonly Entry[];
88
+ /** Harness option fallbacks used when no persisted value exists. */
89
+ defaults: EffectiveLaneConfiguration;
90
+ }
91
+ export interface LaneReductionResult {
92
+ laneState: LaneState;
93
+ effectiveConfiguration: EffectiveLaneConfiguration;
94
+ terminalFailure: TerminalFailureState | null;
95
+ }
96
+ /** Validates a bounded lane recovery slice without reading or mutating session state. */
97
+ export declare function validateRecordLog(input: RecordLogSlice): void;
98
+ /** Purely reconstructs one lane's orchestration state from its bounded recovery inputs. */
99
+ export declare function reduceLaneState(input: LaneReductionInput): LaneReductionResult;
100
+ //# sourceMappingURL=reducer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"reducer.d.ts","sourceRoot":"","sources":["../../src/harness/reducer.ts"],"names":[],"mappings":"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","sourcesContent":["import type { AssistantMessage, DeferredHandle, StopReason } from \"@earendil-works/pi-ai\";\nimport { Guard } from \"typebox/guard\";\nimport type { AgentMessage, AgentToolCall, ThinkingLevel } from \"../types.ts\";\nimport type {\n\tEntry,\n\tLaneRecord,\n\tOperationStartedRecord,\n\tProvisionedEntry,\n\tQueueEnqueuedRecord,\n\tStepAttemptRecord,\n\tToolStartedRecord,\n\tWriteDeferredRecord,\n} from \"./session/types.ts\";\n\n/**\n * Machine-readable category for a contradiction in a lane's durable recovery\n * slice. These indicate states the single-writer record protocol cannot\n * produce, not ordinary operation failures or incomplete-but-recoverable\n * intent/result prefixes. Restore must reject such states rather than repair or\n * continue it; the accompanying error message supplies human-readable detail.\n */\nexport type RecordLogCorruptionReason =\n\t| \"multiple_open_operations\"\n\t| \"unknown_operation\"\n\t| \"record_after_finish\"\n\t| \"non_consecutive_attempt\"\n\t| \"invalid_compaction_reason\"\n\t| \"queue_after_abort\"\n\t| \"invalid_queue_cancellation\"\n\t| \"inconsistent_step\"\n\t| \"tool_call_mismatch\"\n\t| \"duplicate_tool_invocation\"\n\t| \"provisioned_entry_mismatch\"\n\t| \"invalid_deferred_handle\";\n\nexport class RecordLogCorruption extends Error {\n\treadonly reason: RecordLogCorruptionReason;\n\n\tconstructor(reason: RecordLogCorruptionReason, message: string) {\n\t\tsuper(message);\n\t\tthis.name = \"RecordLogCorruption\";\n\t\tthis.reason = reason;\n\t}\n}\n\nexport interface RecordLogSlice {\n\tlane: string;\n\topenOperations: readonly OperationStartedRecord[];\n\trecords: readonly LaneRecord[];\n\t/** Operation-owned entries plus entries fetched directly by provisioned or referenced ids. */\n\tentries: readonly Entry[];\n}\n\nexport interface EffectiveLaneConfiguration {\n\tmodel: { provider: string; modelId: string };\n\tthinkingLevel: ThinkingLevel;\n\tactiveToolNames: string[];\n}\n\nexport interface TerminalFailureState {\n\tentryId: string;\n\tsource: \"step\" | \"deferred_fetch\";\n\tmessage: AssistantMessage;\n}\n\nexport interface ToolBatchState {\n\tassistantEntryId: string;\n\tcalls: {\n\t\ttoolIndex: number;\n\t\ttoolCall: AgentToolCall;\n\t\tstarted?: ToolStartedRecord;\n\t\tresultExists: boolean;\n\t\tterminate?: boolean;\n\t}[];\n\ttruncated: boolean;\n\tunresolved: boolean;\n}\n\nexport interface LaneState {\n\tlane: string;\n\tleafId: string | null;\n\toperation: null | {\n\t\tid: string;\n\t\tkind: \"run\" | \"compaction\" | \"navigation\";\n\t\tintent: OperationStartedRecord[\"intent\"];\n\t\taborting: boolean;\n\t\tstep: null | {\n\t\t\tkind: \"assistant\" | \"compaction\" | \"branch_summary\";\n\t\t\tattempts: number;\n\t\t\tresultEntryId: string;\n\t\t\tcompactionReason?: \"manual\" | \"threshold\" | \"overflow\";\n\t\t};\n\t\ttoolBatch: ToolBatchState | null;\n\t\tmissingInitialMessages: ProvisionedEntry[];\n\t\tpendingSteer: ProvisionedEntry[];\n\t\tpendingFollowUp: ProvisionedEntry[];\n\t\tpendingWrites: ProvisionedEntry[];\n\t\tdeferred: DeferredHandle | null;\n\t\toverflowRecoveryUsed: boolean;\n\t\tnewestOwn: null | {\n\t\t\tentryId: string;\n\t\t\ttype: Entry[\"type\"];\n\t\t\trole?: AgentMessage[\"role\"];\n\t\t\tstopReason?: StopReason;\n\t\t};\n\t\ttargets: { result?: boolean; summary?: boolean };\n\t};\n\tpendingNextRun: ProvisionedEntry[];\n}\n\nexport interface LaneReductionInput extends RecordLogSlice {\n\tleafId: string | null;\n\t/** Entries appended by the open operation, oldest first. Empty when idle. */\n\townEntries: readonly Entry[];\n\t/** Bounded effective-state lookups at the operation anchor or idle leaf, oldest first. */\n\tconfigurationEntries: readonly Entry[];\n\t/** Harness option fallbacks used when no persisted value exists. */\n\tdefaults: EffectiveLaneConfiguration;\n}\n\nexport interface LaneReductionResult {\n\tlaneState: LaneState;\n\teffectiveConfiguration: EffectiveLaneConfiguration;\n\tterminalFailure: TerminalFailureState | null;\n}\n\ninterface AttemptSeries {\n\trecord: StepAttemptRecord;\n}\n\nfunction corrupt(reason: RecordLogCorruptionReason, message: string): never {\n\tthrow new RecordLogCorruption(reason, message);\n}\n\nfunction hasRunId(record: LaneRecord): record is Exclude<LaneRecord, OperationStartedRecord> & { runId: string } {\n\treturn \"runId\" in record && typeof record.runId === \"string\";\n}\n\nfunction matchesProvisionedEntry(entry: Entry, target: ProvisionedEntry): boolean {\n\tconst { parentId: _parentId, seq: _seq, timestamp: _timestamp, ...payload } = entry;\n\treturn Guard.IsDeepEqual(payload, target);\n}\n\nfunction validateExactProvisionedEntry(entriesById: ReadonlyMap<string, Entry>, target: ProvisionedEntry): void {\n\tconst entry = entriesById.get(target.id);\n\tif (entry && !matchesProvisionedEntry(entry, target)) {\n\t\tcorrupt(\n\t\t\t\"provisioned_entry_mismatch\",\n\t\t\t`Provisioned entry ${target.id} exists with content different from its intent`,\n\t\t);\n\t}\n}\n\nfunction validateResultEntry(\n\tentriesById: ReadonlyMap<string, Entry>,\n\tresultEntryId: string,\n\tmatches: (entry: Entry) => boolean,\n\tdescription: string,\n): void {\n\tconst entry = entriesById.get(resultEntryId);\n\tif (entry && !matches(entry)) {\n\t\tcorrupt(\n\t\t\t\"provisioned_entry_mismatch\",\n\t\t\t`Provisioned ${description} entry ${resultEntryId} exists with different content`,\n\t\t);\n\t}\n}\n\nfunction validateAttemptReason(record: StepAttemptRecord): void {\n\tconst reason = (record as { compactionReason?: unknown }).compactionReason;\n\tif (record.step === \"compaction\") {\n\t\tif (reason !== \"manual\" && reason !== \"threshold\" && reason !== \"overflow\") {\n\t\t\tcorrupt(\"invalid_compaction_reason\", `Compaction attempt ${record.id} has no valid compaction reason`);\n\t\t}\n\t} else if (reason !== undefined) {\n\t\tcorrupt(\"invalid_compaction_reason\", `${record.step} attempt ${record.id} has a compaction reason`);\n\t}\n}\n\nfunction validateAttemptSequence(\n\trecord: StepAttemptRecord,\n\tprevious: AttemptSeries | undefined,\n\tentriesById: ReadonlyMap<string, Entry>,\n): void {\n\tconst previousRecord = previous?.record;\n\tconst previousResult = previousRecord ? entriesById.get(previousRecord.resultEntryId) : undefined;\n\tconst continuesSeries =\n\t\tpreviousRecord !== undefined &&\n\t\tpreviousRecord.step === record.step &&\n\t\t(previousResult === undefined || previousResult.seq >= record.seq);\n\tconst expectedAttempt = continuesSeries ? previousRecord.attempt + 1 : 1;\n\tif (record.attempt !== expectedAttempt) {\n\t\tcorrupt(\n\t\t\t\"non_consecutive_attempt\",\n\t\t\t`${record.step} attempt ${record.id} is ${record.attempt}; expected ${expectedAttempt}`,\n\t\t);\n\t}\n\tif (!continuesSeries || record.step === \"assistant\" || previousRecord === undefined) return;\n\tif (record.resultEntryId !== previousRecord.resultEntryId) {\n\t\tcorrupt(\"inconsistent_step\", `${record.step} attempts disagree on their result entry id`);\n\t}\n\tif (record.compactionReason !== previousRecord.compactionReason) {\n\t\tcorrupt(\"inconsistent_step\", `${record.step} attempts disagree on their compaction reason`);\n\t}\n}\n\nfunction validateAttemptResult(entriesById: ReadonlyMap<string, Entry>, record: StepAttemptRecord): void {\n\tswitch (record.step) {\n\t\tcase \"assistant\":\n\t\t\tvalidateResultEntry(\n\t\t\t\tentriesById,\n\t\t\t\trecord.resultEntryId,\n\t\t\t\t(entry) => entry.type === \"message\" && entry.message.role === \"assistant\",\n\t\t\t\t\"assistant result\",\n\t\t\t);\n\t\t\tbreak;\n\t\tcase \"compaction\":\n\t\t\tvalidateResultEntry(\n\t\t\t\tentriesById,\n\t\t\t\trecord.resultEntryId,\n\t\t\t\t(entry) => entry.type === \"compaction\",\n\t\t\t\t\"compaction result\",\n\t\t\t);\n\t\t\tbreak;\n\t\tcase \"branch_summary\":\n\t\t\tvalidateResultEntry(\n\t\t\t\tentriesById,\n\t\t\t\trecord.resultEntryId,\n\t\t\t\t(entry) => entry.type === \"branch_summary\",\n\t\t\t\t\"branch-summary result\",\n\t\t\t);\n\t\t\tbreak;\n\t}\n}\n\nfunction validateToolStart(\n\trecord: Extract<LaneRecord, { type: \"tool_started\" }>,\n\tentriesById: ReadonlyMap<string, Entry>,\n\tinvocations: Set<string>,\n): void {\n\tconst invocation = `${record.assistantEntryId}\\u0000${record.toolIndex}`;\n\tif (invocations.has(invocation)) {\n\t\tcorrupt(\n\t\t\t\"duplicate_tool_invocation\",\n\t\t\t`Tool invocation ${record.assistantEntryId}:${record.toolIndex} is duplicated`,\n\t\t);\n\t}\n\tinvocations.add(invocation);\n\n\tconst assistantEntry = entriesById.get(record.assistantEntryId);\n\tif (!assistantEntry || assistantEntry.type !== \"message\" || assistantEntry.message.role !== \"assistant\") {\n\t\tcorrupt(\"tool_call_mismatch\", `Tool start ${record.id} does not reference an assistant entry`);\n\t}\n\tconst toolCalls = assistantEntry.message.content.filter((content) => content.type === \"toolCall\");\n\tconst toolCall = toolCalls[record.toolIndex];\n\tif (!toolCall || toolCall.id !== record.toolCallId || toolCall.name !== record.toolName) {\n\t\tcorrupt(\"tool_call_mismatch\", `Tool start ${record.id} does not match its assistant tool-call ordinal`);\n\t}\n\n\tvalidateResultEntry(\n\t\tentriesById,\n\t\trecord.resultEntryId,\n\t\t(entry) =>\n\t\t\tentry.type === \"message\" &&\n\t\t\tentry.message.role === \"toolResult\" &&\n\t\t\tentry.message.toolCallId === record.toolCallId &&\n\t\t\tentry.message.toolName === record.toolName,\n\t\t\"tool result\",\n\t);\n}\n\nfunction validateDeferredHandles(entries: Iterable<Entry>): void {\n\tfor (const entry of entries) {\n\t\tif (\n\t\t\tentry.type === \"message\" &&\n\t\t\tentry.message.role === \"assistant\" &&\n\t\t\tentry.message.stopReason === \"deferred\" &&\n\t\t\t!entry.message.deferred\n\t\t) {\n\t\t\tcorrupt(\"invalid_deferred_handle\", `Deferred assistant entry ${entry.id} does not carry a handle`);\n\t\t}\n\t}\n}\n\nfunction validateOperationResult(entriesById: ReadonlyMap<string, Entry>, record: OperationStartedRecord): void {\n\tswitch (record.intent.kind) {\n\t\tcase \"run\":\n\t\t\tfor (const target of record.intent.initialMessages) validateExactProvisionedEntry(entriesById, target);\n\t\t\tbreak;\n\t\tcase \"compaction\":\n\t\t\tvalidateResultEntry(\n\t\t\t\tentriesById,\n\t\t\t\trecord.intent.resultEntryId,\n\t\t\t\t(entry) => entry.type === \"compaction\",\n\t\t\t\t\"manual compaction\",\n\t\t\t);\n\t\t\tbreak;\n\t\tcase \"navigation\":\n\t\t\tif (record.intent.summaryEntryId) {\n\t\t\t\tvalidateResultEntry(\n\t\t\t\t\tentriesById,\n\t\t\t\t\trecord.intent.summaryEntryId,\n\t\t\t\t\t(entry) => entry.type === \"branch_summary\",\n\t\t\t\t\t\"navigation summary\",\n\t\t\t\t);\n\t\t\t}\n\t\t\tbreak;\n\t}\n}\n\n/** Validates a bounded lane recovery slice without reading or mutating session state. */\nexport function validateRecordLog(input: RecordLogSlice): void {\n\tif (input.openOperations.length > 1) {\n\t\tcorrupt(\"multiple_open_operations\", `Lane ${input.lane} has at least two open operations`);\n\t}\n\n\tconst entriesById = new Map(input.entries.map((entry) => [entry.id, entry]));\n\tvalidateDeferredHandles(entriesById.values());\n\tconst starts = new Map<string, OperationStartedRecord>();\n\tconst finishedAt = new Map<string, number>();\n\tconst abortedAt = new Map<string, number>();\n\tconst queueEnqueues = new Map<string, Extract<LaneRecord, { type: \"queue_enqueued\" }>>();\n\tconst latestAttempt = new Map<string, AttemptSeries>();\n\tconst toolInvocations = new Set<string>();\n\tconst records = [...input.records].sort((left, right) => left.seq - right.seq);\n\n\tfor (const record of records) {\n\t\tif (record.type === \"operation_started\") {\n\t\t\tstarts.set(record.id, record);\n\t\t\tvalidateOperationResult(entriesById, record);\n\t\t\tcontinue;\n\t\t}\n\n\t\tif (hasRunId(record)) {\n\t\t\tif (!starts.has(record.runId)) {\n\t\t\t\tcorrupt(\"unknown_operation\", `Record ${record.id} references unknown operation ${record.runId}`);\n\t\t\t}\n\t\t\tconst finishSeq = finishedAt.get(record.runId);\n\t\t\tif (finishSeq !== undefined && record.seq > finishSeq) {\n\t\t\t\tcorrupt(\"record_after_finish\", `Record ${record.id} follows the finish of operation ${record.runId}`);\n\t\t\t}\n\t\t}\n\n\t\tswitch (record.type) {\n\t\t\tcase \"operation_finished\":\n\t\t\t\tfinishedAt.set(record.runId, record.seq);\n\t\t\t\tbreak;\n\t\t\tcase \"abort_requested\":\n\t\t\t\tabortedAt.set(record.runId, record.seq);\n\t\t\t\tbreak;\n\t\t\tcase \"step_attempt\":\n\t\t\t\tvalidateAttemptReason(record);\n\t\t\t\tvalidateAttemptSequence(record, latestAttempt.get(record.runId), entriesById);\n\t\t\t\tvalidateAttemptResult(entriesById, record);\n\t\t\t\tlatestAttempt.set(record.runId, { record });\n\t\t\t\tbreak;\n\t\t\tcase \"tool_started\":\n\t\t\t\tvalidateToolStart(record, entriesById, toolInvocations);\n\t\t\t\tbreak;\n\t\t\tcase \"queue_enqueued\":\n\t\t\t\tif (\n\t\t\t\t\trecord.queue !== \"nextRun\" &&\n\t\t\t\t\tabortedAt.get(record.runId) !== undefined &&\n\t\t\t\t\trecord.seq > abortedAt.get(record.runId)!\n\t\t\t\t) {\n\t\t\t\t\tcorrupt(\"queue_after_abort\", `${record.queue} item ${record.target.id} was enqueued after abort`);\n\t\t\t\t}\n\t\t\t\tqueueEnqueues.set(record.target.id, record);\n\t\t\t\tvalidateExactProvisionedEntry(entriesById, record.target);\n\t\t\t\tbreak;\n\t\t\tcase \"queue_cancelled\": {\n\t\t\t\tconst enqueue = queueEnqueues.get(record.entryId);\n\t\t\t\tif (\n\t\t\t\t\t!enqueue ||\n\t\t\t\t\tenqueue.seq >= record.seq ||\n\t\t\t\t\tenqueue.runId !== record.runId ||\n\t\t\t\t\tentriesById.has(record.entryId)\n\t\t\t\t) {\n\t\t\t\t\tcorrupt(\"invalid_queue_cancellation\", `Queue cancellation ${record.id} has no pending matching enqueue`);\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase \"write_deferred\":\n\t\t\t\tvalidateExactProvisionedEntry(entriesById, record.target);\n\t\t\t\tbreak;\n\t\t\tcase \"usage\":\n\t\t\t\tbreak;\n\t\t}\n\t}\n}\n\nfunction clone<T>(value: T): T {\n\treturn structuredClone(value);\n}\n\nfunction bySequence<T extends { seq: number }>(values: readonly T[]): T[] {\n\treturn [...values].sort((left, right) => left.seq - right.seq);\n}\n\nfunction deriveEffectiveConfiguration(input: LaneReductionInput): EffectiveLaneConfiguration {\n\tlet configuration = clone(input.defaults);\n\tconst entriesById = new Map<string, Entry>();\n\tfor (const entry of [...input.configurationEntries, ...input.ownEntries]) entriesById.set(entry.id, entry);\n\n\tfor (const entry of bySequence([...entriesById.values()])) {\n\t\tswitch (entry.type) {\n\t\t\tcase \"model_change\":\n\t\t\t\tconfiguration = { ...configuration, model: { provider: entry.provider, modelId: entry.modelId } };\n\t\t\t\tbreak;\n\t\t\tcase \"thinking_level_change\":\n\t\t\t\tconfiguration = { ...configuration, thinkingLevel: entry.thinkingLevel as ThinkingLevel };\n\t\t\t\tbreak;\n\t\t\tcase \"active_tools_change\":\n\t\t\t\tconfiguration = { ...configuration, activeToolNames: [...entry.activeToolNames] };\n\t\t\t\tbreak;\n\t\t\tcase \"message\":\n\t\t\t\tif (entry.message.role === \"assistant\") {\n\t\t\t\t\tconfiguration = {\n\t\t\t\t\t\t...configuration,\n\t\t\t\t\t\tmodel: { provider: entry.message.provider, modelId: entry.message.model },\n\t\t\t\t\t};\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t}\n\t}\n\treturn configuration;\n}\n\nfunction deriveNewestOwn(\n\tentry: Entry | undefined,\n): NonNullable<NonNullable<LaneState[\"operation\"]>[\"newestOwn\"]> | null {\n\tif (!entry) return null;\n\tif (entry.type !== \"message\") return { entryId: entry.id, type: entry.type };\n\tif (entry.message.role !== \"assistant\") {\n\t\treturn { entryId: entry.id, type: entry.type, role: entry.message.role };\n\t}\n\treturn {\n\t\tentryId: entry.id,\n\t\ttype: entry.type,\n\t\trole: entry.message.role,\n\t\tstopReason: entry.message.stopReason,\n\t};\n}\n\nfunction deriveToolBatch(\n\toperationId: string,\n\trecords: readonly LaneRecord[],\n\townEntries: readonly Entry[],\n\tentriesById: ReadonlyMap<string, Entry>,\n\tdeferredWriteIds: ReadonlySet<string>,\n): ToolBatchState | null {\n\tconst assistantEntry = [...ownEntries]\n\t\t.reverse()\n\t\t.find(\n\t\t\t(entry) =>\n\t\t\t\tentry.type === \"message\" &&\n\t\t\t\tentry.message.role === \"assistant\" &&\n\t\t\t\tentry.message.content.some((content) => content.type === \"toolCall\"),\n\t\t);\n\tif (!assistantEntry || assistantEntry.type !== \"message\" || assistantEntry.message.role !== \"assistant\") return null;\n\n\tconst toolCalls = assistantEntry.message.content.filter(\n\t\t(content): content is AgentToolCall => content.type === \"toolCall\",\n\t);\n\tconst starts = new Map<number, ToolStartedRecord>();\n\tfor (const record of records) {\n\t\tif (\n\t\t\trecord.type === \"tool_started\" &&\n\t\t\trecord.runId === operationId &&\n\t\t\trecord.assistantEntryId === assistantEntry.id\n\t\t) {\n\t\t\tstarts.set(record.toolIndex, record);\n\t\t}\n\t}\n\n\tconst calls = toolCalls.map((toolCall, toolIndex) => {\n\t\tconst started = starts.get(toolIndex);\n\t\tconst startedResult = started ? entriesById.get(started.resultEntryId) : undefined;\n\t\tconst blockedResult = ownEntries.find(\n\t\t\t(entry) =>\n\t\t\t\tentry.seq > assistantEntry.seq &&\n\t\t\t\t!deferredWriteIds.has(entry.id) &&\n\t\t\t\tentry.type === \"message\" &&\n\t\t\t\tentry.message.role === \"toolResult\" &&\n\t\t\t\tentry.message.toolCallId === toolCall.id,\n\t\t);\n\t\tconst result = startedResult ?? blockedResult;\n\t\treturn {\n\t\t\ttoolIndex,\n\t\t\ttoolCall: clone(toolCall),\n\t\t\t...(started ? { started: clone(started) } : {}),\n\t\t\tresultExists: result !== undefined,\n\t\t\t...(result?.type === \"message\" && result.terminate === true ? { terminate: true } : {}),\n\t\t};\n\t});\n\n\treturn {\n\t\tassistantEntryId: assistantEntry.id,\n\t\tcalls,\n\t\ttruncated: assistantEntry.message.stopReason === \"length\",\n\t\tunresolved: calls.some((call) => !call.resultExists),\n\t};\n}\n\n/** Purely reconstructs one lane's orchestration state from its bounded recovery inputs. */\nexport function reduceLaneState(input: LaneReductionInput): LaneReductionResult {\n\tvalidateRecordLog(input);\n\n\tconst records = bySequence(input.records);\n\tconst ownEntries = bySequence(input.ownEntries);\n\tconst entriesById = new Map<string, Entry>();\n\tfor (const entry of [...input.entries, ...ownEntries]) entriesById.set(entry.id, entry);\n\tconst cancelledQueueIds = new Set(\n\t\trecords.filter((record) => record.type === \"queue_cancelled\").map((record) => record.entryId),\n\t);\n\tconst pendingQueueRecords = records.filter(\n\t\t(record): record is QueueEnqueuedRecord =>\n\t\t\trecord.type === \"queue_enqueued\" &&\n\t\t\t!entriesById.has(record.target.id) &&\n\t\t\t!cancelledQueueIds.has(record.target.id),\n\t);\n\tconst started = input.openOperations[0];\n\tconst capturedInitialMessageIds = new Set(\n\t\tstarted?.intent.kind === \"run\" ? started.intent.initialMessages.map((target) => target.id) : [],\n\t);\n\tconst pendingNextRun = pendingQueueRecords\n\t\t.filter((record) => record.queue === \"nextRun\" && !capturedInitialMessageIds.has(record.target.id))\n\t\t.map((record) => clone(record.target));\n\tconst effectiveConfiguration = deriveEffectiveConfiguration(input);\n\n\tif (!started) {\n\t\treturn {\n\t\t\tlaneState: { lane: input.lane, leafId: input.leafId, operation: null, pendingNextRun },\n\t\t\teffectiveConfiguration,\n\t\t\tterminalFailure: null,\n\t\t};\n\t}\n\n\tconst operationRecords = records.filter((record) =>\n\t\trecord.type === \"operation_started\" ? record.id === started.id : \"runId\" in record && record.runId === started.id,\n\t);\n\tconst aborting = operationRecords.some((record) => record.type === \"abort_requested\");\n\tconst pendingSteer = aborting\n\t\t? []\n\t\t: pendingQueueRecords\n\t\t\t\t.filter((record) => record.queue === \"steer\" && record.runId === started.id)\n\t\t\t\t.map((record) => clone(record.target));\n\tconst pendingFollowUp = aborting\n\t\t? []\n\t\t: pendingQueueRecords\n\t\t\t\t.filter((record) => record.queue === \"followUp\" && record.runId === started.id)\n\t\t\t\t.map((record) => clone(record.target));\n\tconst pendingWrites = operationRecords\n\t\t.filter(\n\t\t\t(record): record is WriteDeferredRecord =>\n\t\t\t\trecord.type === \"write_deferred\" && !entriesById.has(record.target.id),\n\t\t)\n\t\t.map((record) => clone(record.target));\n\tconst missingInitialMessages =\n\t\tstarted.intent.kind === \"run\"\n\t\t\t? started.intent.initialMessages.filter((target) => !entriesById.has(target.id)).map(clone)\n\t\t\t: [];\n\n\tconst newestAttempt = operationRecords.filter((record) => record.type === \"step_attempt\").at(-1);\n\tconst step =\n\t\tnewestAttempt && !entriesById.has(newestAttempt.resultEntryId)\n\t\t\t? {\n\t\t\t\t\tkind: newestAttempt.step,\n\t\t\t\t\tattempts: newestAttempt.attempt,\n\t\t\t\t\tresultEntryId: newestAttempt.resultEntryId,\n\t\t\t\t\t...(newestAttempt.step === \"compaction\" ? { compactionReason: newestAttempt.compactionReason } : {}),\n\t\t\t\t}\n\t\t\t: null;\n\n\tconst consumedInputIds = new Set<string>();\n\tif (started.intent.kind === \"run\") {\n\t\tfor (const target of started.intent.initialMessages) consumedInputIds.add(target.id);\n\t}\n\tfor (const record of operationRecords) {\n\t\tif (record.type === \"queue_enqueued\" && record.queue !== \"nextRun\") consumedInputIds.add(record.target.id);\n\t}\n\tlet newestConsumedInputSequence = Number.NEGATIVE_INFINITY;\n\tfor (const id of consumedInputIds) {\n\t\tconst entry = entriesById.get(id);\n\t\tif (entry?.type === \"message\") newestConsumedInputSequence = Math.max(newestConsumedInputSequence, entry.seq);\n\t}\n\tconst overflowRecoveryUsed = operationRecords.some(\n\t\t(record) =>\n\t\t\trecord.type === \"step_attempt\" &&\n\t\t\trecord.step === \"compaction\" &&\n\t\t\trecord.compactionReason === \"overflow\" &&\n\t\t\trecord.seq > newestConsumedInputSequence,\n\t);\n\n\tconst newestOwnEntry = ownEntries.at(-1);\n\tconst newestOwn = deriveNewestOwn(newestOwnEntry);\n\tconst deferred =\n\t\tnewestOwnEntry?.type === \"message\" &&\n\t\tnewestOwnEntry.message.role === \"assistant\" &&\n\t\tnewestOwnEntry.message.stopReason === \"deferred\" &&\n\t\tnewestOwnEntry.message.deferred\n\t\t\t? clone(newestOwnEntry.message.deferred)\n\t\t\t: null;\n\tconst targets: { result?: boolean; summary?: boolean } = {};\n\tif (started.intent.kind === \"compaction\") {\n\t\ttargets.result = entriesById.has(started.intent.resultEntryId);\n\t} else if (started.intent.kind === \"navigation\" && started.intent.summaryEntryId) {\n\t\ttargets.summary = entriesById.has(started.intent.summaryEntryId);\n\t}\n\n\tconst deferredWriteIds = new Set(\n\t\toperationRecords.filter((record) => record.type === \"write_deferred\").map((record) => record.target.id),\n\t);\n\tlet terminalFailure: TerminalFailureState | null = null;\n\tif (\n\t\tnewestOwnEntry?.type === \"message\" &&\n\t\tnewestOwnEntry.message.role === \"assistant\" &&\n\t\tnewestOwnEntry.message.stopReason === \"error\" &&\n\t\t!deferredWriteIds.has(newestOwnEntry.id)\n\t) {\n\t\tconst producedByStep = operationRecords.some(\n\t\t\t(record) => record.type === \"step_attempt\" && record.resultEntryId === newestOwnEntry.id,\n\t\t);\n\t\tconst previousOwnEntry = ownEntries.at(-2);\n\t\tconst producedByDeferredFetch =\n\t\t\toperationRecords.some(\n\t\t\t\t(record) =>\n\t\t\t\t\trecord.type === \"usage\" && record.cause === \"deferred_fetch\" && record.entryId === newestOwnEntry.id,\n\t\t\t) ||\n\t\t\t(previousOwnEntry?.type === \"message\" &&\n\t\t\t\tpreviousOwnEntry.message.role === \"assistant\" &&\n\t\t\t\tpreviousOwnEntry.message.stopReason === \"deferred\");\n\t\tif (producedByStep || producedByDeferredFetch) {\n\t\t\tterminalFailure = {\n\t\t\t\tentryId: newestOwnEntry.id,\n\t\t\t\tsource: producedByStep ? \"step\" : \"deferred_fetch\",\n\t\t\t\tmessage: clone(newestOwnEntry.message),\n\t\t\t};\n\t\t}\n\t}\n\n\treturn {\n\t\tlaneState: {\n\t\t\tlane: input.lane,\n\t\t\tleafId: input.leafId,\n\t\t\toperation: {\n\t\t\t\tid: started.id,\n\t\t\t\tkind: started.intent.kind,\n\t\t\t\tintent: clone(started.intent),\n\t\t\t\taborting,\n\t\t\t\tstep,\n\t\t\t\ttoolBatch: deriveToolBatch(started.id, operationRecords, ownEntries, entriesById, deferredWriteIds),\n\t\t\t\tmissingInitialMessages,\n\t\t\t\tpendingSteer,\n\t\t\t\tpendingFollowUp,\n\t\t\t\tpendingWrites,\n\t\t\t\tdeferred,\n\t\t\t\toverflowRecoveryUsed,\n\t\t\t\tnewestOwn,\n\t\t\t\ttargets,\n\t\t\t},\n\t\t\tpendingNextRun,\n\t\t},\n\t\teffectiveConfiguration,\n\t\tterminalFailure,\n\t};\n}\n"]}