@cortexkit/opencode-magic-context 0.31.3 → 0.31.5

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 (99) hide show
  1. package/dist/config/index.d.ts.map +1 -1
  2. package/dist/config/project-security.d.ts +16 -12
  3. package/dist/config/project-security.d.ts.map +1 -1
  4. package/dist/features/magic-context/dreamer/classify-prompt.d.ts +2 -2
  5. package/dist/features/magic-context/dreamer/classify-prompt.d.ts.map +1 -1
  6. package/dist/features/magic-context/dreamer/classify.d.ts +8 -0
  7. package/dist/features/magic-context/dreamer/classify.d.ts.map +1 -1
  8. package/dist/features/magic-context/dreamer/evaluate-smart-notes.d.ts.map +1 -1
  9. package/dist/features/magic-context/dreamer/lease.d.ts +1 -0
  10. package/dist/features/magic-context/dreamer/lease.d.ts.map +1 -1
  11. package/dist/features/magic-context/dreamer/manifest-parser.d.ts +7 -0
  12. package/dist/features/magic-context/dreamer/manifest-parser.d.ts.map +1 -0
  13. package/dist/features/magic-context/dreamer/map-memories-prompt.d.ts +2 -3
  14. package/dist/features/magic-context/dreamer/map-memories-prompt.d.ts.map +1 -1
  15. package/dist/features/magic-context/dreamer/map-memories.d.ts +8 -0
  16. package/dist/features/magic-context/dreamer/map-memories.d.ts.map +1 -1
  17. package/dist/features/magic-context/dreamer/promote-primers.d.ts.map +1 -1
  18. package/dist/features/magic-context/dreamer/retrospective-raw-provider.d.ts +5 -0
  19. package/dist/features/magic-context/dreamer/retrospective-raw-provider.d.ts.map +1 -1
  20. package/dist/features/magic-context/dreamer/verify-prompt.d.ts +2 -2
  21. package/dist/features/magic-context/dreamer/verify-prompt.d.ts.map +1 -1
  22. package/dist/features/magic-context/dreamer/verify.d.ts.map +1 -1
  23. package/dist/features/magic-context/memory/storage-memory-embeddings.d.ts +4 -0
  24. package/dist/features/magic-context/memory/storage-memory-embeddings.d.ts.map +1 -1
  25. package/dist/features/magic-context/memory/storage-memory.d.ts +12 -0
  26. package/dist/features/magic-context/memory/storage-memory.d.ts.map +1 -1
  27. package/dist/features/magic-context/message-index-async.d.ts.map +1 -1
  28. package/dist/features/magic-context/message-index.d.ts +6 -0
  29. package/dist/features/magic-context/message-index.d.ts.map +1 -1
  30. package/dist/features/magic-context/project-embedding-registry.d.ts.map +1 -1
  31. package/dist/features/magic-context/search.d.ts.map +1 -1
  32. package/dist/features/magic-context/smart-notes/capabilities.d.ts +1 -0
  33. package/dist/features/magic-context/smart-notes/capabilities.d.ts.map +1 -1
  34. package/dist/features/magic-context/smart-notes/compiler.d.ts +3 -2
  35. package/dist/features/magic-context/smart-notes/compiler.d.ts.map +1 -1
  36. package/dist/features/magic-context/smart-notes/runner.d.ts.map +1 -1
  37. package/dist/features/magic-context/smart-notes/sandbox-runner.d.ts +3 -2
  38. package/dist/features/magic-context/smart-notes/sandbox-runner.d.ts.map +1 -1
  39. package/dist/features/magic-context/smart-notes/schedule.d.ts.map +1 -1
  40. package/dist/features/magic-context/smart-notes/ssrf-guard.d.ts +17 -6
  41. package/dist/features/magic-context/smart-notes/ssrf-guard.d.ts.map +1 -1
  42. package/dist/features/magic-context/smart-notes/types.d.ts +6 -1
  43. package/dist/features/magic-context/smart-notes/types.d.ts.map +1 -1
  44. package/dist/features/magic-context/storage-db.d.ts.map +1 -1
  45. package/dist/features/magic-context/storage-meta-persisted.d.ts +8 -0
  46. package/dist/features/magic-context/storage-meta-persisted.d.ts.map +1 -1
  47. package/dist/features/magic-context/storage-meta.d.ts +1 -1
  48. package/dist/features/magic-context/storage-meta.d.ts.map +1 -1
  49. package/dist/features/magic-context/storage.d.ts +1 -1
  50. package/dist/features/magic-context/storage.d.ts.map +1 -1
  51. package/dist/features/magic-context/workspaces.d.ts +8 -0
  52. package/dist/features/magic-context/workspaces.d.ts.map +1 -1
  53. package/dist/hooks/magic-context/channel2-delivery.d.ts.map +1 -1
  54. package/dist/hooks/magic-context/compartment-runner.d.ts +2 -0
  55. package/dist/hooks/magic-context/compartment-runner.d.ts.map +1 -1
  56. package/dist/hooks/magic-context/read-session-chunk.d.ts.map +1 -1
  57. package/dist/hooks/magic-context/read-session-db.d.ts.map +1 -1
  58. package/dist/hooks/magic-context/read-session-true-raw-tokens.d.ts.map +1 -1
  59. package/dist/hooks/magic-context/tag-messages.d.ts +1 -0
  60. package/dist/hooks/magic-context/tag-messages.d.ts.map +1 -1
  61. package/dist/hooks/magic-context/todo-view.d.ts +27 -2
  62. package/dist/hooks/magic-context/todo-view.d.ts.map +1 -1
  63. package/dist/hooks/magic-context/transform-message-helpers.d.ts +6 -3
  64. package/dist/hooks/magic-context/transform-message-helpers.d.ts.map +1 -1
  65. package/dist/hooks/magic-context/wrapup-orchestrator.d.ts +1 -0
  66. package/dist/hooks/magic-context/wrapup-orchestrator.d.ts.map +1 -1
  67. package/dist/index.js +1039 -331
  68. package/dist/shared/assistant-message-extractor.d.ts +1 -0
  69. package/dist/shared/assistant-message-extractor.d.ts.map +1 -1
  70. package/dist/shared/redaction.d.ts.map +1 -1
  71. package/dist/shared/rpc-notifications.d.ts +21 -16
  72. package/dist/shared/rpc-notifications.d.ts.map +1 -1
  73. package/dist/shared/rpc-server.d.ts +1 -0
  74. package/dist/shared/rpc-server.d.ts.map +1 -1
  75. package/dist/shared/rpc-utils.d.ts +3 -1
  76. package/dist/shared/rpc-utils.d.ts.map +1 -1
  77. package/dist/tools/ctx-memory/tools.d.ts.map +1 -1
  78. package/dist/tools/ctx-search/tools.d.ts.map +1 -1
  79. package/dist/tools/ctx-search/types.d.ts +1 -1
  80. package/dist/tools/ctx-search/types.d.ts.map +1 -1
  81. package/dist/tui/data/notification-socket.d.ts +1 -0
  82. package/dist/tui/data/notification-socket.d.ts.map +1 -1
  83. package/dist/tui-compiled/badge-contrast.d.ts +46 -0
  84. package/dist/tui-compiled/badge-contrast.d.ts.map +1 -0
  85. package/dist/tui-compiled/data/context-db.d.ts +45 -0
  86. package/dist/tui-compiled/data/context-db.d.ts.map +1 -0
  87. package/dist/tui-compiled/data/notification-socket.d.ts +40 -0
  88. package/dist/tui-compiled/data/notification-socket.d.ts.map +1 -0
  89. package/package.json +3 -2
  90. package/src/shared/assistant-message-extractor.ts +20 -0
  91. package/src/shared/redaction.ts +1 -0
  92. package/src/shared/rpc-client.test.ts +164 -5
  93. package/src/shared/rpc-notifications.ts +73 -21
  94. package/src/shared/rpc-server.ts +69 -18
  95. package/src/shared/rpc-utils.ts +12 -2
  96. package/src/tui/data/notification-socket.test.ts +131 -0
  97. package/src/tui/data/notification-socket.ts +81 -16
  98. package/src/tui/entry.mjs +23 -3
  99. package/src/tui-compiled/data/notification-socket.ts +81 -16
