@cortexkit/opencode-magic-context 0.21.5 → 0.21.7
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 +1 -1
- package/dist/agents/permissions.d.ts +142 -0
- package/dist/agents/permissions.d.ts.map +1 -0
- package/dist/config/agent-disable.d.ts +26 -0
- package/dist/config/agent-disable.d.ts.map +1 -0
- package/dist/config/index.d.ts.map +1 -1
- package/dist/config/schema/agent-overrides.d.ts +12 -12
- package/dist/config/schema/magic-context.d.ts +87 -93
- package/dist/config/schema/magic-context.d.ts.map +1 -1
- package/dist/features/magic-context/compartment-lease.d.ts +14 -0
- package/dist/features/magic-context/compartment-lease.d.ts.map +1 -0
- package/dist/features/magic-context/compartment-storage.d.ts +5 -1
- package/dist/features/magic-context/compartment-storage.d.ts.map +1 -1
- package/dist/features/magic-context/compression-depth-storage.d.ts +2 -1
- package/dist/features/magic-context/compression-depth-storage.d.ts.map +1 -1
- package/dist/features/magic-context/migrations.d.ts.map +1 -1
- package/dist/features/magic-context/storage-db.d.ts.map +1 -1
- package/dist/features/magic-context/storage-meta-persisted.d.ts.map +1 -1
- package/dist/features/magic-context/storage-meta-session.d.ts.map +1 -1
- package/dist/features/magic-context/storage.d.ts +1 -1
- package/dist/features/magic-context/storage.d.ts.map +1 -1
- package/dist/hooks/magic-context/auto-search-runner.d.ts.map +1 -1
- package/dist/hooks/magic-context/compartment-runner-compressor.d.ts +4 -0
- package/dist/hooks/magic-context/compartment-runner-compressor.d.ts.map +1 -1
- package/dist/hooks/magic-context/compartment-runner-incremental.d.ts.map +1 -1
- package/dist/hooks/magic-context/compartment-runner-partial-recomp.d.ts.map +1 -1
- package/dist/hooks/magic-context/compartment-runner-recomp.d.ts.map +1 -1
- package/dist/hooks/magic-context/compartment-runner-types.d.ts +2 -0
- package/dist/hooks/magic-context/compartment-runner-types.d.ts.map +1 -1
- package/dist/hooks/magic-context/compartment-runner.d.ts +5 -0
- package/dist/hooks/magic-context/compartment-runner.d.ts.map +1 -1
- package/dist/hooks/magic-context/hook.d.ts.map +1 -1
- package/dist/hooks/magic-context/transform-compartment-phase.d.ts +2 -2
- package/dist/hooks/magic-context/transform-compartment-phase.d.ts.map +1 -1
- package/dist/hooks/magic-context/transform-postprocess-phase.d.ts +1 -0
- package/dist/hooks/magic-context/transform-postprocess-phase.d.ts.map +1 -1
- package/dist/hooks/magic-context/transform.d.ts +2 -0
- package/dist/hooks/magic-context/transform.d.ts.map +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +680 -188
- package/dist/plugin/conflict-warning-hook.d.ts +10 -0
- package/dist/plugin/conflict-warning-hook.d.ts.map +1 -1
- package/dist/plugin/dream-timer.d.ts.map +1 -1
- package/dist/plugin/hooks/create-session-hooks.d.ts.map +1 -1
- package/dist/plugin/rpc-handlers.d.ts +1 -1
- package/dist/plugin/rpc-handlers.d.ts.map +1 -1
- package/dist/plugin/tool-registry.d.ts.map +1 -1
- package/dist/shared/announcement.d.ts +55 -0
- package/dist/shared/announcement.d.ts.map +1 -0
- package/dist/shared/format-threshold.d.ts +24 -0
- package/dist/shared/format-threshold.d.ts.map +1 -0
- package/dist/shared/rpc-types.d.ts +10 -0
- package/dist/shared/rpc-types.d.ts.map +1 -1
- package/dist/tui/data/context-db.d.ts +14 -0
- package/dist/tui/data/context-db.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/shared/announcement.test.ts +143 -0
- package/src/shared/announcement.ts +97 -0
- package/src/shared/format-threshold.ts +28 -0
- package/src/shared/rpc-types.ts +10 -0
- package/src/tui/data/context-db.ts +44 -0
- package/src/tui/index.tsx +75 -6
- package/src/tui/slots/sidebar-content.tsx +24 -3
|
@@ -21,4 +21,14 @@ export declare function cleanupConflictWarnings(client: unknown, directory: stri
|
|
|
21
21
|
* Sends an ignored message that auto-deletes after 1 second.
|
|
22
22
|
*/
|
|
23
23
|
export declare function sendTuiSetupNotification(client: unknown, directory: string, serverUrl?: string): Promise<void>;
|
|
24
|
+
/**
|
|
25
|
+
* Desktop startup announcement: post a one-shot ignored message describing
|
|
26
|
+
* what's new in this release. Mirrors the TUI's RPC-driven dialog path so both
|
|
27
|
+
* surfaces deliver the same announcement once per ANNOUNCEMENT_VERSION.
|
|
28
|
+
*
|
|
29
|
+
* Persistence lives in `getMagicContextStorageDir()/last_announced_version`,
|
|
30
|
+
* shared with the TUI handlers and the Pi plugin so a dismissal in any harness
|
|
31
|
+
* suppresses the others for the same announcement.
|
|
32
|
+
*/
|
|
33
|
+
export declare function sendStartupAnnouncement(client: unknown, directory: string, version: string, features: ReadonlyArray<string>, footer: string, markSeen: (version: string) => void): Promise<void>;
|
|
24
34
|
//# sourceMappingURL=conflict-warning-hook.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"conflict-warning-hook.d.ts","sourceRoot":"","sources":["../../src/plugin/conflict-warning-hook.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAKH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,6BAA6B,CAAC;
|
|
1
|
+
{"version":3,"file":"conflict-warning-hook.d.ts","sourceRoot":"","sources":["../../src/plugin/conflict-warning-hook.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAKH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,6BAA6B,CAAC;AAoLlE;;GAEG;AACH,wBAAsB,mBAAmB,CACrC,MAAM,EAAE,OAAO,EACf,SAAS,EAAE,MAAM,EACjB,cAAc,EAAE,cAAc,GAC/B,OAAO,CAAC,IAAI,CAAC,CA+Cf;AAED;;;GAGG;AACH,wBAAsB,uBAAuB,CACzC,MAAM,EAAE,OAAO,EACf,SAAS,EAAE,MAAM,EACjB,SAAS,CAAC,EAAE,MAAM,GACnB,OAAO,CAAC,IAAI,CAAC,CAqHf;AAkCD;;;GAGG;AACH,wBAAsB,wBAAwB,CAC1C,MAAM,EAAE,OAAO,EACf,SAAS,EAAE,MAAM,EACjB,SAAS,CAAC,EAAE,MAAM,GACnB,OAAO,CAAC,IAAI,CAAC,CAkEf;AAED;;;;;;;;GAQG;AACH,wBAAsB,uBAAuB,CACzC,MAAM,EAAE,OAAO,EACf,SAAS,EAAE,MAAM,EACjB,OAAO,EAAE,MAAM,EACf,QAAQ,EAAE,aAAa,CAAC,MAAM,CAAC,EAC/B,MAAM,EAAE,MAAM,EACd,QAAQ,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,GACpC,OAAO,CAAC,IAAI,CAAC,CA8Df"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dream-timer.d.ts","sourceRoot":"","sources":["../../src/plugin/dream-timer.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,gCAAgC,CAAC;AAapE,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AACjD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAK7C;;;;;GAKG;AACH,UAAU,mBAAmB;IACzB,SAAS,EAAE,MAAM,CAAC;IAClB,eAAe,EAAE,MAAM,CAAC;IACxB,MAAM,EAAE,aAAa,CAAC,QAAQ,CAAC,CAAC;IAChC,aAAa,CAAC,EAAE,aAAa,CAAC;IAC9B,wBAAwB,CAAC,EAAE;QAAE,OAAO,EAAE,OAAO,CAAC;QAAC,kBAAkB,EAAE,MAAM,CAAA;KAAE,CAAC;IAC5E,uBAAuB,CAAC,EAAE;QACtB,OAAO,EAAE,OAAO,CAAC;QACjB,YAAY,EAAE,MAAM,CAAC;QACrB,SAAS,EAAE,MAAM,CAAC;KACrB,CAAC;IACF,iBAAiB,CAAC,EAAE;QAChB,OAAO,EAAE,OAAO,CAAC;QACjB,UAAU,EAAE,MAAM,CAAC;QACnB,WAAW,EAAE,MAAM,CAAC;KACvB,CAAC;IACF,gBAAgB,EAAE,CAAC,SAAS,EAAE,MAAM,EAAE,EAAE,EAAE,QAAQ,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;CACxE;AAQD;;;;;;;;;;GAUG;AACH,wBAAsB,uBAAuB,CACzC,IAAI,EAAE,mBAAmB,GAC1B,OAAO,CAAC,CAAC,MAAM,IAAI,CAAC,GAAG,SAAS,CAAC,
|
|
1
|
+
{"version":3,"file":"dream-timer.d.ts","sourceRoot":"","sources":["../../src/plugin/dream-timer.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,gCAAgC,CAAC;AAapE,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AACjD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAK7C;;;;;GAKG;AACH,UAAU,mBAAmB;IACzB,SAAS,EAAE,MAAM,CAAC;IAClB,eAAe,EAAE,MAAM,CAAC;IACxB,MAAM,EAAE,aAAa,CAAC,QAAQ,CAAC,CAAC;IAChC,aAAa,CAAC,EAAE,aAAa,CAAC;IAC9B,wBAAwB,CAAC,EAAE;QAAE,OAAO,EAAE,OAAO,CAAC;QAAC,kBAAkB,EAAE,MAAM,CAAA;KAAE,CAAC;IAC5E,uBAAuB,CAAC,EAAE;QACtB,OAAO,EAAE,OAAO,CAAC;QACjB,YAAY,EAAE,MAAM,CAAC;QACrB,SAAS,EAAE,MAAM,CAAC;KACrB,CAAC;IACF,iBAAiB,CAAC,EAAE;QAChB,OAAO,EAAE,OAAO,CAAC;QACjB,UAAU,EAAE,MAAM,CAAC;QACnB,WAAW,EAAE,MAAM,CAAC;KACvB,CAAC;IACF,gBAAgB,EAAE,CAAC,SAAS,EAAE,MAAM,EAAE,EAAE,EAAE,QAAQ,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;CACxE;AAQD;;;;;;;;;;GAUG;AACH,wBAAsB,uBAAuB,CACzC,IAAI,EAAE,mBAAmB,GAC1B,OAAO,CAAC,CAAC,MAAM,IAAI,CAAC,GAAG,SAAS,CAAC,CA4DnC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"create-session-hooks.d.ts","sourceRoot":"","sources":["../../../src/plugin/hooks/create-session-hooks.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,cAAc,CAAC;AAU7D,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,8CAA8C,CAAC;AACrF,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAE9C,wBAAgB,kBAAkB,CAAC,IAAI,EAAE;IACrC,GAAG,EAAE,aAAa,CAAC;IACnB,YAAY,EAAE,wBAAwB,CAAC;IACvC,gBAAgB,EAAE,gBAAgB,CAAC;CACtC;;;;;;qBAoDkuJ,CAAC;;;;;;;;;;;;qBAA9sC,CAAC;mBAAyB,CAAC;iBAAuB,CAAC;iBAAuB,CAAC;0BAAc,CAAC;uBAAiB,CAAC;;;;;;
|
|
1
|
+
{"version":3,"file":"create-session-hooks.d.ts","sourceRoot":"","sources":["../../../src/plugin/hooks/create-session-hooks.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,cAAc,CAAC;AAU7D,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,8CAA8C,CAAC;AACrF,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAE9C,wBAAgB,kBAAkB,CAAC,IAAI,EAAE;IACrC,GAAG,EAAE,aAAa,CAAC;IACnB,YAAY,EAAE,wBAAwB,CAAC;IACvC,gBAAgB,EAAE,gBAAgB,CAAC;CACtC;;;;;;qBAoDkuJ,CAAC;;;;;;;;;;;;qBAA9sC,CAAC;mBAAyB,CAAC;iBAAuB,CAAC;iBAAuB,CAAC;0BAAc,CAAC;uBAAiB,CAAC;;;;;;0BAA+luB,CAAC;;;;;;EADju1B"}
|
|
@@ -7,7 +7,7 @@ import { type ContextDatabase as Database } from "../features/magic-context/stor
|
|
|
7
7
|
import type { LiveSessionState } from "../hooks/magic-context/live-session-state";
|
|
8
8
|
import type { MagicContextRpcServer } from "../shared/rpc-server";
|
|
9
9
|
import type { SidebarSnapshot, StatusDetail } from "../shared/rpc-types";
|
|
10
|
-
export declare function buildSidebarSnapshot(db: Database, sessionId: string, directory: string, liveSessionState?: LiveSessionState, injectionBudgetTokens?: number): SidebarSnapshot;
|
|
10
|
+
export declare function buildSidebarSnapshot(db: Database, sessionId: string, directory: string, liveSessionState?: LiveSessionState, injectionBudgetTokens?: number, config?: Record<string, unknown>): SidebarSnapshot;
|
|
11
11
|
export declare function buildStatusDetail(db: Database, sessionId: string, directory: string, modelKey?: string, config?: Record<string, unknown>, liveSessionState?: LiveSessionState, injectionBudgetTokens?: number): StatusDetail;
|
|
12
12
|
/**
|
|
13
13
|
* Register all RPC handlers on the server.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"rpc-handlers.d.ts","sourceRoot":"","sources":["../../src/plugin/rpc-handlers.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,gCAAgC,CAAC;AAGzE,OAAO,EAAE,KAAK,eAAe,IAAI,QAAQ,EAAgB,MAAM,mCAAmC,CAAC;AAWnG,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,2CAA2C,CAAC;
|
|
1
|
+
{"version":3,"file":"rpc-handlers.d.ts","sourceRoot":"","sources":["../../src/plugin/rpc-handlers.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,gCAAgC,CAAC;AAGzE,OAAO,EAAE,KAAK,eAAe,IAAI,QAAQ,EAAgB,MAAM,mCAAmC,CAAC;AAWnG,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,2CAA2C,CAAC;AAgBlF,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,sBAAsB,CAAC;AAClE,OAAO,KAAK,EAAE,eAAe,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAmDzE,wBAAgB,oBAAoB,CAChC,EAAE,EAAE,QAAQ,EACZ,SAAS,EAAE,MAAM,EACjB,SAAS,EAAE,MAAM,EACjB,gBAAgB,CAAC,EAAE,gBAAgB,EACnC,qBAAqB,CAAC,EAAE,MAAM,EAK9B,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GACjC,eAAe,CA6UjB;AAED,wBAAgB,iBAAiB,CAC7B,EAAE,EAAE,QAAQ,EACZ,SAAS,EAAE,MAAM,EACjB,SAAS,EAAE,MAAM,EACjB,QAAQ,CAAC,EAAE,MAAM,EACjB,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAChC,gBAAgB,CAAC,EAAE,gBAAgB,EACnC,qBAAqB,CAAC,EAAE,MAAM,GAC/B,YAAY,CAuKd;AAED;;GAEG;AACH,wBAAgB,mBAAmB,CAC/B,SAAS,EAAE,qBAAqB,EAChC,IAAI,EAAE;IACF,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,kBAAkB,CAAC;IAC3B,MAAM,EAAE,OAAO,CAAC;IAChB,gBAAgB,EAAE,gBAAgB,CAAC;CACtC,GACF,IAAI,CAkKN"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tool-registry.d.ts","sourceRoot":"","sources":["../../src/plugin/tool-registry.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAC1D,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,WAAW,CAAC;
|
|
1
|
+
{"version":3,"file":"tool-registry.d.ts","sourceRoot":"","sources":["../../src/plugin/tool-registry.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAC1D,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,WAAW,CAAC;AAgB1D,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAE7C,wBAAgB,kBAAkB,CAAC,IAAI,EAAE;IACrC,GAAG,EAAE,aAAa,CAAC;IACnB,YAAY,EAAE,wBAAwB,CAAC;CAC1C,GAAG,MAAM,CAAC,MAAM,EAAE,cAAc,CAAC,CAyEjC"}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Release-notes startup announcement shared by OpenCode plugin and Pi plugin.
|
|
3
|
+
*
|
|
4
|
+
* Bump `ANNOUNCEMENT_VERSION` and populate `ANNOUNCEMENT_FEATURES` *only* when a
|
|
5
|
+
* release ships user-facing news worth surfacing once at startup. Patch releases
|
|
6
|
+
* with no user-visible changes should leave both untouched — that way a user who
|
|
7
|
+
* already dismissed the dialog for the current `ANNOUNCEMENT_VERSION` won't see
|
|
8
|
+
* it again on the next bugfix bump.
|
|
9
|
+
*
|
|
10
|
+
* The persisted state is a single line of text (`last_announced_version`) under
|
|
11
|
+
* `getMagicContextStorageDir()`. OpenCode and Pi share the same file because
|
|
12
|
+
* they share the same storage root — so dismissing in one harness suppresses
|
|
13
|
+
* the dialog in the other for the same announcement.
|
|
14
|
+
*
|
|
15
|
+
* Leave both empty (`""` and `[]`) to skip the dialog entirely.
|
|
16
|
+
*/
|
|
17
|
+
/**
|
|
18
|
+
* Bump only when there are user-visible changes worth a startup dialog.
|
|
19
|
+
* Does NOT need to match the published package version.
|
|
20
|
+
*/
|
|
21
|
+
export declare const ANNOUNCEMENT_VERSION = "0.21.7";
|
|
22
|
+
/**
|
|
23
|
+
* Short, user-facing bullet strings. Keep each line ~80 chars or shorter so the
|
|
24
|
+
* TUI dialog renders cleanly without horizontal scroll on a typical terminal.
|
|
25
|
+
*/
|
|
26
|
+
export declare const ANNOUNCEMENT_FEATURES: ReadonlyArray<string>;
|
|
27
|
+
/**
|
|
28
|
+
* Persistent footer rendered below the version-specific bullets in every
|
|
29
|
+
* announcement. Stays in place across releases so users always see the Discord
|
|
30
|
+
* invite without us needing to repeat it in `ANNOUNCEMENT_FEATURES` each time.
|
|
31
|
+
*
|
|
32
|
+
* Leave empty (`""`) to suppress the footer.
|
|
33
|
+
*/
|
|
34
|
+
export declare const ANNOUNCEMENT_FOOTER = "Join us on Discord: https://discord.gg/F2uWxjGnU";
|
|
35
|
+
/**
|
|
36
|
+
* Read the most recently dismissed announcement version, or `""` if none.
|
|
37
|
+
*
|
|
38
|
+
* Best-effort: any read failure returns `""` (which forces the announcement to
|
|
39
|
+
* re-show). The cost of a spurious second dialog is much smaller than the cost
|
|
40
|
+
* of suppressing a real announcement due to a transient FS error.
|
|
41
|
+
*/
|
|
42
|
+
export declare function readLastAnnouncedVersion(): string;
|
|
43
|
+
/**
|
|
44
|
+
* Persist `version` as the most recently dismissed announcement. Best-effort:
|
|
45
|
+
* write failures are swallowed so dialog-confirm flows never throw on storage
|
|
46
|
+
* errors. Worst case the user sees the same dialog once more on next startup.
|
|
47
|
+
*/
|
|
48
|
+
export declare function markAnnouncementSeen(version: string): void;
|
|
49
|
+
/**
|
|
50
|
+
* True when the configured `ANNOUNCEMENT_VERSION` has not yet been dismissed
|
|
51
|
+
* AND there is at least one feature to show. Used by both the TUI dialog path
|
|
52
|
+
* and the Desktop ignored-message fallback.
|
|
53
|
+
*/
|
|
54
|
+
export declare function shouldShowAnnouncement(): boolean;
|
|
55
|
+
//# sourceMappingURL=announcement.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"announcement.d.ts","sourceRoot":"","sources":["../../src/shared/announcement.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAMH;;;GAGG;AACH,eAAO,MAAM,oBAAoB,WAAW,CAAC;AAE7C;;;GAGG;AACH,eAAO,MAAM,qBAAqB,EAAE,aAAa,CAAC,MAAM,CAOvD,CAAC;AAEF;;;;;;GAMG;AACH,eAAO,MAAM,mBAAmB,qDAAqD,CAAC;AAQtF;;;;;;GAMG;AACH,wBAAgB,wBAAwB,IAAI,MAAM,CAQjD;AAED;;;;GAIG;AACH,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAS1D;AAED;;;;GAIG;AACH,wBAAgB,sBAAsB,IAAI,OAAO,CAGhD"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Format an execute-threshold percentage for human-facing display.
|
|
3
|
+
*
|
|
4
|
+
* `executeThreshold` in the snapshot is always a percentage number, but it
|
|
5
|
+
* comes from two very different config paths:
|
|
6
|
+
* 1. `execute_threshold_percentage` (or its model-keyed variant) — user
|
|
7
|
+
* configures an integer like 65 directly. We must render exactly that.
|
|
8
|
+
* 2. `execute_threshold_tokens` — user configures absolute token cap (e.g.
|
|
9
|
+
* 128000). The resolver in `event-resolvers.ts` divides that by the
|
|
10
|
+
* model's context limit (`(128000 / 907788) * 100`) and the result is
|
|
11
|
+
* a long float like `14.099783080260304` that overflows the TUI cell
|
|
12
|
+
* (issue #90).
|
|
13
|
+
*
|
|
14
|
+
* Behaviour:
|
|
15
|
+
* - Integer input (≤0.05 fractional drift) renders without decimals.
|
|
16
|
+
* - Anything else is rendered with one decimal digit, which is precise
|
|
17
|
+
* enough to convey the configured token budget without smearing across
|
|
18
|
+
* two lines in a narrow sidebar.
|
|
19
|
+
*
|
|
20
|
+
* Returns the formatted percentage WITHOUT the trailing `%` so callers can
|
|
21
|
+
* compose richer strings like `47.5% / 65%` consistently.
|
|
22
|
+
*/
|
|
23
|
+
export declare function formatThresholdPercent(value: number | undefined | null): string;
|
|
24
|
+
//# sourceMappingURL=format-threshold.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"format-threshold.d.ts","sourceRoot":"","sources":["../../src/shared/format-threshold.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAgB,sBAAsB,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI,GAAG,MAAM,CAK/E"}
|
|
@@ -44,6 +44,16 @@ export interface SidebarSnapshot {
|
|
|
44
44
|
* shows this as "Tool Definitions".
|
|
45
45
|
*/
|
|
46
46
|
toolDefinitionTokens: number;
|
|
47
|
+
/**
|
|
48
|
+
* Effective execute-threshold percentage for this session's active model,
|
|
49
|
+
* after per-model resolution and the tokens→percentage conversion (when
|
|
50
|
+
* `execute_threshold_tokens` applies). Surfaces in the sidebar / status
|
|
51
|
+
* dialog header alongside `usagePercentage` so users can see how close
|
|
52
|
+
* the session is to triggering compaction. Defaults to `65` when no live
|
|
53
|
+
* model is known yet — matches the runtime fallback used by the
|
|
54
|
+
* scheduler and transform paths.
|
|
55
|
+
*/
|
|
56
|
+
executeThreshold: number;
|
|
47
57
|
}
|
|
48
58
|
export interface StatusDetail extends SidebarSnapshot {
|
|
49
59
|
tagCounter: number;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"rpc-types.d.ts","sourceRoot":"","sources":["../../src/shared/rpc-types.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,MAAM,WAAW,eAAe;IAC5B,SAAS,EAAE,MAAM,CAAC;IAClB,eAAe,EAAE,MAAM,CAAC;IACxB,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,MAAM,CAAC;IACrB,kBAAkB,EAAE,MAAM,CAAC;IAC3B,gBAAgB,EAAE,MAAM,CAAC;IACzB,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;IACpB,gBAAgB,EAAE,MAAM,CAAC;IACzB,eAAe,EAAE,MAAM,CAAC;IACxB,gBAAgB,EAAE,OAAO,CAAC;IAC1B,qBAAqB,EAAE,OAAO,CAAC;IAC/B,gBAAgB,EAAE,MAAM,CAAC;IACzB,mBAAmB,EAAE,MAAM,CAAC;IAC5B,QAAQ,EAAE,MAAM,CAAC;IACjB,gBAAgB,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC,eAAe,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/B,iBAAiB,EAAE,MAAM,CAAC;IAC1B,UAAU,EAAE,MAAM,CAAC;IACnB,YAAY,EAAE,MAAM,CAAC;IACrB;;;;OAIG;IACH,kBAAkB,EAAE,MAAM,CAAC;IAC3B;;;;OAIG;IACH,cAAc,EAAE,MAAM,CAAC;IACvB;;;;;;;OAOG;IACH,oBAAoB,EAAE,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"rpc-types.d.ts","sourceRoot":"","sources":["../../src/shared/rpc-types.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,MAAM,WAAW,eAAe;IAC5B,SAAS,EAAE,MAAM,CAAC;IAClB,eAAe,EAAE,MAAM,CAAC;IACxB,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,MAAM,CAAC;IACrB,kBAAkB,EAAE,MAAM,CAAC;IAC3B,gBAAgB,EAAE,MAAM,CAAC;IACzB,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;IACpB,gBAAgB,EAAE,MAAM,CAAC;IACzB,eAAe,EAAE,MAAM,CAAC;IACxB,gBAAgB,EAAE,OAAO,CAAC;IAC1B,qBAAqB,EAAE,OAAO,CAAC;IAC/B,gBAAgB,EAAE,MAAM,CAAC;IACzB,mBAAmB,EAAE,MAAM,CAAC;IAC5B,QAAQ,EAAE,MAAM,CAAC;IACjB,gBAAgB,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC,eAAe,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/B,iBAAiB,EAAE,MAAM,CAAC;IAC1B,UAAU,EAAE,MAAM,CAAC;IACnB,YAAY,EAAE,MAAM,CAAC;IACrB;;;;OAIG;IACH,kBAAkB,EAAE,MAAM,CAAC;IAC3B;;;;OAIG;IACH,cAAc,EAAE,MAAM,CAAC;IACvB;;;;;;;OAOG;IACH,oBAAoB,EAAE,MAAM,CAAC;IAC7B;;;;;;;;OAQG;IACH,gBAAgB,EAAE,MAAM,CAAC;CAC5B;AAED,MAAM,WAAW,YAAa,SAAQ,eAAe;IACjD,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;IACpB,gBAAgB,EAAE,MAAM,CAAC;IACzB,eAAe,EAAE,MAAM,CAAC;IACxB,aAAa,EAAE,MAAM,CAAC;IACtB,kBAAkB,EAAE,MAAM,GAAG,IAAI,CAAC;IAClC,UAAU,EAAE,OAAO,CAAC;IACpB,UAAU,EAAE,KAAK,CAAC;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IACxD,YAAY,EAAE,MAAM,CAAC;IACrB,UAAU,EAAE,MAAM,CAAC;IACnB,gBAAgB,EAAE,MAAM,CAAC;IACzB,YAAY,EAAE,OAAO,CAAC;IACtB,gBAAgB,EAAE,MAAM,CAAC;IACzB;;;;OAIG;IACH,oBAAoB,EAAE,YAAY,GAAG,QAAQ,CAAC;IAC9C;;;OAGG;IACH,sBAAsB,CAAC,EAAE,MAAM,CAAC;IAChC,iBAAiB,EAAE,MAAM,CAAC;IAC1B,aAAa,EAAE,MAAM,CAAC;IACtB,uBAAuB,EAAE,MAAM,CAAC;IAChC,cAAc,EAAE,MAAM,CAAC;IACvB,kBAAkB,EAAE,MAAM,CAAC;IAC3B,iBAAiB,EAAE,MAAM,GAAG,IAAI,CAAC;IACjC,gBAAgB,EAAE,MAAM,GAAG,IAAI,CAAC;CACnC;AAED,MAAM,WAAW,sBAAsB;IACnC,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"}
|
|
@@ -17,6 +17,20 @@ export interface TuiMessage {
|
|
|
17
17
|
payload: Record<string, unknown>;
|
|
18
18
|
sessionId?: string;
|
|
19
19
|
}
|
|
20
|
+
/**
|
|
21
|
+
* Fetch the current startup announcement from the server, if any.
|
|
22
|
+
* Returns `{show: false}` when there's nothing to announce or when the
|
|
23
|
+
* configured ANNOUNCEMENT_VERSION has already been dismissed.
|
|
24
|
+
*/
|
|
25
|
+
export interface AnnouncementResponse {
|
|
26
|
+
show: boolean;
|
|
27
|
+
version?: string;
|
|
28
|
+
features?: string[];
|
|
29
|
+
footer?: string;
|
|
30
|
+
}
|
|
31
|
+
export declare function getAnnouncement(): Promise<AnnouncementResponse>;
|
|
32
|
+
/** Mark the current ANNOUNCEMENT_VERSION as dismissed on the server. */
|
|
33
|
+
export declare function markAnnounced(): Promise<boolean>;
|
|
20
34
|
/** Poll for pending server→TUI notifications via RPC. */
|
|
21
35
|
export declare function consumeTuiMessages(): Promise<TuiMessage[]>;
|
|
22
36
|
//# sourceMappingURL=context-db.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"context-db.d.ts","sourceRoot":"","sources":["../../../src/tui/data/context-db.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAA0B,eAAe,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAC;AAEpG,YAAY,EAAE,eAAe,EAAE,YAAY,EAAE,CAAC;AAc9C,2DAA2D;AAC3D,wBAAgB,aAAa,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI,CAGrD;AAED,+BAA+B;AAC/B,wBAAgB,QAAQ,IAAI,IAAI,CAI/B;
|
|
1
|
+
{"version":3,"file":"context-db.d.ts","sourceRoot":"","sources":["../../../src/tui/data/context-db.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAA0B,eAAe,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAC;AAEpG,YAAY,EAAE,eAAe,EAAE,YAAY,EAAE,CAAC;AAc9C,2DAA2D;AAC3D,wBAAgB,aAAa,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI,CAGrD;AAED,+BAA+B;AAC/B,wBAAgB,QAAQ,IAAI,IAAI,CAI/B;AAwFD,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,CA4CvB;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,MAAM,WAAW,UAAU;IACvB,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACjC,SAAS,CAAC,EAAE,MAAM,CAAC;CACtB;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;AAED,yDAAyD;AACzD,wBAAsB,kBAAkB,IAAI,OAAO,CAAC,UAAU,EAAE,CAAC,CAqBhE"}
|
package/package.json
CHANGED
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
import { afterEach, beforeEach, describe, expect, test } from "bun:test";
|
|
2
|
+
import * as fs from "node:fs";
|
|
3
|
+
import * as os from "node:os";
|
|
4
|
+
import * as path from "node:path";
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* `announcement.ts` reads/writes a single `last_announced_version` file under
|
|
8
|
+
* `getMagicContextStorageDir()`. The behavior we test:
|
|
9
|
+
* 1. `markAnnouncementSeen` then `readLastAnnouncedVersion` round-trips
|
|
10
|
+
* 2. `shouldShowAnnouncement` returns false after a matching mark
|
|
11
|
+
* 3. `shouldShowAnnouncement` returns true after a non-matching mark
|
|
12
|
+
* 4. Empty-version inputs are no-ops (don't crash, don't write garbage)
|
|
13
|
+
*
|
|
14
|
+
* We isolate writes by pointing `XDG_DATA_HOME` at a temp dir before requiring
|
|
15
|
+
* the module fresh per test, since the module captures the storage path at
|
|
16
|
+
* import time via `getMagicContextStorageDir()`.
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
let tmpRoot = "";
|
|
20
|
+
let originalXdg: string | undefined;
|
|
21
|
+
|
|
22
|
+
beforeEach(() => {
|
|
23
|
+
tmpRoot = fs.mkdtempSync(path.join(os.tmpdir(), "mc-announcement-test-"));
|
|
24
|
+
originalXdg = process.env.XDG_DATA_HOME;
|
|
25
|
+
process.env.XDG_DATA_HOME = tmpRoot;
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
afterEach(() => {
|
|
29
|
+
if (originalXdg === undefined) {
|
|
30
|
+
delete process.env.XDG_DATA_HOME;
|
|
31
|
+
} else {
|
|
32
|
+
process.env.XDG_DATA_HOME = originalXdg;
|
|
33
|
+
}
|
|
34
|
+
try {
|
|
35
|
+
fs.rmSync(tmpRoot, { recursive: true, force: true });
|
|
36
|
+
} catch {
|
|
37
|
+
// best-effort
|
|
38
|
+
}
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
describe("announcement state persistence", () => {
|
|
42
|
+
test("round-trips a dismissed version through the file", async () => {
|
|
43
|
+
// Fresh import after XDG override so the module captures the temp path
|
|
44
|
+
const mod = await import(`./announcement?t=${Date.now()}-rt`);
|
|
45
|
+
const { readLastAnnouncedVersion, markAnnouncementSeen } = mod;
|
|
46
|
+
|
|
47
|
+
expect(readLastAnnouncedVersion()).toBe("");
|
|
48
|
+
|
|
49
|
+
markAnnouncementSeen("9.9.9");
|
|
50
|
+
expect(readLastAnnouncedVersion()).toBe("9.9.9");
|
|
51
|
+
|
|
52
|
+
markAnnouncementSeen("9.9.10");
|
|
53
|
+
expect(readLastAnnouncedVersion()).toBe("9.9.10");
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
test("ignores empty / zero-length version marks", async () => {
|
|
57
|
+
const mod = await import(`./announcement?t=${Date.now()}-empty`);
|
|
58
|
+
const { readLastAnnouncedVersion, markAnnouncementSeen } = mod;
|
|
59
|
+
|
|
60
|
+
markAnnouncementSeen("");
|
|
61
|
+
expect(readLastAnnouncedVersion()).toBe("");
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
test("creates the storage directory if it does not exist", async () => {
|
|
65
|
+
const mod = await import(`./announcement?t=${Date.now()}-mkdir`);
|
|
66
|
+
const { markAnnouncementSeen } = mod;
|
|
67
|
+
|
|
68
|
+
// Storage dir lives under tmpRoot + cortexkit/magic-context — does not
|
|
69
|
+
// exist yet at the start of the test
|
|
70
|
+
const expectedDir = path.join(tmpRoot, "cortexkit", "magic-context");
|
|
71
|
+
expect(fs.existsSync(expectedDir)).toBe(false);
|
|
72
|
+
|
|
73
|
+
markAnnouncementSeen("0.21.7");
|
|
74
|
+
|
|
75
|
+
expect(fs.existsSync(expectedDir)).toBe(true);
|
|
76
|
+
expect(fs.readFileSync(path.join(expectedDir, "last_announced_version"), "utf-8")).toBe(
|
|
77
|
+
"0.21.7",
|
|
78
|
+
);
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
test("trims whitespace from stored version on read", async () => {
|
|
82
|
+
const mod = await import(`./announcement?t=${Date.now()}-trim`);
|
|
83
|
+
const { readLastAnnouncedVersion } = mod;
|
|
84
|
+
|
|
85
|
+
const dir = path.join(tmpRoot, "cortexkit", "magic-context");
|
|
86
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
87
|
+
fs.writeFileSync(path.join(dir, "last_announced_version"), " 1.2.3 \n");
|
|
88
|
+
|
|
89
|
+
expect(readLastAnnouncedVersion()).toBe("1.2.3");
|
|
90
|
+
});
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
describe("shouldShowAnnouncement gating", () => {
|
|
94
|
+
test("returns false when the live version is already marked", async () => {
|
|
95
|
+
const mod = await import(`./announcement?t=${Date.now()}-match`);
|
|
96
|
+
const {
|
|
97
|
+
ANNOUNCEMENT_VERSION,
|
|
98
|
+
ANNOUNCEMENT_FEATURES,
|
|
99
|
+
markAnnouncementSeen,
|
|
100
|
+
shouldShowAnnouncement,
|
|
101
|
+
} = mod;
|
|
102
|
+
|
|
103
|
+
// Skip the test if announcements are currently disabled (empty constants)
|
|
104
|
+
// — the gate's empty-input behavior is covered separately below.
|
|
105
|
+
if (!ANNOUNCEMENT_VERSION || ANNOUNCEMENT_FEATURES.length === 0) {
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
markAnnouncementSeen(ANNOUNCEMENT_VERSION);
|
|
110
|
+
expect(shouldShowAnnouncement()).toBe(false);
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
test("returns true when the live version was never marked", async () => {
|
|
114
|
+
const mod = await import(`./announcement?t=${Date.now()}-none`);
|
|
115
|
+
const { ANNOUNCEMENT_VERSION, ANNOUNCEMENT_FEATURES, shouldShowAnnouncement } = mod;
|
|
116
|
+
|
|
117
|
+
if (!ANNOUNCEMENT_VERSION || ANNOUNCEMENT_FEATURES.length === 0) {
|
|
118
|
+
// When empty, the gate is always false regardless of state
|
|
119
|
+
expect(shouldShowAnnouncement()).toBe(false);
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
// No mark has been written in this fresh tmpRoot, so the gate is open
|
|
124
|
+
expect(shouldShowAnnouncement()).toBe(true);
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
test("returns true when a different (older) version is marked", async () => {
|
|
128
|
+
const mod = await import(`./announcement?t=${Date.now()}-older`);
|
|
129
|
+
const {
|
|
130
|
+
ANNOUNCEMENT_VERSION,
|
|
131
|
+
ANNOUNCEMENT_FEATURES,
|
|
132
|
+
markAnnouncementSeen,
|
|
133
|
+
shouldShowAnnouncement,
|
|
134
|
+
} = mod;
|
|
135
|
+
|
|
136
|
+
if (!ANNOUNCEMENT_VERSION || ANNOUNCEMENT_FEATURES.length === 0) {
|
|
137
|
+
return;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
markAnnouncementSeen("0.0.0-pre-historic");
|
|
141
|
+
expect(shouldShowAnnouncement()).toBe(true);
|
|
142
|
+
});
|
|
143
|
+
});
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Release-notes startup announcement shared by OpenCode plugin and Pi plugin.
|
|
3
|
+
*
|
|
4
|
+
* Bump `ANNOUNCEMENT_VERSION` and populate `ANNOUNCEMENT_FEATURES` *only* when a
|
|
5
|
+
* release ships user-facing news worth surfacing once at startup. Patch releases
|
|
6
|
+
* with no user-visible changes should leave both untouched — that way a user who
|
|
7
|
+
* already dismissed the dialog for the current `ANNOUNCEMENT_VERSION` won't see
|
|
8
|
+
* it again on the next bugfix bump.
|
|
9
|
+
*
|
|
10
|
+
* The persisted state is a single line of text (`last_announced_version`) under
|
|
11
|
+
* `getMagicContextStorageDir()`. OpenCode and Pi share the same file because
|
|
12
|
+
* they share the same storage root — so dismissing in one harness suppresses
|
|
13
|
+
* the dialog in the other for the same announcement.
|
|
14
|
+
*
|
|
15
|
+
* Leave both empty (`""` and `[]`) to skip the dialog entirely.
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
import * as fs from "node:fs";
|
|
19
|
+
import * as path from "node:path";
|
|
20
|
+
import { getMagicContextStorageDir } from "./data-path";
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Bump only when there are user-visible changes worth a startup dialog.
|
|
24
|
+
* Does NOT need to match the published package version.
|
|
25
|
+
*/
|
|
26
|
+
export const ANNOUNCEMENT_VERSION = "0.21.7";
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Short, user-facing bullet strings. Keep each line ~80 chars or shorter so the
|
|
30
|
+
* TUI dialog renders cleanly without horizontal scroll on a typical terminal.
|
|
31
|
+
*/
|
|
32
|
+
export const ANNOUNCEMENT_FEATURES: ReadonlyArray<string> = [
|
|
33
|
+
"Pi parity sweep: 44 audit findings fixed, including a critical SHIP-BLOCKER where /ctx-flush did not drain the pending Pi compaction queue.",
|
|
34
|
+
"Pi historian recovery fix: empty/no-op historian returns now clear emergency recovery so sessions cannot loop forever at 95%.",
|
|
35
|
+
"trimPiMessagesToBoundary now sweeps non-contiguous tool-result orphans, fixing provider 400s after compaction in long Pi sessions.",
|
|
36
|
+
"Hidden subagent tool isolation: historian, dreamer, and sidekick can no longer spawn subagents or run unsafe tools.",
|
|
37
|
+
"TUI sidebar and /ctx-status header now show execute threshold inline: '47.5% / 65%' on the left, '475K / 1.0M' on the right.",
|
|
38
|
+
"doctor --issue now caps GitHub issue bodies at ~60KB with a dedicated 'Recent errors' section so reports stay submittable.",
|
|
39
|
+
];
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Persistent footer rendered below the version-specific bullets in every
|
|
43
|
+
* announcement. Stays in place across releases so users always see the Discord
|
|
44
|
+
* invite without us needing to repeat it in `ANNOUNCEMENT_FEATURES` each time.
|
|
45
|
+
*
|
|
46
|
+
* Leave empty (`""`) to suppress the footer.
|
|
47
|
+
*/
|
|
48
|
+
export const ANNOUNCEMENT_FOOTER = "Join us on Discord: https://discord.gg/F2uWxjGnU";
|
|
49
|
+
|
|
50
|
+
const STATE_FILENAME = "last_announced_version";
|
|
51
|
+
|
|
52
|
+
function getStateFilePath(): string {
|
|
53
|
+
return path.join(getMagicContextStorageDir(), STATE_FILENAME);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Read the most recently dismissed announcement version, or `""` if none.
|
|
58
|
+
*
|
|
59
|
+
* Best-effort: any read failure returns `""` (which forces the announcement to
|
|
60
|
+
* re-show). The cost of a spurious second dialog is much smaller than the cost
|
|
61
|
+
* of suppressing a real announcement due to a transient FS error.
|
|
62
|
+
*/
|
|
63
|
+
export function readLastAnnouncedVersion(): string {
|
|
64
|
+
try {
|
|
65
|
+
const file = getStateFilePath();
|
|
66
|
+
if (!fs.existsSync(file)) return "";
|
|
67
|
+
return fs.readFileSync(file, "utf-8").trim();
|
|
68
|
+
} catch {
|
|
69
|
+
return "";
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Persist `version` as the most recently dismissed announcement. Best-effort:
|
|
75
|
+
* write failures are swallowed so dialog-confirm flows never throw on storage
|
|
76
|
+
* errors. Worst case the user sees the same dialog once more on next startup.
|
|
77
|
+
*/
|
|
78
|
+
export function markAnnouncementSeen(version: string): void {
|
|
79
|
+
if (!version) return;
|
|
80
|
+
try {
|
|
81
|
+
const dir = getMagicContextStorageDir();
|
|
82
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
83
|
+
fs.writeFileSync(getStateFilePath(), version);
|
|
84
|
+
} catch {
|
|
85
|
+
// best-effort
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* True when the configured `ANNOUNCEMENT_VERSION` has not yet been dismissed
|
|
91
|
+
* AND there is at least one feature to show. Used by both the TUI dialog path
|
|
92
|
+
* and the Desktop ignored-message fallback.
|
|
93
|
+
*/
|
|
94
|
+
export function shouldShowAnnouncement(): boolean {
|
|
95
|
+
if (!ANNOUNCEMENT_VERSION || ANNOUNCEMENT_FEATURES.length === 0) return false;
|
|
96
|
+
return readLastAnnouncedVersion() !== ANNOUNCEMENT_VERSION;
|
|
97
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Format an execute-threshold percentage for human-facing display.
|
|
3
|
+
*
|
|
4
|
+
* `executeThreshold` in the snapshot is always a percentage number, but it
|
|
5
|
+
* comes from two very different config paths:
|
|
6
|
+
* 1. `execute_threshold_percentage` (or its model-keyed variant) — user
|
|
7
|
+
* configures an integer like 65 directly. We must render exactly that.
|
|
8
|
+
* 2. `execute_threshold_tokens` — user configures absolute token cap (e.g.
|
|
9
|
+
* 128000). The resolver in `event-resolvers.ts` divides that by the
|
|
10
|
+
* model's context limit (`(128000 / 907788) * 100`) and the result is
|
|
11
|
+
* a long float like `14.099783080260304` that overflows the TUI cell
|
|
12
|
+
* (issue #90).
|
|
13
|
+
*
|
|
14
|
+
* Behaviour:
|
|
15
|
+
* - Integer input (≤0.05 fractional drift) renders without decimals.
|
|
16
|
+
* - Anything else is rendered with one decimal digit, which is precise
|
|
17
|
+
* enough to convey the configured token budget without smearing across
|
|
18
|
+
* two lines in a narrow sidebar.
|
|
19
|
+
*
|
|
20
|
+
* Returns the formatted percentage WITHOUT the trailing `%` so callers can
|
|
21
|
+
* compose richer strings like `47.5% / 65%` consistently.
|
|
22
|
+
*/
|
|
23
|
+
export function formatThresholdPercent(value: number | undefined | null): string {
|
|
24
|
+
if (typeof value !== "number" || !Number.isFinite(value)) return "—";
|
|
25
|
+
const rounded = Math.round(value);
|
|
26
|
+
if (Math.abs(value - rounded) < 0.05) return String(rounded);
|
|
27
|
+
return value.toFixed(1);
|
|
28
|
+
}
|
package/src/shared/rpc-types.ts
CHANGED
|
@@ -45,6 +45,16 @@ export interface SidebarSnapshot {
|
|
|
45
45
|
* shows this as "Tool Definitions".
|
|
46
46
|
*/
|
|
47
47
|
toolDefinitionTokens: number;
|
|
48
|
+
/**
|
|
49
|
+
* Effective execute-threshold percentage for this session's active model,
|
|
50
|
+
* after per-model resolution and the tokens→percentage conversion (when
|
|
51
|
+
* `execute_threshold_tokens` applies). Surfaces in the sidebar / status
|
|
52
|
+
* dialog header alongside `usagePercentage` so users can see how close
|
|
53
|
+
* the session is to triggering compaction. Defaults to `65` when no live
|
|
54
|
+
* model is known yet — matches the runtime fallback used by the
|
|
55
|
+
* scheduler and transform paths.
|
|
56
|
+
*/
|
|
57
|
+
executeThreshold: number;
|
|
48
58
|
}
|
|
49
59
|
|
|
50
60
|
export interface StatusDetail extends SidebarSnapshot {
|
|
@@ -58,6 +58,7 @@ const EMPTY_SNAPSHOT: SidebarSnapshot = {
|
|
|
58
58
|
conversationTokens: 0,
|
|
59
59
|
toolCallTokens: 0,
|
|
60
60
|
toolDefinitionTokens: 0,
|
|
61
|
+
executeThreshold: 65,
|
|
61
62
|
};
|
|
62
63
|
|
|
63
64
|
/**
|
|
@@ -232,6 +233,49 @@ export interface TuiMessage {
|
|
|
232
233
|
sessionId?: string;
|
|
233
234
|
}
|
|
234
235
|
|
|
236
|
+
/**
|
|
237
|
+
* Fetch the current startup announcement from the server, if any.
|
|
238
|
+
* Returns `{show: false}` when there's nothing to announce or when the
|
|
239
|
+
* configured ANNOUNCEMENT_VERSION has already been dismissed.
|
|
240
|
+
*/
|
|
241
|
+
export interface AnnouncementResponse {
|
|
242
|
+
show: boolean;
|
|
243
|
+
version?: string;
|
|
244
|
+
features?: string[];
|
|
245
|
+
footer?: string;
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
export async function getAnnouncement(): Promise<AnnouncementResponse> {
|
|
249
|
+
if (!rpcClient) return { show: false };
|
|
250
|
+
try {
|
|
251
|
+
const result = await rpcClient.call<{
|
|
252
|
+
show?: boolean;
|
|
253
|
+
version?: string;
|
|
254
|
+
features?: string[];
|
|
255
|
+
footer?: string;
|
|
256
|
+
}>("get-announcement", {});
|
|
257
|
+
return {
|
|
258
|
+
show: result.show === true,
|
|
259
|
+
version: result.version,
|
|
260
|
+
features: Array.isArray(result.features) ? result.features : undefined,
|
|
261
|
+
footer: typeof result.footer === "string" ? result.footer : undefined,
|
|
262
|
+
};
|
|
263
|
+
} catch {
|
|
264
|
+
return { show: false };
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
/** Mark the current ANNOUNCEMENT_VERSION as dismissed on the server. */
|
|
269
|
+
export async function markAnnounced(): Promise<boolean> {
|
|
270
|
+
if (!rpcClient) return false;
|
|
271
|
+
try {
|
|
272
|
+
const result = await rpcClient.call<{ ok?: boolean }>("mark-announced", {});
|
|
273
|
+
return result.ok === true;
|
|
274
|
+
} catch {
|
|
275
|
+
return false;
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
|
|
235
279
|
/** Poll for pending server→TUI notifications via RPC. */
|
|
236
280
|
export async function consumeTuiMessages(): Promise<TuiMessage[]> {
|
|
237
281
|
if (!rpcClient) return [];
|