@kontourai/flow-agents 3.7.0 → 3.9.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 (37) hide show
  1. package/CHANGELOG.md +19 -0
  2. package/build/src/builder-flow-runtime.js +118 -17
  3. package/build/src/cli/assignment-provider.js +13 -1
  4. package/build/src/cli/continuation-adapter.d.ts +24 -0
  5. package/build/src/cli/continuation-adapter.js +225 -0
  6. package/build/src/cli/workflow-sidecar.js +29 -11
  7. package/build/src/cli/workflow.js +67 -11
  8. package/build/src/continuation-driver.d.ts +92 -0
  9. package/build/src/continuation-driver.js +444 -0
  10. package/build/src/index.d.ts +2 -0
  11. package/build/src/index.js +1 -0
  12. package/build/src/tools/build-universal-bundles.js +53 -3
  13. package/docs/getting-started.md +9 -1
  14. package/docs/public-workflow-cli.md +54 -0
  15. package/docs/spec/builder-flow-runtime.md +36 -2
  16. package/docs/workflow-usage-guide.md +7 -0
  17. package/evals/integration/test_builder_step_producers.sh +37 -4
  18. package/evals/integration/test_bundle_install.sh +108 -4
  19. package/evals/integration/test_bundle_lifecycle.sh +4 -6
  20. package/evals/integration/test_install_merge.sh +18 -8
  21. package/evals/integration/test_public_workflow_cli.sh +11 -5
  22. package/evals/static/test_universal_bundles.sh +44 -1
  23. package/package.json +4 -4
  24. package/packaging/README.md +1 -1
  25. package/scripts/README.md +1 -1
  26. package/scripts/install-codex-home.sh +63 -23
  27. package/scripts/install-owned-files.js +18 -2
  28. package/src/builder-flow-runtime.ts +108 -10
  29. package/src/cli/assignment-provider.ts +13 -1
  30. package/src/cli/builder-flow-runtime.test.mjs +378 -20
  31. package/src/cli/continuation-adapter.ts +239 -0
  32. package/src/cli/continuation-driver.test.mjs +564 -0
  33. package/src/cli/workflow-sidecar.ts +27 -11
  34. package/src/cli/workflow.ts +68 -11
  35. package/src/continuation-driver.ts +532 -0
  36. package/src/index.ts +20 -0
  37. package/src/tools/build-universal-bundles.ts +51 -3
@@ -663,6 +663,13 @@ the read-only `workflow status` projection without attaching evidence or advanci
663
663
  [`docs/spec/builder-flow-runtime.md`](spec/builder-flow-runtime.md) for the ownership,
664
664
  trust-binding, route-back, and artifact-root contract.
665
665
 
666
+ For unattended runtime re-entry, the active assignment actor may use `workflow drive` with an
667
+ explicit runtime adapter argv file. The driver repeats the same public projection/evidence cycle,
668
+ persists its mission turn budget, and parks on adapter-declared PID or deadline barriers instead of
669
+ spending turns while external work is incomplete. Adapter completion never advances a gate by
670
+ self-report; only trust evidence accepted by canonical Flow can change the current step. See
671
+ [`docs/public-workflow-cli.md`](public-workflow-cli.md#bounded-continuation-driver) for the protocol.
672
+
666
673
  If the same Builder slice is interrupted, inspect its canonical status. Resume
667
674
  only when the public status reports a paused run:
668
675
 
@@ -456,6 +456,9 @@ record_public_expectation() {
456
456
  *) _fail "public producer fixture has no durable artifact for $expectation"; return ;;
457
457
  esac
458
458
  local -a args=(evidence --session-dir "$PUBLIC_SESSION" --expectation "$expectation" --status "$status" --summary "public producer fixture records a reviewable durable artifact" --evidence-ref-json "{\"kind\":\"artifact\",\"file\":\"$artifact\",\"summary\":\"Fixture artifact for $expectation.\"}")
459
+ if [ "$expectation" = "tests-evidence" ] && [ "$status" = "fail" ]; then
460
+ args+=(--route-reason implementation_defect)
461
+ fi
459
462
  if [ "$expectation" = "tests-evidence" ] && [ "$status" = "pass" ]; then
460
463
  local test_command criterion_one criterion_two command_ref
461
464
  test_command="bash checks/check-public-session.sh .kontourai/flow-agents/$slug/state.json"
@@ -614,16 +617,46 @@ ROUTE_CRITIQUE_OUTPUT="$(public_review critique --session-dir "$PUBLIC_SESSION"
614
617
  --artifact-ref "$PUBLIC_SESSION/$(basename "$PUBLIC_SESSION")--deliver.md" \
615
618
  --lane-json "{\"id\":\"code-review\",\"status\":\"pass\",\"summary\":\"Public fixture code review completed.\",\"evidence_refs\":[{\"kind\":\"artifact\",\"file\":\"$PUBLIC_SESSION/$(basename "$PUBLIC_SESSION")--deliver.md\",\"summary\":\"Reviewed public fixture delivery artifact.\"}]}" 2>&1)" \
616
619
  || _fail "public authenticated critique failed before failed tests-evidence: $ROUTE_CRITIQUE_OUTPUT"
