sneakoscope 2.0.16 → 2.0.18
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 +23 -30
- 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/cli/command-registry.js +1 -1
- package/dist/commands/doctor.js +39 -1
- package/dist/commands/proof.js +21 -0
- package/dist/commands/zellij-slot-pane.js +7 -1
- package/dist/core/agents/agent-effort-policy.js +7 -1
- package/dist/core/agents/agent-orchestrator.js +3 -1
- package/dist/core/agents/agent-scheduler.js +14 -1
- package/dist/core/agents/native-cli-session-swarm.js +11 -7
- package/dist/core/agents/native-cli-worker.js +56 -7
- package/dist/core/agents/parallel-runtime-proof.js +68 -9
- package/dist/core/agents/runtime-proof-summary.js +75 -0
- package/dist/core/codex-app/codex-app-handoff.js +77 -0
- package/dist/core/codex-control/codex-0138-capability.js +64 -0
- package/dist/core/codex-control/codex-model-capabilities.js +41 -0
- package/dist/core/codex-control/codex-sdk-config-policy.js +1 -1
- package/dist/core/codex-control/codex-task-runner.js +1 -1
- package/dist/core/codex-plugins/codex-plugin-json.js +152 -0
- package/dist/core/commands/mad-sks-command.js +4 -0
- package/dist/core/commands/naruto-command.js +20 -4
- package/dist/core/commands/qa-loop-command.js +111 -4
- package/dist/core/commands/team-command.js +6 -311
- package/dist/core/commands/team-legacy-observe-command.js +182 -0
- package/dist/core/db-safety.js +15 -0
- package/dist/core/doctor/codex-0138-doctor.js +104 -0
- package/dist/core/doctor/doctor-readiness-matrix.js +11 -0
- package/dist/core/effort-orchestrator.js +9 -0
- package/dist/core/feature-registry.js +4 -2
- package/dist/core/fsx.js +1 -1
- package/dist/core/hooks-runtime.js +38 -4
- package/dist/core/image/image-artifact-path-contract.js +99 -0
- package/dist/core/image-ux-review/imagegen-adapter.js +24 -3
- package/dist/core/init.js +1 -0
- package/dist/core/mad-db/mad-db-capability.js +9 -1
- package/dist/core/mad-db/mad-db-result-lifecycle.js +207 -0
- package/dist/core/mcp/mcp-plugin-inventory.js +29 -0
- package/dist/core/mcp/mcp-server-policy.js +24 -0
- package/dist/core/qa-loop/qa-loop-budget-policy.js +37 -0
- package/dist/core/qa-loop.js +28 -2
- package/dist/core/release/release-gate-affected-selector.js +47 -5
- package/dist/core/release/release-gate-dag.js +5 -1
- package/dist/core/release/release-gate-scheduler.js +2 -1
- package/dist/core/routes.js +3 -1
- package/dist/core/usage/codex-account-usage.js +78 -0
- package/dist/core/version.js +1 -1
- package/dist/core/zellij/zellij-slot-column-anchor.js +16 -7
- package/dist/core/zellij/zellij-slot-pane-renderer.js +92 -1
- package/dist/core/zellij/zellij-slot-telemetry.js +29 -6
- package/dist/core/zellij/zellij-ui-mode.js +12 -2
- package/dist/scripts/prepublish-release-check-or-fast.js +3 -3
- package/dist/scripts/release-gate-existence-audit.js +5 -1
- package/dist/scripts/release-speed-summary.js +22 -2
- package/package.json +38 -4
- package/schemas/agents/parallel-runtime-proof.schema.json +31 -0
- package/schemas/codex-app/codex-app-handoff.schema.json +20 -0
- package/schemas/codex-plugins/codex-plugin-inventory.schema.json +32 -0
- package/schemas/image/image-artifact-path-contract.schema.json +32 -0
- package/schemas/usage/codex-account-usage.schema.json +27 -0
|
@@ -13,6 +13,12 @@ import { scanDbSafety } from '../db-safety.js';
|
|
|
13
13
|
import { maybeFinalizeRoute } from '../proof/auto-finalize.js';
|
|
14
14
|
import { runNativeAgentOrchestrator } from '../agents/agent-orchestrator.js';
|
|
15
15
|
import { flag, promptOf, readBoundedIntegerFlag, readFlagValue, readMaxCycles, resolveMissionId, safeReadTextFile } from './command-utils.js';
|
|
16
|
+
import { runCodexAppHandoff, qaLoopShouldRequestAppHandoff } from '../codex-app/codex-app-handoff.js';
|
|
17
|
+
import { writeCodex0138CapabilityArtifacts } from '../codex-control/codex-0138-capability.js';
|
|
18
|
+
import { writeCodexAccountUsageArtifacts } from '../usage/codex-account-usage.js';
|
|
19
|
+
import { buildQaLoopBudgetPolicy, selectQaLoopEscalatedEffort } from '../qa-loop/qa-loop-budget-policy.js';
|
|
20
|
+
import { discoverImageArtifactsInDir, writeImageArtifactPathContract } from '../image/image-artifact-path-contract.js';
|
|
21
|
+
import { pluginAppTemplatePolicy } from '../codex-plugins/codex-plugin-json.js';
|
|
16
22
|
import fsp from 'node:fs/promises';
|
|
17
23
|
export async function qaLoopCommand(sub, args = []) {
|
|
18
24
|
const known = new Set(['prepare', 'answer', 'run', 'status', 'help', '--help', '-h']);
|
|
@@ -31,8 +37,8 @@ export async function qaLoopCommand(sub, args = []) {
|
|
|
31
37
|
Usage:
|
|
32
38
|
sks qa-loop prepare "target"
|
|
33
39
|
sks qa-loop answer <mission-id|latest> <answers.json>
|
|
34
|
-
sks qa-loop run <mission-id|latest> [--mock] [--max-cycles N]
|
|
35
|
-
sks qa-loop status <mission-id|latest>
|
|
40
|
+
sks qa-loop run <mission-id|latest> [--mock] [--max-cycles N] [--app-handoff] [--app-handoff-required]
|
|
41
|
+
sks qa-loop status <mission-id|latest> [--desktop]
|
|
36
42
|
`);
|
|
37
43
|
}
|
|
38
44
|
function qaRoute() {
|
|
@@ -136,6 +142,90 @@ async function qaLoopRun(args) {
|
|
|
136
142
|
const qaGate = await readJson(path.join(dir, 'qa-gate.json'), {});
|
|
137
143
|
const reportFile = qaGate.qa_report_file;
|
|
138
144
|
const uiRequired = qaUiRequired(contract.answers || {});
|
|
145
|
+
const capabilityArtifact = await writeCodex0138CapabilityArtifacts(root, { missionId: id }).catch((err) => ({ error: err?.message || String(err), report: null }));
|
|
146
|
+
const usageArtifact = await writeCodexAccountUsageArtifacts(root, { missionId: id }).catch((err) => ({ error: err?.message || String(err), snapshot: null }));
|
|
147
|
+
const budgetPolicy = buildQaLoopBudgetPolicy({ usage: usageArtifact?.snapshot || null, provider: 'codex-sdk' });
|
|
148
|
+
await writeJsonAtomic(path.join(dir, 'qa-loop', 'qa-loop-budget-policy.json'), budgetPolicy);
|
|
149
|
+
const effortEscalation = selectQaLoopEscalatedEffort({
|
|
150
|
+
failureCount: Number(qaGate.safe_fix_attempts || qaGate.failure_count || 0),
|
|
151
|
+
currentEffort: String(profile || 'high').replace(/^sks-(?:logic|agent)-/, '').replace(/-fast$/, '') || 'high'
|
|
152
|
+
});
|
|
153
|
+
await writeJsonAtomic(path.join(dir, 'qa-loop', 'qa-loop-effort-escalation.json'), effortEscalation);
|
|
154
|
+
const discoveredImages = await discoverImageArtifactsInDir(dir).catch(() => []);
|
|
155
|
+
const imagePathContract = discoveredImages.length
|
|
156
|
+
? await writeQaLoopImagePathContract(root, dir, id, discoveredImages)
|
|
157
|
+
: null;
|
|
158
|
+
const pluginInventory = await readJson(path.join(root, '.sneakoscope', 'codex-plugin-inventory.json'), null);
|
|
159
|
+
const pluginPolicy = pluginInventory?.schema === 'sks.codex-plugin-inventory.v1' ? pluginAppTemplatePolicy(pluginInventory) : null;
|
|
160
|
+
const appHandoffRequired = flag(args, '--app-handoff-required') || process.env.SKS_QA_LOOP_APP_HANDOFF_REQUIRED === '1';
|
|
161
|
+
const appHandoffRequested = qaLoopShouldRequestAppHandoff({
|
|
162
|
+
args,
|
|
163
|
+
uiRequired,
|
|
164
|
+
visualArtifactsPresent: discoveredImages.length > 0,
|
|
165
|
+
pluginAppTemplateUnavailable: Boolean(pluginPolicy?.unavailable_app_templates?.length),
|
|
166
|
+
userRequestedDesktopReview: appHandoffRequired
|
|
167
|
+
});
|
|
168
|
+
const appHandoff = appHandoffRequested || appHandoffRequired
|
|
169
|
+
? await runCodexAppHandoff(root, {
|
|
170
|
+
schema: 'sks.codex-app-handoff-request.v1',
|
|
171
|
+
mission_id: id,
|
|
172
|
+
route: '$QA-LOOP',
|
|
173
|
+
reason: appHandoffRequired ? 'desktop_app_review_required' : 'desktop_app_review_requested',
|
|
174
|
+
thread_ref: null,
|
|
175
|
+
workspace_path: root,
|
|
176
|
+
artifacts: [
|
|
177
|
+
'decision-contract.json',
|
|
178
|
+
'qa-gate.json',
|
|
179
|
+
'qa-ledger.json',
|
|
180
|
+
reportFile,
|
|
181
|
+
capabilityArtifact && !capabilityArtifact.error ? 'codex-0138-capability.json' : '',
|
|
182
|
+
imagePathContract ? 'qa-loop/image-artifact-path-contract.json' : ''
|
|
183
|
+
].filter(Boolean),
|
|
184
|
+
prompt: mission.prompt || 'QA-LOOP desktop handoff',
|
|
185
|
+
require_desktop: appHandoffRequired,
|
|
186
|
+
capability_required: 'codex-0.138'
|
|
187
|
+
}).catch((err) => ({
|
|
188
|
+
ok: false,
|
|
189
|
+
status: 'blocked_for_desktop_review',
|
|
190
|
+
artifact_path: path.join(dir, 'qa-loop', 'app-handoff.json'),
|
|
191
|
+
blockers: [`codex_app_handoff_failed:${err?.message || String(err)}`],
|
|
192
|
+
desktop_handoff_supported: false
|
|
193
|
+
}))
|
|
194
|
+
: null;
|
|
195
|
+
if (appHandoff || imagePathContract) {
|
|
196
|
+
const latestGate = await readJson(path.join(dir, 'qa-gate.json'), qaGate);
|
|
197
|
+
const nextGate = {
|
|
198
|
+
...latestGate,
|
|
199
|
+
desktop_app_handoff_required: appHandoffRequired,
|
|
200
|
+
desktop_app_handoff_status: appHandoff ? appHandoff.status : 'not_requested',
|
|
201
|
+
desktop_app_handoff_artifact: appHandoff ? path.relative(dir, appHandoff.artifact_path) : null,
|
|
202
|
+
desktop_app_handoff_supported: appHandoff ? appHandoff.desktop_handoff_supported === true : false,
|
|
203
|
+
desktop_app_handoff_is_web_ui_evidence: false,
|
|
204
|
+
image_artifact_path_contract_present: Boolean(imagePathContract),
|
|
205
|
+
image_artifact_path_contract_artifact: imagePathContract ? 'qa-loop/image-artifact-path-contract.json' : null,
|
|
206
|
+
image_artifact_path_contract_blockers: imagePathContract?.contract?.blockers || [],
|
|
207
|
+
blockers: Array.from(new Set([
|
|
208
|
+
...(latestGate.blockers || []),
|
|
209
|
+
...(appHandoffRequired && appHandoff && appHandoff.ok !== true ? ['blocked_for_desktop_review'] : []),
|
|
210
|
+
...(imagePathContract?.contract?.blockers || [])
|
|
211
|
+
])),
|
|
212
|
+
notes: [
|
|
213
|
+
...(latestGate.notes || []),
|
|
214
|
+
...(appHandoff ? ['Codex Desktop /app handoff is tracked separately and is not web UI verification evidence.'] : []),
|
|
215
|
+
...(imagePathContract ? ['Image artifacts expose real saved file paths for follow-up visual edits.'] : [])
|
|
216
|
+
]
|
|
217
|
+
};
|
|
218
|
+
await writeJsonAtomic(path.join(dir, 'qa-gate.json'), nextGate);
|
|
219
|
+
if (appHandoffRequired && appHandoff && appHandoff.ok !== true) {
|
|
220
|
+
await maybeFinalizeRoute(root, { missionId: id, route: '$QA-LOOP', gateFile: 'qa-gate.json', gate: nextGate, artifacts: ['qa-gate.json', 'qa-ledger.json', reportFile, 'qa-loop/app-handoff.json', 'completion-proof.json'], statusHint: 'blocked', blockers: nextGate.blockers, command: { cmd: `sks qa-loop run ${id} --app-handoff-required`, status: 2 } });
|
|
221
|
+
await setCurrent(root, { mission_id: id, mode: 'QALOOP', phase: 'QALOOP_BLOCKED_DESKTOP_APP_HANDOFF', questions_allowed: true });
|
|
222
|
+
if (flag(args, '--json'))
|
|
223
|
+
return console.log(JSON.stringify({ schema: 'sks.qa-loop-run.v1', ok: false, status: 'blocked_for_desktop_review', mission_id: id, app_handoff: appHandoff, gate: nextGate }, null, 2));
|
|
224
|
+
console.error('QA-LOOP blocked: Codex Desktop /app handoff is required but unavailable or still pending.');
|
|
225
|
+
process.exitCode = 2;
|
|
226
|
+
return;
|
|
227
|
+
}
|
|
228
|
+
}
|
|
139
229
|
if (uiRequired && !mock) {
|
|
140
230
|
const chrome = await codexChromeExtensionStatus();
|
|
141
231
|
if (!chrome.ok) {
|
|
@@ -237,7 +327,7 @@ async function qaLoopRun(args) {
|
|
|
237
327
|
for (let cycle = 1; cycle <= maxCycles; cycle += 1) {
|
|
238
328
|
const cycleDir = path.join(dir, 'qa-loop', `cycle-${cycle}`);
|
|
239
329
|
const outputFile = path.join(cycleDir, 'final.md');
|
|
240
|
-
const prompt = buildQaLoopPrompt({ id, mission, contract, cycle, previous: last, reportFile });
|
|
330
|
+
const prompt = buildQaLoopPrompt({ id, mission, contract, cycle, previous: last, reportFile, imagePathContract: imagePathContract?.contract || null, appHandoff });
|
|
241
331
|
await appendJsonlBounded(path.join(dir, 'events.jsonl'), { ts: nowIso(), type: 'qaloop.cycle.start', cycle });
|
|
242
332
|
const result = await runCodexExec({ root, prompt, outputFile, json: true, profile, logDir: cycleDir });
|
|
243
333
|
await writeJsonAtomic(path.join(cycleDir, 'process.json'), { code: result.code, stdout_tail: result.stdout, stderr_tail: result.stderr, stdout_bytes: result.stdoutBytes, stderr_bytes: result.stderrBytes, truncated: result.truncated, timed_out: result.timedOut });
|
|
@@ -276,8 +366,9 @@ async function qaLoopStatus(args) {
|
|
|
276
366
|
const status = await qaStatus(dir);
|
|
277
367
|
const nativeAgentPlan = await readJson(path.join(dir, 'qa-agent-plan.json'), null);
|
|
278
368
|
const agentSessions = await readJson(path.join(dir, 'agents', 'agent-sessions.json'), null);
|
|
369
|
+
const desktop = await readJson(path.join(dir, 'qa-loop', 'app-handoff.json'), null);
|
|
279
370
|
if (flag(args, '--json'))
|
|
280
|
-
return console.log(JSON.stringify({ mission, state, qa: status, native_agent_plan: nativeAgentPlan, agent_sessions: agentSessions?.sessions || null }, null, 2));
|
|
371
|
+
return console.log(JSON.stringify({ mission, state, qa: status, desktop_app_handoff: desktop, native_agent_plan: nativeAgentPlan, agent_sessions: agentSessions?.sessions || null }, null, 2));
|
|
281
372
|
console.log('SKS QA-LOOP Status\n');
|
|
282
373
|
console.log(`Mission: ${id}`);
|
|
283
374
|
console.log(`Phase: ${state.phase || mission.phase}`);
|
|
@@ -286,5 +377,21 @@ async function qaLoopStatus(args) {
|
|
|
286
377
|
console.log(`Gate: ${status.gate?.passed ? 'passed' : 'not passed'}`);
|
|
287
378
|
if (status.gate?.reasons?.length)
|
|
288
379
|
console.log(`Reasons: ${status.gate.reasons.join(', ')}`);
|
|
380
|
+
if (flag(args, '--desktop')) {
|
|
381
|
+
console.log('Desktop:');
|
|
382
|
+
console.log(` /app handoff: ${desktop?.status || 'not_requested'}`);
|
|
383
|
+
if (desktop?.operator_instruction?.prompt_artifact)
|
|
384
|
+
console.log(` prompt: ${desktop.operator_instruction.prompt_artifact}`);
|
|
385
|
+
console.log(' web evidence: not a substitute for Codex Chrome Extension web UI verification');
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
async function writeQaLoopImagePathContract(root, dir, missionId, images) {
|
|
389
|
+
const primary = await writeImageArtifactPathContract(root, {
|
|
390
|
+
missionId,
|
|
391
|
+
images,
|
|
392
|
+
artifactPath: path.join(dir, 'image-artifact-path-contract.json')
|
|
393
|
+
});
|
|
394
|
+
await writeJsonAtomic(path.join(dir, 'qa-loop', 'image-artifact-path-contract.json'), primary.contract);
|
|
395
|
+
return primary;
|
|
289
396
|
}
|
|
290
397
|
//# sourceMappingURL=qa-loop-command.js.map
|
|
@@ -1,29 +1,12 @@
|
|
|
1
1
|
import path from 'node:path';
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import { createMission, findLatestMission, loadMission, setCurrent } from '../mission.js';
|
|
5
|
-
import { buildQuestionSchema, writeQuestions } from '../questions.js';
|
|
6
|
-
import { CODEX_COMPUTER_USE_ONLY_POLICY, CODEX_WEB_VERIFICATION_POLICY, FROM_CHAT_IMG_CHECKLIST_ARTIFACT, FROM_CHAT_IMG_COVERAGE_ARTIFACT, FROM_CHAT_IMG_QA_LOOP_ARTIFACT, FROM_CHAT_IMG_SOURCE_INVENTORY_ARTIFACT, FROM_CHAT_IMG_TEMP_TRIWIKI_ARTIFACT, FROM_CHAT_IMG_VISUAL_MAP_ARTIFACT, FROM_CHAT_IMG_WORK_ORDER_ARTIFACT, ROUTES, hasFromChatImgSignal, routePrompt, routeReasoning, triwikiContextTracking } from '../routes.js';
|
|
7
|
-
import { TEAM_DECOMPOSITION_ARTIFACT, TEAM_GRAPH_ARTIFACT, TEAM_INBOX_DIR, TEAM_RUNTIME_TASKS_ARTIFACT, teamRuntimePlanMetadata, teamRuntimeRequiredArtifacts, writeTeamRuntimeArtifacts } from '../team-dag.js';
|
|
8
|
-
import { SSOT_GUARD_ARTIFACT, buildSsotGuard, ssotGuardPolicyText } from '../safety/ssot-guard.js';
|
|
9
|
-
import { appendTeamEvent, formatAgentReasoning, formatRoleCounts, initTeamLive, isTerminalTeamAgentStatus, normalizeTeamSpec, parseTeamSpecArgs, readTeamControl, readTeamDashboard, readTeamLive, readTeamTranscriptTail, renderTeamAgentLane, renderTeamCleanupSummary, renderTeamWatch, requestTeamSessionCleanup, teamCleanupRequested, teamReasoningPolicy } from '../team-live.js';
|
|
10
|
-
import { evaluateTeamReviewPolicyGate, MIN_TEAM_REVIEWER_LANES, MIN_TEAM_REVIEW_POLICY_TEXT, teamReviewPolicy } from '../team-review-policy.js';
|
|
11
|
-
import { ARTIFACT_FILES } from '../artifact-schemas.js';
|
|
12
|
-
import { writeEffortDecision } from '../effort-orchestrator.js';
|
|
13
|
-
import { createWorkOrderLedger, writeWorkOrderLedger } from '../work-order-ledger.js';
|
|
14
|
-
import { writeFromChatImgArtifacts } from '../from-chat-img-forensics.js';
|
|
15
|
-
import { renderTeamDashboardState, writeTeamDashboardState } from '../team-dashboard-renderer.js';
|
|
16
|
-
import { PIPELINE_PLAN_ARTIFACT, validatePipelinePlan, writePipelinePlan } from '../pipeline.js';
|
|
17
|
-
import { attachZellijSessionInteractive, launchTeamZellijView } from '../zellij/zellij-launcher.js';
|
|
18
|
-
import { maybeFinalizeRoute } from '../proof/auto-finalize.js';
|
|
19
|
-
import { runNativeAgentOrchestrator } from '../agents/agent-orchestrator.js';
|
|
20
|
-
import { ambientGoalContinuation, flag, readBoundedIntegerFlag, readFlagValue } from './command-utils.js';
|
|
2
|
+
import { nowIso, sksRoot, writeJsonAtomic } from '../fsx.js';
|
|
3
|
+
import { findLatestMission } from '../mission.js';
|
|
21
4
|
import { narutoCommand } from './naruto-command.js';
|
|
22
|
-
|
|
5
|
+
import { teamLegacyObserveCommand, teamLegacySubcommands } from './team-legacy-observe-command.js';
|
|
23
6
|
export async function team(args = []) {
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
7
|
+
if (teamLegacySubcommands.has(String(args[0] || ''))) {
|
|
8
|
+
return teamLegacyObserveCommand(String(args[0]), args.slice(1));
|
|
9
|
+
}
|
|
27
10
|
return redirectTeamCreateToNaruto(args);
|
|
28
11
|
}
|
|
29
12
|
async function redirectTeamCreateToNaruto(args = []) {
|
|
@@ -48,292 +31,4 @@ async function redirectTeamCreateToNaruto(args = []) {
|
|
|
48
31
|
}
|
|
49
32
|
return result;
|
|
50
33
|
}
|
|
51
|
-
export function parseTeamCreateArgs(args) {
|
|
52
|
-
const spec = parseTeamSpecArgs(args);
|
|
53
|
-
const prompt = spec.cleanArgs.join(' ').trim();
|
|
54
|
-
const normalized = normalizeTeamSpec({ agentSessions: spec.agentSessions, roleCounts: spec.roleCounts, prompt });
|
|
55
|
-
return { prompt, agentSessions: normalized.agentSessions, roleCounts: normalized.roleCounts, roster: normalized.roster };
|
|
56
|
-
}
|
|
57
|
-
function stripTeamCreateControlArgs(args = []) {
|
|
58
|
-
const booleanFlags = new Set([
|
|
59
|
-
'--open-zellij', '--zellij-open', '--no-open-zellij', '--no-zellij', '--no-attach',
|
|
60
|
-
'--mock', '--ollama', '--local-model', '--no-ollama', '--no-local-model'
|
|
61
|
-
]);
|
|
62
|
-
const valueFlags = new Set(['--ollama-model', '--local-model-model', '--ollama-base-url', '--local-model-base-url']);
|
|
63
|
-
const out = [];
|
|
64
|
-
for (let i = 0; i < args.length; i += 1) {
|
|
65
|
-
const arg = String(args[i]);
|
|
66
|
-
if (booleanFlags.has(arg))
|
|
67
|
-
continue;
|
|
68
|
-
if (valueFlags.has(arg)) {
|
|
69
|
-
i += 1;
|
|
70
|
-
continue;
|
|
71
|
-
}
|
|
72
|
-
if ([...valueFlags].some((flagName) => arg.startsWith(flagName + '=')))
|
|
73
|
-
continue;
|
|
74
|
-
out.push(args[i]);
|
|
75
|
-
}
|
|
76
|
-
return out;
|
|
77
|
-
}
|
|
78
|
-
export function buildTeamPlan(id, prompt, opts = {}) {
|
|
79
|
-
const spec = normalizeTeamSpec({ ...opts, prompt });
|
|
80
|
-
const { agentSessions, roleCounts, roster } = spec;
|
|
81
|
-
const fromChatImgRequired = hasFromChatImgSignal(prompt);
|
|
82
|
-
const ssotGuard = buildSsotGuard({ route: 'Team', mode: 'TEAM', task: prompt });
|
|
83
|
-
const requiredArtifacts = ['team-roster.json', 'work-order-ledger.json', 'effort-decision.json', 'team-dashboard-state.json', 'agents/agent-proof-evidence.json', 'team-analysis.md', ...(fromChatImgRequired ? [FROM_CHAT_IMG_WORK_ORDER_ARTIFACT, FROM_CHAT_IMG_SOURCE_INVENTORY_ARTIFACT, FROM_CHAT_IMG_VISUAL_MAP_ARTIFACT, FROM_CHAT_IMG_COVERAGE_ARTIFACT, FROM_CHAT_IMG_CHECKLIST_ARTIFACT, FROM_CHAT_IMG_TEMP_TRIWIKI_ARTIFACT, FROM_CHAT_IMG_QA_LOOP_ARTIFACT] : []), SSOT_GUARD_ARTIFACT, 'team-consensus.md', ...teamRuntimeRequiredArtifacts(), 'team-review.md', 'team-gate.json', TEAM_SESSION_CLEANUP_ARTIFACT, 'team-live.md', 'team-transcript.jsonl', 'team-dashboard.json', '.sneakoscope/wiki/context-pack.json', 'context7-evidence.jsonl'];
|
|
84
|
-
return {
|
|
85
|
-
schema_version: 1,
|
|
86
|
-
mission_id: id,
|
|
87
|
-
mode: 'team',
|
|
88
|
-
prompt,
|
|
89
|
-
agent_session_count: agentSessions,
|
|
90
|
-
default_agent_session_count: MIN_TEAM_REVIEWER_LANES,
|
|
91
|
-
target_active_slots: opts.targetActiveSlots || agentSessions,
|
|
92
|
-
role_counts: roleCounts,
|
|
93
|
-
session_policy: `Use at most ${opts.targetActiveSlots || agentSessions} native multi-session lanes at a time; parent orchestrator is not counted.`,
|
|
94
|
-
review_policy: teamReviewPolicy(),
|
|
95
|
-
review_gate: evaluateTeamReviewPolicyGate({ roleCounts, agentSessions, roster }),
|
|
96
|
-
bundle_size: roster.bundle_size,
|
|
97
|
-
roster,
|
|
98
|
-
goal_continuation: ambientGoalContinuation(),
|
|
99
|
-
team_model: {
|
|
100
|
-
phases: ['native_agent_intake', 'triwiki_stage_refresh', 'debate_team', 'triwiki_stage_refresh', 'runtime_task_graph', 'development_team', 'triwiki_stage_refresh', 'review', 'session_cleanup'],
|
|
101
|
-
analysis_team: `Read-only native analysis with exactly ${roster.bundle_size} native_agent_N agents.`,
|
|
102
|
-
debate_team: `Read-only role debate with exactly ${roster.bundle_size} participants.`,
|
|
103
|
-
development_team: `Fresh parallel development bundle with exactly ${roster.bundle_size} executor_N developers implementing disjoint slices.`,
|
|
104
|
-
review_team: `Validation runs at least ${MIN_TEAM_REVIEWER_LANES} independent reviewer/QA lanes before integration or final.`
|
|
105
|
-
},
|
|
106
|
-
team_runtime: teamRuntimePlanMetadata(),
|
|
107
|
-
persona_axioms: [
|
|
108
|
-
'Final users are intentionally low-context, impatient, self-interested, stubborn, and hostile to inconvenience.',
|
|
109
|
-
'Executors are capable developers and must receive disjoint write ownership.',
|
|
110
|
-
'Reviewers are strict, skeptical, and block unsupported correctness, DB safety, test, or evidence claims.',
|
|
111
|
-
MIN_TEAM_REVIEW_POLICY_TEXT
|
|
112
|
-
],
|
|
113
|
-
reasoning: teamReasoningPolicy(prompt, roster),
|
|
114
|
-
ssot_guard: ssotGuard,
|
|
115
|
-
context_tracking: triwikiContextTracking(),
|
|
116
|
-
phases: [
|
|
117
|
-
{ id: 'team_roster_confirmation', goal: 'Materialize Team roster and write team-roster.json.', agents: ['parent_orchestrator'], output: 'team-roster.json' },
|
|
118
|
-
{ id: 'native_agent_intake', goal: fromChatImgRequired ? `Complete From-Chat-IMG source inventory and coverage artifacts. Web/browser/webapp screenshots require Codex Chrome Extension readiness first; native Mac/non-web surfaces may use Codex Computer Use. ${CODEX_WEB_VERIFICATION_POLICY} ${CODEX_COMPUTER_USE_ONLY_POLICY}` : 'Read relevant TriWiki context and run read-only native agent intake agents before debate.', agents: roster.analysis_team.map((agent) => agent.id), max_parallel_native_sessions: opts.targetActiveSlots || agentSessions, write_policy: 'read-only', output: 'team-analysis.md' },
|
|
119
|
-
{ id: 'triwiki_refresh', goal: 'Refresh and validate TriWiki from agent intake findings.', agents: ['parent_orchestrator'], commands: ['sks wiki refresh', 'sks wiki validate .sneakoscope/wiki/context-pack.json'], output: '.sneakoscope/wiki/context-pack.json' },
|
|
120
|
-
{ id: 'ssot_guard', goal: ssotGuardPolicyText(), agents: ['parent_orchestrator'], output: SSOT_GUARD_ARTIFACT },
|
|
121
|
-
{ id: 'planning_debate', goal: 'Debate risks and viable approaches with refreshed context.', agents: roster.debate_team.map((agent) => agent.id), max_parallel_native_sessions: opts.targetActiveSlots || agentSessions, write_policy: 'read-only' },
|
|
122
|
-
{ id: 'runtime_task_graph_compile', goal: `Compile ${TEAM_GRAPH_ARTIFACT}, ${TEAM_RUNTIME_TASKS_ARTIFACT}, and ${TEAM_DECOMPOSITION_ARTIFACT}.`, agents: ['parent_orchestrator'] },
|
|
123
|
-
{ id: 'parallel_implementation', goal: 'Fresh executor developers implement disjoint slices.', agents: roster.development_team.map((agent) => agent.id), max_parallel_native_sessions: opts.targetActiveSlots || agentSessions, write_policy: 'workspace-write with explicit ownership' },
|
|
124
|
-
{ id: 'review_and_integrate', goal: `Review with at least ${MIN_TEAM_REVIEWER_LANES} independent lanes.`, agents: roster.validation_team.map((agent) => agent.id).concat(['parent_orchestrator']), min_reviewer_lanes: MIN_TEAM_REVIEWER_LANES },
|
|
125
|
-
{ id: 'session_cleanup', goal: `Write ${TEAM_SESSION_CLEANUP_ARTIFACT}.`, agents: ['parent_orchestrator'], output: TEAM_SESSION_CLEANUP_ARTIFACT }
|
|
126
|
-
],
|
|
127
|
-
invariants: ['The parent thread remains the orchestrator and owns final integration.', 'Native agent intake are read-only.', 'Implementation workers receive disjoint ownership scopes.', 'SSOT guard blocks source-of-truth drift before implementation and final gate pass.', MIN_TEAM_REVIEW_POLICY_TEXT],
|
|
128
|
-
live_visibility: { markdown: 'team-live.md', transcript: 'team-transcript.jsonl', dashboard: 'team-dashboard.json' },
|
|
129
|
-
required_artifacts: requiredArtifacts,
|
|
130
|
-
prompt_command: fromChatImgRequired ? '$From-Chat-IMG' : '$Team'
|
|
131
|
-
};
|
|
132
|
-
}
|
|
133
|
-
export function teamWorkflowMarkdown(plan) {
|
|
134
|
-
const ctx = plan.context_tracking || triwikiContextTracking();
|
|
135
|
-
return `# SKS Team Mission
|
|
136
|
-
|
|
137
|
-
Mission: ${plan.mission_id}
|
|
138
|
-
|
|
139
|
-
Prompt:
|
|
140
|
-
${plan.prompt}
|
|
141
|
-
|
|
142
|
-
## Codex App Prompt
|
|
143
|
-
|
|
144
|
-
\`\`\`text
|
|
145
|
-
${plan.prompt_command || '$Team'} ${plan.prompt}
|
|
146
|
-
|
|
147
|
-
Use at most ${plan.target_active_slots || plan.agent_session_count || MIN_TEAM_REVIEWER_LANES} native multi-session lanes at a time; the parent orchestrator is not counted. ${plan.review_policy?.text || MIN_TEAM_REVIEW_POLICY_TEXT}
|
|
148
|
-
\`\`\`
|
|
149
|
-
|
|
150
|
-
## Context Tracking
|
|
151
|
-
|
|
152
|
-
- SSOT: ${ctx.ssot}
|
|
153
|
-
- Pack: ${ctx.default_pack}
|
|
154
|
-
- Refresh: \`${ctx.pack_command}\`
|
|
155
|
-
- Validate: \`${ctx.validate_command}\`
|
|
156
|
-
|
|
157
|
-
## Analysis Agents
|
|
158
|
-
|
|
159
|
-
${plan.roster.analysis_team.map((agent) => `- ${agent.id}: ${agent.persona} [reasoning: ${formatAgentReasoning(agent)}]`).join('\n')}
|
|
160
|
-
|
|
161
|
-
## Debate Team
|
|
162
|
-
|
|
163
|
-
${plan.roster.debate_team.map((agent) => `- ${agent.id}: ${agent.persona} [reasoning: ${formatAgentReasoning(agent)}]`).join('\n')}
|
|
164
|
-
|
|
165
|
-
## Development Team
|
|
166
|
-
|
|
167
|
-
${plan.roster.development_team.map((agent) => `- ${agent.id}: ${agent.persona} [reasoning: ${formatAgentReasoning(agent)}]`).join('\n')}
|
|
168
|
-
|
|
169
|
-
## Validation Team
|
|
170
|
-
|
|
171
|
-
${plan.roster.validation_team.map((agent) => `- ${agent.id}: ${agent.persona} [reasoning: ${formatAgentReasoning(agent)}]`).join('\n')}
|
|
172
|
-
|
|
173
|
-
## Phases
|
|
174
|
-
|
|
175
|
-
${plan.phases.map((phase, idx) => `${idx + 1}. ${phase.id}: ${phase.goal}`).join('\n')}
|
|
176
|
-
|
|
177
|
-
## Invariants
|
|
178
|
-
|
|
179
|
-
${plan.invariants.map((x) => `- ${x}`).join('\n')}
|
|
180
|
-
`;
|
|
181
|
-
}
|
|
182
|
-
async function teamCommand(sub, args) {
|
|
183
|
-
const root = await sksRoot();
|
|
184
|
-
const missionArg = args[0] && !String(args[0]).startsWith('--') ? args[0] : 'latest';
|
|
185
|
-
const { resolveMissionId } = await import('./command-utils.js');
|
|
186
|
-
const id = await resolveMissionId(root, missionArg);
|
|
187
|
-
if (!id) {
|
|
188
|
-
console.error(`Usage: sks team ${sub} [mission-id|latest]`);
|
|
189
|
-
process.exitCode = 1;
|
|
190
|
-
return;
|
|
191
|
-
}
|
|
192
|
-
const { dir } = await loadMission(root, id);
|
|
193
|
-
if (sub === 'open-tmux' || sub === 'attach-tmux' || sub === 'cleanup-tmux') {
|
|
194
|
-
const result = { ok: false, status: 'removed_runtime', runtime: 'tmux', replacement: 'zellij', operator_actions: ['Use `sks team open-zellij`, `attach-zellij`, or `cleanup-zellij`.'] };
|
|
195
|
-
if (flag(args, '--json'))
|
|
196
|
-
return console.log(JSON.stringify(result, null, 2));
|
|
197
|
-
console.error('tmux runtime has been removed from SKS Team. Use Zellij commands instead.');
|
|
198
|
-
process.exitCode = 2;
|
|
199
|
-
return;
|
|
200
|
-
}
|
|
201
|
-
if (sub === 'open-zellij' || sub === 'attach-zellij') {
|
|
202
|
-
const plan = await readJson(path.join(dir, 'team-plan.json'), null);
|
|
203
|
-
if (!plan) {
|
|
204
|
-
console.error(`Team plan missing for ${id}; cannot open Zellij Team view.`);
|
|
205
|
-
process.exitCode = 2;
|
|
206
|
-
return;
|
|
207
|
-
}
|
|
208
|
-
const slotCount = await inferTeamZellijSlotCount(dir, plan);
|
|
209
|
-
const zellij = await launchTeamZellijView({ root, missionId: id, ledgerRoot: path.join(dir, 'agents'), slotCount, dryRun: flag(args, '--json'), attach: false });
|
|
210
|
-
if (flag(args, '--json'))
|
|
211
|
-
return console.log(JSON.stringify(zellij, null, 2));
|
|
212
|
-
if (!zellij.ok) {
|
|
213
|
-
console.error(`Zellij Team view blocked for ${id}: ${(zellij.blockers || []).join('; ') || 'Zellij launch failed'}`);
|
|
214
|
-
process.exitCode = 2;
|
|
215
|
-
return;
|
|
216
|
-
}
|
|
217
|
-
if (zellij.capability?.status === 'ok')
|
|
218
|
-
console.log(`Zellij: prepared Team lane(s) in ${zellij.session_name}`);
|
|
219
|
-
else
|
|
220
|
-
console.log(`Zellij: optional live panes unavailable (${(zellij.warnings || []).join('; ') || zellij.capability?.status || 'unknown'})`);
|
|
221
|
-
if (zellij.capability?.status === 'ok' && (sub === 'attach-zellij' || shouldAutoAttachTeamZellij(args))) {
|
|
222
|
-
attachZellijSessionInteractive(zellij.session_name, { cwd: root, configPath: zellij.clipboard_config_path });
|
|
223
|
-
}
|
|
224
|
-
return;
|
|
225
|
-
}
|
|
226
|
-
if (sub === 'event') {
|
|
227
|
-
const message = readFlagValue(args, '--message', '');
|
|
228
|
-
if (!message) {
|
|
229
|
-
console.error('Usage: sks team event [mission-id|latest] --agent <name> --phase <phase> --message "..."');
|
|
230
|
-
process.exitCode = 1;
|
|
231
|
-
return;
|
|
232
|
-
}
|
|
233
|
-
const phase = readFlagValue(args, '--phase', 'general');
|
|
234
|
-
const plan = await readJson(path.join(dir, 'team-plan.json'), null).catch(() => null);
|
|
235
|
-
const record = await appendTeamEvent(dir, { agent: readFlagValue(args, '--agent', 'parent_orchestrator'), phase, type: readFlagValue(args, '--type', 'status'), artifact: readFlagValue(args, '--artifact', ''), message });
|
|
236
|
-
if (flag(args, '--json'))
|
|
237
|
-
return console.log(JSON.stringify(record, null, 2));
|
|
238
|
-
console.log(`${record.ts} [${record.phase}] ${record.agent}: ${record.message}`);
|
|
239
|
-
return;
|
|
240
|
-
}
|
|
241
|
-
if (sub === 'message') {
|
|
242
|
-
const message = readFlagValue(args, '--message', '');
|
|
243
|
-
if (!message) {
|
|
244
|
-
console.error('Usage: sks team message [mission-id|latest] --from <agent> --to <agent|all> --message "..."');
|
|
245
|
-
process.exitCode = 1;
|
|
246
|
-
return;
|
|
247
|
-
}
|
|
248
|
-
const record = await appendTeamEvent(dir, { agent: readFlagValue(args, '--from', readFlagValue(args, '--agent', 'parent_orchestrator')), to: readFlagValue(args, '--to', 'all'), phase: readFlagValue(args, '--phase', 'communication'), type: 'message', message });
|
|
249
|
-
if (flag(args, '--json'))
|
|
250
|
-
return console.log(JSON.stringify(record, null, 2));
|
|
251
|
-
console.log(`${record.ts} [${record.phase}] ${record.agent} -> ${record.to}: ${record.message}`);
|
|
252
|
-
return;
|
|
253
|
-
}
|
|
254
|
-
if (sub === 'cleanup-zellij') {
|
|
255
|
-
const control = await requestTeamSessionCleanup(dir, { missionId: id, agent: readFlagValue(args, '--agent', 'parent_orchestrator'), reason: readFlagValue(args, '--reason', 'Team session ended; clean up live follow panes.'), finalMessage: 'Team session ended.' });
|
|
256
|
-
await appendTeamEvent(dir, { agent: readFlagValue(args, '--agent', 'parent_orchestrator'), phase: 'session_cleanup', type: 'cleanup', message: control.cleanup_reason || 'Team session cleanup requested.' });
|
|
257
|
-
const cleanup = { ok: true, runtime: 'zellij', mission_id: id, control, close_requested: flag(args, '--close-session') || flag(args, '--close') };
|
|
258
|
-
await writeJsonAtomic(path.join(dir, 'zellij-session-cleanup.json'), cleanup);
|
|
259
|
-
if (flag(args, '--json'))
|
|
260
|
-
return console.log(JSON.stringify(cleanup, null, 2));
|
|
261
|
-
console.log('Zellij cleanup: marked complete.');
|
|
262
|
-
console.log(renderTeamCleanupSummary(control));
|
|
263
|
-
return;
|
|
264
|
-
}
|
|
265
|
-
if (sub === 'status') {
|
|
266
|
-
const dashboard = await readTeamDashboard(dir);
|
|
267
|
-
if (flag(args, '--json'))
|
|
268
|
-
return console.log(JSON.stringify(dashboard || {}, null, 2));
|
|
269
|
-
if (!dashboard) {
|
|
270
|
-
console.error(`Team dashboard missing for ${id}.`);
|
|
271
|
-
process.exitCode = 2;
|
|
272
|
-
return;
|
|
273
|
-
}
|
|
274
|
-
console.log(`Team mission: ${id}`);
|
|
275
|
-
console.log(`Updated: ${dashboard.updated_at || 'unknown'}`);
|
|
276
|
-
console.log(`Agent sessions: ${dashboard.agent_session_count || MIN_TEAM_REVIEWER_LANES}`);
|
|
277
|
-
if (dashboard.role_counts)
|
|
278
|
-
console.log(`Role counts: ${formatRoleCounts(dashboard.role_counts)}`);
|
|
279
|
-
return;
|
|
280
|
-
}
|
|
281
|
-
if (sub === 'dashboard') {
|
|
282
|
-
await writeTeamDashboardState(dir, { missionId: id });
|
|
283
|
-
const state = await readJson(path.join(dir, ARTIFACT_FILES.team_dashboard_state), {});
|
|
284
|
-
if (flag(args, '--json'))
|
|
285
|
-
return console.log(JSON.stringify(state, null, 2));
|
|
286
|
-
console.log(renderTeamDashboardState(state));
|
|
287
|
-
return;
|
|
288
|
-
}
|
|
289
|
-
if (sub === 'log')
|
|
290
|
-
return console.log(await readTeamLive(dir));
|
|
291
|
-
if (sub === 'lane') {
|
|
292
|
-
const agent = readFlagValue(args, '--agent', 'parent_orchestrator');
|
|
293
|
-
const phase = readFlagValue(args, '--phase', '');
|
|
294
|
-
const lines = Number(readFlagValue(args, '--lines', '12'));
|
|
295
|
-
const text = await renderTeamAgentLane(dir, { missionId: id, agent, phase, lines });
|
|
296
|
-
if (flag(args, '--json'))
|
|
297
|
-
return console.log(JSON.stringify({ mission_id: id, agent, phase, lane: text }, null, 2));
|
|
298
|
-
console.log(text);
|
|
299
|
-
if (flag(args, '--follow') && !teamCleanupRequested(await readTeamControl(dir)) && !isTerminalTeamAgentStatus((await readTeamDashboard(dir).catch(() => null))?.agents?.[agent]?.status || '')) {
|
|
300
|
-
// Follow mode intentionally falls through only for interactive terminals in the full Zellij lane.
|
|
301
|
-
}
|
|
302
|
-
return;
|
|
303
|
-
}
|
|
304
|
-
if (sub === 'tail' || sub === 'watch') {
|
|
305
|
-
const lines = readFlagValue(args, '--lines', '20');
|
|
306
|
-
if (sub === 'watch' && !flag(args, '--raw'))
|
|
307
|
-
console.log(await renderTeamWatch(dir, { missionId: id, lines: Number(lines) }));
|
|
308
|
-
else
|
|
309
|
-
for (const line of await readTeamTranscriptTail(dir, Number(lines)))
|
|
310
|
-
console.log(line);
|
|
311
|
-
}
|
|
312
|
-
}
|
|
313
|
-
async function inferTeamZellijSlotCount(dir, plan = {}) {
|
|
314
|
-
const scheduler = await readJson(path.join(dir, 'agents', 'agent-scheduler-state.json'), null);
|
|
315
|
-
const lanes = await readJson(path.join(dir, 'agents', 'agent-zellij-lanes.json'), null);
|
|
316
|
-
const candidates = [
|
|
317
|
-
plan?.bundle_size,
|
|
318
|
-
plan?.agent_session_count,
|
|
319
|
-
lanes?.lane_count,
|
|
320
|
-
plan?.target_active_slots,
|
|
321
|
-
scheduler?.target_active_slots
|
|
322
|
-
].map((value) => Number(value)).filter((value) => Number.isFinite(value) && value > 0);
|
|
323
|
-
return Math.max(1, Math.min(100, Math.floor(candidates[0] || 5)));
|
|
324
|
-
}
|
|
325
|
-
function shouldAutoAttachTeamZellij(args = []) {
|
|
326
|
-
const list = (args || []).map((arg) => String(arg));
|
|
327
|
-
if (list.includes('--no-attach'))
|
|
328
|
-
return false;
|
|
329
|
-
if (list.includes('--json'))
|
|
330
|
-
return false;
|
|
331
|
-
if (process.env.SKS_NO_ZELLIJ_ATTACH === '1')
|
|
332
|
-
return false;
|
|
333
|
-
if (process.env.ZELLIJ)
|
|
334
|
-
return false;
|
|
335
|
-
if (list.includes('--attach'))
|
|
336
|
-
return true;
|
|
337
|
-
return Boolean(process.stdout.isTTY && process.stdin.isTTY);
|
|
338
|
-
}
|
|
339
34
|
//# sourceMappingURL=team-command.js.map
|