@100xprompt/brain 0.1.5 → 0.1.6

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.
@@ -168,7 +168,7 @@ async function reconnect(ctx: Ctx, tool: string, change: { model?: string; conte
168
168
  if (cs.state === "not-connected") return { ok: true }
169
169
  if (!cs.settings) return { ok: false, error: olderBrain(tool) }
170
170
  if (!hasKey(ctx.home)) return { ok: false, error: "not signed in to the gateway (brain gateway login)" }
171
- const r = await connect(ctx.home, tool, { settings: connectSettings(ctx, tool, change), probe: false, env: ctx.env })
171
+ const r = await connect(ctx.home, tool, { settings: connectSettings(ctx, tool, change), probe: false, env: ctx.env, keepTheirs: { for: change.model !== undefined ? "model" : "contextEdit" } })
172
172
  return r.ok ? { ok: true, notes: notesOf(r.lines, ctx.home, /^warning: /) } : { ok: false, error: cleanText(r.lines.join("; "), ctx.home) }
173
173
  }
174
174
 
@@ -397,7 +397,25 @@ const reports: Family = {
397
397
  },
398
398
  }
399
399
 
400
- export const FAMILIES: Family[] = [gateway, model, contextEdit, mcp, hooks, leanAuto, leanThreshold, leanMinChars, memoryEnabled, reports]
400
+ // Only this computer decides whether the website may move its sessions, and whether its session list leaves it.
401
+ const movesFromDashboard = internal("moves.fromDashboard", {
402
+ label: "Moves from the dashboard",
403
+ help: "A session move asked from your dashboard: ask (this computer asks you first), allow (it runs), or off (it is refused).",
404
+ type: { kind: "choice", choices: ["ask", "allow", "off"] },
405
+ levels: ["computer"],
406
+ remote: "no",
407
+ team: "none",
408
+ })
409
+ const reportsSessions = internal("reports.sessions", {
410
+ label: "Session list to your account",
411
+ help: "This computer sends its session list (titles and project folder names; never chat text) so the dashboard can list and move sessions.",
412
+ type: { kind: "bool" },
413
+ levels: ["computer"],
414
+ remote: "no",
415
+ team: "none",
416
+ })
417
+
418
+ export const FAMILIES: Family[] = [gateway, model, contextEdit, mcp, hooks, leanAuto, leanThreshold, leanMinChars, memoryEnabled, reports, reportsSessions, movesFromDashboard]
401
419
 
402
420
  // ---- ids and values -----------------------------------------------------------------------------------------------------
403
421
 
