instar 1.3.753 → 1.3.754

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.
@@ -11,6 +11,7 @@ import { SessionManager } from '../core/SessionManager.js';
11
11
  import { TelegramAdapter } from '../messaging/TelegramAdapter.js';
12
12
  import { TopicMemory } from '../memory/TopicMemory.js';
13
13
  import { QuotaTracker } from '../monitoring/QuotaTracker.js';
14
+ import type { IntelligenceProvider, InstarConfig } from '../core/types.js';
14
15
  import { UserManager } from '../users/UserManager.js';
15
16
  /**
16
17
  * Pure: should the emergency "fix command" gate intercept this message?
@@ -38,7 +39,35 @@ interface StartOptions {
38
39
  * Commander maps --no-telegram to telegram: false. */
39
40
  telegram?: boolean;
40
41
  }
41
- export declare function wireTelegramRouting(telegram: TelegramAdapter, sessionManager: SessionManager, quotaTracker?: QuotaTracker, topicMemory?: TopicMemory, userManager?: UserManager, fixCommandHandler?: (topicId: number, text: string) => Promise<boolean>, getHubDeps?: () => import('../threadline/hubCommands.js').HubBindDeps | null, getTopicOperatorStore?: () => import('../users/TopicOperatorStore.js').TopicOperatorStore | null, getAttentionTopicId?: () => number | null | undefined): void;
42
+ /**
43
+ * Resolve the hub-intent classifier deps (Conversion #3,
44
+ * docs/specs/keyword-intent-conversions-1-and-3.md) from live config + the shared
45
+ * IntelligenceProvider. `enabled` is DEV-GATED (resolveDevAgentGate): DARK on the
46
+ * fleet, LIVE on a development agent. `dryRun` defaults true (the soak) so a
47
+ * dev-agent classifier RUNS but the message ALWAYS passes through until a
48
+ * deliberate `dryRun:false`. Passed as a late-bound getter so it re-reads config
49
+ * at message time.
50
+ */
51
+ export declare function resolveHubClassifierDeps(config: InstarConfig, intelligence: IntelligenceProvider | null): {
52
+ intelligence: IntelligenceProvider | null;
53
+ enabled: boolean;
54
+ dryRun: boolean;
55
+ minConfidence: number;
56
+ timeoutMs: number;
57
+ contextTurns: number;
58
+ modelTier: 'fast' | 'balanced' | 'capable';
59
+ stateDir: string;
60
+ };
61
+ export declare function wireTelegramRouting(telegram: TelegramAdapter, sessionManager: SessionManager, quotaTracker?: QuotaTracker, topicMemory?: TopicMemory, userManager?: UserManager, fixCommandHandler?: (topicId: number, text: string) => Promise<boolean>, getHubDeps?: () => import('../threadline/hubCommands.js').HubBindDeps | null, getTopicOperatorStore?: () => import('../users/TopicOperatorStore.js').TopicOperatorStore | null, getAttentionTopicId?: () => number | null | undefined, getHubClassifierDeps?: () => {
62
+ intelligence: import('../core/types.js').IntelligenceProvider | null;
63
+ enabled: boolean;
64
+ dryRun: boolean;
65
+ minConfidence: number;
66
+ timeoutMs: number;
67
+ contextTurns: number;
68
+ modelTier: 'fast' | 'balanced' | 'capable';
69
+ stateDir: string;
70
+ } | null): void;
42
71
  export declare function startServer(options: StartOptions): Promise<void>;
