instar 1.2.37 → 1.2.39

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.
Files changed (50) hide show
  1. package/dist/commands/server.d.ts.map +1 -1
  2. package/dist/commands/server.js +19 -1
  3. package/dist/commands/server.js.map +1 -1
  4. package/dist/core/TopicIntent.d.ts +162 -0
  5. package/dist/core/TopicIntent.d.ts.map +1 -0
  6. package/dist/core/TopicIntent.js +355 -0
  7. package/dist/core/TopicIntent.js.map +1 -0
  8. package/dist/core/TopicIntentArcCheck.d.ts +101 -0
  9. package/dist/core/TopicIntentArcCheck.d.ts.map +1 -0
  10. package/dist/core/TopicIntentArcCheck.js +146 -0
  11. package/dist/core/TopicIntentArcCheck.js.map +1 -0
  12. package/dist/core/TopicIntentBriefing.d.ts +44 -0
  13. package/dist/core/TopicIntentBriefing.d.ts.map +1 -0
  14. package/dist/core/TopicIntentBriefing.js +88 -0
  15. package/dist/core/TopicIntentBriefing.js.map +1 -0
  16. package/dist/core/TopicIntentExtractor.d.ts +90 -0
  17. package/dist/core/TopicIntentExtractor.d.ts.map +1 -0
  18. package/dist/core/TopicIntentExtractor.js +153 -0
  19. package/dist/core/TopicIntentExtractor.js.map +1 -0
  20. package/dist/core/TopicIntentPendingConfirm.d.ts +96 -0
  21. package/dist/core/TopicIntentPendingConfirm.d.ts.map +1 -0
  22. package/dist/core/TopicIntentPendingConfirm.js +223 -0
  23. package/dist/core/TopicIntentPendingConfirm.js.map +1 -0
  24. package/dist/messaging/TelegramAdapter.d.ts +45 -0
  25. package/dist/messaging/TelegramAdapter.d.ts.map +1 -1
  26. package/dist/messaging/TelegramAdapter.js +52 -0
  27. package/dist/messaging/TelegramAdapter.js.map +1 -1
  28. package/dist/server/AgentServer.d.ts +3 -0
  29. package/dist/server/AgentServer.d.ts.map +1 -1
  30. package/dist/server/AgentServer.js +10 -0
  31. package/dist/server/AgentServer.js.map +1 -1
  32. package/dist/server/topicIntentRoutes.d.ts +27 -0
  33. package/dist/server/topicIntentRoutes.d.ts.map +1 -0
  34. package/dist/server/topicIntentRoutes.js +189 -0
  35. package/dist/server/topicIntentRoutes.js.map +1 -0
  36. package/dist/tunnel/TunnelManager.d.ts +32 -1
  37. package/dist/tunnel/TunnelManager.d.ts.map +1 -1
  38. package/dist/tunnel/TunnelManager.js +36 -0
  39. package/dist/tunnel/TunnelManager.js.map +1 -1
  40. package/dist/tunnel/TunnelNotifier.d.ts +28 -0
  41. package/dist/tunnel/TunnelNotifier.d.ts.map +1 -1
  42. package/dist/tunnel/TunnelNotifier.js +38 -4
  43. package/dist/tunnel/TunnelNotifier.js.map +1 -1
  44. package/package.json +1 -1
  45. package/src/data/builtin-manifest.json +4 -4
  46. package/src/templates/hooks/telegram-topic-context.sh +19 -0
  47. package/upgrades/1.2.38.md +103 -0
  48. package/upgrades/1.2.39.md +54 -0
  49. package/upgrades/side-effects/owner-dm-channel.md +152 -0
  50. package/upgrades/side-effects/topic-intent-layer.md +86 -0
