atris 3.30.7 → 3.30.12
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 +1 -1
- package/ax +271 -65
- package/bin/atris.js +190 -1
- package/commands/mission.js +349 -12
- package/lib/mission-runtime-loop.js +320 -0
- package/package.json +1 -1
package/bin/atris.js
CHANGED
|
@@ -65,6 +65,7 @@ const {
|
|
|
65
65
|
apiRequestJson, streamProChat, spawnClaudeCodeSession,
|
|
66
66
|
DEFAULT_CLIENT_ID, DEFAULT_USER_AGENT,
|
|
67
67
|
} = require('../utils/api');
|
|
68
|
+
const missionRuntime = require('../lib/mission-runtime-loop');
|
|
68
69
|
|
|
69
70
|
// Bind DI wrappers (utils/auth uses dependency injection for apiRequestJson)
|
|
70
71
|
const validateAccessToken = (token) => _validateAccessToken(token, apiRequestJson);
|
|
@@ -200,10 +201,14 @@ function loadActiveMissions(workspaceDir) {
|
|
|
200
201
|
owner: m.owner || '?',
|
|
201
202
|
objective: m.objective || '',
|
|
202
203
|
status,
|
|
204
|
+
created_at: m.created_at || null,
|
|
205
|
+
updated_at: m.updated_at || null,
|
|
206
|
+
completed_at: m.completed_at || null,
|
|
203
207
|
verifier: m.verifier || null,
|
|
204
208
|
verifier_passed: (m.verifier_result && m.verifier_result.passed) === true,
|
|
205
209
|
next_action: m.next_action || '',
|
|
206
210
|
lane: m.lane || null,
|
|
211
|
+
runner: m.runner || null,
|
|
207
212
|
});
|
|
208
213
|
}
|
|
209
214
|
// Most recently started first (rough — relies on insertion order from reversed walk)
|
|
@@ -213,6 +218,93 @@ function loadActiveMissions(workspaceDir) {
|
|
|
213
218
|
}
|
|
214
219
|
}
|
|
215
220
|
|
|
221
|
+
function readAtrisGoalState(workspaceDir) {
|
|
222
|
+
try {
|
|
223
|
+
const file = path.join(workspaceDir, '.atris', 'state', 'atris_goal.json');
|
|
224
|
+
if (!fs.existsSync(file)) return null;
|
|
225
|
+
const parsed = JSON.parse(fs.readFileSync(file, 'utf8'));
|
|
226
|
+
return parsed?.goal || null;
|
|
227
|
+
} catch {
|
|
228
|
+
return null;
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
function currentAtrisGoal(workspaceDir) {
|
|
233
|
+
const activeMissions = loadActiveMissions(workspaceDir);
|
|
234
|
+
const stored = readAtrisGoalState(workspaceDir);
|
|
235
|
+
if (stored && stored.objective) {
|
|
236
|
+
const mission = activeMissions.find((item) => item.id === stored.mission_id) || null;
|
|
237
|
+
return {
|
|
238
|
+
...stored,
|
|
239
|
+
mission_status: mission?.status || stored.mission_status,
|
|
240
|
+
created_at: stored.created_at || mission?.created_at || null,
|
|
241
|
+
updated_at: stored.updated_at || mission?.updated_at || null,
|
|
242
|
+
completed_at: stored.completed_at || mission?.completed_at || null,
|
|
243
|
+
next_command: stored.next_command || mission?.next_action || null,
|
|
244
|
+
};
|
|
245
|
+
}
|
|
246
|
+
const mission = activeMissions[0] || null;
|
|
247
|
+
if (!mission) return null;
|
|
248
|
+
return {
|
|
249
|
+
objective: mission.objective,
|
|
250
|
+
mission_id: mission.id,
|
|
251
|
+
mission_status: mission.status,
|
|
252
|
+
owner: mission.owner,
|
|
253
|
+
runner: mission.runner || 'mission',
|
|
254
|
+
created_at: mission.created_at,
|
|
255
|
+
updated_at: mission.updated_at,
|
|
256
|
+
completed_at: mission.completed_at,
|
|
257
|
+
next_command: mission.next_action || `atris mission tick ${mission.id} --summary "<what changed>"`,
|
|
258
|
+
};
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
function goalElapsedSeconds(goal) {
|
|
262
|
+
const started = Date.parse(goal?.created_at || '');
|
|
263
|
+
if (!Number.isFinite(started)) return null;
|
|
264
|
+
const ended = Date.parse(goal?.completed_at || '') || Date.now();
|
|
265
|
+
return Math.max(0, Math.floor((ended - started) / 1000));
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
function formatGoalDuration(seconds) {
|
|
269
|
+
if (!Number.isFinite(seconds)) return null;
|
|
270
|
+
const total = Math.max(0, Math.floor(seconds));
|
|
271
|
+
const hours = Math.floor(total / 3600);
|
|
272
|
+
const minutes = Math.floor((total % 3600) / 60);
|
|
273
|
+
const secs = total % 60;
|
|
274
|
+
if (hours > 0) return `${hours}h ${minutes}m`;
|
|
275
|
+
if (minutes > 0) return `${minutes}m ${secs}s`;
|
|
276
|
+
return `${secs}s`;
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
function goalAchieved(goal) {
|
|
280
|
+
return ['complete', 'completed', 'achieved'].includes(String(goal?.mission_status || goal?.status || '').toLowerCase());
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
function printAtrisGoalBanner(workspaceDir = process.cwd(), label = 'Atris goal') {
|
|
284
|
+
if (process.env.ATRIS_SHOW_GOAL_BANNER !== '1' && process.env.AX_SHOW_GOAL_BANNER !== '1') return null;
|
|
285
|
+
const goal = currentAtrisGoal(workspaceDir);
|
|
286
|
+
if (!goal) return null;
|
|
287
|
+
if (String(goal.runner || '').trim().toLowerCase() === 'codex_goal') return null;
|
|
288
|
+
const objective = String(goal.objective || '').length > 92
|
|
289
|
+
? `${String(goal.objective).slice(0, 89)}...`
|
|
290
|
+
: String(goal.objective || '');
|
|
291
|
+
console.log(`${label}: ${objective}`);
|
|
292
|
+
if (goal.mission_id || goal.mission_status || goal.runner) {
|
|
293
|
+
const elapsed = formatGoalDuration(goalElapsedSeconds(goal));
|
|
294
|
+
const parts = [
|
|
295
|
+
goal.mission_id || '?',
|
|
296
|
+
goal.mission_status || '?',
|
|
297
|
+
goal.runner || 'mission',
|
|
298
|
+
];
|
|
299
|
+
if (elapsed) parts.push(`elapsed ${elapsed}`);
|
|
300
|
+
parts.push(`achieved ${goalAchieved(goal) ? 'yes' : 'no'}`);
|
|
301
|
+
console.log(`Mission: ${parts.join(' · ')}`);
|
|
302
|
+
}
|
|
303
|
+
if (goal.next_command) console.log(`Next: ${goal.next_command}`);
|
|
304
|
+
console.log('');
|
|
305
|
+
return goal;
|
|
306
|
+
}
|
|
307
|
+
|
|
216
308
|
function showSearchHelp() {
|
|
217
309
|
console.log('Usage: atris search <keyword>');
|
|
218
310
|
console.log('Example: atris search auth');
|
|
@@ -1313,7 +1405,7 @@ if (command === 'init') {
|
|
|
1313
1405
|
.catch((err) => { console.error(`\n✗ Error: ${err.message || err}`); process.exit(1); });
|
|
1314
1406
|
} else if (command === 'mission') {
|
|
1315
1407
|
Promise.resolve(require('../commands/mission').missionCommand(process.argv.slice(3)))
|
|
1316
|
-
.then(() => process.exit(0))
|
|
1408
|
+
.then(() => process.exit(process.exitCode || 0))
|
|
1317
1409
|
.catch((err) => { console.error(`\n✗ Error: ${err.message || err}`); process.exit(1); });
|
|
1318
1410
|
} else if (command === 'pulse') {
|
|
1319
1411
|
// Pulse: durable overnight self-improvement heartbeat (OS cron) for atris-cli.
|
|
@@ -2359,6 +2451,8 @@ async function agentAtris() {
|
|
|
2359
2451
|
process.exit(1);
|
|
2360
2452
|
}
|
|
2361
2453
|
|
|
2454
|
+
printAtrisGoalBanner(process.cwd());
|
|
2455
|
+
|
|
2362
2456
|
// Check if logged in (with token refresh)
|
|
2363
2457
|
const ensured = await ensureValidCredentials();
|
|
2364
2458
|
if (ensured.error === 'not_logged_in' || !ensured.credentials?.token) {
|
|
@@ -2461,6 +2555,13 @@ async function chatAtris() {
|
|
|
2461
2555
|
process.exit(1);
|
|
2462
2556
|
}
|
|
2463
2557
|
|
|
2558
|
+
const missionIntent = missionRunIntentFromFastMessage(message);
|
|
2559
|
+
if (missionIntent) {
|
|
2560
|
+
process.exit(await runLocalFastMission(missionIntent));
|
|
2561
|
+
}
|
|
2562
|
+
|
|
2563
|
+
printAtrisGoalBanner(process.cwd());
|
|
2564
|
+
|
|
2464
2565
|
// Check agent selected
|
|
2465
2566
|
const config = loadConfig();
|
|
2466
2567
|
if (!config.agent_id) {
|
|
@@ -2550,6 +2651,15 @@ async function chatInteractive(config, credentials) {
|
|
|
2550
2651
|
return;
|
|
2551
2652
|
}
|
|
2552
2653
|
|
|
2654
|
+
const missionIntent = missionRunIntentFromFastMessage(input);
|
|
2655
|
+
if (missionIntent) {
|
|
2656
|
+
console.log('');
|
|
2657
|
+
await runLocalFastMission(missionIntent);
|
|
2658
|
+
console.log('');
|
|
2659
|
+
rl.prompt();
|
|
2660
|
+
return;
|
|
2661
|
+
}
|
|
2662
|
+
|
|
2553
2663
|
// Send to pro-chat
|
|
2554
2664
|
console.log('');
|
|
2555
2665
|
const apiUrl = getApiBaseUrl().replace(/\/api$/, '');
|
|
@@ -2598,6 +2708,78 @@ function atrisFastMessageFromArgs() {
|
|
|
2598
2708
|
return process.argv.slice(offset).join(' ').trim();
|
|
2599
2709
|
}
|
|
2600
2710
|
|
|
2711
|
+
function stripFastMissionQuotes(value) {
|
|
2712
|
+
const text = String(value || '').trim();
|
|
2713
|
+
if (text.length >= 2) {
|
|
2714
|
+
const first = text[0];
|
|
2715
|
+
const last = text[text.length - 1];
|
|
2716
|
+
if ((first === '"' && last === '"') || (first === "'" && last === "'")) {
|
|
2717
|
+
return text.slice(1, -1).trim();
|
|
2718
|
+
}
|
|
2719
|
+
}
|
|
2720
|
+
return text;
|
|
2721
|
+
}
|
|
2722
|
+
|
|
2723
|
+
function extractFastMissionFlag(text, flagName) {
|
|
2724
|
+
const source = String(text || '');
|
|
2725
|
+
const inline = new RegExp(`(^|\\s)${flagName}=([^\\s]+)(?=\\s|$)`).exec(source);
|
|
2726
|
+
if (inline) {
|
|
2727
|
+
return {
|
|
2728
|
+
value: inline[2],
|
|
2729
|
+
text: `${source.slice(0, inline.index)}${inline[1] || ''}${source.slice(inline.index + inline[0].length)}`.replace(/\s+/g, ' ').trim(),
|
|
2730
|
+
};
|
|
2731
|
+
}
|
|
2732
|
+
const split = new RegExp(`(^|\\s)${flagName}\\s+([^\\s]+)(?=\\s|$)`).exec(source);
|
|
2733
|
+
if (split) {
|
|
2734
|
+
return {
|
|
2735
|
+
value: split[2],
|
|
2736
|
+
text: `${source.slice(0, split.index)}${split[1] || ''}${source.slice(split.index + split[0].length)}`.replace(/\s+/g, ' ').trim(),
|
|
2737
|
+
};
|
|
2738
|
+
}
|
|
2739
|
+
return { value: null, text: source.trim() };
|
|
2740
|
+
}
|
|
2741
|
+
|
|
2742
|
+
function missionRunIntentFromFastMessage(message) {
|
|
2743
|
+
return missionRuntime.missionRunIntentFromMessage(message);
|
|
2744
|
+
}
|
|
2745
|
+
|
|
2746
|
+
function printFastMissionStartReceipt(payload) {
|
|
2747
|
+
const mission = payload?.mission || {};
|
|
2748
|
+
const goal = payload?.atris_goal_state?.goal || {};
|
|
2749
|
+
const receiptGoal = {
|
|
2750
|
+
...goal,
|
|
2751
|
+
objective: goal.objective || mission.objective,
|
|
2752
|
+
mission_id: goal.mission_id || mission.id,
|
|
2753
|
+
mission_status: goal.mission_status || mission.status,
|
|
2754
|
+
runner: goal.runner || mission.runner,
|
|
2755
|
+
created_at: goal.created_at || mission.created_at,
|
|
2756
|
+
updated_at: goal.updated_at || mission.updated_at,
|
|
2757
|
+
completed_at: goal.completed_at || mission.completed_at,
|
|
2758
|
+
next_command: goal.next_command || payload?.next_command || mission.next_action,
|
|
2759
|
+
};
|
|
2760
|
+
const elapsed = formatGoalDuration(goalElapsedSeconds(receiptGoal)) || '0s';
|
|
2761
|
+
|
|
2762
|
+
console.log('Atris mission started');
|
|
2763
|
+
console.log(`Goal: ${receiptGoal.objective || '?'}`);
|
|
2764
|
+
console.log(`Mission: ${receiptGoal.mission_id || '?'} · ${receiptGoal.mission_status || '?'} · ${receiptGoal.runner || 'atris2'}`);
|
|
2765
|
+
console.log(`Elapsed: ${elapsed}`);
|
|
2766
|
+
console.log(`Achieved: ${goalAchieved(receiptGoal) ? 'yes' : 'no'}`);
|
|
2767
|
+
if (receiptGoal.next_command) console.log(`Next: ${receiptGoal.next_command}`);
|
|
2768
|
+
}
|
|
2769
|
+
|
|
2770
|
+
async function runLocalFastMission(intent) {
|
|
2771
|
+
const result = await missionRuntime.runRuntimeMissionLoop(intent, {
|
|
2772
|
+
cwd: process.cwd(),
|
|
2773
|
+
cliPath: __filename,
|
|
2774
|
+
onProgress: (line) => {
|
|
2775
|
+
if (/^pursuing /.test(line)) console.log('Pursuing goal...');
|
|
2776
|
+
else if (/^\[tick /.test(line)) console.log(line);
|
|
2777
|
+
},
|
|
2778
|
+
});
|
|
2779
|
+
console.log(result.text || 'Mission command failed.');
|
|
2780
|
+
return result.ok ? 0 : (result.status || 1);
|
|
2781
|
+
}
|
|
2782
|
+
|
|
2601
2783
|
async function atrisFastChat() {
|
|
2602
2784
|
if (command === 'ax' && process.argv[3] !== 'fast') {
|
|
2603
2785
|
console.error('Usage: atris ax fast "message"');
|
|
@@ -2622,6 +2804,13 @@ async function atrisFastChat() {
|
|
|
2622
2804
|
process.exit(1);
|
|
2623
2805
|
}
|
|
2624
2806
|
|
|
2807
|
+
const missionIntent = missionRunIntentFromFastMessage(message);
|
|
2808
|
+
if (missionIntent) {
|
|
2809
|
+
process.exit(await runLocalFastMission(missionIntent));
|
|
2810
|
+
}
|
|
2811
|
+
|
|
2812
|
+
printAtrisGoalBanner(process.cwd());
|
|
2813
|
+
|
|
2625
2814
|
const ensured = await ensureValidCredentials();
|
|
2626
2815
|
if (ensured.error === 'not_logged_in' || !ensured.credentials?.token) {
|
|
2627
2816
|
console.error('✗ Error: Not logged in. Run "atris login" first.');
|