sneakoscope 2.0.16 → 2.0.17
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 +5 -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/.sks-build-stamp.json +4 -4
- package/dist/bin/sks.js +1 -1
- package/dist/cli/command-registry.js +1 -1
- package/dist/commands/proof.js +21 -0
- package/dist/commands/zellij-slot-pane.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/commands/naruto-command.js +17 -3
- 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/feature-registry.js +4 -2
- package/dist/core/fsx.js +1 -1
- package/dist/core/hooks-runtime.js +41 -4
- 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 +136 -0
- 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/version.js +1 -1
- package/dist/core/zellij/zellij-slot-pane-renderer.js +74 -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-speed-summary.js +22 -2
- package/package.json +14 -3
- package/schemas/agents/parallel-runtime-proof.schema.json +31 -0
|
@@ -19,6 +19,7 @@ import { buildNarutoGptFinalPack } from '../naruto/naruto-gpt-final-pack.js';
|
|
|
19
19
|
import { planNarutoZellijDashboard } from '../zellij/zellij-naruto-dashboard.js';
|
|
20
20
|
import { checkPromptPlaceholders } from '../prompt/prompt-placeholder-guard.js';
|
|
21
21
|
import { evaluateGitWorktreeCapability } from '../git/git-worktree-capability.js';
|
|
22
|
+
import { buildRuntimeProofSummary, renderRuntimeProofSummary } from '../agents/runtime-proof-summary.js';
|
|
22
23
|
const NARUTO_RESULT_SCHEMA = 'sks.naruto-command-result.v1';
|
|
23
24
|
const NARUTO_ROUTE = '$Naruto';
|
|
24
25
|
// $Naruto — Shadow Clone Swarm (影分身 / Kage Bunshin no Jutsu).
|
|
@@ -37,6 +38,8 @@ export async function narutoCommand(commandOrArgs = 'naruto', maybeArgs = []) {
|
|
|
37
38
|
return narutoDashboard(parsed);
|
|
38
39
|
if (parsed.action === 'workers')
|
|
39
40
|
return narutoWorkers(parsed);
|
|
41
|
+
if (parsed.action === 'proof')
|
|
42
|
+
return narutoProof(parsed);
|
|
40
43
|
return narutoRun(parsed);
|
|
41
44
|
}
|
|
42
45
|
async function narutoRun(parsed) {
|
|
@@ -702,6 +705,16 @@ async function narutoWorkers(parsed) {
|
|
|
702
705
|
console.log(`Active ${summary.active} · completed ${summary.completed} · failed ${summary.failed} · visible ${summary.visible_worker_panes.length} · headless ${summary.headless_workers.length}`);
|
|
703
706
|
});
|
|
704
707
|
}
|
|
708
|
+
async function narutoProof(parsed) {
|
|
709
|
+
const root = await sksRoot();
|
|
710
|
+
const id = parsed.missionId && parsed.missionId !== 'latest' ? parsed.missionId : await findLatestMission(root);
|
|
711
|
+
if (!id)
|
|
712
|
+
return emit(parsed, { schema: NARUTO_RESULT_SCHEMA, ok: false, action: 'proof', status: 'missing_mission' }, () => console.log('No Naruto mission found.'));
|
|
713
|
+
const summary = await buildRuntimeProofSummary(root, id);
|
|
714
|
+
return emit(parsed, { ...summary, action: 'proof' }, () => {
|
|
715
|
+
console.log(renderRuntimeProofSummary(summary));
|
|
716
|
+
});
|
|
717
|
+
}
|
|
705
718
|
async function narutoHelp(parsed) {
|
|
706
719
|
const help = {
|
|
707
720
|
schema: NARUTO_RESULT_SCHEMA,
|
|
@@ -711,7 +724,8 @@ async function narutoHelp(parsed) {
|
|
|
711
724
|
description: 'Shadow Clone Swarm: fan out up to ' + MAX_NARUTO_AGENT_COUNT + ' parallel clone sessions.',
|
|
712
725
|
usage: [
|
|
713
726
|
'sks naruto run "<task>" [--clones N] [--backend codex-sdk|fake|ollama] [--local-model|--no-ollama] [--work-items N] [--real] [--readonly] [--json]',
|
|
714
|
-
'sks naruto status [--mission <id>] [--json]'
|
|
727
|
+
'sks naruto status [--mission <id>] [--json]',
|
|
728
|
+
'sks naruto proof latest [--json]'
|
|
715
729
|
],
|
|
716
730
|
defaults: { clones: DEFAULT_NARUTO_CLONES, max_clones: MAX_NARUTO_AGENT_COUNT, backend: 'codex-sdk' }
|
|
717
731
|
};
|
|
@@ -726,7 +740,7 @@ function parseNarutoArgs(args = []) {
|
|
|
726
740
|
if (hasFlag(args, '--help') || hasFlag(args, '-h'))
|
|
727
741
|
args = ['help', ...args.filter((arg) => arg !== '--help' && arg !== '-h')];
|
|
728
742
|
const first = args[0] && !String(args[0]).startsWith('--') ? String(args[0]) : '';
|
|
729
|
-
const actions = new Set(['run', 'status', 'help', 'dashboard', 'workers']);
|
|
743
|
+
const actions = new Set(['run', 'status', 'help', 'dashboard', 'workers', 'proof']);
|
|
730
744
|
const action = (actions.has(first) ? first : 'run');
|
|
731
745
|
const rest = action === first ? args.slice(1) : args;
|
|
732
746
|
const json = hasFlag(args, '--json');
|
|
@@ -743,7 +757,7 @@ function parseNarutoArgs(args = []) {
|
|
|
743
757
|
const readonly = hasFlag(args, '--readonly') || hasFlag(args, '--read-only');
|
|
744
758
|
const writeModeRaw = String(readOption(args, '--write-mode', hasFlag(args, '--parallel-write') ? 'parallel' : '') || '');
|
|
745
759
|
const writeMode = (['proof-safe', 'parallel', 'serial', 'off'].includes(writeModeRaw) ? writeModeRaw : null);
|
|
746
|
-
const positionalMission = action === 'dashboard' || action === 'workers' || action === 'status'
|
|
760
|
+
const positionalMission = action === 'dashboard' || action === 'workers' || action === 'status' || action === 'proof'
|
|
747
761
|
? positionalArgs(rest, new Set()).find((arg) => /^latest$|^M-/.test(arg))
|
|
748
762
|
: null;
|
|
749
763
|
const missionId = String(readOption(args, '--mission', readOption(args, '--mission-id', positionalMission || 'latest')));
|
|
@@ -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
|
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
import path from 'node:path';
|
|
2
|
+
import { ARTIFACT_FILES } from '../artifact-schemas.js';
|
|
3
|
+
import { readJson, sksRoot, writeJsonAtomic } from '../fsx.js';
|
|
4
|
+
import { loadMission } from '../mission.js';
|
|
5
|
+
import { MIN_TEAM_REVIEWER_LANES } from '../team-review-policy.js';
|
|
6
|
+
import { renderTeamDashboardState, writeTeamDashboardState } from '../team-dashboard-renderer.js';
|
|
7
|
+
import { appendTeamEvent, formatRoleCounts, isTerminalTeamAgentStatus, readTeamControl, readTeamDashboard, readTeamLive, readTeamTranscriptTail, renderTeamAgentLane, renderTeamCleanupSummary, renderTeamWatch, requestTeamSessionCleanup, teamCleanupRequested } from '../team-live.js';
|
|
8
|
+
import { attachZellijSessionInteractive, launchTeamZellijView } from '../zellij/zellij-launcher.js';
|
|
9
|
+
import { flag, readFlagValue } from './command-utils.js';
|
|
10
|
+
export const teamLegacySubcommands = new Set([
|
|
11
|
+
'log',
|
|
12
|
+
'tail',
|
|
13
|
+
'watch',
|
|
14
|
+
'lane',
|
|
15
|
+
'status',
|
|
16
|
+
'dashboard',
|
|
17
|
+
'event',
|
|
18
|
+
'message',
|
|
19
|
+
'open-zellij',
|
|
20
|
+
'attach-zellij',
|
|
21
|
+
'cleanup-zellij',
|
|
22
|
+
'open-tmux',
|
|
23
|
+
'attach-tmux',
|
|
24
|
+
'cleanup-tmux'
|
|
25
|
+
]);
|
|
26
|
+
export async function teamLegacyObserveCommand(sub, args = []) {
|
|
27
|
+
const root = await sksRoot();
|
|
28
|
+
const missionArg = args[0] && !String(args[0]).startsWith('--') ? args[0] : 'latest';
|
|
29
|
+
const { resolveMissionId } = await import('./command-utils.js');
|
|
30
|
+
const id = await resolveMissionId(root, missionArg);
|
|
31
|
+
if (!id) {
|
|
32
|
+
console.error(`Usage: sks team ${sub} [mission-id|latest]`);
|
|
33
|
+
process.exitCode = 1;
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
const { dir } = await loadMission(root, id);
|
|
37
|
+
if (sub === 'open-tmux' || sub === 'attach-tmux' || sub === 'cleanup-tmux') {
|
|
38
|
+
const result = { ok: false, status: 'removed_runtime', runtime: 'tmux', replacement: 'zellij', operator_actions: ['Use `sks team open-zellij`, `attach-zellij`, or `cleanup-zellij`.'] };
|
|
39
|
+
if (flag(args, '--json'))
|
|
40
|
+
return console.log(JSON.stringify(result, null, 2));
|
|
41
|
+
console.error('tmux runtime has been removed from SKS Team. Use Zellij commands instead.');
|
|
42
|
+
process.exitCode = 2;
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
if (sub === 'open-zellij' || sub === 'attach-zellij') {
|
|
46
|
+
const plan = await readJson(path.join(dir, 'team-plan.json'), null);
|
|
47
|
+
if (!plan) {
|
|
48
|
+
console.error(`Team plan missing for ${id}; cannot open Zellij Team view.`);
|
|
49
|
+
process.exitCode = 2;
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
const slotCount = await inferTeamZellijSlotCount(dir, plan);
|
|
53
|
+
const zellij = await launchTeamZellijView({ root, missionId: id, ledgerRoot: path.join(dir, 'agents'), slotCount, dryRun: flag(args, '--json'), attach: false });
|
|
54
|
+
if (flag(args, '--json'))
|
|
55
|
+
return console.log(JSON.stringify(zellij, null, 2));
|
|
56
|
+
if (!zellij.ok) {
|
|
57
|
+
console.error(`Zellij Team view blocked for ${id}: ${(zellij.blockers || []).join('; ') || 'Zellij launch failed'}`);
|
|
58
|
+
process.exitCode = 2;
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
if (zellij.capability?.status === 'ok')
|
|
62
|
+
console.log(`Zellij: prepared Team lane(s) in ${zellij.session_name}`);
|
|
63
|
+
else
|
|
64
|
+
console.log(`Zellij: optional live panes unavailable (${(zellij.warnings || []).join('; ') || zellij.capability?.status || 'unknown'})`);
|
|
65
|
+
if (zellij.capability?.status === 'ok' && (sub === 'attach-zellij' || shouldAutoAttachTeamZellij(args))) {
|
|
66
|
+
attachZellijSessionInteractive(zellij.session_name, { cwd: root, configPath: zellij.clipboard_config_path });
|
|
67
|
+
}
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
if (sub === 'event') {
|
|
71
|
+
const message = readFlagValue(args, '--message', '');
|
|
72
|
+
if (!message) {
|
|
73
|
+
console.error('Usage: sks team event [mission-id|latest] --agent <name> --phase <phase> --message "..."');
|
|
74
|
+
process.exitCode = 1;
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
const phase = readFlagValue(args, '--phase', 'general');
|
|
78
|
+
const record = await appendTeamEvent(dir, { agent: readFlagValue(args, '--agent', 'parent_orchestrator'), phase, type: readFlagValue(args, '--type', 'status'), artifact: readFlagValue(args, '--artifact', ''), message });
|
|
79
|
+
if (flag(args, '--json'))
|
|
80
|
+
return console.log(JSON.stringify(record, null, 2));
|
|
81
|
+
console.log(`${record.ts} [${record.phase}] ${record.agent}: ${record.message}`);
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
if (sub === 'message') {
|
|
85
|
+
const message = readFlagValue(args, '--message', '');
|
|
86
|
+
if (!message) {
|
|
87
|
+
console.error('Usage: sks team message [mission-id|latest] --from <agent> --to <agent|all> --message "..."');
|
|
88
|
+
process.exitCode = 1;
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
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 });
|
|
92
|
+
if (flag(args, '--json'))
|
|
93
|
+
return console.log(JSON.stringify(record, null, 2));
|
|
94
|
+
console.log(`${record.ts} [${record.phase}] ${record.agent} -> ${record.to}: ${record.message}`);
|
|
95
|
+
return;
|
|
96
|
+
}
|
|
97
|
+
if (sub === 'cleanup-zellij') {
|
|
98
|
+
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.' });
|
|
99
|
+
await appendTeamEvent(dir, { agent: readFlagValue(args, '--agent', 'parent_orchestrator'), phase: 'session_cleanup', type: 'cleanup', message: control.cleanup_reason || 'Team session cleanup requested.' });
|
|
100
|
+
const cleanup = { ok: true, runtime: 'zellij', mission_id: id, control, close_requested: flag(args, '--close-session') || flag(args, '--close') };
|
|
101
|
+
await writeJsonAtomic(path.join(dir, 'zellij-session-cleanup.json'), cleanup);
|
|
102
|
+
if (flag(args, '--json'))
|
|
103
|
+
return console.log(JSON.stringify(cleanup, null, 2));
|
|
104
|
+
console.log('Zellij cleanup: marked complete.');
|
|
105
|
+
console.log(renderTeamCleanupSummary(control));
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
108
|
+
if (sub === 'status') {
|
|
109
|
+
const dashboard = await readTeamDashboard(dir);
|
|
110
|
+
if (flag(args, '--json'))
|
|
111
|
+
return console.log(JSON.stringify(dashboard || {}, null, 2));
|
|
112
|
+
if (!dashboard) {
|
|
113
|
+
console.error(`Team dashboard missing for ${id}.`);
|
|
114
|
+
process.exitCode = 2;
|
|
115
|
+
return;
|
|
116
|
+
}
|
|
117
|
+
console.log(`Team mission: ${id}`);
|
|
118
|
+
console.log(`Updated: ${dashboard.updated_at || 'unknown'}`);
|
|
119
|
+
console.log(`Agent sessions: ${dashboard.agent_session_count || MIN_TEAM_REVIEWER_LANES}`);
|
|
120
|
+
if (dashboard.role_counts)
|
|
121
|
+
console.log(`Role counts: ${formatRoleCounts(dashboard.role_counts)}`);
|
|
122
|
+
return;
|
|
123
|
+
}
|
|
124
|
+
if (sub === 'dashboard') {
|
|
125
|
+
await writeTeamDashboardState(dir, { missionId: id });
|
|
126
|
+
const state = await readJson(path.join(dir, ARTIFACT_FILES.team_dashboard_state), {});
|
|
127
|
+
if (flag(args, '--json'))
|
|
128
|
+
return console.log(JSON.stringify(state, null, 2));
|
|
129
|
+
console.log(renderTeamDashboardState(state));
|
|
130
|
+
return;
|
|
131
|
+
}
|
|
132
|
+
if (sub === 'log')
|
|
133
|
+
return console.log(await readTeamLive(dir));
|
|
134
|
+
if (sub === 'lane') {
|
|
135
|
+
const agent = readFlagValue(args, '--agent', 'parent_orchestrator');
|
|
136
|
+
const phase = readFlagValue(args, '--phase', '');
|
|
137
|
+
const lines = Number(readFlagValue(args, '--lines', '12'));
|
|
138
|
+
const text = await renderTeamAgentLane(dir, { missionId: id, agent, phase, lines });
|
|
139
|
+
if (flag(args, '--json'))
|
|
140
|
+
return console.log(JSON.stringify({ mission_id: id, agent, phase, lane: text }, null, 2));
|
|
141
|
+
console.log(text);
|
|
142
|
+
if (flag(args, '--follow') && !teamCleanupRequested(await readTeamControl(dir)) && !isTerminalTeamAgentStatus((await readTeamDashboard(dir).catch(() => null))?.agents?.[agent]?.status || '')) {
|
|
143
|
+
// Follow mode intentionally falls through only for interactive terminals in the full Zellij lane.
|
|
144
|
+
}
|
|
145
|
+
return;
|
|
146
|
+
}
|
|
147
|
+
if (sub === 'tail' || sub === 'watch') {
|
|
148
|
+
const lines = readFlagValue(args, '--lines', '20');
|
|
149
|
+
if (sub === 'watch' && !flag(args, '--raw'))
|
|
150
|
+
console.log(await renderTeamWatch(dir, { missionId: id, lines: Number(lines) }));
|
|
151
|
+
else
|
|
152
|
+
for (const line of await readTeamTranscriptTail(dir, Number(lines)))
|
|
153
|
+
console.log(line);
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
async function inferTeamZellijSlotCount(dir, plan = {}) {
|
|
157
|
+
const scheduler = await readJson(path.join(dir, 'agents', 'agent-scheduler-state.json'), null);
|
|
158
|
+
const lanes = await readJson(path.join(dir, 'agents', 'agent-zellij-lanes.json'), null);
|
|
159
|
+
const candidates = [
|
|
160
|
+
plan?.bundle_size,
|
|
161
|
+
plan?.agent_session_count,
|
|
162
|
+
lanes?.lane_count,
|
|
163
|
+
plan?.target_active_slots,
|
|
164
|
+
scheduler?.target_active_slots
|
|
165
|
+
].map((value) => Number(value)).filter((value) => Number.isFinite(value) && value > 0);
|
|
166
|
+
return Math.max(1, Math.min(100, Math.floor(candidates[0] || 5)));
|
|
167
|
+
}
|
|
168
|
+
function shouldAutoAttachTeamZellij(args = []) {
|
|
169
|
+
const list = (args || []).map((arg) => String(arg));
|
|
170
|
+
if (list.includes('--no-attach'))
|
|
171
|
+
return false;
|
|
172
|
+
if (list.includes('--json'))
|
|
173
|
+
return false;
|
|
174
|
+
if (process.env.SKS_NO_ZELLIJ_ATTACH === '1')
|
|
175
|
+
return false;
|
|
176
|
+
if (process.env.ZELLIJ)
|
|
177
|
+
return false;
|
|
178
|
+
if (list.includes('--attach'))
|
|
179
|
+
return true;
|
|
180
|
+
return Boolean(process.stdout.isTTY && process.stdin.isTTY);
|
|
181
|
+
}
|
|
182
|
+
//# sourceMappingURL=team-legacy-observe-command.js.map
|
package/dist/core/db-safety.js
CHANGED
|
@@ -5,6 +5,7 @@ import { evaluateMadSksPermissionGate, isMadSksRouteState } from './permission-g
|
|
|
5
5
|
import { resolveMadDbMutationPolicy } from './mad-db/mad-db-policy-resolver.js';
|
|
6
6
|
import { recordMadDbOperation } from './mad-db/mad-db-capability.js';
|
|
7
7
|
import { appendMadDbLedgerEvent, appendMadDbOperationLifecycle } from './mad-db/mad-db-ledger.js';
|
|
8
|
+
import { lifecycleHookFromUnknown, recordPendingMadDbLifecycleHook } from './mad-db/mad-db-result-lifecycle.js';
|
|
8
9
|
export const DEFAULT_DB_SAFETY_POLICY = Object.freeze({
|
|
9
10
|
schema_version: 1,
|
|
10
11
|
mode: 'read_only_default',
|
|
@@ -520,6 +521,14 @@ export async function checkDbOperation(root, state, payload, { duringNoQuestion
|
|
|
520
521
|
sqlHash,
|
|
521
522
|
destructive: classification.level === 'destructive'
|
|
522
523
|
});
|
|
524
|
+
const lifecycleHook = {
|
|
525
|
+
mission_id: String(state.mission_id),
|
|
526
|
+
operation_id: operationId,
|
|
527
|
+
cycle_id: madDbDecision.cycle_id || null,
|
|
528
|
+
tool_name: classification.toolName || null,
|
|
529
|
+
sql_hash: sqlHash,
|
|
530
|
+
destructive: classification.level === 'destructive'
|
|
531
|
+
};
|
|
523
532
|
const decision = {
|
|
524
533
|
allowed: true,
|
|
525
534
|
action: 'allow',
|
|
@@ -534,6 +543,8 @@ export async function checkDbOperation(root, state, payload, { duringNoQuestion
|
|
|
534
543
|
capability_file: 'mad-db-capability.json',
|
|
535
544
|
consumed: false,
|
|
536
545
|
operation_id: operationId,
|
|
546
|
+
lifecycle_result_pending: true,
|
|
547
|
+
ledger_result_hook: lifecycleHook,
|
|
537
548
|
operation_count: Number(madDbDecision.operation_count || 0) + 1,
|
|
538
549
|
max_operations: madDbDecision.max_operations || 20
|
|
539
550
|
}
|
|
@@ -555,6 +566,7 @@ export async function checkDbOperation(root, state, payload, { duringNoQuestion
|
|
|
555
566
|
});
|
|
556
567
|
decision.mad_db.consumed = updatedCapability?.consumed === true;
|
|
557
568
|
decision.mad_db.operation_count = updatedCapability?.operation_count ?? decision.mad_db.operation_count;
|
|
569
|
+
await recordPendingMadDbLifecycleHook(root, state.mission_id, lifecycleHook);
|
|
558
570
|
await appendJsonlBounded(path.join(missionDir(root, state.mission_id), 'db-safety.jsonl'), { ts: nowIso(), decision });
|
|
559
571
|
return decision;
|
|
560
572
|
}
|
|
@@ -567,6 +579,9 @@ export async function checkDbOperation(root, state, payload, { duringNoQuestion
|
|
|
567
579
|
}
|
|
568
580
|
return decision;
|
|
569
581
|
}
|
|
582
|
+
export function madDbLifecycleHookFromDecision(decision) {
|
|
583
|
+
return lifecycleHookFromUnknown(decision);
|
|
584
|
+
}
|
|
570
585
|
export async function checkSqlFile(file) {
|
|
571
586
|
const sql = await readText(file);
|
|
572
587
|
return classifySql(sql);
|
|
@@ -830,7 +830,7 @@ function commandMaturity(name) {
|
|
|
830
830
|
function routeMaturity(command) {
|
|
831
831
|
if (['$Answer', '$DFix', '$SKS', '$Fast-Mode', '$Wiki', '$Help'].includes(command))
|
|
832
832
|
return 'stable';
|
|
833
|
-
if (['$Team', '$Goal', '$DB', '$Computer-Use', '$CU', '$QA-LOOP', '$MAD-SKS'].includes(command))
|
|
833
|
+
if (['$Team', '$Goal', '$DB', '$Computer-Use', '$CU', '$QA-LOOP', '$MAD-SKS', '$MAD-DB'].includes(command))
|
|
834
834
|
return 'beta';
|
|
835
835
|
return 'labs';
|
|
836
836
|
}
|
|
@@ -858,7 +858,7 @@ function knownGapsForCommand(name) {
|
|
|
858
858
|
function routeVoxelContract(command) {
|
|
859
859
|
if (['$Image-UX-Review', '$UX-Review', '$PPT', '$From-Chat-IMG', '$GX'].includes(command))
|
|
860
860
|
return 'image/source/bbox voxel required';
|
|
861
|
-
if (command === '$DB' || command === '$MAD-SKS')
|
|
861
|
+
if (command === '$DB' || command === '$MAD-SKS' || command === '$MAD-DB')
|
|
862
862
|
return 'DB policy voxel required';
|
|
863
863
|
return 'TriWiki anchors required';
|
|
864
864
|
}
|
|
@@ -867,6 +867,8 @@ function routeKnownGaps(command) {
|
|
|
867
867
|
return ['live imagegen/CU evidence required'];
|
|
868
868
|
if (command === '$MAD-SKS')
|
|
869
869
|
return ['permission closed by owning gate'];
|
|
870
|
+
if (command === '$MAD-DB')
|
|
871
|
+
return ['one-cycle capability must be explicitly enabled, consumed, or revoked'];
|
|
870
872
|
return [];
|
|
871
873
|
}
|
|
872
874
|
function checkRow(id, ok, blockers = []) {
|