atris 3.30.2 → 3.30.4
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/commands/mission.js +177 -11
- package/package.json +1 -1
package/commands/mission.js
CHANGED
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
const fs = require('fs');
|
|
4
4
|
const path = require('path');
|
|
5
5
|
const crypto = require('crypto');
|
|
6
|
+
const readline = require('readline');
|
|
6
7
|
const { spawn, spawnSync } = require('child_process');
|
|
7
8
|
const {
|
|
8
9
|
resolveClaudeRunnerModel,
|
|
@@ -141,6 +142,58 @@ function printJsonOrText(payload, lines, asJson) {
|
|
|
141
142
|
for (const line of lines) console.log(line);
|
|
142
143
|
}
|
|
143
144
|
|
|
145
|
+
function missionRunInputRequired(asJson = false) {
|
|
146
|
+
const payload = {
|
|
147
|
+
ok: false,
|
|
148
|
+
action: 'mission_input_required',
|
|
149
|
+
prompt: 'What mission should Atris run?',
|
|
150
|
+
owner_prompt: 'Which team member should own it?',
|
|
151
|
+
example: 'atris mission run "make onboarding magical" --owner mission-lead',
|
|
152
|
+
};
|
|
153
|
+
if (asJson) {
|
|
154
|
+
console.log(JSON.stringify(payload, null, 2));
|
|
155
|
+
} else {
|
|
156
|
+
console.error('What mission should Atris run?');
|
|
157
|
+
console.error('Try: atris mission run "make onboarding magical" --owner mission-lead');
|
|
158
|
+
}
|
|
159
|
+
process.exit(1);
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
function askLine(rl, question) {
|
|
163
|
+
return new Promise((resolve) => rl.question(question, (answer) => resolve(String(answer || '').trim())));
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
function removeValueFlag(args, name) {
|
|
167
|
+
const out = [];
|
|
168
|
+
const prefix = `${name}=`;
|
|
169
|
+
for (let i = 0; i < args.length; i += 1) {
|
|
170
|
+
const arg = String(args[i]);
|
|
171
|
+
if (arg === name) {
|
|
172
|
+
if (args[i + 1] && !String(args[i + 1]).startsWith('--')) i += 1;
|
|
173
|
+
continue;
|
|
174
|
+
}
|
|
175
|
+
if (arg.startsWith(prefix)) continue;
|
|
176
|
+
out.push(args[i]);
|
|
177
|
+
}
|
|
178
|
+
return out;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
async function promptMissionRunInput(args) {
|
|
182
|
+
const defaultOwner = readFlag(args, '--owner', process.env.ATRIS_AGENT_ID || 'mission-lead');
|
|
183
|
+
const rl = readline.createInterface({ input: process.stdin, output: process.stderr });
|
|
184
|
+
try {
|
|
185
|
+
const objective = await askLine(rl, 'Mission: ');
|
|
186
|
+
if (!objective) missionRunInputRequired(false);
|
|
187
|
+
const owner = await askLine(rl, `Team member [${defaultOwner}]: `);
|
|
188
|
+
return {
|
|
189
|
+
objective,
|
|
190
|
+
args: [...removeValueFlag(args, '--owner'), '--owner', owner || defaultOwner],
|
|
191
|
+
};
|
|
192
|
+
} finally {
|
|
193
|
+
rl.close();
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
|
|
144
197
|
function loadTaskDb(asJson = false) {
|
|
145
198
|
try {
|
|
146
199
|
return require('../lib/task-db');
|
|
@@ -798,6 +851,94 @@ function inferRunObjectiveVerifier(objective, root = process.cwd()) {
|
|
|
798
851
|
return missionRunSmokeVerifier();
|
|
799
852
|
}
|
|
800
853
|
|
|
854
|
+
function markMissionRunContinuation(mission) {
|
|
855
|
+
return {
|
|
856
|
+
...mission,
|
|
857
|
+
started_from: 'mission_run_objective',
|
|
858
|
+
continue_on_complete: true,
|
|
859
|
+
continuation_policy: 'decide_and_start_next_useful_mission',
|
|
860
|
+
};
|
|
861
|
+
}
|
|
862
|
+
|
|
863
|
+
function continuationObjective(parent) {
|
|
864
|
+
return `Decide and start the next useful mission after: ${parent.objective}`;
|
|
865
|
+
}
|
|
866
|
+
|
|
867
|
+
function findActiveContinuationMission(parent, root = process.cwd()) {
|
|
868
|
+
return listMissions(root).find((mission) => (
|
|
869
|
+
mission.parent_mission_id === parent.id
|
|
870
|
+
&& mission.started_from === 'mission_run_continuation'
|
|
871
|
+
&& !TERMINAL_STATUSES.has(mission.status)
|
|
872
|
+
)) || null;
|
|
873
|
+
}
|
|
874
|
+
|
|
875
|
+
function seedMissionRunContinuation(parent, root = process.cwd(), proof = '') {
|
|
876
|
+
if (!parent || parent.status !== 'complete') return null;
|
|
877
|
+
if (parent.continue_on_complete !== true) return null;
|
|
878
|
+
if (parent.continuation_seeded_mission_id) return {
|
|
879
|
+
inserted: false,
|
|
880
|
+
reason: 'already_seeded',
|
|
881
|
+
mission_id: parent.continuation_seeded_mission_id,
|
|
882
|
+
};
|
|
883
|
+
|
|
884
|
+
const existing = findActiveContinuationMission(parent, root);
|
|
885
|
+
if (existing) return { inserted: false, reason: 'active_continuation_exists', mission: existing };
|
|
886
|
+
|
|
887
|
+
const owner = parent.owner || process.env.ATRIS_AGENT_ID || 'mission-lead';
|
|
888
|
+
const objective = continuationObjective(parent);
|
|
889
|
+
const mission = missionFromArgs([
|
|
890
|
+
objective,
|
|
891
|
+
'--owner',
|
|
892
|
+
owner,
|
|
893
|
+
'--runner',
|
|
894
|
+
'codex_goal',
|
|
895
|
+
'--lane',
|
|
896
|
+
parent.lane || 'workspace',
|
|
897
|
+
'--cadence',
|
|
898
|
+
'manual',
|
|
899
|
+
'--stop',
|
|
900
|
+
'next useful mission is started, or no useful next mission remains',
|
|
901
|
+
]);
|
|
902
|
+
const nextMission = {
|
|
903
|
+
...mission,
|
|
904
|
+
started_from: 'mission_run_continuation',
|
|
905
|
+
parent_mission_id: parent.id,
|
|
906
|
+
parent_objective: parent.objective,
|
|
907
|
+
continue_on_complete: false,
|
|
908
|
+
continuation_policy: 'choose_next_mission',
|
|
909
|
+
parent_proof: proof || parent.receipt_path || null,
|
|
910
|
+
next_action: `decide next mission, then run: atris mission run "<next useful mission>" --owner ${owner}`,
|
|
911
|
+
};
|
|
912
|
+
|
|
913
|
+
ensureMemberMissionFile(nextMission.owner, root, nextMission.objective);
|
|
914
|
+
const { mission: saved } = saveMission(nextMission, root, 'mission_continuation_started', {
|
|
915
|
+
parent_mission_id: parent.id,
|
|
916
|
+
parent_objective: parent.objective,
|
|
917
|
+
proof: proof || null,
|
|
918
|
+
});
|
|
919
|
+
const worktreeBaseline = captureMissionWorktreeBaseline(saved, root);
|
|
920
|
+
const seededAt = stampIso();
|
|
921
|
+
const { mission: updatedParent } = saveMission({
|
|
922
|
+
...parent,
|
|
923
|
+
continuation_seeded_mission_id: saved.id,
|
|
924
|
+
continuation_seeded_at: seededAt,
|
|
925
|
+
continuation_seeded_objective: saved.objective,
|
|
926
|
+
}, root, 'mission_continuation_seeded', {
|
|
927
|
+
continuation_mission_id: saved.id,
|
|
928
|
+
continuation_objective: saved.objective,
|
|
929
|
+
});
|
|
930
|
+
return {
|
|
931
|
+
inserted: true,
|
|
932
|
+
mission: saved,
|
|
933
|
+
parent: updatedParent,
|
|
934
|
+
worktree_baseline: worktreeBaseline ? {
|
|
935
|
+
path: path.relative(root, missionBaselinePath(saved.id, root)),
|
|
936
|
+
dirty_count: worktreeBaseline.dirty_count,
|
|
937
|
+
dirty_hash: worktreeBaseline.dirty_hash,
|
|
938
|
+
} : null,
|
|
939
|
+
};
|
|
940
|
+
}
|
|
941
|
+
|
|
801
942
|
function startMission(args) {
|
|
802
943
|
const asJson = wantsJson(args);
|
|
803
944
|
const mission = missionFromArgs(args);
|
|
@@ -878,7 +1019,7 @@ function startMissionFromRunObjective(objective, args) {
|
|
|
878
1019
|
if (hasFlag(args, '--always-on')) startArgs.push('--always-on');
|
|
879
1020
|
if (hasFlag(args, '--xp-task') || hasFlag(args, '--agent-xp')) startArgs.push('--xp-task');
|
|
880
1021
|
|
|
881
|
-
const mission = missionFromArgs(startArgs);
|
|
1022
|
+
const mission = markMissionRunContinuation(missionFromArgs(startArgs));
|
|
882
1023
|
const warnings = [missingVerifierWarning(mission)].filter(Boolean);
|
|
883
1024
|
ensureMemberMissionFile(mission.owner, process.cwd(), mission.objective);
|
|
884
1025
|
const { mission: saved } = saveMission(mission, process.cwd(), 'mission_started', { objective: mission.objective, source: 'mission_run_objective' });
|
|
@@ -2176,6 +2317,15 @@ async function runMission(args) {
|
|
|
2176
2317
|
['--json', '--due', '--no-claude', '--no-verify', '--complete-on-pass', '--no-drain', '--always-on', '--xp-task', '--agent-xp'],
|
|
2177
2318
|
).join(' ').trim();
|
|
2178
2319
|
|
|
2320
|
+
if (!dueMode && !ref) {
|
|
2321
|
+
if (asJson || !process.stdin.isTTY || !process.stderr.isTTY) {
|
|
2322
|
+
missionRunInputRequired(asJson);
|
|
2323
|
+
}
|
|
2324
|
+
const prompted = await promptMissionRunInput(args);
|
|
2325
|
+
startMissionFromRunObjective(prompted.objective, prompted.args);
|
|
2326
|
+
return;
|
|
2327
|
+
}
|
|
2328
|
+
|
|
2179
2329
|
let mission = dueMode && !ref ? selectDueMission() : resolveMission(ref);
|
|
2180
2330
|
if (!mission && dueMode && !ref) {
|
|
2181
2331
|
printJsonOrText(
|
|
@@ -2283,6 +2433,7 @@ async function runMission(args) {
|
|
|
2283
2433
|
const startedAt = Date.now();
|
|
2284
2434
|
let backoffAttempt = 0;
|
|
2285
2435
|
let lastRateLimit = null;
|
|
2436
|
+
let continuationGoal = null;
|
|
2286
2437
|
|
|
2287
2438
|
const sessionLabel = skipWorker
|
|
2288
2439
|
? 'caller-session'
|
|
@@ -2495,6 +2646,10 @@ async function runMission(args) {
|
|
|
2495
2646
|
verifier: verifierResult ? (verifierResult.passed ? 'passed' : 'failed') : 'not_run',
|
|
2496
2647
|
receipt: receiptPath,
|
|
2497
2648
|
});
|
|
2649
|
+
if (newStatus === 'complete') {
|
|
2650
|
+
continuationGoal = seedMissionRunContinuation(mission, cwd, receiptPath);
|
|
2651
|
+
if (continuationGoal?.parent) mission = continuationGoal.parent;
|
|
2652
|
+
}
|
|
2498
2653
|
refreshCodexGoalController(cwd);
|
|
2499
2654
|
|
|
2500
2655
|
console.error(`[tick ${tickIdx}] status=${result.status} reason=${result.reason} verifier=${verifierResult ? (verifierResult.passed ? 'pass' : 'fail') : 'skip'} -> ${receiptPath || '-'}`);
|
|
@@ -2572,7 +2727,7 @@ async function runMission(args) {
|
|
|
2572
2727
|
const codexGoalState = refreshCodexGoalController(cwd);
|
|
2573
2728
|
|
|
2574
2729
|
printJsonOrText(
|
|
2575
|
-
{ ok: true, action: 'mission_run', mission, ran_ticks: ranTicks, tick_count: ticks.length, ticks, pause_reason: pauseReason, session_id: sessionId, summary_receipt: finalReceipt, worktree: summaryWorktree, codex_goal_state: codexGoalState },
|
|
2730
|
+
{ ok: true, action: 'mission_run', mission, ran_ticks: ranTicks, tick_count: ticks.length, ticks, pause_reason: pauseReason, session_id: sessionId, summary_receipt: finalReceipt, worktree: summaryWorktree, codex_goal_state: codexGoalState, continuation_goal: continuationGoal },
|
|
2576
2731
|
[
|
|
2577
2732
|
`Ran mission ${mission.id}`,
|
|
2578
2733
|
` objective: ${mission.objective}`,
|
|
@@ -2581,6 +2736,7 @@ async function runMission(args) {
|
|
|
2581
2736
|
pauseReason ? ` pause: ${pauseReason}` : null,
|
|
2582
2737
|
` session: ${sessionId || '(none)'}`,
|
|
2583
2738
|
` summary receipt: ${finalReceipt}`,
|
|
2739
|
+
continuationGoal?.mission ? ` next goal: ${continuationGoal.mission.objective}` : null,
|
|
2584
2740
|
].filter(Boolean),
|
|
2585
2741
|
asJson,
|
|
2586
2742
|
);
|
|
@@ -2709,15 +2865,20 @@ function tickMission(args) {
|
|
|
2709
2865
|
receipt: receiptPath,
|
|
2710
2866
|
summary: summary || undefined,
|
|
2711
2867
|
});
|
|
2868
|
+
const continuationGoal = saved.status === 'complete'
|
|
2869
|
+
? seedMissionRunContinuation(saved, cwd, receiptPath)
|
|
2870
|
+
: null;
|
|
2871
|
+
const outputMission = continuationGoal?.parent || saved;
|
|
2712
2872
|
const codexGoalState = refreshCodexGoalController(process.cwd());
|
|
2713
2873
|
printJsonOrText(
|
|
2714
|
-
{ ok: true, action: 'mission_tick', mission:
|
|
2874
|
+
{ ok: true, action: 'mission_tick', mission: outputMission, tick: tickRecord, verifier_result: verifierResult, receipt_path: receiptPath, log_path: logPath, codex_goal_state: codexGoalState, continuation_goal: continuationGoal },
|
|
2715
2875
|
[
|
|
2716
|
-
`Ticked mission: ${
|
|
2717
|
-
`State: ${
|
|
2876
|
+
`Ticked mission: ${outputMission.objective}`,
|
|
2877
|
+
`State: ${outputMission.status}`,
|
|
2718
2878
|
`Tick: ${tickIdx}`,
|
|
2719
|
-
`Next: ${
|
|
2879
|
+
`Next: ${outputMission.next_action}`,
|
|
2720
2880
|
...(receiptPath ? [`Receipt: ${receiptPath}`] : []),
|
|
2881
|
+
...(continuationGoal?.mission ? [`Next goal: ${continuationGoal.mission.objective}`] : []),
|
|
2721
2882
|
],
|
|
2722
2883
|
asJson,
|
|
2723
2884
|
);
|
|
@@ -2799,24 +2960,28 @@ function completeMission(args) {
|
|
|
2799
2960
|
result: completion.result,
|
|
2800
2961
|
};
|
|
2801
2962
|
const { mission: saved } = saveMission(next, process.cwd(), 'mission_completed', { proof, completion_gate: next.completion_gate });
|
|
2802
|
-
const
|
|
2963
|
+
const continuationGoal = seedMissionRunContinuation(saved, process.cwd(), proof);
|
|
2964
|
+
const outputMission = continuationGoal?.parent || saved;
|
|
2965
|
+
const logPath = appendMemberLog(outputMission.owner, 'Mission completed', { mission: outputMission.objective, proof });
|
|
2803
2966
|
const codexGoalState = refreshCodexGoalController(process.cwd());
|
|
2804
2967
|
printJsonOrText(
|
|
2805
2968
|
{
|
|
2806
2969
|
ok: true,
|
|
2807
2970
|
action: 'mission_completed',
|
|
2808
|
-
mission:
|
|
2971
|
+
mission: outputMission,
|
|
2809
2972
|
landing: completion.landing,
|
|
2810
2973
|
result: completion.result,
|
|
2811
2974
|
log_path: logPath,
|
|
2812
2975
|
codex_goal_state: codexGoalState,
|
|
2813
2976
|
xp_next_command: xpNextCommand,
|
|
2977
|
+
continuation_goal: continuationGoal,
|
|
2814
2978
|
},
|
|
2815
2979
|
[
|
|
2816
|
-
`Completed mission: ${
|
|
2980
|
+
`Completed mission: ${outputMission.objective}`,
|
|
2817
2981
|
...missionResultLines(completion),
|
|
2818
2982
|
`Proof: ${proof}`,
|
|
2819
2983
|
...(xpNextCommand ? [`AgentXP: ${xpNextCommand}`] : []),
|
|
2984
|
+
...(continuationGoal?.mission ? [`Next goal: ${continuationGoal.mission.objective}`] : []),
|
|
2820
2985
|
],
|
|
2821
2986
|
asJson,
|
|
2822
2987
|
);
|
|
@@ -2994,9 +3159,10 @@ atris mission - durable goal + loop + owner + proof state
|
|
|
2994
3159
|
atris mission goal [--heartbeat] [--json]
|
|
2995
3160
|
atris mission goal-loop [--max-wall 28800] [--max-iterations 32] [--no-claude] [--json]
|
|
2996
3161
|
atris mission tick <id> [--verify] [--complete-on-pass] [--summary "..."] [--json]
|
|
2997
|
-
atris mission run
|
|
3162
|
+
atris mission run ["objective"|id|--due] [--owner <member>] [--max-ticks 4] [--max-wall 3600] [--cadence "15m"]
|
|
2998
3163
|
[--no-claude] [--no-verify] [--complete-on-pass] [--no-drain] [--json]
|
|
2999
|
-
(
|
|
3164
|
+
(bare run prompts for mission + owner; --due runs the saved queue)
|
|
3165
|
+
(mission-run completions seed the next visible goal: decide and start the next useful mission)
|
|
3000
3166
|
atris mission complete <id> --proof "..."
|
|
3001
3167
|
atris mission stop <id> [--pause] [--reason "..."]
|
|
3002
3168
|
|