atris 3.57.4 → 3.58.5
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 +12 -1
- package/atris/skills/clawhub/member-runtime/SKILL.md +9 -9
- package/atris/skills/create-member/SKILL.md +16 -13
- package/atris/skills/design/SKILL.md +27 -4
- package/atris/skills/engines/SKILL.md +55 -12
- package/atris/skills/x-search/SKILL.md +8 -6
- package/atris/skills/youtube/SKILL.md +37 -8
- package/atris/team/brainstormer/MEMBER.md +2 -2
- package/atris/team/executor/MEMBER.md +3 -3
- package/atris/team/launcher/MEMBER.md +4 -4
- package/atris/team/navigator/MEMBER.md +3 -3
- package/atris/team/researcher/MEMBER.md +2 -2
- package/atris/team/validator/MEMBER.md +1 -1
- package/atris.md +14 -0
- package/ax +153 -23
- package/bin/atris.js +78 -30
- package/commands/aeo.js +4 -4
- package/commands/agents.js +1 -1
- package/commands/align.js +12 -12
- package/commands/analytics.js +3 -3
- package/commands/app.js +5 -5
- package/commands/ask.js +175 -0
- package/commands/auth.js +7 -7
- package/commands/autoland.js +29 -5
- package/commands/autopilot-front.js +6 -6
- package/commands/autopilot.js +90 -90
- package/commands/avail.js +5 -5
- package/commands/bench.js +63 -1
- package/commands/browse.js +2 -2
- package/commands/business.js +12 -12
- package/commands/card.js +2 -2
- package/commands/clean.js +13 -13
- package/commands/close.js +3 -3
- package/commands/compile.js +13 -13
- package/commands/computer.js +94 -27
- package/commands/console.js +5 -5
- package/commands/decide.js +1 -1
- package/commands/deck.js +9 -9
- package/commands/drive.js +4 -4
- package/commands/engine.js +173 -19
- package/commands/errors.js +3 -3
- package/commands/experiments.js +18 -1
- package/commands/feed.js +2 -2
- package/commands/feedback.js +4 -4
- package/commands/fleet.js +12 -12
- package/commands/game.js +1 -1
- package/commands/guide.js +49 -0
- package/commands/improve.js +19 -17
- package/commands/init.js +67 -19
- package/commands/interview.js +7 -7
- package/commands/land.js +40 -15
- package/commands/learn.js +12 -12
- package/commands/lesson.js +2 -2
- package/commands/log.js +1 -1
- package/commands/loops.js +4 -4
- package/commands/member.js +75 -50
- package/commands/mission.js +90 -82
- package/commands/one-lap.js +1 -1
- package/commands/pack.js +74 -8
- package/commands/plugin.js +6 -6
- package/commands/probe.js +9 -9
- package/commands/pull.js +35 -35
- package/commands/pulse.js +1 -1
- package/commands/push.js +21 -21
- package/commands/rainmaker.js +1 -1
- package/commands/recap.js +7 -7
- package/commands/reel.js +2 -2
- package/commands/release.js +3 -3
- package/commands/report.js +1 -1
- package/commands/review.js +5 -5
- package/commands/revisions.js +1 -1
- package/commands/run.js +1 -1
- package/commands/scout.js +3 -3
- package/commands/security-review.js +7 -7
- package/commands/serve.js +9 -9
- package/commands/setup.js +1 -1
- package/commands/sign.js +4 -4
- package/commands/signup.js +4 -4
- package/commands/site.js +8 -3
- package/commands/skill.js +6 -6
- package/commands/slop.js +18 -18
- package/commands/social.js +3 -3
- package/commands/soul.js +14 -14
- package/commands/spaceship.js +1 -1
- package/commands/status.js +2 -2
- package/commands/strings.js +13 -13
- package/commands/study.js +4 -4
- package/commands/sync.js +8 -8
- package/commands/task.js +50 -25
- package/commands/teach.js +2 -2
- package/commands/team.js +1 -1
- package/commands/theme.js +2 -2
- package/commands/tree.js +34 -0
- package/commands/truth.js +7 -7
- package/commands/verify.js +12 -12
- package/commands/visualize.js +2 -2
- package/commands/workflow.js +97 -40
- package/commands/worktree.js +1 -1
- package/commands/write.js +9 -9
- package/commands/x-search.js +89 -17
- package/commands/xp.js +6 -3
- package/commands/youtube.js +601 -48
- package/lib/accept-verify-gate.js +3 -3
- package/lib/activity-stream.js +1 -1
- package/lib/auto-accept-certified.js +28 -12
- package/lib/autoland.js +6 -6
- package/lib/bench/engines.js +59 -8
- package/lib/bench/report.js +1 -1
- package/lib/bench/runner.js +204 -5
- package/lib/bench/tree-render.js +76 -0
- package/lib/card.js +1 -1
- package/lib/chat-log-scan.js +1 -1
- package/lib/claude-boot-block.js +2 -0
- package/lib/deck-compose.js +1 -1
- package/lib/deck-history.js +2 -2
- package/lib/deck-layout.js +1 -1
- package/lib/deck-review.js +5 -5
- package/lib/deck-schema.js +1 -1
- package/lib/default-verifier.js +1 -1
- package/lib/developer-api.js +3 -0
- package/lib/engine-ask.js +45 -7
- package/lib/engine-registry.js +15 -2
- package/lib/file-ops.js +1 -1
- package/lib/fleet.js +71 -35
- package/lib/html-render.js +1 -1
- package/lib/intents.js +172 -0
- package/lib/journal.js +1 -1
- package/lib/known-commands.js +2 -2
- package/lib/land-green.js +364 -0
- package/lib/lesson-contradiction.js +2 -2
- package/lib/lesson-preflight.js +1 -1
- package/lib/manifest.js +5 -5
- package/lib/member-alive.js +9 -5
- package/lib/memory-view.js +3 -3
- package/lib/mission-protected-lane.js +1 -1
- package/lib/mission-root.js +2 -2
- package/lib/orb-context.js +10 -10
- package/lib/pack-capabilities.js +861 -3
- package/lib/permission-grants.js +2 -2
- package/lib/policy-lessons.js +6 -6
- package/lib/pulse.js +8 -8
- package/lib/receipt-evidence.js +1 -1
- package/lib/reel.js +1 -1
- package/lib/review-integrity.js +1 -1
- package/lib/runner-command.js +35 -5
- package/lib/scorecard.js +4 -4
- package/lib/security-scan.js +5 -5
- package/lib/self-drive.js +1 -1
- package/lib/site-publish.js +372 -0
- package/lib/site.js +1 -1
- package/lib/slides-deck.js +2 -2
- package/lib/state-detection.js +2 -2
- package/lib/sync-telemetry.js +2 -2
- package/lib/task-db.js +17 -10
- package/lib/task-explanation.js +1 -1
- package/lib/task-proof.js +2 -2
- package/lib/task-receipt.js +3 -3
- package/lib/theme.js +12 -12
- package/lib/todo-fallback.js +3 -3
- package/lib/todo-sections.js +1 -1
- package/lib/tree-hash.js +79 -0
- package/lib/usage.js +1 -1
- package/lib/wish-delegate.js +1 -1
- package/lib/workspace-safety.js +4 -4
- package/lib/workspace-scaffold.js +3 -3
- package/package.json +1 -1
- package/scripts/det/concierge-bakeoff.sh +26 -0
- package/scripts/det/ytquote-repair.js +0 -1
- package/scripts/det/ytrail-race.js +0 -5
- package/scripts/member-operate.mjs +52 -14
- package/scripts/outbound-artifact-gate.js +0 -3
- package/utils/auth.js +0 -3
- package/utils/config.js +0 -1
- package/scripts/det/checklist-score.js +0 -191
package/commands/ask.js
ADDED
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* atris ask: chat with your personal Atris computer.
|
|
3
|
+
*
|
|
4
|
+
* Same brain as texting: your memory, calendar, email, and tools.
|
|
5
|
+
* One-shot: atris ask what should I do today
|
|
6
|
+
* Interactive: atris ask
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
const readline = require('readline');
|
|
10
|
+
const { getApiBaseUrl, httpRequest, apiRequestJson } = require('../utils/api');
|
|
11
|
+
const auth = require('../utils/auth');
|
|
12
|
+
|
|
13
|
+
const TIMEOUT_MS = 300000;
|
|
14
|
+
|
|
15
|
+
function stripEvents(text) {
|
|
16
|
+
// Drop inline [[EVENT]]{...} tool chatter; keep only the spoken reply.
|
|
17
|
+
// Tool names are surfaced dimmed in the terminal (texts never see them).
|
|
18
|
+
if (typeof text !== 'string') return text;
|
|
19
|
+
const parts = [];
|
|
20
|
+
const toolNames = [];
|
|
21
|
+
for (let chunk of text.split('[[EVENT]]')) {
|
|
22
|
+
const trimmed = chunk.trimStart();
|
|
23
|
+
if (trimmed.startsWith('{')) {
|
|
24
|
+
let depth = 0;
|
|
25
|
+
let inString = false;
|
|
26
|
+
let escaped = false;
|
|
27
|
+
let end = -1;
|
|
28
|
+
for (let i = 0; i < trimmed.length; i += 1) {
|
|
29
|
+
const ch = trimmed[i];
|
|
30
|
+
if (escaped) { escaped = false; continue; }
|
|
31
|
+
if (ch === '\\') { escaped = true; continue; }
|
|
32
|
+
if (ch === '"') inString = !inString;
|
|
33
|
+
if (inString) continue;
|
|
34
|
+
if (ch === '{') depth += 1;
|
|
35
|
+
if (ch === '}') { depth -= 1; if (depth === 0) { end = i + 1; break; } }
|
|
36
|
+
}
|
|
37
|
+
if (end > 0) {
|
|
38
|
+
try {
|
|
39
|
+
const evt = JSON.parse(trimmed.slice(0, end));
|
|
40
|
+
if (evt.type === 'tool_call' && evt.tool) toolNames.push(evt.tool);
|
|
41
|
+
} catch (_) { /* not JSON, keep going */ }
|
|
42
|
+
chunk = trimmed.slice(end);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
parts.push(chunk);
|
|
46
|
+
}
|
|
47
|
+
const reply = parts.join('').trim();
|
|
48
|
+
if (toolNames.length && process.stdout.isTTY) {
|
|
49
|
+
process.stdout.write(`\x1b[2m[⚙ ${[...new Set(toolNames)].join(', ')}]\x1b[0m\n`);
|
|
50
|
+
}
|
|
51
|
+
return reply;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function startSpinner(label) {
|
|
55
|
+
if (!process.stdout.isTTY) return { stop: () => {} };
|
|
56
|
+
const frames = ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏'];
|
|
57
|
+
const started = Date.now();
|
|
58
|
+
let i = 0;
|
|
59
|
+
const timer = setInterval(() => {
|
|
60
|
+
const secs = Math.floor((Date.now() - started) / 1000);
|
|
61
|
+
process.stdout.write(`\r${frames[i % frames.length]} ${label}${secs > 2 ? ` · ${secs}s` : ''} `);
|
|
62
|
+
i += 1;
|
|
63
|
+
}, 90);
|
|
64
|
+
return {
|
|
65
|
+
stop: () => {
|
|
66
|
+
clearInterval(timer);
|
|
67
|
+
process.stdout.write('\r\x1b[2K');
|
|
68
|
+
},
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
async function typeOut(text) {
|
|
73
|
+
if (!process.stdout.isTTY || text.length > 4000) {
|
|
74
|
+
process.stdout.write(`${text}\n`);
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
for (const ch of text) {
|
|
78
|
+
process.stdout.write(ch);
|
|
79
|
+
// eslint-disable-next-line no-await-in-loop
|
|
80
|
+
await new Promise((r) => setTimeout(r, ch === '\n' ? 12 : 3));
|
|
81
|
+
}
|
|
82
|
+
process.stdout.write('\n');
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
async function sendMessage(token, message) {
|
|
86
|
+
const res = await httpRequest(`${getApiBaseUrl()}/message/incoming`, {
|
|
87
|
+
method: 'POST',
|
|
88
|
+
headers: {
|
|
89
|
+
Authorization: `Bearer ${token}`,
|
|
90
|
+
'Content-Type': 'application/json',
|
|
91
|
+
},
|
|
92
|
+
body: JSON.stringify({ message, source: 'cli' }),
|
|
93
|
+
timeoutMs: TIMEOUT_MS,
|
|
94
|
+
});
|
|
95
|
+
if (res.status !== 200) {
|
|
96
|
+
let detail = res.body.toString('utf8').slice(0, 200);
|
|
97
|
+
try { detail = JSON.parse(detail).detail || detail; } catch (_) { /* raw */ }
|
|
98
|
+
throw new Error(`HTTP ${res.status}: ${detail}`);
|
|
99
|
+
}
|
|
100
|
+
return JSON.parse(res.body.toString('utf8'));
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
async function askAtris(args) {
|
|
104
|
+
const ensured = await auth.ensureValidCredentials(apiRequestJson);
|
|
105
|
+
if (ensured.error === 'not_logged_in' || !ensured.credentials?.token) {
|
|
106
|
+
console.error('✗ Not logged in. Run "atris login" first.');
|
|
107
|
+
process.exit(1);
|
|
108
|
+
}
|
|
109
|
+
if (ensured.error) {
|
|
110
|
+
console.error(`✗ Authentication failed: ${ensured.detail || ensured.error}. Run "atris login".`);
|
|
111
|
+
process.exit(1);
|
|
112
|
+
}
|
|
113
|
+
const { token } = ensured.credentials;
|
|
114
|
+
|
|
115
|
+
const oneShot = (args || []).join(' ').trim();
|
|
116
|
+
if (oneShot === '-h' || oneShot === '--help' || oneShot === 'help') {
|
|
117
|
+
console.log('Usage: atris ask ["message"]');
|
|
118
|
+
console.log('');
|
|
119
|
+
console.log(' Chat with your personal Atris computer: memory, calendar, email, tools.');
|
|
120
|
+
console.log('');
|
|
121
|
+
console.log(' atris ask Interactive chat');
|
|
122
|
+
console.log(' atris ask what is on today One-shot question');
|
|
123
|
+
return;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
if (oneShot) {
|
|
127
|
+
const spinner = startSpinner('thinking');
|
|
128
|
+
try {
|
|
129
|
+
const body = await sendMessage(token, oneShot);
|
|
130
|
+
spinner.stop();
|
|
131
|
+
await typeOut(stripEvents(body.response) || JSON.stringify(body));
|
|
132
|
+
} catch (err) {
|
|
133
|
+
spinner.stop();
|
|
134
|
+
console.error(`✗ ${err.message}`);
|
|
135
|
+
process.exit(1);
|
|
136
|
+
}
|
|
137
|
+
return;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
console.log('Chatting with your computer. Ctrl-C or "bye" to leave.\n');
|
|
141
|
+
await new Promise((resolve) => {
|
|
142
|
+
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
143
|
+
let busy = false;
|
|
144
|
+
let closed = false;
|
|
145
|
+
const prompt = () => { if (!closed) rl.question('you › ', onLine); };
|
|
146
|
+
async function onLine(line) {
|
|
147
|
+
const message = line.trim();
|
|
148
|
+
if (!message) { prompt(); return; }
|
|
149
|
+
if (['bye', 'exit', 'quit'].includes(message.toLowerCase())) { rl.close(); return; }
|
|
150
|
+
busy = true;
|
|
151
|
+
const spinner = startSpinner('thinking');
|
|
152
|
+
try {
|
|
153
|
+
const body = await sendMessage(token, message);
|
|
154
|
+
spinner.stop();
|
|
155
|
+
process.stdout.write('\natris › ');
|
|
156
|
+
await typeOut(stripEvents(body.response) || JSON.stringify(body));
|
|
157
|
+
process.stdout.write('\n');
|
|
158
|
+
} catch (err) {
|
|
159
|
+
spinner.stop();
|
|
160
|
+
console.error(`✗ ${err.message}\n`);
|
|
161
|
+
}
|
|
162
|
+
busy = false;
|
|
163
|
+
if (closed) { console.log('\nbye.'); resolve(); return; }
|
|
164
|
+
prompt();
|
|
165
|
+
}
|
|
166
|
+
rl.on('close', () => {
|
|
167
|
+
closed = true;
|
|
168
|
+
// Let an in-flight answer finish printing before we leave.
|
|
169
|
+
if (!busy) { console.log('\nbye.'); resolve(); }
|
|
170
|
+
});
|
|
171
|
+
prompt();
|
|
172
|
+
});
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
module.exports = { askAtris };
|
package/commands/auth.js
CHANGED
|
@@ -565,7 +565,7 @@ async function switchAccount() {
|
|
|
565
565
|
const profile = loadProfile(name);
|
|
566
566
|
const email = profile?.email || 'unknown';
|
|
567
567
|
const marker = name === currentName ? ' ← active' : '';
|
|
568
|
-
console.log(` ${i + 1}. ${name}
|
|
568
|
+
console.log(` ${i + 1}. ${name}, ${email}${marker}`);
|
|
569
569
|
});
|
|
570
570
|
console.log(` ${profiles.length + 1}. Add new account`);
|
|
571
571
|
console.log(` ${profiles.length + 2}. Cancel`);
|
|
@@ -620,13 +620,13 @@ function activateProfile(name, currentName, { global = false } = {}) {
|
|
|
620
620
|
}
|
|
621
621
|
|
|
622
622
|
if (global || !getTerminalSessionId()) {
|
|
623
|
-
// Global switch
|
|
623
|
+
// Global switch, write to credentials.json (affects all terminals)
|
|
624
624
|
const credentialsPath = getCredentialsPath();
|
|
625
625
|
fs.writeFileSync(credentialsPath, JSON.stringify(profile, null, 2));
|
|
626
626
|
try { fs.chmodSync(credentialsPath, 0o600); } catch {}
|
|
627
|
-
console.log(`Switched to ${profile.email || name} (global
|
|
627
|
+
console.log(`Switched to ${profile.email || name} (global, all terminals)`);
|
|
628
628
|
} else {
|
|
629
|
-
// Per-terminal switch
|
|
629
|
+
// Per-terminal switch, write session file (only this terminal)
|
|
630
630
|
setSessionProfile(name);
|
|
631
631
|
console.log(`Switched to ${profile.email || name}`);
|
|
632
632
|
}
|
|
@@ -682,7 +682,7 @@ function useAccount() {
|
|
|
682
682
|
if (!process.stdout.isTTY) {
|
|
683
683
|
process.stdout.write(`export ATRIS_PROFILE=${match}\n`);
|
|
684
684
|
} else {
|
|
685
|
-
// Interactive
|
|
685
|
+
// Interactive, print instructions
|
|
686
686
|
console.log(`export ATRIS_PROFILE=${match}`);
|
|
687
687
|
console.log(`\n# Run this to activate ${email} in this terminal:`);
|
|
688
688
|
console.log(`# eval "$(atris use ${targetName})"`);
|
|
@@ -893,7 +893,7 @@ function switchSession() {
|
|
|
893
893
|
if (termId) {
|
|
894
894
|
setSessionProfile(name);
|
|
895
895
|
} else {
|
|
896
|
-
// No terminal ID
|
|
896
|
+
// No terminal ID, global fallback
|
|
897
897
|
const credentialsPath = getCredentialsPath();
|
|
898
898
|
fs.writeFileSync(credentialsPath, JSON.stringify(profile, null, 2));
|
|
899
899
|
try { fs.chmodSync(credentialsPath, 0o600); } catch {}
|
|
@@ -910,7 +910,7 @@ function shellInit() {
|
|
|
910
910
|
'# Atris per-terminal account switching',
|
|
911
911
|
'# Added by: eval "$(atris shell-init)"',
|
|
912
912
|
'_atris_session_id() {',
|
|
913
|
-
' # Detect terminal session ID
|
|
913
|
+
' # Detect terminal session ID, same priority as the Node.js code',
|
|
914
914
|
' local _sid="${TERM_SESSION_ID:-${ITERM_SESSION_ID:-${TMUX_PANE:-${WT_SESSION:-${WEZTERM_PANE:-}}}}}"',
|
|
915
915
|
' if [[ -n "$_sid" ]]; then echo "$_sid"; return; fi',
|
|
916
916
|
' # Fallback: TTY device (unique per macOS/Linux terminal tab)',
|
package/commands/autoland.js
CHANGED
|
@@ -752,7 +752,7 @@ async function runTickBody(root, { json, policy, receipt, engineValidationDeps =
|
|
|
752
752
|
// the landing gate reuse the live certification verifier result without
|
|
753
753
|
// persisting trust across heartbeats. Denied lanes and check-less proofs wait.
|
|
754
754
|
// No hardcoded --limit here: a fixed low cap (this used to be 12) silently
|
|
755
|
-
// undercounts a real backlog every single tick
|
|
755
|
+
// undercounts a real backlog every single tick: 12/hour forever even with
|
|
756
756
|
// 78 certified rows waiting. Let `atris task auto-accept-certified` apply
|
|
757
757
|
// its own default (12 without --all, a high safety cap under --all) so a
|
|
758
758
|
// policy with accept_all:true actually drains the full certified set.
|
|
@@ -763,7 +763,7 @@ async function runTickBody(root, { json, policy, receipt, engineValidationDeps =
|
|
|
763
763
|
try {
|
|
764
764
|
const parsed = JSON.parse(accept.stdout);
|
|
765
765
|
const results = Array.isArray(parsed.results) ? parsed.results : [];
|
|
766
|
-
// A refused sweep (ok:false
|
|
766
|
+
// A refused sweep (ok:false, a guard tripped, policy race) carries no
|
|
767
767
|
// summary fields. Name the reason instead of leaving nulls that read as
|
|
768
768
|
// "no work": a blind heartbeat must say WHY it is blind.
|
|
769
769
|
if (parsed.ok === false) {
|
|
@@ -841,7 +841,7 @@ async function runTickBody(root, { json, policy, receipt, engineValidationDeps =
|
|
|
841
841
|
}
|
|
842
842
|
|
|
843
843
|
// 3b. janitor, every tick: a mission left paused past 48h and a worktree
|
|
844
|
-
// already merged into base are the same rot pattern as an unlanded branch
|
|
844
|
+
// already merged into base are the same rot pattern as an unlanded branch,
|
|
845
845
|
// left alone they accumulate until a human runs `mission stop` and
|
|
846
846
|
// `worktree cleanup --apply` by hand, which is exactly the chore autoland
|
|
847
847
|
// exists to remove. Off switch: policy.janitor === false (default on, the
|
|
@@ -880,6 +880,30 @@ async function runTickBody(root, { json, policy, receipt, engineValidationDeps =
|
|
|
880
880
|
recordJanitorState(state, { stopped: stoppedMissions, held: heldMissions, worktrees: receipt.worktrees_reaped });
|
|
881
881
|
}
|
|
882
882
|
|
|
883
|
+
// 3b2. green landing, every tick: one stale branch that merges clean and
|
|
884
|
+
// passes the full verify in a scratch checkout lands on base and is pushed.
|
|
885
|
+
// A red branch opens a close flag the same hour instead of waiting for the
|
|
886
|
+
// TTL reap to salvage it. Off by policy: green_landing:false.
|
|
887
|
+
if (policy.green_landing !== false) {
|
|
888
|
+
try {
|
|
889
|
+
const { landStaleGreenBranches, summaryLine } = require('../lib/land-green');
|
|
890
|
+
const green = landStaleGreenBranches(root, {
|
|
891
|
+
verifyCommand: policy.verify_command || undefined,
|
|
892
|
+
limit: Number(policy.green_landing_limit) > 0 ? Number(policy.green_landing_limit) : 1,
|
|
893
|
+
});
|
|
894
|
+
receipt.green_landing = {
|
|
895
|
+
candidates: green.candidates.length,
|
|
896
|
+
landed: green.landed,
|
|
897
|
+
red: green.red,
|
|
898
|
+
verdicts: green.verdicts.map((v) => ({ branch: v.branch, action: v.action, reason: v.reason || null, detail: v.detail || null, failing: v.verify ? v.verify.failing : undefined })),
|
|
899
|
+
summary: summaryLine(green),
|
|
900
|
+
};
|
|
901
|
+
state.green_landing = { at: green.at, ...receipt.green_landing };
|
|
902
|
+
} catch (err) {
|
|
903
|
+
receipt.green_landing_error = String((err && err.message) || err).slice(0, 200);
|
|
904
|
+
}
|
|
905
|
+
}
|
|
906
|
+
|
|
883
907
|
// 3c. landing sweep, every tick: merged branch residue is cleared,
|
|
884
908
|
// TTL-expired work is salvaged before deletion, and 48h-stale active work
|
|
885
909
|
// is carried into the digest instead of silently aging forever.
|
|
@@ -952,7 +976,7 @@ async function runTickBody(root, { json, policy, receipt, engineValidationDeps =
|
|
|
952
976
|
}
|
|
953
977
|
}
|
|
954
978
|
|
|
955
|
-
// 5b. daily keep/revert experiment
|
|
979
|
+
// 5b. daily keep/revert experiment, self-gated inside experiments daily;
|
|
956
980
|
// hourly ticks are harmless no-ops after the first run each day.
|
|
957
981
|
if (policy.daily_experiment !== false) {
|
|
958
982
|
const daily = runOwnCli(root, ['experiments', 'daily', '--json']);
|
|
@@ -1021,7 +1045,7 @@ async function runTickBody(root, { json, policy, receipt, engineValidationDeps =
|
|
|
1021
1045
|
// Deltas alone ("0 certified, 0 landed") are true and useless: they read
|
|
1022
1046
|
// as "nothing to do" whether the queue is empty or 19-deep and wedged.
|
|
1023
1047
|
// Always print the standing backlog, and name work that cleared every
|
|
1024
|
-
// gate and still did not land
|
|
1048
|
+
// gate and still did not land. That is the shape of a stuck loop.
|
|
1025
1049
|
const stuckNote = stuckBacklogNote(receipt);
|
|
1026
1050
|
const summary = `autoland tick: ${receipt.reviews_certified ?? 0} reviews certified, ${receipt.landed.length} landed${receipt.landed.length ? ` (${receipt.landed.join(', ')})` : ''}, ${receipt.alarms} alarms${stuckNote}, digest ${digestTickStatus(receipt)}${reapNote}${janitorNote}${heldNote}${wishNote}${engineSweepNote}${receiptNote}`;
|
|
1027
1051
|
const validationLine = engineAnswerValidationLine(receipt);
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// atris autopilot: point it at the workspace and it keeps going.
|
|
2
|
-
// Each leg picks the most logical work
|
|
3
|
-
// member choosing useful work, then a self-chosen mission
|
|
2
|
+
// Each leg picks the most logical work, a moving mission first, then a
|
|
3
|
+
// member choosing useful work, then a self-chosen mission, and drives it
|
|
4
4
|
// through the mission runtime. It loops until stopped:
|
|
5
5
|
// atris autopilot stop (from any terminal) or Ctrl-C here.
|
|
6
6
|
// The old suggest→justify→execute loop lives on behind `atris autopilot --legacy`.
|
|
@@ -57,7 +57,7 @@ function positiveNumber(value) {
|
|
|
57
57
|
return Number.isFinite(parsed) && parsed > 0 ? parsed : null;
|
|
58
58
|
}
|
|
59
59
|
|
|
60
|
-
// `--iterations=N` or `--iterations N
|
|
60
|
+
// `--iterations=N` or `--iterations N`, the leg cap legacy callers pass.
|
|
61
61
|
function maxLegsFlag(args) {
|
|
62
62
|
const eq = args.find((a) => typeof a === 'string' && a.startsWith('--iterations='));
|
|
63
63
|
if (eq) return positiveNumber(eq.split('=')[1]);
|
|
@@ -187,7 +187,7 @@ function autopilotStatus(root = process.cwd()) {
|
|
|
187
187
|
const state = readState(root);
|
|
188
188
|
if (!state) { console.log('Autopilot is not running.'); return 0; }
|
|
189
189
|
const alive = pidAlive(state.pid);
|
|
190
|
-
console.log(`Autopilot ${alive ? 'running' : 'not running (stale state)'}
|
|
190
|
+
console.log(`Autopilot ${alive ? 'running' : 'not running (stale state)'}, pid ${state.pid}, started ${state.started_at}, legs ${state.legs || 0}`);
|
|
191
191
|
if (state.current_leg) console.log(`Current leg: ${state.current_leg}`);
|
|
192
192
|
if (!alive) clearState(root);
|
|
193
193
|
return 0;
|
|
@@ -276,7 +276,7 @@ async function autopilotFront(args = []) {
|
|
|
276
276
|
|
|
277
277
|
const current = { child: null };
|
|
278
278
|
// `autopilot stop` SIGTERMs this pid directly, so the farewell must print
|
|
279
|
-
// here
|
|
279
|
+
// here, the signal always beats the loop's own stop-file check.
|
|
280
280
|
const onSignal = () => {
|
|
281
281
|
if (current.child) { try { current.child.kill('SIGTERM'); } catch {} }
|
|
282
282
|
const reason = stopRequested(root) ? 'stop requested' : 'interrupted';
|
|
@@ -314,7 +314,7 @@ async function autopilotFront(args = []) {
|
|
|
314
314
|
if (maxLegs && state.legs >= maxLegs) { stopReason = `leg cap reached (--iterations=${maxLegs})`; break; }
|
|
315
315
|
if (stopRequested(root)) { stopReason = 'stop requested'; break; }
|
|
316
316
|
|
|
317
|
-
// A leg that finishes in seconds means no real work happened
|
|
317
|
+
// A leg that finishes in seconds means no real work happened, a crash,
|
|
318
318
|
// or a degenerate mission whose ticks record instantly (exit 0). Either
|
|
319
319
|
// way, back off and stop instead of hot-looping on it.
|
|
320
320
|
const legSeconds = (Date.now() - legStarted) / 1000;
|