atris 3.37.1 → 3.40.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 +25 -6
- package/README.md +2 -0
- package/atris/PERSONA.md +11 -11
- package/atris.md +17 -2
- package/ax +160 -34
- package/bin/atris.js +78 -6
- package/commands/activate.js +16 -3
- package/commands/agent-spawn.js +13 -18
- package/commands/autoland.js +76 -47
- package/commands/autopilot-front.js +0 -4
- package/commands/autopilot.js +0 -12
- package/commands/bench.js +10 -12
- package/commands/brain.js +0 -7
- package/commands/brief.js +0 -2
- package/commands/business-sync.js +0 -1
- package/commands/business.js +452 -12
- package/commands/chat-scan.js +5 -7
- package/commands/clean.js +31 -2
- package/commands/close.js +46 -11
- package/commands/cloud.js +0 -1
- package/commands/codex-goal.js +8 -12
- package/commands/computer.js +6 -5
- package/commands/console.js +64 -11
- package/commands/decide.js +166 -0
- package/commands/deck.js +30 -12
- package/commands/dream.js +0 -7
- package/commands/drill.js +14 -25
- package/commands/engine.js +206 -26
- package/commands/experiments.js +0 -4
- package/commands/fleet-report.js +12 -4
- package/commands/game.js +261 -0
- package/commands/gm.js +17 -7
- package/commands/goal.js +0 -6
- package/commands/harvest.js +1 -5
- package/commands/improve.js +29 -8
- package/commands/init.js +41 -37
- package/commands/integrations.js +0 -7
- package/commands/land.js +77 -20
- package/commands/launchpad.js +1 -16
- package/commands/lesson.js +17 -7
- package/commands/lifecycle.js +5 -5
- package/commands/log.js +55 -5
- package/commands/loop-front.js +0 -2
- package/commands/loops.js +6 -5
- package/commands/meet.js +0 -2
- package/commands/member.js +622 -573
- package/commands/mission.js +790 -314
- package/commands/next.js +0 -1
- package/commands/now.js +0 -12
- package/commands/one-lap.js +8 -8
- package/commands/orb.js +0 -4
- package/commands/pack-craft.js +0 -1
- package/commands/pack.js +3599 -424
- package/commands/play.js +6 -6
- package/commands/probe.js +12 -6
- package/commands/pulse.js +88 -64
- package/commands/push.js +0 -4
- package/commands/radar.js +14 -11
- package/commands/recover.js +340 -0
- package/commands/release.js +10 -9
- package/commands/report.js +0 -1
- package/commands/router.js +320 -0
- package/commands/run-front.js +0 -2
- package/commands/search.js +0 -3
- package/commands/site-deploy.js +870 -0
- package/commands/site.js +11 -2
- package/commands/skill.js +7 -2
- package/commands/slop.js +237 -7
- package/commands/status.js +6 -0
- package/commands/stream.js +4 -20
- package/commands/study.js +0 -3
- package/commands/sync-checkout.js +51 -0
- package/commands/sync.js +11 -40
- package/commands/task.js +999 -595
- package/commands/taste.js +101 -0
- package/commands/teach.js +86 -0
- package/commands/team.js +83 -3
- package/commands/unknowns.js +0 -10
- package/commands/update.js +0 -1
- package/commands/vercel.js +4 -2
- package/commands/visualize.js +0 -5
- package/commands/voice.js +195 -0
- package/commands/watch.js +1 -28
- package/commands/wiki.js +42 -15
- package/commands/wish.js +0 -1
- package/commands/workflow.js +2 -2
- package/commands/worktree.js +88 -28
- package/commands/xp.js +28 -30
- package/commands/youtube.js +0 -2
- package/lib/accept-verify-gate.js +126 -0
- package/lib/activity-stream.js +0 -2
- package/lib/arg-parser.js +41 -0
- package/lib/auto-accept-certified.js +261 -3
- package/lib/autoland.js +66 -3
- package/lib/ax-auto-lane.js +68 -7
- package/lib/bench/context.js +0 -2
- package/lib/bench/report.js +0 -1
- package/lib/bench/runner.js +18 -11
- package/lib/brief-ledger.js +0 -6
- package/lib/business-simulate.js +382 -0
- package/lib/chat-log-scan.js +0 -5
- package/lib/checkout-sync.js +52 -0
- package/lib/claude-boot-block.js +48 -0
- package/lib/cloud-mission.js +0 -6
- package/lib/codex-flight.js +0 -1
- package/lib/conductor-artifacts.js +25 -0
- package/lib/context-gatherer.js +7 -2
- package/lib/deck-compose.js +0 -1
- package/lib/deck-history.js +0 -1
- package/lib/deck-layout.js +0 -5
- package/lib/deck-review.js +0 -7
- package/lib/deck-schema.js +0 -1
- package/lib/default-verifier.js +0 -2
- package/lib/engine-registry.js +157 -28
- package/lib/experiments/daily.js +0 -6
- package/lib/falsifier-probe.js +84 -0
- package/lib/fleet.js +275 -42
- package/lib/functional-owner.js +0 -3
- package/lib/git-spawn.js +15 -0
- package/lib/inspect-fields.js +0 -1
- package/lib/json-file.js +37 -0
- package/lib/known-commands.js +2 -2
- package/lib/learnings.js +0 -2
- package/lib/lesson-preflight.js +146 -0
- package/lib/loop-doctor.js +11 -19
- package/lib/manifest.js +0 -2
- package/lib/member-alive.js +0 -1
- package/lib/member-switches.js +0 -5
- package/lib/mission-artifact.js +0 -1
- package/lib/mission-human-asks.js +28 -0
- package/lib/mission-protected-lane.js +296 -0
- package/lib/mission-room.js +0 -2
- package/lib/mission-runtime-loop.js +0 -1
- package/lib/next-moves.js +0 -18
- package/lib/official-cli-integration.js +47 -6
- package/lib/orb-context.js +14 -4
- package/lib/orb-scorecard.js +0 -1
- package/lib/outbound-send-gate.js +0 -6
- package/lib/pack-capabilities.js +685 -0
- package/lib/permission-grants.js +0 -6
- package/lib/policy-lessons.js +0 -2
- package/lib/pulse.js +68 -15
- package/lib/review-integrity.js +0 -2
- package/lib/router-brain.js +51 -3
- package/lib/runner-command.js +0 -10
- package/lib/runtime-bootstrap.js +0 -1
- package/lib/scorecard.js +0 -1
- package/lib/self-drive.js +45 -14
- package/lib/state-detection.js +0 -6
- package/lib/task-db.js +137 -7
- package/lib/task-decision.js +50 -0
- package/lib/taste-lessons.js +153 -0
- package/lib/team-presence.js +0 -1
- package/lib/tool-result-encode.js +17 -1
- package/lib/voice-gate.js +129 -2
- package/lib/wiki.js +152 -9
- package/lib/wish-audit.js +21 -7
- package/lib/wish-delegate.js +27 -21
- package/lib/wish-stats.js +0 -5
- package/lib/wish-store.js +0 -1
- package/lib/workspace-safety.js +0 -2
- package/lib/zip.js +95 -8
- package/package.json +2 -1
- package/templates/business-starter/persona.md +9 -0
package/AGENTS.md
CHANGED
|
@@ -1,9 +1,28 @@
|
|
|
1
|
-
# AGENTS.md
|
|
1
|
+
# AGENTS.md: Universal Agent Instructions
|
|
2
2
|
|
|
3
|
-
> **New here?** Read [`FOR_AGENTS.md`](FOR_AGENTS.md)
|
|
3
|
+
> **New here?** Read [`FOR_AGENTS.md`](FOR_AGENTS.md): open letter from an agent who tried it.
|
|
4
4
|
|
|
5
5
|
> Works with: Claude Code, Cursor, Codex, OpenClaw, Windsurf, and any AI coding agent.
|
|
6
6
|
|
|
7
|
+
## The way of talking (every reply, no exceptions)
|
|
8
|
+
|
|
9
|
+
Talk like a person. This governs every message you send the operator: boot, status, summaries, everything.
|
|
10
|
+
|
|
11
|
+
- Plain words. Say what happened and what it means for the reader, cause and effect. Not the machinery.
|
|
12
|
+
- No task codes, branch names, commit hashes, PR numbers, or system nouns (worktree, verifier, projection, tick) in the message body. If the reader needs a command, put ONE copyable line at the end.
|
|
13
|
+
- No insider terms without defining them in the same breath. A sharp non-engineer must follow it on first read.
|
|
14
|
+
- Lead with the outcome. Complete sentences, one or two per paragraph, blank line between. No headers, bullet stacks, or tables in chat replies; those belong in files.
|
|
15
|
+
- Detail lives in files and receipts. Offer "want the detail?" instead of dumping it.
|
|
16
|
+
- No em dashes anywhere; use a comma, colon, or period.
|
|
17
|
+
- The test before sending: read it fried at 2am. If decoding takes work, rewrite it.
|
|
18
|
+
|
|
19
|
+
copy these shapes:
|
|
20
|
+
- **Status:** "The reply check is built. I am running the final checks now, so the result is not ready yet."
|
|
21
|
+
- **Landing:** "Replies now get a plain-language check before they reach you. The checks passed, and the change is ready."
|
|
22
|
+
- **Failure:** "The plain-language check could not run because its model was unavailable. Your reply still went through, so no work was blocked."
|
|
23
|
+
|
|
24
|
+
Canonical source: `atris/atris.md` `## voice`. This block is a copy for tools that only read `AGENTS.md`; if they differ, atris.md wins.
|
|
25
|
+
|
|
7
26
|
## Quick Start
|
|
8
27
|
|
|
9
28
|
```bash
|
|
@@ -31,7 +50,7 @@ task truth, proof, review, and backend/cloud sync all flow through Atris.
|
|
|
31
50
|
|------|---------|
|
|
32
51
|
| `atris/atris.md` | Protocol/backbone for this workspace |
|
|
33
52
|
| `atris/PERSONA.md` | Communication style (read first) |
|
|
34
|
-
| `atris task` | Current tasks, claims, dialogue, proof
|
|
53
|
+
| `atris task` | Current tasks, claims, dialogue, proof; deep dive: [`tasks.md`](tasks.md) |
|
|
35
54
|
| `.atris/state/tasks.projection.json` | Readable task projection for UIs/agents |
|
|
36
55
|
| `atris/TODO.md` | Rendered/legacy task view only |
|
|
37
56
|
| `atris/MAP.md` | Navigation (where is X?) |
|
|
@@ -125,13 +144,13 @@ member -> mission start --verify -> status --status active -> one bounded step -
|
|
|
125
144
|
- Close: if the verifier passes, run `atris mission complete <id> --proof "<receipt_path>"`; if current-agent work should keep going, repeat status -> step -> tick.
|
|
126
145
|
- Rollout: `atrisos-backend` and `atrisos-web` agents must check active missions before picking work; if no active mission exists and autonomy was requested, create one with owner, verifier, lane, and stop condition first.
|
|
127
146
|
|
|
128
|
-
## Build Craft
|
|
147
|
+
## Build Craft: what decides acceptance
|
|
129
148
|
|
|
130
149
|
Mined from this repo's receipts (803 receipts, 1711 episodes): proofs naming a
|
|
131
150
|
runnable verify command were accepted 670/674 at the gate; 24 of 28 bounces
|
|
132
151
|
named none. These rules are the difference, in priority order:
|
|
133
152
|
|
|
134
|
-
1. **Name a runnable verify command in every proof**, and run it bare
|
|
153
|
+
1. **Name a runnable verify command in every proof**, and run it bare; never
|
|
135
154
|
`test | tail` or `| grep`; a pipe replaces your exit code with the filter's.
|
|
136
155
|
2. **A task naming a spec file (`atris/features/<name>/idea.md`) is a contract.**
|
|
137
156
|
Read it before writing code; build the named slice only; use its verify
|
|
@@ -157,7 +176,7 @@ named none. These rules are the difference, in priority order:
|
|
|
157
176
|
reproduces it against the real runtime, not a mock that can stay green
|
|
158
177
|
through breakage.
|
|
159
178
|
10. **When your engine dies mid-build (credits, limits), that is a staffing
|
|
160
|
-
event, not a failure
|
|
179
|
+
event, not a failure**; leave the worktree intact with a note; the
|
|
161
180
|
conductor restaffs it.
|
|
162
181
|
|
|
163
182
|
## Rules
|
package/README.md
CHANGED
|
@@ -226,6 +226,8 @@ atris business record atris/reports/2026-04-12-operator-recap.md --outcome mixed
|
|
|
226
226
|
| `atris experiments` | Run small experiments and compare results |
|
|
227
227
|
| `atris computer card` | Show or write the local owner/computer card |
|
|
228
228
|
| `atris release` | Draft or publish a version bump, tag, GitHub release, and launch post |
|
|
229
|
+
| `atris slop` | Deterministic slop detector: frontend/prose tells, plus `slop dead --exports` for dead code |
|
|
230
|
+
| `atris clean` | Housekeeping: heal MAP refs, archive journals, report stale pages and dead code |
|
|
229
231
|
|
|
230
232
|
## Built-In Systems
|
|
231
233
|
|
package/atris/PERSONA.md
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# PERSONA.md
|
|
1
|
+
# PERSONA.md: Atris Agent Personality
|
|
2
2
|
|
|
3
3
|
This defines how Atris agents communicate, decide, and work.
|
|
4
4
|
|
|
@@ -28,21 +28,17 @@ This defines how Atris agents communicate, decide, and work.
|
|
|
28
28
|
|
|
29
29
|
## Core Workflow
|
|
30
30
|
|
|
31
|
-
**Read before you act.** Before planning or building, read the relevant files. Understand the current state. Your first action in any new area is always reconnaissance
|
|
31
|
+
**Read before you act.** Before planning or building, read the relevant files. Understand the current state. Your first action in any new area is always reconnaissance, not execution.
|
|
32
32
|
|
|
33
|
-
**
|
|
33
|
+
**Ask for intent when it's fuzzy or the blast radius is real.** A vague brief gets 2-3 named interpretations with a recommendation, not open questions. Small, reversible, clearly-scoped work: act, then report. Standing autonomy (autoland, push-asap) means asking permission for routine work is a failure smell, not politeness.
|
|
34
34
|
|
|
35
|
-
**Use ASCII visualization to confirm understanding:**
|
|
35
|
+
**Use ASCII visualization to confirm understanding when a plan is worth confirming:**
|
|
36
36
|
- **UI elements:** Show design using ASCII
|
|
37
37
|
- **Backend:** Use arrows, diagrams, logic gates
|
|
38
38
|
- **Databases:** Tables and graphs showing relationships
|
|
39
39
|
- **Other cases:** Use best judgment
|
|
40
40
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
Then go 3-4 sentences one by one through each task.
|
|
44
|
-
|
|
45
|
-
Once every task is confirmed, create a plan.
|
|
41
|
+
A one-file fix needs no diagram; a new surface or cross-system change does.
|
|
46
42
|
|
|
47
43
|
**If a task is too big, break it down.** One job per task. If you can't describe "done" in one sentence, decompose it. Small precise tasks compound into big results.
|
|
48
44
|
|
|
@@ -56,9 +52,13 @@ We can always add layer by layer.
|
|
|
56
52
|
|
|
57
53
|
## Communication Style
|
|
58
54
|
|
|
59
|
-
**
|
|
55
|
+
**Talk like a person.** Every message the operator reads: plain words, what happened and what it means for them, cause and effect. Not the machinery.
|
|
56
|
+
|
|
57
|
+
**No codes in the message body.** No task ids, branch names, commit hashes, PR numbers, or system nouns (worktree, verifier, projection, tick). If the reader needs a command, ONE copyable line at the end. Insider terms get defined in the same breath or cut.
|
|
58
|
+
|
|
59
|
+
**Lead with the outcome.** Complete sentences, one or two per paragraph, blank line between. No headers, bullet stacks, or tables in chat replies. Detail lives in files; offer "want the detail?" instead of dumping it.
|
|
60
60
|
|
|
61
|
-
|
|
61
|
+
**The test before sending:** read it fried at 2am. If decoding takes work, rewrite it.
|
|
62
62
|
|
|
63
63
|
If something is slop, call it out. Optimize ruthlessly.
|
|
64
64
|
|
package/atris.md
CHANGED
|
@@ -7,7 +7,9 @@ leave a trail another agent or human can trust.
|
|
|
7
7
|
|
|
8
8
|
## activate
|
|
9
9
|
|
|
10
|
-
On session start, before responding
|
|
10
|
+
On session start, before responding (if the harness already booted for you, via a
|
|
11
|
+
SessionStart hook or an `atris` status block in context, do not repeat it; go
|
|
12
|
+
straight to the work):
|
|
11
13
|
|
|
12
14
|
1. Read:
|
|
13
15
|
- `atris/logs/YYYY/YYYY-MM-DD.md`: today's journal
|
|
@@ -39,13 +41,17 @@ If a task was already given, show the box and proceed with that task.
|
|
|
39
41
|
|
|
40
42
|
You can move fast. You do not get to move blindly.
|
|
41
43
|
|
|
42
|
-
Before
|
|
44
|
+
Scale ceremony to blast radius. Before a nontrivial change (new surface,
|
|
45
|
+
multi-file, migrations, anything hard to roll back), state:
|
|
43
46
|
- the goal
|
|
44
47
|
- the files or systems in scope
|
|
45
48
|
- what "done" means
|
|
46
49
|
- how it will be checked
|
|
47
50
|
- what happens if it fails
|
|
48
51
|
|
|
52
|
+
For a small bounded fix, the task's Exit and Verify fields carry this; do not
|
|
53
|
+
restate them as ritual.
|
|
54
|
+
|
|
49
55
|
Then:
|
|
50
56
|
- when you begin work in a repo that has no `atris/` folder, run `npx -y atris init` before anything else, then read `atris/atris.md`
|
|
51
57
|
- do not execute if another agent owns the same task or files
|
|
@@ -77,6 +83,13 @@ What you ship should not read as generated. The test: if someone said "an AI mad
|
|
|
77
83
|
|
|
78
84
|
The same discipline for words. Output stays sharp no matter how bloated the context. A full context is not license to ramble.
|
|
79
85
|
|
|
86
|
+
- **The way of talking.** Chat replies to the operator carry no task codes, branch names, commit hashes, PR numbers, or system nouns (worktree, verifier, projection, tick); if the reader needs a command, it is ONE copyable line at the end. Insider terms get defined in the same breath or cut. The test before sending: read it fried at 2am; if decoding takes work, rewrite. This is the canonical statement; `AGENTS.md` and other adapters carry a copy of it, never a variant.
|
|
87
|
+
|
|
88
|
+
copy these shapes:
|
|
89
|
+
- **Status:** "The reply check is built. I am running the final checks now, so the result is not ready yet."
|
|
90
|
+
- **Landing:** "Replies now get a plain-language check before they reach you. The checks passed, and the change is ready."
|
|
91
|
+
- **Failure:** "The plain-language check could not run because its model was unavailable. Your reply still went through, so no work was blocked."
|
|
92
|
+
|
|
80
93
|
- **Lead with the move.** Answer first, support after. No preamble, no agreement reflex ("great question", "you're absolutely right").
|
|
81
94
|
- **Specific over buzzy.** Name the exact thing. If you can't, you don't understand it yet; go look, don't hedge.
|
|
82
95
|
- **Cut filler.** Drop "it's worth noting", "in order to", "leverage", "seamless", "robust", "delve", stacked hedges, and em dashes. `atris slop` flags the prose tells (em-dash, hype-copy) too.
|
|
@@ -90,6 +103,8 @@ The same discipline for words. Output stays sharp no matter how bloated the cont
|
|
|
90
103
|
- "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
104
|
- "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
105
|
- **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.
|
|
106
|
+
- **Pace with whitespace.** Words for the operator arrive as complete sentences, one or two per paragraph, blank line between. The whitespace does the pacing, never fragments: fast to scan, still flowing.
|
|
107
|
+
- **Assume attention is scarce and ambition is not.** The reader is at the end of a long day and still moving something big forward. Nothing may require held state (remembering step 2 while reading step 5), every surface ends in one obvious next action that starts in under a minute, and each glance should leave the reader one step closer. Engineers and non-engineers get the same plain sentence; precision stays one ask away, never in the way.
|
|
93
108
|
- **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
109
|
- **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.
|
|
95
110
|
|
package/ax
CHANGED
|
@@ -12,7 +12,8 @@ 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
|
+
const { loadOverrides, pickLane } = require('./lib/ax-auto-lane');
|
|
16
|
+
const { buildToolResultBody } = require('./lib/tool-result-encode');
|
|
16
17
|
|
|
17
18
|
const EXIT_WORDS = new Set(['exit', 'quit', ':q']);
|
|
18
19
|
const BACKEND = {
|
|
@@ -957,16 +958,92 @@ function normalizeMode(mode) {
|
|
|
957
958
|
return mode === 'fast' ? 'fast' : 'pro';
|
|
958
959
|
}
|
|
959
960
|
|
|
961
|
+
function autoPickLogPath(options = {}) {
|
|
962
|
+
return options.pickLogPath
|
|
963
|
+
|| process.env.ATRIS_ROUTER_PICKS_PATH
|
|
964
|
+
|| path.join(os.homedir(), '.atris', 'ax-auto-picks.jsonl');
|
|
965
|
+
}
|
|
966
|
+
|
|
967
|
+
function autoMessageWords(message) {
|
|
968
|
+
return new Set(String(message || '').toLowerCase().match(/[a-z0-9]+/g) || []);
|
|
969
|
+
}
|
|
970
|
+
|
|
971
|
+
function autoMessageOverlap(left, right) {
|
|
972
|
+
const leftWords = autoMessageWords(left);
|
|
973
|
+
const rightWords = autoMessageWords(right);
|
|
974
|
+
const union = new Set([...leftWords, ...rightWords]);
|
|
975
|
+
if (!union.size) return 0;
|
|
976
|
+
let shared = 0;
|
|
977
|
+
for (const word of leftWords) {
|
|
978
|
+
if (rightWords.has(word)) shared += 1;
|
|
979
|
+
}
|
|
980
|
+
return shared / union.size;
|
|
981
|
+
}
|
|
982
|
+
|
|
983
|
+
function appendRephrasedOutcome(message, logPath) {
|
|
984
|
+
try {
|
|
985
|
+
const rows = fs.readFileSync(logPath, 'utf8').split(/\r?\n/).filter((line) => line.trim());
|
|
986
|
+
let previous = null;
|
|
987
|
+
for (let index = rows.length - 1; index >= 0; index -= 1) {
|
|
988
|
+
try {
|
|
989
|
+
const row = JSON.parse(rows[index]);
|
|
990
|
+
if (row && row.pick_id && row.lane && row.message) {
|
|
991
|
+
previous = row;
|
|
992
|
+
break;
|
|
993
|
+
}
|
|
994
|
+
} catch (_) {
|
|
995
|
+
// Ignore malformed historical lines.
|
|
996
|
+
}
|
|
997
|
+
}
|
|
998
|
+
if (!previous) return;
|
|
999
|
+
const ageMs = Date.now() - Date.parse(previous.at);
|
|
1000
|
+
if (ageMs < 0 || ageMs >= 10 * 60 * 1000) return;
|
|
1001
|
+
if (autoMessageOverlap(previous.message, message) < 0.5) return;
|
|
1002
|
+
fs.appendFileSync(logPath, `${JSON.stringify({
|
|
1003
|
+
event: 'outcome',
|
|
1004
|
+
pick_id: previous.pick_id,
|
|
1005
|
+
outcome: 'rephrased',
|
|
1006
|
+
})}\n`);
|
|
1007
|
+
} catch (_) {
|
|
1008
|
+
// Rephrase detection is best-effort and must never block a turn.
|
|
1009
|
+
}
|
|
1010
|
+
}
|
|
1011
|
+
|
|
960
1012
|
function appendAutoPick(message, picked, options = {}) {
|
|
1013
|
+
const pick = { ...picked, pick_id: crypto.randomUUID() };
|
|
1014
|
+
const logPath = autoPickLogPath(options);
|
|
1015
|
+
Object.defineProperty(pick, '_logPath', { value: logPath, enumerable: false });
|
|
961
1016
|
try {
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
fs.appendFileSync(
|
|
1017
|
+
fs.mkdirSync(path.dirname(logPath), { recursive: true });
|
|
1018
|
+
appendRephrasedOutcome(message, logPath);
|
|
1019
|
+
fs.appendFileSync(logPath, `${JSON.stringify({
|
|
965
1020
|
at: new Date().toISOString(),
|
|
966
|
-
|
|
967
|
-
|
|
1021
|
+
pick_id: pick.pick_id,
|
|
1022
|
+
lane: pick.lane,
|
|
1023
|
+
reason: pick.reason,
|
|
1024
|
+
message: String(message || ''),
|
|
968
1025
|
message_chars: String(message || '').length,
|
|
969
1026
|
print: Boolean(options.print),
|
|
1027
|
+
...(pick.override_id ? { override_id: pick.override_id } : {}),
|
|
1028
|
+
})}\n`);
|
|
1029
|
+
} catch (_) {
|
|
1030
|
+
// Lane telemetry is best-effort and must never block a turn.
|
|
1031
|
+
}
|
|
1032
|
+
return pick;
|
|
1033
|
+
}
|
|
1034
|
+
|
|
1035
|
+
function appendAutoOutcome(picked, result, options = {}) {
|
|
1036
|
+
if (!picked || !picked.pick_id) return;
|
|
1037
|
+
try {
|
|
1038
|
+
const logPath = autoPickLogPath({ pickLogPath: options.pickLogPath || picked._logPath });
|
|
1039
|
+
fs.mkdirSync(path.dirname(logPath), { recursive: true });
|
|
1040
|
+
fs.appendFileSync(logPath, `${JSON.stringify({
|
|
1041
|
+
event: 'outcome',
|
|
1042
|
+
pick_id: picked.pick_id,
|
|
1043
|
+
ok: Boolean(result && result.ok),
|
|
1044
|
+
model: String((result && result.model) || modelForMode(picked.lane)),
|
|
1045
|
+
duration_ms: Number((result && (result.duration_ms || result.durationMs)) || 0),
|
|
1046
|
+
error: result && result.error ? String(result.error) : null,
|
|
970
1047
|
})}\n`);
|
|
971
1048
|
} catch (_) {
|
|
972
1049
|
// Lane telemetry is best-effort and must never block a turn.
|
|
@@ -974,8 +1051,10 @@ function appendAutoPick(message, picked, options = {}) {
|
|
|
974
1051
|
}
|
|
975
1052
|
|
|
976
1053
|
function autoLaneForMessage(message, options = {}) {
|
|
977
|
-
const
|
|
978
|
-
|
|
1054
|
+
const overrides = Array.isArray(options.overrides)
|
|
1055
|
+
? options.overrides
|
|
1056
|
+
: loadOverrides(options.overridesPath);
|
|
1057
|
+
const picked = appendAutoPick(message, pickLane(message, { overrides }), options);
|
|
979
1058
|
const errorOutput = options.errorOutput || process.stderr;
|
|
980
1059
|
errorOutput.write(`${picked.reason}\n`);
|
|
981
1060
|
return picked;
|
|
@@ -2793,7 +2872,7 @@ async function postTurn(message, options = {}) {
|
|
|
2793
2872
|
if (options.business) {
|
|
2794
2873
|
return options.business.postToolResult(callId, result, backendBaseUrl({ route: 'cloud' }));
|
|
2795
2874
|
}
|
|
2796
|
-
return postJson(BACKEND_API_TOOL_RESULT_PATH,
|
|
2875
|
+
return postJson(BACKEND_API_TOOL_RESULT_PATH, buildToolResultBody(callId, result), {
|
|
2797
2876
|
token,
|
|
2798
2877
|
route: endpointRoute,
|
|
2799
2878
|
timeoutMs: 30000,
|
|
@@ -3016,8 +3095,11 @@ async function runHeadlessTurn(message, options = {}) {
|
|
|
3016
3095
|
const startedAt = Date.now();
|
|
3017
3096
|
const sink = options.output || bufferedOutput();
|
|
3018
3097
|
const turnFunction = options.turnFunction || turnFunctionForMode(mode);
|
|
3019
|
-
const
|
|
3020
|
-
|
|
3098
|
+
const autoPick = options.autoPick || null;
|
|
3099
|
+
const autoFields = autoPick
|
|
3100
|
+
? { auto_lane: normalizeMode(autoPick.lane), auto_reason: String(autoPick.reason) }
|
|
3101
|
+
: options.autoLane && options.autoReason
|
|
3102
|
+
? { auto_lane: normalizeMode(options.autoLane), auto_reason: String(options.autoReason) }
|
|
3021
3103
|
: {};
|
|
3022
3104
|
|
|
3023
3105
|
try {
|
|
@@ -3030,15 +3112,17 @@ async function runHeadlessTurn(message, options = {}) {
|
|
|
3030
3112
|
output: sink,
|
|
3031
3113
|
showProgress: false,
|
|
3032
3114
|
});
|
|
3033
|
-
|
|
3115
|
+
const payload = {
|
|
3034
3116
|
ok: true,
|
|
3035
3117
|
model: modelForMode(mode),
|
|
3036
3118
|
output: String((result && result.output) || '').trim(),
|
|
3037
3119
|
durationMs: Number((result && result.durationMs) || 0) || (Date.now() - startedAt),
|
|
3038
3120
|
...autoFields,
|
|
3039
3121
|
};
|
|
3122
|
+
appendAutoOutcome(autoPick, payload);
|
|
3123
|
+
return payload;
|
|
3040
3124
|
} catch (error) {
|
|
3041
|
-
|
|
3125
|
+
const payload = {
|
|
3042
3126
|
ok: false,
|
|
3043
3127
|
model: modelForMode(mode),
|
|
3044
3128
|
output: '',
|
|
@@ -3046,6 +3130,8 @@ async function runHeadlessTurn(message, options = {}) {
|
|
|
3046
3130
|
error: String((error && error.message) || error || 'headless turn failed'),
|
|
3047
3131
|
...autoFields,
|
|
3048
3132
|
};
|
|
3133
|
+
appendAutoOutcome(autoPick, payload);
|
|
3134
|
+
return payload;
|
|
3049
3135
|
}
|
|
3050
3136
|
}
|
|
3051
3137
|
|
|
@@ -3125,11 +3211,16 @@ async function chat(options = {}) {
|
|
|
3125
3211
|
return false;
|
|
3126
3212
|
}
|
|
3127
3213
|
|
|
3214
|
+
let autoPick = null;
|
|
3128
3215
|
if (options.auto) {
|
|
3129
|
-
|
|
3216
|
+
autoPick = autoLaneForMessage(trimmed, {
|
|
3130
3217
|
errorOutput: options.errorOutput,
|
|
3131
3218
|
print: false,
|
|
3132
|
-
|
|
3219
|
+
overrides: options.overrides,
|
|
3220
|
+
overridesPath: options.overridesPath,
|
|
3221
|
+
pickLogPath: options.pickLogPath,
|
|
3222
|
+
});
|
|
3223
|
+
mode = autoPick.lane;
|
|
3133
3224
|
}
|
|
3134
3225
|
|
|
3135
3226
|
if (logger) logger.write(`${formatPrompt(mode)}${trimmed}\n`);
|
|
@@ -3230,22 +3321,38 @@ async function chat(options = {}) {
|
|
|
3230
3321
|
lastCompactionNotice = compactedTurns;
|
|
3231
3322
|
output.write(`${paint(`· context: ${compactedTurns} earlier turn${compactedTurns === 1 ? '' : 's'} compacted into a digest`, [ANSI.muted], output)}\n`);
|
|
3232
3323
|
}
|
|
3233
|
-
|
|
3234
|
-
|
|
3235
|
-
|
|
3236
|
-
|
|
3237
|
-
|
|
3238
|
-
|
|
3239
|
-
|
|
3240
|
-
|
|
3241
|
-
|
|
3242
|
-
|
|
3243
|
-
|
|
3244
|
-
|
|
3245
|
-
|
|
3246
|
-
|
|
3247
|
-
|
|
3248
|
-
|
|
3324
|
+
let result;
|
|
3325
|
+
try {
|
|
3326
|
+
result = await turnFunction(trimmed, {
|
|
3327
|
+
mode,
|
|
3328
|
+
cwd,
|
|
3329
|
+
history,
|
|
3330
|
+
input,
|
|
3331
|
+
output,
|
|
3332
|
+
route,
|
|
3333
|
+
business: options.business,
|
|
3334
|
+
verify: options.verify,
|
|
3335
|
+
conversationId,
|
|
3336
|
+
turnId,
|
|
3337
|
+
approveBackendApi: options.approveBackendApi,
|
|
3338
|
+
apiRequestJson: options.apiRequestJson,
|
|
3339
|
+
loadCredentials: options.loadCredentials,
|
|
3340
|
+
getApiBaseUrl: options.getApiBaseUrl,
|
|
3341
|
+
});
|
|
3342
|
+
appendAutoOutcome(autoPick, {
|
|
3343
|
+
ok: true,
|
|
3344
|
+
model: modelForMode(mode),
|
|
3345
|
+
durationMs: Number(result && result.durationMs) || (Date.now() - turnStartedAt),
|
|
3346
|
+
});
|
|
3347
|
+
} catch (error) {
|
|
3348
|
+
appendAutoOutcome(autoPick, {
|
|
3349
|
+
ok: false,
|
|
3350
|
+
model: modelForMode(mode),
|
|
3351
|
+
durationMs: Date.now() - turnStartedAt,
|
|
3352
|
+
error: String((error && error.message) || error),
|
|
3353
|
+
});
|
|
3354
|
+
throw error;
|
|
3355
|
+
}
|
|
3249
3356
|
if (result.output && !result.output.endsWith('\n')) output.write('\n');
|
|
3250
3357
|
const approvalExecution = normalizeMode(mode) === 'code-fast'
|
|
3251
3358
|
? null
|
|
@@ -4288,8 +4395,7 @@ async function main() {
|
|
|
4288
4395
|
route: route === 'auto' ? undefined : route,
|
|
4289
4396
|
business,
|
|
4290
4397
|
verify,
|
|
4291
|
-
|
|
4292
|
-
autoReason: autoPick && autoPick.reason,
|
|
4398
|
+
autoPick,
|
|
4293
4399
|
})
|
|
4294
4400
|
: { ok: false, model: modelForMode(mode), output: '', durationMs: 0, error: 'missing prompt' };
|
|
4295
4401
|
console.log(JSON.stringify(payload));
|
|
@@ -4305,7 +4411,23 @@ async function main() {
|
|
|
4305
4411
|
console.log('');
|
|
4306
4412
|
maybeWriteAtrisGoalBanner(process.stdout, process.cwd());
|
|
4307
4413
|
const turnStartedAt = Date.now();
|
|
4308
|
-
|
|
4414
|
+
let result;
|
|
4415
|
+
try {
|
|
4416
|
+
result = await turnFunctionForMode(mode)(prompt, { mode, cwd: process.cwd(), route: route === 'auto' ? undefined : route, business, verify });
|
|
4417
|
+
appendAutoOutcome(autoPick, {
|
|
4418
|
+
ok: true,
|
|
4419
|
+
model: modelForMode(mode),
|
|
4420
|
+
durationMs: Number(result && result.durationMs) || (Date.now() - turnStartedAt),
|
|
4421
|
+
});
|
|
4422
|
+
} catch (error) {
|
|
4423
|
+
appendAutoOutcome(autoPick, {
|
|
4424
|
+
ok: false,
|
|
4425
|
+
model: modelForMode(mode),
|
|
4426
|
+
durationMs: Date.now() - turnStartedAt,
|
|
4427
|
+
error: String((error && error.message) || error),
|
|
4428
|
+
});
|
|
4429
|
+
throw error;
|
|
4430
|
+
}
|
|
4309
4431
|
const receipt = receiptFromState(result);
|
|
4310
4432
|
const approvalRequest = normalizeMode(mode) === 'code-fast' ? null : approvalRequestFromReceipt(receipt);
|
|
4311
4433
|
const approvalRecord = approvalRequest
|
|
@@ -4338,6 +4460,10 @@ if (require.main === module) {
|
|
|
4338
4460
|
}
|
|
4339
4461
|
|
|
4340
4462
|
module.exports = {
|
|
4463
|
+
appendAutoOutcome,
|
|
4464
|
+
appendAutoPick,
|
|
4465
|
+
autoLaneForMessage,
|
|
4466
|
+
autoMessageOverlap,
|
|
4341
4467
|
authToken,
|
|
4342
4468
|
authUserId,
|
|
4343
4469
|
approvalExecutionRequestFromReceipt,
|