@@ -1,2 +1,3 @@
1
1
  export declare function extractLatestAssistantText(messages: unknown): string | null;
2
+ export declare function hasLengthCappedOutput(value: unknown): boolean;
2
3
  //# sourceMappingURL=assistant-message-extractor.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"assistant-message-extractor.d.ts","sourceRoot":"","sources":["../../src/shared/assistant-message-extractor.ts"],"names":[],"mappings":"AAwDA,wBAAgB,0BAA0B,CAAC,QAAQ,EAAE,OAAO,GAAG,MAAM,GAAG,IAAI,CAiB3E"}
1
+ {"version":3,"file":"assistant-message-extractor.d.ts","sourceRoot":"","sources":["../../src/shared/assistant-message-extractor.ts"],"names":[],"mappings":"AAwDA,wBAAgB,0BAA0B,CAAC,QAAQ,EAAE,OAAO,GAAG,MAAM,GAAG,IAAI,CAiB3E;AAED,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAkB7D"}
@@ -1 +1 @@
1
- {"version":3,"file":"redaction.d.ts","sourceRoot":"","sources":["../../src/shared/redaction.ts"],"names":[],"mappings":"AAyEA,wBAAgB,WAAW,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAkChD;AAED,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAcxD;AA0ED,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAatD;AAED,wBAAgB,sBAAsB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAE5D;AAsBD,wBAAgB,4BAA4B,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAOlE;AAED,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,OAAO,EAAE,OAAO,GAAE,MAAM,EAAO,GAAG,OAAO,CAkBnF"}
1
+ {"version":3,"file":"redaction.d.ts","sourceRoot":"","sources":["../../src/shared/redaction.ts"],"names":[],"mappings":"AAyEA,wBAAgB,WAAW,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAkChD;AAED,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAcxD;AA0ED,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAatD;AAED,wBAAgB,sBAAsB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAE5D;AAsBD,wBAAgB,4BAA4B,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAOlE;AAED,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,OAAO,EAAE,OAAO,GAAE,MAAM,EAAO,GAAG,OAAO,CAmBnF"}
@@ -32,25 +32,30 @@ export declare function registerNotificationSink(sink: NotificationSink): () =>
32
32
  * At-least-once: a live push that the socket drops is re-delivered from the
33
33
  * queue on reconnect (pruned only when the client acks via `lastReceivedId`). */
34
34
  export declare function pushNotification(type: string, payload: Record<string, unknown>, sessionId?: string): void;
