atris 3.30.12 → 3.32.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.
Files changed (64) hide show
  1. package/AGENTS.md +16 -3
  2. package/FOR_AGENTS.md +81 -0
  3. package/README.md +6 -4
  4. package/atris/CLAUDE.md +8 -0
  5. package/atris/atris.md +51 -19
  6. package/atris/skills/README.md +1 -0
  7. package/atris/skills/blocks/SKILL.md +134 -0
  8. package/atris/skills/clawhub/philosophy-of-work/SKILL.md +56 -0
  9. package/atris/skills/youtube/SKILL.md +31 -11
  10. package/atris.md +15 -0
  11. package/ax +189 -7
  12. package/bin/atris.js +273 -225
  13. package/commands/autoland.js +379 -0
  14. package/commands/autopilot-front.js +273 -0
  15. package/commands/autopilot.js +94 -4
  16. package/commands/business.js +1 -1
  17. package/commands/clean.js +72 -9
  18. package/commands/codex-goal.js +72 -22
  19. package/commands/computer.js +48 -3
  20. package/commands/gm.js +1 -1
  21. package/commands/harvest.js +179 -0
  22. package/commands/init.js +22 -1
  23. package/commands/land.js +442 -0
  24. package/commands/loop-front.js +122 -4
  25. package/commands/member.js +551 -19
  26. package/commands/mission.js +3674 -278
  27. package/commands/play.js +1 -1
  28. package/commands/pulse.js +65 -7
  29. package/commands/run-front.js +144 -0
  30. package/commands/run.js +10 -7
  31. package/commands/sign.js +90 -0
  32. package/commands/strings.js +301 -0
  33. package/commands/task.js +782 -108
  34. package/commands/truth.js +171 -0
  35. package/commands/xp.js +32 -8
  36. package/commands/youtube.js +72 -5
  37. package/decks/README.md +6 -12
  38. package/lib/auto-accept-certified.js +10 -0
  39. package/lib/autoland.js +391 -0
  40. package/lib/context-gatherer.js +0 -8
  41. package/lib/mission-artifact.js +504 -0
  42. package/lib/mission-room.js +846 -0
  43. package/lib/next-moves.js +212 -6
  44. package/lib/operator-next.js +7 -0
  45. package/lib/pulse.js +78 -4
  46. package/lib/runner-command.js +51 -20
  47. package/lib/runs-prune.js +242 -0
  48. package/lib/task-db.js +19 -2
  49. package/lib/task-proof.js +1 -1
  50. package/package.json +4 -4
  51. package/decks/atris-seed-pitch-v3.json +0 -118
  52. package/decks/atris-seed-pitch-v4-skeleton.json +0 -106
  53. package/decks/atris-seed-pitch-v5.json +0 -109
  54. package/decks/atris-seed-pitch-v6.json +0 -137
  55. package/decks/atris-seed-pitch-v7.json +0 -133
  56. package/decks/mark-pincus-narrative.json +0 -102
  57. package/decks/mark-pincus-sourcery.json +0 -94
  58. package/decks/yash-applied-compute-detailed.json +0 -150
  59. package/decks/yash-applied-compute-generalist.json +0 -82
  60. package/decks/yash-applied-compute-narrative.json +0 -54
  61. package/lib/ax-chat-input.js +0 -164
  62. package/lib/ax-goal.js +0 -307
  63. package/lib/ax-prefs.js +0 -63
  64. package/lib/ax-shimmer.js +0 -63