620
+ # Seed current verified acceptance prerequisites through the private producer
621
+ # without synchronizing Flow. The subsequent public failed tests claim replaces
622
+ # the provisional passing tests check, preserves these criteria + the clean
623
+ # critique, and is the only attachment/evaluation for this gate visit.
624
+ ROUTE_TEST_COMMAND="bash checks/check-public-session.sh .kontourai/flow-agents/$(basename "$PUBLIC_SESSION")/state.json"
625
+ ROUTE_COMMAND_REF="$(node - "$ROUTE_TEST_COMMAND" <<'NODE'
626
+ const command = process.argv[2];
627
+ process.stdout.write(JSON.stringify({ kind: 'command', excerpt: command, summary: 'Current route-back prerequisite command.' }));
628
+ NODE
629
+ )"
630
+ ROUTE_CRITERION_ONE="$(node - "$ROUTE_TEST_COMMAND" <<'NODE'
631
+ const command = process.argv[2];
632
+ process.stdout.write(JSON.stringify({ id: 'AC-1', status: 'pass', evidence_refs: [{ kind: 'command', excerpt: command, summary: 'Current AC-1 prerequisite.' }] }));
633
+ NODE
634
+ )"
635
+ ROUTE_CRITERION_TWO="$(node - "$ROUTE_TEST_COMMAND" <<'NODE'
636
+ const command = process.argv[2];
637
+ process.stdout.write(JSON.stringify({ id: 'AC-2', status: 'pass', evidence_refs: [{ kind: 'command', excerpt: command, summary: 'Current AC-2 prerequisite.' }] }));
638
+ NODE
639
+ )"
640
+ ROUTE_OBSERVED="$(node - "$ROUTE_TEST_COMMAND" <<'NODE'
641
+ const command = process.argv[2];
642
+ process.stdout.write(JSON.stringify({ command, exit_code: 0, test_count: 1, output_sha256: '0'.repeat(64) }));
643
+ NODE
644
+ )"
645
+ CODEX_SESSION_ID=builder-public-producers flow_agents_node "workflow-sidecar" record-gate-claim "$PUBLIC_SESSION" \
646
+ --expectation tests-evidence --status pass --summary "Seed complete current acceptance prerequisites before routed failure." \
647
+ --command "$ROUTE_TEST_COMMAND" --observed-command-json "$ROUTE_OBSERVED" \
648
+ --evidence-ref-json "$ROUTE_COMMAND_REF" --criterion-json "$ROUTE_CRITERION_ONE" --criterion-json "$ROUTE_CRITERION_TWO" >/dev/null 2>&1 \
649
+ || _fail "failed to seed current acceptance prerequisites for routed failure"
617
650
  record_public_expectation "tests-evidence" "fail"
618
651
  ROUTE_REPORT="$(public_flow status --session-dir "$PUBLIC_SESSION" --json 2>/dev/null)"
619
652
  if node - "$ROUTE_REPORT" <<'NODE'
620
653
  const report = JSON.parse(process.argv[2]);
621
- if (report.current_step !== 'verify') process.exit(1);
622
- if ((report.next_action?.skills || []).join(',') !== 'review-work,verify-work') process.exit(2);
623
- if (!/attempt 1\/3 returned to `verify`/.test(report.next_action?.summary || '')) process.exit(3);
654
+ if (report.current_step !== 'execute') process.exit(1);
655
+ if ((report.next_action?.skills || []).join(',') !== 'execute-plan') process.exit(2);
656
+ if (!/attempt 1\/3 returned to `execute` for `implementation_defect`/.test(report.next_action?.summary || '')) process.exit(3);
624
657
  NODE
625
658
  then
626
- _pass "failed verify evidence routes back through Flow with attempt 1/3 and canonical producers"
659
+ _pass "complete failed verify evidence routes back through Flow once to execute for implementation_defect"
627
660
  else
628
661
  _fail "public failed-verify route-back was not projected correctly: $ROUTE_REPORT"
629
662
  fi
@@ -285,7 +285,7 @@ else
285
285
  _fail "pi install failed"
286
286
  fi
287
287
 
288
- USER_SKILLS_DIR="$CODEX_FULL_DEST/.codex/sk""ills/user-skill"
288
+ USER_SKILLS_DIR="$CODEX_FULL_DEST/.agents/sk""ills/user-skill"
289
289
  mkdir -p "$CODEX_FULL_DEST/.codex/ag""ents" "$USER_SKILLS_DIR"
290
290
  printf 'name = "user-agent"\n' > "$CODEX_FULL_DEST/.codex/ag""ents/user-agent.toml"
291
291
  printf '# user skill\n' > "$USER_SKILLS_DIR/SKILL.md"
@@ -309,7 +309,7 @@ for dir in \
309
309
  "$CLAUDE_DEST/.flow-agents" \
310
310
  "$CLAUDE_DEST/.kontourai/flow-agents" \
311
311
  "$CODEX_DEST/.codex/agents" \
312
- "$CODEX_DEST/.codex/skills" \
312
+ "$CODEX_DEST/.agents/skills" \
313
313
  "$CODEX_DEST/.flow-agents" \
314
314
  "$CODEX_DEST/.kontourai/flow-agents" \
315
315
  "$CODEX_FULL_DEST/.flow-agents" \
@@ -1069,7 +1069,7 @@ else
1069
1069
  _fail "packed npm consumer did not complete the public Builder workflow contract and lifecycle commands"
1070
1070
  fi
1071
1071
 
1072
- if node - "$CODEX_FULL_DEST/.codex/skills" "$CODEX_FULL_DEST" <<'NODE'
1072
+ if node - "$CODEX_FULL_DEST/.agents/skills" "$CODEX_FULL_DEST" <<'NODE'
1073
1073
  const fs = require('node:fs');
1074
1074
  const path = require('node:path');
1075
1075
  const [root, installRoot] = process.argv.slice(2);
@@ -1094,7 +1094,12 @@ for (const skill of [...builder, 'agentic-engineering']) {
1094
1094
  throw new Error('verify-work must ship criterion-backed substantive verification evidence guidance');
1095
1095
  }
