@geravant/sinain 1.18.2 → 1.19.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/cli.js CHANGED
@@ -81,6 +81,13 @@ switch (cmd) {
81
81
  await import("./install.js");
82
82
  break;
83
83
 
84
+ case "mcp": {
85
+ const sub = process.argv[3]; // install | list | remove
86
+ const { runMcpCli } = await import("./mcp-register.js");
87
+ await runMcpCli(sub, process.argv.slice(4));
88
+ break;
89
+ }
90
+
84
91
  case "export-knowledge":
85
92
  await exportKnowledge();
86
93
  break;
@@ -396,6 +403,9 @@ Usage:
396
403
  sinain export-knowledge Export knowledge for transfer to another machine
397
404
  sinain import-knowledge <file> Import knowledge from export file
398
405
  sinain install Install OpenClaw plugin (server-side)
406
+ sinain mcp install Register sinain MCP for your agents (Claude, Cursor, Codex, Goose, Junie)
407
+ sinain mcp list Show MCP registration status across agents
408
+ sinain mcp remove <agent> Unregister sinain MCP from one agent
399
409
 
400
410
  Start options:
401
411
  --no-sense Skip screen capture (sense_client)
package/onboard.js CHANGED
@@ -12,6 +12,7 @@ import {
12
12
  stepApiKey, stepTranscription, stepGateway, stepPrivacy, stepModel,
13
13
  HOME, SINAIN_DIR, ENV_PATH, PKG_DIR, IS_WINDOWS, IS_MAC,
14
14
  } from "./config-shared.js";
15
+ import { stepMcpInstall, detectMcpAgents } from "./mcp-register.js";
15
16
 
16
17
  // ── Header ──────────────────────────────────────────────────────────────────
17
18
 
@@ -138,7 +139,7 @@ export async function runOnboard(args = {}) {
138
139
  initialValue: "quickstart",
139
140
  }));
140
141
 
141
- const totalSteps = flow === "quickstart" ? 2 : 5;
142
+ const totalSteps = flow === "quickstart" ? 2 : 6;
142
143
 
143
144
  // ── Collect vars ────────────────────────────────────────────────────────
144
145
 
