atris 3.15.56 → 3.16.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 +2 -2
- package/GETTING_STARTED.md +1 -1
- package/PERSONA.md +4 -4
- package/README.md +11 -11
- package/atris/skills/copy-editor/SKILL.md +30 -4
- package/atris/skills/improve/SKILL.md +18 -20
- package/atris/wiki/concepts/agent-activation-contract.md +5 -3
- package/atris/wiki/concepts/workspace-initialization-contract.md +4 -4
- package/atris/wiki/index.md +1 -0
- package/ax +522 -73
- package/bin/atris.js +32 -31
- package/commands/align.js +0 -14
- package/commands/apps.js +102 -1
- package/commands/autopilot.js +197 -22
- package/commands/brain.js +219 -34
- package/commands/brainstorm.js +0 -829
- package/commands/computer.js +45 -83
- package/commands/improve.js +501 -0
- package/commands/integrations.js +228 -0
- package/commands/lesson.js +44 -0
- package/commands/member.js +4498 -226
- package/commands/mission.js +302 -27
- package/commands/now.js +89 -1
- package/commands/radar.js +181 -56
- package/commands/skill.js +37 -6
- package/commands/soul.js +0 -4
- package/commands/task.js +5582 -517
- package/commands/terminal.js +14 -10
- package/commands/wiki.js +87 -1
- package/commands/workflow.js +288 -73
- package/commands/worktree.js +52 -15
- package/commands/xp.js +41 -65
- package/lib/auto-accept-certified.js +294 -0
- package/lib/file-ops.js +0 -184
- package/lib/member-alive.js +232 -0
- package/lib/policy-lessons.js +280 -0
- package/lib/receipt-evidence.js +64 -0
- package/lib/state-detection.js +34 -0
- package/lib/task-db.js +568 -16
- package/lib/task-proof.js +43 -0
- package/package.json +1 -1
- package/utils/auth.js +13 -4
- package/commands/research.js +0 -52
- package/lib/section-merge.js +0 -196
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const GENERIC_COMPLETION_PROOF_RE = /^(?:done|done now|complete|completed|finished|fixed|handled|ship|shipped|ok|okay|yes|yep|looks good|looks good to me|all set|should be good|works now|approved|approve|lgtm|failed)$/i;
|
|
4
|
+
|
|
5
|
+
const COMMAND_PROOF_RE = /\b(?:npm\s+run|npm\s+test|node\s+--test|node\s+scripts\/|pnpm\b|yarn\b|npx\b|pytest\b|python\s+-m|tsc\b|vite\s+build|git\s+diff\s+--check|curl\b|atris\s+task|\.\/ax\b|ax\s+--|test\s+-s)\b/i;
|
|
6
|
+
const FILE_PROOF_RE = /(?:^|[\s'"`])(?:\.{0,2}\/|~\/|\/Users\/|src\/|scripts\/|atris\/|backend\/|public\/|resources\/|package[.]json|main[.]js|preload[.]js|AGENTXP_PROOF[.]md)[^\s'"`,;)]*/i;
|
|
7
|
+
const PATH_ONLY_PROOF_RE = /(?:^|[\s'"`])(?:\.{0,2}\/|~\/|\/Users\/|\/private\/|\/var\/|atris\/runs\/|\.atris\/state\/)[^\s'"`,;)]+(?:[.](?:json|jsonl|md|log|txt|png|jpg|jpeg|pdf))?/i;
|
|
8
|
+
const RECEIPT_OR_ARTIFACT_RE = /\b(?:receipt|artifact|screenshot|log|trace|path=|file=|bytes=|model=|opened=|https?:\/\/)\b/i;
|
|
9
|
+
const RESULT_PAIR_RE = /\b(?:typecheck|build|smoke|test|pytest|verifier|validation|validated|verified|render|diff|sync|lineage|projection)\b.{0,80}\b(?:pass|passed|failed|green|ok|exit\s*0|reviewed)\b|\b(?:pass|passed|failed|green|ok|exit\s*0|reviewed)\b.{0,80}\b(?:typecheck|build|smoke|test|pytest|verifier|validation|validated|verified|render|diff|sync|lineage|projection)\b/i;
|
|
10
|
+
const HUMAN_PROOF_RE = /\b(?:team human approved|human approved|human approval|approved by|accepted by|reviewed by|customer replied|customer approved|customer accepted|replied)\b/i;
|
|
11
|
+
const FILE_ACTION_RE = /\b(?:changed|updated|edited|created|deleted|saved|wrote|patched|reviewed|verified|validated|opened|read)\b/i;
|
|
12
|
+
|
|
13
|
+
function compactWhitespace(text) {
|
|
14
|
+
return String(text || '').replace(/\s+/g, ' ').trim();
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function taskProofState(proof) {
|
|
18
|
+
const text = compactWhitespace(proof);
|
|
19
|
+
if (!text) return { ok: false, reason: 'proof required' };
|
|
20
|
+
if (GENERIC_COMPLETION_PROOF_RE.test(text)) {
|
|
21
|
+
return { ok: false, reason: 'proof must name what was verified, changed, approved, or produced' };
|
|
22
|
+
}
|
|
23
|
+
if (COMMAND_PROOF_RE.test(text)) return { ok: true, reason: 'proof names a command' };
|
|
24
|
+
if (HUMAN_PROOF_RE.test(text)) return { ok: true, reason: 'proof names human/customer approval' };
|
|
25
|
+
if (RESULT_PAIR_RE.test(text)) return { ok: true, reason: 'proof names a verifier result' };
|
|
26
|
+
if (PATH_ONLY_PROOF_RE.test(text)) return { ok: true, reason: 'proof names a receipt or artifact path' };
|
|
27
|
+
if (RECEIPT_OR_ARTIFACT_RE.test(text) && (FILE_PROOF_RE.test(text) || RESULT_PAIR_RE.test(text))) {
|
|
28
|
+
return { ok: true, reason: 'proof names a receipt or artifact' };
|
|
29
|
+
}
|
|
30
|
+
if (FILE_PROOF_RE.test(text) && FILE_ACTION_RE.test(text) && RESULT_PAIR_RE.test(text)) {
|
|
31
|
+
return { ok: true, reason: 'proof names changed files and validation' };
|
|
32
|
+
}
|
|
33
|
+
return { ok: false, reason: 'proof needs concrete evidence: command, verifier result, receipt/artifact path, changed file plus validation, or explicit human/customer approval' };
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function taskProofLooksMeaningful(proof) {
|
|
37
|
+
return taskProofState(proof).ok;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
module.exports = {
|
|
41
|
+
taskProofLooksMeaningful,
|
|
42
|
+
taskProofState,
|
|
43
|
+
};
|
package/package.json
CHANGED
package/utils/auth.js
CHANGED
|
@@ -324,7 +324,15 @@ function saveCredentials(token, refreshToken, email, userId, provider) {
|
|
|
324
324
|
}
|
|
325
325
|
|
|
326
326
|
function loadCredentials() {
|
|
327
|
-
// Priority: ATRIS_PROFILE env var → per-terminal session file → global credentials.json
|
|
327
|
+
// Priority: ATRIS_TOKEN env var → ATRIS_PROFILE env var → per-terminal session file → global credentials.json
|
|
328
|
+
|
|
329
|
+
// 0. Raw token injection. Headless boxes (cloud business computers) have no
|
|
330
|
+
// browser for `atris login`; the runner injects a scoped token as env
|
|
331
|
+
// instead, so no credentials file ever lands on disk.
|
|
332
|
+
const envToken = process.env.ATRIS_TOKEN;
|
|
333
|
+
if (envToken && envToken.trim()) {
|
|
334
|
+
return { token: envToken.trim(), provider: null, source: 'env' };
|
|
335
|
+
}
|
|
328
336
|
|
|
329
337
|
// 1. Explicit env var override
|
|
330
338
|
const profileOverride = process.env.ATRIS_PROFILE;
|
|
@@ -481,9 +489,10 @@ async function ensureValidCredentials(apiRequestJson, options = {}) {
|
|
|
481
489
|
const updatedUserId = user?.id || credentials.user_id;
|
|
482
490
|
|
|
483
491
|
if (
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
492
|
+
credentials.source !== 'env' &&
|
|
493
|
+
(updatedEmail !== credentials.email ||
|
|
494
|
+
updatedProvider !== credentials.provider ||
|
|
495
|
+
updatedUserId !== credentials.user_id)
|
|
487
496
|
) {
|
|
488
497
|
saveCredentials(
|
|
489
498
|
credentials.token,
|
package/commands/research.js
DELETED
|
@@ -1,52 +0,0 @@
|
|
|
1
|
-
const { initResearchWorkspace } = require('./business');
|
|
2
|
-
|
|
3
|
-
async function researchQuickstart() {
|
|
4
|
-
console.log(`
|
|
5
|
-
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
6
|
-
Start a Research Lab in 3 Commands
|
|
7
|
-
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
8
|
-
|
|
9
|
-
1. Create:
|
|
10
|
-
atris research init "Frontier Lab"
|
|
11
|
-
|
|
12
|
-
2. Open the local workspace:
|
|
13
|
-
cd ~/arena/atris-business/frontier-lab
|
|
14
|
-
|
|
15
|
-
3. Push local state to cloud:
|
|
16
|
-
atris align --fix
|
|
17
|
-
|
|
18
|
-
The research template starts with:
|
|
19
|
-
hypotheses + experiment lanes
|
|
20
|
-
eval-first reward policy
|
|
21
|
-
literature + findings workflow
|
|
22
|
-
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
23
|
-
`);
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
async function researchCommand(subcommand, ...args) {
|
|
27
|
-
switch (subcommand) {
|
|
28
|
-
case 'init':
|
|
29
|
-
case 'workspace':
|
|
30
|
-
case 'create':
|
|
31
|
-
await initResearchWorkspace(args[0], ...args.slice(1));
|
|
32
|
-
break;
|
|
33
|
-
case 'quickstart':
|
|
34
|
-
case 'start':
|
|
35
|
-
case 'guide':
|
|
36
|
-
await researchQuickstart();
|
|
37
|
-
break;
|
|
38
|
-
default:
|
|
39
|
-
console.log('Usage: atris research <command> [args]');
|
|
40
|
-
console.log('');
|
|
41
|
-
console.log(' quickstart ← Start here! 3-command guide');
|
|
42
|
-
console.log('');
|
|
43
|
-
console.log(' init <name> Create a research lab workspace (cloud + local)');
|
|
44
|
-
console.log(' workspace <name> Alias for init');
|
|
45
|
-
console.log(' create <name> Alias for init');
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
module.exports = {
|
|
50
|
-
researchCommand,
|
|
51
|
-
researchQuickstart,
|
|
52
|
-
};
|
package/lib/section-merge.js
DELETED
|
@@ -1,196 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Section-level three-way merge for structured markdown files.
|
|
3
|
-
*
|
|
4
|
-
* Parses markdown into sections (split on ## headers + YAML frontmatter).
|
|
5
|
-
* Merges non-conflicting section changes. Flags same-section conflicts.
|
|
6
|
-
*
|
|
7
|
-
* This is what makes us better than git for context files.
|
|
8
|
-
* Git merges by line. We merge by section.
|
|
9
|
-
*/
|
|
10
|
-
|
|
11
|
-
/**
|
|
12
|
-
* Parse a markdown document into sections.
|
|
13
|
-
* Returns: { __frontmatter__: string, __header__: string, sections: [{name, content}] }
|
|
14
|
-
*/
|
|
15
|
-
function parseSections(content) {
|
|
16
|
-
if (!content) return { frontmatter: '', header: '', sections: [] };
|
|
17
|
-
|
|
18
|
-
const lines = content.split('\n');
|
|
19
|
-
let frontmatter = '';
|
|
20
|
-
let header = '';
|
|
21
|
-
const sections = [];
|
|
22
|
-
let current = null;
|
|
23
|
-
let inFrontmatter = false;
|
|
24
|
-
let frontmatterDone = false;
|
|
25
|
-
let headerLines = [];
|
|
26
|
-
|
|
27
|
-
for (let i = 0; i < lines.length; i++) {
|
|
28
|
-
const line = lines[i];
|
|
29
|
-
|
|
30
|
-
// YAML frontmatter
|
|
31
|
-
if (i === 0 && line.trim() === '---') {
|
|
32
|
-
inFrontmatter = true;
|
|
33
|
-
headerLines.push(line);
|
|
34
|
-
continue;
|
|
35
|
-
}
|
|
36
|
-
if (inFrontmatter) {
|
|
37
|
-
headerLines.push(line);
|
|
38
|
-
if (line.trim() === '---') {
|
|
39
|
-
inFrontmatter = false;
|
|
40
|
-
frontmatterDone = true;
|
|
41
|
-
frontmatter = headerLines.join('\n');
|
|
42
|
-
headerLines = [];
|
|
43
|
-
}
|
|
44
|
-
continue;
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
// Section headers
|
|
48
|
-
if (line.startsWith('## ')) {
|
|
49
|
-
// Save previous section or header
|
|
50
|
-
if (current) {
|
|
51
|
-
sections.push(current);
|
|
52
|
-
} else if (headerLines.length > 0) {
|
|
53
|
-
header = headerLines.join('\n');
|
|
54
|
-
}
|
|
55
|
-
current = { name: line.substring(3).trim(), content: line };
|
|
56
|
-
continue;
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
// Content belongs to current section or header
|
|
60
|
-
if (current) {
|
|
61
|
-
current.content += '\n' + line;
|
|
62
|
-
} else {
|
|
63
|
-
headerLines.push(line);
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
// Save last section or header
|
|
68
|
-
if (current) {
|
|
69
|
-
sections.push(current);
|
|
70
|
-
} else if (headerLines.length > 0 && !header) {
|
|
71
|
-
header = headerLines.join('\n');
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
return { frontmatter, header, sections };
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
/**
|
|
78
|
-
* Reconstruct a markdown document from parsed sections.
|
|
79
|
-
*/
|
|
80
|
-
function reconstructDocument(parsed) {
|
|
81
|
-
const parts = [];
|
|
82
|
-
if (parsed.frontmatter) parts.push(parsed.frontmatter);
|
|
83
|
-
if (parsed.header) parts.push(parsed.header);
|
|
84
|
-
for (const section of parsed.sections) {
|
|
85
|
-
parts.push(section.content);
|
|
86
|
-
}
|
|
87
|
-
return parts.join('\n');
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
/**
|
|
91
|
-
* Three-way section merge.
|
|
92
|
-
*
|
|
93
|
-
* @param {string} base - Common ancestor content
|
|
94
|
-
* @param {string} local - Your version
|
|
95
|
-
* @param {string} remote - Their version
|
|
96
|
-
* @returns {{ merged: string|null, conflicts: [{section, local, remote}] }}
|
|
97
|
-
*
|
|
98
|
-
* If merged is non-null, the merge succeeded (conflicts array is empty).
|
|
99
|
-
* If merged is null, there are conflicts that need manual resolution.
|
|
100
|
-
*/
|
|
101
|
-
function sectionMerge(base, local, remote) {
|
|
102
|
-
const baseParsed = parseSections(base);
|
|
103
|
-
const localParsed = parseSections(local);
|
|
104
|
-
const remoteParsed = parseSections(remote);
|
|
105
|
-
|
|
106
|
-
const conflicts = [];
|
|
107
|
-
|
|
108
|
-
// Merge frontmatter (field-by-field if both changed, otherwise take the changed one)
|
|
109
|
-
let mergedFrontmatter = baseParsed.frontmatter;
|
|
110
|
-
if (localParsed.frontmatter !== baseParsed.frontmatter && remoteParsed.frontmatter === baseParsed.frontmatter) {
|
|
111
|
-
mergedFrontmatter = localParsed.frontmatter;
|
|
112
|
-
} else if (remoteParsed.frontmatter !== baseParsed.frontmatter && localParsed.frontmatter === baseParsed.frontmatter) {
|
|
113
|
-
mergedFrontmatter = remoteParsed.frontmatter;
|
|
114
|
-
} else if (localParsed.frontmatter !== remoteParsed.frontmatter && localParsed.frontmatter !== baseParsed.frontmatter) {
|
|
115
|
-
conflicts.push({ section: 'frontmatter', local: localParsed.frontmatter, remote: remoteParsed.frontmatter });
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
// Merge header
|
|
119
|
-
let mergedHeader = baseParsed.header;
|
|
120
|
-
if (localParsed.header !== baseParsed.header && remoteParsed.header === baseParsed.header) {
|
|
121
|
-
mergedHeader = localParsed.header;
|
|
122
|
-
} else if (remoteParsed.header !== baseParsed.header && localParsed.header === baseParsed.header) {
|
|
123
|
-
mergedHeader = remoteParsed.header;
|
|
124
|
-
} else if (localParsed.header !== remoteParsed.header && localParsed.header !== baseParsed.header) {
|
|
125
|
-
conflicts.push({ section: 'header', local: localParsed.header, remote: remoteParsed.header });
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
// Build section maps
|
|
129
|
-
const baseMap = {};
|
|
130
|
-
for (const s of baseParsed.sections) baseMap[s.name] = s.content;
|
|
131
|
-
const localMap = {};
|
|
132
|
-
for (const s of localParsed.sections) localMap[s.name] = s.content;
|
|
133
|
-
const remoteMap = {};
|
|
134
|
-
for (const s of remoteParsed.sections) remoteMap[s.name] = s.content;
|
|
135
|
-
|
|
136
|
-
// Get all section names preserving order (base order, then new sections)
|
|
137
|
-
const allNames = [];
|
|
138
|
-
const seen = new Set();
|
|
139
|
-
for (const s of baseParsed.sections) { allNames.push(s.name); seen.add(s.name); }
|
|
140
|
-
for (const s of localParsed.sections) { if (!seen.has(s.name)) { allNames.push(s.name); seen.add(s.name); } }
|
|
141
|
-
for (const s of remoteParsed.sections) { if (!seen.has(s.name)) { allNames.push(s.name); seen.add(s.name); } }
|
|
142
|
-
|
|
143
|
-
// Merge each section
|
|
144
|
-
const mergedSections = [];
|
|
145
|
-
for (const name of allNames) {
|
|
146
|
-
const b = baseMap[name] || null;
|
|
147
|
-
const l = localMap[name] || null;
|
|
148
|
-
const r = remoteMap[name] || null;
|
|
149
|
-
|
|
150
|
-
if (l === r) {
|
|
151
|
-
// Both same — take either (or null = both deleted)
|
|
152
|
-
if (l !== null) mergedSections.push({ name, content: l });
|
|
153
|
-
continue;
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
if (b === null) {
|
|
157
|
-
// New section — exists in one or both
|
|
158
|
-
if (l && !r) { mergedSections.push({ name, content: l }); continue; }
|
|
159
|
-
if (r && !l) { mergedSections.push({ name, content: r }); continue; }
|
|
160
|
-
// Both added same-named section with different content
|
|
161
|
-
conflicts.push({ section: name, local: l, remote: r });
|
|
162
|
-
mergedSections.push({ name, content: l }); // default to local
|
|
163
|
-
continue;
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
const localChanged = l !== b;
|
|
167
|
-
const remoteChanged = r !== b;
|
|
168
|
-
|
|
169
|
-
if (!localChanged && remoteChanged) {
|
|
170
|
-
if (r !== null) mergedSections.push({ name, content: r });
|
|
171
|
-
// else: remote deleted it, local didn't change → accept deletion
|
|
172
|
-
} else if (localChanged && !remoteChanged) {
|
|
173
|
-
if (l !== null) mergedSections.push({ name, content: l });
|
|
174
|
-
// else: local deleted it, remote didn't change → accept deletion
|
|
175
|
-
} else {
|
|
176
|
-
// Both changed the same section → conflict
|
|
177
|
-
conflicts.push({ section: name, local: l, remote: r });
|
|
178
|
-
if (l !== null) mergedSections.push({ name, content: l }); // default to local
|
|
179
|
-
}
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
if (conflicts.length > 0) {
|
|
183
|
-
return { merged: null, conflicts };
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
// Reconstruct
|
|
187
|
-
const merged = reconstructDocument({
|
|
188
|
-
frontmatter: mergedFrontmatter,
|
|
189
|
-
header: mergedHeader,
|
|
190
|
-
sections: mergedSections,
|
|
191
|
-
});
|
|
192
|
-
|
|
193
|
-
return { merged, conflicts: [] };
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
module.exports = { parseSections, reconstructDocument, sectionMerge };
|