1096
1096
  const refs = [...text.matchAll(/\b(?:context|docs|kits)\/[A-Za-z0-9_./-]+\.(?:md|json)\b/g)].map((match) => match[0]);
1097
- for (const ref of refs) if (!fs.existsSync(path.join(installRoot, ref))) throw new Error(`${skill} has unresolved installed resource ${ref}`);
1097
+ for (const ref of refs) {
1098
+ const skillLocal = path.join(path.dirname(file), ref);
1099
+ if (!fs.existsSync(skillLocal) && !fs.existsSync(path.join(installRoot, ref))) {
1100
+ throw new Error(`${skill} has unresolved installed resource ${ref}`);
1101
+ }
1102
+ }
1098
1103
  }
1099
1104
  }
1100
1105
  NODE
@@ -1110,6 +1115,105 @@ else
1110
1115
  _fail "Codex full install removed unknown user files"
1111
1116
  fi
1112
1117
 
1118
+ echo ""
1119
+ echo "--- Dedicated Codex Home Universal Skills ---"
1120
+ GLOBAL_HOME="$TMPDIR_EVAL/global-home"
1121
+ GLOBAL_CODEX="$TMPDIR_EVAL/global-codex"
1122
+ GLOBAL_SKILLS="$TMPDIR_EVAL/global-agents/skills"
1123
+ mkdir -p "$GLOBAL_SKILLS/user-skill"
1124
+ printf '# user skill\n' > "$GLOBAL_SKILLS/user-skill/SKILL.md"
1125
+ GLOBAL_OUTPUT="$TMPDIR_EVAL/global-install.out"
1126
+ if HOME="$GLOBAL_HOME" CODEX_REAL_HOME="$GLOBAL_CODEX" bash "$ROOT_DIR/scripts/install-codex-home.sh" "$GLOBAL_CODEX" --skills-dir "$GLOBAL_SKILLS" >"$GLOBAL_OUTPUT"; then
1127
+ _pass "dedicated Codex installer accepts independent hermetic runtime and skill roots"
1128
+ else
1129
+ _fail "dedicated Codex installer failed with independent hermetic roots"
1130
+ fi
1131
+ if [[ -f "$GLOBAL_SKILLS/plan-work/SKILL.md" && -f "$GLOBAL_SKILLS/plan-work/context/contracts/planning-contract.md" && -f "$GLOBAL_SKILLS/user-skill/SKILL.md" && ! -e "$GLOBAL_CODEX/skills/plan-work" ]]; then
1132
+ _pass "global install uses self-contained universal skills and preserves user skills"
1133
+ else
1134
+ _fail "global universal skill layout, resource closure, or user preservation is wrong"
1135
+ fi
1136
+ if rg -q 'Installed Flow Agents into Codex home at .+/global-codex$' "$GLOBAL_OUTPUT" && rg -q 'Installed portable skills at .+/global-agents/skills$' "$GLOBAL_OUTPUT"; then
1137
+ _pass "installer reports both runtime and portable skill destinations"
1138
+ else
1139
+ _fail "installer output does not report both destinations"
1140
+ fi
1141
+ if HOME="$GLOBAL_HOME" CODEX_REAL_HOME="$GLOBAL_CODEX" bash "$ROOT_DIR/scripts/install-codex-home.sh" "$GLOBAL_CODEX" --skills-dir "$GLOBAL_SKILLS" >/dev/null; then
1142
+ _pass "split-root reinstall is idempotent"
1143
+ else
1144
+ _fail "split-root reinstall is not idempotent"
1145
+ fi
1146
+
1147
+ CONFLICT_CODEX="$TMPDIR_EVAL/conflict-codex"
1148
+ CONFLICT_SKILLS="$TMPDIR_EVAL/conflict-agents/skills"
1149
+ mkdir -p "$CONFLICT_SKILLS/plan-work" "$CONFLICT_CODEX/skills/plan-work" "$CONFLICT_CODEX/.flow-agents"
1150
+ printf '# user collision\n' > "$CONFLICT_SKILLS/plan-work/SKILL.md"
1151
+ cp "$ROOT_DIR/dist/codex/.agents/skills/plan-work/SKILL.md" "$CONFLICT_CODEX/skills/plan-work/SKILL.md"
1152
+ printf 'runtime sentinel\n' > "$CONFLICT_CODEX/runtime-sentinel"
1153
+ node - "$CONFLICT_CODEX" <<'NODE'
1154
+ const fs=require('node:fs'), path=require('node:path'), crypto=require('node:crypto'); const root=process.argv[2];
1155
+ const rel='skills' + '/plan-work/SKILL.md'; const sha256=crypto.createHash('sha256').update(fs.readFileSync(path.join(root,rel))).digest('hex');
1156
+ fs.writeFileSync(path.join(root,'.flow-agents/codex-install-manifest.json'), JSON.stringify({schema_version:'1.0',files:[{path:rel,sha256}]},null,2)+'\n');
1157
+ NODE
1158
+ CONFLICT_RUNTIME_BEFORE="$(shasum -a 256 "$CONFLICT_CODEX/skills/plan-work/SKILL.md" "$CONFLICT_CODEX/.flow-agents/codex-install-manifest.json" "$CONFLICT_CODEX/runtime-sentinel")"
1159
+ if HOME="$GLOBAL_HOME" CODEX_REAL_HOME="$CONFLICT_CODEX" bash "$ROOT_DIR/scripts/install-codex-home.sh" "$CONFLICT_CODEX" --skills-dir "$CONFLICT_SKILLS" >/dev/null 2>&1; then
1160
+ _fail "global installer overwrote a user-owned skill collision"
1161
+ else
1162
+ CONFLICT_RUNTIME_AFTER="$(shasum -a 256 "$CONFLICT_CODEX/skills/plan-work/SKILL.md" "$CONFLICT_CODEX/.flow-agents/codex-install-manifest.json" "$CONFLICT_CODEX/runtime-sentinel")"
1163
+ if [[ "$(cat "$CONFLICT_SKILLS/plan-work/SKILL.md")" == "# user collision" && "$CONFLICT_RUNTIME_BEFORE" == "$CONFLICT_RUNTIME_AFTER" && ! -e "$CONFLICT_SKILLS/.flow-agents" ]]; then
1164
+ _pass "cross-root collision preflight preserves universal content, legacy skill, runtime sentinel, and manifests"
1165
+ else
1166
+ _fail "cross-root collision failure partially mutated an install root"
1167
+ fi
1168
+ fi
1169
+
1170
+ SYMLINK_TARGET="$TMPDIR_EVAL/symlink-target"
1171
+ SYMLINK_PARENT="$TMPDIR_EVAL/symlink-parent"
1172
+ mkdir -p "$SYMLINK_TARGET" "$SYMLINK_PARENT"
1173
+ ln -s "$SYMLINK_TARGET" "$SYMLINK_PARENT/catalog"
1174
+ if HOME="$GLOBAL_HOME" CODEX_REAL_HOME="$TMPDIR_EVAL/symlink-codex" bash "$ROOT_DIR/scripts/install-codex-home.sh" "$TMPDIR_EVAL/symlink-codex" --skills-dir "$SYMLINK_PARENT/catalog/skills" >/dev/null 2>&1; then
1175
+ _fail "global installer followed a symlinked skill destination component"
1176
+ else
1177
+ [[ ! -e "$SYMLINK_TARGET/skills" ]] && _pass "global installer refuses symlinked skill destination components without mutation" || _fail "symlink rejection mutated its target"
1178
+ fi
1179
+
1180
+ SOURCE_ANCESTOR="$(dirname "$ROOT_DIR")"
1181
+ SOURCE_DESCENDANT="$ROOT_DIR/.fa-550-forbidden-skills"
1182
+ for unsafe in / "$SOURCE_ANCESTOR" "$SOURCE_DESCENDANT"; do
1183
+ rm -rf "$SOURCE_DESCENDANT"
1184
+ if HOME="$GLOBAL_HOME" CODEX_REAL_HOME="$TMPDIR_EVAL/unsafe-codex" bash "$ROOT_DIR/scripts/install-codex-home.sh" "$TMPDIR_EVAL/unsafe-codex" --skills-dir "$unsafe" >/dev/null 2>&1; then
1185
+ _fail "global installer accepted unsafe skill namespace root: $unsafe"
1186
+ elif [[ ! -e "$SOURCE_DESCENDANT" && ! -e "$TMPDIR_EVAL/unsafe-codex" ]]; then
1187
+ _pass "unsafe root/overlap rejection occurs before destination mutation: $unsafe"
1188
+ else
1189
+ _fail "unsafe root/overlap rejection left destination mutations: $unsafe"
1190
+ fi
1191
+ done
1192
+
1193
+ MIGRATE_CODEX="$TMPDIR_EVAL/migrate-codex"
1194
+ MIGRATE_SKILLS="$TMPDIR_EVAL/migrate-agents/skills"
1195
+ LEGACY_SKILLS_SEGMENT="skills"
1196
+ LEGACY_USER_DIR="$MIGRATE_CODEX/$LEGACY_SKILLS_SEGMENT/user-legacy"
1197
+ mkdir -p "$MIGRATE_CODEX/skills/plan-work" "$LEGACY_USER_DIR" "$MIGRATE_CODEX/.flow-agents"
1198
+ cp "$ROOT_DIR/dist/codex/.agents/skills/plan-work/SKILL.md" "$MIGRATE_CODEX/skills/plan-work/SKILL.md"
1199
+ printf '# modified legacy user content\n' > "$LEGACY_USER_DIR/SKILL.md"
1200
+ node - "$MIGRATE_CODEX" <<'NODE'
1201
+ const fs = require('node:fs'); const path = require('node:path'); const crypto = require('node:crypto');
1202
+ const root = process.argv[2];
1203
+ const rels = ['skills' + '/plan-work/SKILL.md', 'skills' + '/user-legacy/SKILL.md'];
1204
+ const files = rels.map(p => ({path:p, sha256:crypto.createHash('sha256').update(fs.readFileSync(path.join(root,p))).digest('hex')}));
1205
+ fs.writeFileSync(path.join(root,'.flow-agents/codex-install-manifest.json'), JSON.stringify({schema_version:'1.0',files},null,2)+'\n');
1206
+ fs.appendFileSync(path.join(root,'skills' + '/user-legacy/SKILL.md'), 'kept modification\n');
1207
+ NODE
1208
+ if HOME="$GLOBAL_HOME" CODEX_REAL_HOME="$MIGRATE_CODEX" bash "$ROOT_DIR/scripts/install-codex-home.sh" "$MIGRATE_CODEX" --skills-dir "$MIGRATE_SKILLS" >/dev/null 2>"$TMPDIR_EVAL/migrate.err" \
1209
+ && [[ ! -e "$MIGRATE_CODEX/skills/plan-work/SKILL.md" ]] \
1210
+ && rg -q 'kept modification' "$LEGACY_USER_DIR/SKILL.md" \
1211
+ && [[ -f "$MIGRATE_SKILLS/plan-work/SKILL.md" ]]; then
1212
+ _pass "legacy migration removes unchanged owned skills and preserves modified legacy content"
1213
+ else
1214
+ _fail "legacy Flow Agents skill migration was destructive or incomplete"
1215
+ fi
1216
+
1113
1217
  OPENCODE_AGENTS_DIR="$OPENCODE_FULL_DEST/.opencode/agents"
