sneakoscope 1.18.0 → 1.18.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -2
- package/crates/sks-core/Cargo.lock +1 -1
- package/crates/sks-core/Cargo.toml +1 -1
- package/crates/sks-core/src/main.rs +1 -1
- package/dist/.sks-build-stamp.json +4 -4
- package/dist/bin/sks.js +1 -1
- package/dist/build-manifest.json +17 -9
- package/dist/commands/image-ux-review.d.ts +46 -1
- package/dist/commands/ppt.d.ts +46 -1
- package/dist/core/agents/agent-central-ledger.d.ts +1 -0
- package/dist/core/agents/agent-central-ledger.js +4 -3
- package/dist/core/agents/agent-codex-cockpit.d.ts +7 -0
- package/dist/core/agents/agent-codex-cockpit.js +31 -1
- package/dist/core/agents/agent-janitor.d.ts +3 -0
- package/dist/core/agents/agent-janitor.js +32 -0
- package/dist/core/agents/agent-ledger-schemas.d.ts +162 -0
- package/dist/core/agents/agent-ledger-schemas.js +23 -0
- package/dist/core/agents/agent-lifecycle.js +40 -16
- package/dist/core/agents/agent-orchestrator.d.ts +46 -1
- package/dist/core/agents/agent-orchestrator.js +75 -21
- package/dist/core/agents/agent-output-validator.d.ts +16 -0
- package/dist/core/agents/agent-output-validator.js +12 -0
- package/dist/core/agents/agent-proof-evidence.d.ts +19 -0
- package/dist/core/agents/agent-proof-evidence.js +42 -1
- package/dist/core/agents/agent-runner-codex-exec.d.ts +2 -0
- package/dist/core/agents/agent-runner-codex-exec.js +6 -6
- package/dist/core/agents/agent-runner-fake.js +2 -0
- package/dist/core/agents/agent-runner-process.js +10 -4
- package/dist/core/agents/agent-runner-tmux.d.ts +1 -32
- package/dist/core/agents/agent-runner-tmux.js +83 -12
- package/dist/core/agents/agent-scheduler.d.ts +77 -0
- package/dist/core/agents/agent-scheduler.js +295 -0
- package/dist/core/agents/agent-schema.d.ts +2 -0
- package/dist/core/agents/agent-session-generation.d.ts +55 -0
- package/dist/core/agents/agent-session-generation.js +110 -0
- package/dist/core/agents/agent-terminal-session.d.ts +10 -0
- package/dist/core/agents/agent-terminal-session.js +31 -8
- package/dist/core/agents/agent-trust-report.d.ts +5 -0
- package/dist/core/agents/agent-trust-report.js +9 -1
- package/dist/core/agents/agent-work-queue.d.ts +53 -0
- package/dist/core/agents/agent-work-queue.js +128 -0
- package/dist/core/agents/agent-worker-pipeline.d.ts +2 -0
- package/dist/core/agents/agent-worker-pipeline.js +4 -0
- package/dist/core/agents/agent-worker-slot.d.ts +35 -0
- package/dist/core/agents/agent-worker-slot.js +90 -0
- package/dist/core/agents/agent-wrongness.d.ts +1 -1
- package/dist/core/agents/agent-wrongness.js +2 -0
- package/dist/core/agents/route-collaboration-ledger.d.ts +46 -1
- package/dist/core/agents/tmux-right-lane-cockpit.d.ts +22 -0
- package/dist/core/agents/tmux-right-lane-cockpit.js +12 -2
- package/dist/core/commands/image-ux-review-command.d.ts +46 -1
- package/dist/core/commands/ppt-command.d.ts +46 -1
- package/dist/core/fsx.d.ts +1 -1
- package/dist/core/fsx.js +1 -1
- package/dist/core/mad-sks/immutable-harness-guard.js +2 -0
- package/dist/core/release-parallel-full-coverage.js +9 -0
- package/dist/core/version.d.ts +1 -1
- package/dist/core/version.js +1 -1
- package/package.json +10 -1
- package/schemas/codex/agent-result.schema.json +12 -0
package/README.md
CHANGED
|
@@ -10,7 +10,7 @@ SKS does not try to clone every other harness. It focuses on one thing: making C
|
|
|
10
10
|
|
|
11
11
|
## Current Release
|
|
12
12
|
|
|
13
|
-
SKS **1.18.
|
|
13
|
+
SKS **1.18.1** adds Dynamic Agent Pool replenishment: `agents=5` now means keep up to five active worker slots running until the work queue drains, with immutable session generations, generation-aware terminal close reports, real/fake-tmux pane launch evidence, and Source Intelligence / Goal mode refs propagated to every generation.
|
|
14
14
|
|
|
15
15
|
```bash
|
|
16
16
|
sks mad-sks plan --target-root <path> --json
|
|
@@ -612,7 +612,7 @@ npm run release:check
|
|
|
612
612
|
npm run publish:dry
|
|
613
613
|
```
|
|
614
614
|
|
|
615
|
-
`release:check` runs the 1.18.
|
|
615
|
+
`release:check` runs the 1.18.1 dynamic agent pool closure DAG, writes a source digest stamp under `.sneakoscope/reports/`, then refreshes release readiness so publish commands can verify the same stamp. The DAG preserves the 1.18 baseline gates and adds dynamic pool, backfill replenishment, scheduler proof, session generation, terminal generation, tmux real right-lane, dynamic cockpit, Source Intelligence propagation, and Goal mode propagation checks. Broader live or historical gates remain explicit scripts such as `release:real-check`. Generate the human-readable registry with `sks features inventory --write-docs`. Plain `npm publish` uses the `latest` dist-tag. npm's `prepublishOnly` verifies the fresh release stamp instead of rerunning the full gate, and `prepack` only rebuilds `dist`; publish no longer repeats the expensive release suite during packaging. `npm run publish:dry` remains the explicit dry-run helper.
|
|
616
616
|
|
|
617
617
|
Version bumps are manual. Run `sks versioning bump` only when preparing release metadata; SKS will not create `.git/hooks/pre-commit` or auto-bump during ordinary commits.
|
|
618
618
|
|
|
@@ -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.
|
|
7
|
+
Some("--version") => println!("sks-rs 1.18.1"),
|
|
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.
|
|
5
|
-
"source_digest": "
|
|
6
|
-
"source_file_count":
|
|
7
|
-
"built_at_source_time":
|
|
4
|
+
"package_version": "1.18.1",
|
|
5
|
+
"source_digest": "6df40e82722a56a4df2efafa956011204d7a96fd218b43d19aa10672bbd0842c",
|
|
6
|
+
"source_file_count": 1312,
|
|
7
|
+
"built_at_source_time": 1779722031788
|
|
8
8
|
}
|
package/dist/bin/sks.js
CHANGED
package/dist/build-manifest.json
CHANGED
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
{
|
|
2
2
|
"schema": "sks.dist-build.v2",
|
|
3
|
-
"version": "1.18.
|
|
4
|
-
"package_version": "1.18.
|
|
3
|
+
"version": "1.18.1",
|
|
4
|
+
"package_version": "1.18.1",
|
|
5
5
|
"typescript": true,
|
|
6
6
|
"mjs_runtime_files": 0,
|
|
7
|
-
"compiled_file_count":
|
|
8
|
-
"compiled_js_count":
|
|
9
|
-
"compiled_dts_count":
|
|
10
|
-
"source_digest": "
|
|
11
|
-
"source_file_count":
|
|
12
|
-
"source_files_hash": "
|
|
13
|
-
"source_list_hash": "
|
|
7
|
+
"compiled_file_count": 858,
|
|
8
|
+
"compiled_js_count": 429,
|
|
9
|
+
"compiled_dts_count": 429,
|
|
10
|
+
"source_digest": "6df40e82722a56a4df2efafa956011204d7a96fd218b43d19aa10672bbd0842c",
|
|
11
|
+
"source_file_count": 1312,
|
|
12
|
+
"source_files_hash": "fbe2aa994acefbdab214a17074d539960fa99369b928c3679442019ace20e7c6",
|
|
13
|
+
"source_list_hash": "fbe2aa994acefbdab214a17074d539960fa99369b928c3679442019ace20e7c6",
|
|
14
14
|
"src_mjs_runtime_files": 0,
|
|
15
15
|
"dist_stamp_schema": "sks.dist-build-stamp.v1",
|
|
16
16
|
"files": [
|
|
@@ -234,8 +234,12 @@
|
|
|
234
234
|
"core/agents/agent-runner-process.js",
|
|
235
235
|
"core/agents/agent-runner-tmux.d.ts",
|
|
236
236
|
"core/agents/agent-runner-tmux.js",
|
|
237
|
+
"core/agents/agent-scheduler.d.ts",
|
|
238
|
+
"core/agents/agent-scheduler.js",
|
|
237
239
|
"core/agents/agent-schema.d.ts",
|
|
238
240
|
"core/agents/agent-schema.js",
|
|
241
|
+
"core/agents/agent-session-generation.d.ts",
|
|
242
|
+
"core/agents/agent-session-generation.js",
|
|
239
243
|
"core/agents/agent-session-rows.d.ts",
|
|
240
244
|
"core/agents/agent-session-rows.js",
|
|
241
245
|
"core/agents/agent-task-slicer.d.ts",
|
|
@@ -246,8 +250,12 @@
|
|
|
246
250
|
"core/agents/agent-trust-report.js",
|
|
247
251
|
"core/agents/agent-work-partition.d.ts",
|
|
248
252
|
"core/agents/agent-work-partition.js",
|
|
253
|
+
"core/agents/agent-work-queue.d.ts",
|
|
254
|
+
"core/agents/agent-work-queue.js",
|
|
249
255
|
"core/agents/agent-worker-pipeline.d.ts",
|
|
250
256
|
"core/agents/agent-worker-pipeline.js",
|
|
257
|
+
"core/agents/agent-worker-slot.d.ts",
|
|
258
|
+
"core/agents/agent-worker-slot.js",
|
|
251
259
|
"core/agents/agent-wrongness.d.ts",
|
|
252
260
|
"core/agents/agent-wrongness.js",
|
|
253
261
|
"core/agents/route-collaboration-ledger.d.ts",
|
|
@@ -436,6 +436,28 @@ export declare function run(command: any, args?: any): Promise<void | {
|
|
|
436
436
|
lease_count: number;
|
|
437
437
|
blockers: string[];
|
|
438
438
|
};
|
|
439
|
+
scheduler: {
|
|
440
|
+
schema: string;
|
|
441
|
+
ok: boolean;
|
|
442
|
+
state: import("../core/agents/agent-scheduler.js").AgentSchedulerState;
|
|
443
|
+
queue: import("../core/agents/agent-work-queue.js").AgentWorkQueue;
|
|
444
|
+
slots: import("../core/agents/agent-worker-slot.js").AgentWorkerSlot[];
|
|
445
|
+
results: any[];
|
|
446
|
+
};
|
|
447
|
+
source_intelligence: {
|
|
448
|
+
artifact: string;
|
|
449
|
+
ok: boolean;
|
|
450
|
+
mode: import("../core/source-intelligence/source-intelligence-policy.js").SourceIntelligenceMode;
|
|
451
|
+
cache_key: string;
|
|
452
|
+
proof_ok: boolean;
|
|
453
|
+
};
|
|
454
|
+
goal_mode: {
|
|
455
|
+
artifact: string;
|
|
456
|
+
ok: boolean;
|
|
457
|
+
mode: "official_goal_default" | "sks_goal_fallback";
|
|
458
|
+
official_goal_available: boolean;
|
|
459
|
+
default_enabled: boolean;
|
|
460
|
+
};
|
|
439
461
|
results: any[];
|
|
440
462
|
consensus: {
|
|
441
463
|
schema: string;
|
|
@@ -526,6 +548,11 @@ export declare function run(command: any, args?: any): Promise<void | {
|
|
|
526
548
|
all_sessions_closed: boolean;
|
|
527
549
|
terminal_sessions_closed: any;
|
|
528
550
|
terminal_close_report: string;
|
|
551
|
+
target_active_slots: any;
|
|
552
|
+
max_observed_active_slots: any;
|
|
553
|
+
backfill_count: any;
|
|
554
|
+
pending_queue_drained: any;
|
|
555
|
+
generation_count: any;
|
|
529
556
|
tmux_attach_command: string | null;
|
|
530
557
|
tmux_lane_manifest: string;
|
|
531
558
|
output_schema_ok: boolean;
|
|
@@ -542,7 +569,7 @@ export declare function run(command: any, args?: any): Promise<void | {
|
|
|
542
569
|
generated_at: string;
|
|
543
570
|
records: {
|
|
544
571
|
schema: string;
|
|
545
|
-
kind: "xai_available_not_used" | "context7_missing" | "codex_web_search_missing" | "recursion_attempt" | "lease_conflict" | "session_not_closed" | "terminal_missing" | "terminal_not_closed" | "schema_invalid_output" | "stale_heartbeat" | "legacy_multiagent_runtime_usage_attempt";
|
|
572
|
+
kind: "xai_available_not_used" | "context7_missing" | "codex_web_search_missing" | "recursion_attempt" | "lease_conflict" | "session_not_closed" | "terminal_missing" | "terminal_not_closed" | "scheduler_starvation" | "session_generation_missing" | "schema_invalid_output" | "stale_heartbeat" | "legacy_multiagent_runtime_usage_attempt";
|
|
546
573
|
blocker: string;
|
|
547
574
|
created_at: string;
|
|
548
575
|
status: string;
|
|
@@ -564,9 +591,27 @@ export declare function run(command: any, args?: any): Promise<void | {
|
|
|
564
591
|
closed_session_count: number;
|
|
565
592
|
terminal_sessions_closed: boolean;
|
|
566
593
|
terminal_session_count: number;
|
|
594
|
+
terminal_generation_count: number;
|
|
595
|
+
terminal_close_report_count: number;
|
|
567
596
|
terminal_close_report: string;
|
|
597
|
+
session_generation_count: number;
|
|
598
|
+
all_generations_closed: boolean;
|
|
599
|
+
scheduler_state: string;
|
|
600
|
+
target_active_slots: any;
|
|
601
|
+
max_observed_active_slots: any;
|
|
602
|
+
pending_queue_drained: boolean;
|
|
603
|
+
backfill_count: any;
|
|
604
|
+
expected_backfill_count: any;
|
|
605
|
+
slot_count: any;
|
|
606
|
+
generation_count: number;
|
|
607
|
+
all_slots_closed_after_drain: boolean;
|
|
608
|
+
generated_work_item_count: any;
|
|
609
|
+
source_intelligence_generation_refs_ok: boolean;
|
|
610
|
+
goal_mode_generation_refs_ok: boolean;
|
|
568
611
|
tmux_lane_manifest: string;
|
|
569
612
|
tmux_lane_manifest_ok: boolean;
|
|
613
|
+
tmux_pane_launch_ledger: string;
|
|
614
|
+
tmux_pane_launch_count: number;
|
|
570
615
|
ledger_hash_chain_ok: boolean;
|
|
571
616
|
no_overlap_ok: boolean;
|
|
572
617
|
consensus_ok: boolean;
|
package/dist/commands/ppt.d.ts
CHANGED
|
@@ -288,6 +288,28 @@ export declare function run(command: any, args?: any): Promise<void | {
|
|
|
288
288
|
lease_count: number;
|
|
289
289
|
blockers: string[];
|
|
290
290
|
};
|
|
291
|
+
scheduler: {
|
|
292
|
+
schema: string;
|
|
293
|
+
ok: boolean;
|
|
294
|
+
state: import("../core/agents/agent-scheduler.js").AgentSchedulerState;
|
|
295
|
+
queue: import("../core/agents/agent-work-queue.js").AgentWorkQueue;
|
|
296
|
+
slots: import("../core/agents/agent-worker-slot.js").AgentWorkerSlot[];
|
|
297
|
+
results: any[];
|
|
298
|
+
};
|
|
299
|
+
source_intelligence: {
|
|
300
|
+
artifact: string;
|
|
301
|
+
ok: boolean;
|
|
302
|
+
mode: import("../core/source-intelligence/source-intelligence-policy.js").SourceIntelligenceMode;
|
|
303
|
+
cache_key: string;
|
|
304
|
+
proof_ok: boolean;
|
|
305
|
+
};
|
|
306
|
+
goal_mode: {
|
|
307
|
+
artifact: string;
|
|
308
|
+
ok: boolean;
|
|
309
|
+
mode: "official_goal_default" | "sks_goal_fallback";
|
|
310
|
+
official_goal_available: boolean;
|
|
311
|
+
default_enabled: boolean;
|
|
312
|
+
};
|
|
291
313
|
results: any[];
|
|
292
314
|
consensus: {
|
|
293
315
|
schema: string;
|
|
@@ -378,6 +400,11 @@ export declare function run(command: any, args?: any): Promise<void | {
|
|
|
378
400
|
all_sessions_closed: boolean;
|
|
379
401
|
terminal_sessions_closed: any;
|
|
380
402
|
terminal_close_report: string;
|
|
403
|
+
target_active_slots: any;
|
|
404
|
+
max_observed_active_slots: any;
|
|
405
|
+
backfill_count: any;
|
|
406
|
+
pending_queue_drained: any;
|
|
407
|
+
generation_count: any;
|
|
381
408
|
tmux_attach_command: string | null;
|
|
382
409
|
tmux_lane_manifest: string;
|
|
383
410
|
output_schema_ok: boolean;
|
|
@@ -394,7 +421,7 @@ export declare function run(command: any, args?: any): Promise<void | {
|
|
|
394
421
|
generated_at: string;
|
|
395
422
|
records: {
|
|
396
423
|
schema: string;
|
|
397
|
-
kind: "xai_available_not_used" | "context7_missing" | "codex_web_search_missing" | "recursion_attempt" | "lease_conflict" | "session_not_closed" | "terminal_missing" | "terminal_not_closed" | "schema_invalid_output" | "stale_heartbeat" | "legacy_multiagent_runtime_usage_attempt";
|
|
424
|
+
kind: "xai_available_not_used" | "context7_missing" | "codex_web_search_missing" | "recursion_attempt" | "lease_conflict" | "session_not_closed" | "terminal_missing" | "terminal_not_closed" | "scheduler_starvation" | "session_generation_missing" | "schema_invalid_output" | "stale_heartbeat" | "legacy_multiagent_runtime_usage_attempt";
|
|
398
425
|
blocker: string;
|
|
399
426
|
created_at: string;
|
|
400
427
|
status: string;
|
|
@@ -416,9 +443,27 @@ export declare function run(command: any, args?: any): Promise<void | {
|
|
|
416
443
|
closed_session_count: number;
|
|
417
444
|
terminal_sessions_closed: boolean;
|
|
418
445
|
terminal_session_count: number;
|
|
446
|
+
terminal_generation_count: number;
|
|
447
|
+
terminal_close_report_count: number;
|
|
419
448
|
terminal_close_report: string;
|
|
449
|
+
session_generation_count: number;
|
|
450
|
+
all_generations_closed: boolean;
|
|
451
|
+
scheduler_state: string;
|
|
452
|
+
target_active_slots: any;
|
|
453
|
+
max_observed_active_slots: any;
|
|
454
|
+
pending_queue_drained: boolean;
|
|
455
|
+
backfill_count: any;
|
|
456
|
+
expected_backfill_count: any;
|
|
457
|
+
slot_count: any;
|
|
458
|
+
generation_count: number;
|
|
459
|
+
all_slots_closed_after_drain: boolean;
|
|
460
|
+
generated_work_item_count: any;
|
|
461
|
+
source_intelligence_generation_refs_ok: boolean;
|
|
462
|
+
goal_mode_generation_refs_ok: boolean;
|
|
420
463
|
tmux_lane_manifest: string;
|
|
421
464
|
tmux_lane_manifest_ok: boolean;
|
|
465
|
+
tmux_pane_launch_ledger: string;
|
|
466
|
+
tmux_pane_launch_count: number;
|
|
422
467
|
ledger_hash_chain_ok: boolean;
|
|
423
468
|
no_overlap_ok: boolean;
|
|
424
469
|
consensus_ok: boolean;
|
|
@@ -17,6 +17,7 @@ export declare function initializeAgentCentralLedger(missionDir: string, input:
|
|
|
17
17
|
partition?: any;
|
|
18
18
|
route?: string;
|
|
19
19
|
prompt?: string;
|
|
20
|
+
dynamicScheduler?: boolean;
|
|
20
21
|
}): Promise<string>;
|
|
21
22
|
export declare function appendAgentLedgerEvent(root: string, event: {
|
|
22
23
|
agent_id: string;
|
|
@@ -47,10 +47,11 @@ export function validateAgentLedgerWriteScope(input) {
|
|
|
47
47
|
const mode = input.mode || 'write';
|
|
48
48
|
const orchestrator = actor === 'orchestrator' || actor === 'parent_orchestrator';
|
|
49
49
|
const sessionMatch = target.match(/^sessions\/([^/]+)\.json$/);
|
|
50
|
+
const generationSessionMatch = target.match(/^sessions\/([^/]+)\/gen-\d+\/agent-session-record\.json$/);
|
|
50
51
|
const messageAppend = target === 'agent-messages.jsonl' && mode === 'append';
|
|
51
52
|
const eventAppend = target === 'agent-events.jsonl' && mode === 'append';
|
|
52
53
|
const handoffAppend = target === 'agent-handoffs.jsonl' && mode === 'append';
|
|
53
|
-
const ownSessionWrite = Boolean(sessionMatch && sessionMatch[1] === actor);
|
|
54
|
+
const ownSessionWrite = Boolean((sessionMatch && sessionMatch[1] === actor) || (generationSessionMatch && generationSessionMatch[1] === actor));
|
|
54
55
|
const orchestratorOnly = AGENT_ORCHESTRATOR_ONLY_FILES.includes(target) || target === 'agent-sessions.json';
|
|
55
56
|
if (orchestrator)
|
|
56
57
|
return { ok: true, reason: 'orchestrator_write_allowed', actor_agent_id: actor, target_path: target, mode };
|
|
@@ -58,7 +59,7 @@ export function validateAgentLedgerWriteScope(input) {
|
|
|
58
59
|
return { ok: true, reason: 'own_session_record_allowed', actor_agent_id: actor, target_path: target, mode };
|
|
59
60
|
if (messageAppend || eventAppend || handoffAppend)
|
|
60
61
|
return { ok: true, reason: 'central_append_allowed', actor_agent_id: actor, target_path: target, mode };
|
|
61
|
-
if (sessionMatch && sessionMatch[1] !== actor)
|
|
62
|
+
if ((sessionMatch && sessionMatch[1] !== actor) || (generationSessionMatch && generationSessionMatch[1] !== actor))
|
|
62
63
|
return { ok: false, reason: 'agent_cannot_modify_other_session_record', actor_agent_id: actor, target_path: target, mode };
|
|
63
64
|
if (orchestratorOnly)
|
|
64
65
|
return { ok: false, reason: 'agent_cannot_modify_orchestrator_only_file', actor_agent_id: actor, target_path: target, mode };
|
|
@@ -71,7 +72,7 @@ export async function initializeAgentCentralLedger(missionDir, input) {
|
|
|
71
72
|
await writeTextAtomic(path.join(root, 'agent-events.jsonl'), '');
|
|
72
73
|
await writeTextAtomic(path.join(root, 'agent-messages.jsonl'), '');
|
|
73
74
|
await writeTextAtomic(path.join(root, 'agent-handoffs.jsonl'), '');
|
|
74
|
-
const sessions = Object.fromEntries((input.roster.roster || []).map((agent) => [agent.id, {
|
|
75
|
+
const sessions = input.dynamicScheduler ? {} : Object.fromEntries((input.roster.roster || []).map((agent) => [agent.id, {
|
|
75
76
|
agent_id: agent.id,
|
|
76
77
|
session_id: agent.session_id,
|
|
77
78
|
status: 'pending',
|
|
@@ -35,6 +35,13 @@ export interface AgentCodexCockpitState {
|
|
|
35
35
|
goal_mode_status: string | null;
|
|
36
36
|
terminal_session_status: string | null;
|
|
37
37
|
tmux_attach_command: string | null;
|
|
38
|
+
target_active_slots: number | null;
|
|
39
|
+
active_slot_count: number | null;
|
|
40
|
+
pending_queue_count: number | null;
|
|
41
|
+
backfill_count: number | null;
|
|
42
|
+
scheduler_status: string | null;
|
|
43
|
+
worker_slots: Array<Record<string, unknown>>;
|
|
44
|
+
session_generations: Array<Record<string, unknown>>;
|
|
38
45
|
blockers: string[];
|
|
39
46
|
agents: Array<Record<string, unknown>>;
|
|
40
47
|
recent_events: string[];
|
|
@@ -37,6 +37,9 @@ export async function buildAgentCodexCockpitState(missionDir, opts = {}) {
|
|
|
37
37
|
const sourceIntelligence = await readJson(path.join(missionDir, 'source-intelligence-evidence.json'), null);
|
|
38
38
|
const goalMode = await readJson(path.join(missionDir, 'goal-mode-applied.json'), null);
|
|
39
39
|
const tmuxLayout = await readJson(path.join(root, 'agent-tmux-layout.json'), null);
|
|
40
|
+
const scheduler = await readJson(path.join(root, 'agent-scheduler-state.json'), null);
|
|
41
|
+
const workerSlots = await readJson(path.join(root, 'agent-worker-slots.json'), null);
|
|
42
|
+
const generations = await readJson(path.join(root, 'agent-session-generations.json'), null);
|
|
40
43
|
const terminalClosed = proof?.terminal_sessions_closed === true;
|
|
41
44
|
const eventsTail = await readTailLines(path.join(root, 'agent-events.jsonl'), 8);
|
|
42
45
|
const cockpitEventsTail = await readTailLines(path.join(root, AGENT_CODEX_COCKPIT_EVENTS), 8);
|
|
@@ -68,6 +71,13 @@ export async function buildAgentCodexCockpitState(missionDir, opts = {}) {
|
|
|
68
71
|
goal_mode_status: goalMode?.mode || null,
|
|
69
72
|
terminal_session_status: terminalClosed ? 'closed' : proof ? 'blocked_or_unverified' : null,
|
|
70
73
|
tmux_attach_command: tmuxLayout?.attach_command || null,
|
|
74
|
+
target_active_slots: scheduler?.target_active_slots ?? null,
|
|
75
|
+
active_slot_count: scheduler?.active_slot_count ?? null,
|
|
76
|
+
pending_queue_count: scheduler?.pending_count ?? null,
|
|
77
|
+
backfill_count: scheduler?.backfill_count ?? null,
|
|
78
|
+
scheduler_status: scheduler?.status || null,
|
|
79
|
+
worker_slots: Array.isArray(workerSlots?.slots) ? workerSlots.slots : [],
|
|
80
|
+
session_generations: generations?.generations ? Object.values(generations.generations) : [],
|
|
71
81
|
blockers,
|
|
72
82
|
agents,
|
|
73
83
|
recent_events: [...eventsTail, ...cockpitEventsTail, ...teamTail].slice(-12),
|
|
@@ -98,6 +108,11 @@ export function renderAgentCodexDashboard(state) {
|
|
|
98
108
|
`- Terminal sessions: ${state.terminal_session_status || 'unknown'}`,
|
|
99
109
|
`- tmux attach: ${state.tmux_attach_command || 'unknown'}`,
|
|
100
110
|
`- All sessions closed: ${state.all_sessions_closed ?? 'unknown'}`,
|
|
111
|
+
`- Scheduler: ${state.scheduler_status || 'unknown'}`,
|
|
112
|
+
`- Target active slots: ${state.target_active_slots ?? 'unknown'}`,
|
|
113
|
+
`- Active slots: ${state.active_slot_count ?? 'unknown'}`,
|
|
114
|
+
`- Pending queue: ${state.pending_queue_count ?? 'unknown'}`,
|
|
115
|
+
`- Backfill events: ${state.backfill_count ?? 'unknown'}`,
|
|
101
116
|
'',
|
|
102
117
|
'| Agent | Persona | Task | State | Heartbeat age | Lease | Blockers | Artifact |',
|
|
103
118
|
'| --- | --- | --- | --- | --- | --- | --- | --- |',
|
|
@@ -116,6 +131,14 @@ export function renderAgentCodexDashboard(state) {
|
|
|
116
131
|
return `${[...header, ...rows].join('\n')}\n`;
|
|
117
132
|
}
|
|
118
133
|
export function renderAgentSessionCards(state) {
|
|
134
|
+
const slotBlocks = state.worker_slots.map((slot) => [
|
|
135
|
+
`## ${cell(slot.slot_id)}`,
|
|
136
|
+
'',
|
|
137
|
+
`- Status: ${cell(slot.status)}`,
|
|
138
|
+
`- Current generation: ${cell(slot.current_generation_index)}`,
|
|
139
|
+
`- Current session: ${cell(slot.current_session_id)}`,
|
|
140
|
+
`- Generation count: ${cell(slot.generation_count)}`,
|
|
141
|
+
].join('\n'));
|
|
119
142
|
const blocks = state.agents.map((agent) => [
|
|
120
143
|
`## ${cell(agent.id)}`,
|
|
121
144
|
'',
|
|
@@ -125,7 +148,7 @@ export function renderAgentSessionCards(state) {
|
|
|
125
148
|
`- Lease: ${cell(agent.lease || agent.lease_id || agent.write_policy)}`,
|
|
126
149
|
`- Artifact: ${cell(agent.output_artifact || agent.artifact || '')}`,
|
|
127
150
|
].join('\n'));
|
|
128
|
-
return `# Agent Session Cards\n\n${blocks.join('\n\n')}\n`;
|
|
151
|
+
return `# Agent Session Cards\n\n${[...slotBlocks, ...blocks].join('\n\n')}\n`;
|
|
129
152
|
}
|
|
130
153
|
export function renderAgentProgressTimeline(state) {
|
|
131
154
|
return `# Agent Progress Timeline\n\n${state.recent_events.map((line) => `- ${line}`).join('\n')}\n`;
|
|
@@ -140,6 +163,13 @@ function summarizeLiveState(state) {
|
|
|
140
163
|
agent_count: state.agent_count,
|
|
141
164
|
concurrency: state.concurrency,
|
|
142
165
|
active_agents: state.agents.filter((agent) => !['closed', 'done', 'completed'].includes(String(agent.status || ''))).length,
|
|
166
|
+
target_active_slots: state.target_active_slots,
|
|
167
|
+
active_slot_count: state.active_slot_count,
|
|
168
|
+
pending_queue_count: state.pending_queue_count,
|
|
169
|
+
backfill_count: state.backfill_count,
|
|
170
|
+
scheduler_status: state.scheduler_status,
|
|
171
|
+
worker_slot_count: state.worker_slots.length,
|
|
172
|
+
session_generation_count: state.session_generations.length,
|
|
143
173
|
proof_status: state.proof_status,
|
|
144
174
|
source_intelligence_status: state.source_intelligence_status,
|
|
145
175
|
xai_status: state.xai_status,
|
|
@@ -7,6 +7,9 @@ export interface AgentJanitorReport {
|
|
|
7
7
|
stale_heartbeat_sessions: string[];
|
|
8
8
|
zombie_process_sessions: string[];
|
|
9
9
|
stale_tmux_sessions: string[];
|
|
10
|
+
active_generation_sessions: string[];
|
|
11
|
+
orphan_generation_dirs: string[];
|
|
12
|
+
slot_generation_cleanup: string[];
|
|
10
13
|
orphan_temp_dirs: string[];
|
|
11
14
|
stale_locks: string[];
|
|
12
15
|
cleaned: string[];
|
|
@@ -9,6 +9,11 @@ export async function runAgentJanitor(input) {
|
|
|
9
9
|
const namespace = await readJson(path.join(input.missionDir, 'project-session-namespace.json'), null);
|
|
10
10
|
const projectHash = input.projectHash || namespace?.root_hash || null;
|
|
11
11
|
const rows = normalizeAgentSessionRows(sessions);
|
|
12
|
+
const generations = await readJson(path.join(agentRoot, 'agent-session-generations.json'), null);
|
|
13
|
+
const generationRows = generations?.generations ? Object.values(generations.generations) : [];
|
|
14
|
+
const activeGenerationSessions = generationRows
|
|
15
|
+
.filter((row) => !row.closed_at && ['running', 'launching', 'collecting'].includes(String(row.status || 'running')))
|
|
16
|
+
.map((row) => String(row.session_id));
|
|
12
17
|
const now = Date.now();
|
|
13
18
|
const staleHeartbeat = rows
|
|
14
19
|
.filter((row) => {
|
|
@@ -30,10 +35,15 @@ export async function runAgentJanitor(input) {
|
|
|
30
35
|
}
|
|
31
36
|
const zombieProcesses = await detectZombieProcessSessions(agentRoot, statusByAgent, statusBySession);
|
|
32
37
|
const staleTmuxSessions = await detectStaleTmuxSessions(agentRoot, staleMs);
|
|
38
|
+
const orphanGenerationDirs = await detectOrphanGenerationDirs(agentRoot, new Set(generationRows.map((row) => String(row.artifact_dir || ''))));
|
|
33
39
|
const orphanTempDirs = await scopedExistingPaths(Array.isArray(namespace?.orphan_temp_dirs) ? namespace.orphan_temp_dirs : [], projectHash);
|
|
34
40
|
const staleLocks = await scopedStaleLockPaths(namespace?.lock_dir ? [namespace.lock_dir] : [], projectHash, staleMs);
|
|
35
41
|
const cleaned = [];
|
|
36
42
|
if (input.cleanup) {
|
|
43
|
+
for (const dir of orphanGenerationDirs) {
|
|
44
|
+
await fsp.rm(path.join(agentRoot, dir), { recursive: true, force: true }).catch(() => { });
|
|
45
|
+
cleaned.push(path.join(agentRoot, dir));
|
|
46
|
+
}
|
|
37
47
|
for (const dir of orphanTempDirs) {
|
|
38
48
|
await fsp.rm(dir, { recursive: true, force: true }).catch(() => { });
|
|
39
49
|
cleaned.push(dir);
|
|
@@ -54,6 +64,9 @@ export async function runAgentJanitor(input) {
|
|
|
54
64
|
stale_heartbeat_sessions: staleHeartbeat,
|
|
55
65
|
zombie_process_sessions: zombieProcesses,
|
|
56
66
|
stale_tmux_sessions: staleTmuxSessions,
|
|
67
|
+
active_generation_sessions: activeGenerationSessions,
|
|
68
|
+
orphan_generation_dirs: orphanGenerationDirs,
|
|
69
|
+
slot_generation_cleanup: cleaned.filter((entry) => entry.includes(`${path.sep}sessions${path.sep}`)),
|
|
57
70
|
orphan_temp_dirs: orphanTempDirs,
|
|
58
71
|
stale_locks: staleLocks,
|
|
59
72
|
cleaned,
|
|
@@ -62,6 +75,25 @@ export async function runAgentJanitor(input) {
|
|
|
62
75
|
await writeAgentJanitorReport(input.missionDir, report);
|
|
63
76
|
return report;
|
|
64
77
|
}
|
|
78
|
+
async function detectOrphanGenerationDirs(agentRoot, knownGenerationDirs) {
|
|
79
|
+
const sessionsDir = path.join(agentRoot, 'sessions');
|
|
80
|
+
const out = [];
|
|
81
|
+
if (!(await exists(sessionsDir)))
|
|
82
|
+
return out;
|
|
83
|
+
for (const slot of await fsp.readdir(sessionsDir, { withFileTypes: true }).catch(() => [])) {
|
|
84
|
+
if (!slot.isDirectory())
|
|
85
|
+
continue;
|
|
86
|
+
const slotDir = path.join(sessionsDir, slot.name);
|
|
87
|
+
for (const gen of await fsp.readdir(slotDir, { withFileTypes: true }).catch(() => [])) {
|
|
88
|
+
if (!gen.isDirectory() || !/^gen-\d+$/.test(gen.name))
|
|
89
|
+
continue;
|
|
90
|
+
const rel = path.join('sessions', slot.name, gen.name);
|
|
91
|
+
if (!knownGenerationDirs.has(rel))
|
|
92
|
+
out.push(rel);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
return out;
|
|
96
|
+
}
|
|
65
97
|
export async function writeAgentJanitorReport(missionDir, report) {
|
|
66
98
|
await writeJsonAtomic(path.join(missionDir, 'agents', 'agent-janitor-report.json'), report);
|
|
67
99
|
}
|