mindvest-atlas 0.95.1 → 0.96.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mindvest-atlas",
3
- "version": "0.95.1",
3
+ "version": "0.96.0",
4
4
  "description": "Atlas CLI \u2014 OAuth login, tool calls, and live alert/flow streaming for the Atlas trading API",
5
5
  "type": "module",
6
6
  "bin": {
@@ -43,9 +43,19 @@ function printCard(card) {
43
43
  println(` ${color.bold(lines[0])}`);
44
44
  for (const l of lines.slice(1)) println(` ${l}`);
45
45
  const project = card.project || card.source?.project;
46
- println(color.dim(` ${card.kind}${project ? ` · ${project}` : ''} · w${card.weight} · used ${card.score} · ${fade(card)} · stored ${stored(card)} · ${card.id.slice(0, 8)}`));
46
+ const by = card.by || card.source?.by;
47
+ println(color.dim(` ${card.kind}${project ? ` · ${project}` : ''} · w${card.weight} · used ${card.score} · ${fade(card)} · stored ${stored(card)}${by ? ` by ${by}` : ''} · ${card.id.slice(0, 8)}`));
47
48
  }
48
49
 
50
+ // WHO IS WRITING (owner, 2026-10-08: "they say claude gpt etc. To say who
51
+ // stored this"). A model running this from a chat passes its own name with
52
+ // `--by claude`; the app keeps it on the memory. A person at a terminal says
53
+ // nothing and is the CLI. A name is a short plain word, never free text.
54
+ const whoSays = (flags) => {
55
+ const said = String(flags?.by ?? '').trim().toLowerCase().replace(/\s+/g, '-').replace(/[^a-z0-9._+-]/g, '').slice(0, 40);
56
+ return said || 'cli';
57
+ };
58
+
49
59
  export async function memoryCommand(args) {
50
60
  const flags = args.flags || {};
51
61
  const at = askAddress(flags.root);
@@ -97,7 +107,7 @@ export async function memoryCommand(args) {
97
107
  project: flags.project === undefined
98
108
  ? (process.env.ATLAS_JOB || undefined)
99
109
  : (typeof flags.project === 'string' ? flags.project : ''),
100
- by: 'cli',
110
+ by: whoSays(flags),
101
111
  });
102
112
  println('Remembered.');
103
113
  printCard(out.memory);
@@ -111,7 +121,7 @@ export async function memoryCommand(args) {
111
121
  const id = String(rest[0] || flags.id || '');
112
122
  const weight = Number(flags.weight ?? rest[1]);
113
123
  if (!id || !weight) { eprintln(color.red('`atlas memory weight <id> <1-10>`')); return 1; }
114
- const out = await post(at, '/memory/update', { id, weight, by: 'cli' });
124
+ const out = await post(at, '/memory/update', { id, weight, by: whoSays(flags) });
115
125
  printCard(out.memory);
116
126
  return 0;
117
127
  }
@@ -119,7 +129,7 @@ export async function memoryCommand(args) {
119
129
  case 'demote': {
120
130
  const id = String(rest[0] || flags.id || '');
121
131
  if (!id) { eprintln(color.red(`\`atlas memory ${sub} <id> [--tier long|permanent|short]\``)); return 1; }
122
- const out = await post(at, '/memory/update', { id, tier: flags.tier || (sub === 'promote' ? 'long' : 'short'), by: 'cli' });
132
+ const out = await post(at, '/memory/update', { id, tier: flags.tier || (sub === 'promote' ? 'long' : 'short'), by: whoSays(flags) });
123
133
  printCard(out.memory);
124
134
  return 0;
125
135
  }
@@ -127,7 +137,7 @@ export async function memoryCommand(args) {
127
137
  case 'unpin': {
128
138
  const id = String(rest[0] || flags.id || '');
129
139
  if (!id) { eprintln(color.red(`\`atlas memory ${sub} <id>\``)); return 1; }
130
- const out = await post(at, '/memory/update', { id, pinned: sub === 'pin', by: 'cli' });
140
+ const out = await post(at, '/memory/update', { id, pinned: sub === 'pin', by: whoSays(flags) });
131
141
  printCard(out.memory);
132
142
  return 0;
133
143
  }
@@ -138,7 +148,7 @@ export async function memoryCommand(args) {
138
148
  // came back, so forgetting by the 8-character id it shows (which forget
139
149
  // did not accept) said Forgotten and removed nothing. A miss is an
140
150
  // error from the app now; this reports only what it was told.
141
- const out = await post(at, '/memory/forget', { id, why: flags.why || 'removed from the CLI', by: 'cli' });
151
+ const out = await post(at, '/memory/forget', { id, why: flags.why || 'removed from the CLI', by: whoSays(flags) });
142
152
  if (!out.forgotten) { eprintln(color.red(`Nothing was forgotten: no memory matched "${id}".`)); return 1; }
143
153
  println(`Forgotten (${out.id || id}). It is in the trash for 30 days: \`atlas memory restore ${String(out.id || id).slice(0, 8)}\`.`);
144
154
  return 0;
@@ -246,7 +256,7 @@ export async function memoryCommand(args) {
246
256
  const out = await post(at, '/memory/list', {});
247
257
  const verdict = out.queue.find((v) => v.id === id || v.id.startsWith(id));
248
258
  if (!verdict) { eprintln(color.red('No proposal by that id. `atlas memory review` lists them.')); return 1; }
249
- await post(at, '/memory/decide', { verdict, accept: sub === 'accept', by: 'cli' });
259
+ await post(at, '/memory/decide', { verdict, accept: sub === 'accept', by: whoSays(flags) });
250
260
  println(sub === 'accept' ? 'Done.' : 'Kept as it was.');
251
261
  return 0;
252
262
  }
@@ -29,6 +29,7 @@ import {
29
29
  SCRIPTS_DIR, projectsForAlert, readMode,
30
30
  streamStudioFrames,
31
31
  localBrokers, localBrokerCall,
32
+ localMemory,
32
33
  studioVoice,
33
34
  appendSaid,
34
35
  } from '../remote-control.js';
@@ -490,6 +491,16 @@ async function handleAsk(ask, root) {
490
491
  .catch((err) => answer(ask.request_id, { ok: false, error: String(err.message || err) }));
491
492
  return undefined;
492
493
  }
494
+ // MEMORY SHARING (owner, 2026-10-08): what Atlas Studio remembers on this
495
+ // computer, read or changed from the web, the phone or a tool. Not awaited:
496
+ // a write checks the new memory against the old ones with a model, which
497
+ // can take a minute, and the stream must keep serving everyone else.
498
+ if (ask.op === 'memory') {
499
+ void localMemory(ask.what, ask.data, root)
500
+ .then((out) => answer(ask.request_id, out && typeof out === 'object' ? out : { ok: false, error: 'No answer.' }))
501
+ .catch((err) => answer(ask.request_id, { ok: false, error: String(err.message || err) }));
502
+ return undefined;
503
+ }
493
504
  if (ask.op === 'voice') {
494
505
  // A voice the person is talking to on their phone, working Atlas Studio
495
506
  // on this computer: one screen tool, carried out by the app itself.
package/src/config.js CHANGED
@@ -2,7 +2,7 @@
2
2
  import path from 'node:path';
3
3
  import os from 'node:os';
4
4
 
5
- export const VERSION = '0.95.1';
5
+ export const VERSION = '0.96.0';
6
6
  export const DEFAULT_BASE_URL = 'https://atlasmcp.finmanagerai.com';
7
7
  export const DEFAULT_SCOPE = 'atlas broker';
8
8
  export const CLIENT_NAME = 'Atlas CLI';
@@ -2018,6 +2018,36 @@ export async function localBrokerCall(connector, tool, args, root, timeoutMs = 1
2018
2018
  return out;
2019
2019
  }
2020
2020
 
2021
+ // What a person's computer answers about its memory when the web, the phone or
2022
+ // an assistant asks (the server's `op: 'memory'`): the endpoints Atlas Studio's
2023
+ // own memory already has, by name. Anything else is refused here, before the
2024
+ // app is asked.
2025
+ export const MEMORY_OPS = ['list', 'recall', 'tension', 'write', 'update', 'forget', 'restore'];
2026
+
2027
+ /**
2028
+ * One memory request, answered by Atlas Studio on THIS computer.
2029
+ *
2030
+ * The store is the app's (`.studio/memory`); this only passes the question to
2031
+ * it and the answer back, the way `atlas memory` does from a terminal. Memory
2032
+ * sharing is the person's own switch on the web (Profile, Memory): the server
2033
+ * asks nothing of a computer while it is off.
2034
+ */
2035
+ export async function localMemory(what, data, root, timeoutMs = 150000) {
2036
+ const op = String(what || '').trim().toLowerCase();
2037
+ if (!MEMORY_OPS.includes(op)) return { ok: false, error: `unknown memory request ${op || '(none)'}` };
2038
+ const at = askAddress(root);
2039
+ if (!at) return { ok: false, error: 'Atlas Studio is not open on this computer. It holds the memory.' };
2040
+ const res = await fetch(at.url.replace('/ask', `/memory/${op}`), {
2041
+ method: 'POST',
2042
+ headers: { 'Content-Type': 'application/json', 'X-Atlas-Token': at.token },
2043
+ body: JSON.stringify(data && typeof data === 'object' && !Array.isArray(data) ? data : {}),
2044
+ signal: AbortSignal.timeout(timeoutMs),
2045
+ });
2046
+ const out = await res.json().catch(() => ({}));
2047
+ if (!res.ok) return { ok: false, error: out.error || `The memory request failed (${res.status}).` };
2048
+ return out && typeof out === 'object' ? out : { ok: false, error: 'No answer.' };
2049
+ }
2050
+
2021
2051
  /**
2022
2052
  * A VOICE THE PERSON IS TALKING TO SOMEWHERE ELSE, WORKING ATLAS STUDIO HERE
2023
2053
  * (owner, 2026-10-06: "when I switch to local, where is the voice ... it