atris 3.16.1 → 3.17.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/README.md +32 -7
- package/atris/skills/atris/SKILL.md +15 -2
- package/atris/skills/atris-feedback/SKILL.md +7 -0
- package/atris/skills/design/SKILL.md +29 -2
- package/atris/skills/engines/SKILL.md +44 -0
- package/atris/skills/flow/SKILL.md +1 -1
- package/atris/skills/wake/SKILL.md +37 -0
- package/atris/skills/youtube/SKILL.md +13 -39
- package/atris/team/validator/MEMBER.md +1 -0
- package/atris/wiki/concepts/agent-activation-contract.md +3 -3
- package/atris/wiki/concepts/workspace-initialization-contract.md +3 -3
- package/atris/wiki/index.md +1 -0
- package/atris.md +43 -19
- package/bin/atris.js +400 -30
- package/commands/agent-spawn.js +480 -0
- package/commands/analytics.js +6 -3
- package/commands/apps.js +11 -0
- package/commands/autopilot.js +42 -18
- package/commands/brain.js +74 -7
- package/commands/brainstorm.js +9 -58
- package/commands/clean.js +1 -4
- package/commands/compile.js +9 -4
- package/commands/console.js +8 -3
- package/commands/deck.js +135 -0
- package/commands/init.js +22 -11
- package/commands/lesson.js +76 -0
- package/commands/member.js +252 -48
- package/commands/mission.js +405 -13
- package/commands/now.js +4 -2
- package/commands/probe.js +105 -27
- package/commands/pulse.js +504 -0
- package/commands/radar.js +1 -0
- package/commands/recap.js +55 -25
- package/commands/run.js +615 -22
- package/commands/slop.js +173 -0
- package/commands/spaceship.js +39 -0
- package/commands/sync.js +0 -2
- package/commands/task.js +429 -37
- package/commands/verify.js +7 -3
- package/lib/activity-stream.js +166 -0
- package/lib/auto-accept-certified.js +23 -1
- package/lib/context-gatherer.js +170 -0
- package/lib/escape-regexp.js +13 -0
- package/lib/file-ops.js +6 -3
- package/lib/journal.js +1 -1
- package/lib/lesson-contradiction.js +113 -0
- package/lib/policy-lessons.js +3 -2
- package/lib/pulse.js +401 -0
- package/lib/runner-command.js +156 -0
- package/lib/slides-deck.js +236 -0
- package/lib/state-detection.js +1 -4
- package/lib/task-db.js +101 -4
- package/lib/task-proof.js +1 -1
- package/lib/todo-fallback.js +2 -1
- package/lib/todo-sections.js +33 -0
- package/package.json +1 -2
- package/utils/api.js +14 -2
- package/atris/atrisDev.md +0 -717
package/commands/now.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
const fs = require('fs');
|
|
2
2
|
const path = require('path');
|
|
3
|
+
const { hasRenderedSections, isOpenSection } = require('../lib/todo-sections');
|
|
3
4
|
|
|
4
5
|
const NOW_PATH = path.join('atris', 'now.md');
|
|
5
6
|
const TASK_EPISODES_PATH = path.join('.atris', 'state', 'task_episodes.jsonl');
|
|
@@ -71,7 +72,8 @@ function countMatches(filePath, pattern) {
|
|
|
71
72
|
function countOpenTodoItems(filePath) {
|
|
72
73
|
if (!fs.existsSync(filePath)) return 0;
|
|
73
74
|
const content = fs.readFileSync(filePath, 'utf8');
|
|
74
|
-
|
|
75
|
+
// Emoji-decorated headings ("## In Progress 🔄") handled by lib/todo-sections.
|
|
76
|
+
const rendered = hasRenderedSections(content);
|
|
75
77
|
let section = null;
|
|
76
78
|
let count = 0;
|
|
77
79
|
|
|
@@ -83,7 +85,7 @@ function countOpenTodoItems(filePath) {
|
|
|
83
85
|
}
|
|
84
86
|
const isTaskBullet = /^-\s+(?:\[[ ]\]\s+)?\*\*.+?\*\*/.test(line);
|
|
85
87
|
if (!isTaskBullet) continue;
|
|
86
|
-
if (!
|
|
88
|
+
if (!rendered || isOpenSection(section)) {
|
|
87
89
|
count += 1;
|
|
88
90
|
}
|
|
89
91
|
}
|
package/commands/probe.js
CHANGED
|
@@ -57,7 +57,10 @@ function normalizeBash(cmd, label) {
|
|
|
57
57
|
return cmd.split(l + '/').join('').split(l).join('.');
|
|
58
58
|
}
|
|
59
59
|
|
|
60
|
-
//
|
|
60
|
+
// No write/edit file ops, and the `..` guard only covers args.path. The
|
|
61
|
+
// `bash` op still executes whatever command the model sends, verbatim, on
|
|
62
|
+
// the remote ai-computer — that is the production relay contract and the
|
|
63
|
+
// table must stay in lockstep with it, so this probe is NOT read-only.
|
|
61
64
|
function fileOpCommand(args, label) {
|
|
62
65
|
const op = String(args.type || '').toLowerCase();
|
|
63
66
|
const raw = normalizePath(String(args.path || '') || '.', label);
|
|
@@ -116,7 +119,9 @@ function atrisCliCommand(args) {
|
|
|
116
119
|
}
|
|
117
120
|
|
|
118
121
|
function atrisCliResult(command, term) {
|
|
119
|
-
|
|
122
|
+
// a terminal response without a numeric exit_code is a broken endpoint,
|
|
123
|
+
// not a success — never let it masquerade as ok with empty stdout
|
|
124
|
+
const code = typeof term.exit_code === 'number' ? term.exit_code : -1;
|
|
120
125
|
const out = {
|
|
121
126
|
schema: 'atris.local_cli_result.v1',
|
|
122
127
|
status: code === 0 ? 'ok' : 'error',
|
|
@@ -124,7 +129,11 @@ function atrisCliResult(command, term) {
|
|
|
124
129
|
stdout: String(term.stdout || '').slice(0, 12000),
|
|
125
130
|
exit_code: code,
|
|
126
131
|
};
|
|
127
|
-
if (code !== 0)
|
|
132
|
+
if (code !== 0) {
|
|
133
|
+
out.error = typeof term.exit_code === 'number'
|
|
134
|
+
? String(term.stderr || term.stdout || 'command failed').slice(0, 2000)
|
|
135
|
+
: 'terminal endpoint returned no exit_code';
|
|
136
|
+
}
|
|
128
137
|
return out;
|
|
129
138
|
}
|
|
130
139
|
|
|
@@ -154,7 +163,10 @@ function postJson(urlString, token, payload, timeoutMs) {
|
|
|
154
163
|
reject(new Error(`HTTP ${res.statusCode}: ${data.slice(0, 200)}`));
|
|
155
164
|
return;
|
|
156
165
|
}
|
|
157
|
-
|
|
166
|
+
if (!data) { resolve({}); return; }
|
|
167
|
+
try { resolve(JSON.parse(data)); } catch (e) {
|
|
168
|
+
reject(new Error(`invalid JSON from ${url.pathname}: ${data.slice(0, 120)}`));
|
|
169
|
+
}
|
|
158
170
|
});
|
|
159
171
|
});
|
|
160
172
|
req.on('timeout', () => { req.destroy(new Error('request timeout')); });
|
|
@@ -188,35 +200,46 @@ function parseArgs(argv) {
|
|
|
188
200
|
return a;
|
|
189
201
|
}
|
|
190
202
|
|
|
191
|
-
|
|
192
|
-
|
|
203
|
+
// Core /atris2/turn client: one streamed turn over the full local tool relay.
|
|
204
|
+
// Shared by `atris probe` (the instrument) and `atris mission run --runner atris2`
|
|
205
|
+
// (the worker). Transport-level outcome only — callers apply their own
|
|
206
|
+
// pass/fail policy on top of the returned fields.
|
|
207
|
+
async function runAtris2Turn(opts = {}) {
|
|
208
|
+
const {
|
|
209
|
+
prompt,
|
|
210
|
+
model = 'atris:fast',
|
|
211
|
+
business = null,
|
|
212
|
+
memberId = null,
|
|
213
|
+
memberSlug = null,
|
|
214
|
+
maxTurns = 8,
|
|
215
|
+
idleMs = 180000,
|
|
216
|
+
connectTimeoutMs = 60000,
|
|
217
|
+
signal = null,
|
|
218
|
+
} = opts;
|
|
219
|
+
const t0 = Date.now();
|
|
220
|
+
const out = { ok: false, text: '', engine: null, tools_run: 0, cli_ops: [], unsupported: [], error: null, duration_ms: 0 };
|
|
193
221
|
const creds = loadCredentials();
|
|
194
222
|
if (!creds || !creds.token) {
|
|
195
|
-
|
|
196
|
-
|
|
223
|
+
out.error = 'not-logged-in';
|
|
224
|
+
out.duration_ms = Date.now() - t0;
|
|
225
|
+
return out;
|
|
197
226
|
}
|
|
198
227
|
const token = creds.token;
|
|
199
228
|
const base = getApiBaseUrl();
|
|
200
|
-
const label = workspaceLabel(
|
|
201
|
-
const where = a.business ? `business ${String(a.business).slice(0, 8)}` : 'personal';
|
|
202
|
-
const member = a.memberSlug || a.memberId;
|
|
203
|
-
const prompt = a.message || (a.calendar
|
|
204
|
-
? "What's on my calendar today? Use your tools."
|
|
205
|
-
: 'Read the first 5 lines of any markdown file in this workspace and quote one real line from it. Use your file tools.');
|
|
229
|
+
const label = workspaceLabel(business);
|
|
206
230
|
|
|
207
231
|
const body = {
|
|
208
|
-
message: prompt, model
|
|
232
|
+
message: prompt, model, max_turns: maxTurns,
|
|
209
233
|
verify_command: 'true', local_executor: true, workspace_path: label,
|
|
210
234
|
};
|
|
211
|
-
if (
|
|
212
|
-
if (
|
|
235
|
+
if (memberId) body.member_id = memberId;
|
|
236
|
+
if (memberSlug) body.member_slug = memberSlug;
|
|
213
237
|
|
|
214
|
-
const t0 = Date.now();
|
|
215
238
|
let toolsRun = 0;
|
|
216
|
-
let cliCalendarOps = 0;
|
|
217
239
|
let resultText = '';
|
|
218
240
|
let err = null;
|
|
219
241
|
let engine = null;
|
|
242
|
+
const cliOps = [];
|
|
220
243
|
const unsupported = [];
|
|
221
244
|
|
|
222
245
|
try {
|
|
@@ -235,7 +258,11 @@ async function probeCommand(argv) {
|
|
|
235
258
|
Accept: 'text/event-stream',
|
|
236
259
|
Authorization: `Bearer ${token}`,
|
|
237
260
|
},
|
|
261
|
+
// connect/first-byte guard; cleared once headers arrive so the
|
|
262
|
+
// idle timer below is the only judge of a flowing stream
|
|
263
|
+
timeout: connectTimeoutMs,
|
|
238
264
|
}, (res) => {
|
|
265
|
+
req.setTimeout(0);
|
|
239
266
|
if (res.statusCode < 200 || res.statusCode >= 300) {
|
|
240
267
|
let data = '';
|
|
241
268
|
res.on('data', (c) => data += c);
|
|
@@ -244,10 +271,9 @@ async function probeCommand(argv) {
|
|
|
244
271
|
}
|
|
245
272
|
let buffer = '';
|
|
246
273
|
let idleTimer = null;
|
|
247
|
-
const IDLE_MS = 180000;
|
|
248
274
|
const resetIdle = () => {
|
|
249
275
|
if (idleTimer) clearTimeout(idleTimer);
|
|
250
|
-
idleTimer = setTimeout(() => { req.destroy(); reject(new Error(`stream stalled: no events for ${
|
|
276
|
+
idleTimer = setTimeout(() => { req.destroy(); reject(new Error(`stream stalled: no events for ${idleMs / 1000}s`)); }, idleMs);
|
|
251
277
|
};
|
|
252
278
|
resetIdle();
|
|
253
279
|
|
|
@@ -273,11 +299,16 @@ async function probeCommand(argv) {
|
|
|
273
299
|
out = { status: 'error', error: 'unsupported op or unsafe path' };
|
|
274
300
|
unsupported.push(`file_op:${op}`);
|
|
275
301
|
} else {
|
|
276
|
-
const term = await runTerminal(base, token, cmd,
|
|
277
|
-
const ok =
|
|
302
|
+
const term = await runTerminal(base, token, cmd, business);
|
|
303
|
+
const ok = term.exit_code === 0;
|
|
278
304
|
out = ok
|
|
279
305
|
? { status: 'ok', stdout: String(term.stdout || '').slice(0, 12000) }
|
|
280
|
-
: {
|
|
306
|
+
: {
|
|
307
|
+
status: 'error',
|
|
308
|
+
error: typeof term.exit_code === 'number'
|
|
309
|
+
? String(term.stderr || 'command failed').slice(0, 2000)
|
|
310
|
+
: 'terminal endpoint returned no exit_code',
|
|
311
|
+
};
|
|
281
312
|
}
|
|
282
313
|
} else if (name === 'local_atris_cli_op') {
|
|
283
314
|
const cmd = atrisCliCommand(args);
|
|
@@ -285,9 +316,9 @@ async function probeCommand(argv) {
|
|
|
285
316
|
out = { status: 'error', error: `unsupported atris cli op: ${op || '?'}` };
|
|
286
317
|
unsupported.push(`cli_op:${op || '?'}`);
|
|
287
318
|
} else {
|
|
288
|
-
const term = await runTerminal(base, token, cmd,
|
|
319
|
+
const term = await runTerminal(base, token, cmd, business);
|
|
289
320
|
out = atrisCliResult(cmd, term);
|
|
290
|
-
|
|
321
|
+
cliOps.push(op || '?');
|
|
291
322
|
}
|
|
292
323
|
} else {
|
|
293
324
|
out = { status: 'error', error: `unsupported relayed tool: ${name || '?'}` };
|
|
@@ -321,7 +352,9 @@ async function probeCommand(argv) {
|
|
|
321
352
|
});
|
|
322
353
|
res.on('error', (e) => { if (idleTimer) clearTimeout(idleTimer); reject(e); });
|
|
323
354
|
});
|
|
355
|
+
req.on('timeout', () => { req.destroy(new Error(`no response headers within ${Math.round(connectTimeoutMs / 1000)}s`)); });
|
|
324
356
|
req.on('error', reject);
|
|
357
|
+
if (signal) signal.addEventListener('abort', () => { req.destroy(new Error('aborted')); }, { once: true });
|
|
325
358
|
req.write(postData);
|
|
326
359
|
req.end();
|
|
327
360
|
});
|
|
@@ -329,6 +362,42 @@ async function probeCommand(argv) {
|
|
|
329
362
|
if (!err) err = `${e.name || 'Error'}: ${e.message || e}`;
|
|
330
363
|
}
|
|
331
364
|
|
|
365
|
+
out.text = resultText;
|
|
366
|
+
out.engine = engine;
|
|
367
|
+
out.tools_run = toolsRun;
|
|
368
|
+
out.cli_ops = cliOps;
|
|
369
|
+
out.unsupported = unsupported;
|
|
370
|
+
out.error = err;
|
|
371
|
+
out.ok = err === null;
|
|
372
|
+
out.duration_ms = Date.now() - t0;
|
|
373
|
+
return out;
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
async function probeCommand(argv) {
|
|
377
|
+
const a = parseArgs(argv || []);
|
|
378
|
+
const creds = loadCredentials();
|
|
379
|
+
if (!creds || !creds.token) {
|
|
380
|
+
console.error('✗ Not logged in. Run: atris login');
|
|
381
|
+
return 1;
|
|
382
|
+
}
|
|
383
|
+
const where = a.business ? `business ${String(a.business).slice(0, 8)}` : 'personal';
|
|
384
|
+
const member = a.memberSlug || a.memberId;
|
|
385
|
+
const prompt = a.message || (a.calendar
|
|
386
|
+
? "What's on my calendar today? Use your tools."
|
|
387
|
+
: 'Read the first 5 lines of any markdown file in this workspace and quote one real line from it. Use your file tools.');
|
|
388
|
+
|
|
389
|
+
const t0 = Date.now();
|
|
390
|
+
const turn = await runAtris2Turn({
|
|
391
|
+
prompt, model: a.model, business: a.business,
|
|
392
|
+
memberId: a.memberId, memberSlug: a.memberSlug, maxTurns: 8,
|
|
393
|
+
});
|
|
394
|
+
const toolsRun = turn.tools_run;
|
|
395
|
+
const cliCalendarOps = turn.cli_ops.filter((op) => String(op).startsWith('calendar')).length;
|
|
396
|
+
const resultText = turn.text;
|
|
397
|
+
const err = turn.error;
|
|
398
|
+
const engine = turn.engine;
|
|
399
|
+
const unsupported = turn.unsupported;
|
|
400
|
+
|
|
332
401
|
// Name the dead-end (first match wins); null = converged.
|
|
333
402
|
let deadEnd = null;
|
|
334
403
|
if (err) {
|
|
@@ -363,4 +432,13 @@ async function probeCommand(argv) {
|
|
|
363
432
|
return ok ? 0 : 1;
|
|
364
433
|
}
|
|
365
434
|
|
|
366
|
-
module.exports = {
|
|
435
|
+
module.exports = {
|
|
436
|
+
probeCommand,
|
|
437
|
+
runAtris2Turn,
|
|
438
|
+
// exported for tests
|
|
439
|
+
normalizePath,
|
|
440
|
+
normalizeBash,
|
|
441
|
+
fileOpCommand,
|
|
442
|
+
atrisCliCommand,
|
|
443
|
+
atrisCliResult,
|
|
444
|
+
};
|