atris 3.82.17 → 3.82.19

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.
@@ -32,6 +32,18 @@ function normalizeGrokModel(raw) {
32
32
  throw new Error(`grok does not know the model "${raw}". use grok 4.7 fast, grok 4.7, or a full grok- id`);
33
33
  }
34
34
 
35
+ // Muse takes friendly names: "muse wonder" is muse-wonder-1.0, "spark 1.3"
36
+ // is muse-spark-1.3. Any full muse- id passes through as typed.
37
+ const MUSE_MODEL_SPELLING = /^(?:muse[\s_-]*)?(wonder|spark)(?:[\s_-]*(\d+(?:\.\d+)?))?$/;
38
+ const MUSE_DEFAULT_VERSIONS = Object.freeze({ wonder: '1.0', spark: '1.3' });
39
+
40
+ function normalizeMuseModel(raw) {
41
+ const match = MUSE_MODEL_SPELLING.exec(raw.toLowerCase());
42
+ if (match) return `muse-${match[1]}-${match[2] || MUSE_DEFAULT_VERSIONS[match[1]]}`;
43
+ if (/^muse-[a-z0-9][a-z0-9._-]*$/i.test(raw)) return raw;
44
+ throw new Error(`muse does not know the model "${raw}". use muse wonder, muse spark, or a full muse- id`);
45
+ }
46
+
35
47
  // The model to run for a claude id: its replacement when it is retired,
36
48
  // else the id as given. Keeps a "[1m]" tail.