1114
1218
  if [[ -f "$OPENCODE_AGENTS_DIR/tool-planner.md" && -f "$OPENCODE_AGENTS_DIR/dev.md" ]]; then
1115
1219
  _pass "opencode full install ships the complete agent base"
@@ -164,7 +164,7 @@ OPENCODE_UPGRADE="$TMPDIR_EVAL/upgrade-opencode"
164
164
  # Touch a marker into skill files in the COPIES (not dist/ originals)
165
165
  UPGRADE_MARKER="# flow-agents-upgrade-test-marker"
166
166
  CLAUDE_SKILL_FILE="$CLAUDE_BUNDLE_COPY/.claude/skills/plan-work/SKILL.md"
167
- CODEX_SKILL_FILE="$CODEX_BUNDLE_COPY/.codex/skills/plan-work/SKILL.md"
167
+ CODEX_SKILL_FILE="$CODEX_BUNDLE_COPY/.agents/skills/plan-work/SKILL.md"
168
168
  OPENCODE_SKILL_FILE="$OPENCODE_BUNDLE_COPY/.opencode/skills/plan-work/SKILL.md"
169
169
 
170
170
  if [[ -f "$CLAUDE_SKILL_FILE" ]]; then
@@ -191,12 +191,10 @@ else
191
191
  _fail "claude-code upgrade: skill change did not propagate to workspace"