@@ -399,6 +399,11 @@ export async function statusCommand(home: string, io: Io = { out: (l) => console
399
399
  if (refused.length) io.out(` refused by your account (this computer went back to its value): ${refused.map((r) => `${r.id} = ${String(r.value)} (${r.message || r.code})`).join("; ")}`)
400
400
  const { serviceStatusLines } = await import("../service.ts")
401
401
  if (acct.paired) serviceStatusLines(home).forEach((l) => io.out(l))
402
+ if (acct.paired) {
403
+ const { movesState } = await import("../moves.ts")
404
+ const mv = movesState(home)
405
+ if (mv.waiting) io.out(`moves from the dashboard: ${mv.waiting} wait${mv.waiting === 1 ? "s" : ""} for you here (brain moves)`)
406
+ }
402
407
  const recorded = readRecordedEnv(home)
403
408
  // As the code that obeys them reads them: BRAIN_NO_REPORT anything but empty, 0 or false; BRAIN_NO_MEMORY exactly 1.
404
409
  const envOff = [...(off && /BRAIN_NO_REPORT/.test(off) ? ["BRAIN_NO_REPORT"] : []), ...(env.BRAIN_NO_MEMORY === "1" ? ["BRAIN_NO_MEMORY"] : [])]
@@ -439,6 +444,9 @@ export async function syncCommand(home: string, io: Io = { out: (l) => console.l
439
444
  else {
440
445
  noticeOnce(home, io.out)
441
446
  const { buildReport } = await import("../dashboard/report.ts")
447
+ const { sendSessions } = await import("../dashboard/sessions.ts")
448
+ const listSent = await sendSessions(home, { env, force: true, ...(deps.fetchImpl ? { fetchImpl: deps.fetchImpl } : {}) })
449
+ if (listSent.sent) io.out(`sessions: sent this computer's list (${listSent.total}) for the dashboard`)
442
450
  const sent = await sendReport(home, await buildReport(home, { env }), { timeoutMs: 5_000, ...(deps.fetchImpl ? { fetchImpl: deps.fetchImpl } : {}) })
443
451
  io.out(sent.ok ? `report: sent to ${gatewayUrl(home)}` : `report: not sent (${sent.why})`)
444
452
  }
@@ -12,15 +12,22 @@ export const LIMITS = {
12
12
  "lean.minChars": { min: 300, max: 100_000 },
13
13
  } as const
14
14
 
15
+ /** Whether a session move asked from the dashboard runs here: ask the person first, run it, or refuse it. */
16
+ export const MOVES_CHOICES = ["ask", "allow", "off"] as const
17
+ export type MovesSetting = (typeof MOVES_CHOICES)[number]
18
+
15
19
  export const DEFAULTS: Record<string, Value> = {
16
20
  "lean.auto": LEAN_DEFAULTS.auto,
17
21
  "lean.threshold": LEAN_DEFAULTS.threshold,
18
22
  "lean.minChars": LEAN_DEFAULTS.minChars,
19
23
  "memory.enabled": true,
24
+ "moves.fromDashboard": "ask",
25
+ "reports.sessions": true,
20
26
  }
21
27
 
22
28
  /** Whether a value is a valid one for this setting (a hand-edited or newer file may hold anything). */
23
29
  export function validValue(id: string, v: Value): boolean {
30
+ if (id === "moves.fromDashboard") return typeof v === "string" && (MOVES_CHOICES as readonly string[]).includes(v)
24
31
  if (typeof DEFAULTS[id] === "boolean") return typeof v === "boolean"
25
32
  const lim = LIMITS[id as keyof typeof LIMITS]
26
33
  return typeof v === "number" && Number.isInteger(v) && (!lim || (v >= lim.min && v <= lim.max))
@@ -55,6 +62,27 @@ export function memoryOn(home: string, d?: SettingsDoc): boolean {
55
62
  return r.problem === "damaged" ? false : (valueOf(r.doc, "memory.enabled") as boolean)
56
63
  }
57
64
 
65
+ /** A setting only this computer can have: its own level counts, nothing set from elsewhere or by a team. */
66
+ const localValue = (d: SettingsDoc, id: string): Value => {
67
+ const e = d.levels.computer[id]
68
+ return e && validValue(id, e.value) ? e.value : DEFAULTS[id]!
69
+ }
70
+
71
+ /** Whether moves asked from the dashboard run here (`brain settings set moves.fromDashboard ask|allow|off`; ask by default). */
72
+ export function movesSetting(home: string, d?: SettingsDoc): MovesSetting {
73
+ if (d) return localValue(d, "moves.fromDashboard") as MovesSetting
74
+ const r = readSettingsFile(home)
75
+ // A file that cannot be read gives no permission: the person is asked.
76
+ return r.problem === "damaged" ? "ask" : (localValue(r.doc, "moves.fromDashboard") as MovesSetting)
77
+ }
78
+
79
+ /** Whether this computer sends its session list (titles, project folder names) to the account; on by default. */
80
+ export function sessionsListOn(home: string, d?: SettingsDoc): boolean {
81
+ if (d) return localValue(d, "reports.sessions") as boolean
82
+ const r = readSettingsFile(home)
83
+ return r.problem === "damaged" ? false : (localValue(r.doc, "reports.sessions") as boolean)
84
+ }
85
+
58
86
  /** What brain says where memory would have been used. */
59
87
  export const MEMORY_OFF = "memory is turned off on this computer (`brain settings set memory.enabled on` turns it on)"
60
88