atris 3.15.22 → 3.15.23
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 +117 -7
- package/package.json +1 -1
package/commands/mission.js
CHANGED
|
@@ -132,6 +132,79 @@ function printJsonOrText(payload, lines, asJson) {
|
|
|
132
132
|
for (const line of lines) console.log(line);
|
|
133
133
|
}
|
|
134
134
|
|
|
135
|
+
function loadTaskDb(asJson = false) {
|
|
136
|
+
try {
|
|
137
|
+
return require('../lib/task-db');
|
|
138
|
+
} catch (error) {
|
|
139
|
+
const message = error && error.message ? error.message : String(error);
|
|
140
|
+
if (error?.code === 'ERR_UNKNOWN_BUILTIN_MODULE' || /node:sqlite/.test(message)) {
|
|
141
|
+
exitMissionError('AgentXP mission tasks require Node 22+ with node:sqlite.', 2, asJson);
|
|
142
|
+
}
|
|
143
|
+
throw error;
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
function writeMissionTaskProjection(taskDb, db, workspaceRoot) {
|
|
148
|
+
const projection = taskDb.taskProjection(db, { workspaceRoot, limit: 500 });
|
|
149
|
+
const outPath = path.join(workspaceRoot, '.atris', 'state', 'tasks.projection.json');
|
|
150
|
+
fs.mkdirSync(path.dirname(outPath), { recursive: true });
|
|
151
|
+
fs.writeFileSync(outPath, JSON.stringify(projection, null, 2) + '\n', 'utf8');
|
|
152
|
+
return { projection, outPath };
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
function missionTaskRef(task) {
|
|
156
|
+
return task?.display_id || task?.legacy_ref || String(task?.id || '').replace(/[^a-zA-Z0-9]/g, '').toUpperCase().slice(0, 8);
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
function createMissionXpTask(mission, root = process.cwd(), asJson = false) {
|
|
160
|
+
const taskDb = loadTaskDb(asJson);
|
|
161
|
+
const db = taskDb.open();
|
|
162
|
+
const workspaceRoot = taskDb.workspaceRoot(root);
|
|
163
|
+
const title = `Mission XP: ${mission.objective}`;
|
|
164
|
+
const metadata = {
|
|
165
|
+
assigned_to: mission.owner,
|
|
166
|
+
delegate_via: 'mission_goal_loop',
|
|
167
|
+
created_for_day: todayName(),
|
|
168
|
+
goal_id: mission.id,
|
|
169
|
+
goal_objective: mission.objective,
|
|
170
|
+
mission_id: mission.id,
|
|
171
|
+
mission_objective: mission.objective,
|
|
172
|
+
mission_owner: mission.owner,
|
|
173
|
+
mission_lane: mission.lane,
|
|
174
|
+
mission_runner: mission.runner,
|
|
175
|
+
verify: mission.verifier || null,
|
|
176
|
+
stop_condition: mission.stop_condition || null,
|
|
177
|
+
};
|
|
178
|
+
const result = taskDb.addTask(db, {
|
|
179
|
+
title,
|
|
180
|
+
tag: 'agent-xp',
|
|
181
|
+
workspaceRoot,
|
|
182
|
+
sourceKey: `mission-xp:${mission.id}`,
|
|
183
|
+
status: 'claimed',
|
|
184
|
+
claimedBy: mission.owner,
|
|
185
|
+
metadata,
|
|
186
|
+
});
|
|
187
|
+
const rows = taskDb.withTaskDisplayRefs(taskDb.listTasks(db, { workspaceRoot }));
|
|
188
|
+
const task = rows.find(row => row.id === result.id);
|
|
189
|
+
if (task) {
|
|
190
|
+
taskDb.noteTask(db, {
|
|
191
|
+
id: task.id,
|
|
192
|
+
actor: process.env.ATRIS_AGENT_ID || mission.owner || 'mission-lead',
|
|
193
|
+
content: `Mission goal loop XP bridge for ${mission.id}. Proof goes through task ready; AgentXP lands only after human accept.`,
|
|
194
|
+
});
|
|
195
|
+
}
|
|
196
|
+
const { outPath } = writeMissionTaskProjection(taskDb, db, workspaceRoot);
|
|
197
|
+
return {
|
|
198
|
+
task_id: result.id,
|
|
199
|
+
ref: missionTaskRef(task) || result.id,
|
|
200
|
+
title,
|
|
201
|
+
status: task?.status || 'claimed',
|
|
202
|
+
assigned_to: mission.owner,
|
|
203
|
+
inserted: result.inserted !== false,
|
|
204
|
+
projection_path: outPath,
|
|
205
|
+
};
|
|
206
|
+
}
|
|
207
|
+
|
|
135
208
|
function statePaths(root = process.cwd()) {
|
|
136
209
|
const stateDir = path.join(root, '.atris', 'state');
|
|
137
210
|
return {
|
|
@@ -323,6 +396,7 @@ function renderMemberNowMarkdown(owner, missions) {
|
|
|
323
396
|
lines.push(`- cadence: ${mission.cadence}`);
|
|
324
397
|
lines.push(`- runner: ${mission.runner}`);
|
|
325
398
|
lines.push(`- lane: ${mission.lane}`);
|
|
399
|
+
if (mission.xp_task?.ref) lines.push(`- AgentXP task: ${mission.xp_task.ref}`);
|
|
326
400
|
if (mission.verifier) lines.push(`- verifier: ${mission.verifier}`);
|
|
327
401
|
if (mission.stop_condition) lines.push(`- stop: ${mission.stop_condition}`);
|
|
328
402
|
if (mission.next_action) lines.push(`- next: ${mission.next_action}`);
|
|
@@ -366,6 +440,7 @@ function renderMissionStatus(root = process.cwd()) {
|
|
|
366
440
|
lines.push(` - owner: ${mission.owner}`);
|
|
367
441
|
lines.push(` - state: ${mission.status}`);
|
|
368
442
|
lines.push(` - next: ${mission.next_action || 'tick or verify'}`);
|
|
443
|
+
if (mission.xp_task?.ref) lines.push(` - AgentXP task: ${mission.xp_task.ref}`);
|
|
369
444
|
if (mission.receipt_path) lines.push(` - proof: ${mission.receipt_path}`);
|
|
370
445
|
}
|
|
371
446
|
lines.push('');
|
|
@@ -376,6 +451,18 @@ function renderMissionStatus(root = process.cwd()) {
|
|
|
376
451
|
return paths.statusNow;
|
|
377
452
|
}
|
|
378
453
|
|
|
454
|
+
function missionXpTaskRefFromMission(mission) {
|
|
455
|
+
if (mission?.xp_task?.ref) return mission.xp_task.ref;
|
|
456
|
+
if (mission?.xp_task_enabled && mission?.task_ids?.[0]) return mission.task_ids[0];
|
|
457
|
+
return '';
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
function missionXpReadyAction(mission, receiptPath) {
|
|
461
|
+
const ref = missionXpTaskRefFromMission(mission);
|
|
462
|
+
if (!ref || !receiptPath) return null;
|
|
463
|
+
return `queue AgentXP review: atris task ready ${ref} --proof "${receiptPath}"`;
|
|
464
|
+
}
|
|
465
|
+
|
|
379
466
|
function missionFromArgs(args) {
|
|
380
467
|
const objective = stripKnownFlags(args, [
|
|
381
468
|
'--owner',
|
|
@@ -387,7 +474,7 @@ function missionFromArgs(args) {
|
|
|
387
474
|
'--stop',
|
|
388
475
|
'--task',
|
|
389
476
|
'--ask',
|
|
390
|
-
], ['--json', '--always-on']).join(' ').trim();
|
|
477
|
+
], ['--json', '--always-on', '--xp-task', '--agent-xp']).join(' ').trim();
|
|
391
478
|
if (!objective) {
|
|
392
479
|
exitMissionError('Usage: atris mission start "<objective>" --owner <member> [--verify "..."] [--cadence manual]', 1, wantsJson(args));
|
|
393
480
|
}
|
|
@@ -401,6 +488,7 @@ function missionFromArgs(args) {
|
|
|
401
488
|
const taskIds = readRepeatedFlag(args, '--task');
|
|
402
489
|
const humanAsks = readRepeatedFlag(args, '--ask');
|
|
403
490
|
const alwaysOn = hasFlag(args, '--always-on');
|
|
491
|
+
const xpTaskEnabled = hasFlag(args, '--xp-task') || hasFlag(args, '--agent-xp');
|
|
404
492
|
const id = missionId(objective);
|
|
405
493
|
const mission = {
|
|
406
494
|
schema: 'atris.mission.v1',
|
|
@@ -414,6 +502,7 @@ function missionFromArgs(args) {
|
|
|
414
502
|
lane,
|
|
415
503
|
verifier,
|
|
416
504
|
always_on: alwaysOn,
|
|
505
|
+
xp_task_enabled: xpTaskEnabled,
|
|
417
506
|
stop_condition: stopCondition,
|
|
418
507
|
task_ids: taskIds,
|
|
419
508
|
human_asks: humanAsks,
|
|
@@ -437,6 +526,14 @@ function missingVerifierWarning(mission) {
|
|
|
437
526
|
function startMission(args) {
|
|
438
527
|
const asJson = wantsJson(args);
|
|
439
528
|
const mission = missionFromArgs(args);
|
|
529
|
+
if (mission.xp_task_enabled) {
|
|
530
|
+
const xpTask = createMissionXpTask(mission, process.cwd(), asJson);
|
|
531
|
+
mission.xp_task = xpTask;
|
|
532
|
+
mission.task_ids = Array.from(new Set([...(mission.task_ids || []), xpTask.task_id]));
|
|
533
|
+
if (!mission.verifier && !mission.always_on) {
|
|
534
|
+
mission.next_action = `work task then run: atris task ready ${xpTask.ref} --proof "<proof>"`;
|
|
535
|
+
}
|
|
536
|
+
}
|
|
440
537
|
const warnings = [missingVerifierWarning(mission)].filter(Boolean);
|
|
441
538
|
ensureMemberMissionFile(mission.owner, process.cwd(), mission.objective);
|
|
442
539
|
const { mission: saved } = saveMission(mission, process.cwd(), 'mission_started', { objective: mission.objective });
|
|
@@ -455,6 +552,7 @@ function startMission(args) {
|
|
|
455
552
|
`Owner: ${saved.owner}`,
|
|
456
553
|
`State: ${saved.status}`,
|
|
457
554
|
...warnings.map((warning) => `Warning: ${warning.message}`),
|
|
555
|
+
...(saved.xp_task ? [`AgentXP task: ${saved.xp_task.ref}`] : []),
|
|
458
556
|
`Next: atris mission tick ${saved.id}`,
|
|
459
557
|
],
|
|
460
558
|
asJson,
|
|
@@ -694,6 +792,10 @@ function codexGoalObjective(mission) {
|
|
|
694
792
|
}
|
|
695
793
|
|
|
696
794
|
function codexGoalNextCommand(mission) {
|
|
795
|
+
if (mission.status === 'ready') {
|
|
796
|
+
const xpAction = missionXpReadyAction(mission, mission.receipt_path);
|
|
797
|
+
if (xpAction) return xpAction.replace(/^queue AgentXP review: /, '');
|
|
798
|
+
}
|
|
697
799
|
if (mission.verifier && missionDueAt(mission)) {
|
|
698
800
|
return 'atris mission run --due --max-ticks 1 --complete-on-pass';
|
|
699
801
|
}
|
|
@@ -1294,7 +1396,9 @@ async function runMission(args) {
|
|
|
1294
1396
|
worktree: tickWorktree,
|
|
1295
1397
|
});
|
|
1296
1398
|
|
|
1399
|
+
const xpReadyAction = missionXpReadyAction(mission, receiptPath);
|
|
1297
1400
|
const newStatus = (verifierResult?.passed && mission.always_on) ? 'running' :
|
|
1401
|
+
(verifierResult?.passed && xpReadyAction) ? 'ready' :
|
|
1298
1402
|
(verifierResult?.passed && completeOnPass) ? 'complete' :
|
|
1299
1403
|
(verifierResult?.passed ? 'ready' :
|
|
1300
1404
|
(verifierResult ? 'blocked' :
|
|
@@ -1302,6 +1406,8 @@ async function runMission(args) {
|
|
|
1302
1406
|
let nextAction = mission.next_action;
|
|
1303
1407
|
if (verifierResult?.passed && mission.always_on) {
|
|
1304
1408
|
nextAction = nextCandidateTickAction(mission);
|
|
1409
|
+
} else if (verifierResult?.passed && xpReadyAction) {
|
|
1410
|
+
nextAction = xpReadyAction;
|
|
1305
1411
|
} else if (verifierResult?.passed && completeOnPass) {
|
|
1306
1412
|
nextAction = 'mission complete';
|
|
1307
1413
|
} else if (verifierResult?.passed) {
|
|
@@ -1482,9 +1588,10 @@ function tickMission(args) {
|
|
|
1482
1588
|
let status = 'running';
|
|
1483
1589
|
let nextAction = mission.verifier ? `run verifier: ${mission.verifier}` : 'attach task, verifier, or proof';
|
|
1484
1590
|
if (verifierResult?.passed) {
|
|
1485
|
-
|
|
1591
|
+
const xpReadyAction = missionXpReadyAction(mission, receiptPath);
|
|
1592
|
+
status = (completeOnPass && !mission.always_on && !xpReadyAction) ? 'complete' : 'ready';
|
|
1486
1593
|
nextAction = mission.always_on ? nextCandidateTickAction(mission) :
|
|
1487
|
-
(completeOnPass ? 'mission complete' : `review proof then run: atris mission complete ${mission.id} --proof "${receiptPath}"`);
|
|
1594
|
+
(xpReadyAction || (completeOnPass ? 'mission complete' : `review proof then run: atris mission complete ${mission.id} --proof "${receiptPath}"`));
|
|
1488
1595
|
} else if (verifierResult) {
|
|
1489
1596
|
status = 'blocked';
|
|
1490
1597
|
nextAction = 'fix verifier failure or revise mission';
|
|
@@ -1549,9 +1656,10 @@ function completeMission(args) {
|
|
|
1549
1656
|
const { mission: saved } = saveMission(next, process.cwd(), 'mission_completed', { proof });
|
|
1550
1657
|
const logPath = appendMemberLog(saved.owner, 'Mission completed', { mission: saved.objective, proof });
|
|
1551
1658
|
const codexGoalState = refreshCodexGoalController(process.cwd());
|
|
1659
|
+
const xpNextCommand = missionXpReadyAction(saved, proof);
|
|
1552
1660
|
printJsonOrText(
|
|
1553
|
-
{ ok: true, action: 'mission_completed', mission: saved, log_path: logPath, codex_goal_state: codexGoalState },
|
|
1554
|
-
[`Completed mission: ${saved.objective}`, `Proof: ${proof}`],
|
|
1661
|
+
{ ok: true, action: 'mission_completed', mission: saved, log_path: logPath, codex_goal_state: codexGoalState, xp_next_command: xpNextCommand },
|
|
1662
|
+
[`Completed mission: ${saved.objective}`, `Proof: ${proof}`, ...(xpNextCommand ? [`AgentXP: ${xpNextCommand}`] : [])],
|
|
1555
1663
|
asJson,
|
|
1556
1664
|
);
|
|
1557
1665
|
}
|
|
@@ -1683,7 +1791,7 @@ function help() {
|
|
|
1683
1791
|
console.log(`
|
|
1684
1792
|
atris mission - durable goal + loop + owner + proof state
|
|
1685
1793
|
|
|
1686
|
-
atris mission start "<objective>" --owner <member> [--verify "..."] [--always-on]
|
|
1794
|
+
atris mission start "<objective>" --owner <member> [--verify "..."] [--always-on] [--xp-task]
|
|
1687
1795
|
atris mission status [id] [--status <state>] [--limit <n>] [--json]
|
|
1688
1796
|
atris mission goal [--heartbeat] [--json]
|
|
1689
1797
|
atris mission goal-loop [--max-wall 28800] [--max-iterations 32] [--no-claude] [--json]
|
|
@@ -1696,13 +1804,15 @@ atris mission - durable goal + loop + owner + proof state
|
|
|
1696
1804
|
Autonomy recipe:
|
|
1697
1805
|
1. Pick an owner member: atris member create <member> (if missing)
|
|
1698
1806
|
2. Start a current-agent mission with a verifier:
|
|
1699
|
-
atris mission start "ship one proof" --owner <member> --runner codex_goal --lane code --verify "npm test" --stop "verifier passes"
|
|
1807
|
+
atris mission start "ship one proof" --owner <member> --runner codex_goal --lane code --verify "npm test" --stop "verifier passes" --xp-task
|
|
1700
1808
|
3. Codex sessions: atris mission goal --json, then set /goal to goal.objective
|
|
1701
1809
|
Overnight controller: atris mission goal --heartbeat --json
|
|
1702
1810
|
Bounded overnight runner: atris mission goal-loop --max-wall 28800 --no-claude --json
|
|
1703
1811
|
4. Do one bounded step, then record it:
|
|
1704
1812
|
atris mission tick <id> --verify --summary "what changed"
|
|
1705
1813
|
5. Close or continue from the receipt:
|
|
1814
|
+
atris task ready <xp_task_ref> --proof "<receipt_path>" (if --xp-task)
|
|
1815
|
+
atris task accept <xp_task_ref> --reward <n> (human accept mints AgentXP)
|
|
1706
1816
|
atris mission complete <id> --proof "<receipt_path>"
|
|
1707
1817
|
repeat status -> step -> tick for current-agent work
|
|
1708
1818
|
atris mission run <id> --max-ticks 4 --complete-on-pass (Claude/always-on runner)
|