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.
Files changed (2) hide show
  1. package/commands/mission.js +117 -7
  2. package/package.json +1 -1
@@ -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
- status = (completeOnPass && !mission.always_on) ? 'complete' : 'ready';
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)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "atris",
3
- "version": "3.15.22",
3
+ "version": "3.15.23",
4
4
  "main": "bin/atris.js",
5
5
  "bin": {
6
6
  "atris": "bin/atris.js"