35
- /** Return pending notifications after acking the client's last received id.
35
+ export interface DrainNotificationsOptions {
36
+ /**
37
+ * Cursor for global notifications when a session-scoped client sends separate
38
+ * session and global watermarks.
39
+ */
40
+ globalLastReceivedId?: number;
41
+ /** Ack/drain only the named session, not global notifications. */
42
+ sessionOnly?: boolean;
43
+ /** Ack/drain only session-less global notifications. */
44
+ globalOnly?: boolean;
45
+ }
46
+ /** Return pending notifications after pruning only the scopes the client acked.
36
47
  *
37
- * Session scoping: when `sessionId` is provided, only notifications tagged for
38
- * that session (or session-less/global ones) are returned and pruned — a
39
- * notification tagged for a DIFFERENT session is never handed to this client
40
- * and is never pruned by this client's ack. This matters because the in-memory
41
- * queue is per-process but a TUI can end up bound to a process that also serves
42
- * OTHER sessions: e.g. opening OpenCode Desktop on the same project starts a
43
- * newer RPC server that the TUI's port discovery (newest-pid-wins) then selects,
44
- * so a Desktop-session upgrade-dialog action would otherwise surface in an
45
- * unrelated TUI session. Each client also tracks its own `lastReceivedId`, so a
46
- * global watermark prune would let session A's ack drop session B's still-unseen
47
- * notification — scoping the prune to the acking session prevents that too.
48
+ * Session-scoped and global notifications have independent cursors. A TUI can
49
+ * switch from session A to session B after handling a high id in A; that high
50
+ * watermark must never prune B's lower, still-unseen ids. Global notifications
51
+ * are also tracked separately so a global dialog does not become a session
52
+ * watermark. Legacy callers that omit options keep the original single-cursor
53
+ * behavior.
48
54
  *
49
55
  * Delivery is at-least-once (non-destructive return + prune-on-ack): a returned
50
- * notification stays queued until a later call acks it via a higher
51
- * `lastReceivedId`, so a dropped WS socket re-delivers the backlog on reconnect
52
- * (the client sends its `lastReceivedId` in the hello). */
53
- export declare function drainNotifications(lastReceivedId?: number, sessionId?: string): RpcNotification[];
56
+ * notification stays queued until a later call acks it via the matching scope's
57
+ * cursor, so a dropped WS socket re-delivers unhandled backlog on reconnect. */
58
+ export declare function drainNotifications(lastReceivedId?: number, sessionId?: string, options?: DrainNotificationsOptions): RpcNotification[];
54
59
  /** Whether a TUI client is connected via a live notification socket.
55
60
  * Now exact socket liveness (a registered WS sink), not a poll-drain timestamp.
56
61
  *
@@ -1 +1 @@
1
- {"version":3,"file":"rpc-notifications.d.ts","sourceRoot":"","sources":["../../src/shared/rpc-notifications.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,MAAM,WAAW,eAAe;IAC5B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACjC,SAAS,CAAC,EAAE,MAAM,CAAC;CACtB;AAKD;;;;;GAKG;AACH,MAAM,WAAW,gBAAgB;IAC7B,kEAAkE;IAClE,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,6DAA6D;IAC7D,IAAI,EAAE,CAAC,YAAY,EAAE,eAAe,KAAK,IAAI,CAAC;CACjD;AAYD,iFAAiF;AACjF,wBAAgB,wBAAwB,CAAC,IAAI,EAAE,gBAAgB,GAAG,MAAM,IAAI,CAK3E;AAcD;;;;kFAIkF;AAClF,wBAAgB,gBAAgB,CAC5B,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAChC,SAAS,CAAC,EAAE,MAAM,GACnB,IAAI,CAoCN;AAED;;;;;;;;;;;;;;;;;4DAiB4D;AAC5D,wBAAgB,kBAAkB,CAAC,cAAc,SAAI,EAAE,SAAS,CAAC,EAAE,MAAM,GAAG,eAAe,EAAE,CAe5F;AAED;;;;;;;;sEAQsE;AACtE,wBAAgB,cAAc,CAAC,SAAS,CAAC,EAAE,MAAM,GAAG,OAAO,CAO1D"}
1
+ {"version":3,"file":"rpc-notifications.d.ts","sourceRoot":"","sources":["../../src/shared/rpc-notifications.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,MAAM,WAAW,eAAe;IAC5B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACjC,SAAS,CAAC,EAAE,MAAM,CAAC;CACtB;AAKD;;;;;GAKG;AACH,MAAM,WAAW,gBAAgB;IAC7B,kEAAkE;IAClE,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,6DAA6D;IAC7D,IAAI,EAAE,CAAC,YAAY,EAAE,eAAe,KAAK,IAAI,CAAC;CACjD;AAYD,iFAAiF;AACjF,wBAAgB,wBAAwB,CAAC,IAAI,EAAE,gBAAgB,GAAG,MAAM,IAAI,CAK3E;AAcD;;;;kFAIkF;AAClF,wBAAgB,gBAAgB,CAC5B,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAChC,SAAS,CAAC,EAAE,MAAM,GACnB,IAAI,CAoCN;AAED,MAAM,WAAW,yBAAyB;IACtC;;;OAGG;IACH,oBAAoB,CAAC,EAAE,MAAM,CAAC;IAC9B,kEAAkE;IAClE,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,wDAAwD;IACxD,UAAU,CAAC,EAAE,OAAO,CAAC;CACxB;AAMD;;;;;;;;;;;iFAWiF;AACjF,wBAAgB,kBAAkB,CAC9B,cAAc,SAAI,EAClB,SAAS,CAAC,EAAE,MAAM,EAClB,OAAO,GAAE,yBAA8B,GACxC,eAAe,EAAE,CAqDnB;AAED;;;;;;;;sEAQsE;AACtE,wBAAgB,cAAc,CAAC,SAAS,CAAC,EAAE,MAAM,GAAG,OAAO,CAO1D"}
@@ -18,6 +18,7 @@ export declare class MagicContextRpcServer {
18
18
  private portFilePath;
19
19
  private portDir;
20
20
  private startedAt;
21
+ private readonly instanceId;
21
22
  /** Every authenticated WS socket, so dispose can close them all. */
22
23
  private sockets;
23
24
  private readonly token;
@@ -1 +1 @@
1
- {"version":3,"file":"rpc-server.d.ts","sourceRoot":"","sources":["../../src/shared/rpc-server.ts"],"names":[],"mappings":"AAqBA,KAAK,UAAU,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;AAiCxF;;;;;;;;;;;GAWG;AACH,qBAAa,qBAAqB;IAC9B,OAAO,CAAC,MAAM,CAA+B;IAC7C,OAAO,CAAC,IAAI,CAAK;IACjB,OAAO,CAAC,QAAQ,CAAiC;IACjD,OAAO,CAAC,YAAY,CAAS;IAC7B,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,SAAS,CAAc;IAC/B,oEAAoE;IACpE,OAAO,CAAC,OAAO,CAAsC;IAMrD,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAmC;gBAE7C,UAAU,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM;IAKjD,sCAAsC;IACtC,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,UAAU,GAAG,IAAI;IAIjD,6DAA6D;IACvD,KAAK,IAAI,OAAO,CAAC,MAAM,CAAC;IAmF9B,6EAA6E;IAC7E,IAAI,IAAI,IAAI;IAuBZ,OAAO,CAAC,uBAAuB;IAgB/B;+CAC2C;YAC7B,WAAW;IAuDzB;8EAC0E;IAC1E,OAAO,CAAC,eAAe;CA0D1B"}
1
+ {"version":3,"file":"rpc-server.d.ts","sourceRoot":"","sources":["../../src/shared/rpc-server.ts"],"names":[],"mappings":"AAqBA,KAAK,UAAU,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;AA0CxF;;;;;;;;;;;GAWG;AACH,qBAAa,qBAAqB;IAC9B,OAAO,CAAC,MAAM,CAA+B;IAC7C,OAAO,CAAC,IAAI,CAAK;IACjB,OAAO,CAAC,QAAQ,CAAiC;IACjD,OAAO,CAAC,YAAY,CAAS;IAC7B,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,SAAS,CAAc;IAC/B,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAkC;IAC7D,oEAAoE;IACpE,OAAO,CAAC,OAAO,CAAsC;IAMrD,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAmC;gBAE7C,UAAU,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM;IAKjD,sCAAsC;IACtC,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,UAAU,GAAG,IAAI;IAIjD,6DAA6D;IACvD,KAAK,IAAI,OAAO,CAAC,MAAM,CAAC;IAqF9B,6EAA6E;IAC7E,IAAI,IAAI,IAAI;IAuBZ,OAAO,CAAC,uBAAuB;IAgB/B;+CAC2C;YAC7B,WAAW;IAyDzB;8EAC0E;IAC1E,OAAO,CAAC,eAAe;CA+F1B"}
@@ -11,6 +11,8 @@ export interface RpcPortFileRecord {
11
11
  * auth required" only when the server itself didn't set one).
12
12
  */
13
13
  token?: string;
14
+ /** Per-server filename nonce; prevents same-process instances from sharing one file. */
15
+ instance_id?: string;
14
16
  }
15
17
  /**
16
18
  * Stable hash for a project directory — scopes RPC port files per-project
@@ -20,7 +22,7 @@ export declare function projectHash(directory: string): string;
20
22
  /** Directory containing per-process RPC discovery files for a project. */
21
23
  export declare function rpcPortDir(storageDir: string, directory: string): string;
