@kontourai/flow-agents 3.8.0 → 3.10.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 (109) hide show
  1. package/.github/workflows/ci.yml +8 -0
  2. package/CHANGELOG.md +24 -0
  3. package/README.md +1 -1
  4. package/build/src/builder-flow-runtime.js +16 -10
  5. package/build/src/cli/assignment-provider.js +4 -18
  6. package/build/src/cli/continuation-adapter.d.ts +26 -0
  7. package/build/src/cli/continuation-adapter.js +243 -0
  8. package/build/src/cli/validate-workflow-artifacts.js +21 -1
  9. package/build/src/cli/workflow-sidecar.js +15 -20
  10. package/build/src/cli/workflow.js +99 -6
  11. package/build/src/continuation-driver.d.ts +116 -0
  12. package/build/src/continuation-driver.js +527 -0
  13. package/build/src/index.d.ts +2 -0
  14. package/build/src/index.js +1 -0
  15. package/build/src/tools/build-universal-bundles.js +52 -25
  16. package/build/src/tools/validate-source-tree.js +5 -2
  17. package/context/scripts/hooks/lib/continuation-turn-authority.js +386 -0
  18. package/context/scripts/hooks/stop-goal-fit.js +244 -73
  19. package/context/scripts/hooks/workflow-steering.js +24 -15
  20. package/context/scripts/telemetry/lib/pricing.sh +18 -6
  21. package/context/scripts/telemetry/lib/transport.sh +139 -37
  22. package/context/scripts/telemetry/lib/usage.sh +282 -32
  23. package/context/scripts/telemetry/lib/usage_model_guard.sh +42 -0
  24. package/context/scripts/telemetry/telemetry.sh +76 -1
  25. package/docs/coordination-guide.md +21 -5
  26. package/docs/fixture-ownership.md +2 -1
  27. package/docs/public-workflow-cli.md +98 -0
  28. package/docs/spec/runtime-hook-surface.md +8 -4
  29. package/docs/workflow-usage-guide.md +7 -0
  30. package/evals/acceptance/prove-capture-teeth-declared.sh +13 -0
  31. package/evals/acceptance/prove-capture-teeth.sh +13 -0
  32. package/evals/ci/run-baseline.sh +2 -0
  33. package/evals/fixtures/codex-legacy-agents/5273878130bdafc8a024a650bb5b66c9b003f1f859b5dc6e5b588cbf4ab23228.md +25 -0
  34. package/evals/fixtures/telemetry/usage-transcript-adversarial.jsonl +2 -0
  35. package/evals/fixtures/telemetry/usage-transcript-malformed-sandwich.jsonl +3 -0
  36. package/evals/fixtures/telemetry/usage-transcript-negative-tokens.jsonl +1 -0
  37. package/evals/fixtures/telemetry/usage-transcript-oversized-prefix.jsonl +201 -0
  38. package/evals/fixtures/telemetry/usage-transcript-oversized-tokens.jsonl +1 -0
  39. package/evals/fixtures/telemetry/usage-transcript-prefixed-secret-lower.jsonl +1 -0
  40. package/evals/fixtures/telemetry/usage-transcript-prefixed-secret-upper.jsonl +1 -0
  41. package/evals/fixtures/telemetry/usage-transcript-secret-model-jwt.jsonl +1 -0
  42. package/evals/fixtures/telemetry/usage-transcript-secret-model.jsonl +1 -0
  43. package/evals/fixtures/telemetry/usage-transcript-torn-trailing.jsonl +2 -0
  44. package/evals/integration/test_actor_identity.sh +63 -0
  45. package/evals/integration/test_assignment_provider_local_file.sh +34 -0
  46. package/evals/integration/test_builder_entry_enforcement.sh +5 -1
  47. package/evals/integration/test_bundle_install.sh +106 -17
  48. package/evals/integration/test_bundle_lifecycle.sh +17 -3
  49. package/evals/integration/test_captured_fail_reconciliation.sh +11 -0
  50. package/evals/integration/test_ci_actor_identity.sh +2 -2
  51. package/evals/integration/test_command_log_concurrency.sh +11 -0
  52. package/evals/integration/test_command_log_fork_classification.sh +11 -0
  53. package/evals/integration/test_command_log_integrity.sh +11 -0
  54. package/evals/integration/test_current_json_per_actor.sh +416 -21
  55. package/evals/integration/test_enforcer_expects_driven.sh +11 -0
  56. package/evals/integration/test_ensure_session_ownership_guard.sh +39 -0
  57. package/evals/integration/test_evidence_capture_hook.sh +25 -6
  58. package/evals/integration/test_fixture_retirement_audit.sh +2 -2
  59. package/evals/integration/test_flowdef_union_floor_regression.sh +11 -0
  60. package/evals/integration/test_gate_bypass_chain.sh +11 -0
  61. package/evals/integration/test_gate_lockdown.sh +10 -0
  62. package/evals/integration/test_goal_fit_ghost_session.sh +11 -0
  63. package/evals/integration/test_goal_fit_hook.sh +257 -9
  64. package/evals/integration/test_goal_fit_rederive.sh +11 -0
  65. package/evals/integration/test_install_merge.sh +141 -3
  66. package/evals/integration/test_liveness_conflict_injection.sh +39 -3
  67. package/evals/integration/test_liveness_heartbeat.sh +46 -9
  68. package/evals/integration/test_phase_map_and_gate_claim.sh +10 -1
  69. package/evals/integration/test_public_workflow_cli.sh +6 -6
  70. package/evals/integration/test_session_resume_roundtrip.sh +30 -0
  71. package/evals/integration/test_stop_hook_release.sh +111 -11
  72. package/evals/integration/test_telemetry_tool_usage.sh +394 -0
  73. package/evals/integration/test_verify_hold.sh +64 -1
  74. package/evals/integration/test_workflow_sidecar_writer.sh +10 -1
  75. package/evals/integration/test_workflow_steering_hook.sh +20 -4
  76. package/evals/run.sh +2 -0
  77. package/evals/static/test_universal_bundles.sh +123 -0
  78. package/package.json +4 -4
  79. package/packaging/README.md +21 -0
  80. package/packaging/codex-legacy-agents-fingerprints.json +33 -0
  81. package/scripts/README.md +2 -1
  82. package/scripts/audit-codex-legacy-agents.js +43 -0
  83. package/scripts/classify-codex-legacy-agents.js +71 -0
  84. package/scripts/hooks/evidence-capture.js +18 -6
  85. package/scripts/hooks/lib/actor-identity.js +62 -29
  86. package/scripts/hooks/lib/continuation-turn-authority.js +386 -0
  87. package/scripts/hooks/lib/current-pointer.js +114 -12
  88. package/scripts/hooks/lib/liveness-heartbeat.js +15 -12
  89. package/scripts/hooks/stop-goal-fit.js +244 -73
  90. package/scripts/hooks/workflow-steering.js +24 -15
  91. package/scripts/install-codex-home.sh +8 -7
  92. package/scripts/telemetry/lib/pricing.sh +18 -6
  93. package/scripts/telemetry/lib/transport.sh +71 -0
  94. package/scripts/telemetry/lib/usage.sh +282 -32
  95. package/scripts/telemetry/lib/usage_model_guard.sh +42 -0
  96. package/scripts/telemetry/telemetry.sh +76 -1
  97. package/src/builder-flow-runtime.ts +9 -3
  98. package/src/cli/assignment-provider.ts +5 -26
  99. package/src/cli/builder-flow-runtime.test.mjs +215 -2
  100. package/src/cli/continuation-adapter.ts +265 -0
  101. package/src/cli/continuation-driver.test.mjs +1303 -0
  102. package/src/cli/public-api.test.mjs +1 -0
  103. package/src/cli/validate-workflow-artifacts.ts +19 -1
  104. package/src/cli/workflow-sidecar.ts +18 -23
  105. package/src/cli/workflow.ts +113 -6
  106. package/src/continuation-driver.ts +638 -0
  107. package/src/index.ts +24 -0
  108. package/src/tools/build-universal-bundles.ts +61 -25
  109. package/src/tools/validate-source-tree.ts +5 -2
