sneakoscope 1.18.8 → 1.18.9

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 (66) hide show
  1. package/README.md +6 -2
  2. package/crates/sks-core/Cargo.lock +1 -1
  3. package/crates/sks-core/Cargo.toml +1 -1
  4. package/crates/sks-core/src/main.rs +1 -1
  5. package/dist/.sks-build-stamp.json +4 -4
  6. package/dist/bin/sks.js +1 -1
  7. package/dist/build-manifest.json +11 -9
  8. package/dist/commands/image-ux-review.d.ts +64 -0
  9. package/dist/commands/ppt.d.ts +64 -0
  10. package/dist/core/agents/agent-codex-cockpit.d.ts +7 -0
  11. package/dist/core/agents/agent-codex-cockpit.js +33 -0
  12. package/dist/core/agents/agent-command-surface.js +1 -1
  13. package/dist/core/agents/agent-merge-coordinator.d.ts +62 -1
  14. package/dist/core/agents/agent-merge-coordinator.js +130 -19
  15. package/dist/core/agents/agent-orchestrator.d.ts +64 -0
  16. package/dist/core/agents/agent-orchestrator.js +166 -1
  17. package/dist/core/agents/agent-output-validator.d.ts +143 -0
  18. package/dist/core/agents/agent-output-validator.js +58 -0
  19. package/dist/core/agents/agent-patch-apply-worker.d.ts +44 -0
  20. package/dist/core/agents/agent-patch-apply-worker.js +120 -52
  21. package/dist/core/agents/agent-patch-proof.d.ts +13 -0
  22. package/dist/core/agents/agent-patch-proof.js +44 -1
  23. package/dist/core/agents/agent-patch-queue-store.d.ts +23 -0
  24. package/dist/core/agents/agent-patch-queue-store.js +66 -0
  25. package/dist/core/agents/agent-patch-queue.d.ts +48 -16
  26. package/dist/core/agents/agent-patch-queue.js +28 -7
  27. package/dist/core/agents/agent-patch-schema.d.ts +21 -1
  28. package/dist/core/agents/agent-patch-schema.js +47 -2
  29. package/dist/core/agents/agent-proof-evidence.d.ts +22 -0
  30. package/dist/core/agents/agent-proof-evidence.js +77 -1
  31. package/dist/core/agents/agent-runner-codex-exec.d.ts +4 -0
  32. package/dist/core/agents/agent-runner-fake.d.ts +1 -0
  33. package/dist/core/agents/agent-runner-fake.js +42 -0
  34. package/dist/core/agents/agent-runner-process.js +3 -0
  35. package/dist/core/agents/agent-runner-tmux.js +3 -0
  36. package/dist/core/agents/agent-schema.d.ts +18 -0
  37. package/dist/core/agents/agent-task-graph.d.ts +13 -11
  38. package/dist/core/agents/agent-task-graph.js +12 -2
  39. package/dist/core/agents/agent-work-partition.d.ts +22 -1
  40. package/dist/core/agents/agent-work-partition.js +1 -1
  41. package/dist/core/agents/agent-worker-pipeline.d.ts +7 -0
  42. package/dist/core/agents/agent-worker-pipeline.js +46 -1
  43. package/dist/core/agents/route-collaboration-ledger.d.ts +64 -0
  44. package/dist/core/agents/work-partition/lease-planner.d.ts +21 -1
  45. package/dist/core/agents/work-partition/lease-planner.js +26 -3
  46. package/dist/core/codex/appshots-detector.d.ts +35 -0
  47. package/dist/core/codex/appshots-detector.js +55 -5
  48. package/dist/core/commands/agent-command.js +26 -2
  49. package/dist/core/commands/image-ux-review-command.d.ts +64 -0
  50. package/dist/core/commands/ppt-command.d.ts +64 -0
  51. package/dist/core/fsx.d.ts +1 -1
  52. package/dist/core/fsx.js +1 -1
  53. package/dist/core/mcp/mcp-0-134-policy.d.ts +32 -0
  54. package/dist/core/mcp/mcp-0-134-policy.js +63 -0
  55. package/dist/core/source-intelligence/appshots-evidence.d.ts +11 -1
  56. package/dist/core/source-intelligence/appshots-evidence.js +19 -2
  57. package/dist/core/source-intelligence/source-intelligence-runner.d.ts +14 -0
  58. package/dist/core/source-intelligence/source-intelligence-runner.js +1 -0
  59. package/dist/core/strategy/adhd-orchestrating-gate.js +1 -1
  60. package/dist/core/strategy/strategy-compiler.d.ts +9 -0
  61. package/dist/core/strategy/strategy-compiler.js +29 -3
  62. package/dist/core/version.d.ts +1 -1
  63. package/dist/core/version.js +1 -1
  64. package/dist/scripts/release-parallel-check.js +14 -1
  65. package/package.json +18 -3
  66. package/schemas/codex/agent-result.schema.json +64 -0