22
24
  /** Per-process RPC port file path. */
23
- export declare function rpcPortFilePath(storageDir: string, directory: string, pid?: number): string;
25
+ export declare function rpcPortFilePath(storageDir: string, directory: string, pid?: number, instanceId?: string): string;
24
26
  /** Legacy single-port file used by v0.18.0 and earlier. */
25
27
  export declare function legacyRpcPortFilePath(storageDir: string, directory: string): string;
26
28
  export declare function isPidAlive(pid: number): boolean;
@@ -1 +1 @@
1
- {"version":3,"file":"rpc-utils.d.ts","sourceRoot":"","sources":["../../src/shared/rpc-utils.ts"],"names":[],"mappings":"AAGA,MAAM,WAAW,iBAAiB;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,MAAM,CAAC;IACZ,UAAU,EAAE,MAAM,CAAC;IACnB;;;;;;;OAOG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC;CAClB;AAED;;;GAGG;AACH,wBAAgB,WAAW,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CAGrD;AAED,0EAA0E;AAC1E,wBAAgB,UAAU,CAAC,UAAU,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM,CAExE;AAED,sCAAsC;AACtC,wBAAgB,eAAe,CAAC,UAAU,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,GAAG,SAAc,GAAG,MAAM,CAEhG;AAED,2DAA2D;AAC3D,wBAAgB,qBAAqB,CAAC,UAAU,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM,CAEnF;AAED,wBAAgB,UAAU,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAQ/C;AAED,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,MAAM,EAAE,WAAW,SAAI,GAAG,iBAAiB,GAAG,IAAI,CAyB3F"}
1
+ {"version":3,"file":"rpc-utils.d.ts","sourceRoot":"","sources":["../../src/shared/rpc-utils.ts"],"names":[],"mappings":"AAGA,MAAM,WAAW,iBAAiB;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,MAAM,CAAC;IACZ,UAAU,EAAE,MAAM,CAAC;IACnB;;;;;;;OAOG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,wFAAwF;IACxF,WAAW,CAAC,EAAE,MAAM,CAAC;CACxB;AAED;;;GAGG;AACH,wBAAgB,WAAW,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CAGrD;AAED,0EAA0E;AAC1E,wBAAgB,UAAU,CAAC,UAAU,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM,CAExE;AAED,sCAAsC;AACtC,wBAAgB,eAAe,CAC3B,UAAU,EAAE,MAAM,EAClB,SAAS,EAAE,MAAM,EACjB,GAAG,SAAc,EACjB,UAAU,CAAC,EAAE,MAAM,GACpB,MAAM,CAGR;AAED,2DAA2D;AAC3D,wBAAgB,qBAAqB,CAAC,UAAU,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM,CAEnF;AAED,wBAAgB,UAAU,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAQ/C;AAED,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,MAAM,EAAE,WAAW,SAAI,GAAG,iBAAiB,GAAG,IAAI,CA2B3F"}
@@ -1 +1 @@
1
- {"version":3,"file":"tools.d.ts","sourceRoot":"","sources":["../../../src/tools/ctx-memory/tools.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,cAAc,EAAQ,MAAM,qBAAqB,CAAC;AA0ChE,OAAO,EAKH,KAAK,iBAAiB,EACzB,MAAM,SAAS,CAAC;AAqqBjB,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,iBAAiB,GAAG,MAAM,CAAC,MAAM,EAAE,cAAc,CAAC,CAI5F"}
1
+ {"version":3,"file":"tools.d.ts","sourceRoot":"","sources":["../../../src/tools/ctx-memory/tools.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,cAAc,EAAQ,MAAM,qBAAqB,CAAC;AA0ChE,OAAO,EAKH,KAAK,iBAAiB,EACzB,MAAM,SAAS,CAAC;AAitBjB,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,iBAAiB,GAAG,MAAM,CAAC,MAAM,EAAE,cAAc,CAAC,CAI5F"}
@@ -1 +1 @@
1
- {"version":3,"file":"tools.d.ts","sourceRoot":"","sources":["../../../src/tools/ctx-search/tools.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,cAAc,EAAQ,MAAM,qBAAqB,CAAC;AAahE,OAAO,KAAK,EAAkC,iBAAiB,EAAE,MAAM,SAAS,CAAC;AAwOjF,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,iBAAiB,GAAG,MAAM,CAAC,MAAM,EAAE,cAAc,CAAC,CAI5F"}
1
+ {"version":3,"file":"tools.d.ts","sourceRoot":"","sources":["../../../src/tools/ctx-search/tools.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,cAAc,EAAQ,MAAM,qBAAqB,CAAC;AAahE,OAAO,KAAK,EAAkC,iBAAiB,EAAE,MAAM,SAAS,CAAC;AAyOjF,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,iBAAiB,GAAG,MAAM,CAAC,MAAM,EAAE,cAAc,CAAC,CAI5F"}
@@ -6,7 +6,7 @@ export type CtxSearchSource = "memory" | "message" | "git_commit" | "primer" | "
6
6
  export interface CtxSearchArgs {
7
7
  query: string;
8
8
  limit?: number;
9
- /** Restrict search to specific sources. Omit to search all. */
9
+ /** Restrict search to specific sources. Omit to search all; [] searches none. */
10
10
  sources?: CtxSearchSource[];
11
11
  }
12
12
  export interface CtxSearchToolDeps {
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/tools/ctx-search/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAC;AAEpD;;iEAEiE;AACjE,MAAM,MAAM,eAAe,GAAG,QAAQ,GAAG,SAAS,GAAG,YAAY,GAAG,QAAQ,GAAG,MAAM,CAAC;AAEtF,MAAM,WAAW,aAAa;IAC1B,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,+DAA+D;IAC/D,OAAO,CAAC,EAAE,eAAe,EAAE,CAAC;CAC/B;AAED,MAAM,WAAW,iBAAiB;IAC9B,EAAE,EAAE,QAAQ,CAAC;IACb,uBAAuB,CAAC,EAAE,CAAC,SAAS,EAAE,MAAM,EAAE,EAAE,EAAE,QAAQ,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC7E;;;;;OAKG;IACH,kBAAkB,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,MAAM,CAAC;IAClD,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,0EAA0E;IAC1E,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B,8EAA8E;IAC9E,YAAY,CAAC,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,KAAK,CAAC;QACxC,OAAO,EAAE,MAAM,CAAC;QAChB,EAAE,EAAE,MAAM,CAAC;QACX,IAAI,EAAE,MAAM,CAAC;QACb,KAAK,EAAE,OAAO,EAAE,CAAC;KACpB,CAAC,CAAC;CACN"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/tools/ctx-search/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAC;AAEpD;;iEAEiE;AACjE,MAAM,MAAM,eAAe,GAAG,QAAQ,GAAG,SAAS,GAAG,YAAY,GAAG,QAAQ,GAAG,MAAM,CAAC;AAEtF,MAAM,WAAW,aAAa;IAC1B,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,iFAAiF;IACjF,OAAO,CAAC,EAAE,eAAe,EAAE,CAAC;CAC/B;AAED,MAAM,WAAW,iBAAiB;IAC9B,EAAE,EAAE,QAAQ,CAAC;IACb,uBAAuB,CAAC,EAAE,CAAC,SAAS,EAAE,MAAM,EAAE,EAAE,EAAE,QAAQ,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC7E;;;;;OAKG;IACH,kBAAkB,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,MAAM,CAAC;IAClD,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,0EAA0E;IAC1E,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B,8EAA8E;IAC9E,YAAY,CAAC,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,KAAK,CAAC;QACxC,OAAO,EAAE,MAAM,CAAC;QAChB,EAAE,EAAE,MAAM,CAAC;QACX,IAAI,EAAE,MAAM,CAAC;QACb,KAAK,EAAE,OAAO,EAAE,CAAC;KACpB,CAAC,CAAC;CACN"}
@@ -35,5 +35,6 @@ interface NotificationSocketOptions {
35
35
  export declare function startNotificationSocket(options: NotificationSocketOptions): void;
36
36
  /** Close the socket and stop reconnecting. Call on TUI dispose. */
37
37
  export declare function stopNotificationSocket(): void;
38
+ export declare function _resetNotificationSocketStateForTesting(): void;
38
39
  export {};
39
40
  //# sourceMappingURL=notification-socket.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"notification-socket.d.ts","sourceRoot":"","sources":["../../../src/tui/data/notification-socket.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAIH,MAAM,WAAW,kBAAkB;IAC/B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACjC,SAAS,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,UAAU,yBAAyB;IAC/B,8EAA8E;IAC9E,YAAY,EAAE,MAAM,MAAM,GAAG,IAAI,CAAC;IAClC;+EAC2E;IAC3E,cAAc,EAAE,CAAC,YAAY,EAAE,kBAAkB,KAAK,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;CACpF;AAsBD;wDACwD;AACxD,wBAAgB,uBAAuB,CAAC,OAAO,EAAE,yBAAyB,GAAG,IAAI,CAQhF;AAED,mEAAmE;AACnE,wBAAgB,sBAAsB,IAAI,IAAI,CAkB7C"}
1
+ {"version":3,"file":"notification-socket.d.ts","sourceRoot":"","sources":["../../../src/tui/data/notification-socket.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAIH,MAAM,WAAW,kBAAkB;IAC/B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACjC,SAAS,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,UAAU,yBAAyB;IAC/B,8EAA8E;IAC9E,YAAY,EAAE,MAAM,MAAM,GAAG,IAAI,CAAC;IAClC;+EAC2E;IAC3E,cAAc,EAAE,CAAC,YAAY,EAAE,kBAAkB,KAAK,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;CACpF;AAkCD;wDACwD;AACxD,wBAAgB,uBAAuB,CAAC,OAAO,EAAE,yBAAyB,GAAG,IAAI,CAQhF;AAED,mEAAmE;AACnE,wBAAgB,sBAAsB,IAAI,IAAI,CAkB7C;AA6KD,wBAAgB,uCAAuC,IAAI,IAAI,CAM9D"}
@@ -0,0 +1,46 @@
1
+ /**
2
+ * Pick the text color for the sidebar header badge (a bold label drawn on a
3
+ * `theme.accent` background).
4
+ *
5
+ * Primary rule (matches AFT's sidebar by construction): paint the theme's own
6
+ * `background` color as the label, the inverse-of-panel look. Because it is a
7
+ * fixed theme token rather than an accent-derived computation, MC's badge and
8
+ * AFT's badge agree on EVERY accent automatically, so the same theme can never
9
+ * make one badge black and the other white (issue #198).
10
+ *
11
+ * Fallback rule (the reason a luminance pick exists at all): `theme.background`
12
+ * can be unusable as a label color in two degenerate cases, where it would
13
+ * render the label invisible on the accent:
14
+ * 1. Transparent background. Themes that respect terminal transparency set
15
+ * `background: "none"`, which resolves to `RGBA(0,0,0,0)`; drawing it as
16
+ * text renders fully transparent and the label disappears (issue #186).
17
+ * 2. Background ~= accent. If the theme's background and accent are nearly the
18
+ * same color, background-on-accent text has no contrast.
19
+ * In either case we fall back to a black/white pick that is guaranteed visible
20
+ * on the always-opaque accent.
21
+ *
22
+ * `RGBA` channels from @opentui/core are normalized 0..1 floats (alpha included).
23
+ * We accept the minimal `{ r, g, b, a? }` shape so this stays a pure, trivially
24
+ * testable function independent of the native color class, and we return the
25
+ * passed-in `background` object unchanged on the primary path so it stays the
26
+ * exact same theme token AFT uses.
27
+ */
28
+ type Color = {
29
+ r: number;
30
+ g: number;
31
+ b: number;
32
+ a?: number;
33
+ };
34
+ /**
35
+ * Pure black/white pick by accent luminance. Used as the badge fallback and kept
36
+ * exported for callers that only have the accent.
37
+ */
38
+ export declare function readableTextColorOn(bg: Color): string;
39
+ /**
40
+ * Badge label color on the accent: the theme background (AFT parity) when it is
41
+ * usable, else a guaranteed-visible black/white fallback. Returns the passed-in
42
+ * `background` reference unchanged on the primary path.
43
+ */
44
+ export declare function badgeTextColor<T extends Color>(accent: T, background: T): T | string;
45
+ export {};
46
+ //# sourceMappingURL=badge-contrast.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"badge-contrast.d.ts","sourceRoot":"","sources":["../../src/tui-compiled/badge-contrast.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AAEH,KAAK,KAAK,GAAG;IAAE,CAAC,EAAE,MAAM,CAAC;IAAC,CAAC,EAAE,MAAM,CAAC;IAAC,CAAC,EAAE,MAAM,CAAC;IAAC,CAAC,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC;AAoC7D;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,EAAE,EAAE,KAAK,GAAG,MAAM,CAErD;AAED;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,CAAC,SAAS,KAAK,EAAE,MAAM,EAAE,CAAC,EAAE,UAAU,EAAE,CAAC,GAAG,CAAC,GAAG,MAAM,CAMpF"}
@@ -0,0 +1,45 @@
1
+ import { MagicContextRpcClient } from "../../shared/rpc-client";
2
+ import type { EmbedDetail, SidebarSnapshot, StatusDetail } from "../../shared/rpc-types";
3
+ export type { EmbedDetail, SidebarSnapshot, StatusDetail };
4
+ /** Initialize the RPC client. Call once on TUI startup. */
5
+ export declare function initRpcClient(directory: string): void;
6
+ export declare function getRpcGeneration(): number;
7
+ /** The live RPC client (for the WS notification socket's endpoint discovery).
8
+ * Null before init / after close. */
9
+ export declare function getRpcClient(): MagicContextRpcClient | null;
10
+ /** Clean up the RPC client. */
11
+ export declare function closeRpc(): void;
12
+ /** Fetch sidebar snapshot from the server via RPC. */
13
+ export declare function loadSidebarSnapshot(sessionId: string, directory: string): Promise<SidebarSnapshot>;
14
+ /** Fetch full status detail from the server via RPC. */
15
+ export declare function loadStatusDetail(sessionId: string, directory: string, modelKey?: string): Promise<StatusDetail>;
16
+ /** Fetch embedding coverage status for `/ctx-embed` via RPC. */
17
+ export declare function loadEmbedDetail(sessionId: string, directory: string): Promise<EmbedDetail>;
18
+ /** Get compartment count via RPC. */
19
+ export declare function getCompartmentCount(sessionId: string): Promise<number>;
20
+ /** Send recomp request to server via RPC. */
21
+ export declare function requestRecomp(sessionId: string): Promise<boolean>;
22
+ /** Run `/ctx-session-upgrade` for the session (full recomp + once-per-project
23
+ * memory migration). Fired from the upgrade dialog's "Run upgrade now" action. */
24
+ export declare function requestUpgrade(sessionId: string): Promise<boolean>;
25
+ /** Mark the upgrade reminder dismissed (the user made an explicit Confirm/Cancel
26
+ * choice), setting the durable stamp so the FRESH dialog won't re-show. Resume
27
+ * prompts are staging-driven and unaffected. */
28
+ export declare function dismissUpgradeReminder(sessionId: string): Promise<boolean>;
29
+ /** Resolve global toast duration from server config via RPC. */
30
+ export declare function loadToastDurationMs(): Promise<number>;
31
+ /**
32
+ * Fetch the current startup announcement from the server, if any.
33
+ * Returns `{show: false}` when there's nothing to announce or when the
34
+ * configured ANNOUNCEMENT_VERSION has already been dismissed.
35
+ */
36
+ export interface AnnouncementResponse {
37
+ show: boolean;
38
+ version?: string;
39
+ features?: string[];
40
+ footer?: string;
41
+ }
42
+ export declare function getAnnouncement(): Promise<AnnouncementResponse>;
43
+ /** Mark the current ANNOUNCEMENT_VERSION as dismissed on the server. */
44
+ export declare function markAnnounced(): Promise<boolean>;
45
+ //# sourceMappingURL=context-db.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"context-db.d.ts","sourceRoot":"","sources":["../../../src/tui-compiled/data/context-db.ts"],"names":[],"mappings":"AAMA,OAAO,EAAE,qBAAqB,EAAE,MAAM,yBAAyB,CAAC;AAChE,OAAO,KAAK,EAAE,WAAW,EAAE,eAAe,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAC;AAEzF,YAAY,EAAE,WAAW,EAAE,eAAe,EAAE,YAAY,EAAE,CAAC;AAc3D,2DAA2D;AAC3D,wBAAgB,aAAa,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI,CAOrD;AAED,wBAAgB,gBAAgB,IAAI,MAAM,CAEzC;AAED;sCACsC;AACtC,wBAAgB,YAAY,IAAI,qBAAqB,GAAG,IAAI,CAE3D;AAED,+BAA+B;AAC/B,wBAAgB,QAAQ,IAAI,IAAI,CAM/B;AA2FD,sDAAsD;AACtD,wBAAsB,mBAAmB,CACrC,SAAS,EAAE,MAAM,EACjB,SAAS,EAAE,MAAM,GAClB,OAAO,CAAC,eAAe,CAAC,CA4B1B;AAED,wDAAwD;AACxD,wBAAsB,gBAAgB,CAClC,SAAS,EAAE,MAAM,EACjB,SAAS,EAAE,MAAM,EACjB,QAAQ,CAAC,EAAE,MAAM,GAClB,OAAO,CAAC,YAAY,CAAC,CA0CvB;AAYD,gEAAgE;AAChE,wBAAsB,eAAe,CAAC,SAAS,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,CAAC,CAchG;AAED,qCAAqC;AACrC,wBAAsB,mBAAmB,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAQ5E;AAED,6CAA6C;AAC7C,wBAAsB,aAAa,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAQvE;AAED;mFACmF;AACnF,wBAAsB,cAAc,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAQxE;AAED;;iDAEiD;AACjD,wBAAsB,sBAAsB,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAUhF;AAED,gEAAgE;AAChE,wBAAsB,mBAAmB,IAAI,OAAO,CAAC,MAAM,CAAC,CAQ3D;AAED;;;;GAIG;AACH,MAAM,WAAW,oBAAoB;IACjC,IAAI,EAAE,OAAO,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;IACpB,MAAM,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,wBAAsB,eAAe,IAAI,OAAO,CAAC,oBAAoB,CAAC,CAkBrE;AAED,wEAAwE;AACxE,wBAAsB,aAAa,IAAI,OAAO,CAAC,OAAO,CAAC,CAQtD"}
@@ -0,0 +1,40 @@
1
+ /**
2
+ * Persistent WebSocket to the server plugin's RPC server, replacing the old
3
+ * 500ms HTTP notification poll.
4
+ *
5
+ * Why this exists: the TUI plugin and the server plugin run in separate Bun
6
+ * runners in the same process, so they bridge over a localhost socket. The old
7
+ * bridge polled `pending-notifications` over HTTP every 500ms — and each poll
8
+ * opened a NEW loopback TCP connection (Bun's fetch isn't pooled to our server),
9
+ * which was the entire source of idle TUI CPU (#200). A single long-lived WS
10
+ * carries server→TUI pushes with zero per-event connection cost, and the server
11
+ * pushes notifications the instant they're queued (no polling latency).
12
+ *
13
+ * Session scope: the socket carries the TUI's active session in its `hello` so
14
+ * the server delivers only that session's (plus global) notifications and its
15
+ * `isTuiConnected(session)` routing stays correct. The active session is tracked
16
+ * with a cheap watcher that only reads `api.route.current` (a property access,
17
+ * no IPC) and re-scopes the socket ONLY when the session actually changes — so
18
+ * unlike the old poll it does no network work at idle.
19
+ */
20
+ export interface SocketNotification {
21
+ id: number;
22
+ type: string;
23
+ payload: Record<string, unknown>;
24
+ sessionId?: string;
25
+ }
26
+ interface NotificationSocketOptions {
27
+ /** Current active session id (re-read cheaply to follow session switches). */
28
+ getSessionId: () => string | null;
29
+ /** Handle one delivered notification. Returns true if it was consumed (so its
30
+ * id can advance the ack cursor). Async because dialog handlers await. */
31
+ onNotification: (notification: SocketNotification) => boolean | Promise<boolean>;
32
+ }
33
+ /** Open the persistent notification socket. Idempotent: a second call while open
34
+ * is a no-op. Reconnects on its own after any drop. */
35
+ export declare function startNotificationSocket(options: NotificationSocketOptions): void;
36
+ /** Close the socket and stop reconnecting. Call on TUI dispose. */
37
+ export declare function stopNotificationSocket(): void;
38
+ export declare function _resetNotificationSocketStateForTesting(): void;
39
+ export {};
40
+ //# sourceMappingURL=notification-socket.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"notification-socket.d.ts","sourceRoot":"","sources":["../../../src/tui-compiled/data/notification-socket.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAIH,MAAM,WAAW,kBAAkB;IAC/B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACjC,SAAS,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,UAAU,yBAAyB;IAC/B,8EAA8E;IAC9E,YAAY,EAAE,MAAM,MAAM,GAAG,IAAI,CAAC;IAClC;+EAC2E;IAC3E,cAAc,EAAE,CAAC,YAAY,EAAE,kBAAkB,KAAK,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;CACpF;AAkCD;wDACwD;AACxD,wBAAgB,uBAAuB,CAAC,OAAO,EAAE,yBAAyB,GAAG,IAAI,CAQhF;AAED,mEAAmE;AACnE,wBAAgB,sBAAsB,IAAI,IAAI,CAkB7C;AA6KD,wBAAgB,uCAAuC,IAAI,IAAI,CAM9D"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cortexkit/opencode-magic-context",
3
- "version": "0.31.3",
3
+ "version": "0.31.5",
4
4
  "type": "module",
5
5
  "description": "OpenCode plugin for Magic Context — cross-session memory and context management",
6
6
  "main": "dist/index.js",
@@ -30,8 +30,9 @@
30
30
  "README.md"
31
31
  ],
32
32
  "scripts": {
33
- "build": "bun build src/index.ts --outdir dist --target node --format esm --external @opencode-ai/plugin --external @huggingface/transformers --external onnxruntime-web --external bun:sqlite --external node:sqlite && tsc --emitDeclarationOnly",
33
+ "build": "bun run build:tui && bun build src/index.ts --outdir dist --target node --format esm --external @opencode-ai/plugin --external @huggingface/transformers --external onnxruntime-web --external bun:sqlite --external node:sqlite && tsc --emitDeclarationOnly",
34
34
  "build:tui": "bun scripts/build-tui.ts",
35
+ "check:tui-compiled": "bun run build:tui && git diff --exit-code -- src/tui-compiled && test -z \"$(git status --porcelain -- src/tui-compiled)\"",
35
36
  "typecheck": "tsc --noEmit && tsc -p tsconfig.scripts.json",
36
37
  "test": "bun test",
37
38
  "lint": "biome check .",
@@ -72,3 +72,23 @@ export function extractLatestAssistantText(messages: unknown): string | null {
72
72
  .join("\n") || null
73
73
  );
74
74
  }
75
+
76
+ export function hasLengthCappedOutput(value: unknown): boolean {
77
+ if (Array.isArray(value)) return value.some((item) => hasLengthCappedOutput(item));
78
+ if (!isRecord(value)) return false;
79
+
80
+ if (value.length_capped === true || value.lengthCapped === true) return true;
81
+ const finishReason = value.finish_reason ?? value.finishReason;
82
+ if (typeof finishReason === "string") {
83
+ const normalized = finishReason.toLowerCase();
84
+ if (
85
+ normalized === "length" ||
86
+ normalized === "max_tokens" ||
87
+ normalized === "max_output_tokens"
88
+ ) {
89
+ return true;
90
+ }
91
+ }
92
+
93
+ return Object.values(value).some((item) => hasLengthCappedOutput(item));
94
+ }
@@ -244,6 +244,7 @@ export function hasShareabilitySensitiveText(text: string): boolean {
244
244
  }
245
245
 
246
246
  export function sanitizeConfigValue(value: unknown, keyPath: string[] = []): unknown {
247
+ if (value === null || typeof value === "number" || typeof value === "boolean") return value;
247
248
  const key = keyPath.at(-1) ?? "";
248
249
  if (key && isSecretKey(key)) {
249
250
  return `<REDACTED:${redactionTypeForKey(key)}>`;
@@ -1,11 +1,12 @@
1
1
  import { afterEach, describe, expect, test } from "bun:test";
2
- import { mkdirSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from "node:fs";
2
+ import { mkdirSync, mkdtempSync, readdirSync, readFileSync, rmSync, writeFileSync } from "node:fs";
3
3
  import { createServer } from "node:http";
4
4
  import { tmpdir } from "node:os";
5
5
  import { dirname, join } from "node:path";
6
6
  import { MagicContextRpcClient } from "./rpc-client";
7
+ import { drainNotifications, isTuiConnected, pushNotification } from "./rpc-notifications";
7
8
  import { MagicContextRpcServer } from "./rpc-server";
8
- import { parseRpcPortFile, rpcPortFilePath } from "./rpc-utils";
9
+ import { parseRpcPortFile, type RpcPortFileRecord, rpcPortDir, rpcPortFilePath } from "./rpc-utils";
9
10
 
10
11
  interface TestServer {
11
12
  port: number;
@@ -56,6 +57,78 @@ function writePortFileForPid(
56
57
  writeFileSync(portFile, JSON.stringify({ port, pid, started_at: startedAt }), "utf-8");
57
58
  }
58
59
 
60
+ function readNewestPortRecord(storageDir: string, directory: string): RpcPortFileRecord | null {
61
+ const records: RpcPortFileRecord[] = [];
62
+ for (const entry of readdirSync(rpcPortDir(storageDir, directory))) {
63
+ if (!entry.startsWith("port-") || !entry.endsWith(".json")) continue;
64
+ const record = parseRpcPortFile(
65
+ readFileSync(join(rpcPortDir(storageDir, directory), entry), "utf-8"),
66
+ );
67
+ if (record) records.push(record);
68
+ }
69
+ records.sort((a, b) => b.started_at - a.started_at);
70
+ return records[0] ?? null;
71
+ }
72
+
73
+ async function waitFor(condition: () => boolean, label: string, timeoutMs = 2_000): Promise<void> {
74
+ const start = Date.now();
75
+ while (Date.now() - start < timeoutMs) {
76
+ if (condition()) return;
77
+ await new Promise((resolve) => setTimeout(resolve, 25));
78
+ }
79
+ throw new Error(`Timed out waiting for ${label}`);
80
+ }
81
+
82
+ async function openSocket(port: number, token: string): Promise<WebSocket> {
83
+ const ws = new WebSocket(`ws://127.0.0.1:${port}/ws?token=${encodeURIComponent(token)}`);
84
+ await new Promise<void>((resolve, reject) => {
85
+ const timeout = setTimeout(() => reject(new Error("socket open timed out")), 2_000);
86
+ ws.addEventListener(
87
+ "open",
88
+ () => {
89
+ clearTimeout(timeout);
90
+ resolve();
91
+ },
92
+ { once: true },
93
+ );
94
+ ws.addEventListener(
95
+ "error",
96
+ () => {
97
+ clearTimeout(timeout);
98
+ reject(new Error("socket open failed"));
99
+ },
100
+ { once: true },
101
+ );
102
+ });
103
+ return ws;
104
+ }
105
+
106
+ function waitForJsonMessage<T extends { type?: string }>(
107
+ ws: WebSocket,
108
+ predicate: (message: T) => boolean,
109
+ timeoutMs = 2_000,
110
+ ): Promise<T> {
111
+ return new Promise((resolve, reject) => {
112
+ const timeout = setTimeout(() => {
113
+ ws.removeEventListener("message", onMessage);
114
+ reject(new Error("socket message timed out"));
115
+ }, timeoutMs);
116
+ const onMessage = (event: MessageEvent) => {
117
+ let message: T;
118
+ try {
119
+ message = JSON.parse(String(event.data)) as T;
120
+ } catch {
121
+ return;
122
+ }
123
+ if (!predicate(message)) return;
124
+ clearTimeout(timeout);
125
+ ws.removeEventListener("message", onMessage);
126
+ resolve(message);
127
+ };
128
+ ws.addEventListener("message", onMessage);
129
+ });
130
+ }
131
+
59
132
  async function startRpcServer(handler: (method: string) => Response | object): Promise<TestServer> {
60
133
  const server = createServer(async (req, res) => {
61
134
  if (req.method === "GET" && req.url === "/health") {
@@ -145,9 +218,7 @@ describe("MagicContextRpcClient", () => {
145
218
  const port = await server.start();
146
219
  try {
147
220
  // Sanity: the port file carries a non-empty token.
148
- const record = parseRpcPortFile(
149
- readFileSync(rpcPortFilePath(storageDir, directory), "utf-8"),
150
- );
221
+ const record = readNewestPortRecord(storageDir, directory);
151
222
  expect(typeof record?.token).toBe("string");
152
223
  expect((record?.token ?? "").length).toBeGreaterThan(0);
153
224
 
@@ -167,6 +238,94 @@ describe("MagicContextRpcClient", () => {
167
238
  }
168
239
  });
169
240
 
241
+ test("websocket upgrade rejects missing bearer token before a socket is created", async () => {
242
+ const storageDir = makeTempDir();
243
+ const directory = "/repo-ws-auth";
244
+ const server = new MagicContextRpcServer(storageDir, directory);
245
+ const port = await server.start();
246
+ try {
247
+ const res = await fetch(`http://127.0.0.1:${port}/ws`);
248
+ expect(res.status).toBe(401);
249
+ expect(isTuiConnected()).toBe(false);
250
+ } finally {
251
+ server.stop();
252
+ }
253
+ });
254
+
255
+ test("re-hello replaces the previous websocket notification sink", async () => {
256
+ drainNotifications(Number.MAX_SAFE_INTEGER);
257
+ const storageDir = makeTempDir();
258
+ const directory = "/repo-ws-rehello";
259
+ const server = new MagicContextRpcServer(storageDir, directory);
260
+ const port = await server.start();
261
+ const record = readNewestPortRecord(storageDir, directory);
262
+ expect(typeof record?.token).toBe("string");
263
+
264
+ const ws = await openSocket(port, record?.token ?? "");
265
+ const notifications: unknown[] = [];
266
+ ws.addEventListener("message", (event) => {
267
+ const message = JSON.parse(String(event.data)) as {
268
+ type?: string;
269
+ notification?: unknown;
270
+ };
271
+ if (message.type === "notification") notifications.push(message.notification);
272
+ });
273
+
274
+ try {
275
+ ws.send(JSON.stringify({ type: "hello", token: record?.token, sessionId: "ses_A" }));
276
+ await waitForJsonMessage(ws, (message) => message.type === "hello-ack");
277
+ expect(isTuiConnected("ses_A")).toBe(true);
278
+
279
+ ws.send(JSON.stringify({ type: "hello", token: record?.token, sessionId: "ses_B" }));
280
+ await waitForJsonMessage(ws, (message) => message.type === "hello-ack");
281
+ expect(isTuiConnected("ses_A")).toBe(false);
282
+ expect(isTuiConnected("ses_B")).toBe(true);
283
+
284
+ ws.send(JSON.stringify({ type: "hello", token: record?.token, sessionId: "ses_B" }));
285
+ await waitForJsonMessage(ws, (message) => message.type === "hello-ack");
286
+ pushNotification("live", { ok: true }, "ses_B");
287
+ await waitFor(() => notifications.length >= 1, "one live notification");
288
+ await new Promise((resolve) => setTimeout(resolve, 50));
289
+ expect(notifications).toHaveLength(1);
290
+
291
+ ws.close();
292
+ await waitFor(() => !isTuiConnected(), "socket sink cleanup");
293
+ } finally {
294
+ try {
295
+ ws.close();
296
+ } catch {
297
+ // best-effort
298
+ }
299
+ server.stop();
300
+ }
301
+ });
302
+
303
+ test("same-process servers keep distinct port files during overlap", async () => {
304
+ const storageDir = makeTempDir();
305
+ const directory = "/repo-port-collision";
306
+ const first = new MagicContextRpcServer(storageDir, directory);
307
+ const second = new MagicContextRpcServer(storageDir, directory);
308
+ await first.start();
309
+ const secondPort = await second.start();
310
+
311
+ try {
312
+ const files = readdirSync(rpcPortDir(storageDir, directory)).filter(
313
+ (entry) => entry.startsWith("port-") && entry.endsWith(".json"),
314
+ );
315
+ expect(files.length).toBeGreaterThanOrEqual(2);
316
+
317
+ first.stop();
318
+ const remaining = readNewestPortRecord(storageDir, directory);
319
+ expect(remaining?.port).toBe(secondPort);
320
+
321
+ const client = new MagicContextRpcClient(storageDir, directory);
322
+ expect((await client.resolveEndpoint())?.port).toBe(secondPort);
323
+ } finally {
324
+ first.stop();
325
+ second.stop();
326
+ }
327
+ });
328
+
170
329
  test("gives up when the port file points at a dead server", async () => {
171
330
  const storageDir = makeTempDir();
172
331
  const directory = "/repo";