@@ -180,7 +181,11 @@ export async function runOnboard(args = {}) {
180
181
 
181
182
  agentsPatch = {
182
183
  default: base.SINAIN_AGENT || "claude",
183
- escalationMode: "off",
184
+ // No `escalationMode` written — lane (default agent) is the single
185
+ // source of truth for whether escalation runs. If the user picks a
186
+ // local agent (claude), the runtime's default mode ("rich" from
187
+ // config.ts) takes effect and registerBareAgent ensures lane and
188
+ // mode stay reconciled at boot.
184
189
  };
185
190
 
186
191
  if (enableGateway) {
@@ -207,9 +212,28 @@ export async function runOnboard(args = {}) {
207
212
  ].join("\n"),
208
213
  "QuickStart defaults",
209
214
  );
215
+
216
+ // QuickStart MCP install: a single confirm gated on at least one
217
+ // detected agent. Avoids interrupting users who don't have any
218
+ // MCP-aware agent installed. Re-runnable later via `sinain mcp install`.
219
+ try {
220
+ const detected = (await detectMcpAgents()).filter((a) => a.present);
221
+ if (detected.length > 0) {
222
+ const labels = detected.map((a) => a.label).join(", ");
223
+ const wantMcp = guard(await p.confirm({
224
+ message: `Detected ${labels} — register sinain MCP for them?`,
225
+ initialValue: true,
226
+ }));
227
+ if (wantMcp) {
228
+ await stepMcpInstall(base, "Connect MCP agents");
229
+ }
230
+ }
231
+ } catch (err) {
232
+ p.log.info(`Skipping MCP setup: ${err.message}`);
233
+ }
210
234
  } else {
211
- // Advanced flow: steps 2-5
212
- const transcription = await stepTranscription(base, "[2/5] Audio transcription");
235
+ // Advanced flow: steps 2-6 (MCP install is step 6)
236
+ const transcription = await stepTranscription(base, "[2/6] Audio transcription");
213
237
  vars.TRANSCRIPTION_BACKEND = transcription;
214
238
  p.log.success(`Using ${transcription === "openrouter" ? "cloud" : "local"} transcription.`);
215
239
 
@@ -245,7 +269,7 @@ export async function runOnboard(args = {}) {
245
269
 
246
270
  // stepGateway returns { envVars, agentsPatch }: tokens go to .env,
247
271
  // URLs + session + escalation mode go to agents.json's openclaw profile.
248
- const gatewayResult = await stepGateway(base, "[3/5] OpenClaw gateway");
272
+ const gatewayResult = await stepGateway(base, "[3/6] OpenClaw gateway");
249
273
  Object.assign(vars, gatewayResult.envVars);
250
274
  Object.assign(agentsPatch, gatewayResult.agentsPatch);
251
275
  if (gatewayResult.agentsPatch.escalationMode === "off") {
@@ -254,17 +278,23 @@ export async function runOnboard(args = {}) {
254
278
  p.log.success("Gateway configured.");
255
279
  }
256
280
 
257
- const privacy = await stepPrivacy(base, "[4/5] Privacy mode");
281
+ const privacy = await stepPrivacy(base, "[4/6] Privacy mode");
258
282
  vars.PRIVACY_MODE = privacy;
259
283
  p.log.success(`Privacy: ${privacy}.`);
260
284
 
261
- const model = await stepModel(base, "[5/5] AI model for HUD analysis");
285
+ const model = await stepModel(base, "[5/6] AI model for HUD analysis");
262
286
  vars.AGENT_MODEL = model;
263
287
  p.log.success(`Model: ${model}.`);
264
288
 
265
289
  // Default agent goes into agents.json `default` field (was SINAIN_AGENT
266
290
  // env var). Overlay's chip selector lets the user switch at runtime.
267
291
  agentsPatch.default = base.SINAIN_AGENT || "claude";
292
+
293
+ // Step 6: register sinain MCP with locally-installed MCP-aware agents
294
+ // (Claude Code, Cursor, Codex, Goose, Junie, Claude Desktop). Detection
295
+ // is non-destructive — if no agents are installed, the step prints a
296
+ // skip note and returns. Idempotent across re-runs.
297
+ await stepMcpInstall(base, "[6/6] Connect MCP agents");
268
298
  }
269
299
 
270
300
  // ── Common defaults ───────────────────────────────────────────────────
@@ -397,9 +427,9 @@ if (flags.nonInteractive) {
397
427
  }
398
428
 
399
429
  writeEnv(vars);
400
- // Default agent + escalation off + openclaw explicitly disabled. Gateway is
401
- // opt-in via the interactive wizard (`sinain onboard`) or `sinain config`.
402
- writeAgentsConfig({ default: "claude", escalationMode: "off", openclawProfile: null });
430
+ // Default agent + openclaw explicitly disabled. No escalation mode written
431
+ // lane is the source of truth, registerBareAgent reconciles at boot.
432
+ writeAgentsConfig({ default: "claude", openclawProfile: null });
403
433
  console.log(c.green(` Config written to ${ENV_PATH} + ~/.sinain/agents.json`));
404
434
  process.exit(0);
405
435
  } else {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@geravant/sinain",
3
- "version": "1.18.2",
3
+ "version": "1.19.0",
4
4
  "description": "Ambient intelligence that sees what you see, hears what you hear, and acts on your behalf",
5
5
  "type": "module",
6
6
  "bin": {
@@ -64,7 +64,7 @@
64
64
  },
65
65
 
66
66
  "escalation": {
67
- "mode": "off",
67
+ "_comment": "Lane (the agent picked for escalation in agents.json `default` or via the overlay chip) is the source of truth for whether escalation runs. `mode` is intentionally omitted here — the runtime default (rich) applies, and registerBareAgent reconciles mode with lane at boot. Override `mode` only if you want to force selective/focus/off semantics.",
68
68
  "cooldownMs": 30000,
69
69
  "staleMs": 90000
70
70
  },
@@ -734,6 +734,16 @@ async function main() {
734
734
  }
735
735
  wsHandler.updateState({ agents: { ...bareAgentState } });
736
736
  log(TAG, `bareagent register: available=[${clean.join(",")}] current=${current} → lanes esc=${bareAgentState.escalationAgent} spawn=${bareAgentState.spawnAgent}`);
737
+
738
+ // Lane is the source of truth for "is escalation active?". If a lane is
739
+ // set but mode is still "off" (e.g. an old wizard run wrote mode=off and
740
+ // the user has since picked an agent in the chip selector — or we just
741
+ // booted from agents.json with that combination), reconcile by promoting
742
+ // mode to match. Mirrors the existing set_agent → resumeEscalation flow,
743
+ // applied at register time so the boot-from-disk case isn't an exception.
744
+ if (bareAgentState.escalationAgent && config.escalationConfig.mode === "off") {
745
+ resumeEscalationInternal();
746
+ }
737
747
  }
738
748
 
739
749
  // ── Create HTTP + WS server ──
@@ -60,7 +60,7 @@ let allFacts = [];
60
60
  async function loadFacts() {
61
61
  document.getElementById('status').textContent = 'Loading...';
62
62
  try {
63
- const res = await fetch('/knowledge/entities?max=200');
63
+ const res = await fetch('/knowledge/entities?max=1000');
64
64
  const data = await res.json();
65
65
  allFacts = typeof data.entities === 'string' ? JSON.parse(data.entities) : data.entities;
66
66
  const domains = [...new Set(allFacts.map(f => f.domain).filter(Boolean))].sort();
@@ -440,7 +440,7 @@ export function createAppServer(deps: ServerDeps) {
440
440
 
441
441
  if (req.method === "GET" && url.pathname === "/knowledge/entities") {
442
442
  // List all entities in the knowledge graph
443
- const max = Math.min(parseInt(url.searchParams.get("max") || "50"), 200);
443
+ const max = Math.min(parseInt(url.searchParams.get("max") || "50"), 1000);
444
444
  if (deps.listKnowledgeEntities) {
445
445
  try {
446
446
  const entities = await deps.listKnowledgeEntities(max);