@@ -280,14 +280,40 @@ function appendExportNote(body: string, note: string): string {
280
280
  function generatedAgentsSummary(agents: Agent[]): string {
281
281
  return agents.slice().sort((a, b) => a.name.localeCompare(b.name)).map((spec) => `- \`${spec.name}\` — ${String(spec.description ?? "").trim()}`).join("\n");
282
282
  }
283
- function exportRootAgentsMd(label: string, agents: Agent[], taskDir: string): string {
284
- return `# Universal Agent Bundle (${label})\n\nThis bundle was generated from the canonical source in this repo. Treat the repo root as the source of truth and regenerate the bundle instead of editing exported agent files by hand.\n\n## Shared Conventions\n\n- \`skills/\`, \`context/\`, \`powers/\`, \`prompts/\`, \`scripts/\`, and \`evals/\` were copied from the canonical source.\n- Cross-session task artifacts should live under \`${taskDir}\`.\n- Kiro-only hook wiring was stripped from exported non-Kiro agents to keep the package portable.\n\n## Exported Agents\n\n${generatedAgentsSummary(agents)}\n`;
283
+ type BundleCapability = {
284
+ label: string;
285
+ installHint: string;
286
+ instructionPath?: "AGENTS.md" | "CLAUDE.md";
287
+ taskDir: string;
288
+ };
289
+
290
+ const BUNDLE_CAPABILITIES = {
291
+ "base": { label: "Base", installHint: "bash install.sh /path/to/workspace", instructionPath: "AGENTS.md", taskDir: runtimeTaskDir },
292
+ "kiro": { label: "Kiro", installHint: "bash install.sh $HOME/.flow-agents", instructionPath: "AGENTS.md", taskDir: runtimeTaskDir },
293
+ "claude-code": { label: "Claude Code", installHint: "bash install.sh /path/to/workspace", instructionPath: "CLAUDE.md", taskDir: manifest.claude_code.task_dir },
294
+ "codex": { label: "Codex", installHint: "bash install.sh /path/to/workspace", taskDir: manifest.codex.task_dir },
295
+ "opencode": { label: "opencode", installHint: "bash install.sh /path/to/workspace", instructionPath: "AGENTS.md", taskDir: manifest.opencode.task_dir },
296
+ "pi": { label: "pi", installHint: "bash install.sh /path/to/workspace", instructionPath: "AGENTS.md", taskDir: manifest.pi.task_dir },
297
+ } satisfies Record<string, BundleCapability>;
298
+
299
+ function renderOperationalInstructions(capability: BundleCapability): string {
300
+ return `# Flow Agents\n\nUse the installed Flow Agents skills and runtime integrations for delivery work. Keep cross-session task artifacts under \`${capability.taskDir}\`.\n`;
285
301
  }
286
302
  const CODEX_LIVE_HOOKS_README = `\n## Running with hooks active (live)\n\n\`install.sh\` lays the bundle into a workspace, but a live Codex session needs a \`CODEX_HOME\` that has both the bundle's hooks/scripts AND your real credentials. Use the dedicated installer, which defaults to \`CODEX_HOME\` when set and otherwise \`~/.codex\`, flattens the config to the home root, and copies your auth from \`~/.codex\` when installing elsewhere:\n\n\`\`\`bash\nbash scripts/install-codex-home.sh\ncodex exec --dangerously-bypass-hook-trust -C /path/to/project \"<prompt>\"\n\`\`\`\n\nPass a positional destination to install into an explicit alternate Codex home.\n\nThe goal-fit Stop hook then enforces by default (\`FLOW_AGENTS_GOAL_FIT_MODE=block\`); set it to \`warn\` or \`off\` to override.\n`;
287
303
  function exportTargetReadme(label: string, installHint: string, extra = ""): string {
288
304
  return `# ${label} Bundle\n\nGenerated from the canonical source in this repository.\n\n## Install\n\n\`\`\`bash\n${installHint}\n\`\`\`\n\nThe install ships the full standalone base (skills, agents, powers) plus the\nFlow Kits. Kit depth is activated through the Kit Catalog, not at install time.\n\n## Contents\n\n- Harness-specific agents\n- Shared skills\n- Shared context, powers, prompts, scripts, and evals\n${extra}`;
289
305
  }
290
306
 
307
+ function renderBundleReadme(capability: BundleCapability, agents: Agent[], extra = ""): string {
308
+ const inventory = `\n## Exported agents\n\nThe following agent definitions were generated from the canonical agent specifications in this repository. This inventory is documentation, not agent instructions.\n\n${generatedAgentsSummary(agents)}\n`;
309
+ return exportTargetReadme(capability.label, capability.installHint, `${inventory}${extra}`);
310
+ }
311
+
312
+ function writeBundlePolicy(bundle: string, capability: BundleCapability, agents: Agent[], readmeExtra = ""): void {
313
+ writeText(path.join(bundle, "README.md"), renderBundleReadme(capability, agents, readmeExtra));
314
+ if (capability.instructionPath) writeText(path.join(bundle, capability.instructionPath), renderOperationalInstructions(capability));
315
+ }
316
+
291
317
  function mapClaudeTools(allowedTools: unknown): string[] {
292
318
  const ordered: string[] = [];
293
319
  for (const tool of Array.isArray(allowedTools) ? allowedTools : []) {
@@ -420,48 +446,59 @@ function copySharedContent(targetRoot: string, targetName: string, token: string
420
446
  }
421
447
  }
422
448
  for (const dir of manifest.optional_copy_dirs ?? []) copyTree(path.join(root, dir), path.join(targetRoot, dir), targetName, token);
423
- writeText(path.join(targetRoot, "build/package.json"), `${JSON.stringify({ type: "module" }, null, 2)}\n`);
449
+ writeText(path.join(targetRoot, "build/package.json"), `${JSON.stringify({
450
+ name: "@kontourai/flow-agents",
451
+ version: pkgVersion,
452
+ type: "module",
453
+ }, null, 2)}\n`);
424
454
  const commonBuilt = path.join(root, "build/src/tools/common.js");
425
455
  if (fs.existsSync(commonBuilt)) writeText(path.join(targetRoot, "scripts/common.mjs"), readText(commonBuilt));
426
456
  copyTree(path.join(root, "build/src"), path.join(targetRoot, "build/src"), targetName, token);
427
457
  }
428
- function installScript(label: string, defaultDestDisplay: string, token?: string, destFallbackShell?: string, mergeConfig?: { configRelPath: string; managedConfigRelPath: string; runtime: string; version: string }, stampConfig?: { runtime: string; version: string }): string {
429
- const replaceBlock = token ? `\nexport DEST\nfind "$DEST" -type f \\( -name '*.json' -o -name '*.md' -o -name '*.sh' -o -name '*.js' -o -name '*.ts' -o -name '*.yaml' -o -name '*.yml' \\) -print0 | xargs -0 perl -0pi -e 's#${token.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}#$ENV{DEST}#g'` : "";
458
+ function installScript(label: string, capability: BundleCapability, defaultDestDisplay: string, token?: string, destFallbackShell?: string, mergeConfig?: { configRelPath: string; managedConfigRelPath: string; runtime: string; version: string }, stampConfig?: { runtime: string; version: string }): string {
459
+ const replaceBlock = token ? `\nexport DEST\nfind "$DEST" \\( -path "$DEST/AGENTS.md" -o -path "$DEST/CLAUDE.md" \\) -prune -o -type f \\( -name '*.json' -o -name '*.md' -o -name '*.sh' -o -name '*.js' -o -name '*.ts' -o -name '*.yaml' -o -name '*.yml' \\) -print0 | xargs -0 perl -0pi -e 's#${token.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}#$ENV{DEST}#g'` : "";
430
460
  const destFallback = destFallbackShell ? `\nif [[ -z "$DEST" ]]; then\n DEST="${destFallbackShell}"\nfi` : "";
431
461
  const destRequired = !destFallbackShell;
432
- const requiredCheck = destRequired ? `if [[ -z "$DEST" ]]; then\n usage\n exit 2\nfi\n` : "";
462
+ const requiredCheck = `${destRequired ? `if [[ -z "$DEST" ]]; then\n usage\n exit 2\nfi\n` : ""}while [[ "$DEST" != "/" && "$DEST" == */ ]]; do\n DEST="\${DEST%/}"\ndone\n`;
433
463
  const usageDest = destRequired ? "/path/to/workspace" : defaultDestDisplay;
434
464
  const mergeBlock = mergeConfig
435
465
  ? `\nif command -v node >/dev/null 2>&1; then\n node "$DEST/scripts/install-merge.js" --config "$DEST/${mergeConfig.configRelPath}" --managed-hooks "$SRC/${mergeConfig.managedConfigRelPath}" --version "${mergeConfig.version}" --install-record "$DEST/${durableInstallRecordRel}" --runtime "${mergeConfig.runtime}" || true\nfi`
436
466
  : stampConfig
437
467
  ? `\nif command -v node >/dev/null 2>&1; then\n node "$DEST/scripts/install-merge.js" --stamp-only --version "${stampConfig.version}" --install-record "$DEST/${durableInstallRecordRel}" --runtime "${stampConfig.runtime}" || true\nfi`
438
468
  : "";
439
- return `#!/usr/bin/env bash\nset -euo pipefail\n\nusage() {\n cat >&2 <<'EOF'\nusage: bash install.sh ${usageDest} [options]\n\nOptions:\n --telemetry-sink NAME local-files, local-kontour-console,\n kontour-hosted-console, user-hosted-console,\n or legacy aliases. May be repeated.\n --console-url URL Persist Console telemetry base URL.\n --console-endpoint URL Persist full Console telemetry records endpoint URL.\n --console-token-file PATH\n Read Console telemetry bearer token from a file.\n --console-tenant ID Persist Console tenant identifier.\nEOF\n}\n\nDEST=""\nDEST_SET=0\nCONSOLE_CONFIG_ARGS=()\nwhile [[ $# -gt 0 ]]; do\n case "$1" in\n --telemetry-sink|--telemetry-sinks|--console-url|--console-endpoint|--console-endpoint-url|--console-token-file|--console-tenant|--console-tenant-id)\n [[ $# -ge 2 ]] || { echo "install.sh: $1 requires a value" >&2; exit 2; }\n CONSOLE_CONFIG_ARGS+=("$1" "$2")\n shift 2\n ;;\n --help|-h)\n usage\n exit 0\n ;;\n -*)\n echo "install.sh: unknown option: $1" >&2\n usage\n exit 2\n ;;\n *)\n if [[ "$DEST_SET" -eq 1 ]]; then\n echo "install.sh: unexpected argument: $1" >&2\n usage\n exit 2\n fi\n DEST="$1"\n DEST_SET=1\n shift\n ;;\n esac\ndone${destFallback}\n${requiredCheck}SRC="$(cd "$(dirname "\${BASH_SOURCE[0]}")" && pwd)"\n\nmkdir -p "$DEST"\nrsync -a ${token ? "--delete " : ""}${mergeConfig ? `--exclude="${mergeConfig.configRelPath}" ` : ""}"$SRC"/ "$DEST"/${replaceBlock}${mergeBlock}\nif [[ \${#CONSOLE_CONFIG_ARGS[@]} -gt 0 || -n "\${FLOW_AGENTS_TELEMETRY_SINK:-}" || -n "\${FLOW_AGENTS_TELEMETRY_SINKS:-}" || -n "\${FLOW_AGENTS_CONSOLE_URL:-}" || -n "\${CONSOLE_TELEMETRY_URL:-}" || -n "\${CONSOLE_URL:-}" || -n "\${FLOW_AGENTS_CONSOLE_TOKEN_FILE:-}" || -n "\${CONSOLE_TELEMETRY_TOKEN_FILE:-}" ]]; then\n bash "$DEST/scripts/telemetry/install-console-config.sh" "$DEST/scripts/telemetry/telemetry.conf" "\${CONSOLE_CONFIG_ARGS[@]}"\nfi\necho "Installed ${label} bundle ${token ? "to" : "into"} $DEST"\n`;
469
+ // Excluding both discovery filenames protects any existing filesystem
470
+ // object, including during Kiro's --delete sync. Required guidance is then
471
+ // published with a no-overwrite hard link from a destination-local temp file.
472
+ const instructionBlock = capability.instructionPath
473
+ ? `\nif [[ ! -e "$DEST/${capability.instructionPath}" && ! -L "$DEST/${capability.instructionPath}" ]]; then\n instruction_tmp="$(mktemp "$DEST/.flow-agents-instructions.XXXXXX")"\n cp "$SRC/${capability.instructionPath}" "$instruction_tmp"\n if ! ln "$instruction_tmp" "$DEST/${capability.instructionPath}" 2>/dev/null; then\n if [[ ! -e "$DEST/${capability.instructionPath}" && ! -L "$DEST/${capability.instructionPath}" ]]; then\n rm -f "$instruction_tmp"\n echo "install.sh: could not safely install ${capability.instructionPath}" >&2\n exit 1\n fi\n fi\n rm -f "$instruction_tmp"\nfi`
474
+ : "";
475
+ return `#!/usr/bin/env bash\nset -euo pipefail\n\nusage() {\n cat >&2 <<'EOF'\nusage: bash install.sh ${usageDest} [options]\n\nOptions:\n --telemetry-sink NAME local-files, local-kontour-console,\n kontour-hosted-console, user-hosted-console,\n or legacy aliases. May be repeated.\n --console-url URL Persist Console telemetry base URL.\n --console-endpoint URL Persist full Console telemetry records endpoint URL.\n --console-token-file PATH\n Read Console telemetry bearer token from a file.\n --console-tenant ID Persist Console tenant identifier.\nEOF\n}\n\nDEST=""\nDEST_SET=0\nCONSOLE_CONFIG_ARGS=()\nwhile [[ $# -gt 0 ]]; do\n case "$1" in\n --telemetry-sink|--telemetry-sinks|--console-url|--console-endpoint|--console-endpoint-url|--console-token-file|--console-tenant|--console-tenant-id)\n [[ $# -ge 2 ]] || { echo "install.sh: $1 requires a value" >&2; exit 2; }\n CONSOLE_CONFIG_ARGS+=("$1" "$2")\n shift 2\n ;;\n --help|-h)\n usage\n exit 0\n ;;\n -*)\n echo "install.sh: unknown option: $1" >&2\n usage\n exit 2\n ;;\n *)\n if [[ "$DEST_SET" -eq 1 ]]; then\n echo "install.sh: unexpected argument: $1" >&2\n usage\n exit 2\n fi\n DEST="$1"\n DEST_SET=1\n shift\n ;;\n esac\ndone${destFallback}\n${requiredCheck}SRC="$(cd "$(dirname "\${BASH_SOURCE[0]}")" && pwd)"\n\nmkdir -p "$DEST"\nrsync -a ${token ? "--delete " : ""}--exclude='/AGENTS.md' --exclude='/CLAUDE.md' ${mergeConfig ? `--exclude="${mergeConfig.configRelPath}" ` : ""}"$SRC"/ "$DEST"/${instructionBlock}${replaceBlock}${mergeBlock}\nif [[ \${#CONSOLE_CONFIG_ARGS[@]} -gt 0 || -n "\${FLOW_AGENTS_TELEMETRY_SINK:-}" || -n "\${FLOW_AGENTS_TELEMETRY_SINKS:-}" || -n "\${FLOW_AGENTS_CONSOLE_URL:-}" || -n "\${CONSOLE_TELEMETRY_URL:-}" || -n "\${CONSOLE_URL:-}" || -n "\${FLOW_AGENTS_CONSOLE_TOKEN_FILE:-}" || -n "\${CONSOLE_TELEMETRY_TOKEN_FILE:-}" ]]; then\n bash "$DEST/scripts/telemetry/install-console-config.sh" "$DEST/scripts/telemetry/telemetry.conf" "\${CONSOLE_CONFIG_ARGS[@]}"\nfi\necho "Installed ${label} bundle ${token ? "to" : "into"} $DEST"\n`;
440
476
  }
441
477
 
442
478
  function buildBase(agents: Agent[]): void {
479
+ const capability = BUNDLE_CAPABILITIES["base"];
443
480
  const bundle = path.join(dist, "base");
444
481
  resetDir(bundle);
445
482
  copySharedContent(bundle, "base", "<bundle-root>");
446
483
  writeText(path.join(bundle, runtimeTaskDir, ".gitkeep"), "");
447
- writeText(path.join(bundle, "AGENTS.md"), exportRootAgentsMd("Base", agents, runtimeTaskDir));
448
- writeText(path.join(bundle, "README.md"), exportTargetReadme("Base", "bash install.sh /path/to/workspace"));
449
- writeText(path.join(bundle, "install.sh"), installScript("Base", "/path/to/workspace", undefined, undefined, undefined, { runtime: "base", version: pkgVersion }));
484
+ writeBundlePolicy(bundle, capability, agents);
485
+ writeText(path.join(bundle, "install.sh"), installScript("Base", capability, "/path/to/workspace", undefined, undefined, undefined, { runtime: "base", version: pkgVersion }));
450
486
  fs.chmodSync(path.join(bundle, "install.sh"), 0o755);
451
487
  }
452
488
 
453
489
  function buildKiro(agents: Agent[]): void {
490
+ const capability = BUNDLE_CAPABILITIES["kiro"];
454
491
  const bundle = path.join(dist, "kiro");
455
492
  const token = manifest.kiro.path_token;
456
493
  resetDir(bundle);
457
494
  copySharedContent(bundle, "kiro", token);
458
495
  for (const spec of agents) writeText(path.join(bundle, "agents", `${spec.name}.json`), sanitizeText(`${JSON.stringify(sanitizeAgentJson(spec), null, 2)}\n`, "kiro", token));
459
- writeText(path.join(bundle, "AGENTS.md"), exportRootAgentsMd("Kiro", agents, runtimeTaskDir));
460
- writeText(path.join(bundle, "README.md"), exportTargetReadme("Kiro", "bash install.sh $HOME/.flow-agents"));
461
- writeText(path.join(bundle, "install.sh"), installScript("Kiro", "$HOME/.flow-agents", token, '${FLOW_AGENTS_DEST:-$HOME/.flow-agents}', undefined, { runtime: "kiro", version: pkgVersion }));
496
+ writeBundlePolicy(bundle, capability, agents);
497
+ writeText(path.join(bundle, "install.sh"), installScript("Kiro", capability, "$HOME/.flow-agents", token, '${FLOW_AGENTS_DEST:-$HOME/.flow-agents}', undefined, { runtime: "kiro", version: pkgVersion }));
462
498
  fs.chmodSync(path.join(bundle, "install.sh"), 0o755);
463
499
  }
464
500
  function buildClaudeCode(agents: Agent[]): void {
501
+ const capability = BUNDLE_CAPABILITIES["claude-code"];
465
502
  const bundle = path.join(dist, "claude-code");
466
503
  resetDir(bundle);
467
504
  copySharedContent(bundle, "claude-code", "<bundle-root>");
@@ -471,12 +508,12 @@ function buildClaudeCode(agents: Agent[]): void {
471
508
  writeText(path.join(bundle, ".claude/skills", name, "SKILL.md"), sanitizeText(readText(src), "claude-code", "<bundle-root>"));
472
509
  }
473
510
  writeText(path.join(bundle, ".claude/settings.json"), exportClaudeSettings());
474
- writeText(path.join(bundle, "AGENTS.md"), exportRootAgentsMd("Claude Code", agents, manifest.claude_code.task_dir));
475
- writeText(path.join(bundle, "README.md"), exportTargetReadme("Claude Code", "bash install.sh /path/to/workspace"));
476
- writeText(path.join(bundle, "install.sh"), installScript("Claude Code", "/path/to/workspace", undefined, undefined, { configRelPath: ".claude/settings.json", managedConfigRelPath: ".claude/settings.json", runtime: "claude-code", version: pkgVersion }));
511
+ writeBundlePolicy(bundle, capability, agents);
512
+ writeText(path.join(bundle, "install.sh"), installScript("Claude Code", capability, "/path/to/workspace", undefined, undefined, { configRelPath: ".claude/settings.json", managedConfigRelPath: ".claude/settings.json", runtime: "claude-code", version: pkgVersion }));
477
513
  fs.chmodSync(path.join(bundle, "install.sh"), 0o755);
478
514
  }
479
515
  function buildCodex(agents: Agent[]): void {
516
+ const capability = BUNDLE_CAPABILITIES["codex"];
480
517
  const bundle = path.join(dist, "codex");
481
518
  const excluded = new Set(manifest.codex.excluded_agents ?? []);
482
519
  const targetAgents = agents.filter((spec) => !excluded.has(spec.name));
@@ -491,9 +528,8 @@ function buildCodex(agents: Agent[]): void {
491
528
  const skillsRoot = path.join(bundle, ".agents/skills");
492
529
  for (const { name, src } of collectAllSkills()) exportSkillPackage(src, path.join(skillsRoot, name), "codex");
493
530
  validateExportedSkillPackages(skillsRoot);
494
- writeText(path.join(bundle, "AGENTS.md"), exportRootAgentsMd("Codex", targetAgents, manifest.codex.task_dir));
495
- writeText(path.join(bundle, "README.md"), exportTargetReadme("Codex", "bash install.sh /path/to/workspace", CODEX_LIVE_HOOKS_README));
496
- writeText(path.join(bundle, "install.sh"), installScript("Codex", "/path/to/workspace", undefined, undefined, { configRelPath: ".codex/hooks.json", managedConfigRelPath: ".codex/hooks.json", runtime: "codex", version: pkgVersion }));
531
+ writeBundlePolicy(bundle, capability, targetAgents, CODEX_LIVE_HOOKS_README);
532
+ writeText(path.join(bundle, "install.sh"), installScript("Codex", capability, "/path/to/workspace", undefined, undefined, { configRelPath: ".codex/hooks.json", managedConfigRelPath: ".codex/hooks.json", runtime: "codex", version: pkgVersion }));
497
533
  fs.chmodSync(path.join(bundle, "install.sh"), 0o755);
498
534
  }
499
535
  function exportOpencodeAgent(spec: Agent): string {
@@ -661,6 +697,7 @@ function exportOpencodeConfig(): string {
661
697
  }, null, 2)}\n`;
662
698
  }
663
699
  function buildOpencode(agents: Agent[]): void {
700
+ const capability = BUNDLE_CAPABILITIES["opencode"];
664
701
  const bundle = path.join(dist, "opencode");
665
702
  resetDir(bundle);
666
703
  copySharedContent(bundle, "opencode", "<bundle-root>");
@@ -673,9 +710,8 @@ function buildOpencode(agents: Agent[]): void {
673
710
  }
674
711
  writeText(path.join(bundle, ".opencode/plugins/flow-agents.js"), exportOpencodePlugin());
675
712
  writeText(path.join(bundle, "opencode.json"), exportOpencodeConfig());
676
- writeText(path.join(bundle, "AGENTS.md"), exportRootAgentsMd("opencode", agents, manifest.opencode.task_dir));
677
- writeText(path.join(bundle, "README.md"), exportTargetReadme("opencode", "bash install.sh /path/to/workspace"));
678
- writeText(path.join(bundle, "install.sh"), installScript("opencode", "/path/to/workspace", undefined, undefined, { configRelPath: "opencode.json", managedConfigRelPath: "opencode.json", runtime: "opencode", version: pkgVersion }));
713
+ writeBundlePolicy(bundle, capability, agents);
714
+ writeText(path.join(bundle, "install.sh"), installScript("opencode", capability, "/path/to/workspace", undefined, undefined, { configRelPath: "opencode.json", managedConfigRelPath: "opencode.json", runtime: "opencode", version: pkgVersion }));
679
715
  fs.chmodSync(path.join(bundle, "install.sh"), 0o755);
680
716
  }
681
717
  function exportPiExtension(): string {
@@ -774,6 +810,7 @@ export default function (pi: ExtensionAPI) {
774
810
  `;
775
811
  }
776
812
  function buildPi(agents: Agent[]): void {
813
+ const capability = BUNDLE_CAPABILITIES["pi"];
777
814
  const bundle = path.join(dist, "pi");
778
815
  resetDir(bundle);
779
816
  copySharedContent(bundle, "pi", "<bundle-root>");
@@ -784,9 +821,8 @@ function buildPi(agents: Agent[]): void {
784
821
  writeText(path.join(bundle, ".pi/skills", name, "SKILL.md"), sanitizeText(readText(src), "pi", "<bundle-root>"));
785
822
  }
786
823
  writeText(path.join(bundle, ".pi/extensions/flow-agents.ts"), exportPiExtension());
787
- writeText(path.join(bundle, "AGENTS.md"), exportRootAgentsMd("pi", agents, manifest.pi.task_dir));
788
- writeText(path.join(bundle, "README.md"), exportTargetReadme("pi", "bash install.sh /path/to/workspace"));
789
- writeText(path.join(bundle, "install.sh"), installScript("pi", "/path/to/workspace", undefined, undefined, undefined, { runtime: "pi", version: pkgVersion }));
824
+ writeBundlePolicy(bundle, capability, agents);
825
+ writeText(path.join(bundle, "install.sh"), installScript("pi", capability, "/path/to/workspace", undefined, undefined, undefined, { runtime: "pi", version: pkgVersion }));
790
826
  fs.chmodSync(path.join(bundle, "install.sh"), 0o755);
791
827
  }
792
828
  function buildCatalog(agents: Agent[]): Record<string, unknown> {
@@ -32,6 +32,7 @@ const mirroredFiles = new Map<string, { mirror: string; allowedDifferences: Arra
32
32
  // fails `npm run validate:source` instead of silently accumulating.
33
33
  ["scripts/hooks/config-protection.js", { mirror: "context/scripts/hooks/config-protection.js", allowedDifferences: [] }],
34
34
  ["scripts/hooks/lib/config-protection-remedies.js", { mirror: "context/scripts/hooks/lib/config-protection-remedies.js", allowedDifferences: [] }],
35
+ ["scripts/hooks/lib/continuation-turn-authority.js", { mirror: "context/scripts/hooks/lib/continuation-turn-authority.js", allowedDifferences: [] }],
35
36
  ["scripts/hooks/lib/kit-catalog.js", { mirror: "context/scripts/hooks/lib/kit-catalog.js", allowedDifferences: [] }],
36
37
  ["scripts/hooks/lib/runnable-command.js", { mirror: "context/scripts/hooks/lib/runnable-command.js", allowedDifferences: [] }],
37
38
  ["scripts/hooks/stop-goal-fit.js", { mirror: "context/scripts/hooks/stop-goal-fit.js", allowedDifferences: [] }],
@@ -83,10 +84,11 @@ const hookFilePolicies = new Map<string, { category: string; requiredNeedles: st
83
84
  ["scripts/hooks/utterance-check.js", { category: "policy hook", requiredNeedles: ["Utterance Check Hook", "FLOW_AGENTS_UTTERANCE_CHECK_ENABLED"] }],
84
85
  ["scripts/hooks/workflow-steering.js", { category: "policy hook", requiredNeedles: ["Workflow Steering Hook"] }],
85
86
  ["scripts/hooks/desktop-notify.sh", { category: "local notification helper", requiredNeedles: ["desktop-notify.sh", "osascript"] }],
86
- ["scripts/hooks/lib/actor-identity.js", { category: "shared hook library", requiredNeedles: ["resolveActor", "FLOW_AGENTS_ACTOR"] }],
87
+ ["scripts/hooks/lib/actor-identity.js", { category: "shared hook library", requiredNeedles: ["resolveActor", "FLOW_AGENTS_ACTOR", "CODEX_THREAD_ID", "flow-agents:codex-thread:v1:"] }],
87
88
  ["scripts/hooks/lib/audit-transport.sh", { category: "shared hook library", requiredNeedles: ["audit_emit"] }],
88
89
  ["scripts/hooks/lib/codex-exit-code.js", { category: "shared hook library", requiredNeedles: ["extractExitCodeFromBanner", "readExitCodeFromRollout"] }],
89
90
  ["scripts/hooks/lib/config-protection-remedies.js", { category: "shared hook library", requiredNeedles: ["SANCTIONED_REMEDIES", "REMEDY_COMMAND_CANDIDATES"] }],
91
+ ["scripts/hooks/lib/continuation-turn-authority.js", { category: "shared hook library", requiredNeedles: ["issueActiveTurnAuthority", "validateActiveTurnAuthority", "validateSignedActiveTurnAssignmentAuthority"] }],
90
92
  ["scripts/hooks/lib/current-pointer.js", { category: "shared hook library", requiredNeedles: ["readCurrentPointer", "perActorCurrentFile"] }],
91
93
  ["scripts/hooks/lib/hook-flags.js", { category: "shared hook library", requiredNeedles: ["isHookEnabled"] }],
92
94
  ["scripts/hooks/lib/kit-catalog.js", { category: "shared hook library", requiredNeedles: ["readKitManifests", "workflowTriggersFor"] }],
@@ -105,6 +107,7 @@ const fixtureOwnerPolicies = new Map<string, { owners: string[]; classification:
105
107
  ["evals/fixtures/backlog-provider-settings", { owners: ["evals/integration/test_effective_backlog_settings.sh"], classification: "settings precedence fixtures" }],
106
108
  ["evals/fixtures/builder-kit-workflow-state", { owners: ["evals/static/test_workflow_skills.sh"], classification: "Builder Kit workflow-state fixtures" }],
107
109
  ["evals/fixtures/console-learning-projection", { owners: ["evals/integration/test_console_learning_projection.sh"], classification: "console learning projection fixtures" }],
110
+ ["evals/fixtures/codex-legacy-agents", { owners: ["evals/integration/test_install_merge.sh"], classification: "exact historical Flow Agents-generated Codex AGENTS.md migration fixtures (#563)" }],
108
111
  ["evals/fixtures/economics", { owners: ["evals/integration/test_economics_record.sh"], classification: "per-run kit-economics record fixtures (#349): a transcript with .message.usage blocks, state.json/acceptance.json/critique.json join sources, a session.usage event, and the golden expected kontour.console.economics record" }],
109
112
  ["evals/fixtures/flow-kit-repository", { owners: ["evals/integration/test_flow_kit_repository.sh", "evals/integration/test_local_flow_kit_install.sh", "evals/integration/test_runtime_adapter_activation.sh", "evals/integration/test_activate_npx_context.sh", "evals/integration/test_flow_kit_install_git.sh", "evals/static/test_workflow_skills.sh"], classification: "Flow Kit repository contract fixtures" }],
110
113
  ["evals/fixtures/kit-conformance-levels", { owners: ["evals/integration/test_kit_conformance_levels.sh"], classification: "K-level conformance and consumer-target derivation fixtures" }],
@@ -113,7 +116,7 @@ const fixtureOwnerPolicies = new Map<string, { owners: string[]; classification:
113
116
  ["evals/fixtures/pull-work-provider", { owners: ["evals/integration/test_pull_work_provider.sh"], classification: "work item provider normalization fixtures" }],
114
117
  ["evals/fixtures/pull-work-wip-shepherding", { owners: ["evals/static/test_workflow_skills.sh"], classification: "WIP shepherding state fixtures" }],
115
118
  ["evals/fixtures/surface-trust", { owners: ["evals/integration/test_workflow_sidecar_writer.sh"], classification: "Surface trust evidence fixtures" }],
116
- ["evals/fixtures/telemetry", { owners: ["evals/integration/test_usage_cost.sh", "evals/integration/test_telemetry_usage_pipeline.sh"], classification: "hermetic Stop-hook usage transcript fixture (#defect-2026-07 telemetry-usage-cost-extraction): a multi-model JSONL transcript with .message.model + .message.usage token blocks proving real token/cost/model extraction end-to-end" }],
119
+ ["evals/fixtures/telemetry", { owners: ["evals/integration/test_usage_cost.sh", "evals/integration/test_telemetry_usage_pipeline.sh", "evals/integration/test_telemetry_tool_usage.sh"], classification: "hermetic Stop-hook usage transcript fixture (#defect-2026-07 telemetry-usage-cost-extraction): a multi-model JSONL transcript with .message.model + .message.usage token blocks proving real token/cost/model extraction end-to-end; also reused by the #568 slice 1 per-tool-call usage-enrichment eval (test_telemetry_tool_usage.sh), which adds usage-transcript-oversized-prefix.jsonl to prove the bounded tail-read" }],
117
120
  ["evals/fixtures/reconcile-preflight", { owners: ["evals/integration/test_reconcile_preflight.sh"], classification: "#356 reconcile-preflight shape fixtures not already covered by trust-reconcile-exploits (un-superseded disputed critique, standalone disputed session-local claim)" }],
118
121
  ["evals/fixtures/trust-reconcile-exploits", { owners: ["evals/integration/test_trust_reconcile_negatives.sh", "evals/integration/test_reconcile_preflight.sh"], classification: "WS8 trust-reconcile anti-gaming exploit fixtures (frozen negative regressions); also reused by the #356 local reconcile-preflight eval (same shapes, no forked copies)" }],
119
122
  ["evals/fixtures/trust-reconcile-mixed-bundle", { owners: ["evals/integration/test_trust_reconcile_mixed_bundle.sh", "evals/integration/test_reconcile_preflight.sh"], classification: "WS8 trust-reconcile mixed-evidence end-to-end proof fixture; also reused by the #356 reconcile-preflight eval as its CLEAN-BUNDLE (AC4) case" }],