192
192
  fi
193
193
 
194
- if [[ -f "$CODEX_SKILL_FILE" ]] && grep -qF "$UPGRADE_MARKER" "$CODEX_UPGRADE/.codex/skills/plan-work/SKILL.md" 2>/dev/null; then
194
+ if [[ -f "$CODEX_SKILL_FILE" ]] && grep -qF "$UPGRADE_MARKER" "$CODEX_UPGRADE/.agents/skills/plan-work/SKILL.md" 2>/dev/null; then
195
195
  _pass "codex upgrade: modified skill file propagated to workspace"
196
- elif [[ ! -f "$CODEX_SKILL_FILE" ]]; then
197
- _pass "codex upgrade: skill file not in bundle (skipped)"
198
196
  else
199
- _fail "codex upgrade: skill change did not propagate to workspace"
197
+ _fail "codex upgrade: required universal skill missing or change did not propagate"
200
198
  fi
201
199
 
202
200
  if [[ -f "$OPENCODE_SKILL_FILE" ]] && grep -qF "$UPGRADE_MARKER" "$OPENCODE_UPGRADE/.opencode/skills/plan-work/SKILL.md" 2>/dev/null; then
@@ -246,7 +244,7 @@ echo '{"custom":"data"}' > "$OPENCODE_USER/.kontourai/flow-agents/my-session/sta
246
244
 
247
245
  # Modify an installed skill file to simulate user edits
248
246
  CLAUDE_INSTALLED_SKILL="$CLAUDE_USER/.claude/skills/plan-work/SKILL.md"
249
- CODEX_INSTALLED_SKILL="$CODEX_USER/.codex/skills/plan-work/SKILL.md"
247
+ CODEX_INSTALLED_SKILL="$CODEX_USER/.agents/skills/plan-work/SKILL.md"
250
248
  OPENCODE_INSTALLED_SKILL="$OPENCODE_USER/.opencode/skills/plan-work/SKILL.md"
251
249
 
252
250
  USER_EDIT_MARKER="# USER EDIT - should be overwritten by re-install"
@@ -22,6 +22,10 @@ set -euo pipefail
22
22
 
23
23
  ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
24
24
  TMPDIR_EVAL="$(mktemp -d /tmp/install-merge.XXXXXX)"
25
+ export HOME="$TMPDIR_EVAL/hermetic-home"
26
+ export FLOW_AGENTS_SKILLS_DIR="$TMPDIR_EVAL/hermetic-universal/skills"
27
+ mkdir -p "$HOME/.agents" "$FLOW_AGENTS_SKILLS_DIR"
28
+ printf 'installer tests must not replace this file\n' > "$HOME/.agents/ambient-sentinel"
25
29
  pass=0
26
30
  fail=0
27
31
 
@@ -1138,7 +1142,8 @@ echo ""
1138
1142
  echo "--- CH4: codex-home preserves user config, profiles, hooks, local kit state, and user skills ---"
1139
1143
 
1140
1144
  CH4_DEST="$TMPDIR_EVAL/codex-home-ch4"
1141
- mkdir -p "$CH4_DEST/kits/local/repositories/user-kit" "$CH4_DEST/sk""ills/user-owned-skill" "$CH4_DEST/ag""ents"
1145
+ CH4_SKILLS="$TMPDIR_EVAL/codex-home-ch4-universal-skills"
1146
+ mkdir -p "$CH4_DEST/kits/local/repositories/user-kit" "$CH4_SKILLS/user-owned-skill" "$CH4_DEST/ag""ents"
1142
1147
 
1143
1148
  CH4_GENERIC_DIRS=(agent-cards build context docs evals integrations packaging powers prompts schemas scripts kits)
1144
1149
  for CH4_DIR in "${CH4_GENERIC_DIRS[@]}"; do
@@ -1197,14 +1202,14 @@ JSON
1197
1202
  cat > "$CH4_DEST/kits/local/repositories/user-kit/kit.json" << 'JSON'
1198
1203
  {"id":"user-kit","name":"User Kit"}
1199
1204
  JSON
1200
- cat > "$CH4_DEST/sk""ills/user-owned-skill/SKILL.md" << 'EOF_SKILL'
1205
+ cat > "$CH4_SKILLS/user-owned-skill/SKILL.md" << 'EOF_SKILL'
1201
1206
  # User Owned Skill