@@ -0,0 +1,504 @@
1
+ 'use strict';
2
+
3
+ const fs = require('fs');
4
+ const path = require('path');
5
+
6
+ function stampIso() {
7
+ return new Date().toISOString();
8
+ }
9
+
10
+ function toPosixPath(value) {
11
+ return String(value || '').split(path.sep).join('/');
12
+ }
13
+
14
+ function safeSegment(value) {
15
+ return String(value || 'mission')
16
+ .replace(/[^a-zA-Z0-9._-]+/g, '-')
17
+ .replace(/^-+|-+$/g, '')
18
+ .slice(0, 120) || 'mission';
19
+ }
20
+
21
+ function missionArtifactPaths(mission, root = process.cwd()) {
22
+ const dir = path.join(root, 'atris', 'runs', safeSegment(mission?.id || mission?.slug || 'mission'));
23
+ const indexMd = path.join(dir, 'index.md');
24
+ const indexHtml = path.join(dir, 'index.html');
25
+ const blocksJson = path.join(dir, 'blocks.json');
26
+ const rawJson = path.join(dir, 'raw.json');
27
+ return {
28
+ dir,
29
+ indexMd,
30
+ indexHtml,
31
+ blocksJson,
32
+ rawJson,
33
+ relativeDir: toPosixPath(path.relative(root, dir)),
34
+ relativeIndexMd: toPosixPath(path.relative(root, indexMd)),
35
+ relativeIndexHtml: toPosixPath(path.relative(root, indexHtml)),
36
+ relativeBlocksJson: toPosixPath(path.relative(root, blocksJson)),
37
+ relativeRawJson: toPosixPath(path.relative(root, rawJson)),
38
+ };
39
+ }
40
+
41
+ function escapeHtml(value) {
42
+ return String(value ?? '')
43
+ .replace(/&/g, '&')
44
+ .replace(/</g, '&lt;')
45
+ .replace(/>/g, '&gt;')
46
+ .replace(/"/g, '&quot;')
47
+ .replace(/'/g, '&#39;');
48
+ }
49
+
50
+ function readJsonFile(filePath) {
51
+ if (!filePath) return null;
52
+ try {
53
+ return JSON.parse(fs.readFileSync(filePath, 'utf8'));
54
+ } catch {
55
+ return null;
56
+ }
57
+ }
58
+
59
+ function readProofReceipt(proof, root) {
60
+ const raw = String(proof || '').trim();
61
+ if (!raw) return null;
62
+ return readJsonFile(path.isAbsolute(raw) ? raw : path.join(root, raw));
63
+ }
64
+
65
+ function readMissionEvents(missionId, root) {
66
+ const file = path.join(root, '.atris', 'state', 'mission_events.jsonl');
67
+ if (!fs.existsSync(file)) return [];
68
+ return fs.readFileSync(file, 'utf8')
69
+ .split(/\r?\n/)
70
+ .map((line) => line.trim())
71
+ .filter(Boolean)
72
+ .map((line) => {
73
+ try {
74
+ return JSON.parse(line);
75
+ } catch {
76
+ return null;
77
+ }
78
+ })
79
+ .filter((event) => event && event.mission_id === missionId)
80
+ .sort((a, b) => String(a.at || '').localeCompare(String(b.at || '')));
81
+ }
82
+
83
+ function firstEventTime(events, type, fallback = '') {
84
+ return events.find((event) => event.type === type)?.at || fallback || null;
85
+ }
86
+
87
+ function oneLine(value, fallback = '') {
88
+ const text = String(value || '').replace(/\s+/g, ' ').trim();
89
+ if (!text) return fallback;
90
+ return text.length > 220 ? `${text.slice(0, 217).replace(/\s+\S*$/, '')}...` : text;
91
+ }
92
+
93
+ function firstUsefulReceiptLine(text) {
94
+ return String(text || '')
95
+ .split(/\r?\n/)
96
+ .map((line) => line.trim().replace(/^[-*]\s*/, ''))
97
+ .filter(Boolean)
98
+ .filter((line) => !/^#+\s*(receipt|summary|result|final answer)\b/i.test(line))
99
+ .find((line) => line.length > 8) || '';
100
+ }
101
+
102
+ function receiptWorkSummary(receipt) {
103
+ const result = receipt?.result || {};
104
+ const tick = result.tick || {};
105
+ return oneLine(
106
+ tick.summary
107
+ || tick.atris2?.summary
108
+ || tick.claude?.summary
109
+ || firstUsefulReceiptLine(tick.atris2?.receipt_text)
110
+ || firstUsefulReceiptLine(tick.claude?.receipt_text)
111
+ || result.summary
112
+ || '',
113
+ 'a proof receipt for this mission',
114
+ );
115
+ }
116
+
117
+ function verifierSummary(mission, receipt) {
118
+ const verifier = mission?.verifier_result || receipt?.result?.verifier_result || {};
119
+ if (verifier.passed === true) {
120
+ return {
121
+ title: 'Verifier passed',
122
+ body: `We checked it with ${oneLine(verifier.command || mission?.verifier || 'the configured verifier')}.`,
123
+ meaning: 'The proof is not just a claim; it passed the recorded check.',
124
+ status: 'done',
125
+ };
126
+ }
127
+ if (mission?.completion_gate?.source === 'no_verifier') {
128
+ return {
129
+ title: 'Proof file saved',
130
+ body: 'We saved the proof file for this completed mission.',
131
+ meaning: 'This is ready for human inspection; repeat runs should save the same check command every time.',
132
+ status: 'watch',
133
+ };
134
+ }
135
+ return null;
136
+ }
137
+
138
+ function timelineItem(order, title, body, meaning, at = null, status = 'done') {
139
+ return { order, title, body: oneLine(body), meaning: oneLine(meaning), at, status };
140
+ }
141
+
142
+ function buildTimeline(mission, { proof = '', proofReceipt = null, events = [], continuationGoal = null } = {}) {
143
+ const items = [];
144
+ items.push(timelineItem(
145
+ 1,
146
+ 'Mission started',
147
+ `We named the outcome: ${mission.objective}.`,
148
+ 'This turned the ask into one durable mission Atris can keep running.',
149
+ firstEventTime(events, 'mission_started', mission.created_at),
150
+ ));
151
+
152
+ if (mission.native_goal_ack?.objective) {
153
+ items.push(timelineItem(
154
+ items.length + 1,
155
+ 'Goal set',
156
+ `We set the visible goal: ${mission.native_goal_ack.objective}.`,
157
+ 'The work in Codex now matches the mission the operator sees.',
158
+ mission.native_goal_ack.acknowledged_at || firstEventTime(events, 'native_goal_acknowledged'),
159
+ ));
160
+ }
161
+
162
+ if (mission.xp_task?.ref) {
163
+ items.push(timelineItem(
164
+ items.length + 1,
165
+ 'Task claimed',
166
+ `We attached ${mission.xp_task.ref} to ${mission.owner}.`,
167
+ 'The work has a durable owner and a human review gate.',
168
+ firstEventTime(events, 'mission_task_spine_attached'),
169
+ ));
170
+ }
171
+
172
+ if (proof || proofReceipt) {
173
+ items.push(timelineItem(
174
+ items.length + 1,
175
+ 'Goal 1 done',
176
+ `We produced ${receiptWorkSummary(proofReceipt)}.`,
177
+ proof ? `The inspectable proof is ${proof}.` : 'The proof is stored in mission state.',
178
+ proofReceipt?.at || mission.last_tick_at || null,
179
+ ));
180
+ }
181
+
182
+ const verifier = verifierSummary(mission, proofReceipt);
183
+ if (verifier) {
184
+ items.push(timelineItem(
185
+ items.length + 1,
186
+ verifier.title,
187
+ verifier.body,
188
+ verifier.meaning,
189
+ mission.verifier_result?.finished_at || proofReceipt?.at || null,
190
+ verifier.status,
191
+ ));
192
+ }
193
+
194
+ if (continuationGoal?.mission?.objective) {
195
+ items.push(timelineItem(
196
+ items.length + 1,
197
+ 'Next goal queued',
198
+ `Atris queued the next mission: ${continuationGoal.mission.objective}.`,
199
+ 'The mission loop can keep compounding without hiding the completed proof.',
200
+ continuationGoal.mission.created_at || null,
201
+ 'next',
202
+ ));
203
+ }
204
+
205
+ items.push(timelineItem(
206
+ items.length + 1,
207
+ 'Mission accomplished',
208
+ `${mission.objective} is complete.`,
209
+ 'The operator can inspect the timeline, open the proof, then accept or revise.',
210
+ mission.completed_at || firstEventTime(events, 'mission_completed'),
211
+ ));
212
+
213
+ return items;
214
+ }
215
+
216
+ function buildMissionArtifactBlocks(mission, options = {}) {
217
+ const root = options.root || process.cwd();
218
+ const proof = options.proof || mission.proof || mission.receipt_path || '';
219
+ const proofReceipt = options.proofReceipt || readProofReceipt(proof, root);
220
+ const events = options.events || readMissionEvents(mission.id, root);
221
+ const paths = missionArtifactPaths(mission, root);
222
+ const timeline = buildTimeline(mission, { proof, proofReceipt, events, continuationGoal: options.continuationGoal });
223
+ const nextAction = mission.xp_task?.ref
224
+ ? 'Review the timeline and proof, then human-accept the task if it matches what you wanted.'
225
+ : 'Review the timeline and proof, then pick the next customer-facing move.';
226
+
227
+ return {
228
+ schema: 'atris.mission_artifact_blocks.v1',
229
+ mission_id: mission.id,
230
+ objective: mission.objective,
231
+ owner: mission.owner || null,
232
+ generated_at: stampIso(),
233
+ artifact: {
234
+ dir: paths.relativeDir,
235
+ index_md: paths.relativeIndexMd,
236
+ index_html: paths.relativeIndexHtml,
237
+ blocks_json: paths.relativeBlocksJson,
238
+ raw_json: paths.relativeRawJson,
239
+ },
240
+ landing: {
241
+ changed: options.completion?.landing?.happened || `${mission.objective} is complete.`,
242
+ proof: proof || null,
243
+ artifact: paths.relativeIndexHtml,
244
+ next: nextAction,
245
+ },
246
+ blocks: [
247
+ {
248
+ type: 'landing',
249
+ title: 'Mission landing',
250
+ changed: options.completion?.landing?.happened || `${mission.objective} is complete.`,
251
+ checked: options.completion?.landing?.checked || '',
252
+ proof: proof || '',
253
+ artifact: paths.relativeIndexHtml,
254
+ },
255
+ {
256
+ type: 'timeline',
257
+ title: 'Timeline',
258
+ items: timeline,
259
+ },
260
+ {
261
+ type: 'proof',
262
+ title: 'Proof',
263
+ links: [
264
+ { label: 'Timeline page', path: paths.relativeIndexHtml },
265
+ { label: 'Markdown', path: paths.relativeIndexMd },
266
+ { label: 'Blocks', path: paths.relativeBlocksJson },
267
+ { label: 'Raw', path: paths.relativeRawJson },
268
+ ...(proof ? [{ label: 'Machine receipt', path: proof }] : []),
269
+ ],
270
+ },
271
+ {
272
+ type: 'next_action',
273
+ title: 'Next',
274
+ body: nextAction,
275
+ },
276
+ ],
277
+ };
278
+ }
279
+
280
+ function renderMarkdown(blocks) {
281
+ const timeline = blocks.blocks.find((block) => block.type === 'timeline')?.items || [];
282
+ const proof = blocks.blocks.find((block) => block.type === 'proof')?.links || [];
283
+ const next = blocks.blocks.find((block) => block.type === 'next_action')?.body || blocks.landing.next;
284
+ const lines = [
285
+ '# Mission timeline',
286
+ '',
287
+ blocks.objective,
288
+ '',
289
+ 'This is the human view. Raw JSON stays available, but the story comes first.',
290
+ '',
291
+ '## Timeline',
292
+ '',
293
+ ];
294
+ for (const item of timeline) {
295
+ lines.push(`${item.order}. **${item.title}**`);
296
+ lines.push(` ${item.body}`);
297
+ lines.push(` What it meant: ${item.meaning}`);
298
+ if (item.at) lines.push(` Time: ${item.at}`);
299
+ lines.push('');
300
+ }
301
+ lines.push('## Proof', '');
302
+ for (const link of proof) lines.push(`- ${link.label}: \`${link.path}\``);
303
+ lines.push('', '## Next', '', next, '');
304
+ return lines.join('\n');
305
+ }
306
+
307
+ function renderHtml(blocks) {
308
+ const timeline = blocks.blocks.find((block) => block.type === 'timeline')?.items || [];
309
+ const proof = blocks.blocks.find((block) => block.type === 'proof')?.links || [];
310
+ const next = blocks.blocks.find((block) => block.type === 'next_action')?.body || blocks.landing.next;
311
+ const rows = timeline.map((item) => `
312
+ <li>
313
+ <div class="step-number">${escapeHtml(item.order)}</div>
314
+ <div class="step-body">
315
+ <p class="step-title">${escapeHtml(item.title)}</p>
316
+ <p>${escapeHtml(item.body)}</p>
317
+ <p class="meaning">What it meant: ${escapeHtml(item.meaning)}</p>
318
+ ${item.at ? `<p class="time">${escapeHtml(item.at)}</p>` : ''}
319
+ </div>
320
+ </li>`).join('');
321
+ const proofRows = proof.map((link) => `<li><span>${escapeHtml(link.label)}</span><code>${escapeHtml(link.path)}</code></li>`).join('');
322
+ return `<!doctype html>
323
+ <html lang="en">
324
+ <head>
325
+ <meta charset="utf-8">
326
+ <meta name="viewport" content="width=device-width, initial-scale=1">
327
+ <title>Mission timeline</title>
328
+ <style>
329
+ :root {
330
+ --bone: oklch(96% 0.012 75);
331
+ --bone-2: oklch(93% 0.014 72);
332
+ --ink: oklch(22% 0.015 55);
333
+ --ink-soft: oklch(35% 0.012 55);
334
+ --ink-faint: oklch(55% 0.010 60);
335
+ --clay: oklch(58% 0.13 45);
336
+ --ochre: oklch(72% 0.09 75);
337
+ --line: oklch(88% 0.010 70);
338
+ }
339
+ * { box-sizing: border-box; }
340
+ body {
341
+ margin: 0;
342
+ background: radial-gradient(circle at 75% 0%, oklch(91% 0.035 72), transparent 32rem), var(--bone);
343
+ color: var(--ink);
344
+ font-family: "Hanken Grotesk", Avenir Next, sans-serif;
345
+ line-height: 1.6;
346
+ }
347
+ main {
348
+ width: min(1120px, calc(100% - clamp(2rem, 10vw, 10rem)));
349
+ margin: 0 auto;
350
+ padding: clamp(3rem, 7vw, 7rem) 0;
351
+ }
352
+ h1 {
353
+ max-width: 12ch;
354
+ margin: 0;
355
+ font-family: Spectral, Georgia, serif;
356
+ font-size: clamp(3rem, 8vw, 7rem);
357
+ font-weight: 700;
358
+ line-height: 0.95;
359
+ letter-spacing: 0;
360
+ }
361
+ .objective {
362
+ max-width: 52ch;
363
+ margin: 1.25rem 0 0;
364
+ color: var(--ink-soft);
365
+ font-size: clamp(1.15rem, 1rem + 0.5vw, 1.45rem);
366
+ }
367
+ .timeline {
368
+ list-style: none;
369
+ margin: clamp(3rem, 7vw, 6rem) 0 0;
370
+ padding: 0;
371
+ border-top: 1px solid var(--line);
372
+ }
373
+ .timeline li {
374
+ display: grid;
375
+ grid-template-columns: 4rem minmax(0, 1fr);
376
+ gap: clamp(1rem, 3vw, 3rem);
377
+ padding: clamp(1.25rem, 3vw, 2.5rem) 0;
378
+ border-bottom: 1px solid var(--line);
379
+ }
380
+ .step-number {
381
+ width: 2.4rem;
382
+ height: 2.4rem;
383
+ display: grid;
384
+ place-items: center;
385
+ border: 1px solid color-mix(in oklch, var(--clay), var(--ink) 18%);
386
+ border-radius: 999px;
387
+ color: var(--clay);
388
+ font-weight: 700;
389
+ font-variant-numeric: tabular-nums;
390
+ }
391
+ .step-title {
392
+ margin: 0 0 0.3rem;
393
+ font-family: Spectral, Georgia, serif;
394
+ font-size: clamp(1.45rem, 1.1rem + 1vw, 2.2rem);
395
+ font-weight: 700;
396
+ line-height: 1.15;
397
+ }
398
+ p { margin: 0.35rem 0 0; }
399
+ .meaning { color: var(--ink-soft); }
400
+ .time {
401
+ color: var(--ink-faint);
402
+ font-family: "JetBrains Mono", monospace;
403
+ font-size: 0.85rem;
404
+ }
405
+ .proof {
406
+ margin-top: clamp(3rem, 7vw, 6rem);
407
+ padding-top: 1.5rem;
408
+ border-top: 2px solid var(--ink);
409
+ }
410
+ h2 {
411
+ margin: 0 0 1rem;
412
+ font-family: Spectral, Georgia, serif;
413
+ font-size: clamp(1.8rem, 1.2rem + 2vw, 3rem);
414
+ line-height: 1.1;
415
+ }
416
+ .proof ul { list-style: none; margin: 0; padding: 0; }
417
+ .proof li {
418
+ display: flex;
419
+ align-items: baseline;
420
+ justify-content: space-between;
421
+ gap: 1rem;
422
+ padding: 0.7rem 0;
423
+ border-bottom: 1px solid var(--line);
424
+ }
425
+ code {
426
+ color: var(--clay);
427
+ font-family: "JetBrains Mono", monospace;
428
+ font-size: 0.9rem;
429
+ word-break: break-word;
430
+ }
431
+ .next {
432
+ max-width: 52ch;
433
+ margin-top: clamp(2rem, 5vw, 4rem);
434
+ color: var(--ink-soft);
435
+ font-size: 1.1rem;
436
+ }
437
+ @media (max-width: 680px) {
438
+ main { width: min(100% - 2rem, 1120px); padding: 2.5rem 0; }
439
+ .timeline li { grid-template-columns: 1fr; gap: 0.75rem; }
440
+ .proof li { display: block; }
441
+ .proof code { display: block; margin-top: 0.25rem; }
442
+ }
443
+ </style>
444
+ </head>
445
+ <body>
446
+ <main>
447
+ <h1>Mission timeline</h1>
448
+ <p class="objective">${escapeHtml(blocks.objective)}</p>
449
+ <ol class="timeline">${rows}
450
+ </ol>
451
+ <section class="proof">
452
+ <h2>Proof</h2>
453
+ <ul>${proofRows}</ul>
454
+ </section>
455
+ <p class="next">${escapeHtml(next)}</p>
456
+ </main>
457
+ </body>
458
+ </html>
459
+ `;
460
+ }
461
+
462
+ function writeMissionArtifact(mission, options = {}) {
463
+ const root = options.root || process.cwd();
464
+ const paths = missionArtifactPaths(mission, root);
465
+ const proof = options.proof || mission.proof || mission.receipt_path || '';
466
+ const proofReceipt = options.proofReceipt || readProofReceipt(proof, root);
467
+ const events = options.events || readMissionEvents(mission.id, root);
468
+ const blocks = buildMissionArtifactBlocks(mission, {
469
+ ...options,
470
+ root,
471
+ proof,
472
+ proofReceipt,
473
+ events,
474
+ });
475
+ const raw = {
476
+ schema: 'atris.mission_artifact_raw.v1',
477
+ generated_at: blocks.generated_at,
478
+ mission,
479
+ completion: options.completion || null,
480
+ proof,
481
+ proof_receipt: proofReceipt,
482
+ events,
483
+ continuation_goal: options.continuationGoal || null,
484
+ };
485
+ fs.mkdirSync(paths.dir, { recursive: true });
486
+ fs.writeFileSync(paths.indexMd, renderMarkdown(blocks), 'utf8');
487
+ fs.writeFileSync(paths.indexHtml, renderHtml(blocks), 'utf8');
488
+ fs.writeFileSync(paths.blocksJson, JSON.stringify(blocks, null, 2) + '\n', 'utf8');
489
+ fs.writeFileSync(paths.rawJson, JSON.stringify(raw, null, 2) + '\n', 'utf8');
490
+ return {
491
+ dir: paths.relativeDir,
492
+ index_md: paths.relativeIndexMd,
493
+ index_html: paths.relativeIndexHtml,
494
+ blocks_json: paths.relativeBlocksJson,
495
+ raw_json: paths.relativeRawJson,
496
+ timeline_count: blocks.blocks.find((block) => block.type === 'timeline')?.items?.length || 0,
497
+ };
498
+ }
499
+
500
+ module.exports = {
501
+ buildMissionArtifactBlocks,
502
+ missionArtifactPaths,
503
+ writeMissionArtifact,
504
+ };