@@ -29,6 +29,7 @@
29
29
  export class TunnelNotifier {
30
30
  sink;
31
31
  clock;
32
+ credentialProvider;
32
33
  stateChangeMinIntervalMs;
33
34
  flapThreshold;
34
35
  // Dedup state. The lifecycle's epoch is monotonic per manager instance,
@@ -49,6 +50,7 @@ export class TunnelNotifier {
49
50
  constructor(opts) {
50
51
  this.sink = opts.sink;
51
52
  this.clock = opts.clock ?? { now: () => Date.now() };
53
+ this.credentialProvider = opts.credentialProvider;
52
54
  this.stateChangeMinIntervalMs = opts.stateChangeMinIntervalMs ?? 15 * 60_000;
53
55
  this.flapThreshold = opts.flapThreshold ?? 3;
54
56
  }
@@ -147,7 +149,7 @@ export class TunnelNotifier {
147
149
  messages.push({
148
150
  channel: 'owner-dm',
149
151
  class: 'state-change',
150
- text: `__OWNER_DM_RESTORED_URL_PLACEHOLDER__`,
152
+ text: this.composeRestoredDM(),
151
153
  episodeId: ep,
152
154
  epoch: e.epoch,
153
155
  });
@@ -164,7 +166,7 @@ export class TunnelNotifier {
164
166
  messages.push({
165
167
  channel: 'owner-dm',
166
168
  class: 'state-change',
167
- text: `__OWNER_DM_RECOVERED_URL_PLACEHOLDER__`,
169
+ text: this.composeRecoveredDM(),
168
170
  episodeId: ep,
169
171
  epoch: e.epoch,
170
172
  });
@@ -183,7 +185,7 @@ export class TunnelNotifier {
183
185
  messages.push({
184
186
  channel: 'owner-dm',
185
187
  class: 'action-required',
186
- text: `__OWNER_DM_CONSENT_PROMPT_PLACEHOLDER__`,
188
+ text: this.composeConsentPromptDM(),
187
189
  episodeId: ep,
188
190
  epoch: e.epoch,
189
191
  });
@@ -200,7 +202,7 @@ export class TunnelNotifier {
200
202
  messages.push({
201
203
  channel: 'owner-dm',
202
204
  class: 'state-change',
203
- text: `__OWNER_DM_RELAY_URL_PLACEHOLDER__`,
205
+ text: this.composeRelayActiveDM(),
204
206
  episodeId: ep,
205
207
  epoch: e.epoch,
206
208
  });
@@ -250,6 +252,38 @@ export class TunnelNotifier {
250
252
  return true;
251
253
  return this.clock.now() - lastAt >= this.stateChangeMinIntervalMs;
252
254
  }
255
+ // ── Owner-DM message composers ───────────────────────────────────
256
+ /** Snapshot of credentials at compose time (safely degrades when no provider). */
257
+ creds() {
258
+ if (!this.credentialProvider)
259
+ return { url: null };
260
+ try {
261
+ return this.credentialProvider();
262
+ }
263
+ catch {
264
+ return { url: null };
265
+ }
266
+ }
267
+ renderLink() {
268
+ const { url, pin } = this.creds();
269
+ if (!url)
270
+ return `(link not available yet — I'll send it as soon as the tunnel is up)`;
271
+ if (pin)
272
+ return `Link: ${url}\nPIN: ${pin}`;
273
+ return `Link: ${url}`;
274
+ }
275
+ composeRecoveredDM() {
276
+ return `Cloudflare is back. Your dashboard link:\n\n${this.renderLink()}`;
277
+ }
278
+ composeRestoredDM() {
279
+ return `Your permanent Cloudflare link is back — switched off the backup. New link:\n\n${this.renderLink()}\n\nIf you had your dashboard open, you may need to re-enter the PIN.`;
280
+ }
281
+ composeRelayActiveDM() {
282
+ return `Backup tunnel is up and serving your dashboard. New link below.\n\nHeads up: this routes through a third-party server, so your dashboard traffic is briefly going through their machines while Cloudflare is down. The agent will switch you back to your normal link automatically as soon as Cloudflare recovers.\n\n${this.renderLink()}`;
283
+ }
284
+ composeConsentPromptDM() {
285
+ return `Cloudflare is unavailable and I can't get you a dashboard link through the usual path.\n\nI can try a backup that routes through a third-party server. This means your dashboard PIN and any private view links would briefly be visible to whoever operates that backup while it's in use — and your auth token will be rotated after the backup is no longer needed, which will sign you out of any open dashboard tabs and invalidate any previously-shared private view links.\n\nReply "yes, use a backup" to approve, or "no" to keep waiting for Cloudflare. If you don't reply, I'll keep waiting for Cloudflare and won't use a backup.`;
286
+ }
253
287
  }
254
288
  function throttleKey(state, channel) {
255
289
  return `${state}::${channel}`;
@@ -1 +1 @@
1
- {"version":3,"file":"TunnelNotifier.js","sourceRoot":"","sources":["../../src/tunnel/TunnelNotifier.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AAyCH,MAAM,OAAO,cAAc;IACR,IAAI,CAAe;IACnB,KAAK,CAAgB;IACrB,wBAAwB,CAAS;IACjC,aAAa,CAAS;IAEvC,wEAAwE;IACxE,4DAA4D;IACpD,gBAAgB,GAAG,CAAC,CAAC,CAAC;IAE9B,2BAA2B;IACnB,gBAAgB,GAAkB,IAAI,CAAC;IAC/C;;;kDAG8C;IACtC,aAAa,GAAwB,IAAI,GAAG,EAAE,CAAC;IACvD;4DACwD;IAChD,uBAAuB,GAAG,KAAK,CAAC;IAChC,UAAU,GAAG,CAAC,CAAC;IACf,aAAa,GAAG,KAAK,CAAC;IAE9B,YAAY,IAA2B;QACrC,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC;QACtB,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC,KAAK,IAAI,EAAE,GAAG,EAAE,GAAG,EAAE,CAAC,IAAI,CAAC,GAAG,EAAE,EAAE,CAAC;QACrD,IAAI,CAAC,wBAAwB,GAAG,IAAI,CAAC,wBAAwB,IAAI,EAAE,GAAG,MAAM,CAAC;QAC7E,IAAI,CAAC,aAAa,GAAG,IAAI,CAAC,aAAa,IAAI,CAAC,CAAC;IAC/C,CAAC;IAED;;;OAGG;IACH,KAAK,CAAC,YAAY,CAAC,CAAkB;QACnC,oEAAoE;QACpE,qCAAqC;QACrC,IAAI,CAAC,CAAC,KAAK,IAAI,IAAI,CAAC,gBAAgB;YAAE,OAAO;QAC7C,IAAI,CAAC,gBAAgB,GAAG,CAAC,CAAC,KAAK,CAAC;QAEhC,6DAA6D;QAC7D,MAAM,YAAY,GAAG,CAAC,CAAC,OAAO,EAAE,SAAS,IAAI,IAAI,CAAC;QAClD,IAAI,YAAY,KAAK,IAAI,CAAC,gBAAgB,EAAE,CAAC;YAC3C,IAAI,CAAC,gBAAgB,GAAG,YAAY,CAAC;YACrC,IAAI,CAAC,aAAa,GAAG,IAAI,GAAG,EAAE,CAAC;YAC/B,IAAI,CAAC,uBAAuB,GAAG,KAAK,CAAC;YACrC,IAAI,CAAC,UAAU,GAAG,CAAC,CAAC;YACpB,IAAI,CAAC,aAAa,GAAG,KAAK,CAAC;QAC7B,CAAC;QAED,kDAAkD;QAClD,MAAM,QAAQ,GAAG,IAAI,CAAC,eAAe,CAAC,CAAC,CAAC,CAAC;QAEzC,KAAK,MAAM,CAAC,IAAI,QAAQ,EAAE,CAAC;YACzB,MAAM,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,CAAC,CAAC;YACpC,IAAI,CAAC,OAAO;gBAAE,SAAS;YACvB,IAAI,CAAC;gBACH,IAAI,CAAC,CAAC,OAAO,KAAK,OAAO,EAAE,CAAC;oBAC1B,MAAM,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;gBACpC,CAAC;qBAAM,CAAC;oBACN,MAAM,IAAI,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;gBACtC,CAAC;gBACD,gEAAgE;gBAChE,MAAM,GAAG,GAAG,WAAW,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,OAAO,CAAC,CAAC;gBACzC,IAAI,CAAC,aAAa,CAAC,GAAG,CAAC,GAAG,EAAE,IAAI,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,CAAC;gBAC9C,IAAI,CAAC,CAAC,KAAK,KAAK,OAAO,EAAE,CAAC;oBACxB,IAAI,CAAC,uBAAuB,GAAG,IAAI,CAAC;gBACtC,CAAC;YACH,CAAC;YAAC,MAAM,CAAC;gBACP,wDAAwD;gBACxD,6CAA6C;YAC/C,CAAC;QACH,CAAC;IACH,CAAC;IAED;;;;;;;;;OASG;IACK,eAAe,CAAC,CAAkB;QACxC,MAAM,EAAE,GAAG,CAAC,CAAC,OAAO,EAAE,SAAS,IAAI,IAAI,CAAC;QACxC,MAAM,MAAM,GAAG,CAAC,CAAC,iBAAiB,CAAC;QAEnC,MAAM,QAAQ,GAAsB,EAAE,CAAC;QACvC,QAAQ,CAAC,CAAC,EAAE,EAAE,CAAC;YACb,KAAK,UAAU;gBACb,oDAAoD;gBACpD,QAAQ,CAAC,IAAI,CAAC;oBACZ,OAAO,EAAE,OAAO;oBAChB,KAAK,EAAE,cAAc;oBACrB,IAAI,EAAE,6CAA6C,YAAY,CAAC,MAAM,CAAC,mBAAmB;oBAC1F,SAAS,EAAE,EAAE;oBACb,KAAK,EAAE,CAAC,CAAC,KAAK;iBACf,CAAC,CAAC;gBACH,4BAA4B;gBAC5B,IAAI,CAAC,UAAU,IAAI,CAAC,CAAC;gBACrB,IAAI,IAAI,CAAC,UAAU,IAAI,IAAI,CAAC,aAAa,IAAI,CAAC,IAAI,CAAC,aAAa,EAAE,CAAC;oBACjE,IAAI,CAAC,aAAa,GAAG,IAAI,CAAC;oBAC1B,QAAQ,CAAC,IAAI,CAAC;wBACZ,OAAO,EAAE,OAAO;wBAChB,KAAK,EAAE,OAAO;wBACd,IAAI,EAAE,wGAAwG;wBAC9G,SAAS,EAAE,EAAE;wBACb,KAAK,EAAE,CAAC,CAAC,KAAK;qBACf,CAAC,CAAC;gBACL,CAAC;gBACD,MAAM;YAER,KAAK,QAAQ;gBACX,IAAI,CAAC,CAAC,IAAI,KAAK,cAAc,IAAI,CAAC,CAAC,IAAI,KAAK,cAAc,EAAE,CAAC;oBAC3D,8BAA8B;oBAC9B,QAAQ,CAAC,IAAI,CAAC;wBACZ,OAAO,EAAE,OAAO;wBAChB,KAAK,EAAE,cAAc;wBACrB,IAAI,EAAE,mDAAmD;wBACzD,SAAS,EAAE,EAAE;wBACb,KAAK,EAAE,CAAC,CAAC,KAAK;qBACf,CAAC,CAAC;oBACH,QAAQ,CAAC,IAAI,CAAC;wBACZ,OAAO,EAAE,UAAU;wBACnB,KAAK,EAAE,cAAc;wBACrB,IAAI,EAAE,uCAAuC;wBAC7C,SAAS,EAAE,EAAE;wBACb,KAAK,EAAE,CAAC,CAAC,KAAK;qBACf,CAAC,CAAC;gBACL,CAAC;qBAAM,IAAI,CAAC,CAAC,IAAI,KAAK,UAAU,EAAE,CAAC;oBACjC,qCAAqC;oBACrC,QAAQ,CAAC,IAAI,CAAC;wBACZ,OAAO,EAAE,OAAO;wBAChB,KAAK,EAAE,cAAc;wBACrB,IAAI,EAAE,+BAA+B;wBACrC,SAAS,EAAE,EAAE;wBACb,KAAK,EAAE,CAAC,CAAC,KAAK;qBACf,CAAC,CAAC;oBACH,QAAQ,CAAC,IAAI,CAAC;wBACZ,OAAO,EAAE,UAAU;wBACnB,KAAK,EAAE,cAAc;wBACrB,IAAI,EAAE,wCAAwC;wBAC9C,SAAS,EAAE,EAAE;wBACb,KAAK,EAAE,CAAC,CAAC,KAAK;qBACf,CAAC,CAAC;gBACL,CAAC;gBACD,wFAAwF;gBACxF,MAAM;YAER,KAAK,kBAAkB;gBACrB,sDAAsD;gBACtD,QAAQ,CAAC,IAAI,CAAC;oBACZ,OAAO,EAAE,OAAO;oBAChB,KAAK,EAAE,cAAc;oBACrB,IAAI,EAAE,sEAAsE;oBAC5E,SAAS,EAAE,EAAE;oBACb,KAAK,EAAE,CAAC,CAAC,KAAK;iBACf,CAAC,CAAC;gBACH,QAAQ,CAAC,IAAI,CAAC;oBACZ,OAAO,EAAE,UAAU;oBACnB,KAAK,EAAE,iBAAiB;oBACxB,IAAI,EAAE,yCAAyC;oBAC/C,SAAS,EAAE,EAAE;oBACb,KAAK,EAAE,CAAC,CAAC,KAAK;iBACf,CAAC,CAAC;gBACH,MAAM;YAER,KAAK,cAAc;gBACjB,mEAAmE;gBACnE,QAAQ,CAAC,IAAI,CAAC;oBACZ,OAAO,EAAE,OAAO;oBAChB,KAAK,EAAE,cAAc;oBACrB,IAAI,EAAE,2CAA2C;oBACjD,SAAS,EAAE,EAAE;oBACb,KAAK,EAAE,CAAC,CAAC,KAAK;iBACf,CAAC,CAAC;gBACH,QAAQ,CAAC,IAAI,CAAC;oBACZ,OAAO,EAAE,UAAU;oBACnB,KAAK,EAAE,cAAc;oBACrB,IAAI,EAAE,oCAAoC;oBAC1C,SAAS,EAAE,EAAE;oBACb,KAAK,EAAE,CAAC,CAAC,KAAK;iBACf,CAAC,CAAC;gBACH,MAAM;YAER,KAAK,WAAW;gBACd,QAAQ,CAAC,IAAI,CAAC;oBACZ,OAAO,EAAE,OAAO;oBAChB,KAAK,EAAE,cAAc;oBACrB,IAAI,EAAE,2JAA2J;oBACjK,SAAS,EAAE,EAAE;oBACb,KAAK,EAAE,CAAC,CAAC,KAAK;iBACf,CAAC,CAAC;gBACH,MAAM;YAER,KAAK,cAAc;gBACjB,iEAAiE;gBACjE,0CAA0C;gBAC1C,MAAM;YAER,kDAAkD;YAClD,KAAK,MAAM,CAAC;YACZ,KAAK,UAAU;gBACb,MAAM;QACV,CAAC;QACD,OAAO,QAAQ,CAAC;IAClB,CAAC;IAED;;;;;;;;;;;OAWG;IACK,KAAK,CAAC,CAAkB,EAAE,OAA6B;QAC7D,IAAI,CAAC,CAAC,KAAK,KAAK,iBAAiB;YAAE,OAAO,IAAI,CAAC;QAC/C,IAAI,CAAC,CAAC,KAAK,KAAK,OAAO,EAAE,CAAC;YACxB,OAAO,CAAC,IAAI,CAAC,uBAAuB,CAAC;QACvC,CAAC;QACD,4CAA4C;QAC5C,MAAM,GAAG,GAAG,WAAW,CAAC,OAAO,EAAE,CAAC,CAAC,OAAO,CAAC,CAAC;QAC5C,MAAM,MAAM,GAAG,IAAI,CAAC,aAAa,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QAC3C,IAAI,MAAM,KAAK,SAAS;YAAE,OAAO,IAAI,CAAC;QACtC,OAAO,IAAI,CAAC,KAAK,CAAC,GAAG,EAAE,GAAG,MAAM,IAAI,IAAI,CAAC,wBAAwB,CAAC;IACpE,CAAC;CACF;AAED,SAAS,WAAW,CAAC,KAA2B,EAAE,OAA4B;IAC5E,OAAO,GAAG,KAAK,KAAK,OAAO,EAAE,CAAC;AAChC,CAAC;AAED,SAAS,YAAY,CAAC,MAAoC;IACxD,QAAQ,MAAM,EAAE,CAAC;QACf,KAAK,cAAc,CAAC,CAAC,OAAO,mCAAmC,CAAC;QAChE,KAAK,gBAAgB,CAAC,CAAC,OAAO,mCAAmC,CAAC;QAClE,KAAK,SAAS,CAAC,CAAC,OAAO,2BAA2B,CAAC;QACnD,KAAK,SAAS,CAAC,CAAC,OAAO,4BAA4B,CAAC;QACpD,KAAK,cAAc,CAAC,CAAC,OAAO,8BAA8B,CAAC;QAC3D,KAAK,qBAAqB,CAAC,CAAC,OAAO,6BAA6B,CAAC;QACjE,OAAO,CAAC,CAAC,OAAO,EAAE,CAAC;IACrB,CAAC;AACH,CAAC"}
1
+ {"version":3,"file":"TunnelNotifier.js","sourceRoot":"","sources":["../../src/tunnel/TunnelNotifier.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "instar",
3
- "version": "1.2.37",
3
+ "version": "1.2.39",
4
4
  "description": "Persistent autonomy infrastructure for AI agents",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "$schema": "./builtin-manifest.schema.json",
3
3
  "schemaVersion": 1,
4
- "generatedAt": "2026-05-23T01:00:34.909Z",
5
- "instarVersion": "1.2.37",
4
+ "generatedAt": "2026-05-23T02:07:34.437Z",
5
+ "instarVersion": "1.2.39",
6
6
  "entryCount": 191,
7
7
  "entries": {
8
8
  "hook:session-start": {
@@ -1064,7 +1064,7 @@
1064
1064
  "type": "template",
1065
1065
  "domain": "safety",
1066
1066
  "sourcePath": "src/templates/hooks/telegram-topic-context.sh",
1067
- "contentHash": "8a80976731dd5dd6b981834f0c4e18d495ee0efd350adc7b88a65c1aa6356b5d",
1067
+ "contentHash": "57cfb744bbffcd21f6290c5f38726b105b93258bf09cc539c24d66aebed9c40e",
1068
1068
  "since": "2025-01-01"
1069
1069
  },
1070
1070
  "template:convergence-check.sh": {
@@ -1440,7 +1440,7 @@
1440
1440
  "type": "subsystem",
1441
1441
  "domain": "server",
1442
1442
  "sourcePath": "src/server/AgentServer.ts",
1443
- "contentHash": "d8d019cc9691191bca75fabd916fa5a4400bb614abcd48b49afa4f650d533fdd",
1443
+ "contentHash": "0c4f63b37d72bc85415e464e181507b66bb247df1e903da2fe27c5d6539dab0f",
1444
1444
  "since": "2025-01-01"
1445
1445
  },
1446
1446
  "subsystem:session-manager": {
@@ -65,6 +65,25 @@ else
65
65
  "http://localhost:${PORT}/telegram/topics/${TOPIC_ID}/messages?limit=30" 2>/dev/null)
66
66
  fi
67
67
 
68
+ # Layer 2: prepend the topic intent briefing if anything has accumulated.
69
+ # Returns empty body when nothing tracked yet — we silently skip injection.
70
+ # Degrades open: any failure here (server down, route 503'd, network blip)
71
+ # leaves recent-history-only output unchanged.
72
+ TOPIC_BRIEFING=""
73
+ if [ -n "$AUTH_TOKEN" ]; then
74
+ TOPIC_BRIEFING=$(curl -s --max-time 2 \
75
+ -H "Authorization: Bearer ${AUTH_TOKEN}" \
76
+ "http://localhost:${PORT}/topic-intent/${TOPIC_ID}/briefing" 2>/dev/null)
77
+ else
78
+ TOPIC_BRIEFING=$(curl -s --max-time 2 \
79
+ "http://localhost:${PORT}/topic-intent/${TOPIC_ID}/briefing" 2>/dev/null)
80
+ fi
81
+
82
+ if [ -n "$TOPIC_BRIEFING" ]; then
83
+ echo "$TOPIC_BRIEFING"
84
+ echo ""
85
+ fi
86
+
68
87
  # Format and output context with unanswered message detection
69
88
  echo "$RECENT_MSGS" | python3 -c "
70
89
  import sys, json
@@ -0,0 +1,103 @@
1
+ # Upgrade Guide — vNEXT
2
+
3
+ <!-- bump: patch -->
4
+ <!-- patch = owner-DM channel + notifier sink wiring; no public API breakage -->
5
+
6
+ ## What Changed
7
+
8
+ **feat(tunnel,telegram): owner-DM channel + two-channel notifier wired into the tunnel manager.**
9
+
10
+ PR 3 of the tunnel-failure-resilience chain. Lands the owner-DM
11
+ messaging surface that the upcoming consent UX + relay providers
12
+ (PR 4) and credential rotation lifecycle (PR 5) depend on. Spec
13
+ `specs/dev-infrastructure/tunnel-failure-resilience.md` is converged
14
+ and approved on main.
15
+
16
+ What's new:
17
+
18
+ - `TelegramAdapter.ownerUserId` config field — the single Telegram
19
+ user id who controls security-sensitive decisions for this
20
+ instance (consent gate for backup tunnels, credential exposure).
21
+ Separate from the broader `authorizedUserIds` allowlist per the
22
+ GPT external review's CRITICAL finding that consent decisions
23
+ must not trust the broader set. Falls back to
24
+ `promptGate.ownerId` for back-compat when not set explicitly.
25
+ - `TelegramAdapter.getOwnerUserId()` — returns the configured owner
26
+ principal (or the back-compat fallback).
27
+ - `TelegramAdapter.sendToOwnerDM(text)` — sends a private DM to the
28
+ owner's direct bot chat. Failure modes ("no owner configured",
29
+ "owner hasn't messaged the bot yet", network error) log a warning
30
+ and return null; never throw into the caller. Telegram bots can
31
+ only initiate a DM after the user has messaged the bot at least
32
+ once, so an early forbidden response is expected and non-fatal.
33
+ - `TunnelManager.attachTelegram(adapter, dashboardPin)` — wires the
34
+ notifier sink. Group messages route to the Dashboard topic (with
35
+ Lifeline fallback); owner-DM messages route to
36
+ `sendToOwnerDM`. A credentialProvider callback returns the live
37
+ URL + current PIN at compose time, so the notifier never holds
38
+ stale credentials in its own state. `server.ts` calls
39
+ `attachTelegram` after the Dashboard topic is ensured.
40
+ - `TunnelNotifier` composers — the four owner-DM messages
41
+ (recovered, restored, consent prompt, relay activated) now render
42
+ real English text with credentials substituted. Group-channel
43
+ messages continue to carry status text only — credentials NEVER
44
+ appear in group messages.
45
+
46
+ What's NOT new yet (deliberate, future PRs in this chain):
47
+
48
+ - Inline-button consent UX + callback handler (PR 4).
49
+ - Tier-2 relay providers (localtunnel) + the actual consent-driven
50
+ transition into `relay-active` (PR 4).
51
+ - `authToken` / PIN rotation on relay-episode end + boot recovery
52
+ (PR 5).
53
+ - Full N-consecutive-success self-heal stability gate (PR 6).
54
+ - The auth-gated `/tunnel` route (PR 7).
55
+
56
+ The lifecycle state machine already supports all of these; the
57
+ manager just doesn't transition into the relevant states yet.
58
+
59
+ ## Evidence
60
+
61
+ 3 new unit tests in `tests/unit/tunnel-manager-rewrite.test.ts`:
62
+
63
+ - `attachTelegram` routes the "couldn't reach" group message to the
64
+ Dashboard topic id.
65
+ - `attachTelegram` falls back to the Lifeline topic when no Dashboard
66
+ topic is configured.
67
+ - `attachTelegram` owner-DM message carries the live URL and current
68
+ PIN (credential substitution at compose time).
69
+
70
+ 1 new + updated tests in `tests/unit/tunnel-notifier.test.ts`:
71
+
72
+ - The credential-snapshot test now uses a real credentialProvider and
73
+ asserts the URL + PIN appear in owner-DM messages AND that they
74
+ NEVER appear in group messages.
75
+ - New "renders a graceful 'link not available' placeholder when no
76
+ credentialProvider is wired" test for the test-injection /
77
+ partial-wire-up path.
78
+
79
+ All 71 tunnel-related unit tests pass (32 lifecycle + 10 providers +
80
+ 13 notifier + 19 manager). Typescript and lint clean.
81
+
82
+ ## What to Tell Your User
83
+
84
+ This release adds the plumbing for owner-only private messages from
85
+ the agent to you, separate from the group topics where everyone
86
+ sees the same messages. Nothing visible to you yet — the upcoming
87
+ release uses this plumbing to message you directly when Cloudflare
88
+ goes down and asks whether to use a backup. The release after that
89
+ uses the same channel to send you new dashboard credentials after a
90
+ backup tunnel finishes its work and your auth token rotates.
91
+
92
+ If you want to set the owner explicitly, the relevant config field
93
+ is your Telegram user id. The agent falls back to your prompt-gate
94
+ owner id if it's already set, so most existing installs don't need
95
+ to change anything.
96
+
97
+ ## Summary of New Capabilities
98
+
99
+ | Capability | How to Use |
100
+ |-----------|-----------|
101
+ | Owner-DM channel | Automatic — the agent picks up your owner id from existing config and uses it for security-sensitive messages |
102
+ | Live credential substitution in DMs | Automatic — when the tunnel sends you a link, the URL + PIN are always the current ones |
103
+ | Group-vs-DM channel separation | Structural — credentials never appear in group messages regardless of how the tunnel layer wires up |
@@ -0,0 +1,54 @@
1
+ # Upgrade Guide — vNEXT
2
+
3
+ <!-- bump: minor -->
4
+ <!-- minor = new feature, requires meaningful user-visible behavior addition -->
5
+
6
+ ## What Changed
7
+
8
+ **feat(topic-intent-layer): Layers 1-3 — per-topic confidence tracking + resume briefing + ArcCheck pre-send signal.**
9
+
10
+ A new three-layer feature that gives the agent a real sense of each conversation's arc. When you talk to the agent across many turns (or days, or after a context reset), it now keeps quiet track of what the topic is about, what's settled, and what's still tentative — without you ever typing a command. The agent does the bookkeeping in the background.
11
+
12
+ - **Layer 1** — a per-topic confidence tracker. Each candidate fact or decision the conversation establishes is tracked with confidence that rises with reinforcement (you re-reference, you affirm) and falls with contradiction or long silence. Authority requires user-authored evidence — the agent cannot silently promote its own guesses to "settled" by re-referencing itself.
13
+ - **Layer 2** — a briefing the agent reads at the top of every session resume. Telegram bootstrap automatically fetches it and prepends it to the recent-history block. Settled items shown unhedged; tentative items shown with confidence; observation tier (background noise) deliberately not surfaced.
14
+ - **Layer 3** — ArcCheck, a pre-send signal that fires when the agent's draft would either act on a tentative item without confirming OR contradict something already settled. Signal-only — the agent decides whether to redraft including a natural-language confirmation question. Never blocks.
15
+
16
+ Spec: `docs/specs/topic-intent-layer.md` (v14 CLEAN, approved 2026-05-22, 14 internal rounds plus 13 external rounds).
17
+
18
+ This build was also the test bed for evaluating whether to integrate GSD as a runtime dependency. Verdict: cherry-pick the methodologies into Instar primitives, do not try runtime composition. Comparison report archived at `.planning/SPIKE-FINDINGS.md` plus `.planning/phases/01-layer1/PLAN.md`.
19
+
20
+ ## Evidence
21
+
22
+ The original failure modes that motivated the feature are documented as canonical incidents in the spec — qalatra topic 9235 (arc drift across a multi-turn investigation) and GCI/Luna topic 365 (decision amnesia where the agent proposed redesigning a settled OAuth architecture mid-debugging, contradicting an earlier decision).
23
+
24
+ 96 tests across all three tiers, all green locally:
25
+ - Decay arithmetic at days 104, 105, 106 matches spec values to 3 decimal places.
26
+ - User-authored-episode authority gating clamps at 0.69 when no qualifying user-authored episode exists.
27
+ - Per-message dedup with larger-delta-wins on collision.
28
+ - Pending-confirm queue depth 3 with silent drop plus telemetry counter.
29
+ - Sharpen-retry up to 2 then abandoned.
30
+ - PII-safe diagnostics endpoint (raw evidence meta allowlist-filtered, never returns user message text).
31
+ - Framework-parity grep gate (zero Claude-Code or Codex-CLI specific tokens in any Layer 1/2/3 source).
32
+ - Tier-3 lifecycle test boots through the real production server path and observes tier transitions through HTTP.
33
+
34
+ Side-effects review artifact: `upgrades/side-effects/topic-intent-layer.md`.
35
+
36
+ ## What to Tell Your User
37
+
38
+ In long, multi-day, or multi-topic conversations, the agent stays on-point. It resumes knowing the goal. It stops re-litigating things you already settled together. When it is about to do something based on an assumption that might be wrong, it asks first in plain English, as part of its reply. When you correct it, it actually updates.
39
+
40
+ In short, simple conversations, nothing changes. The layer only kicks in once a conversation has substance worth tracking, and stays quiet otherwise.
41
+
42
+ No commands to learn. No configuration to set. The agent does the bookkeeping; you just talk.
43
+
44
+ A diagnostics view per conversation tells you exactly what the agent is tracking, how confident it is, what signals built that confidence, and whether anything is in conflict.
45
+
46
+ ## Summary of New Capabilities
47
+
48
+ - New core modules covering confidence tracking, pending-confirmation lifecycle, LLM-backed extraction, resume briefing rendering, and pre-send classifier.
49
+ - New HTTP routes for diagnostics, refs filter, pending state, telemetry, briefing, and ArcCheck.
50
+ - Telegram bootstrap hook auto-fetches the briefing on session resume. Degrades open on any failure.
51
+ - Production server boot path constructs the store and passes it to AgentServer. Mount unconditional (503 stub when store disabled).
52
+ - Out of v1: cross-conversation insight spreading, retroactive backfill of pre-shipped conversations, cross-machine CRDT collaborative state, automatic conversation renaming, rich dashboard editor, user-tunable confidence weights (operator may tune; end user cannot).
53
+
54
+ Layer 3's outbound-path wiring (the actual every-send-fires-ArcCheck plumbing into the tone-gate and response-review stack) is intentionally separated from this ship. The classifier IS the spec's Layer 3 mechanism; the rollout is best done with care under a dedicated follow-up since it touches the existing outbound gate authority.
@@ -0,0 +1,152 @@
1
+ # Side-effects review — owner-DM channel + notifier sink wiring (PR 3 of tunnel-failure-resilience chain)
2
+
3
+ **Scope (PR 3 of the chain):** Add the owner-DM messaging surface and
4
+ wire it into the tunnel manager's two-channel notifier. Spec
5
+ `specs/dev-infrastructure/tunnel-failure-resilience.md` is converged
6
+ + approved on main. Public API: `TelegramAdapter.getOwnerUserId()` and
7
+ `TelegramAdapter.sendToOwnerDM(text)` are new; `TunnelManager.attachTelegram()`
8
+ is new; the notifier now substitutes live URL + PIN credentials into
9
+ owner-DM messages via a `credentialProvider` callback.
10
+
11
+ **Files touched:**
12
+ - `src/messaging/TelegramAdapter.ts` — adds `ownerUserId?: number` to
13
+ `TelegramConfig`; adds `getOwnerUserId()` accessor (falls back to
14
+ `promptGate?.ownerId` for back-compat); adds `sendToOwnerDM(text)`
15
+ which posts to the owner's private bot chat via `sendMessage` with
16
+ `chat_id: ownerUserId`. The DM send is fire-and-forget: failure paths
17
+ ("no owner configured", "owner hasn't messaged the bot", network
18
+ error) log-not-throw and return `null` so callers can degrade
19
+ gracefully.
20
+ - `src/tunnel/TunnelNotifier.ts` — adds `credentialProvider?: () =>
21
+ CredentialSnapshot` to `TunnelNotifierOptions`. The four owner-DM
22
+ message variants (recovered, restored, consent prompt, relay
23
+ activated) now compose real English text with the credentials
24
+ substituted at compose time. The four group-channel variants
25
+ continue to carry status text only — credentials NEVER appear in
26
+ group messages.
27
+ - `src/tunnel/TunnelManager.ts` — new `attachTelegram(adapter,
28
+ dashboardPin)` method builds a notifier sink from a duck-typed
29
+ `TunnelMessagingAdapter` (group → Dashboard topic with Lifeline
30
+ fallback; owner DM → `sendToOwnerDM`) and a credentialProvider that
31
+ returns `{ url: this.url, pin: dashboardPin() }`. Holds the notifier
32
+ in a mutable field so callers can wire it AFTER tunnel construction
33
+ (the messaging adapter is constructed later in server.ts).
34
+ - `src/commands/server.ts` — calls `tunnel.attachTelegram(telegram,
35
+ () => liveConfig.get(...) || config.dashboardPin)` after the
36
+ Dashboard topic is ensured. The wire is non-fatal: if attachTelegram
37
+ throws, the catch around `ensureDashboardTopic` already swallows it.
38
+ - `tests/unit/tunnel-notifier.test.ts` — replaces the placeholder
39
+ assertions with real-credential assertions: a DM with a credential
40
+ provider contains the URL + PIN; a DM without a provider renders a
41
+ graceful "link not available yet" placeholder; group messages NEVER
42
+ contain credentials.
43
+ - `tests/unit/tunnel-manager-rewrite.test.ts` — 3 new tests for
44
+ `attachTelegram`: Dashboard-topic routing, Lifeline fallback when
45
+ Dashboard isn't ensured, owner-DM credential snapshot includes the
46
+ live URL and current PIN.
47
+
48
+ **Under-block**: None. The owner-DM channel adds NEW capability;
49
+ existing behavior is unchanged when no `ownerUserId` is configured
50
+ (`sendToOwnerDM` logs a warning and returns null; the notifier's DM
51
+ sink call is a no-op). Existing Telegram group flows are untouched.
52
+
53
+ **Over-block**: None — but note the existing
54
+ `config.promptGate?.ownerId` continues to serve as a back-compat
55
+ fallback when `ownerUserId` isn't set explicitly. Operators who used
56
+ `promptGate.ownerId` to mark their session-prompt owner will see that
57
+ same user receiving the tunnel-DM messages, which is consistent with
58
+ the spec's "owner principal" intent. New deployments should set
59
+ `ownerUserId` explicitly.
60
+
61
+ **Level-of-abstraction fit**:
62
+ - TunnelManager remains the single owner of the lifecycle; the
63
+ notifier sink is a thin pass-through to the messaging adapter.
64
+ - The `TunnelMessagingAdapter` duck-typed interface keeps the tunnel
65
+ module from importing `TelegramAdapter` directly — preserves the
66
+ layered architecture (tunnel does not depend on messaging type).
67
+ - Credential substitution happens in the notifier's composer methods.
68
+ The notifier never holds credentials in its own state; it calls
69
+ the provider at compose time so the credentials are always fresh.
70
+
71
+ **Signal vs authority**: Compliant. The credentialProvider returns a
72
+ SIGNAL (current URL + PIN); the notifier remains the AUTHORITY for
73
+ deciding when an owner-DM message goes out and what it says. The
74
+ sink is the routing layer (executes the send); the messaging adapter
75
+ is the platform layer. No new authority introduced at the wrong
76
+ layer.
77
+
78
+ **Interactions**:
79
+ - `TunnelManager` constructed in server.ts BEFORE telegram, so the
80
+ manager initially has no notifier. The wire-up happens AFTER
81
+ telegram + Dashboard topic are ready (one-shot call to
82
+ `attachTelegram`). Until then, transitions go through the lifecycle
83
+ but no user-facing messages fire — preserving the boot-time
84
+ ordering established in PR 2.
85
+ - The credentialProvider closes over `this._legacyState.url` and
86
+ the dashboardPin accessor; both are read at each compose, so a
87
+ PIN rotation (PR 5) will be visible to subsequent messages without
88
+ re-attaching.
89
+ - `sendToOwnerDM` failure modes are absorbed inside the adapter — the
90
+ sink wrapper never throws into the notifier path. Preserves the
91
+ fire-and-forget invariant.
92
+
93
+ **External surfaces**:
94
+ - New config field: `messaging.config.ownerUserId` (number). Optional.
95
+ Falls back to `messaging.config.promptGate.ownerId`. Documented in
96
+ the spec.
97
+ - No new API endpoint. No new CLI command.
98
+ - `TelegramAdapter.sendToOwnerDM(text)` is a new public method but
99
+ intended for in-process callers only (the tunnel manager is the
100
+ first caller; future PRs may add more).
101
+
102
+ **Migration parity**:
103
+ - No agent-installed file change in this PR. Existing agents pick up
104
+ the new behavior the moment they update — the `ownerUserId` field
105
+ remains optional with the documented fallback.
106
+ - A future PR in the chain (the ConfigDefaults / migration PR) will
107
+ add a ConfigDefaults entry for `ownerUserId` and a CLAUDE.md
108
+ template note so the agent surfaces the new capability
109
+ conversationally. For now the manager's behavior degrades
110
+ gracefully when the field is absent.
111
+
112
+ **Rollback cost**: Trivial. Revert four source files + the test
113
+ updates. The two new public methods (`getOwnerUserId`,
114
+ `sendToOwnerDM`, `attachTelegram`) become unused; the
115
+ `TunnelMessagingAdapter` interface becomes dead code. Nothing else
116
+ breaks.
117
+
118
+ **Tests**:
119
+ - 19/19 `tests/unit/tunnel-manager-rewrite.test.ts` (3 new for
120
+ `attachTelegram`).
121
+ - 13/13 `tests/unit/tunnel-notifier.test.ts` (placeholder assertions
122
+ replaced with credential assertions; 1 new "no-credential
123
+ gracefully renders 'link not available'" test).
124
+ - 32/32 `tests/unit/tunnel-lifecycle.test.ts` (no change).
125
+ - 10/10 `tests/unit/tunnel-providers.test.ts` (no change).
126
+ - `tsc --noEmit` clean. `npm run lint` clean.
127
+
128
+ **Decision-point inventory**:
129
+ 1. Owner principal modeled as `ownerUserId` separate from
130
+ `authorizedUserIds` (vs. reusing the broader set) — the GPT
131
+ external review specifically flagged that consent / credential
132
+ exposure decisions must NOT trust the broader authorized-users
133
+ set. A separate single-principal field is the structural fix.
134
+ 2. `sendToOwnerDM` returns null on failure (vs. throwing) — preserves
135
+ the fire-and-forget contract callers expect for outbound
136
+ messaging. The bot can't initiate a DM until the user has messaged
137
+ the bot first, so an early "forbidden" response is expected during
138
+ onboarding; treating it as fatal would block tunnel notifications
139
+ indefinitely.
140
+ 3. `attachTelegram` is a post-construction method (vs. constructor
141
+ parameter) — the messaging adapter is constructed later than the
142
+ tunnel manager in server.ts. Putting the sink in the constructor
143
+ would require restructuring boot order. The post-attach pattern
144
+ keeps the existing boot sequence and is testable.
145
+ 4. `TunnelMessagingAdapter` duck-typed interface (vs. direct
146
+ `TelegramAdapter` import) — keeps the tunnel module from depending
147
+ on the messaging layer's concrete types; future PRs can add Slack
148
+ / iMessage variants without modifying tunnel/.
149
+ 5. credentialProvider returns a snapshot (vs. holding the URL/PIN as
150
+ notifier state) — the notifier composes messages over time; a
151
+ stale PIN cached in notifier state would leak after rotation. The
152
+ provider pattern keeps credential freshness in one place.
@@ -0,0 +1,86 @@
1
+ # Side-Effects Review — Topic Intent Layer (Layers 1-3)
2
+
3
+ **PR:** https://github.com/JKHeadley/instar/pull/332
4
+ **Spec:** `docs/specs/topic-intent-layer.md` (v14 CLEAN, approved 2026-05-22)
5
+ **Author:** Echo · autonomous run · 2026-05-22
6
+ **Process:** Per `feedback_side_effects_review.md` — seven-dimension review before ship.
7
+
8
+ ## 1. Over-block
9
+
10
+ The feature could over-block if a hook surfaces tentative items as if settled, or if ArcCheck started blocking sends.
11
+
12
+ **Mitigations in place:**
13
+ - ArcCheck verdict shape never includes a `block` or `authority` field. Signal-only by spec and by test (`tests/unit/TopicIntent-arccheck.test.ts` asserts this).
14
+ - Tentative items in the Layer 2 briefing are explicitly hedged with the word "TENTATIVE" + confidence value. Settled items in a separate "SETTLED" section.
15
+ - Observation tier (< 0.30) is NOT surfaced in the briefing at all — no noise leak.
16
+ - The briefing endpoint returns empty body when nothing has accumulated, so bootstrap hooks can skip injection cleanly without distorting normal session start.
17
+
18
+ ## 2. Under-block
19
+
20
+ The feature could under-block if it lets a single LLM extraction silently launder its own guesses to authoritative tier (the canonical GCI failure mode).
21
+
22
+ **Mitigations in place:**
23
+ - Authority hard rule (spec section "Authority gating"): confidence cannot reach the authoritative tier (≥0.7) unless at least one evidence event qualifies as a user-authored episode. Enforced in `projectConfidence` with a clamp at 0.69.
24
+ - Per-message dedup: multiple signals about the same refId from the same source message count as ONE episode with the larger applicable delta — agent cannot fork its own message into multiple episodes.
25
+ - Affirmation safety: per-refId per-24h cap of 1 affirmation bonus, plus per-single-user-message max of 3 distinct refIds affirmed, defends against a single "yes" mass-promoting.
26
+ - Signal-specific caps: `agent-reref` capped at +0.05 cumulative, `extract-agent` at +0.10. Even an unbounded loop of agent-origin signals cannot accumulate to authority.
27
+
28
+ ## 3. Level-of-abstraction fit
29
+
30
+ Is the feature implemented at the right layer of the stack?
31
+
32
+ **Assessment:**
33
+ - Storage at `.instar/topic-intent/<topicId>.json` parallels the existing `.instar/topic-resume-map.json` convention — same level, same JSON-first approach, same file-per-topic granularity.
34
+ - Routes mounted via the same `createXxxRoutes` factory pattern as `worktreeRoutes`, `fileRoutes`, `machineRoutes` — consistent with the codebase's idiom.
35
+ - AgentServer options pattern mirrors existing optional dependencies (e.g., `worktreeManager?`). DI is uniform.
36
+ - Telegram bootstrap hook modification is minimal — one curl call with degrade-open semantics. Slack bootstrap hook is NOT modified (slack channel IDs are string-keyed, not numeric topic IDs; the cleanest adapter pattern for Slack is a follow-up).
37
+
38
+ ## 4. Signal-vs-authority compliance
39
+
40
+ Per `feedback_signal_vs_authority`: brittle/low-context filters detect signals; only higher-level intelligent gates with full context block.
41
+
42
+ **Assessment:**
43
+ - ArcCheck is the signal layer. It returns verdicts that include `kind`, `refId`, `currentTier`, `currentConfidence`, `reason`, `suggestedRewriteHint` — no authority claim.
44
+ - The agent itself is the authority layer. The agent reads the ArcCheck signal in its draft preparation and chooses to either redraft (with a conversational confirmation question) or proceed.
45
+ - The existing outbound gates (tone-gate, response-review, outbound-dedup) retain their blocking authority and are unaffected by this change.
46
+
47
+ ## 5. Cross-feature interactions
48
+
49
+ What other Instar systems could this affect?
50
+
51
+ - **CompactionSentinel** — the briefing prepended on every bootstrap may slightly increase context-window usage at session start. Mitigation: maxPerSection cap (default 8) + overflow note keeps it bounded; observation tier deliberately omitted. The briefing is empty body when nothing tracked.
52
+ - **Tone-gate / response-review** — Layer 3 does NOT integrate into the outbound path in this ship. The classifier exists and is reachable; the actual every-send wiring is a deliberate follow-up so we can think carefully about whether ArcCheck signals should be routed through the existing gate or augment them.
53
+ - **Telegram bootstrap hook** — modified, but failure modes are degrade-open (any error → empty briefing, recent-history output unchanged). The 2-second timeout (`curl --max-time 2`) bounds latency.
54
+ - **Slack bootstrap hook** — NOT modified. Slack channel IDs are string-keyed; topic-intent is numeric-keyed. Adapter pattern for Slack is a v1.1 follow-up.
55
+ - **iMessage / WhatsApp** — same situation as Slack, deferred.
56
+
57
+ ## 6. Rollback cost
58
+
59
+ If this needs to revert, what's the blast radius?
60
+
61
+ - All new files (`src/core/TopicIntent*.ts`, `src/server/topicIntentRoutes.ts`, all `tests/unit/TopicIntent-*.test.ts`, `tests/integration/topic-intent-routes.test.ts`, `tests/e2e/topic-intent-lifecycle.test.ts`) can be deleted without affecting any other system.
62
+ - Modified files (`src/server/AgentServer.ts`, `src/commands/server.ts`, `src/templates/hooks/telegram-topic-context.sh`) have small surgical additions that can be reverted independently — store construction, route mount, optional briefing-fetch with degrade-open.
63
+ - Storage at `.instar/topic-intent/<topicId>.json` is per-topic JSON files; deleting them has no effect on any other Instar state.
64
+ - No new dependencies in `package.json`. No DB migrations. No config-format changes.
65
+
66
+ **Net rollback cost: low.** A clean revert of the feature commit + deleting the storage directory is sufficient.
67
+
68
+ ## 7. Migration parity
69
+
70
+ Per `feedback_migration_parity_standard` (built into CLAUDE.md): changes to agent-installed files must reach existing agents via the update path.
71
+
72
+ **Files in this PR that need migration handling:**
73
+ - `src/templates/hooks/telegram-topic-context.sh` — built-in hook script. Per the Migration Parity Standard, built-in hooks are **always overwritten** on every migration run via `installBuiltinHooks()`. No additional migration code needed — the script will reach existing agents on their next `instar upgrade`.
74
+
75
+ **No other agent-installed files changed** in this PR. CLAUDE.md template is unchanged in this PR; Agent Awareness Standard update (so agents know about the new `/topic-intent/*` endpoints) is the recommended next follow-up before the cherry-pick v1.3.x project.
76
+
77
+ ## Conclusion
78
+
79
+ Ship. Seven-dimension review clean. Rollback cost low. No new external dependencies. All three test tiers green. PII boundary explicitly tested. Framework-parity grep gate in place.
80
+
81
+ Follow-up scoped for future work (in priority order):
82
+ 1. Layer 3 outbound-path wiring (the every-send ArcCheck integration with tone-gate / response-review).
83
+ 2. Slack / iMessage / WhatsApp bootstrap parity for Layer 2 briefing.
84
+ 3. CLAUDE.md template update for Agent Awareness Standard.
85
+ 4. Daily probe job that replays the qalatra-9235 and GCI/Luna-365 fixtures against the live extractor.
86
+ 5. The seven cherry-pick recommendations from the GSD spike (separate v1.3.x project).