1202
1207
  EOF_SKILL
1203
1208
  cat > "$CH4_DEST/ag""ents/user-owned-agent.toml" << 'EOF_AGENT'
1204
1209
  name = "user-owned-agent"
1205
1210
  EOF_AGENT
1206
1211
 
1207
- CODEX_REAL_HOME="$TMPDIR_EVAL/fake-real-codex" bash "$ROOT_DIR/scripts/install-codex-home.sh" "$CH4_DEST" >/dev/null 2>&1
1212
+ CODEX_REAL_HOME="$TMPDIR_EVAL/fake-real-codex" FLOW_AGENTS_SKILLS_DIR="$CH4_SKILLS" bash "$ROOT_DIR/scripts/install-codex-home.sh" "$CH4_DEST" >/dev/null 2>&1
1208
1213
 
1209
1214
  if grep -q 'user-model' "$CH4_DEST/config.toml" \
1210
1215
  && grep -q 'user-profile-model' "$CH4_DEST/custom.config.toml" \
@@ -1243,10 +1248,10 @@ else
1243
1248
  _fail "CH4: kits/local registry or repository was removed"
1244
1249
  fi
1245
1250
 
1246
- if [[ -f "$CH4_DEST/sk""ills/search-first/SKILL.md" && -f "$CH4_DEST/sk""ills/plan-work/SKILL.md" && -f "$CH4_DEST/sk""ills/user-owned-skill/SKILL.md" ]]; then
1247
- _pass "CH4: root, Builder Kit workflow, and user-owned skills coexist in flattened Codex skills"
1251
+ if [[ -f "$CH4_SKILLS/search-first/SKILL.md" && -f "$CH4_SKILLS/plan-work/SKILL.md" && -f "$CH4_SKILLS/user-owned-skill/SKILL.md" ]]; then
1252
+ _pass "CH4: root, Builder Kit workflow, and user-owned skills coexist in universal Codex skills"
1248
1253
  else
1249
- _fail "CH4: root skill, Builder Kit workflow skill, or user-owned skill missing from flattened Codex skills"
1254
+ _fail "CH4: root skill, Builder Kit workflow skill, or user-owned skill missing from universal Codex skills"
1250
1255
  fi
1251
1256
 
1252
1257
  if [[ -f "$CH4_DEST/scripts/install-merge.js" && -f "$CH4_DEST/build/src/cli/kit.js" && -f "$CH4_DEST/ag""ents/tool-worker.toml" && -f "$CH4_DEST/ag""ents/user-owned-agent.toml" && -f "$CH4_DEST/hooks.json" ]] \
@@ -1272,7 +1277,7 @@ else
1272
1277
  _fail "CH4: installed scripts/kit.js could not run with installed build bundle"
1273
1278
  fi
1274
1279
 
1275
- CODEX_REAL_HOME="$TMPDIR_EVAL/fake-real-codex" bash "$ROOT_DIR/scripts/install-codex-home.sh" "$CH4_DEST" >/dev/null 2>&1
1280
+ CODEX_REAL_HOME="$TMPDIR_EVAL/fake-real-codex" FLOW_AGENTS_SKILLS_DIR="$CH4_SKILLS" bash "$ROOT_DIR/scripts/install-codex-home.sh" "$CH4_DEST" >/dev/null 2>&1
1276
1281
  CH4_GENERIC_PRESERVED=1
1277
1282
  for CH4_DIR in "${CH4_GENERIC_DIRS[@]}"; do
1278
1283
  [[ "$(cat "$CH4_DEST/$CH4_DIR/user-owned.txt" 2>/dev/null)" == "user-owned-$CH4_DIR" ]] || CH4_GENERIC_PRESERVED=0
@@ -1288,7 +1293,7 @@ echo ""
1288
1293
  # ─── codex-home: CH5: destination symlink containment ───────────────────────
1289
1294
  echo "--- CH5: codex-home refuses managed destination symlinks ---"
1290
1295
 
1291
- for CH5_REL in scripts kits sk""ills hooks.json; do
1296
+ for CH5_REL in scripts kits hooks.json; do
1292
1297
  CH5_DEST="$TMPDIR_EVAL/codex-home-ch5-${CH5_REL//\//-}"
1293
1298
  CH5_OUTSIDE="$TMPDIR_EVAL/codex-home-ch5-outside-${CH5_REL//\//-}"
1294
1299
  mkdir -p "$CH5_DEST" "$CH5_OUTSIDE"
@@ -1680,6 +1685,11 @@ echo ""
1680
1685
 
1681
1686
  echo ""
1682
1687
  echo "==========================="
1688
+ if [[ "$(cat "$HOME/.agents/ambient-sentinel" 2>/dev/null)" == "installer tests must not replace this file" ]]; then
1689
+ _pass "dedicated Codex installer calls remain inside hermetic universal roots"
1690
+ else
1691
+ _fail "dedicated Codex installer calls mutated the isolated default-home sentinel"
1692
+ fi
1683
1693
  total=$((pass + fail))
1684
1694
  echo "Results: ${pass}/${total} passed, ${fail} failed"
1685
1695
  [[ "$fail" -gt 0 ]] && exit 1
@@ -160,15 +160,21 @@ pass "status is canonical and byte-read-only"
160
160
 
161
161
  FLOW_MANIFEST="$CONSUMER/.kontourai/flow/runs/acme-widgets-101/evidence/manifest.json"
162
162
  BEFORE_EVIDENCE="$(node -p "JSON.parse(require('fs').readFileSync('$FLOW_MANIFEST')).evidence.length")"