43
72
  export declare function stopServer(options: {
44
73
  dir?: string;
@@ -1 +1 @@
1
- {"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../src/commands/server.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAiDH,OAAO,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAC;AAU3D,OAAO,EAAE,eAAe,EAAiC,MAAM,iCAAiC,CAAC;AAyBjG,OAAO,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAC;AAGvD,OAAO,EAAE,YAAY,EAAE,MAAM,+BAA+B,CAAC;AA8H7D,OAAO,EAAE,WAAW,EAAE,MAAM,yBAAyB,CAAC;AAgFtD;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,yBAAyB,CACvC,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,MAAM,EACf,gBAAgB,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAC1C,OAAO,CAUT;AAyID,UAAU,YAAY;IACpB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb;2DACuD;IACvD,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AA2/CD,wBAAgB,mBAAmB,CACjC,QAAQ,EAAE,eAAe,EACzB,cAAc,EAAE,cAAc,EAC9B,YAAY,CAAC,EAAE,YAAY,EAC3B,WAAW,CAAC,EAAE,WAAW,EACzB,WAAW,CAAC,EAAE,WAAW,EACzB,iBAAiB,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,OAAO,CAAC,EAGvE,UAAU,CAAC,EAAE,MAAM,OAAO,8BAA8B,EAAE,WAAW,GAAG,IAAI,EAK5E,qBAAqB,CAAC,EAAE,MAAM,OAAO,gCAAgC,EAAE,kBAAkB,GAAG,IAAI,EAKhG,mBAAmB,CAAC,EAAE,MAAM,MAAM,GAAG,IAAI,GAAG,SAAS,GACpD,IAAI,CAijBN;AA2lBD,wBAAsB,WAAW,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC,CA4ylBtE;AAED,wBAAsB,UAAU,CAAC,OAAO,EAAE;IAAE,GAAG,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAsDzE;AAED;;;;;;;;;;;GAWG;AACH,wBAAsB,aAAa,CAAC,OAAO,EAAE;IAAE,GAAG,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAuD5E"}
1
+ {"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../src/commands/server.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAiDH,OAAO,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAC;AAU3D,OAAO,EAAE,eAAe,EAAiC,MAAM,iCAAiC,CAAC;AAyBjG,OAAO,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAC;AAGvD,OAAO,EAAE,YAAY,EAAE,MAAM,+BAA+B,CAAC;AA6H7D,OAAO,KAAK,EAAW,oBAAoB,EAAe,YAAY,EAAE,MAAM,kBAAkB,CAAC;AACjG,OAAO,EAAE,WAAW,EAAE,MAAM,yBAAyB,CAAC;AAgFtD;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,yBAAyB,CACvC,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,MAAM,EACf,gBAAgB,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAC1C,OAAO,CAUT;AAyID,UAAU,YAAY;IACpB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb;2DACuD;IACvD,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AA2/CD;;;;;;;;GAQG;AACH,wBAAgB,wBAAwB,CACtC,MAAM,EAAE,YAAY,EACpB,YAAY,EAAE,oBAAoB,GAAG,IAAI,GACxC;IACD,YAAY,EAAE,oBAAoB,GAAG,IAAI,CAAC;IAC1C,OAAO,EAAE,OAAO,CAAC;IACjB,MAAM,EAAE,OAAO,CAAC;IAChB,aAAa,EAAE,MAAM,CAAC;IACtB,SAAS,EAAE,MAAM,CAAC;IAClB,YAAY,EAAE,MAAM,CAAC;IACrB,SAAS,EAAE,MAAM,GAAG,UAAU,GAAG,SAAS,CAAC;IAC3C,QAAQ,EAAE,MAAM,CAAC;CAClB,CAqBA;AAED,wBAAgB,mBAAmB,CACjC,QAAQ,EAAE,eAAe,EACzB,cAAc,EAAE,cAAc,EAC9B,YAAY,CAAC,EAAE,YAAY,EAC3B,WAAW,CAAC,EAAE,WAAW,EACzB,WAAW,CAAC,EAAE,WAAW,EACzB,iBAAiB,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,OAAO,CAAC,EAGvE,UAAU,CAAC,EAAE,MAAM,OAAO,8BAA8B,EAAE,WAAW,GAAG,IAAI,EAK5E,qBAAqB,CAAC,EAAE,MAAM,OAAO,gCAAgC,EAAE,kBAAkB,GAAG,IAAI,EAKhG,mBAAmB,CAAC,EAAE,MAAM,MAAM,GAAG,IAAI,GAAG,SAAS,EAQrD,oBAAoB,CAAC,EAAE,MAAM;IAC3B,YAAY,EAAE,OAAO,kBAAkB,EAAE,oBAAoB,GAAG,IAAI,CAAC;IACrE,OAAO,EAAE,OAAO,CAAC;IACjB,MAAM,EAAE,OAAO,CAAC;IAChB,aAAa,EAAE,MAAM,CAAC;IACtB,SAAS,EAAE,MAAM,CAAC;IAClB,YAAY,EAAE,MAAM,CAAC;IACrB,SAAS,EAAE,MAAM,GAAG,UAAU,GAAG,SAAS,CAAC;IAC3C,QAAQ,EAAE,MAAM,CAAC;CAClB,GAAG,IAAI,GACP,IAAI,CAonBN;AA2lBD,wBAAsB,WAAW,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC,CA8ylBtE;AAED,wBAAsB,UAAU,CAAC,OAAO,EAAE;IAAE,GAAG,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAsDzE;AAED;;;;;;;;;;;GAWG;AACH,wBAAsB,aAAa,CAAC,OAAO,EAAE;IAAE,GAAG,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAuD5E"}
@@ -1833,6 +1833,28 @@ async function handleProfileConfirm(telegram, surface, slots, topicId, principal
1833
1833
  await send('That switch is no longer pending.');
1834
1834
  return true;
1835
1835
  }
1836
+ /**
1837
+ * Resolve the hub-intent classifier deps (Conversion #3,
1838
+ * docs/specs/keyword-intent-conversions-1-and-3.md) from live config + the shared
1839
+ * IntelligenceProvider. `enabled` is DEV-GATED (resolveDevAgentGate): DARK on the
1840
+ * fleet, LIVE on a development agent. `dryRun` defaults true (the soak) so a
1841
+ * dev-agent classifier RUNS but the message ALWAYS passes through until a
1842
+ * deliberate `dryRun:false`. Passed as a late-bound getter so it re-reads config
1843
+ * at message time.
1844
+ */
1845
+ export function resolveHubClassifierDeps(config, intelligence) {
1846
+ const cfg = config.threadline?.hubIntent;
1847
+ return {
1848
+ intelligence,
1849
+ enabled: resolveDevAgentGate(cfg?.enabled, config),
1850
+ dryRun: cfg?.dryRun ?? true,
1851
+ minConfidence: cfg?.minConfidence ?? 0.85,
1852
+ timeoutMs: cfg?.timeoutMs ?? 4000,
1853
+ contextTurns: cfg?.contextWindowTurns ?? 6,
1854
+ modelTier: cfg?.modelTier ?? 'fast',
1855
+ stateDir: config.stateDir,
1856
+ };
1857
+ }
1836
1858
  export function wireTelegramRouting(telegram, sessionManager, quotaTracker, topicMemory, userManager, fixCommandHandler,
1837
1859
  // Late-bound: the threadline hub deps are constructed AFTER this is wired, so
1838
1860
  // resolve them at message-time (CMT-529 deterministic "open this" intercept).
@@ -1846,7 +1868,15 @@ getTopicOperatorStore,
1846
1868
  // fix-command gate (below) only fires in that topic; everywhere else a
1847
1869
  // message starting with restart/fix/clean is normal conversation and must
1848
1870
  // route to the session instead of being swallowed.
1849
- getAttentionTopicId) {
1871
+ getAttentionTopicId,
1872
+ // Late-bound (same reason as getHubDeps): the hub-intent classifier deps —
1873
+ // the config-resolved dev-gate/settings + the shared IntelligenceProvider.
1874
+ // Conversion #3 (docs/specs/keyword-intent-conversions-1-and-3.md): the
1875
+ // "open this"/"tie this to <topic>" DECISION is inferred by an LLM over the
1876
+ // message + recent conversation (HubIntentClassifier), NOT a regex — the
1877
+ // regex SWALLOWED the message before the agent saw it, so a misread ATE it.
1878
+ // Resolved at the callsite where config + sharedIntelligence are in scope.
1879
+ getHubClassifierDeps) {
1850
1880
  // Guard: tracks which topic IDs have a spawn in progress.
1851
1881
  // Prevents duplicate concurrent spawns for the same topic when messages
1852
1882
  // arrive faster than the async spawn completes.
@@ -1942,10 +1972,19 @@ getAttentionTopicId) {
1942
1972
  return;
1943
1973
  }
1944
1974
  // ── Threadline hub commands: "open this" / "tie this to <topic>" (CMT-529) ──
1945
- // When a message in the Threadline HUB topic is a deterministic hub command,
1946
- // bind the conversation to a topic STRUCTURALLY — before any agent interprets
1947
- // it. This onTopicMessage seam is the convergence both inbound paths reach
1975
+ // When a message in the Threadline HUB topic is a bind command, bind the
1976
+ // conversation to a topic STRUCTURALLY — before any agent interprets it. This
1977
+ // onTopicMessage seam is the convergence both inbound paths reach
1948
1978
  // (lifeline-forward AND server-polling), so one intercept covers both modes.
1979
+ //
1980
+ // Conversion #3 (docs/specs/keyword-intent-conversions-1-and-3.md): the
1981
+ // "is this a bind command?" DECISION is inferred by an LLM over the message +
1982
+ // recent conversation (HubIntentClassifier), NOT a regex. The old regex
1983
+ // SWALLOWED the message before the agent saw it, so a misread silently ATE a
1984
+ // real message. Dev-gated dark (resolveDevAgentGate at the callsite) +
1985
+ // dry-run FIRST: while dark or dry-run the message ALWAYS passes through
1986
+ // (never swallowed); the classifier still runs on a dev agent and LOGS
1987
+ // would-swallow vs would-pass for the soak. Fail-OPEN on any uncertainty.
1949
1988
  // FAIL-OPEN: any error falls through to normal routing.
1950
1989
  const hubDeps = getHubDeps?.() ?? null;
1951
1990
  if (hubDeps) {
@@ -1958,17 +1997,78 @@ getAttentionTopicId) {
1958
1997
  hubTopicId = undefined;
1959
1998
  }
1960
1999
  if (hubTopicId !== undefined && Number(topicId) === Number(hubTopicId)) {
1961
- const { parseHubCommand, bindHubConversation } = await import('../threadline/hubCommands.js');
1962
- const cmd = parseHubCommand(text);
1963
- if (cmd) {
1964
- const result = await bindHubConversation(hubDeps, { ...cmd, autoPick: true }); // human "open this" → auto-pick most-recent, never 409
1965
- if (!result.ok) {
1966
- await telegram.sendToTopic(topicId, result.status === 404
1967
- ? 'Nothing waiting in the hub to open right now.'
1968
- : `Couldn't open that ${result.error}`).catch(() => { });
2000
+ const clsDeps = getHubClassifierDeps?.() ?? null;
2001
+ // Dark gate: when the classifier isn't enabled (fleet default), never
2002
+ // swallow — pass the message through to the agent (no regex fallback).
2003
+ if (clsDeps?.enabled) {
2004
+ const { bindHubConversation } = await import('../threadline/hubCommands.js');
2005
+ const { classifyHubIntent, toHubCommand } = await import('../threadline/HubIntentClassifier.js');
2006
+ // The enum of bindable topics (real existing topics) — the model can
2007
+ // only tie to one of these (structured-output guardrail).
2008
+ let bindableTopics = [];
2009
+ try {
2010
+ bindableTopics = telegram.getAllTopicMappings()
2011
+ .map((m) => ({ topicId: m.topicId, topicName: m.topicName ?? `topic ${m.topicId}` }));
2012
+ }
2013
+ catch { /* best-effort — an empty enum simply means only "open" is possible */ }
2014
+ // Bounded window of recent turns so context-dependent commands ("yes,
2015
+ // tie it to that one") can resolve their target. Best-effort.
2016
+ let conversationContext = [];
2017
+ try {
2018
+ const hist = telegram.getTopicHistory(topicId, clsDeps.contextTurns) ?? [];
2019
+ conversationContext = hist.map((h) => ({ fromUser: !!h.fromUser, text: String(h.text ?? '') }));
2020
+ }
2021
+ catch { /* context is best-effort; classify on the message alone */ }
2022
+ const result = await classifyHubIntent({
2023
+ text,
2024
+ bindableTopics,
2025
+ conversationContext,
2026
+ intelligence: clsDeps.intelligence,
2027
+ minConfidence: clsDeps.minConfidence,
2028
+ timeoutMs: clsDeps.timeoutMs,
2029
+ maxContextTurns: clsDeps.contextTurns,
2030
+ modelTier: clsDeps.modelTier,
2031
+ });
2032
+ const willAct = result.isCommand && !clsDeps.dryRun;
2033
+ // Audit the soak: log LLM-engaged decisions (a prefilter-skip is the
2034
+ // bulk of traffic and always a would-pass — skipping it cuts volume
2035
+ // AND the message-preview privacy surface). Never gates the message.
2036
+ if (result.source !== 'prefilter-skip') {
2037
+ try {
2038
+ const logDir = path.join(clsDeps.stateDir, 'logs');
2039
+ fs.mkdirSync(logDir, { recursive: true });
2040
+ const decision = result.isCommand ? (willAct ? 'swallow' : 'would-swallow') : 'pass';
2041
+ fs.appendFileSync(path.join(logDir, 'hub-intent.jsonl'), JSON.stringify({
2042
+ ts: new Date().toISOString(),
2043
+ topicId,
2044
+ decision,
2045
+ dryRun: clsDeps.dryRun,
2046
+ source: result.source,
2047
+ intent: result.intent,
2048
+ targetTopicId: result.targetTopicId,
2049
+ confidence: result.confidence,
2050
+ reason: result.reason,
2051
+ textPreview: String(text).replace(/\s+/g, ' ').trim().slice(0, 80),
2052
+ }) + '\n');
2053
+ }
2054
+ catch { /* audit is observability — never gates the message path */ }
2055
+ }
2056
+ // Dry-run (the soak) OR not-a-command OR fail-open → pass the message
2057
+ // through untouched. Real swallowing needs a deliberate dryRun:false,
2058
+ // AND fail-open means uncertainty never swallows.
2059
+ if (willAct) {
2060
+ const cmd = toHubCommand(result);
2061
+ if (cmd) {
2062
+ const bindResult = await bindHubConversation(hubDeps, { ...cmd, autoPick: true }); // human "open this" → auto-pick most-recent, never 409
2063
+ if (!bindResult.ok) {
2064
+ await telegram.sendToTopic(topicId, bindResult.status === 404
2065
+ ? 'Nothing waiting in the hub to open right now.'
2066
+ : `Couldn't open that — ${bindResult.error}`).catch(() => { });
2067
+ }
2068
+ // On success bindHubConversation already posted the hub confirmation.
2069
+ return; // structural: never inject the command into a session
2070
+ }
1969
2071
  }
1970
- // On success bindHubConversation already posted the hub confirmation.
1971
- return; // structural: never inject the command into a session
1972
2072
  }
1973
2073
  }
1974
2074
  }
@@ -6198,7 +6298,7 @@ export async function startServer(options) {
6198
6298
  const userManagerSendOnly = new UserManager(config.stateDir, config.users);
6199
6299
  attachUserReplication(userManagerSendOnly); // WS2.6 send-side (dark by default)
6200
6300
  _fixDeps = { state, liveConfig, sessionManager, telegram, config };
6201
- wireTelegramRouting(telegram, sessionManager, quotaTracker, topicMemory, userManagerSendOnly, (topicId, text) => handleFixCommand(topicId, text, _fixDeps), () => (collaborationSurfacer && conversationStore && telegram) ? { collaborationSurfacer, conversationStore, commitmentTracker, telegram, brief: briefDeps } : null, () => _agentServerRef?.getTopicOperatorStore() ?? null, () => state.get('agent-attention-topic'));
6301
+ wireTelegramRouting(telegram, sessionManager, quotaTracker, topicMemory, userManagerSendOnly, (topicId, text) => handleFixCommand(topicId, text, _fixDeps), () => (collaborationSurfacer && conversationStore && telegram) ? { collaborationSurfacer, conversationStore, commitmentTracker, telegram, brief: briefDeps } : null, () => _agentServerRef?.getTopicOperatorStore() ?? null, () => state.get('agent-attention-topic'), () => resolveHubClassifierDeps(config, _sharedIntelligence));
6202
6302
  wireTelegramCallbacks(telegram, sessionManager, state, quotaTracker, undefined, config.sessions.claudePath, topicMemory);
6203
6303
  console.log(pc.green(' Telegram routing + command callbacks wired (send-only)'));
6204
6304
  }
@@ -6330,7 +6430,7 @@ export async function startServer(options) {
6330
6430
  config,
6331
6431
  };
6332
6432
  // Wire up topic → session routing and session management callbacks
6333
- wireTelegramRouting(telegram, sessionManager, quotaTracker, topicMemory, userManager, (topicId, text) => handleFixCommand(topicId, text, _fixDeps), () => (collaborationSurfacer && conversationStore && telegram) ? { collaborationSurfacer, conversationStore, commitmentTracker, telegram, brief: briefDeps } : null, () => _agentServerRef?.getTopicOperatorStore() ?? null, () => state.get('agent-attention-topic'));
6433
+ wireTelegramRouting(telegram, sessionManager, quotaTracker, topicMemory, userManager, (topicId, text) => handleFixCommand(topicId, text, _fixDeps), () => (collaborationSurfacer && conversationStore && telegram) ? { collaborationSurfacer, conversationStore, commitmentTracker, telegram, brief: briefDeps } : null, () => _agentServerRef?.getTopicOperatorStore() ?? null, () => state.get('agent-attention-topic'), () => resolveHubClassifierDeps(config, _sharedIntelligence));
6334
6434
  wireTelegramCallbacks(telegram, sessionManager, state, quotaTracker, accountSwitcher, config.sessions.claudePath, topicMemory);
6335
6435
  // Wire up unknown-user handling (Multi-User Setup Wizard Phase 4.5)
6336
6436
  telegram.onGetRegistrationPolicy = () => ({