atris 3.35.0 → 3.36.0
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/AGENTS.md +37 -0
- package/README.md +5 -3
- package/atris/GETTING_STARTED.md +1 -1
- package/atris/atris.md +3 -0
- package/atris/policies/day-loop-voice.md +102 -0
- package/atris/policies/outbound-artifact-gate.md +2 -0
- package/atris/skills/design/SKILL.md +56 -32
- package/atris/skills/endgame/SKILL.md +12 -6
- package/atris/skills/engines/SKILL.md +22 -4
- package/atris/skills/fable-method/SKILL.md +66 -0
- package/atris/skills/improve/SKILL.md +65 -45
- package/atris/skills/youtube/SKILL.md +10 -1
- package/atris.md +2 -0
- package/ax +147 -19
- package/bin/atris.js +565 -265
- package/commands/activate.js +194 -88
- package/commands/agents.js +166 -0
- package/commands/autoland.js +459 -107
- package/commands/autopilot-front.js +20 -2
- package/commands/autopilot.js +118 -2
- package/commands/avail.js +407 -0
- package/commands/bench.js +188 -0
- package/commands/brain.js +3 -0
- package/commands/brief.js +651 -0
- package/commands/business-sync.js +192 -6
- package/commands/clean.js +50 -24
- package/commands/close.js +1083 -0
- package/commands/cloud.js +245 -0
- package/commands/compile.js +292 -1
- package/commands/computer.js +150 -3
- package/commands/dream.js +365 -0
- package/commands/drill.js +371 -0
- package/commands/engine.js +993 -32
- package/commands/experiments.js +28 -0
- package/commands/feedback.js +34 -12
- package/commands/fleet-report.js +206 -0
- package/commands/gm.js +23 -0
- package/commands/goal.js +247 -0
- package/commands/improve.js +642 -26
- package/commands/init.js +72 -44
- package/commands/interview.js +67 -1
- package/commands/land.js +152 -52
- package/commands/lifecycle.js +39 -3
- package/commands/log.js +84 -1
- package/commands/loops.js +220 -16
- package/commands/meet.js +220 -0
- package/commands/member.js +511 -34
- package/commands/mission.js +3029 -339
- package/commands/next.js +137 -0
- package/commands/now.js +220 -25
- package/commands/one-lap.js +776 -0
- package/commands/orb.js +314 -0
- package/commands/pack-craft.js +179 -0
- package/commands/pack.js +823 -0
- package/commands/play.js +3 -2
- package/commands/probe.js +30 -3
- package/commands/pulse.js +241 -46
- package/commands/push.js +260 -82
- package/commands/rainmaker.js +49 -0
- package/commands/report.js +415 -0
- package/commands/scout.js +147 -0
- package/commands/search.js +363 -0
- package/commands/skill.js +47 -3
- package/commands/slop.js +50 -2
- package/commands/soul.js +1 -1
- package/commands/stream.js +861 -0
- package/commands/study.js +693 -0
- package/commands/sync.js +67 -54
- package/commands/task.js +1346 -117
- package/commands/team.js +73 -0
- package/commands/verify.js +96 -0
- package/commands/watch.js +303 -0
- package/commands/wish.js +500 -0
- package/commands/workflow.js +11 -5
- package/commands/worktree.js +234 -13
- package/commands/xp.js +29 -11
- package/lib/auto-accept-certified.js +331 -34
- package/lib/autoland.js +319 -54
- package/lib/ax-auto-lane.js +79 -0
- package/lib/bench/context.js +147 -0
- package/lib/bench/engines.js +141 -0
- package/lib/bench/report.js +140 -0
- package/lib/bench/runner.js +512 -0
- package/lib/brief-ledger.js +350 -0
- package/lib/cloud-mission.js +259 -0
- package/lib/codex-flight.js +154 -0
- package/lib/default-runner.js +45 -0
- package/lib/default-verifier.js +70 -0
- package/lib/engine-registry.js +232 -0
- package/lib/experiments/daily.js +640 -0
- package/lib/fleet.js +2219 -67
- package/lib/improve-vitals-html.js +171 -0
- package/lib/known-commands.js +58 -0
- package/lib/loop-doctor.js +416 -0
- package/lib/member-switches.js +144 -0
- package/lib/mission-room.js +1 -0
- package/lib/mission-root.js +52 -0
- package/lib/next-moves.js +327 -10
- package/lib/one-lap-validator.js +60 -0
- package/lib/orb-context.js +477 -0
- package/lib/orb-scorecard.js +224 -0
- package/lib/policy-lessons.js +52 -1
- package/lib/pulse.js +277 -3
- package/lib/receipt-block.js +168 -0
- package/lib/receipt-evidence.js +65 -4
- package/lib/router-brain.js +352 -0
- package/lib/runner-command.js +10 -0
- package/lib/self-drive.js +258 -0
- package/lib/short-name.js +103 -0
- package/lib/spawn-env.js +18 -0
- package/lib/state-detection.js +56 -1
- package/lib/sync-status.js +59 -0
- package/lib/task-db.js +108 -29
- package/lib/task-proof.js +23 -1
- package/lib/team-presence.js +260 -0
- package/lib/tool-result-encode.js +7 -0
- package/lib/trust-tiers.js +90 -0
- package/lib/usage.js +107 -0
- package/lib/voice-gate.js +163 -0
- package/lib/wish-audit.js +1368 -0
- package/lib/wish-delegate.js +1840 -0
- package/lib/wish-design.js +110 -0
- package/lib/wish-stats.js +183 -0
- package/lib/wish-store.js +354 -0
- package/lib/zip.js +221 -0
- package/package.json +3 -1
- package/templates/loops/atris/loops/LOOPS.md +55 -0
- package/templates/loops/atris/loops/TICK.md +24 -0
- package/templates/loops/atris/loops/feedback.md +22 -0
- package/templates/loops/atris/loops/quality.md +22 -0
- package/templates/loops/atris/wiki/systems/loops.md +41 -0
- package/utils/api.js +5 -1
- package/utils/auth.js +57 -21
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: improve
|
|
3
|
-
description: "Run one
|
|
4
|
-
version: 1.
|
|
3
|
+
description: "Run one verified, scored improvement tick and write its receipt. Use when the user asks to improve, make this better, ship one thing, run a tick, or get smarter. `atris improve` alone shows metabolism vitals; use `atris improve tick` to ship work."
|
|
4
|
+
version: 1.1.0
|
|
5
5
|
tags:
|
|
6
6
|
- rl
|
|
7
7
|
- improve
|
|
@@ -12,74 +12,94 @@ tags:
|
|
|
12
12
|
|
|
13
13
|
# /improve
|
|
14
14
|
|
|
15
|
-
|
|
15
|
+
Run one improvement tick on the workspace. A successful tick ships one result, passes a real verifier, receives a score, and writes a machine-readable scorecard.
|
|
16
16
|
|
|
17
|
-
|
|
17
|
+
One call means one verifiable result. Never turn one invocation into a hidden batch.
|
|
18
18
|
|
|
19
|
-
##
|
|
19
|
+
## Command contract
|
|
20
20
|
|
|
21
|
+
`atris improve` alone shows the self-improvement metabolism vitals. It does not ship a change.
|
|
22
|
+
|
|
23
|
+
Use the explicit `tick` subcommand when the user asks for improvement work:
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
atris improve tick # one full tick: plan, build, verify, score
|
|
27
|
+
atris improve tick --json # the same tick as machine-readable output
|
|
28
|
+
atris improve tick plan # plan only; no change or receipt
|
|
29
|
+
atris improve tick --dry-run # execute without committing; no shipping receipt
|
|
30
|
+
atris improve tick --no-fallback # report API failure instead of running locally
|
|
31
|
+
atris improve history # reward trend, credits, and pass rate
|
|
32
|
+
atris improve # vitals only
|
|
33
|
+
atris improve --json # vitals only as JSON
|
|
21
34
|
```
|
|
22
|
-
/improve
|
|
23
|
-
→ POST /api/improve { workspace: ".", mode: "full" }
|
|
24
|
-
→ backend picks a task, plans, builds, reviews, verifies
|
|
25
|
-
→ returns { task, reward, files_changed, verify_pass, summary }
|
|
26
|
-
→ CLI writes scorecard to .atris/presidio/scorecards.md
|
|
27
|
-
→ CLI reports result to user
|
|
28
|
-
```
|
|
29
35
|
|
|
30
|
-
|
|
36
|
+
## Run one tick
|
|
37
|
+
|
|
38
|
+
1. Run `atris improve tick`, adding `--json` only when structured output is useful.
|
|
39
|
+
2. Inspect the returned source, task summary, verifier result, reward, files, and receipt path.
|
|
40
|
+
3. Count the tick only when `ok` is true, verification passed, and the scorecard was written.
|
|
41
|
+
4. Report what shipped and the exact verifier result. If the tick fails, report the failure and stop.
|
|
42
|
+
|
|
43
|
+
For a paid API tick, the CLI:
|
|
31
44
|
|
|
32
|
-
|
|
45
|
+
1. Loads credentials with `utils/auth.loadCredentials`.
|
|
46
|
+
2. Calls `POST /api/improve` with the workspace, mode, model, and dry-run setting.
|
|
47
|
+
3. Lets the backend plan, build, verify, score, and bill the successful tick.
|
|
48
|
+
4. Writes the normalized receipt to `.atris/state/scorecards.jsonl` and the human trail to the daily Atris journal.
|
|
33
49
|
|
|
34
|
-
|
|
50
|
+
The full response may omit the billed credit count. In that case the CLI reports that billing happened server-side instead of inventing a number.
|
|
51
|
+
|
|
52
|
+
## Local fallback
|
|
53
|
+
|
|
54
|
+
Local fallback is allowed when there is no login, the backend is unreachable, or the hosted backend cannot access the local workspace path. The CLI runs this exact bounded command internally:
|
|
35
55
|
|
|
36
56
|
```bash
|
|
37
|
-
atris
|
|
38
|
-
atris improve plan # show the plan only, change nothing
|
|
39
|
-
atris improve --json # machine-readable result (this is what the member loop consumes)
|
|
40
|
-
atris improve --no-fallback # fail loudly instead of running a local tick when the backend is down
|
|
57
|
+
atris mission run --due --headless --max-ticks 1 --complete-on-pass --json
|
|
41
58
|
```
|
|
42
59
|
|
|
43
|
-
|
|
60
|
+
The local result succeeds only when all of these are true:
|
|
44
61
|
|
|
45
|
-
1.
|
|
46
|
-
2.
|
|
47
|
-
3. The
|
|
48
|
-
4.
|
|
49
|
-
5. Falls back to a local autopilot tick **only** when you are not logged in or the backend is unreachable — a real error (insufficient credits, server error) is reported, never silently retried
|
|
62
|
+
1. Exactly one mission tick ran.
|
|
63
|
+
2. A headless worker actually ran; caller-session and no-worker placeholders are rejected.
|
|
64
|
+
3. The mission verifier passed.
|
|
65
|
+
4. The scorecard and journal receipt were written.
|
|
50
66
|
|
|
51
|
-
|
|
67
|
+
A verified local tick earns the conservative local reward of `+1` and deducts no credits. No due headless mission, a skipped worker, a failed or missing verifier, or a receipt write failure makes the improve command fail without a reward.
|
|
52
68
|
|
|
53
|
-
|
|
69
|
+
Do not silently fall back for answerable API failures such as insufficient credits, unrelated authorization failures, or server errors.
|
|
54
70
|
|
|
55
|
-
|
|
56
|
-
- `plan` — just pick the task and show what it would do
|
|
57
|
-
- `dry_run` — run everything but don't commit
|
|
71
|
+
## Expected output
|
|
58
72
|
|
|
59
|
-
|
|
73
|
+
User says: "Improve this once."
|
|
60
74
|
|
|
61
|
-
|
|
75
|
+
Action: run `atris improve tick`.
|
|
62
76
|
|
|
63
|
-
|
|
77
|
+
Expected shape:
|
|
64
78
|
|
|
65
|
-
```
|
|
79
|
+
```text
|
|
66
80
|
improved.
|
|
81
|
+
task: fixed the stale wiki reference
|
|
82
|
+
verify: pass
|
|
83
|
+
reward: 4
|
|
84
|
+
files: atris/wiki/auth-flow.md
|
|
85
|
+
scorecard: .atris/state/scorecards.jsonl
|
|
86
|
+
```
|
|
67
87
|
|
|
68
|
-
|
|
69
|
-
verify: pass (npm test, 143/143)
|
|
70
|
-
reward: +4
|
|
71
|
-
files: atris/wiki/briefs/auth-flow.md
|
|
72
|
-
time: 47s
|
|
88
|
+
For local fallback, the first line is `improved (local fallback).` and the report still names the task, passing verifier, reward, and scorecard.
|
|
73
89
|
|
|
74
|
-
|
|
75
|
-
|
|
90
|
+
## Failure handling
|
|
91
|
+
|
|
92
|
+
- If verification fails or is missing, halt honestly and write a durable lesson. Do not claim improvement.
|
|
93
|
+
- If the command reports no due headless mission, stop; do not manufacture busywork or a scorecard.
|
|
94
|
+
- If the API returns insufficient credits or another answerable error, report it without a local retry.
|
|
95
|
+
- If the workspace is dirty, preserve existing changes. Use an isolated worktree for manual follow-up fixes.
|
|
96
|
+
- If a worker lands work but the outer receipt fails, report the landed commit separately and do not count the improve tick.
|
|
76
97
|
|
|
77
98
|
## Rules
|
|
78
99
|
|
|
79
100
|
- One tick only. Never batch.
|
|
80
|
-
-
|
|
101
|
+
- No reward without a passing verifier.
|
|
81
102
|
- Show what shipped, not what was attempted.
|
|
82
|
-
-
|
|
83
|
-
-
|
|
84
|
-
- Fallback to local if backend is unreachable. Never error silently.
|
|
103
|
+
- A scorecard is required for success.
|
|
104
|
+
- Preserve user work and unrelated changes.
|
|
85
105
|
- The user pays because something real happened. Never fake it.
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: youtube
|
|
3
3
|
description: "Process YouTube videos — extract insights, answer questions, store as knowledge. 5 credits per video. Triggers on: youtube, video, process video, watch this, learn from video."
|
|
4
|
-
version: 2.
|
|
4
|
+
version: 2.3.0
|
|
5
5
|
tags:
|
|
6
6
|
- youtube
|
|
7
7
|
- research
|
|
@@ -13,6 +13,15 @@ tags:
|
|
|
13
13
|
|
|
14
14
|
Process any YouTube video through Atris transcript-first analysis. The CLI extracts local captions with timestamps when available, sends that transcript to Atris, and falls back to cloud video processing when captions are unavailable or unusable. 5 credits per video, refunded if processing fails.
|
|
15
15
|
|
|
16
|
+
## Route first: learning vs product
|
|
17
|
+
|
|
18
|
+
Two rails process YouTube videos — pick before running anything:
|
|
19
|
+
|
|
20
|
+
- **Learning / work rail** → use the `alpha-learn` skill (ytnotes). Local yt-dlp + grok, zero credits, podcastnotes-style notes, tweet-feed output, `[claimable]` entries in today's journal for other agents. Use this when the goal is to LEARN from a video or mine it for Atris work.
|
|
21
|
+
- **Product rail** → this skill (`atris youtube process`). Credits-billed, stores knowledge in the Atris backend, customer-facing path. Use this when a customer/agent needs the video stored as Atris knowledge or answered via the API.
|
|
22
|
+
|
|
23
|
+
If the user says "learn from", "notes on", "alpha", or "rabbit hole" → alpha-learn. If they say "process", "store", "add to knowledge" → this skill.
|
|
24
|
+
|
|
16
25
|
## Bootstrap (ALWAYS Run First)
|
|
17
26
|
|
|
18
27
|
```bash
|
package/atris.md
CHANGED
|
@@ -90,6 +90,8 @@ The same discipline for words. Output stays sharp no matter how bloated the cont
|
|
|
90
90
|
- "Missions survive everything: one engine starts the work, another picks it up cold, a third lands it. Work is no longer tied to a chat window, a session, or a vendor."
|
|
91
91
|
- "The system reports in plain English: one daily message with what landed, what waits on you, and who should own what's next. One person can supervise many projects."
|
|
92
92
|
- **Fit the screen.** An operator-facing report shows three results, air between them, and holds the rest on ask. No scrolling: reading it is one glance, and the reader asks for more if they want more.
|
|
93
|
+
- **Human surfaces are gated.** Anything rendered for the operator (brief, digest, boot) goes through the operator-voice translator and a deterministic jargon gate; raw ids, commands, paths, and test tallies never reach a page. A gate violation is a build error, not a style note.
|
|
94
|
+
- **Interview the fuzz.** When the operator hands off a fuzzy build ("make me the best X", "yeah ok this is my thought, go make it"), do not ask open questions and do not dispatch as-is. Offer 2-3 named interpretations with a recommendation ("best could mean capture, resurfacing, or closure: I'd bet resurfacing, right?") so the operator confirms or corrects in five words. Repeat until the brief fits one screen (goal, why now, done-looks-like, open risks), show the brief, then dispatch. The interview lends the operator a structure to think in; the sharpened brief is what makes every downstream lap cheap.
|
|
93
95
|
|
|
94
96
|
`expected`: this is how an Atris agent writes and builds. Shipping slop or rambling is a failure smell, same as drift or a stale task.
|
|
95
97
|
|
package/ax
CHANGED
|
@@ -12,6 +12,7 @@ const permissionGrants = require(path.join(__dirname, 'lib', 'permission-grants.
|
|
|
12
12
|
const { loadCredentials } = require('./utils/auth');
|
|
13
13
|
const { apiRequestJson, getApiBaseUrl } = require('./utils/api');
|
|
14
14
|
const missionRuntime = require('./lib/mission-runtime-loop');
|
|
15
|
+
const { pickLane } = require('./lib/ax-auto-lane');
|
|
15
16
|
|
|
16
17
|
const EXIT_WORDS = new Set(['exit', 'quit', ':q']);
|
|
17
18
|
const BACKEND = {
|
|
@@ -34,6 +35,9 @@ const APPROVAL_EXECUTE_PATH = '/api/atris2/approvals/execute';
|
|
|
34
35
|
const BACKEND_API_TOOL_NAME = 'backend_api';
|
|
35
36
|
const BACKEND_API_TOOL_RESULT_PATH = '/api/atris2/turn/tool-result';
|
|
36
37
|
const DEFAULT_APPROVAL_MAX_AGE_MS = 24 * 60 * 60 * 1000;
|
|
38
|
+
const DEFAULT_TURN_TIMEOUT_MS = 60000;
|
|
39
|
+
const PRO_TURN_TIMEOUT_MS = 180000;
|
|
40
|
+
const MAX_TURN_TIMEOUT_MS = 300000;
|
|
37
41
|
const CONNECTOR_NAMES = {
|
|
38
42
|
gmail: 'Gmail',
|
|
39
43
|
google_calendar: 'Google Calendar',
|
|
@@ -97,6 +101,33 @@ function modelForMode(mode) {
|
|
|
97
101
|
return mode === 'fast' ? 'atris:fast' : 'atris:pro';
|
|
98
102
|
}
|
|
99
103
|
|
|
104
|
+
function payloadCanRunTools(payload = {}, options = {}) {
|
|
105
|
+
return Boolean(
|
|
106
|
+
options.business
|
|
107
|
+
|| options.local
|
|
108
|
+
|| payload.local_executor
|
|
109
|
+
|| (Array.isArray(payload.local_tools) && payload.local_tools.length > 0)
|
|
110
|
+
);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
function postTurnTimeoutMs(payload = {}, options = {}) {
|
|
114
|
+
const model = String(payload.model || '');
|
|
115
|
+
let timeoutMs = DEFAULT_TURN_TIMEOUT_MS;
|
|
116
|
+
if (model === 'atris:max') timeoutMs = MAX_TURN_TIMEOUT_MS;
|
|
117
|
+
else if (model === 'atris:pro') timeoutMs = PRO_TURN_TIMEOUT_MS;
|
|
118
|
+
|
|
119
|
+
// AX postTurn is the tool-capable Atris2 path. Fast tool turns need the same
|
|
120
|
+
// headroom as pro because the backend can spend long stretches inside tools
|
|
121
|
+
// without SSE traffic. Plain tool-free one-shot fast chat stays at 60s.
|
|
122
|
+
if (model === 'atris:fast' && payloadCanRunTools(payload, options)) {
|
|
123
|
+
timeoutMs = Math.max(timeoutMs, PRO_TURN_TIMEOUT_MS);
|
|
124
|
+
}
|
|
125
|
+
if (options.business || options.local) {
|
|
126
|
+
timeoutMs = Math.max(timeoutMs, PRO_TURN_TIMEOUT_MS);
|
|
127
|
+
}
|
|
128
|
+
return timeoutMs;
|
|
129
|
+
}
|
|
130
|
+
|
|
100
131
|
function formatDuration(ms) {
|
|
101
132
|
const value = Number(ms) || 0;
|
|
102
133
|
if (value < 1000) return `${Math.max(0, Math.round(value))}ms`;
|
|
@@ -131,9 +162,9 @@ function formatUsage() {
|
|
|
131
162
|
'ax - Atris local/code agent',
|
|
132
163
|
'',
|
|
133
164
|
'Usage:',
|
|
134
|
-
' ax [--max|--pro|--fast|--code-fast] [--local|--cloud] <message>',
|
|
135
|
-
' ax [--max|--pro|--fast|--code-fast] [--local|--cloud] --print <message>',
|
|
136
|
-
' ax [--max|--pro|--fast|--code-fast] [--local|--cloud] --chat',
|
|
165
|
+
' ax [--auto|--max|--pro|--fast|--code-fast] [--local|--cloud] <message>',
|
|
166
|
+
' ax [--auto|--max|--pro|--fast|--code-fast] [--local|--cloud] --print <message>',
|
|
167
|
+
' ax [--auto|--max|--pro|--fast|--code-fast] [--local|--cloud] --chat',
|
|
137
168
|
' ax [--max|--pro|--fast] --business <slug> [<message>|--chat]',
|
|
138
169
|
' ax [--max|--pro|--fast|--code-fast] --doctor',
|
|
139
170
|
' ax --approvals',
|
|
@@ -147,13 +178,14 @@ function formatUsage() {
|
|
|
147
178
|
' ax [--max|--fast] --benchmark',
|
|
148
179
|
'',
|
|
149
180
|
'Modes:',
|
|
181
|
+
' --auto pick a lane for each message and say why',
|
|
150
182
|
' --max hosted Atris 2, highest reasoning, slowest turns',
|
|
151
183
|
' --pro hosted Atris 2, deeper tool loop',
|
|
152
184
|
' --fast hosted Atris 2, faster low-latency turns',
|
|
153
185
|
' --code-fast Atris Code Fast public lane',
|
|
154
186
|
' --local opt into local backend/workspace tools',
|
|
155
187
|
' --cloud force authenticated cloud connectors/chat',
|
|
156
|
-
' --print headless JSON result
|
|
188
|
+
' --print headless JSON result, including auto_lane and auto_reason with --auto',
|
|
157
189
|
' --business <slug> run tools on that business cloud workspace (EC2)',
|
|
158
190
|
' --verify <cmd> gate the turn on this command passing (default: no verifier)',
|
|
159
191
|
'',
|
|
@@ -199,10 +231,29 @@ function createRunLogger({ cwd = process.cwd(), mode = 'pro', kind = 'play', out
|
|
|
199
231
|
].join('\n'));
|
|
200
232
|
|
|
201
233
|
let redactedChars = 0;
|
|
234
|
+
let logBuffer = '';
|
|
235
|
+
let logFlushTimer = null;
|
|
236
|
+
const flushLogBuffer = () => {
|
|
237
|
+
if (logFlushTimer) {
|
|
238
|
+
clearTimeout(logFlushTimer);
|
|
239
|
+
logFlushTimer = null;
|
|
240
|
+
}
|
|
241
|
+
if (!logBuffer) return;
|
|
242
|
+
fs.appendFileSync(logPath, logBuffer);
|
|
243
|
+
logBuffer = '';
|
|
244
|
+
};
|
|
245
|
+
const scheduleLogFlush = () => {
|
|
246
|
+
if (logFlushTimer) return;
|
|
247
|
+
logFlushTimer = setTimeout(() => {
|
|
248
|
+
logFlushTimer = null;
|
|
249
|
+
flushLogBuffer();
|
|
250
|
+
}, 500);
|
|
251
|
+
};
|
|
202
252
|
const writeLog = (chunk) => {
|
|
203
253
|
const text = stripAnsi(chunk);
|
|
204
254
|
if (fullTranscript) {
|
|
205
|
-
|
|
255
|
+
logBuffer += text;
|
|
256
|
+
scheduleLogFlush();
|
|
206
257
|
return;
|
|
207
258
|
}
|
|
208
259
|
redactedChars += text.length;
|
|
@@ -222,6 +273,7 @@ function createRunLogger({ cwd = process.cwd(), mode = 'pro', kind = 'play', out
|
|
|
222
273
|
output: teeOutput,
|
|
223
274
|
write: writeLog,
|
|
224
275
|
close(exitCode = 0) {
|
|
276
|
+
flushLogBuffer();
|
|
225
277
|
if (!fullTranscript) {
|
|
226
278
|
fs.appendFileSync(logPath, `redacted_chars: ${redactedChars}\n`);
|
|
227
279
|
fs.appendFileSync(logPath, 'note: full transcript disabled by default; set AX_LOG_FULL=1 to opt in.\n');
|
|
@@ -229,6 +281,7 @@ function createRunLogger({ cwd = process.cwd(), mode = 'pro', kind = 'play', out
|
|
|
229
281
|
return;
|
|
230
282
|
}
|
|
231
283
|
writeLog(`\nexit_code: ${exitCode}\nfinished_at: ${new Date().toISOString()}\n`);
|
|
284
|
+
flushLogBuffer();
|
|
232
285
|
}
|
|
233
286
|
};
|
|
234
287
|
}
|
|
@@ -904,10 +957,38 @@ function normalizeMode(mode) {
|
|
|
904
957
|
return mode === 'fast' ? 'fast' : 'pro';
|
|
905
958
|
}
|
|
906
959
|
|
|
960
|
+
function appendAutoPick(message, picked, options = {}) {
|
|
961
|
+
try {
|
|
962
|
+
const dir = path.join(os.homedir(), '.atris');
|
|
963
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
964
|
+
fs.appendFileSync(path.join(dir, 'ax-auto-picks.jsonl'), `${JSON.stringify({
|
|
965
|
+
at: new Date().toISOString(),
|
|
966
|
+
lane: picked.lane,
|
|
967
|
+
reason: picked.reason,
|
|
968
|
+
message_chars: String(message || '').length,
|
|
969
|
+
print: Boolean(options.print),
|
|
970
|
+
})}\n`);
|
|
971
|
+
} catch (_) {
|
|
972
|
+
// Lane telemetry is best-effort and must never block a turn.
|
|
973
|
+
}
|
|
974
|
+
}
|
|
975
|
+
|
|
976
|
+
function autoLaneForMessage(message, options = {}) {
|
|
977
|
+
const picked = pickLane(message);
|
|
978
|
+
appendAutoPick(message, picked, options);
|
|
979
|
+
const errorOutput = options.errorOutput || process.stderr;
|
|
980
|
+
errorOutput.write(`${picked.reason}\n`);
|
|
981
|
+
return picked;
|
|
982
|
+
}
|
|
983
|
+
|
|
907
984
|
function formatPrompt(mode, options = {}) {
|
|
908
985
|
if (!mode) return '› ';
|
|
909
986
|
const tier = normalizeMode(mode);
|
|
910
|
-
|
|
987
|
+
const label = paint(tier, [ANSI.bold, tierColor(tier)], options);
|
|
988
|
+
if (options.approveMode === 'auto') {
|
|
989
|
+
return `${label} ${paint('[auto-approve]', [ANSI.muted], options)} › `;
|
|
990
|
+
}
|
|
991
|
+
return `${label} › `;
|
|
911
992
|
}
|
|
912
993
|
|
|
913
994
|
const TIER_COMMANDS = new Map([
|
|
@@ -924,6 +1005,8 @@ const CHAT_COMMANDS = [
|
|
|
924
1005
|
['/fast', 'quick answers, lowest latency'],
|
|
925
1006
|
['/pro', 'deeper tool loop for real work'],
|
|
926
1007
|
['/max', 'highest reasoning for the hardest jobs'],
|
|
1008
|
+
['/clear', 'wipe chat history and reset conversation id'],
|
|
1009
|
+
['/context', 'show turn count and rough token estimate'],
|
|
927
1010
|
['/help', 'show this menu'],
|
|
928
1011
|
['exit', 'leave chat'],
|
|
929
1012
|
];
|
|
@@ -2670,14 +2753,7 @@ async function postTurn(message, options = {}) {
|
|
|
2670
2753
|
const payload = buildPayload(message, { ...options, route, connectionContext, connectionUserId, turnId });
|
|
2671
2754
|
const postData = JSON.stringify(payload);
|
|
2672
2755
|
const output = options.output || process.stdout;
|
|
2673
|
-
|
|
2674
|
-
// SSE traffic in between, so the socket-idle timeout needs more headroom.
|
|
2675
|
-
const baseTimeoutMs = payload.model === 'atris:max' ? 300000 : payload.model === 'atris:pro' ? 180000 : 60000;
|
|
2676
|
-
let timeoutMs = options.business ? Math.max(baseTimeoutMs, 180000) : baseTimeoutMs;
|
|
2677
|
-
// Local workspace tool loops (max_turns 16/24) legitimately run past the fast
|
|
2678
|
-
// lane's 60s chat wall — SwapBench 2026-07-02: three tool-loop tasks died at
|
|
2679
|
-
// ~60s as "Atris cloud did not respond". Same headroom as business relays.
|
|
2680
|
-
if (local) timeoutMs = Math.max(timeoutMs, 180000);
|
|
2756
|
+
const timeoutMs = postTurnTimeoutMs(payload, { business: options.business, local });
|
|
2681
2757
|
const turnUrl = new URL(backendUrl({ route: endpointRoute }));
|
|
2682
2758
|
const transport = turnUrl.protocol === 'https:' ? https : http;
|
|
2683
2759
|
const state = {
|
|
@@ -2940,6 +3016,9 @@ async function runHeadlessTurn(message, options = {}) {
|
|
|
2940
3016
|
const startedAt = Date.now();
|
|
2941
3017
|
const sink = options.output || bufferedOutput();
|
|
2942
3018
|
const turnFunction = options.turnFunction || turnFunctionForMode(mode);
|
|
3019
|
+
const autoFields = options.autoLane && options.autoReason
|
|
3020
|
+
? { auto_lane: normalizeMode(options.autoLane), auto_reason: String(options.autoReason) }
|
|
3021
|
+
: {};
|
|
2943
3022
|
|
|
2944
3023
|
try {
|
|
2945
3024
|
const result = await turnFunction(message, {
|
|
@@ -2956,6 +3035,7 @@ async function runHeadlessTurn(message, options = {}) {
|
|
|
2956
3035
|
model: modelForMode(mode),
|
|
2957
3036
|
output: String((result && result.output) || '').trim(),
|
|
2958
3037
|
durationMs: Number((result && result.durationMs) || 0) || (Date.now() - startedAt),
|
|
3038
|
+
...autoFields,
|
|
2959
3039
|
};
|
|
2960
3040
|
} catch (error) {
|
|
2961
3041
|
return {
|
|
@@ -2964,6 +3044,7 @@ async function runHeadlessTurn(message, options = {}) {
|
|
|
2964
3044
|
output: '',
|
|
2965
3045
|
durationMs: Date.now() - startedAt,
|
|
2966
3046
|
error: String((error && error.message) || error || 'headless turn failed'),
|
|
3047
|
+
...autoFields,
|
|
2967
3048
|
};
|
|
2968
3049
|
}
|
|
2969
3050
|
}
|
|
@@ -2977,7 +3058,9 @@ async function chat(options = {}) {
|
|
|
2977
3058
|
const output = logger ? logger.output : baseOutput;
|
|
2978
3059
|
const history = [];
|
|
2979
3060
|
let lastCompactionNotice = 0;
|
|
2980
|
-
|
|
3061
|
+
let conversationId = options.conversationId || `ax-${process.pid}-${Date.now().toString(36)}`;
|
|
3062
|
+
const session = { approveMode: 'stage' };
|
|
3063
|
+
let turnInFlight = false;
|
|
2981
3064
|
|
|
2982
3065
|
output.write(`${formatHeader({ mode, cwd, chat: true }, output)}\n\n`);
|
|
2983
3066
|
if (logger) output.write(`${formatAuxRow('log', formatPathSubject(logger.path, output), output)}\n\n`);
|
|
@@ -3024,11 +3107,31 @@ async function chat(options = {}) {
|
|
|
3024
3107
|
return false;
|
|
3025
3108
|
}
|
|
3026
3109
|
|
|
3110
|
+
if (trimmed === '/clear') {
|
|
3111
|
+
history.length = 0;
|
|
3112
|
+
conversationId = `ax-${process.pid}-${Date.now().toString(36)}`;
|
|
3113
|
+
output.write(`${paint('context cleared', [ANSI.muted], output)}\n\n`);
|
|
3114
|
+
return false;
|
|
3115
|
+
}
|
|
3116
|
+
|
|
3117
|
+
if (trimmed === '/context') {
|
|
3118
|
+
const ctx = estimateHistoryContext(history);
|
|
3119
|
+
output.write(`${paint(`· ${ctx.turns} turn${ctx.turns === 1 ? '' : 's'} · ~${ctx.chars} chars · ~${ctx.tokens} tokens`, [ANSI.muted], output)}\n\n`);
|
|
3120
|
+
return false;
|
|
3121
|
+
}
|
|
3122
|
+
|
|
3027
3123
|
if (trimmed.startsWith('/')) {
|
|
3028
3124
|
output.write(`${chatMenu(output)}\n\n`);
|
|
3029
3125
|
return false;
|
|
3030
3126
|
}
|
|
3031
3127
|
|
|
3128
|
+
if (options.auto) {
|
|
3129
|
+
mode = autoLaneForMessage(trimmed, {
|
|
3130
|
+
errorOutput: options.errorOutput,
|
|
3131
|
+
print: false,
|
|
3132
|
+
}).lane;
|
|
3133
|
+
}
|
|
3134
|
+
|
|
3032
3135
|
if (logger) logger.write(`${formatPrompt(mode)}${trimmed}\n`);
|
|
3033
3136
|
output.write('\n');
|
|
3034
3137
|
|
|
@@ -3956,7 +4059,17 @@ async function main() {
|
|
|
3956
4059
|
args.splice(verifyIdx, 2);
|
|
3957
4060
|
}
|
|
3958
4061
|
|
|
3959
|
-
const
|
|
4062
|
+
const explicitLane = args.includes('--code-fast') || args.includes('--code')
|
|
4063
|
+
? 'code-fast'
|
|
4064
|
+
: args.includes('--max')
|
|
4065
|
+
? 'max'
|
|
4066
|
+
: args.includes('--fast')
|
|
4067
|
+
? 'fast'
|
|
4068
|
+
: args.includes('--pro')
|
|
4069
|
+
? 'pro'
|
|
4070
|
+
: null;
|
|
4071
|
+
const autoEnabled = args.includes('--auto') && !explicitLane;
|
|
4072
|
+
let mode = explicitLane || 'pro';
|
|
3960
4073
|
const doctor = args.includes('--doctor');
|
|
3961
4074
|
const selfTest = args.includes('--self-test');
|
|
3962
4075
|
const benchmark = args.includes('--benchmark');
|
|
@@ -4017,7 +4130,7 @@ async function main() {
|
|
|
4017
4130
|
|
|
4018
4131
|
const route = forceCloud ? 'cloud' : forceLocal ? 'local' : 'auto';
|
|
4019
4132
|
const prompt = args
|
|
4020
|
-
.filter(arg => !['--max', '--fast', '--pro', '--code-fast', '--code', '--chat', '--doctor', '--approvals', '--self-test', '--benchmark', '--print', '--headless', '--local', '--cloud', '--help', '-h'].includes(arg))
|
|
4133
|
+
.filter(arg => !['--auto', '--max', '--fast', '--pro', '--code-fast', '--code', '--chat', '--doctor', '--approvals', '--self-test', '--benchmark', '--print', '--headless', '--local', '--cloud', '--help', '-h'].includes(arg))
|
|
4021
4134
|
.join(' ')
|
|
4022
4135
|
.trim();
|
|
4023
4136
|
|
|
@@ -4039,6 +4152,12 @@ async function main() {
|
|
|
4039
4152
|
return;
|
|
4040
4153
|
}
|
|
4041
4154
|
|
|
4155
|
+
const startsChat = !printMode && (!prompt || args.includes('--chat'));
|
|
4156
|
+
const autoPick = autoEnabled && prompt && !startsChat
|
|
4157
|
+
? autoLaneForMessage(prompt, { print: printMode })
|
|
4158
|
+
: null;
|
|
4159
|
+
if (autoPick) mode = autoPick.lane;
|
|
4160
|
+
|
|
4042
4161
|
let business = null;
|
|
4043
4162
|
if (businessSlug) {
|
|
4044
4163
|
if (normalizeMode(mode) === 'code-fast') {
|
|
@@ -4163,14 +4282,22 @@ async function main() {
|
|
|
4163
4282
|
|
|
4164
4283
|
if (printMode) {
|
|
4165
4284
|
const payload = prompt
|
|
4166
|
-
? await runHeadlessTurn(prompt, {
|
|
4285
|
+
? await runHeadlessTurn(prompt, {
|
|
4286
|
+
mode,
|
|
4287
|
+
cwd: process.cwd(),
|
|
4288
|
+
route: route === 'auto' ? undefined : route,
|
|
4289
|
+
business,
|
|
4290
|
+
verify,
|
|
4291
|
+
autoLane: autoPick && autoPick.lane,
|
|
4292
|
+
autoReason: autoPick && autoPick.reason,
|
|
4293
|
+
})
|
|
4167
4294
|
: { ok: false, model: modelForMode(mode), output: '', durationMs: 0, error: 'missing prompt' };
|
|
4168
4295
|
console.log(JSON.stringify(payload));
|
|
4169
4296
|
process.exit(payload.ok ? 0 : 1);
|
|
4170
4297
|
}
|
|
4171
4298
|
|
|
4172
4299
|
if (!prompt || args.includes('--chat')) {
|
|
4173
|
-
await chat({ mode, cwd: process.cwd(), route: route === 'auto' ? undefined : route, business, verify });
|
|
4300
|
+
await chat({ mode, cwd: process.cwd(), route: route === 'auto' ? undefined : route, business, verify, auto: autoEnabled });
|
|
4174
4301
|
return;
|
|
4175
4302
|
}
|
|
4176
4303
|
|
|
@@ -4276,6 +4403,7 @@ module.exports = {
|
|
|
4276
4403
|
postApprovalExecution,
|
|
4277
4404
|
postCodeFastTurn,
|
|
4278
4405
|
postTurn,
|
|
4406
|
+
postTurnTimeoutMs,
|
|
4279
4407
|
readApprovalStore,
|
|
4280
4408
|
removeStoredApproval,
|
|
4281
4409
|
renderStreamingMarkdown,
|