163
- run_candidate evidence --session-dir "$RELEASE_SESSION" --expectation pickup-probe-readiness --status not_verified --summary "Consumer fixture intentionally leaves this claim unverified." --json >/dev/null
163
+ PARTIAL_EVIDENCE="$(run_candidate evidence --session-dir "$RELEASE_SESSION" --expectation pickup-probe-readiness --status not_verified --summary "Consumer fixture intentionally leaves this claim unverified." --json)"
164
164
  AFTER_EVIDENCE="$(node -p "JSON.parse(require('fs').readFileSync('$FLOW_MANIFEST')).evidence.length")"
165
- [[ "$AFTER_EVIDENCE" -eq $((BEFORE_EVIDENCE + 1)) ]] || fail "evidence invocation did not attach exactly once"
166
- pass "evidence records and synchronizes exactly once"
165
+ [[ "$AFTER_EVIDENCE" -eq "$BEFORE_EVIDENCE" ]] || fail "partial evidence changed the canonical manifest"
166
+ node -e 'const r=JSON.parse(process.argv[1]);if(r.attached!==false||r.awaiting_evidence!==true||r.current_step!=="design-probe")process.exit(1)' "$PARTIAL_EVIDENCE" || fail "partial evidence did not report an explicit awaiting-evidence result"
167
+ pass "partial evidence records locally without evaluation or canonical attachment"
167
168
 
168
169
  PULL_REPORT="$RELEASE_SESSION/$(basename "$RELEASE_SESSION")--pull-work.md"
169
170
  PULL_REPORT_REF="{\"kind\":\"artifact\",\"file\":\"$PULL_REPORT\",\"summary\":\"Concrete selected-work and probe report.\"}"
170
- run_candidate evidence --session-dir "$RELEASE_SESSION" --expectation pickup-probe-readiness --status pass --summary "Complete the consumer readiness expectation after exercising NOT_VERIFIED." --evidence-ref-json "$PULL_REPORT_REF" --json >/dev/null
171
- run_candidate evidence --session-dir "$RELEASE_SESSION" --expectation probe-decisions-or-accepted-gaps --status pass --summary "Complete the consumer probe gate." --evidence-ref-json "$PULL_REPORT_REF" --json >/dev/null
171
+ READINESS_PARTIAL="$(run_candidate evidence --session-dir "$RELEASE_SESSION" --expectation pickup-probe-readiness --status pass --summary "Complete the consumer readiness expectation after exercising NOT_VERIFIED." --evidence-ref-json "$PULL_REPORT_REF" --json)"
172
+ node -e 'const r=JSON.parse(process.argv[1]);if(r.attached!==false||r.awaiting_evidence!==true||r.current_step!=="design-probe")process.exit(1)' "$READINESS_PARTIAL" || fail "first passing member of a multi-expectation gate did not remain pending"
173
+ PROBE_COMPLETE="$(run_candidate evidence --session-dir "$RELEASE_SESSION" --expectation probe-decisions-or-accepted-gaps --status pass --summary "Complete the consumer probe gate." --evidence-ref-json "$PULL_REPORT_REF" --json)"
174
+ node -e 'const r=JSON.parse(process.argv[1]);if(r.attached!==true||r.awaiting_evidence!==false||r.current_step!=="plan")process.exit(1)' "$PROBE_COMPLETE" || fail "complete multi-expectation gate did not attach and advance exactly once"
175
+ AFTER_COMPLETE_EVIDENCE="$(node -p "JSON.parse(require('fs').readFileSync('$FLOW_MANIFEST')).evidence.length")"
176
+ [[ "$AFTER_COMPLETE_EVIDENCE" -eq $((BEFORE_EVIDENCE + 1)) ]] || fail "complete multi-expectation gate did not attach exactly once"
177
+ pass "multi-expectation evidence attaches atomically only when complete"
172
178
  seed_pull_work acme/widgets#104
173
179
  run_candidate_as poison-pointer start --artifact-root "$ARTIFACT_ROOT" --flow builder.build --work-item acme/widgets#104 --assignment-provider local-file --summary "Poison global pointer fixture" >/dev/null
174
180
  node - "$ARTIFACT_ROOT" "$(basename "$RELEASE_SESSION")" <<'NODE'
@@ -10,7 +10,7 @@ pass=0
10
10
  fail=0
11
11
 
12
12
  cleanup() {
13
- rm -rf "$REPRO_FIRST_DIR" "$REPRO_SECOND_DIR" "$ROOT_DIR/kits/zzz-collision-eval-probe"
13
+ rm -rf "$REPRO_FIRST_DIR" "$REPRO_SECOND_DIR" "$ROOT_DIR/kits/zzz-collision-eval-probe" "${MISSING_SKILL_DIR:-}"
14
14
  }
15
15
  trap cleanup EXIT
16
16
 
@@ -65,6 +65,49 @@ for dir in "$DIST_DIR/kiro" "$DIST_DIR/claude-code" "$DIST_DIR/codex" "$DIST_DIR
65
65
  fi
66
66
  done
67
67
 
