sneakoscope 4.8.5 → 4.8.6
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 +3 -3
- 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/bin/sks.js +1 -1
- package/dist/cli/command-registry.js +1 -0
- package/dist/core/agents/agent-central-ledger.js +10 -5
- package/dist/core/agents/agent-orchestrator.js +22 -1
- package/dist/core/agents/agent-runner-codex-exec.js +24 -0
- package/dist/core/agents/fast-mode-policy.js +3 -4
- package/dist/core/agents/native-cli-session-proof.js +1 -1
- package/dist/core/agents/native-cli-worker.js +73 -113
- package/dist/core/agents/worker-pane-communication-contract.js +2 -3
- package/dist/core/codex/codex-cli-syntax-builder.js +2 -0
- package/dist/core/codex-app/codex-init-deep.js +10 -3
- package/dist/core/codex-control/codex-reliability-shield.js +36 -17
- package/dist/core/codex-control/codex-sdk-adapter.js +2 -2
- package/dist/core/codex-control/codex-sdk-config-policy.js +4 -0
- package/dist/core/codex-control/codex-task-runner.js +23 -11
- package/dist/core/codex-native/core-skill-manifest.js +2 -1
- package/dist/core/commands/gates-command.js +58 -0
- package/dist/core/commands/naruto-command.js +1 -14
- package/dist/core/commands/pipeline-command.js +2 -2
- package/dist/core/commands/release-command.js +5 -5
- package/dist/core/commands/status-command.js +2 -3
- package/dist/core/commands/stop-gate-command.js +1 -1
- package/dist/core/fsx.js +1 -1
- package/dist/core/hooks-runtime/team-digest.js +86 -0
- package/dist/core/hooks-runtime.js +5 -90
- package/dist/core/init/skills.js +395 -0
- package/dist/core/init.js +2 -376
- package/dist/core/lean-engineering-policy.js +2 -1
- package/dist/core/mission.js +4 -41
- package/dist/core/naruto/naruto-active-pool.js +36 -7
- package/dist/core/naruto/naruto-real-worker-child.js +67 -11
- package/dist/core/permission-gates.js +45 -19
- package/dist/core/pipeline-internals/runtime-core.js +7 -10
- package/dist/core/pipeline-internals/runtime-gates.js +28 -62
- package/dist/core/ppt/artifacts.js +19 -0
- package/dist/core/ppt/html.js +96 -0
- package/dist/core/ppt/style-tokens.js +248 -0
- package/dist/core/ppt.js +6 -352
- package/dist/core/qa-loop/{qa-contract-v2.js → qa-contract.js} +1 -1
- package/dist/core/qa-loop/{qa-gate-v2.js → qa-gate.js} +1 -1
- package/dist/core/qa-loop/qa-runtime-artifacts.js +2 -2
- package/dist/core/qa-loop.js +11 -4
- package/dist/core/recallpulse/policy.js +226 -0
- package/dist/core/recallpulse.js +2 -225
- package/dist/core/release/release-gate-dag.js +6 -2
- package/dist/core/research/mock-result.js +406 -0
- package/dist/core/research/prompt.js +15 -0
- package/dist/core/research.js +2 -402
- package/dist/core/routes/constants.js +48 -0
- package/dist/core/routes/design-policy.js +37 -0
- package/dist/core/routes/evidence.js +77 -0
- package/dist/core/routes/ppt-policy.js +26 -0
- package/dist/core/routes.js +13 -190
- package/dist/core/safety/mutation-ledger.js +2 -0
- package/dist/core/safety/ssot-guard.js +2 -2
- package/dist/core/stop-gate/stop-gate-check.js +10 -17
- package/dist/core/stop-gate/stop-gate-resolver.js +0 -3
- package/dist/core/version.js +1 -1
- package/dist/core/zellij/zellij-slot-pane-renderer.js +15 -8
- package/dist/core/zellij/zellij-slot-telemetry.js +28 -12
- package/dist/scripts/check-pipeline-budget.js +1 -7
- package/dist/scripts/check-pipeline-runtime.js +1 -8
- package/dist/scripts/codex-control-all-pipelines-check.js +5 -14
- package/dist/scripts/codex-sdk-all-pipelines-check.js +5 -9
- package/dist/scripts/fast-codex-service-tier-proof-check.js +2 -2
- package/dist/scripts/gate-policy-audit-check.js +70 -0
- package/dist/scripts/official-docs-compat-report.js +2 -2
- package/dist/scripts/product-design-plugin-routing-check.js +10 -5
- package/dist/scripts/qa-loop-surface-router-check.js +2 -2
- package/dist/scripts/release-check-dynamic-execute.js +32 -35
- package/dist/scripts/release-check-dynamic.js +19 -28
- package/dist/scripts/release-check-stamp.js +2 -2
- package/dist/scripts/release-dag-full-coverage-check.js +57 -394
- package/dist/scripts/release-dynamic-performance-check.js +18 -19
- package/dist/scripts/release-dynamic-presets-check.js +2 -2
- package/dist/scripts/release-gate-dag-runner.js +3 -1
- package/dist/scripts/release-gate-existence-audit.js +59 -91
- package/dist/scripts/release-gate-planner.js +43 -23
- package/dist/scripts/release-metadata-1-19-check.js +100 -12
- package/dist/scripts/worker-pane-communication-contract-check.js +1 -2
- package/dist/scripts/zellij-slot-pane-renderer-check.js +3 -2
- package/package.json +13 -965
- package/dist/core/pipeline-runtime.js +0 -2
package/README.md
CHANGED
|
@@ -24,7 +24,7 @@
|
|
|
24
24
|
| 🍥 **`$Naruto` swarm** | Up to 100 parallel clone workers behind a hardware-safe concurrency governor, lease-based parallel writes, and a scheduler that backfills idle slots while runnable work remains |
|
|
25
25
|
| 🖥️ **Live Zellij cockpit** | Workers stack vertically as native Zellij stacked panes; each pane streams heartbeat, current file, tool events, and stdout tails every second |
|
|
26
26
|
| 🧾 **Completion Proof** | Every serious route ends with schema-backed proof artifacts — no "trust me, it's done" |
|
|
27
|
-
| 🛡️ **Safety gates** | DB/migration/destructive operations stay gated,
|
|
27
|
+
| 🛡️ **Safety gates** | DB/migration/destructive operations stay gated, ≤200 release-preset gates, Honest Mode |
|
|
28
28
|
| 🖼️ **Visual evidence** | Image Voxel TriWiki plus `$imagegen`/`gpt-image-2`-bound visual review flows |
|
|
29
29
|
|
|
30
30
|
## LLM Init Prompt for Codex Native Agent Builders
|
|
@@ -49,7 +49,7 @@ sks seo-geo-optimizer apply latest --mode seo --apply --json
|
|
|
49
49
|
sks seo-geo-optimizer audit --mode geo --target package --offline --json
|
|
50
50
|
```
|
|
51
51
|
|
|
52
|
-
> 📋 **Current release: `v4.8.
|
|
52
|
+
> 📋 **Current release: `v4.8.6`** — full release history lives in [CHANGELOG.md](CHANGELOG.md). This README documents how Sneakoscope works today, not its version-by-version changes. Release readiness is tracked in [docs/release-readiness.md](docs/release-readiness.md).
|
|
53
53
|
|
|
54
54
|
## 🍥 Parallelism, UX, And Integrations
|
|
55
55
|
|
|
@@ -763,7 +763,7 @@ npm run release:check
|
|
|
763
763
|
npm run publish:dry
|
|
764
764
|
```
|
|
765
765
|
|
|
766
|
-
`release:check` runs the change-aware affected release gate for ordinary local checks. Publish readiness uses `release:check:full`, which runs the full release DAG and writes a source digest stamp under `.sneakoscope/reports/` so publish commands can verify the same source/dist state. The
|
|
766
|
+
`release:check` runs the change-aware affected release gate for ordinary local checks. Publish readiness uses `release:check:full`, which runs the full release DAG and writes a source digest stamp under `.sneakoscope/reports/` so publish commands can verify the same source/dist state. The release preset is capped at 200 user-concern gates, with terminal harness gates split into `infra-harness-gates.json` and runnable through `sks gates run harness`. Gate commands execute directly from `release-gates.v2.json`; `package.json` keeps a small user-facing script surface. Broader live gates remain explicit commands; real Codex patch smoke, real Codex parallel worker proof, and real Zellij proof are optional unless their `SKS_REQUIRE_REAL_*` or `SKS_REQUIRE_ZELLIJ=1` environment variables are set.
|
|
767
767
|
|
|
768
768
|
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.
|
|
769
769
|
|
|
@@ -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 4.8.
|
|
7
|
+
Some("--version") => println!("sks-rs 4.8.6"),
|
|
8
8
|
Some("compact-info") => {
|
|
9
9
|
let mut input = String::new();
|
|
10
10
|
let _ = io::stdin().read_to_string(&mut input);
|
package/dist/bin/sks.js
CHANGED
|
@@ -78,6 +78,7 @@ export const COMMANDS = {
|
|
|
78
78
|
version: entry('stable', 'Show SKS version', 'dist/commands/version.js', directCommand(() => import('../commands/version.js'), 'dist/commands/version.js')),
|
|
79
79
|
commands: entry('stable', 'List SKS commands', 'dist/core/commands/basic-cli.js', basicArgs('commandsCommand')),
|
|
80
80
|
check: entry('stable', 'Run five-minute proof-bank affected checks', 'dist/core/commands/check-command.js', argsCommand(() => import('../core/commands/check-command.js'), 'checkCommand', 'dist/core/commands/check-command.js')),
|
|
81
|
+
gates: entry('stable', 'Run release gate DAG by gate id or preset', 'dist/core/commands/gates-command.js', argsCommand(() => import('../core/commands/gates-command.js'), 'gatesCommand', 'dist/core/commands/gates-command.js')),
|
|
81
82
|
task: entry('stable', 'Run an SLA-bounded SKS task check', 'dist/core/commands/task-command.js', argsCommand(() => import('../core/commands/task-command.js'), 'taskCommand', 'dist/core/commands/task-command.js')),
|
|
82
83
|
release: entry('stable', 'Run affected/full/background release gates', 'dist/core/commands/release-command.js', argsCommand(() => import('../core/commands/release-command.js'), 'releaseCommand', 'dist/core/commands/release-command.js')),
|
|
83
84
|
triwiki: entry('stable', 'Inspect TriWiki index, affected graph, and proof bank', 'dist/core/commands/triwiki-command.js', argsCommand(() => import('../core/commands/triwiki-command.js'), 'triwikiCommand', 'dist/core/commands/triwiki-command.js')),
|
|
@@ -2,7 +2,7 @@ import fs from 'node:fs/promises';
|
|
|
2
2
|
import path from 'node:path';
|
|
3
3
|
import crypto from 'node:crypto';
|
|
4
4
|
import { AGENT_LEDGER_EVENT_SCHEMA, AGENT_PROOF_EVIDENCE_SCHEMA } from './agent-schema.js';
|
|
5
|
-
import { ensureDir, nowIso, writeJsonAtomic, writeTextAtomic } from '../fsx.js';
|
|
5
|
+
import { appendJsonlBounded, ensureDir, nowIso, writeJsonAtomic, writeTextAtomic } from '../fsx.js';
|
|
6
6
|
import { redactSecrets } from '../secret-redaction.js';
|
|
7
7
|
const LEDGER_LOCKS = new Map();
|
|
8
8
|
export const AGENT_ORCHESTRATOR_ONLY_FILES = Object.freeze([
|
|
@@ -111,7 +111,7 @@ export async function appendAgentLedgerEvent(root, event) {
|
|
|
111
111
|
};
|
|
112
112
|
const current_hash = hashEntry(entryWithoutHash);
|
|
113
113
|
const entry = { ...entryWithoutHash, current_hash };
|
|
114
|
-
await
|
|
114
|
+
await appendJsonlBounded(file, entry, 5 * 1024 * 1024);
|
|
115
115
|
return entry;
|
|
116
116
|
});
|
|
117
117
|
}
|
|
@@ -123,15 +123,20 @@ export async function validateAgentLedgerHashChain(root) {
|
|
|
123
123
|
let expectedSequence = 1;
|
|
124
124
|
for (const line of lines) {
|
|
125
125
|
const entry = JSON.parse(line);
|
|
126
|
+
if (entry?.type === 'log.rotated') {
|
|
127
|
+
expectedSequence = 0;
|
|
128
|
+
previousHash = null;
|
|
129
|
+
continue;
|
|
130
|
+
}
|
|
126
131
|
const { current_hash, ...withoutHash } = entry;
|
|
127
|
-
if (entry.sequence !== expectedSequence)
|
|
132
|
+
if (expectedSequence > 0 && entry.sequence !== expectedSequence)
|
|
128
133
|
blockers.push('sequence_mismatch:' + entry.sequence + ':' + expectedSequence);
|
|
129
|
-
if ((entry.previous_hash || null) !== previousHash)
|
|
134
|
+
if (previousHash !== null && (entry.previous_hash || null) !== previousHash)
|
|
130
135
|
blockers.push('previous_hash_mismatch:' + entry.sequence);
|
|
131
136
|
if (hashEntry(withoutHash) !== current_hash)
|
|
132
137
|
blockers.push('current_hash_mismatch:' + entry.sequence);
|
|
133
138
|
previousHash = current_hash;
|
|
134
|
-
expectedSequence
|
|
139
|
+
expectedSequence = Number(entry.sequence || expectedSequence) + 1;
|
|
135
140
|
}
|
|
136
141
|
return { ok: blockers.length === 0, entries: lines.length, blockers };
|
|
137
142
|
}
|
|
@@ -66,6 +66,7 @@ import { applyGitWorktreeMergeQueue } from '../git/git-worktree-merge-queue.js';
|
|
|
66
66
|
import { crossRebaseIdleWorktrees } from '../git/git-worktree-cross-rebase.js';
|
|
67
67
|
import { gitOutputLine, runGitCommand } from '../git/git-worktree-runner.js';
|
|
68
68
|
import { writeParallelRuntimeProof } from './parallel-runtime-proof.js';
|
|
69
|
+
import { enforceRetention } from '../retention.js';
|
|
69
70
|
export async function runNativeAgentOrchestrator(opts = {}) {
|
|
70
71
|
const root = path.resolve(opts.root || process.cwd());
|
|
71
72
|
const prompt = String(opts.prompt || 'Native agent run');
|
|
@@ -345,6 +346,15 @@ export async function runNativeAgentOrchestrator(opts = {}) {
|
|
|
345
346
|
zellijVisiblePaneCap: Number(opts.zellijVisiblePaneCap || visualLaneCount || targetActiveSlots),
|
|
346
347
|
projectRoot: root
|
|
347
348
|
});
|
|
349
|
+
const schedulerHardTimeoutMs = normalizeMissionHardTimeoutMs(opts, route);
|
|
350
|
+
let lastTimeoutReapMs = 0;
|
|
351
|
+
async function reapTimedOutAgentSessions(force = false) {
|
|
352
|
+
const now = Date.now();
|
|
353
|
+
if (!force && now - lastTimeoutReapMs < 30000)
|
|
354
|
+
return null;
|
|
355
|
+
lastTimeoutReapMs = now;
|
|
356
|
+
return killTimedOutAgentSessions(ledgerRoot, now, { hardTimeoutMs: schedulerHardTimeoutMs });
|
|
357
|
+
}
|
|
348
358
|
await nativeCliSwarm.initialize();
|
|
349
359
|
await setCurrent(root, { mission_id: missionId, mode: 'AGENT', phase: 'AGENT_NATIVE_KERNEL_RUNNING', route_command: routeCommand, native_agent_backend: backend });
|
|
350
360
|
const scheduler = await runAgentScheduler({
|
|
@@ -361,6 +371,7 @@ export async function runNativeAgentOrchestrator(opts = {}) {
|
|
|
361
371
|
sourceIntelligenceRefs: sourceIntelligenceRef,
|
|
362
372
|
goalModeRef,
|
|
363
373
|
launchSession: async ({ agent, workItem }) => {
|
|
374
|
+
await reapTimedOutAgentSessions();
|
|
364
375
|
const slice = workItem.slice || { id: workItem.id, description: workItem.description || prompt };
|
|
365
376
|
const workerWorktree = await prepareWorkerGitWorktree({
|
|
366
377
|
root,
|
|
@@ -401,6 +412,7 @@ export async function runNativeAgentOrchestrator(opts = {}) {
|
|
|
401
412
|
const result = opts.nativeCliSwarm === false
|
|
402
413
|
? await runAgentByBackend(backend, runtimeAgent, runtimeSlice, backendOpts)
|
|
403
414
|
: await nativeCliSwarm.launchWorker({ agent: runtimeAgent, slice: runtimeSlice, opts: backendOpts });
|
|
415
|
+
await reapTimedOutAgentSessions();
|
|
404
416
|
if (route === '$Naruto')
|
|
405
417
|
attachNarutoRuntimeProof(result, runtimeAgent, runtimeSlice);
|
|
406
418
|
if (workerWorktree)
|
|
@@ -452,6 +464,7 @@ export async function runNativeAgentOrchestrator(opts = {}) {
|
|
|
452
464
|
return result;
|
|
453
465
|
},
|
|
454
466
|
onSchedulerEvent: async ({ event, slots, state }) => {
|
|
467
|
+
await reapTimedOutAgentSessions();
|
|
455
468
|
const paneBySlot = await readZellijPaneIdsBySlot(ledgerRoot);
|
|
456
469
|
const enrichedSlots = slots.map((slot) => ({ ...slot, pane_id: paneBySlot.get(slot.slot_id) || null, launch_status: paneBySlot.has(slot.slot_id) ? 'launched' : slot.status }));
|
|
457
470
|
await writeZellijRightLaneCockpit(ledgerRoot, { missionId, sessionName: `sks-${missionId}`, slots: enrichedSlots });
|
|
@@ -472,6 +485,7 @@ export async function runNativeAgentOrchestrator(opts = {}) {
|
|
|
472
485
|
}
|
|
473
486
|
}
|
|
474
487
|
});
|
|
488
|
+
await reapTimedOutAgentSessions(true);
|
|
475
489
|
await nativeCliSwarm.finalize();
|
|
476
490
|
const parallelRuntimeProof = await writeParallelRuntimeProof(ledgerRoot, missionId, {
|
|
477
491
|
requestedWorkers: Number(opts.agents || roster.agent_count || targetActiveSlots),
|
|
@@ -544,7 +558,7 @@ export async function runNativeAgentOrchestrator(opts = {}) {
|
|
|
544
558
|
const stale = await detectStaleAgentSessions(ledgerRoot);
|
|
545
559
|
if (!stale.ok)
|
|
546
560
|
await appendAgentLedgerEvent(ledgerRoot, { agent_id: 'orchestrator', session_id: 'orchestrator', event_type: 'stale_sessions_detected', payload: stale });
|
|
547
|
-
const timeoutKill = await killTimedOutAgentSessions(ledgerRoot);
|
|
561
|
+
const timeoutKill = await killTimedOutAgentSessions(ledgerRoot, Date.now(), { hardTimeoutMs: schedulerHardTimeoutMs });
|
|
548
562
|
const recursion = await writeAgentRecursionGuardReport(ledgerRoot, results);
|
|
549
563
|
const consensus = await writeAgentConsensus(ledgerRoot, results);
|
|
550
564
|
const outputValidation = await writeAgentOutputValidationReport(ledgerRoot, results);
|
|
@@ -612,6 +626,7 @@ export async function runNativeAgentOrchestrator(opts = {}) {
|
|
|
612
626
|
finalGptPatchStage
|
|
613
627
|
});
|
|
614
628
|
await writeAgentCodexCockpitArtifacts(dir, { missionId, projectHash: namespace.root_hash });
|
|
629
|
+
await enforceRetention(root, { afterRoute: true, completedMissionId: missionId, rotateLargeJsonl: true, lightweight: true }).catch(() => null);
|
|
615
630
|
await setCurrent(root, { mission_id: missionId, mode: 'AGENT', phase: proof.ok ? 'AGENT_NATIVE_KERNEL_DONE' : 'AGENT_NATIVE_KERNEL_BLOCKED', native_agent_backend: backend, updated_at: nowIso() });
|
|
616
631
|
return {
|
|
617
632
|
schema: 'sks.agent-run.v1',
|
|
@@ -659,6 +674,12 @@ export async function runNativeAgentOrchestrator(opts = {}) {
|
|
|
659
674
|
proof
|
|
660
675
|
};
|
|
661
676
|
}
|
|
677
|
+
function normalizeMissionHardTimeoutMs(opts = {}, route = '') {
|
|
678
|
+
const raw = Number(opts.hardTimeoutMs || opts.agentHardTimeoutMs || process.env.SKS_AGENT_HARD_TIMEOUT_MS || 0);
|
|
679
|
+
if (Number.isFinite(raw) && raw > 0)
|
|
680
|
+
return Math.max(1000, Math.min(Math.floor(raw), 24 * 60 * 60 * 1000));
|
|
681
|
+
return String(route || '').replace(/^\$/, '').toUpperCase() === 'NARUTO' ? 10 * 60 * 1000 : 30 * 60 * 1000;
|
|
682
|
+
}
|
|
662
683
|
function withFinalGptPatchEnvelopes(results, patchEnvelopes = []) {
|
|
663
684
|
const byAgent = new Map();
|
|
664
685
|
for (const envelope of patchEnvelopes) {
|
|
@@ -7,6 +7,7 @@ import { buildCodexExecArgs } from '../codex/codex-cli-syntax-builder.js';
|
|
|
7
7
|
export function buildCodexExecAgentArgs(agent, prompt, opts = {}) {
|
|
8
8
|
const resultFile = opts.resultFile || defaultCodexResultFile(agent, opts);
|
|
9
9
|
const sandbox = opts.workspaceWrite ? 'workspace-write' : 'read-only';
|
|
10
|
+
const noMcp = workerNoMcpDefault(agent, opts, sandbox);
|
|
10
11
|
const args = buildCodexExecArgs({
|
|
11
12
|
json: true,
|
|
12
13
|
outputSchema: opts.schemaFile || path.join(packageRoot(), 'schemas/codex/agent-result.schema.json'),
|
|
@@ -15,6 +16,7 @@ export function buildCodexExecAgentArgs(agent, prompt, opts = {}) {
|
|
|
15
16
|
skipGitRepoCheck: opts.skipGitRepoCheck !== false,
|
|
16
17
|
profile: opts.profile ? String(opts.profile) : null,
|
|
17
18
|
ignoreUserConfig: !opts.profile,
|
|
19
|
+
noMcp,
|
|
18
20
|
ignoreRules: true,
|
|
19
21
|
sandbox,
|
|
20
22
|
serviceTier: opts.serviceTier || (opts.fastMode === false ? 'standard' : 'fast'),
|
|
@@ -49,6 +51,7 @@ export async function runCodexExecAgent(agent, slice, opts = {}) {
|
|
|
49
51
|
fast_mode: fastPolicy.fast_mode,
|
|
50
52
|
service_tier_passed_to_codex: codexArgsIncludeServiceTier(command.args, fastPolicy.service_tier),
|
|
51
53
|
service_tier_cli_override_present: codexArgsIncludeServiceTier(command.args, fastPolicy.service_tier),
|
|
54
|
+
no_mcp: command.args.includes('--no-mcp'),
|
|
52
55
|
output_schema_used: command.args.includes('--output-schema'),
|
|
53
56
|
output_last_message_path: command.resultFile,
|
|
54
57
|
agent_worker_env_injected: false,
|
|
@@ -78,6 +81,7 @@ export async function runCodexExecAgent(agent, slice, opts = {}) {
|
|
|
78
81
|
fast_mode: fastPolicy.fast_mode,
|
|
79
82
|
service_tier_passed_to_codex: codexArgsIncludeServiceTier(command.args, fastPolicy.service_tier),
|
|
80
83
|
service_tier_cli_override_present: codexArgsIncludeServiceTier(command.args, fastPolicy.service_tier),
|
|
84
|
+
no_mcp: command.args.includes('--no-mcp'),
|
|
81
85
|
output_schema_used: command.args.includes('--output-schema'),
|
|
82
86
|
output_last_message_path: command.resultFile,
|
|
83
87
|
agent_worker_env_injected: Object.keys(workerEnv).length > 0,
|
|
@@ -119,6 +123,26 @@ export async function runCodexExecAgent(agent, slice, opts = {}) {
|
|
|
119
123
|
}
|
|
120
124
|
return validateAgentWorkerResult({ mission_id: opts.missionId || opts.mission_id || '', agent_id: agent.id, session_id: agent.session_id, persona_id: agent.persona_id || agent.id, task_slice_id: slice?.id || '', status: result.code === 0 ? 'done' : 'failed', backend: 'codex-exec', summary: result.stdout.slice(-1000) || result.stderr.slice(-1000), artifacts: [command.resultFile, report], blockers: result.code === 0 ? ['codex_exec_output_last_message_missing_or_invalid'] : ['codex_exec_exit_' + result.code], confidence: 'verified_partial', unverified: result.code === 0 ? ['codex-exec stdout fallback; resultFile JSON missing or invalid'] : [], writes: [], source_intelligence_refs: agent.source_intelligence_refs || null, goal_mode_ref: agent.goal_mode_ref || null, verification: { status: result.code === 0 ? 'partial' : 'failed', checks: ['codex-exec-exit-code', 'codex-exec-process-report', 'codex-exec-output-last-message'] } });
|
|
121
125
|
}
|
|
126
|
+
function workerNoMcpDefault(agent, opts = {}, sandbox = 'read-only') {
|
|
127
|
+
if (opts.noMcp !== undefined)
|
|
128
|
+
return opts.noMcp !== false;
|
|
129
|
+
if (sandbox === 'read-only')
|
|
130
|
+
return true;
|
|
131
|
+
const roleText = [
|
|
132
|
+
agent?.role,
|
|
133
|
+
agent?.persona_id,
|
|
134
|
+
agent?.required_role,
|
|
135
|
+
agent?.kind,
|
|
136
|
+
opts.role,
|
|
137
|
+
opts.kind
|
|
138
|
+
].map((value) => String(value || '')).join(' ');
|
|
139
|
+
if (/\b(?:verify|verifier|verification|review|reviewer|qa|audit|read[-_ ]?only)\b/i.test(roleText))
|
|
140
|
+
return true;
|
|
141
|
+
const safeList = Array.isArray(opts.workerSafeMcpServers)
|
|
142
|
+
? opts.workerSafeMcpServers
|
|
143
|
+
: String(process.env.SKS_WORKER_SAFE_MCP_SERVERS || '').split(',').map((item) => item.trim()).filter(Boolean);
|
|
144
|
+
return !(opts.allowWorkerMcp === true && safeList.length > 0);
|
|
145
|
+
}
|
|
122
146
|
async function writeCodexProcessReport(root, agent, report) {
|
|
123
147
|
const rel = path.join(agent.session_artifact_dir || path.join('sessions', agent.id), 'agent-process-report.json');
|
|
124
148
|
await writeJsonAtomic(path.join(root, rel), { schema: 'sks.agent-process-report.v1', backend: 'codex-exec', agent_id: agent.id, session_id: agent.session_id, service_tier: report.service_tier || agent.service_tier || 'fast', fast_mode: report.fast_mode !== false, ...report });
|
|
@@ -122,13 +122,12 @@ export function applyFastModeToRoster(roster, policy) {
|
|
|
122
122
|
};
|
|
123
123
|
}
|
|
124
124
|
export async function writeFastModePropagationProof(root, input = { policy: resolveFastModePolicy() }) {
|
|
125
|
-
const workerFastReports = await collectNamedJson(root, 'worker-fast-mode.json');
|
|
126
125
|
const workerProcessReports = await collectNamedJson(root, 'worker-process-report.json');
|
|
127
126
|
const agentProcessReports = await collectNamedJson(root, 'agent-process-report.json');
|
|
128
127
|
const zellijReports = await collectNamedJson(root, 'agent-zellij-report.json');
|
|
129
128
|
const madReports = await collectNamedJson(root, 'mad-sks-worker-report.json');
|
|
130
129
|
const defaultFastExpected = input.policy.fast_mode === true;
|
|
131
|
-
const childReports = [...
|
|
130
|
+
const childReports = [...workerProcessReports, ...agentProcessReports, ...zellijReports, ...madReports];
|
|
132
131
|
const missingFast = defaultFastExpected
|
|
133
132
|
? childReports.filter((row) => row.json?.fast_mode !== true && row.json?.fast_mode !== 'true')
|
|
134
133
|
: [];
|
|
@@ -137,7 +136,7 @@ export async function writeFastModePropagationProof(root, input = { policy: reso
|
|
|
137
136
|
return value && value !== input.policy.service_tier;
|
|
138
137
|
});
|
|
139
138
|
const missingCliOverride = [];
|
|
140
|
-
const workerMissing = defaultFastExpected &&
|
|
139
|
+
const workerMissing = defaultFastExpected && workerProcessReports.length === 0
|
|
141
140
|
? ['fast_mode_worker_reports_missing']
|
|
142
141
|
: [];
|
|
143
142
|
const report = {
|
|
@@ -149,7 +148,7 @@ export async function writeFastModePropagationProof(root, input = { policy: reso
|
|
|
149
148
|
default_fast_mode: input.policy.default_fast_mode,
|
|
150
149
|
service_tier: input.policy.service_tier,
|
|
151
150
|
fast_mode: input.policy.fast_mode,
|
|
152
|
-
worker_fast_report_count:
|
|
151
|
+
worker_fast_report_count: 0,
|
|
153
152
|
worker_process_report_count: workerProcessReports.length,
|
|
154
153
|
codex_sdk_process_report_count: agentProcessReports.filter((row) => row.json?.backend === 'codex-sdk').length,
|
|
155
154
|
process_report_count: agentProcessReports.filter((row) => row.json?.backend === 'process').length,
|
|
@@ -6,7 +6,7 @@ export async function writeNativeCliSessionProof(root, input = {}) {
|
|
|
6
6
|
const swarm = await readJson(path.join(root, 'agent-native-cli-session-swarm.json'), null);
|
|
7
7
|
const scheduler = await readJson(path.join(root, 'agent-scheduler-state.json'), null);
|
|
8
8
|
const workerProcessReports = await collectNamedJson(root, 'worker-process-report.json');
|
|
9
|
-
const workerCloseReports =
|
|
9
|
+
const workerCloseReports = workerProcessReports.filter((row) => row.json?.session_proof || row.json?.exit_code !== undefined);
|
|
10
10
|
const workerHeartbeats = await collectHeartbeatCounts(root);
|
|
11
11
|
const requestedAgents = Number(input.requestedAgents || swarm?.requested_agents || scheduler?.target_active_slots || 0);
|
|
12
12
|
const targetActiveSlots = Number(input.targetActiveSlots || swarm?.target_active_slots || scheduler?.target_active_slots || requestedAgents);
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import path from 'node:path';
|
|
2
|
-
import { ensureDir, nowIso, readJson, writeJsonAtomic
|
|
2
|
+
import { appendJsonlBounded, ensureDir, nowIso, readJson, writeJsonAtomic } from '../fsx.js';
|
|
3
3
|
import { scanAgentTextForRecursion } from './agent-recursion-guard.js';
|
|
4
4
|
import { validateAgentWorkerResult } from './agent-worker-pipeline.js';
|
|
5
5
|
import { resolveFastModePolicy } from './fast-mode-policy.js';
|
|
@@ -53,48 +53,49 @@ export async function runNativeCliWorker(input = {}) {
|
|
|
53
53
|
worker_env: process.env.SKS_AGENT_WORKER === '1',
|
|
54
54
|
violations: recursion.violations
|
|
55
55
|
};
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
worker_artifact_dir: workerDirRel,
|
|
70
|
-
result_path: resultRel,
|
|
71
|
-
heartbeat_path: heartbeatRel,
|
|
72
|
-
patch_envelope_path: patchRel,
|
|
73
|
-
backend,
|
|
74
|
-
codex_task: {
|
|
75
|
-
backend: backend === 'zellij' ? 'codex-sdk' : backend,
|
|
76
|
-
output_schema_id: 'sks.agent-worker-result.v1',
|
|
77
|
-
sandbox_policy: Array.isArray(slice.write_paths) && slice.write_paths.length > 0 ? 'workspace-write' : 'read-only',
|
|
78
|
-
thread_policy: 'new_thread_per_generation',
|
|
56
|
+
if (process.env.SKS_DEBUG_ARTIFACTS === '1')
|
|
57
|
+
await writeJsonAtomic(path.join(workerDir, 'worker-intake.json'), {
|
|
58
|
+
schema: NATIVE_CLI_WORKER_SCHEMA,
|
|
59
|
+
generated_at: nowIso(),
|
|
60
|
+
parent_mission_id: String(intake.parent_mission_id || input.parentMissionId || intake.mission_id || ''),
|
|
61
|
+
slot_id: String(agent.slot_id || input.slotId || ''),
|
|
62
|
+
generation_index: Number(agent.generation_index || input.generationIndex || 1),
|
|
63
|
+
task_id: String(slice.id || input.taskId || ''),
|
|
64
|
+
persona_id: String(agent.persona_id || input.personaId || ''),
|
|
65
|
+
lease_id: String(intake.lease_id || input.leaseId || ''),
|
|
66
|
+
agent_root: agentRoot,
|
|
67
|
+
main_repo_root: String(input.mainRepoRoot || intake.main_repo_root || worktree?.main_repo_root || agentRoot),
|
|
68
|
+
cwd: workerCwd,
|
|
79
69
|
worktree,
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
70
|
+
worker_artifact_dir: workerDirRel,
|
|
71
|
+
result_path: resultRel,
|
|
72
|
+
heartbeat_path: heartbeatRel,
|
|
73
|
+
patch_envelope_path: patchRel,
|
|
74
|
+
backend,
|
|
75
|
+
codex_task: {
|
|
76
|
+
backend: backend === 'zellij' ? 'codex-sdk' : backend,
|
|
77
|
+
output_schema_id: 'sks.agent-worker-result.v1',
|
|
78
|
+
sandbox_policy: Array.isArray(slice.write_paths) && slice.write_paths.length > 0 ? 'workspace-write' : 'read-only',
|
|
79
|
+
thread_policy: 'new_thread_per_generation',
|
|
80
|
+
worktree,
|
|
81
|
+
cwd: workerCwd
|
|
82
|
+
},
|
|
83
|
+
service_tier: policy.service_tier,
|
|
84
|
+
fast_mode: policy.fast_mode,
|
|
85
|
+
source_intelligence_refs: agent.source_intelligence_refs || intake.source_intelligence_refs || null,
|
|
86
|
+
goal_mode_ref: agent.goal_mode_ref || intake.goal_mode_ref || null,
|
|
87
|
+
strategy_refs: slice.strategy_refs || intake.strategy_refs || null,
|
|
88
|
+
no_recursive_orchestrator_guard: guard.ok
|
|
89
|
+
});
|
|
89
90
|
await workerTelemetry(agentRoot, intake, agent, slice, {
|
|
90
91
|
eventType: 'task_started',
|
|
91
92
|
status: 'running',
|
|
92
93
|
backend,
|
|
93
94
|
serviceTier: policy.service_tier,
|
|
94
|
-
artifacts: [
|
|
95
|
+
artifacts: [heartbeatRel],
|
|
95
96
|
logTail: String(slice.description || slice.title || slice.id || 'worker task started')
|
|
96
97
|
});
|
|
97
|
-
await
|
|
98
|
+
await appendJsonlBounded(path.resolve(agentRoot, heartbeatRel), {
|
|
98
99
|
schema: 'sks.native-cli-worker-heartbeat.v1',
|
|
99
100
|
ts: nowIso(),
|
|
100
101
|
event: 'started',
|
|
@@ -104,7 +105,7 @@ export async function runNativeCliWorker(input = {}) {
|
|
|
104
105
|
generation_index: agent.generation_index || null,
|
|
105
106
|
fast_mode: policy.fast_mode,
|
|
106
107
|
service_tier: policy.service_tier
|
|
107
|
-
});
|
|
108
|
+
}, 2 * 1024 * 1024);
|
|
108
109
|
await workerTelemetry(agentRoot, intake, agent, slice, {
|
|
109
110
|
eventType: 'heartbeat',
|
|
110
111
|
status: 'running',
|
|
@@ -113,18 +114,6 @@ export async function runNativeCliWorker(input = {}) {
|
|
|
113
114
|
artifacts: [heartbeatRel],
|
|
114
115
|
logTail: 'worker heartbeat started'
|
|
115
116
|
});
|
|
116
|
-
await writeJsonAtomic(path.join(workerDir, 'worker-fast-mode.json'), {
|
|
117
|
-
schema: 'sks.native-cli-worker-fast-mode.v1',
|
|
118
|
-
generated_at: nowIso(),
|
|
119
|
-
ok: true,
|
|
120
|
-
fast_mode: policy.fast_mode,
|
|
121
|
-
service_tier: policy.service_tier,
|
|
122
|
-
env: {
|
|
123
|
-
SKS_FAST_MODE: process.env.SKS_FAST_MODE || null,
|
|
124
|
-
SKS_SERVICE_TIER: process.env.SKS_SERVICE_TIER || null
|
|
125
|
-
}
|
|
126
|
-
});
|
|
127
|
-
await writeJsonAtomic(path.join(workerDir, 'worker-recursion-guard.json'), guard);
|
|
128
117
|
let noPatchReason = null;
|
|
129
118
|
const progressTelemetry = startWorkerProgressTelemetry({
|
|
130
119
|
agentRoot,
|
|
@@ -184,13 +173,12 @@ export async function runNativeCliWorker(input = {}) {
|
|
|
184
173
|
backend,
|
|
185
174
|
blockers: Array.isArray(slice.write_paths) && slice.write_paths.length && backend !== 'fake' ? ['write_capable_no_patch_envelope'] : []
|
|
186
175
|
};
|
|
187
|
-
await writeJsonAtomic(path.join(workerDir, 'worker-no-patch-reason.json'), noPatchReason);
|
|
188
176
|
await workerTelemetry(agentRoot, intake, agent, slice, {
|
|
189
177
|
eventType: 'artifact_written',
|
|
190
178
|
status: 'running',
|
|
191
179
|
backend,
|
|
192
180
|
serviceTier: policy.service_tier,
|
|
193
|
-
artifacts: [
|
|
181
|
+
artifacts: [resultRel],
|
|
194
182
|
blockers: noPatchReason.blockers || [],
|
|
195
183
|
logTail: noPatchReason.reason
|
|
196
184
|
});
|
|
@@ -203,6 +191,9 @@ export async function runNativeCliWorker(input = {}) {
|
|
|
203
191
|
generated_at: nowIso(),
|
|
204
192
|
ok: guard.ok,
|
|
205
193
|
backend,
|
|
194
|
+
fast_mode: policy.fast_mode,
|
|
195
|
+
service_tier: policy.service_tier,
|
|
196
|
+
codex_desktop_service_tier: policy.codex_desktop_service_tier,
|
|
206
197
|
cwd: workerCwd,
|
|
207
198
|
worktree,
|
|
208
199
|
agent_id: agent.id,
|
|
@@ -213,8 +204,6 @@ export async function runNativeCliWorker(input = {}) {
|
|
|
213
204
|
ppid: process.ppid,
|
|
214
205
|
process_id: process.pid,
|
|
215
206
|
command_line: redactCommandLine(process.argv),
|
|
216
|
-
fast_mode: policy.fast_mode,
|
|
217
|
-
service_tier: policy.service_tier,
|
|
218
207
|
sdk_thread_id: routed.report?.sdk_thread_id || null,
|
|
219
208
|
sdk_run_id: routed.report?.sdk_run_id || null,
|
|
220
209
|
stream_event_count: Number(routed.report?.stream_event_count || 0),
|
|
@@ -224,21 +213,32 @@ export async function runNativeCliWorker(input = {}) {
|
|
|
224
213
|
backend_child_execution: routed.report.child_process_ids.length > 0 || Boolean(routed.report?.sdk_thread_id) || backend === 'fake' || backend === 'ollama',
|
|
225
214
|
recursion_guard_env: process.env.SKS_DISABLE_ROUTE_RECURSION === '1',
|
|
226
215
|
worker_env: process.env.SKS_AGENT_WORKER === '1',
|
|
216
|
+
fast_mode_report: {
|
|
217
|
+
ok: true,
|
|
218
|
+
fast_mode: policy.fast_mode,
|
|
219
|
+
service_tier: policy.service_tier,
|
|
220
|
+
env: {
|
|
221
|
+
SKS_FAST_MODE: process.env.SKS_FAST_MODE || null,
|
|
222
|
+
SKS_SERVICE_TIER: process.env.SKS_SERVICE_TIER || null
|
|
223
|
+
}
|
|
224
|
+
},
|
|
225
|
+
recursion_guard: guard,
|
|
226
|
+
session_proof: {
|
|
227
|
+
ok: guard.ok,
|
|
228
|
+
session_id: agent.session_id,
|
|
229
|
+
slot_id: agent.slot_id || null,
|
|
230
|
+
generation_index: agent.generation_index || null,
|
|
231
|
+
artifact_dir: workerDirRel,
|
|
232
|
+
patch_envelope: patchEnvelopes.length ? patchRel : null
|
|
233
|
+
},
|
|
227
234
|
exit_code: guard.ok ? 0 : 1
|
|
228
235
|
};
|
|
229
236
|
await writeJsonAtomic(path.join(workerDir, 'worker-process-report.json'), report);
|
|
230
237
|
const artifacts = [
|
|
231
|
-
path.join(workerDirRel, 'worker-intake.json'),
|
|
232
|
-
heartbeatRel,
|
|
233
|
-
path.join(workerDirRel, 'worker-process-report.json'),
|
|
234
|
-
routed.reportRel,
|
|
235
|
-
...routed.result.artifacts.filter((artifact) => artifact.includes('codex-worker-') || artifact.includes('agent-process-report') || artifact.includes('agent-zellij-report')),
|
|
236
238
|
resultRel,
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
path.join(workerDirRel, 'worker-recursion-guard.json'),
|
|
241
|
-
path.join(workerDirRel, 'worker-session-proof.json')
|
|
239
|
+
path.join(workerDirRel, 'worker-process-report.json'),
|
|
240
|
+
heartbeatRel,
|
|
241
|
+
...(patchEnvelopes.length ? [patchRel] : [])
|
|
242
242
|
];
|
|
243
243
|
const result = validateAgentWorkerResult({
|
|
244
244
|
...routed.result,
|
|
@@ -268,7 +268,7 @@ export async function runNativeCliWorker(input = {}) {
|
|
|
268
268
|
zellij_child_report: routed.result.zellij_child_report,
|
|
269
269
|
model_authored_patch_envelopes: patchEnvelopes.some((envelope) => envelope.source === 'model_authored'),
|
|
270
270
|
fixture_patch_envelopes: patchEnvelopes.some((envelope) => envelope.source === 'fixture'),
|
|
271
|
-
...(!patchEnvelopes.length ? { no_patch_reason: noPatchReason
|
|
271
|
+
...(!patchEnvelopes.length ? { no_patch_reason: noPatchReason } : {}),
|
|
272
272
|
source_intelligence_refs: agent.source_intelligence_refs || intake.source_intelligence_refs || null,
|
|
273
273
|
goal_mode_ref: agent.goal_mode_ref || intake.goal_mode_ref || null,
|
|
274
274
|
verification: { status: guard.ok && routed.result.status === 'done' ? 'passed' : 'failed', checks: [...(routed.result.verification?.checks || []), 'native-cli-worker-process', 'worker-artifact-contract', 'fast-mode-policy', 'native-worker-backend-router'] },
|
|
@@ -284,40 +284,14 @@ export async function runNativeCliWorker(input = {}) {
|
|
|
284
284
|
blockers: result.blockers || [],
|
|
285
285
|
logTail: result.summary || ''
|
|
286
286
|
});
|
|
287
|
-
await
|
|
288
|
-
schema: 'sks.native-cli-worker-session-proof.v1',
|
|
289
|
-
generated_at: nowIso(),
|
|
290
|
-
ok: result.status === 'done',
|
|
291
|
-
session_id: result.session_id,
|
|
292
|
-
slot_id: agent.slot_id || null,
|
|
293
|
-
generation_index: agent.generation_index || null,
|
|
294
|
-
process_id: process.pid,
|
|
295
|
-
main_repo_root: String(input.mainRepoRoot || intake.main_repo_root || worktree?.main_repo_root || agentRoot),
|
|
296
|
-
cwd: workerCwd,
|
|
297
|
-
worktree,
|
|
298
|
-
artifact_dir: workerDirRel,
|
|
299
|
-
patch_envelope: patchEnvelopes.length ? patchRel : null,
|
|
300
|
-
no_patch_reason: patchEnvelopes.length ? null : path.join(workerDirRel, 'worker-no-patch-reason.json'),
|
|
301
|
-
backend_router_report: routed.reportRel,
|
|
302
|
-
backend_child_process_ids: routed.report.child_process_ids,
|
|
303
|
-
sdk_thread_id: routed.report?.sdk_thread_id || null,
|
|
304
|
-
sdk_run_id: routed.report?.sdk_run_id || null,
|
|
305
|
-
stream_event_count: Number(routed.report?.stream_event_count || 0),
|
|
306
|
-
structured_output_valid: routed.report?.structured_output_valid === true,
|
|
307
|
-
model_authored_patch_envelopes: patchEnvelopes.some((envelope) => envelope.source === 'model_authored'),
|
|
308
|
-
fixture_patch_envelopes: patchEnvelopes.some((envelope) => envelope.source === 'fixture'),
|
|
309
|
-
fast_mode: policy.fast_mode,
|
|
310
|
-
service_tier: policy.service_tier,
|
|
311
|
-
blockers: result.blockers
|
|
312
|
-
});
|
|
313
|
-
await appendJsonl(path.resolve(agentRoot, heartbeatRel), {
|
|
287
|
+
await appendJsonlBounded(path.resolve(agentRoot, heartbeatRel), {
|
|
314
288
|
schema: 'sks.native-cli-worker-heartbeat.v1',
|
|
315
289
|
ts: nowIso(),
|
|
316
290
|
event: 'finished',
|
|
317
291
|
pid: process.pid,
|
|
318
292
|
session_id: agent.session_id,
|
|
319
293
|
status: result.status
|
|
320
|
-
});
|
|
294
|
+
}, 2 * 1024 * 1024);
|
|
321
295
|
await workerTelemetry(agentRoot, intake, agent, slice, {
|
|
322
296
|
eventType: 'heartbeat',
|
|
323
297
|
status: result.status === 'done' ? 'completed' : 'failed',
|
|
@@ -327,32 +301,18 @@ export async function runNativeCliWorker(input = {}) {
|
|
|
327
301
|
blockers: result.blockers || [],
|
|
328
302
|
logTail: 'worker heartbeat finished'
|
|
329
303
|
});
|
|
330
|
-
await writeJsonAtomic(path.join(workerDir, 'worker-terminal-close-report.json'), {
|
|
331
|
-
schema: 'sks.native-cli-worker-terminal-close-report.v1',
|
|
332
|
-
generated_at: nowIso(),
|
|
333
|
-
ok: result.status === 'done',
|
|
334
|
-
session_id: result.session_id,
|
|
335
|
-
slot_id: agent.slot_id || null,
|
|
336
|
-
generation_index: agent.generation_index || null,
|
|
337
|
-
process_id: process.pid,
|
|
338
|
-
exit_code: result.status === 'done' ? 0 : 1,
|
|
339
|
-
fast_mode: policy.fast_mode,
|
|
340
|
-
service_tier: policy.service_tier,
|
|
341
|
-
blockers: result.blockers
|
|
342
|
-
});
|
|
343
304
|
return result;
|
|
344
305
|
}
|
|
345
306
|
function delay(ms) {
|
|
346
307
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
347
308
|
}
|
|
348
309
|
function startWorkerProgressTelemetry(input) {
|
|
349
|
-
const parsed = Number(process.env.SKS_ZELLIJ_WORKER_PROGRESS_MS ||
|
|
350
|
-
const intervalMs = Math.max(
|
|
310
|
+
const parsed = Number(process.env.SKS_ZELLIJ_WORKER_PROGRESS_MS || 10000);
|
|
311
|
+
const intervalMs = Math.max(1000, Number.isFinite(parsed) ? Math.floor(parsed) : 10000);
|
|
351
312
|
let tick = 0;
|
|
352
313
|
const timer = setInterval(() => {
|
|
353
314
|
tick += 1;
|
|
354
|
-
|
|
355
|
-
appendJsonl(path.resolve(input.agentRoot, input.heartbeatRel), {
|
|
315
|
+
appendJsonlBounded(path.resolve(input.agentRoot, input.heartbeatRel), {
|
|
356
316
|
schema: 'sks.native-cli-worker-heartbeat.v1',
|
|
357
317
|
ts: nowIso(),
|
|
358
318
|
event: 'progress',
|
|
@@ -360,15 +320,14 @@ function startWorkerProgressTelemetry(input) {
|
|
|
360
320
|
session_id: input.agent.session_id,
|
|
361
321
|
slot_id: input.agent.slot_id || null,
|
|
362
322
|
generation_index: input.agent.generation_index || null,
|
|
363
|
-
progress
|
|
364
|
-
}).catch(() => undefined);
|
|
323
|
+
progress: null
|
|
324
|
+
}, 2 * 1024 * 1024).catch(() => undefined);
|
|
365
325
|
workerTelemetry(input.agentRoot, input.intake, input.agent, input.slice, {
|
|
366
|
-
eventType: '
|
|
326
|
+
eventType: 'heartbeat',
|
|
367
327
|
status: 'running',
|
|
368
328
|
backend: input.backend,
|
|
369
329
|
serviceTier: input.serviceTier,
|
|
370
330
|
artifacts: [input.heartbeatRel],
|
|
371
|
-
progress,
|
|
372
331
|
logTail: `backend running ${tick}`
|
|
373
332
|
}).catch(() => undefined);
|
|
374
333
|
}, intervalMs);
|
|
@@ -435,7 +394,8 @@ async function workerTelemetry(agentRoot, intake, agent, slice, input) {
|
|
|
435
394
|
service_tier: input.serviceTier,
|
|
436
395
|
worktree_id: agent.worktree?.id || slice.worktree?.id || intake.worktree?.id || null,
|
|
437
396
|
worktree_path: agent.worktree?.path || slice.worktree?.path || intake.worktree?.path || null,
|
|
438
|
-
|
|
397
|
+
task_id: String(slice.id || 'worker-task'),
|
|
398
|
+
...(input.eventType === 'task_started' ? { task_title: String(slice.description || slice.title || slice.id || 'worker task') } : {}),
|
|
439
399
|
current_file: firstString([slice.write_paths?.[0], slice.readonly_paths?.[0], slice.input_files?.[0]]) || null,
|
|
440
400
|
...(input.progress ? { progress: input.progress } : {}),
|
|
441
401
|
artifact_paths: input.artifacts || [],
|