instar 1.3.444 → 1.3.445

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/README.md CHANGED
@@ -18,7 +18,7 @@
18
18
  </p>
19
19
 
20
20
  <p align="center">
21
- <a href="https://www.npmjs.com/package/instar">npm</a> · <a href="https://github.com/JKHeadley/instar">GitHub</a> · <a href="https://instar.sh">instar.sh</a> · <a href="https://instar.sh/introduction/">Docs</a>
21
+ <a href="https://www.npmjs.com/package/instar">npm</a> · <a href="https://github.com/JKHeadley/instar">GitHub</a> · <a href="https://instar.sh">instar.sh</a> · <a href="https://instar.sh/introduction/">Docs</a> · <a href="https://instar.sh/exo3">EXO&nbsp;3.0</a>
22
22
  </p>
23
23
 
24
24
  ---
@@ -116,6 +116,14 @@ An agent that forgets what you discussed yesterday, doesn't recognize someone it
116
116
 
117
117
  > **Deep dive:** [The Coherence Problem](https://instar.sh/concepts/coherence/) · [Values & Identity](https://instar.sh/concepts/values/) · [Coherence Is Safety](https://instar.sh/concepts/safety/)
118
118
 
119
+ ## EXO 3.0 — governed by your organization's intent
120
+
121
+ Instar independently converged on Salim Ismail's **EXO 3.0 / "The Organizational Singularity"** framework: the idea that an organization's purpose should be encoded as *machine-readable* intent — "in code, not culture" — that actually **governs** its agents rather than just inspiring them. Instar was built around coherence before the framework existed, and the overlap turned out to be close to the line.
122
+
123
+ We didn't just claim it works. We ran controlled experiments — same model, same requests, with the organization's intent switched off as the control — to show the intent itself is what changes the agent's behavior. The engine stays **neutral**: it enforces whatever intent a deploying organization gives it, never Instar's own values. Two case studies enforce two very different fictional companies' values equally well, neither of them ours.
124
+
125
+ > **See the controlled proof → [instar.sh/exo3](https://instar.sh/exo3)** — two governed-vs-ungoverned case studies, with full transcripts.
126
+
119
127
  ## Features
120
128
 
121
129
  | Feature | Description | Docs |
@@ -12,6 +12,25 @@ import { TelegramAdapter } from '../messaging/TelegramAdapter.js';
12
12
  import { TopicMemory } from '../memory/TopicMemory.js';
13
13
  import { QuotaTracker } from '../monitoring/QuotaTracker.js';
14
14
  import { UserManager } from '../users/UserManager.js';
15
+ /**
16
+ * Pure: should the emergency "fix command" gate intercept this message?
17
+ *
18
+ * Fix commands ("fix auth", "restart sessions", "clean processes", …) are
19
+ * mechanical server-side operations that only make sense in the Agent Attention
20
+ * topic, where the agent posts actionable notifications the user taps to resolve.
21
+ * In ANY other topic, a message that merely starts with "restart" / "fix " /
22
+ * "clean " is ordinary conversation ("restart the build", "fix the login page",
23
+ * "clean up this function") and must route to the session — never be swallowed.
24
+ *
25
+ * The previous logic ran this verb test in every topic and, on a non-attention
26
+ * topic, bounced the message back with "I didn't recognize that command" while
27
+ * also swallowing it (the gate `return`s). That is exactly why a user trying to
28
+ * revive a stuck session by typing "restart sessions" in that session's own
29
+ * topic never reached the session: the gate ate the message and replied with a
30
+ * help list that even advertised "restart sessions" as valid. Scoping the gate
31
+ * to the attention topic closes that hole.
32
+ */
33
+ export declare function shouldInterceptFixCommand(text: string, topicId: number, attentionTopicId: number | null | undefined): boolean;
15
34
  interface StartOptions {
16
35
  foreground?: boolean;
17
36
  dir?: string;
@@ -19,7 +38,7 @@ interface StartOptions {
19
38
  * Commander maps --no-telegram to telegram: false. */
20
39
  telegram?: boolean;
21
40
  }
22
- 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): void;
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;
23
42
  export declare function startServer(options: StartOptions): Promise<void>;
24
43
  export declare function stopServer(options: {
25
44
  dir?: string;
@@ -1 +1 @@
1
- {"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../src/commands/server.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAaH,OAAO,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAC;AAS3D,OAAO,EAAE,eAAe,EAAiC,MAAM,iCAAiC,CAAC;AAuBjG,OAAO,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAC;AAGvD,OAAO,EAAE,YAAY,EAAE,MAAM,+BAA+B,CAAC;AAiG7D,OAAO,EAAE,WAAW,EAAE,MAAM,yBAAyB,CAAC;AA6JtD,UAAU,YAAY;IACpB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb;2DACuD;IACvD,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AA05BD,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,GAC/F,IAAI,CAyUN;AA2lBD,wBAAsB,WAAW,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC,CAuvUtE;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;AAaH,OAAO,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAC;AAS3D,OAAO,EAAE,eAAe,EAAiC,MAAM,iCAAiC,CAAC;AAuBjG,OAAO,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAC;AAGvD,OAAO,EAAE,YAAY,EAAE,MAAM,+BAA+B,CAAC;AAiG7D,OAAO,EAAE,WAAW,EAAE,MAAM,yBAAyB,CAAC;AAsBtD;;;;;;;;;;;;;;;;;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;AA05BD,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,CA0UN;AA2lBD,wBAAsB,WAAW,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC,CAyvUtE;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"}
@@ -144,6 +144,34 @@ import { toInjection } from '../types/pipeline.js';
144
144
  import { UserManager } from '../users/UserManager.js';
145
145
  import { formatUserContextForSession, hasUserContext } from '../users/UserContextBuilder.js';
146
146
  import { SafeFsExecutor } from '../core/SafeFsExecutor.js';
147
+ /**
148
+ * Pure: should the emergency "fix command" gate intercept this message?
149
+ *
150
+ * Fix commands ("fix auth", "restart sessions", "clean processes", …) are
151
+ * mechanical server-side operations that only make sense in the Agent Attention
152
+ * topic, where the agent posts actionable notifications the user taps to resolve.
153
+ * In ANY other topic, a message that merely starts with "restart" / "fix " /
154
+ * "clean " is ordinary conversation ("restart the build", "fix the login page",
155
+ * "clean up this function") and must route to the session — never be swallowed.
156
+ *
157
+ * The previous logic ran this verb test in every topic and, on a non-attention
158
+ * topic, bounced the message back with "I didn't recognize that command" while
159
+ * also swallowing it (the gate `return`s). That is exactly why a user trying to
160
+ * revive a stuck session by typing "restart sessions" in that session's own
161
+ * topic never reached the session: the gate ate the message and replied with a
162
+ * help list that even advertised "restart sessions" as valid. Scoping the gate
163
+ * to the attention topic closes that hole.
164
+ */
165
+ export function shouldInterceptFixCommand(text, topicId, attentionTopicId) {
166
+ if (!attentionTopicId || topicId !== attentionTopicId)
167
+ return false;
168
+ const cmd = text.trim().toLowerCase();
169
+ return (cmd.startsWith('fix ') ||
170
+ cmd.startsWith('clean ') ||
171
+ cmd.startsWith('restart') ||
172
+ cmd === 'fix' ||
173
+ cmd === 'clean');
174
+ }
147
175
  /**
148
176
  * Handle "fix X" and "clean X" commands from Agent Attention notifications.
149
177
  * These are mechanical server-side operations — no Claude session needed.
@@ -1100,7 +1128,12 @@ getHubDeps,
1100
1128
  // the SAME instance the routes use (a second instance on the same file would
1101
1129
  // lose updates between the two in-memory caches). Know Your Principal #898,
1102
1130
  // increment 2e: the polling-path operator auto-bind.
1103
- getTopicOperatorStore) {
1131
+ getTopicOperatorStore,
1132
+ // Late-bound resolver for the Agent Attention topic id. The emergency
1133
+ // fix-command gate (below) only fires in that topic; everywhere else a
1134
+ // message starting with restart/fix/clean is normal conversation and must
1135
+ // route to the session instead of being swallowed.
1136
+ getAttentionTopicId) {
1104
1137
  // Guard: tracks which topic IDs have a spawn in progress.
1105
1138
  // Prevents duplicate concurrent spawns for the same topic when messages
1106
1139
  // arrive faster than the async spawn completes.
@@ -1214,34 +1247,35 @@ getTopicOperatorStore) {
1214
1247
  // ── Fix commands from notification messages ──────────────────────
1215
1248
  // Handle "fix auth", "clean processes", "restart", etc. directly
1216
1249
  // in the server process — no need to spawn a Claude session for these.
1217
- if (fixCommandHandler) {
1218
- const cmdText = text.trim().toLowerCase();
1219
- const isFixCommand = cmdText.startsWith('fix ') || cmdText.startsWith('clean ') ||
1220
- cmdText.startsWith('restart') || cmdText === 'fix' || cmdText === 'clean';
1221
- if (isFixCommand) {
1222
- (async () => {
1223
- try {
1224
- const handled = await fixCommandHandler(topicId, text);
1225
- if (!handled) {
1226
- // Not a recognized fix command — fall through to session routing
1227
- // Re-trigger the normal routing by calling the topic message handler again
1228
- // Actually, since we can't re-trigger, just send a help message
1229
- await telegram.sendToTopic(topicId, `I didn't recognize that command. Available fix commands:\n` +
1230
- `• "fix auth" Generate an API security token\n` +
1231
- `• "fix lifeline" Restart the crash-recovery system\n` +
1232
- `• "fix shadow" — Remove shadow installation\n` +
1233
- `• "clean processes" — Kill external Claude processes\n` +
1234
- `• "restart" — Restart the server\n` +
1235
- `• "restart sessions" — Restart stuck sessions`);
1236
- }
1237
- }
1238
- catch (err) {
1239
- console.error(`[telegram] Fix command error:`, err);
1240
- await telegram.sendToTopic(topicId, `Something went wrong while trying to fix that: ${err instanceof Error ? err.message : String(err)}`).catch(() => { });
1250
+ //
1251
+ // These ONLY apply in the Agent Attention topic. Scoping the gate there
1252
+ // (shouldInterceptFixCommand) is deliberate: previously the verb test ran
1253
+ // in every topic, so a message starting with restart/fix/clean in a normal
1254
+ // conversation was swallowed and bounced back with an "I didn't recognize
1255
+ // that command" help list — which is exactly why typing "restart sessions"
1256
+ // in a stuck session's own topic never reached the session. Outside the
1257
+ // attention topic the message now falls through to session routing below.
1258
+ if (fixCommandHandler && shouldInterceptFixCommand(text, topicId, getAttentionTopicId?.())) {
1259
+ (async () => {
1260
+ try {
1261
+ const handled = await fixCommandHandler(topicId, text);
1262
+ if (!handled) {
1263
+ // In the attention topic, but not a recognized fix command — show help.
1264
+ await telegram.sendToTopic(topicId, `I didn't recognize that command. Available fix commands:\n` +
1265
+ `• "fix auth" — Generate an API security token\n` +
1266
+ `• "fix lifeline" — Restart the crash-recovery system\n` +
1267
+ `• "fix shadow" — Remove shadow installation\n` +
1268
+ `• "clean processes" — Kill external Claude processes\n` +
1269
+ `• "restart" — Restart the server\n` +
1270
+ `• "restart sessions" — Restart stuck sessions`);
1241
1271
  }
1242
- })();
1243
- return;
1244
- }
1272
+ }
1273
+ catch (err) {
1274
+ console.error(`[telegram] Fix command error:`, err);
1275
+ await telegram.sendToTopic(topicId, `Something went wrong while trying to fix that: ${err instanceof Error ? err.message : String(err)}`).catch(() => { });
1276
+ }
1277
+ })();
1278
+ return;
1245
1279
  }
1246
1280
  // ── Pipeline-typed routing ──────────────────────────────────────
1247
1281
  // Convert to PipelineMessage — types enforce that sender identity
@@ -3717,7 +3751,7 @@ export async function startServer(options) {
3717
3751
  // reached the AI session as plain chat text.
3718
3752
  const userManagerSendOnly = new UserManager(config.stateDir, config.users);
3719
3753
  _fixDeps = { state, liveConfig, sessionManager, telegram, config };
3720
- 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);
3754
+ 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'));
3721
3755
  wireTelegramCallbacks(telegram, sessionManager, state, quotaTracker, undefined, config.sessions.claudePath, topicMemory);
3722
3756
  console.log(pc.green(' Telegram routing + command callbacks wired (send-only)'));
3723
3757
  }
@@ -3843,7 +3877,7 @@ export async function startServer(options) {
3843
3877
  config,
3844
3878
  };
3845
3879
  // Wire up topic → session routing and session management callbacks
3846
- 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);
3880
+ 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'));
3847
3881
  wireTelegramCallbacks(telegram, sessionManager, state, quotaTracker, accountSwitcher, config.sessions.claudePath, topicMemory);
3848
3882
  // Wire up unknown-user handling (Multi-User Setup Wizard Phase 4.5)
3849
3883
  telegram.onGetRegistrationPolicy = () => ({