68
+ if [[ -d "$DIST_DIR/codex/.agents/skills/plan-work" && ! -e "$DIST_DIR/codex/.codex/skills" ]]; then
69
+ _pass "Codex exports portable skills only in the universal .agents catalog"
70
+ else
71
+ _fail "Codex portable skills are not rooted exclusively at .agents/skills"
72
+ fi
73
+
74
+ if node - "$DIST_DIR/codex/.agents/skills" <<'NODE'
75
+ const fs = require("node:fs");
76
+ const path = require("node:path");
77
+ const root = process.argv[2];
78
+ const pattern = /(?:^|[`("'\s])(context\/contracts\/[A-Za-z0-9._/-]+\.md)(?=$|[`),"'\s])/gm;
79
+ for (const name of fs.readdirSync(root)) {
80
+ const skill = path.join(root, name);
81
+ const file = path.join(skill, "SKILL.md");
82
+ if (!fs.existsSync(file)) continue;
83
+ for (const match of fs.readFileSync(file, "utf8").matchAll(pattern)) {
84
+ const resolved = path.resolve(skill, match[1]);
85
+ if (!resolved.startsWith(`${path.resolve(skill)}${path.sep}`) || !fs.existsSync(resolved)) throw new Error(`${name}: ${match[1]}`);
86
+ }
87
+ }
88
+ NODE
89
+ then
90
+ _pass "Codex skill-local instruction resources resolve inside each package"
91
+ else
92
+ _fail "Codex skill-local instruction resource closure failed"
93
+ fi
94
+
95
+ MISSING_SKILL_DIR="$ROOT_DIR/${SKILLS_SEGMENT:-skills}/zzz-missing-resource-probe"
96
+ MISSING_CONTRACT="${CONTEXT_SEGMENT:-context}/contracts/definitely-missing.md"
97
+ mkdir -p "$MISSING_SKILL_DIR"
98
+ printf '%s\n' '# Probe' '' "Read \`$MISSING_CONTRACT\`." > "$MISSING_SKILL_DIR/SKILL.md"
99
+ if (cd "$ROOT_DIR" && npm run build:bundles >/tmp/missing-skill-resource.stdout 2>/tmp/missing-skill-resource.stderr); then
100
+ _fail "bundle generation accepted a missing skill instruction resource"
101
+ else
102
+ if rg -q "skill 'zzz-missing-resource-probe': missing local resource '$MISSING_CONTRACT'" /tmp/missing-skill-resource.stderr; then
103
+ _pass "bundle generation rejects missing skill resources with actionable diagnostics"
104
+ else
105
+ _fail "missing skill resource failure did not name its skill and path"
106
+ fi
107
+ fi
108
+ rm -rf "$MISSING_SKILL_DIR"
109
+ (cd "$ROOT_DIR" && npm run build:bundles >/dev/null)
110
+
68
111
  source_agents=$(find "$ROOT_DIR/agents" -maxdepth 1 -name '*.json' | wc -l | tr -d ' ')
69
112
  codex_excluded_agents=$(node - "$ROOT_DIR/packaging/manifest.json" <<'NODE'
70
113
  const fs = require("node:fs");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kontourai/flow-agents",
3
- "version": "3.7.0",
3
+ "version": "3.9.0",
4
4
  "description": "Flow Agents — a Kontour product that applies Flow and Veritas discipline as a portable process layer inside the agent tools you already use: Claude Code, Codex, Kiro, opencode, pi, and GitHub Actions — with framework adapters (AWS Strands preview) on the same policy-engine contract.",
5
5
  "keywords": [
6
6
  "agents",
@@ -144,9 +144,9 @@
144
144
  "kit": "npm run build --silent && node build/src/cli.js kit"
145
145
  },
146
146
  "devDependencies": {
147
- "@types/node": "^26.1.0",
148
- "promptfoo": "^0.121.17",
149
- "typescript": "^6.0.3"
147
+ "@types/node": "^26.1.1",
148
+ "promptfoo": "^0.121.18",
149
+ "typescript": "^7.0.2"
150
150
  },
151
151
  "dependencies": {
152
152
  "@kontourai/flow": "^3.1.4"
@@ -19,7 +19,7 @@ For the full source/generated/runtime inventory, see [Repository Structure](../d
19
19
 
20
20
  - `dist/kiro/` keeps native Kiro JSON agents and rewrites path-bound config through the install token.
21
21
  - `dist/claude-code/` exports `.claude/agents/*.md` and `.claude/skills/*/SKILL.md`.
22
- - `dist/codex/` exports `.codex/agents/*.toml`, `.codex/skills/*/SKILL.md`, and generated profile config for operating intents such as `builder` and `personal`.
22
+ - `dist/codex/` exports Codex-only agents and profiles beneath `.codex/`, and portable, self-contained skills beneath the universal `.agents/skills/` catalog.
23
23
 
24
24
  All targets also receive shared canonical directories where supported: `context/`, `powers/`, `prompts/`, `scripts/`, and `evals/`.
25
25
 
package/scripts/README.md CHANGED
@@ -113,7 +113,7 @@ prompting. Add `--allow-network` to probe a non-local HTTPS Console endpoint.
113
113
 
114
114
  ## Install And Repo Utilities
115
115
 
116
- - `install-codex-home.sh`: installs the generated Codex bundle into `CODEX_HOME`, or `~/.codex` when `CODEX_HOME` is unset. A positional destination argument is the explicit override for isolated homes and tests.
116
+ - `install-codex-home.sh`: installs Codex runtime assets into `CODEX_HOME` (or `~/.codex`) and portable skills into `$HOME/.agents/skills`. Use `--skills-dir PATH` or `FLOW_AGENTS_SKILLS_DIR` to select a hermetic or advanced-user skill catalog independently of the positional runtime destination. The installer reports both resolved roots, preserves user-owned files, migrates only unchanged Flow Agents-owned legacy skills, and refuses symlink destinations rather than creating a compatibility symlink.
117
117
  - `setup-repo-hooks.sh`: configures this clone's Git hook path.
118
118
  - `check-content-boundary.cjs`, `detect-tools.sh`, `discover-agents.sh`, `git-status.sh`: repo-local helper commands.
119
119
  - `context-budget/` and `statusline/`: specialized support tooling copied into bundles where needed.