package/README.md CHANGED
@@ -16,7 +16,7 @@ Set up this agent project with Sneakoscope Codex. Use [[mandarange/Sneakoscope-C
16
16
 
17
17
  ## Current Release
18
18
 
19
- SKS **1.18.8** closes the Codex 0.134 ultra-stability loop: the release matrix now covers `--profile` as the primary selector, bounded local Codex history search, managed proxy propagation, MCP 0.134 environment/OAuth/schema/readOnlyHint policy, proof-safe parallel agent patches, runtime truth P6 rows, the 1.18.8 release gate audit, MAD-SKS as general scoped permission widening, gpt-image-2 imagegen as a core evidence capability, and retention cleanup that preserves durable learning while removing finished route scratch.
19
+ SKS **1.18.9** closes the Patch Swarm runtime surface: Appshots evidence now records Codex thread attachment provenance without claiming CLI-created screenshots, MCP `readOnlyHint` gates require runtime overlap/serialization proof, Codex 0.134 runner truth covers profile, proxy, history, and process reports, and release gates expose these checks alongside the patch queue/apply/proof work.
20
20
 
21
21
  ```bash
22
22
  sks mad-sks plan --target-root <path> --json
@@ -44,8 +44,10 @@ npm run strategy:adhd-orchestrating-gate
44
44
  npm run strategy:parallel-modification-plan
45
45
  npm run appshots:evidence
46
46
  npm run appshots:source-intelligence
47
+ npm run appshots:thread-attachment-discovery
47
48
  npm run mcp:0.134-modernization
48
- npm run mcp:readonly-concurrency
49
+ npm run mcp:readonly-runtime-scheduler
50
+ npm run codex:0.134-runner-truth
49
51
  npm run source-intelligence:codex-history-search
50
52
  npm run agent:parallel-write-kernel
51
53
  npm run release:gate-existence-audit
@@ -111,6 +113,8 @@ The cleanup contract is policy-backed in `.sneakoscope/policy.json`, but the def
111
113
  - ADHD orchestration gate: [docs/adhd-orchestrating-gate.md](docs/adhd-orchestrating-gate.md)
112
114
  - Strategy-first parallel write: [docs/strategy-first-parallel-write.md](docs/strategy-first-parallel-write.md)
113
115
  - Appshots pipeline: [docs/appshots-pipeline.md](docs/appshots-pipeline.md)
116
+ - Appshots thread attachments: [docs/appshots-thread-attachments.md](docs/appshots-thread-attachments.md)
117
+ - MCP readOnly scheduler: [docs/mcp-readonly-scheduler.md](docs/mcp-readonly-scheduler.md)
114
118
  - Parallel write agents: [docs/parallel-write-agents.md](docs/parallel-write-agents.md)
115
119
  - Agent patch queue: [docs/agent-patch-queue.md](docs/agent-patch-queue.md)
116
120
  - Migration 1.18.7 to 1.18.8: [docs/migration-1.18.7-to-1.18.8.md](docs/migration-1.18.7-to-1.18.8.md)
@@ -76,7 +76,7 @@ dependencies = [
76
76
 
77
77
  [[package]]
78
78
  name = "sks-core"
79
- version = "1.18.8"
79
+ version = "1.18.9"
80
80
  dependencies = [
81
81
  "serde_json",
82
82
  ]
@@ -1,6 +1,6 @@
1
1
  [package]
2
2
  name = "sks-core"
3
- version = "1.18.8"
3
+ version = "1.18.9"
4
4
  edition = "2021"
5
5
 
6
6
  [dependencies]
@@ -4,7 +4,7 @@ use std::io::{self, Read, Seek, SeekFrom};
4
4
  fn main() {
5
5
  let mut args = std::env::args().skip(1);
6
6
  match args.next().as_deref() {
7
- Some("--version") => println!("sks-rs 1.18.8"),
7
+ Some("--version") => println!("sks-rs 1.18.9"),
8
8
  Some("compact-info") => {
9
9
  let mut input = String::new();
10
10
  let _ = io::stdin().read_to_string(&mut input);
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "schema": "sks.dist-build-stamp.v1",
3
3
  "package_name": "sneakoscope",
4
- "package_version": "1.18.8",
5
- "source_digest": "f00f42e2af3a45681da126c20bfcd5d3345fd2ace666c76010660d39ca88cdc6",
6
- "source_file_count": 1514,
7
- "built_at_source_time": 1779887241763
4
+ "package_version": "1.18.9",
5
+ "source_digest": "6eee8ecc34d3c7ce9e86cabc59e92764287b996a5387f26f12ec3253fee6869e",
6
+ "source_file_count": 1537,
7
+ "built_at_source_time": 1779893098646
8
8
  }
package/dist/bin/sks.js CHANGED
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- const FAST_PACKAGE_VERSION = '1.18.8';
2
+ const FAST_PACKAGE_VERSION = '1.18.9';
3
3
  const args = process.argv.slice(2);
4
4
  try {
5
5
  if (args[0] === '--version' || args[0] === '-v' || args[0] === 'version') {
@@ -1,16 +1,16 @@
1
1
  {
2
2
  "schema": "sks.dist-build.v2",
3
- "version": "1.18.8",
4
- "package_version": "1.18.8",
3
+ "version": "1.18.9",
4
+ "package_version": "1.18.9",
5
5
  "typescript": true,
6
6
  "mjs_runtime_files": 0,
7
- "compiled_file_count": 908,
8
- "compiled_js_count": 454,
9
- "compiled_dts_count": 454,
10
- "source_digest": "f00f42e2af3a45681da126c20bfcd5d3345fd2ace666c76010660d39ca88cdc6",
11
- "source_file_count": 1514,
12
- "source_files_hash": "617adc02ea5a7a6cf0ad7cfac691a817e9fb26380d5f21a249eaea89c0a7b8ea",
13
- "source_list_hash": "617adc02ea5a7a6cf0ad7cfac691a817e9fb26380d5f21a249eaea89c0a7b8ea",
7
+ "compiled_file_count": 910,
8
+ "compiled_js_count": 455,
9
+ "compiled_dts_count": 455,
10
+ "source_digest": "6eee8ecc34d3c7ce9e86cabc59e92764287b996a5387f26f12ec3253fee6869e",
11
+ "source_file_count": 1537,
12
+ "source_files_hash": "3475322f249a9a6a5286d536a5e69dfe6f2b06ad2d64a1b0bd9c408b19257f10",
13
+ "source_list_hash": "3475322f249a9a6a5286d536a5e69dfe6f2b06ad2d64a1b0bd9c408b19257f10",
14
14
  "src_mjs_runtime_files": 0,
15
15
  "dist_stamp_schema": "sks.dist-build-stamp.v1",
16
16
  "files": [
@@ -226,6 +226,8 @@
226
226
  "core/agents/agent-patch-apply-worker.js",
227
227
  "core/agents/agent-patch-proof.d.ts",
228
228
  "core/agents/agent-patch-proof.js",
229
+ "core/agents/agent-patch-queue-store.d.ts",
230
+ "core/agents/agent-patch-queue-store.js",
229
231
  "core/agents/agent-patch-queue.d.ts",
230
232
  "core/agents/agent-patch-queue.js",
231
233
  "core/agents/agent-patch-schema.d.ts",
@@ -511,6 +511,7 @@ export declare function run(command: any, args?: any): Promise<void | {
511
511
  status: string;
512
512
  blockers: string[];
513
513
  };
514
+ patch_swarm?: never;
514
515
  } | {
515
516
  schema: string;
516
517
  ok: boolean;
@@ -781,6 +782,48 @@ export declare function run(command: any, args?: any): Promise<void | {
781
782
  appshots_operator_action_required: boolean;
782
783
  };
783
784
  };
785
+ patch_swarm: {
786
+ schema: string;
787
+ generated_at: string;
788
+ ok: boolean;
789
+ mission_id: string;
790
+ route: string;
791
+ route_command: string;
792
+ write_capable: boolean;
793
+ dry_run: boolean;
794
+ patch_envelope_count: number;
795
+ apply_started_at: string;
796
+ apply_finished_at: string;
797
+ apply_latency_ms: number;
798
+ parallel_apply_count: number;
799
+ parallel_apply_groups: {
800
+ group_id: string;
801
+ entry_ids: string[];
802
+ agents: string[];
803
+ expected_speedup: number;
804
+ }[];
805
+ serial_merge_groups: {
806
+ group_id: string;
807
+ entry_ids: string[];
808
+ agents: string[];
809
+ reason: string;
810
+ file: string;
811
+ }[];
812
+ wall_clock_parallel_evidence: string[];
813
+ expected_speedup: number;
814
+ conflicted_agent_count: number;
815
+ artifacts: {
816
+ queue: string;
817
+ events: string;
818
+ ownership: string;
819
+ merge: string;
820
+ apply_results: string;
821
+ verification: string;
822
+ rollback: string;
823
+ proof: string;
824
+ };
825
+ blockers: string[];
826
+ };
784
827
  proof: {
785
828
  schema: string;
786
829
  ok: boolean;
@@ -801,6 +844,27 @@ export declare function run(command: any, args?: any): Promise<void | {
801
844
  parallel_write_apply_patches: boolean;
802
845
  parallel_write_dry_run_patches: boolean;
803
846
  parallel_write_max_write_agents: number;
847
+ patch_swarm_runtime: string | null;
848
+ patch_queue: string | null;
849
+ patch_queue_events: string | null;
850
+ patch_queue_event_count: number;
851
+ patch_proof: string | null;
852
+ patch_queue_ok: boolean;
853
+ patch_apply_ok: any;
854
+ patch_verification_ok: any;
855
+ patch_rollback_ok: any;
856
+ parallel_patch_apply_verified: boolean;
857
+ patch_conflict_count: number;
858
+ serial_bottleneck_count: number;
859
+ changed_files_by_agent: Record<string, string[]>;
860
+ lease_compliance_by_patch: {
861
+ patch_entry_id: any;
862
+ agent_id: any;
863
+ lease_id: any;
864
+ ok: boolean;
865
+ write_paths: any;
866
+ }[];
867
+ rollback_digest_count: any;
804
868
  real_parallel_claim: boolean;
805
869
  fake_backend_disclaimer: string | null;
806
870
  agent_count: any;
@@ -360,6 +360,7 @@ export declare function run(command: any, args?: any): Promise<void | {
360
360
  status: string;
361
361
  blockers: string[];
362
362
  };
363
+ patch_swarm?: never;
363
364
  } | {
364
365
  schema: string;
365
366
  ok: boolean;
@@ -630,6 +631,48 @@ export declare function run(command: any, args?: any): Promise<void | {
630
631
  appshots_operator_action_required: boolean;
631
632
  };
632
633
  };
634
+ patch_swarm: {
635
+ schema: string;
636
+ generated_at: string;
637
+ ok: boolean;
638
+ mission_id: string;
639
+ route: string;
640
+ route_command: string;
641
+ write_capable: boolean;
642
+ dry_run: boolean;
643
+ patch_envelope_count: number;
644
+ apply_started_at: string;
645
+ apply_finished_at: string;
646
+ apply_latency_ms: number;
647
+ parallel_apply_count: number;
648
+ parallel_apply_groups: {
649
+ group_id: string;
650
+ entry_ids: string[];
651
+ agents: string[];
652
+ expected_speedup: number;
653
+ }[];
654
+ serial_merge_groups: {
655
+ group_id: string;
656
+ entry_ids: string[];
657
+ agents: string[];
658
+ reason: string;
659
+ file: string;
660
+ }[];
661
+ wall_clock_parallel_evidence: string[];
662
+ expected_speedup: number;
663
+ conflicted_agent_count: number;
664
+ artifacts: {
665
+ queue: string;
666
+ events: string;
667
+ ownership: string;
668
+ merge: string;
669
+ apply_results: string;
670
+ verification: string;
671
+ rollback: string;
672
+ proof: string;
673
+ };
674
+ blockers: string[];
675
+ };
633
676
  proof: {
634
677
  schema: string;
635
678
  ok: boolean;
@@ -650,6 +693,27 @@ export declare function run(command: any, args?: any): Promise<void | {
650
693
  parallel_write_apply_patches: boolean;
651
694
  parallel_write_dry_run_patches: boolean;
652
695
  parallel_write_max_write_agents: number;
696
+ patch_swarm_runtime: string | null;
697
+ patch_queue: string | null;
698
+ patch_queue_events: string | null;
699
+ patch_queue_event_count: number;
700
+ patch_proof: string | null;
701
+ patch_queue_ok: boolean;
702
+ patch_apply_ok: any;
703
+ patch_verification_ok: any;
704
+ patch_rollback_ok: any;
705
+ parallel_patch_apply_verified: boolean;
706
+ patch_conflict_count: number;
707
+ serial_bottleneck_count: number;
708
+ changed_files_by_agent: Record<string, string[]>;
709
+ lease_compliance_by_patch: {
710
+ patch_entry_id: any;
711
+ agent_id: any;
712
+ lease_id: any;
713
+ ok: boolean;
714
+ write_paths: any;
715
+ }[];
716
+ rollback_digest_count: any;
653
717
  real_parallel_claim: boolean;
654
718
  fake_backend_disclaimer: string | null;
655
719
  agent_count: any;
@@ -43,6 +43,13 @@ export interface AgentCodexCockpitState {
43
43
  pending_queue_count: number | null;
44
44
  backfill_count: number | null;
45
45
  scheduler_status: string | null;
46
+ patch_swarm_phase: string | null;
47
+ patch_queue_depth: number | null;
48
+ patch_apply_groups: Array<Record<string, unknown>>;
49
+ patch_changed_files_by_agent: Record<string, unknown>;
50
+ patch_verification_status: Array<Record<string, unknown>>;
51
+ patch_rollback_status: Array<Record<string, unknown>>;
52
+ patch_micro_win_links: Array<Record<string, unknown>>;
46
53
  worker_slots: Array<Record<string, unknown>>;
47
54
  session_generations: Array<Record<string, unknown>>;
48
55
  blockers: string[];
@@ -40,6 +40,12 @@ export async function buildAgentCodexCockpitState(missionDir, opts = {}) {
40
40
  const scheduler = await readJson(path.join(root, 'agent-scheduler-state.json'), null);
41
41
  const workerSlots = await readJson(path.join(root, 'agent-worker-slots.json'), null);
42
42
  const generations = await readJson(path.join(root, 'agent-session-generations.json'), null);
43
+ const patchSwarm = await readJson(path.join(root, 'agent-patch-swarm-runtime.json'), null);
44
+ const patchQueue = await readJson(path.join(root, 'agent-patch-queue.json'), null);
45
+ const patchMerge = await readJson(path.join(root, 'agent-merge-coordinator-report.json'), null);
46
+ const patchVerification = await readJson(path.join(root, 'agent-patch-verification-results.json'), null);
47
+ const patchRollback = await readJson(path.join(root, 'agent-patch-rollback-proof.json'), null);
48
+ const patchProof = await readJson(path.join(root, 'agent-patch-proof.json'), null);
43
49
  const terminalClosed = proof?.terminal_sessions_closed === true;
44
50
  const eventsTail = await readTailLines(path.join(root, 'agent-events.jsonl'), 8);
45
51
  const cockpitEventsTail = await readTailLines(path.join(root, AGENT_CODEX_COCKPIT_EVENTS), 8);
@@ -76,6 +82,13 @@ export async function buildAgentCodexCockpitState(missionDir, opts = {}) {
76
82
  pending_queue_count: scheduler?.pending_count ?? null,
77
83
  backfill_count: scheduler?.backfill_count ?? null,
78
84
  scheduler_status: scheduler?.status || null,
85
+ patch_swarm_phase: patchSwarm ? (patchSwarm.ok === true ? 'passed' : 'blocked') : null,
86
+ patch_queue_depth: patchQueue?.queued_count ?? null,
87
+ patch_apply_groups: Array.isArray(patchMerge?.parallel_apply_groups) ? patchMerge.parallel_apply_groups : [],
88
+ patch_changed_files_by_agent: patchProof?.changed_files_by_agent || {},
89
+ patch_verification_status: Array.isArray(patchVerification?.results) ? patchVerification.results : [],
90
+ patch_rollback_status: Array.isArray(patchRollback?.entries) ? patchRollback.entries : [],
91
+ patch_micro_win_links: buildPatchMicroWinLinks(patchQueue),
79
92
  worker_slots: Array.isArray(workerSlots?.slots) ? workerSlots.slots : [],
80
93
  session_generations: generations?.generations ? Object.values(generations.generations) : [],
81
94
  blockers,
@@ -109,6 +122,9 @@ export function renderAgentCodexDashboard(state) {
109
122
  `- tmux attach: ${state.tmux_attach_command || 'unknown'}`,
110
123
  `- All sessions closed: ${state.all_sessions_closed ?? 'unknown'}`,
111
124
  `- Scheduler: ${state.scheduler_status || 'unknown'}`,
125
+ `- Patch swarm: ${state.patch_swarm_phase || 'unknown'}`,
126
+ `- Patch queue depth: ${state.patch_queue_depth ?? 'unknown'}`,
127
+ `- Patch apply groups: ${state.patch_apply_groups.length}`,
112
128
  `- Target active slots: ${state.target_active_slots ?? 'unknown'}`,
113
129
  `- Active slots: ${state.active_slot_count ?? 'unknown'}`,
114
130
  `- Pending queue: ${state.pending_queue_count ?? 'unknown'}`,
@@ -168,6 +184,13 @@ function summarizeLiveState(state) {
168
184
  pending_queue_count: state.pending_queue_count,
169
185
  backfill_count: state.backfill_count,
170
186
  scheduler_status: state.scheduler_status,
187
+ patch_swarm_phase: state.patch_swarm_phase,
188
+ patch_queue_depth: state.patch_queue_depth,
189
+ patch_apply_groups: state.patch_apply_groups,
190
+ patch_changed_files_by_agent: state.patch_changed_files_by_agent,
191
+ patch_verification_status: state.patch_verification_status,
192
+ patch_rollback_status: state.patch_rollback_status,
193
+ patch_micro_win_links: state.patch_micro_win_links,
171
194
  worker_slot_count: state.worker_slots.length,
172
195
  session_generation_count: state.session_generations.length,
173
196
  proof_status: state.proof_status,
@@ -180,6 +203,16 @@ function summarizeLiveState(state) {
180
203
  blockers: state.blockers,
181
204
  };
182
205
  }
206
+ function buildPatchMicroWinLinks(patchQueue) {
207
+ const entries = Array.isArray(patchQueue?.entries) ? patchQueue.entries : [];
208
+ return entries.map((entry) => ({
209
+ patch_entry_id: entry.id,
210
+ agent_id: entry.agent_id,
211
+ micro_win_id: entry.envelope?.lease_proof?.micro_win_id || null,
212
+ strategy_task_id: entry.envelope?.lease_proof?.strategy_task_id || null,
213
+ write_paths: entry.write_paths || []
214
+ }));
215
+ }
183
216
  function mergeAgentRows(sessions, roster, leases, events) {
184
217
  const byId = new Map();
185
218
  for (const row of roster)
@@ -1,7 +1,7 @@
1
1
  import { DEFAULT_AGENT_COUNT } from './agent-schema.js';
2
2
  export function parseAgentCommandArgs(command, args = []) {
3
3
  const first = args[0] && !String(args[0]).startsWith('--') ? String(args[0]) : '';
4
- const actions = new Set(['run', 'status', 'plan', 'spawn', 'watch', 'dashboard', 'cockpit', 'lane', 'board', 'ledger', 'collect', 'consensus', 'close', 'cleanup', 'proof', 'explain']);
4
+ const actions = new Set(['run', 'status', 'plan', 'spawn', 'watch', 'dashboard', 'cockpit', 'lane', 'board', 'ledger', 'collect', 'consensus', 'close', 'cleanup', 'proof', 'explain', 'rollback-patches']);
5
5
  const action = actions.has(first) ? first : 'run';
6
6
  const rest = action === first ? args.slice(1) : args;
7
7
  const json = hasFlag(args, '--json');
@@ -1,23 +1,84 @@
1
1
  import type { AgentPatchEnvelope } from './agent-patch-schema.js';
2
+ import type { AgentPatchQueueEntry } from './agent-patch-queue.js';
2
3
  export declare const AGENT_MERGE_COORDINATOR_SCHEMA = "sks.agent-merge-coordinator.v1";
3
- export declare function coordinateAgentPatchMerge(envelopes: AgentPatchEnvelope[]): {
4
+ export type AgentPatchMergeInput = AgentPatchEnvelope | AgentPatchQueueEntry;
5
+ export interface AgentPatchMergeCoordinatorOptions {
6
+ artifactsDir?: string;
7
+ }
8
+ export declare function coordinateAgentPatchMerge(inputs: AgentPatchMergeInput[], opts?: AgentPatchMergeCoordinatorOptions): {
4
9
  schema: string;
5
10
  ok: boolean;
6
11
  merge_order: string[];
12
+ apply_order: string[];
7
13
  touched_files: string[];
8
14
  conflicts: {
15
+ type: string;
9
16
  file: string;
17
+ entries: string[];
10
18
  agents: string[];
19
+ reason: string;
20
+ }[];
21
+ conflict_graph: {
22
+ nodes: {
23
+ entry_id: string;
24
+ agent_id: string;
25
+ lease_id: string | null;
26
+ }[];
27
+ edges: {
28
+ file: string;
29
+ entries: string[];
30
+ reason: string;
31
+ }[];
32
+ };
33
+ parallel_apply_groups: {
34
+ group_id: string;
35
+ entry_ids: string[];
36
+ agents: string[];
37
+ expected_speedup: number;
38
+ }[];
39
+ serial_merge_groups: {
40
+ group_id: string;
41
+ entry_ids: string[];
42
+ agents: string[];
43
+ reason: string;
44
+ file: string;
45
+ }[];
46
+ blocked_conflicts: {
47
+ type: string;
48
+ file: string;
49
+ entries: string[];
50
+ agents: string[];
51
+ reason: string;
11
52
  }[];
12
53
  parallel_batches: {
13
54
  batch_id: string;
14
55
  agents: string[];
15
56
  }[];
16
57
  serial_conflicts: {
58
+ type: string;
17
59
  file: string;
60
+ entries: string[];
18
61
  agents: string[];
62
+ reason: string;
19
63
  }[];
64
+ apply_plan: {
65
+ parallel_apply_groups: {
66
+ group_id: string;
67
+ entry_ids: string[];
68
+ agents: string[];
69
+ expected_speedup: number;
70
+ }[];
71
+ serial_merge_groups: {
72
+ group_id: string;
73
+ entry_ids: string[];
74
+ agents: string[];
75
+ reason: string;
76
+ file: string;
77
+ }[];
78
+ retry_policy: string;
79
+ };
20
80
  wall_clock_parallel_evidence: string[];
21
81
  blockers: string[];
22
82
  };
83
+ export declare function writeAgentMergeCoordinatorArtifacts(artifactDir: string, report: ReturnType<typeof coordinateAgentPatchMerge>): Promise<void>;
23
84
  //# sourceMappingURL=agent-merge-coordinator.d.ts.map
@@ -1,46 +1,146 @@
1
+ import path from 'node:path';
2
+ import { writeJsonAtomic } from '../fsx.js';
1
3
  export const AGENT_MERGE_COORDINATOR_SCHEMA = 'sks.agent-merge-coordinator.v1';
2
- export function coordinateAgentPatchMerge(envelopes) {
4
+ export function coordinateAgentPatchMerge(inputs, opts = {}) {
5
+ const items = normalizeInputs(inputs);
3
6
  const writers = new Map();
4
- for (const envelope of envelopes) {
5
- for (const operation of envelope.operations || []) {
7
+ const blockedConflicts = [];
8
+ for (const item of items) {
9
+ if (item.status && item.status !== 'pending') {
10
+ blockedConflicts.push({ type: 'status', file: '.', entries: [item.entry_id], agents: [item.agent_id], reason: `entry_not_pending:${item.status}` });
11
+ }
12
+ for (const operation of item.envelope.operations || []) {
6
13
  const key = normalizePatchPath(operation.path || '');
7
14
  if (!writers.has(key))
8
15
  writers.set(key, []);
9
- writers.get(key)?.push(envelope.agent_id);
16
+ writers.get(key)?.push(item);
17
+ if (protectedPath(key)) {
18
+ blockedConflicts.push({ type: 'protected_path', file: key, entries: [item.entry_id], agents: [item.agent_id], reason: `protected_path:${key}` });
19
+ }
20
+ if (!pathAllowedByLease(key, item.envelope)) {
21
+ blockedConflicts.push({ type: 'lease', file: key, entries: [item.entry_id], agents: [item.agent_id], reason: `lease_path_not_allowed:${key}` });
22
+ }
10
23
  }
11
24
  }
25
+ for (const conflict of domainConflicts(items))
26
+ blockedConflicts.push(conflict);
12
27
  const conflicts = mergeConflicts(writers);
13
- const serialConflictAgents = new Set(conflicts.flatMap((conflict) => conflict.agents));
14
- const parallelBatch = envelopes.filter((envelope) => !serialConflictAgents.has(envelope.agent_id)).map((envelope) => envelope.agent_id);
15
- return {
28
+ const allBlockedConflicts = [...blockedConflicts, ...conflicts];
29
+ const serialConflictEntries = new Set(allBlockedConflicts.flatMap((conflict) => conflict.entries));
30
+ const parallelEntries = items.filter((item) => !serialConflictEntries.has(item.entry_id));
31
+ const parallelApplyGroups = parallelEntries.length ? [{
32
+ group_id: 'parallel-001',
33
+ entry_ids: parallelEntries.map((item) => item.entry_id),
34
+ agents: parallelEntries.map((item) => item.agent_id),
35
+ expected_speedup: parallelEntries.length
36
+ }] : [];
37
+ const serialMergeGroups = allBlockedConflicts.map((conflict, index) => ({
38
+ group_id: `serial-${String(index + 1).padStart(3, '0')}`,
39
+ entry_ids: conflict.entries,
40
+ agents: conflict.agents,
41
+ reason: conflict.reason,
42
+ file: conflict.file
43
+ }));
44
+ const result = {
16
45
  schema: AGENT_MERGE_COORDINATOR_SCHEMA,
17
- ok: conflicts.length === 0,
18
- merge_order: envelopes.map((envelope) => envelope.agent_id),
46
+ ok: allBlockedConflicts.length === 0,
47
+ merge_order: items.map((item) => item.agent_id),
48
+ apply_order: [...parallelApplyGroups.map((group) => group.group_id), ...serialMergeGroups.map((group) => group.group_id)],
19
49
  touched_files: [...writers.keys()].sort(),
20
- conflicts,
21
- parallel_batches: parallelBatch.length ? [{ batch_id: 'batch-001', agents: parallelBatch }] : [],
22
- serial_conflicts: conflicts,
23
- wall_clock_parallel_evidence: parallelBatch.length ? [`batch-001:${parallelBatch.length}_agents_can_apply_without_overlapping_paths`] : [],
24
- blockers: conflicts.map((conflict) => `parallel_write_conflict:${conflict.file}`)
50
+ conflicts: allBlockedConflicts,
51
+ conflict_graph: {
52
+ nodes: items.map((item) => ({ entry_id: item.entry_id, agent_id: item.agent_id, lease_id: item.lease_id })),
53
+ edges: allBlockedConflicts.map((conflict) => ({ file: conflict.file, entries: conflict.entries, reason: conflict.reason }))
54
+ },
55
+ parallel_apply_groups: parallelApplyGroups,
56
+ serial_merge_groups: serialMergeGroups,
57
+ blocked_conflicts: allBlockedConflicts,
58
+ parallel_batches: parallelApplyGroups.map((group) => ({ batch_id: group.group_id, agents: group.agents })),
59
+ serial_conflicts: allBlockedConflicts,
60
+ apply_plan: {
61
+ parallel_apply_groups: parallelApplyGroups,
62
+ serial_merge_groups: serialMergeGroups,
63
+ retry_policy: 'rebase_stale_context_then_requeue'
64
+ },
65
+ wall_clock_parallel_evidence: parallelApplyGroups.length ? [`parallel-001:${parallelEntries.length}_entries_can_apply_without_overlapping_paths`] : [],
66
+ blockers: allBlockedConflicts.map((conflict) => conflict.reason)
25
67
  };
68
+ if (opts.artifactsDir)
69
+ void writeAgentMergeCoordinatorArtifacts(opts.artifactsDir, result);
70
+ return result;
71
+ }
72
+ export async function writeAgentMergeCoordinatorArtifacts(artifactDir, report) {
73
+ await writeJsonAtomic(path.join(artifactDir, 'agent-merge-coordinator-report.json'), report);
74
+ await writeJsonAtomic(path.join(artifactDir, 'agent-patch-conflict-graph.json'), report.conflict_graph);
75
+ await writeJsonAtomic(path.join(artifactDir, 'agent-patch-apply-plan.json'), report.apply_plan);
76
+ await writeJsonAtomic(path.join(artifactDir, 'agent-patch-apply-order.json'), {
77
+ schema: 'sks.agent-patch-apply-order.v1',
78
+ order: report.apply_order
79
+ });
26
80
  }
27
81
  function mergeConflicts(writers) {
28
82
  const rows = [...writers.entries()].sort(([left], [right]) => left.localeCompare(right));
29
83
  const conflicts = [];
30
84
  for (let i = 0; i < rows.length; i += 1) {
31
- const [leftFile, leftAgents] = rows[i];
85
+ const [leftFile, leftItems] = rows[i];
32
86
  for (let j = i; j < rows.length; j += 1) {
33
- const [rightFile, rightAgents] = rows[j];
87
+ const [rightFile, rightItems] = rows[j];
34
88
  if (!pathsOverlap(leftFile, rightFile))
35
89
  continue;
36
- const agents = [...new Set([...leftAgents, ...rightAgents])];
37
- if (agents.length <= 1)
90
+ const entries = [...new Set([...leftItems, ...rightItems].map((item) => item.entry_id))];
91
+ if (entries.length <= 1)
38
92
  continue;
39
- conflicts.push({ file: leftFile === rightFile ? leftFile : `${leftFile}<->${rightFile}`, agents });
93
+ const agents = [...new Set([...leftItems, ...rightItems].map((item) => item.agent_id))];
94
+ const file = leftFile === rightFile ? leftFile : `${leftFile}<->${rightFile}`;
95
+ const reason = leftFile === rightFile ? `parallel_write_conflict:${file}` : `subtree_write_conflict:${file}`;
96
+ conflicts.push({ type: leftFile === rightFile ? 'path' : 'subtree', file, entries, agents, reason });
40
97
  }
41
98
  }
42
99
  return conflicts;
43
100
  }
101
+ function domainConflicts(items) {
102
+ const byPrediction = new Map();
103
+ for (const item of items) {
104
+ const prediction = item.envelope.lease_proof?.conflict_prediction_id;
105
+ if (!prediction)
106
+ continue;
107
+ const key = String(prediction);
108
+ if (!byPrediction.has(key))
109
+ byPrediction.set(key, []);
110
+ byPrediction.get(key)?.push(item);
111
+ }
112
+ return [...byPrediction.entries()].flatMap(([prediction, rows]) => {
113
+ const entries = [...new Set(rows.map((item) => item.entry_id))];
114
+ if (entries.length <= 1)
115
+ return [];
116
+ return [{
117
+ type: 'domain',
118
+ file: prediction,
119
+ entries,
120
+ agents: [...new Set(rows.map((item) => item.agent_id))],
121
+ reason: `domain_conflict:${prediction}`
122
+ }];
123
+ });
124
+ }
125
+ function normalizeInputs(inputs) {
126
+ return inputs.map((input, index) => {
127
+ if ('envelope' in input) {
128
+ return {
129
+ entry_id: input.id,
130
+ agent_id: input.agent_id,
131
+ lease_id: input.lease_id || input.envelope.lease_id || input.envelope.lease_proof?.lease_id || null,
132
+ status: input.status,
133
+ envelope: input.envelope
134
+ };
135
+ }
136
+ return {
137
+ entry_id: `${input.agent_id}-${String(index + 1).padStart(4, '0')}`,
138
+ agent_id: input.agent_id,
139
+ lease_id: input.lease_id || input.lease_proof?.lease_id || null,
140
+ envelope: input
141
+ };
142
+ });
143
+ }
44
144
  function pathsOverlap(left, right) {
45
145
  return left === right || left.startsWith(`${right}/`) || right.startsWith(`${left}/`);
46
146
  }
@@ -49,4 +149,15 @@ function normalizePatchPath(value) {
49
149
  const compact = normalized.split('/').filter((part) => part && part !== '.').join('/');
50
150
  return compact || '.';
51
151
  }
152
+ const PROTECTED_PATH_RE = /^(?:\.codex\/|\.agents\/skills\/|\.codex\/agents\/|AGENTS\.md$|node_modules\/sneakoscope\/|\.sneakoscope\/.*policy.*\.json$)/;
153
+ function protectedPath(value) {
154
+ return PROTECTED_PATH_RE.test(value);
155
+ }
156
+ function pathAllowedByLease(operationPath, envelope) {
157
+ const allowedPaths = envelope.lease_proof?.allowed_paths;
158
+ if (!allowedPaths?.length)
159
+ return true;
160
+ const rel = normalizePatchPath(operationPath);
161
+ return allowedPaths.map(normalizePatchPath).some((allowed) => rel === allowed || rel.startsWith(`${allowed}/`));
162
+ }
52
163
  //# sourceMappingURL=agent-merge-coordinator.js.map