37
49
  function currentClaudeModel(value) {
@@ -75,6 +87,7 @@ function normalizeRosterModel(engineId, value) {
75
87
  const raw = String(value || '').trim();
76
88
  if (!raw) return '';
77
89
  if (engineId === 'grok') return normalizeGrokModel(raw);
90
+ if (engineId === 'muse') return normalizeMuseModel(raw);
78
91
  if (!CLAUDE_FAMILY_ENGINES.includes(engineId)) return raw;
79
92
  // "[1m]" asks the claude cli for the long context window; keep it on
80
93
  // whatever name it follows.
@@ -120,6 +120,16 @@ const RUNNER_PROFILE_DEFS = Object.freeze({
120
120
  effortFlag: '--reasoning-effort',
121
121
  efforts: Object.freeze(['low', 'medium', 'high']),
122
122
  }),
123
+ // Meta's Muse Code. Its sandbox sends network through a proxy by default,
124
+ // which blocks atris calls; "enabled" keeps the sandbox and approval judge
125
+ // on and lets the run reach the network. Trusting the workspace loads its
126
+ // AGENTS.md rules. Muse runs only Muse models (verified 2026-11-11).
127
+ muse: Object.freeze({
128
+ bin: 'muse',
129
+ model: '',
130
+ ownModelsOnly: true,
131
+ commandTemplate: '{bin} exec --sandbox-network enabled --trust-workspace {pinnedModelFlag} {prompt}',
132
+ }),
123
133
  agy: Object.freeze({
124
134
  bin: 'agy',
125
135
  // No hard pin: the agy CLI keeps its own session model, so an unpinned
package/lib/runs-on.js ADDED
@@ -0,0 +1,131 @@
1
+ 'use strict';
2
+
3
+ // Where a team member does its work: "runs on" on a team line.
4
+ //
5
+ // - cosmo: claude, on: atris computer
6
+ // - amara: on: grok bot
7
+ //
8
+ // Three kinds of place:
9
+ // local this machine, shown by its own name ("Studio Mac mini")
10
+ // atris computer the computer Atris hosts for the person or business
11
+ // anything else a named bot on another machine ("grok bot", "claude duet")
12
+ //
13
+ // Naming an engine installed here ("on: muse") means that engine, locally.
14
+ // Only local members run here; the rest wait for their own machine.
15
+ //
16
+ // The other machine knows it is that place through ATRIS_RUNS_AS, a comma
17
+ // list of the names it answers to: ATRIS_RUNS_AS="grok bot" on the Grok bot's
18
+ // computer, ATRIS_RUNS_AS="atris computer" on the hosted one. Scheduled jobs
19
+ // rarely see shell settings, so the same list can live in ~/.atris/RUNS_AS
20
+ // (one line). A member seated there runs there and nowhere else.
21
+
22
+ const os = require('os');
23
+ const { execFileSync } = require('child_process');
24
+
25
+ const LOCAL_WORDS = new Set(['', 'local', 'here', 'this mac', 'this computer', 'this machine', 'my mac', 'my laptop', 'mac', 'laptop']);
26
+ const ATRIS_COMPUTER_WORDS = new Set(['atris computer', 'atris cloud', 'cloud', 'computer', 'atris', 'your atris computer', 'business computer', 'personal computer', 'cloud computer']);
27
+ const ATRIS_COMPUTER = 'atris computer';
28
+ const KNOWN_LABELS = Object.freeze({
29
+ 'atris computer': 'Atris computer',
30
+ 'grok bot': 'Grok bot',
31
+ 'claude duet': 'Claude Duet',
32
+ dots: 'Dots',
33
+ hermes: 'Hermes',
34
+ muse: 'Muse',
35
+ });
36
+
37
+ let cachedMachineName = null;
38
+
39
+ // The name a person gave this machine, the one AirDrop shows. Falls back to
40
+ // the host name without its .local tail.
41
+ function machineName() {
42
+ if (cachedMachineName !== null) return cachedMachineName;
43
+ let name = '';
44
+ if (process.env.ATRIS_MACHINE_NAME) name = process.env.ATRIS_MACHINE_NAME;
45
+ if (!name && process.platform === 'darwin') {
46
+ try {
47
+ name = execFileSync('scutil', ['--get', 'ComputerName'], { encoding: 'utf8', stdio: ['ignore', 'pipe', 'ignore'], timeout: 2000 }).trim();
48
+ } catch { name = ''; }
49
+ }
50
+ if (!name) name = String(os.hostname() || '').replace(/\.local$/i, '');
51
+ cachedMachineName = name || 'this computer';
52
+ return cachedMachineName;
53
+ }
54
+
55
+ // The place names this machine answers to, besides its own name.
56
+ function runsAsNames() {
57
+ let list = process.env.ATRIS_RUNS_AS;
58
+ if (list === undefined) {
59
+ try {
60
+ list = require('fs').readFileSync(require('path').join(os.homedir(), '.atris', 'RUNS_AS'), 'utf8').split(/\r?\n/)[0];
61
+ } catch { list = ''; }
62
+ }
63
+ return String(list || '')
64
+ .split(',')
65
+ .map((name) => cleanWords(name))
66
+ .filter(Boolean)
67
+ .map((name) => (ATRIS_COMPUTER_WORDS.has(name) ? ATRIS_COMPUTER : name));
68
+ }
69
+
70
+ function cleanWords(text) {
71
+ return String(text || '')
72
+ .replace(/[‘’]/g, "'")
73
+ .replace(/\s+/g, ' ')
74
+ .trim()
75
+ .toLowerCase();
76
+ }
77
+
78
+ function placeLabel(id) {
79
+ if (KNOWN_LABELS[id]) return KNOWN_LABELS[id];
80
+ return id.charAt(0).toUpperCase() + id.slice(1);
81
+ }
82
+
83
+ // What "on: <words>" means. isEngine(name) answers whether the words name an
84
+ // engine atris knows; it is passed in so this file stays free of the
85
+ // registry. Throws a plain sentence on words that cannot be a place.
86
+ function readRunsOn(words, options = {}) {
87
+ const raw = String(words || '').trim();
88
+ const clean = cleanWords(raw);
89
+ if (/[,:]/.test(clean)) throw new Error(`"${raw}" is not a place; use on: atris computer, on: local, or a bot name like on: grok bot`);
90
+ if (clean.length > 40) throw new Error(`"${raw}" is too long for a place name`);
91
+ if (LOCAL_WORDS.has(clean) || clean === cleanWords(machineName())) {
92
+ return { id: 'local', label: machineName(), here: true };
93
+ }
94
+ const answersTo = runsAsNames();
95
+ if (ATRIS_COMPUTER_WORDS.has(clean)) return { id: ATRIS_COMPUTER, label: placeLabel(ATRIS_COMPUTER), here: answersTo.includes(ATRIS_COMPUTER) };
96
+ const engine = typeof options.isEngine === 'function' ? options.isEngine(clean) : '';
97
+ if (engine) return { id: 'local', label: machineName(), here: true, engine };
98
+ return { id: clean, label: placeLabel(clean), here: answersTo.includes(clean) };
99
+ }
100
+
101
+ // The "on:" part of a team line value, split off so the rest reads exactly
102
+ // as it always did. Returns { rest, on } where on is the words after "on:"
103
+ // or '' when the line names no place.
104
+ function splitRunsOn(value) {
105
+ const parts = String(value || '').split(',').map((part) => part.trim()).filter(Boolean);
106
+ let on = '';
107
+ const rest = [];
108
+ for (const part of parts) {
109
+ const match = /^on\s*:\s*(.*)$/i.exec(part);
110
+ if (match && !on) on = match[1].trim() || 'local';
111
+ else if (match) throw new Error('names two places; keep one on: part');
112
+ else rest.push(part);
113
+ }
114
+ return { rest: rest.join(', '), on };
115
+ }
116
+
117
+ // The "runs on" text a table shows for one member.
118
+ function runsOnText(place, engineId) {
119
+ if (!place || place.here) {
120
+ const where = place && place.label ? place.label : machineName();
121
+ return engineId ? `${engineId} on ${where}` : where;
122
+ }
123
+ return place.label;
124
+ }
125
+
126
+ module.exports = {
127
+ machineName,
128
+ readRunsOn,
129
+ splitRunsOn,
130
+ runsOnText,
131
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "atris",
3
- "version": "3.82.17",
3
+ "version": "3.82.19",
4
4
  "description": "you say what you want in plain words. atris builds it, checks it, and shows you proof. project memory your coding agents share across sessions.",
5
5
  "mcpName": "io.github.atrislabs/atris",
6
6
  "main": "bin/atris.js",