@cortexkit/opencode-magic-context 0.33.1 → 0.34.1
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 +20 -3
- package/dist/config/agent-disable.d.ts +22 -0
- package/dist/config/agent-disable.d.ts.map +1 -1
- package/dist/config/index.d.ts.map +1 -1
- package/dist/config/migrate-experimental.d.ts +2 -1
- package/dist/config/migrate-experimental.d.ts.map +1 -1
- package/dist/config/project-security.d.ts +9 -0
- package/dist/config/project-security.d.ts.map +1 -1
- package/dist/config/schema/magic-context.d.ts +90 -17
- package/dist/config/schema/magic-context.d.ts.map +1 -1
- package/dist/config/transform-mode.d.ts +2 -0
- package/dist/config/transform-mode.d.ts.map +1 -1
- package/dist/{conflict-warning-hook-n48c783v.js → conflict-warning-hook-fvpfvt0w.js} +1 -1
- package/dist/{derive-budgets-e9j09sjr.js → derive-budgets-9crp0cvd.js} +1 -1
- package/dist/features/builtin-commands/commands.d.ts +1 -1
- package/dist/features/builtin-commands/commands.d.ts.map +1 -1
- package/dist/features/magic-context/compaction-marker.d.ts +57 -0
- package/dist/features/magic-context/compaction-marker.d.ts.map +1 -1
- package/dist/features/magic-context/compartment-chunk-embedding.d.ts.map +1 -1
- package/dist/features/magic-context/dreamer/classify.d.ts +1 -0
- package/dist/features/magic-context/dreamer/classify.d.ts.map +1 -1
- package/dist/features/magic-context/dreamer/evaluate-smart-notes.d.ts.map +1 -1
- package/dist/features/magic-context/dreamer/map-memories.d.ts +1 -0
- package/dist/features/magic-context/dreamer/map-memories.d.ts.map +1 -1
- package/dist/features/magic-context/dreamer/refresh-primers.d.ts +1 -0
- package/dist/features/magic-context/dreamer/refresh-primers.d.ts.map +1 -1
- package/dist/features/magic-context/dreamer/storage-dream-runs.d.ts +2 -0
- package/dist/features/magic-context/dreamer/storage-dream-runs.d.ts.map +1 -1
- package/dist/features/magic-context/dreamer/task-executor.d.ts +4 -1
- package/dist/features/magic-context/dreamer/task-executor.d.ts.map +1 -1
- package/dist/features/magic-context/dreamer/task-gates.d.ts +19 -1
- package/dist/features/magic-context/dreamer/task-gates.d.ts.map +1 -1
- package/dist/features/magic-context/dreamer/task-registry.d.ts +28 -0
- package/dist/features/magic-context/dreamer/task-registry.d.ts.map +1 -1
- package/dist/features/magic-context/dreamer/task-scheduler.d.ts +7 -1
- package/dist/features/magic-context/dreamer/task-scheduler.d.ts.map +1 -1
- package/dist/features/magic-context/dreamer/verify.d.ts +1 -0
- package/dist/features/magic-context/dreamer/verify.d.ts.map +1 -1
- package/dist/features/magic-context/fail-closed-block.d.ts +10 -0
- package/dist/features/magic-context/fail-closed-block.d.ts.map +1 -1
- package/dist/features/magic-context/git-commits/storage-git-commit-embeddings.d.ts.map +1 -1
- package/dist/features/magic-context/git-commits/storage-git-commits.d.ts.map +1 -1
- package/dist/features/magic-context/memory/embedding-identity.d.ts.map +1 -1
- package/dist/features/magic-context/memory/embedding-local.d.ts +7 -1
- package/dist/features/magic-context/memory/embedding-local.d.ts.map +1 -1
- package/dist/features/magic-context/memory/embedding.d.ts.map +1 -1
- package/dist/features/magic-context/memory/memory-migration.d.ts.map +1 -1
- package/dist/features/magic-context/memory/project-identity.d.ts +2 -6
- package/dist/features/magic-context/memory/project-identity.d.ts.map +1 -1
- package/dist/features/magic-context/memory/storage-memory-embeddings.d.ts.map +1 -1
- package/dist/features/magic-context/migrations.d.ts +23 -1
- package/dist/features/magic-context/migrations.d.ts.map +1 -1
- package/dist/features/magic-context/mural/compress-cues.d.ts +1 -0
- package/dist/features/magic-context/mural/compress-cues.d.ts.map +1 -1
- package/dist/features/magic-context/mural/render-mural.d.ts.map +1 -1
- package/dist/features/magic-context/overflow-detection.d.ts +8 -1
- package/dist/features/magic-context/overflow-detection.d.ts.map +1 -1
- package/dist/features/magic-context/project-embedding-registry.d.ts.map +1 -1
- package/dist/features/magic-context/scheduler.d.ts.map +1 -1
- package/dist/features/magic-context/schema-fence-probe.d.ts +28 -0
- package/dist/features/magic-context/schema-fence-probe.d.ts.map +1 -0
- package/dist/features/magic-context/sidekick/agent.d.ts.map +1 -1
- package/dist/features/magic-context/smart-notes/compiler.d.ts.map +1 -1
- package/dist/features/magic-context/storage-db.d.ts +37 -2
- package/dist/features/magic-context/storage-db.d.ts.map +1 -1
- package/dist/features/magic-context/storage-meta-persisted.d.ts +38 -4
- package/dist/features/magic-context/storage-meta-persisted.d.ts.map +1 -1
- package/dist/features/magic-context/storage-meta.d.ts +1 -1
- package/dist/features/magic-context/storage-meta.d.ts.map +1 -1
- package/dist/features/magic-context/storage.d.ts +2 -2
- package/dist/features/magic-context/storage.d.ts.map +1 -1
- package/dist/features/magic-context/user-memory/review-user-memories.d.ts.map +1 -1
- package/dist/hooks/magic-context/boundary-execution.d.ts +1 -1
- package/dist/hooks/magic-context/boundary-execution.d.ts.map +1 -1
- package/dist/hooks/magic-context/cache-busting-signals.d.ts +1 -0
- package/dist/hooks/magic-context/cache-busting-signals.d.ts.map +1 -1
- package/dist/hooks/magic-context/child-session-spawn.d.ts +26 -0
- package/dist/hooks/magic-context/child-session-spawn.d.ts.map +1 -0
- package/dist/hooks/magic-context/command-handler.d.ts +3 -0
- package/dist/hooks/magic-context/command-handler.d.ts.map +1 -1
- package/dist/hooks/magic-context/compaction-off-transition.d.ts +96 -0
- package/dist/hooks/magic-context/compaction-off-transition.d.ts.map +1 -0
- package/dist/hooks/magic-context/compartment-runner-historian.d.ts +2 -0
- package/dist/hooks/magic-context/compartment-runner-historian.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-mapping.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-trigger.d.ts +12 -5
- package/dist/hooks/magic-context/compartment-trigger.d.ts.map +1 -1
- package/dist/hooks/magic-context/ctx-reduce-availability.d.ts +47 -1
- package/dist/hooks/magic-context/ctx-reduce-availability.d.ts.map +1 -1
- package/dist/hooks/magic-context/emergency-drop.d.ts +2 -2
- package/dist/hooks/magic-context/event-handler.d.ts +7 -0
- package/dist/hooks/magic-context/event-handler.d.ts.map +1 -1
- package/dist/hooks/magic-context/event-resolvers.d.ts +13 -21
- package/dist/hooks/magic-context/event-resolvers.d.ts.map +1 -1
- package/dist/hooks/magic-context/execute-status.d.ts +5 -1
- package/dist/hooks/magic-context/execute-status.d.ts.map +1 -1
- package/dist/hooks/magic-context/final-wire-token-estimate.d.ts +2 -0
- package/dist/hooks/magic-context/final-wire-token-estimate.d.ts.map +1 -1
- package/dist/hooks/magic-context/heuristic-cleanup.d.ts +1 -1
- package/dist/hooks/magic-context/hook-handlers.d.ts +1 -0
- package/dist/hooks/magic-context/hook-handlers.d.ts.map +1 -1
- package/dist/hooks/magic-context/hook.d.ts +14 -6
- package/dist/hooks/magic-context/hook.d.ts.map +1 -1
- package/dist/hooks/magic-context/inject-compartments.d.ts +10 -1
- package/dist/hooks/magic-context/inject-compartments.d.ts.map +1 -1
- package/dist/hooks/magic-context/live-session-state.d.ts +3 -0
- package/dist/hooks/magic-context/live-session-state.d.ts.map +1 -1
- package/dist/hooks/magic-context/protected-tail-boundary.d.ts.map +1 -1
- package/dist/hooks/magic-context/read-session-db.d.ts.map +1 -1
- package/dist/hooks/magic-context/read-session-true-raw-tokens.d.ts.map +1 -1
- package/dist/hooks/magic-context/rust-mode-transform.d.ts +3 -0
- package/dist/hooks/magic-context/rust-mode-transform.d.ts.map +1 -1
- package/dist/hooks/magic-context/send-session-notification.d.ts +20 -1
- package/dist/hooks/magic-context/send-session-notification.d.ts.map +1 -1
- package/dist/hooks/magic-context/transform-compartment-phase.d.ts +3 -0
- package/dist/hooks/magic-context/transform-compartment-phase.d.ts.map +1 -1
- package/dist/hooks/magic-context/transform-postprocess-phase.d.ts +31 -1
- package/dist/hooks/magic-context/transform-postprocess-phase.d.ts.map +1 -1
- package/dist/hooks/magic-context/transform.d.ts +29 -2
- package/dist/hooks/magic-context/transform.d.ts.map +1 -1
- package/dist/{index-56yjaeb2.js → index-2y0ybv5x.js} +11 -1
- package/dist/{index-ey1bc14z.js → index-b563sga5.js} +7 -264
- package/dist/{index-y1c9tcm3.js → index-cn960ffd.js} +101 -20
- package/dist/{index-xyaekybw.js → index-dtas91s5.js} +6 -4
- package/dist/index-fcyvznch.js +279 -0
- package/dist/{index-63dq2g4d.js → index-ry8rh8cv.js} +138 -24
- package/dist/{index-c2gahrks.js → index-x8spt5wy.js} +1682 -893
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1677 -723
- package/dist/plugin/dream-timer.d.ts +4 -2
- package/dist/plugin/dream-timer.d.ts.map +1 -1
- package/dist/plugin/embedding-bootstrap.d.ts.map +1 -1
- package/dist/plugin/hooks/create-session-hooks.d.ts +12 -1
- package/dist/plugin/hooks/create-session-hooks.d.ts.map +1 -1
- package/dist/plugin/messages-transform.d.ts +10 -0
- package/dist/plugin/messages-transform.d.ts.map +1 -1
- package/dist/plugin/rpc-handlers.d.ts +3 -7
- package/dist/plugin/rpc-handlers.d.ts.map +1 -1
- package/dist/plugin/tool-registry.d.ts +8 -0
- package/dist/plugin/tool-registry.d.ts.map +1 -1
- package/dist/{recomp-orchestrator-2yb2hfh2.js → recomp-orchestrator-k72ejkkh.js} +6 -4
- package/dist/send-session-notification-6njqewzk.js +20 -0
- package/dist/shared/conflict-detector.d.ts +38 -1
- package/dist/shared/conflict-detector.d.ts.map +1 -1
- package/dist/shared/conflict-fixer.d.ts +18 -1
- package/dist/shared/conflict-fixer.d.ts.map +1 -1
- package/dist/shared/context-limit-provenance.d.ts +3 -0
- package/dist/shared/context-limit-provenance.d.ts.map +1 -0
- package/dist/shared/escalation-bands.d.ts +9 -0
- package/dist/shared/escalation-bands.d.ts.map +1 -0
- package/dist/shared/format-threshold.d.ts +1 -1
- package/dist/shared/models-dev-cache.d.ts +29 -3
- package/dist/shared/models-dev-cache.d.ts.map +1 -1
- package/dist/shared/rpc-server.d.ts.map +1 -1
- package/dist/shared/rpc-types.d.ts +51 -1
- package/dist/shared/rpc-types.d.ts.map +1 -1
- package/dist/shared/rpc-utils.d.ts +19 -0
- package/dist/shared/rpc-utils.d.ts.map +1 -1
- package/dist/shared/storage-permissions.d.ts +5 -0
- package/dist/shared/storage-permissions.d.ts.map +1 -0
- package/dist/shared/tui-runtime-specifiers.d.ts +20 -0
- package/dist/shared/tui-runtime-specifiers.d.ts.map +1 -0
- package/dist/{storage-meta-session-td6bwtky.js → storage-meta-session-xt41zbw4.js} +2 -1
- package/dist/{task-config-5nx3qfz8.js → task-config-gbp7r23g.js} +1 -1
- package/dist/tui/compaction-off.d.ts +13 -0
- package/dist/tui/compaction-off.d.ts.map +1 -0
- package/dist/tui/data/context-db.d.ts.map +1 -1
- package/dist/tui-compiled/compaction-off.d.ts +13 -0
- package/dist/tui-compiled/compaction-off.d.ts.map +1 -0
- package/dist/tui-compiled/data/context-db.d.ts.map +1 -1
- package/package.json +6 -1
- package/src/agents/dreamer.ts +68 -0
- package/src/agents/hidden-agent-registrations.ts +331 -0
- package/src/agents/historian.ts +3 -0
- package/src/agents/language-directive.test.ts +138 -0
- package/src/agents/language-directive.ts +121 -0
- package/src/agents/magic-context-prompt.language.test.ts +35 -0
- package/src/agents/magic-context-prompt.test.ts +292 -0
- package/src/agents/magic-context-prompt.ts +182 -0
- package/src/agents/permissions.test.ts +252 -0
- package/src/agents/permissions.ts +186 -0
- package/src/agents/sidekick.ts +1 -0
- package/src/agents/smart-note-compiler.ts +1 -0
- package/src/config/agent-disable.ts +106 -0
- package/src/config/compaction-accessor-guard.test.ts +82 -0
- package/src/config/compaction-backcompat.test.ts +52 -0
- package/src/config/index.test.ts +936 -0
- package/src/config/index.ts +601 -0
- package/src/config/migrate-config-location.test.ts +332 -0
- package/src/config/migrate-config-location.ts +635 -0
- package/src/config/migrate-dreamer-v2.test.ts +287 -0
- package/src/config/migrate-dreamer-v2.ts +336 -0
- package/src/config/migrate-experimental.test.ts +41 -0
- package/src/config/migrate-experimental.ts +185 -0
- package/src/config/project-security.test.ts +388 -0
- package/src/config/project-security.ts +611 -0
- package/src/config/prune-config-leaf.test.ts +78 -0
- package/src/config/prune-config-leaf.ts +68 -0
- package/src/config/removed-config-keys.test.ts +116 -0
- package/src/config/schema/agent-overrides.ts +45 -0
- package/src/config/schema/magic-context.test.ts +359 -0
- package/src/config/schema/magic-context.ts +991 -0
- package/src/config/transform-mode.test.ts +62 -0
- package/src/config/transform-mode.ts +34 -0
- package/src/config/variable.test.ts +323 -0
- package/src/config/variable.ts +192 -0
- package/src/features/magic-context/defaults.ts +1 -0
- package/src/features/magic-context/dreamer/cron.ts +243 -0
- package/src/features/magic-context/dreamer/task-registry.ts +151 -0
- package/src/shared/conflict-detector.test.ts +121 -0
- package/src/shared/conflict-detector.ts +50 -3
- package/src/shared/conflict-fixer.test.ts +309 -0
- package/src/shared/conflict-fixer.ts +82 -5
- package/src/shared/context-limit-provenance.ts +6 -0
- package/src/shared/escalation-bands.test.ts +23 -0
- package/src/shared/escalation-bands.ts +18 -0
- package/src/shared/format-threshold.ts +1 -1
- package/src/shared/models-dev-cache.test.ts +171 -0
- package/src/shared/models-dev-cache.ts +195 -38
- package/src/shared/rpc-server.ts +27 -18
- package/src/shared/rpc-types.ts +55 -1
- package/src/shared/rpc-utils.test.ts +157 -0
- package/src/shared/rpc-utils.ts +137 -0
- package/src/shared/storage-permissions.ts +20 -0
- package/src/shared/tui-runtime-specifiers.ts +32 -0
- package/src/tui/compaction-off.test.ts +56 -0
- package/src/tui/compaction-off.ts +29 -0
- package/src/tui/data/context-db.ts +6 -0
- package/src/tui/index.tsx +113 -65
- package/src/tui/slots/sidebar-content.tsx +189 -91
- package/src/tui/tui-compiled-runtime-imports.test.ts +151 -0
- package/src/tui-compiled/compaction-off.ts +29 -0
- package/src/tui-compiled/data/context-db.ts +6 -0
- package/src/tui-compiled/index.tsx +558 -455
- package/src/tui-compiled/slots/sidebar-content.tsx +334 -169
- package/dist/send-session-notification-tghq9gnh.js +0 -11
- /package/dist/{index-v7fmc1s2.js → index-ygcng9t8.js} +0 -0
package/src/shared/rpc-types.ts
CHANGED
|
@@ -3,13 +3,31 @@
|
|
|
3
3
|
* Both sides import these — no SQLite dependency.
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
+
import type {
|
|
7
|
+
DreamTaskBacklogMap,
|
|
8
|
+
DreamTaskProgress,
|
|
9
|
+
} from "../features/magic-context/dreamer/task-registry";
|
|
10
|
+
|
|
6
11
|
export interface SidebarSnapshot {
|
|
7
12
|
sessionId: string;
|
|
8
13
|
usagePercentage: number;
|
|
9
14
|
inputTokens: number;
|
|
10
15
|
contextLimit: number;
|
|
16
|
+
/**
|
|
17
|
+
* Raw wire-input pressure against the resolved model window. This is separate
|
|
18
|
+
* from Magic Context's execute threshold so native-compaction UI never shows
|
|
19
|
+
* a threshold-relative fill percentage.
|
|
20
|
+
*/
|
|
21
|
+
native_context_usage_percentage?: number;
|
|
22
|
+
/**
|
|
23
|
+
* Magic Context compaction mode is resolved at startup and sent in the
|
|
24
|
+
* status-detail wire payload; snake_case matches that payload's field naming.
|
|
25
|
+
*/
|
|
26
|
+
compaction_enabled?: boolean;
|
|
11
27
|
systemPromptTokens: number;
|
|
12
28
|
compartmentCount: number;
|
|
29
|
+
/** Historical compartment rows retained while native compaction owns the window. */
|
|
30
|
+
archivedCompartmentCount?: number;
|
|
13
31
|
memoryCount: number;
|
|
14
32
|
memoryBlockCount: number;
|
|
15
33
|
pendingOpsCount: number;
|
|
@@ -18,6 +36,8 @@ export interface SidebarSnapshot {
|
|
|
18
36
|
sessionNoteCount: number;
|
|
19
37
|
readySmartNoteCount: number;
|
|
20
38
|
cacheTtl: string;
|
|
39
|
+
/** Persistent runtime failure shown directly in the sidebar when non-null. */
|
|
40
|
+
lastTransformError: string | null;
|
|
21
41
|
lastDreamerRunAt: number | null;
|
|
22
42
|
projectIdentity: string | null;
|
|
23
43
|
compartmentTokens: number;
|
|
@@ -69,7 +89,7 @@ export interface SidebarSnapshot {
|
|
|
69
89
|
executeThreshold: number;
|
|
70
90
|
/**
|
|
71
91
|
* True when `executeThreshold` was clamped down from a higher configured value
|
|
72
|
-
* (tokens config above
|
|
92
|
+
* (tokens config above 90% × contextLimit, or a percentage above the 90% cap).
|
|
73
93
|
* The sidebar/status dialog append a small marker so the user knows their
|
|
74
94
|
* configured value was reduced rather than applied verbatim (issue #241).
|
|
75
95
|
* Absent when no clamp occurred.
|
|
@@ -86,6 +106,10 @@ export interface SidebarSnapshot {
|
|
|
86
106
|
* sidebar "Recomp"/"Upgrade" progress bar and the /ctx-status dialog. Mirrors
|
|
87
107
|
* the runtime `RecompProgress` shape from compartment-runner-types.ts.
|
|
88
108
|
*/
|
|
109
|
+
/** Read-only per-task candidate counts; populated by the server RPC. */
|
|
110
|
+
dreamerBacklog?: DreamTaskBacklogMap;
|
|
111
|
+
/** Process-local task progress; absent when no Dreamer task is running. */
|
|
112
|
+
dreamerProgress?: DreamTaskProgress | null;
|
|
89
113
|
recompProgress?: {
|
|
90
114
|
/** "recomp" → "Recomp" labels; "upgrade" → "Upgrade" labels. */
|
|
91
115
|
kind?: "recomp" | "upgrade" | "embed" | "wrapup";
|
|
@@ -111,9 +135,20 @@ export interface StatusDetail extends SidebarSnapshot {
|
|
|
111
135
|
isSubagent: boolean;
|
|
112
136
|
pendingOps: Array<{ tagId: number; operation: string }>;
|
|
113
137
|
contextLimit: number;
|
|
138
|
+
/**
|
|
139
|
+
* Parsed cache TTL in ms. -1 = never expires (cacheTtl "never"; Infinity
|
|
140
|
+
* cannot ride JSON-RPC and 0 would be indistinguishable from unset). The
|
|
141
|
+
* values discriminate without the cacheNeverExpires flag: -1 never /
|
|
142
|
+
* N live. Falsy-value contract: see storage_versions for the precedent.
|
|
143
|
+
*/
|
|
114
144
|
cacheTtlMs: number;
|
|
145
|
+
/** Remaining ms in the idle-TTL window. -1 = never expires; 0 = expired
|
|
146
|
+
* (only meaningful when lastResponseTime > 0); N = live countdown. */
|
|
115
147
|
cacheRemainingMs: number;
|
|
116
148
|
cacheExpired: boolean;
|
|
149
|
+
/** True when cacheTtl is "never" — the idle-TTL heuristic is disabled on
|
|
150
|
+
* this lane. Redundant with cacheTtlMs === -1; kept as the readable form. */
|
|
151
|
+
cacheNeverExpires?: boolean;
|
|
117
152
|
executeThreshold: number;
|
|
118
153
|
/**
|
|
119
154
|
* Which config source produced `executeThreshold`. "tokens" means
|
|
@@ -135,6 +170,25 @@ export interface StatusDetail extends SidebarSnapshot {
|
|
|
135
170
|
toastDurationMs: number;
|
|
136
171
|
/** One-line status data for the experimental memory mural. */
|
|
137
172
|
mural?: { present: boolean; ageMs: number | null };
|
|
173
|
+
/**
|
|
174
|
+
* Stable storage-version probe: "which schema is the DB at, which fence does
|
|
175
|
+
* this binary carry". Field names are deliberately snake_case, mirroring the
|
|
176
|
+
* `storage_versions` block of the mc-module status envelope, so fleet probes
|
|
177
|
+
* parse one shape across both surfaces. The module cannot read context.db
|
|
178
|
+
* (the plugin owns it), so this surface supplies the live DB value; the module
|
|
179
|
+
* surface supplies the module-store value instead.
|
|
180
|
+
*/
|
|
181
|
+
storage_versions: {
|
|
182
|
+
/**
|
|
183
|
+
* Persisted schema version of context.db (MAX of schema_migrations).
|
|
184
|
+
* null = the version probe FAILED (read threw — broken/unreachable store);
|
|
185
|
+
* 0 = probe succeeded on a fresh DB without a migrations table. Distinct so
|
|
186
|
+
* readers never conflate broken-with-empty (fleet status-surface contract).
|
|
187
|
+
*/
|
|
188
|
+
context_db_schema_version: number | null;
|
|
189
|
+
/** Highest context.db schema version this plugin build supports. */
|
|
190
|
+
plugin_supported_version: number;
|
|
191
|
+
};
|
|
138
192
|
}
|
|
139
193
|
|
|
140
194
|
/** Embedding coverage for `/ctx-embed` status (mirrors getEmbeddingCoverageStatus). */
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
/// <reference types="bun-types" />
|
|
2
|
+
|
|
3
|
+
import { afterEach, describe, expect, test } from "bun:test";
|
|
4
|
+
import type { execFileSync } from "node:child_process";
|
|
5
|
+
import type { readFileSync } from "node:fs";
|
|
6
|
+
import {
|
|
7
|
+
__resetRpcIdentityTestHooks,
|
|
8
|
+
__setRpcIdentityTestHooks,
|
|
9
|
+
isPidIdentityPlausible,
|
|
10
|
+
type RpcPortFileRecord,
|
|
11
|
+
} from "./rpc-utils";
|
|
12
|
+
|
|
13
|
+
const PID = 1234;
|
|
14
|
+
const NOW_MS = 2_000_000;
|
|
15
|
+
const UPTIME_SECONDS = 1_000;
|
|
16
|
+
|
|
17
|
+
function record(startedAt: number): RpcPortFileRecord {
|
|
18
|
+
return { port: 43123, pid: PID, started_at: startedAt };
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function procStat(startTimeTicks: number): string {
|
|
22
|
+
// After the closing command-name parenthesis, field 3 is `state` and field
|
|
23
|
+
// 22 is the twentieth value in the suffix.
|
|
24
|
+
return `${PID} (opencode) S ${Array.from({ length: 18 }, () => "0").join(" ")} ${startTimeTicks}`;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function linuxFiles(files: Record<string, string | Error>): typeof readFileSync {
|
|
28
|
+
return ((path: string | URL) => {
|
|
29
|
+
const value = files[String(path)];
|
|
30
|
+
if (value instanceof Error) throw value;
|
|
31
|
+
if (value === undefined) throw new Error(`unexpected read: ${String(path)}`);
|
|
32
|
+
return value;
|
|
33
|
+
}) as typeof readFileSync;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function psOutput(output: string | Error): typeof execFileSync {
|
|
37
|
+
return (() => {
|
|
38
|
+
if (output instanceof Error) throw output;
|
|
39
|
+
return output;
|
|
40
|
+
}) as typeof execFileSync;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
afterEach(() => {
|
|
44
|
+
__resetRpcIdentityTestHooks();
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
describe("isPidIdentityPlausible", () => {
|
|
48
|
+
test("rejects a reused Linux PID when proc start time is substantially newer", () => {
|
|
49
|
+
const readPaths: string[] = [];
|
|
50
|
+
__setRpcIdentityTestHooks({
|
|
51
|
+
platform: "linux",
|
|
52
|
+
nowMs: () => NOW_MS,
|
|
53
|
+
readFileSync: ((path: string | URL) => {
|
|
54
|
+
readPaths.push(String(path));
|
|
55
|
+
const files = {
|
|
56
|
+
[`/proc/${PID}/stat`]: procStat(10_000),
|
|
57
|
+
"/proc/uptime": `${UPTIME_SECONDS}.0 0.0`,
|
|
58
|
+
};
|
|
59
|
+
return files[String(path) as keyof typeof files];
|
|
60
|
+
}) as typeof readFileSync,
|
|
61
|
+
execFileSync: (() => {
|
|
62
|
+
throw new Error("ps must not run on Linux");
|
|
63
|
+
}) as typeof execFileSync,
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
expect(isPidIdentityPlausible(record(500_000))).toBe(false);
|
|
67
|
+
expect(readPaths).toEqual([`/proc/${PID}/stat`, "/proc/uptime"]);
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
test("accepts a genuine Linux record when the process started no later than the record", () => {
|
|
71
|
+
__setRpcIdentityTestHooks({
|
|
72
|
+
platform: "linux",
|
|
73
|
+
nowMs: () => NOW_MS,
|
|
74
|
+
readFileSync: linuxFiles({
|
|
75
|
+
[`/proc/${PID}/stat`]: procStat(10_000),
|
|
76
|
+
"/proc/uptime": `${UPTIME_SECONDS}.0 0.0`,
|
|
77
|
+
}),
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
// The mocked process start is 1,100,000ms. The 120s tolerance is part of
|
|
81
|
+
// the contract because port-file creation follows process startup.
|
|
82
|
+
expect(isPidIdentityPlausible(record(1_000_000))).toBe(true);
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
test("treats an unreadable Linux start-time probe as live", () => {
|
|
86
|
+
__setRpcIdentityTestHooks({
|
|
87
|
+
platform: "linux",
|
|
88
|
+
readFileSync: linuxFiles({
|
|
89
|
+
[`/proc/${PID}/stat`]: new Error("procfs unavailable"),
|
|
90
|
+
}),
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
expect(isPidIdentityPlausible(record(500_000))).toBe(true);
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
test("uses the legacy Linux command fallback and fails closed on probe errors", () => {
|
|
97
|
+
__setRpcIdentityTestHooks({
|
|
98
|
+
platform: "linux",
|
|
99
|
+
readFileSync: linuxFiles({
|
|
100
|
+
[`/proc/${PID}/cmdline`]: "/usr/sbin/opendkim --config /etc/opendkim.conf",
|
|
101
|
+
}),
|
|
102
|
+
});
|
|
103
|
+
expect(isPidIdentityPlausible(record(0))).toBe(false);
|
|
104
|
+
|
|
105
|
+
__setRpcIdentityTestHooks({
|
|
106
|
+
platform: "linux",
|
|
107
|
+
readFileSync: linuxFiles({
|
|
108
|
+
[`/proc/${PID}/cmdline`]: "/usr/local/bin/opencode serve",
|
|
109
|
+
}),
|
|
110
|
+
});
|
|
111
|
+
expect(isPidIdentityPlausible(record(0))).toBe(true);
|
|
112
|
+
|
|
113
|
+
__setRpcIdentityTestHooks({
|
|
114
|
+
platform: "linux",
|
|
115
|
+
readFileSync: linuxFiles({
|
|
116
|
+
[`/proc/${PID}/cmdline`]: new Error("procfs unavailable"),
|
|
117
|
+
}),
|
|
118
|
+
});
|
|
119
|
+
expect(isPidIdentityPlausible(record(0))).toBe(true);
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
test("uses ps start time and command probes on non-Linux platforms", () => {
|
|
123
|
+
__setRpcIdentityTestHooks({
|
|
124
|
+
platform: "darwin",
|
|
125
|
+
execFileSync: psOutput("Mon Aug 7 00:00:00 1970"),
|
|
126
|
+
});
|
|
127
|
+
expect(
|
|
128
|
+
isPidIdentityPlausible(record(Date.parse("Mon Aug 7 00:00:00 1970") - 121_000)),
|
|
129
|
+
).toBe(false);
|
|
130
|
+
|
|
131
|
+
__setRpcIdentityTestHooks({
|
|
132
|
+
platform: "darwin",
|
|
133
|
+
execFileSync: psOutput("Mon Aug 7 00:00:00 1970"),
|
|
134
|
+
});
|
|
135
|
+
expect(
|
|
136
|
+
isPidIdentityPlausible(record(Date.parse("Mon Aug 7 00:00:00 1970") - 120_000)),
|
|
137
|
+
).toBe(true);
|
|
138
|
+
|
|
139
|
+
__setRpcIdentityTestHooks({
|
|
140
|
+
platform: "darwin",
|
|
141
|
+
execFileSync: psOutput("/usr/sbin/opendkim -f"),
|
|
142
|
+
});
|
|
143
|
+
expect(isPidIdentityPlausible(record(0))).toBe(false);
|
|
144
|
+
|
|
145
|
+
__setRpcIdentityTestHooks({
|
|
146
|
+
platform: "darwin",
|
|
147
|
+
execFileSync: psOutput("/Applications/OpenCode.app/Contents/MacOS/opencode"),
|
|
148
|
+
});
|
|
149
|
+
expect(isPidIdentityPlausible(record(0))).toBe(true);
|
|
150
|
+
|
|
151
|
+
__setRpcIdentityTestHooks({
|
|
152
|
+
platform: "darwin",
|
|
153
|
+
execFileSync: psOutput(new Error("ps unavailable")),
|
|
154
|
+
});
|
|
155
|
+
expect(isPidIdentityPlausible(record(0))).toBe(true);
|
|
156
|
+
});
|
|
157
|
+
});
|
package/src/shared/rpc-utils.ts
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
|
+
import { execFileSync } from "node:child_process";
|
|
1
2
|
import { createHash } from "node:crypto";
|
|
3
|
+
import { readFileSync } from "node:fs";
|
|
2
4
|
import { join } from "node:path";
|
|
3
5
|
|
|
4
6
|
export interface RpcPortFileRecord {
|
|
@@ -58,6 +60,141 @@ export function isPidAlive(pid: number): boolean {
|
|
|
58
60
|
}
|
|
59
61
|
}
|
|
60
62
|
|
|
63
|
+
const RPC_IDENTITY_SKEW_TOLERANCE_MS = 120_000;
|
|
64
|
+
const LINUX_CLOCK_TICKS_PER_SECOND = 100;
|
|
65
|
+
const PS_PROBE_TIMEOUT_MS = 1_000;
|
|
66
|
+
const OPEN_CODE_COMMAND_MARKERS = ["opencode", "node", "bun", "electron"];
|
|
67
|
+
|
|
68
|
+
let rpcIdentityReadFileSync: typeof readFileSync = readFileSync;
|
|
69
|
+
let rpcIdentityExecFileSync: typeof execFileSync = execFileSync;
|
|
70
|
+
let rpcIdentityPlatform: NodeJS.Platform = process.platform;
|
|
71
|
+
let rpcIdentityNowMs: () => number = () => Date.now();
|
|
72
|
+
|
|
73
|
+
function parseLinuxProcessStartTime(statContent: string, uptimeContent: string): number | null {
|
|
74
|
+
const closingCommandName = statContent.lastIndexOf(")");
|
|
75
|
+
if (closingCommandName < 0) return null;
|
|
76
|
+
|
|
77
|
+
// The fields after the command name begin at field 3 (`state`), so field 22
|
|
78
|
+
// (`starttime`) is index 19 in this suffix. The command name can contain ')',
|
|
79
|
+
// hence the last closing parenthesis rather than the first one is significant.
|
|
80
|
+
const statFields = statContent
|
|
81
|
+
.slice(closingCommandName + 1)
|
|
82
|
+
.trim()
|
|
83
|
+
.split(/\s+/);
|
|
84
|
+
const startTimeTicks = Number(statFields[19]);
|
|
85
|
+
const uptimeSeconds = Number(uptimeContent.trim().split(/\s+/)[0]);
|
|
86
|
+
if (
|
|
87
|
+
!Number.isFinite(startTimeTicks) ||
|
|
88
|
+
startTimeTicks < 0 ||
|
|
89
|
+
!Number.isFinite(uptimeSeconds) ||
|
|
90
|
+
uptimeSeconds < 0
|
|
91
|
+
) {
|
|
92
|
+
return null;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
const processStartTime =
|
|
96
|
+
rpcIdentityNowMs() -
|
|
97
|
+
uptimeSeconds * 1_000 +
|
|
98
|
+
(startTimeTicks / LINUX_CLOCK_TICKS_PER_SECOND) * 1_000;
|
|
99
|
+
return Number.isFinite(processStartTime) ? processStartTime : null;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
function readLinuxProcessStartTime(pid: number): number | null {
|
|
103
|
+
try {
|
|
104
|
+
const statContent = String(rpcIdentityReadFileSync(`/proc/${pid}/stat`, "utf8"));
|
|
105
|
+
const uptimeContent = String(rpcIdentityReadFileSync("/proc/uptime", "utf8"));
|
|
106
|
+
return parseLinuxProcessStartTime(statContent, uptimeContent);
|
|
107
|
+
} catch {
|
|
108
|
+
return null;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
function readPsProcessStartTime(pid: number): number | null {
|
|
113
|
+
try {
|
|
114
|
+
const output = rpcIdentityExecFileSync("ps", ["-p", String(pid), "-o", "lstart="], {
|
|
115
|
+
encoding: "utf8",
|
|
116
|
+
timeout: PS_PROBE_TIMEOUT_MS,
|
|
117
|
+
});
|
|
118
|
+
const processStartTime = Date.parse(String(output).trim());
|
|
119
|
+
return Number.isFinite(processStartTime) ? processStartTime : null;
|
|
120
|
+
} catch {
|
|
121
|
+
return null;
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
function readLinuxProcessCommand(pid: number): string | null {
|
|
126
|
+
try {
|
|
127
|
+
return String(rpcIdentityReadFileSync(`/proc/${pid}/cmdline`, "utf8"));
|
|
128
|
+
} catch {
|
|
129
|
+
return null;
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
function readPsProcessCommand(pid: number): string | null {
|
|
134
|
+
try {
|
|
135
|
+
const output = rpcIdentityExecFileSync("ps", ["-p", String(pid), "-o", "command="], {
|
|
136
|
+
encoding: "utf8",
|
|
137
|
+
timeout: PS_PROBE_TIMEOUT_MS,
|
|
138
|
+
});
|
|
139
|
+
return String(output);
|
|
140
|
+
} catch {
|
|
141
|
+
return null;
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
function commandLooksLikeOpenCode(command: string): boolean {
|
|
146
|
+
const normalized = command.toLowerCase();
|
|
147
|
+
return OPEN_CODE_COMMAND_MARKERS.some((marker) => normalized.includes(marker));
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* Verify that a live PID still belongs to the process that wrote a port record.
|
|
152
|
+
*
|
|
153
|
+
* A PID can be reused after its original process exits. On Linux, procfs gives
|
|
154
|
+
* us a process start time without spawning a helper; other platforms use `ps`
|
|
155
|
+
* only on this cold database-open guard path. Legacy records without a start
|
|
156
|
+
* time use a weaker command-name check, and every probe failure stays live so
|
|
157
|
+
* this identity check cannot weaken the migration guard on uncertainty.
|
|
158
|
+
*/
|
|
159
|
+
export function isPidIdentityPlausible(record: RpcPortFileRecord): boolean {
|
|
160
|
+
if (!Number.isInteger(record.pid) || record.pid <= 0) return false;
|
|
161
|
+
|
|
162
|
+
if (Number.isFinite(record.started_at) && record.started_at > 0) {
|
|
163
|
+
const processStartTime =
|
|
164
|
+
rpcIdentityPlatform === "linux"
|
|
165
|
+
? readLinuxProcessStartTime(record.pid)
|
|
166
|
+
: readPsProcessStartTime(record.pid);
|
|
167
|
+
if (processStartTime === null) return true;
|
|
168
|
+
return processStartTime <= record.started_at + RPC_IDENTITY_SKEW_TOLERANCE_MS;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
const command =
|
|
172
|
+
rpcIdentityPlatform === "linux"
|
|
173
|
+
? readLinuxProcessCommand(record.pid)
|
|
174
|
+
: readPsProcessCommand(record.pid);
|
|
175
|
+
if (command === null) return true;
|
|
176
|
+
return commandLooksLikeOpenCode(command);
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
export function __setRpcIdentityTestHooks(hooks: {
|
|
180
|
+
readFileSync?: typeof readFileSync;
|
|
181
|
+
execFileSync?: typeof execFileSync;
|
|
182
|
+
platform?: NodeJS.Platform;
|
|
183
|
+
nowMs?: () => number;
|
|
184
|
+
}): void {
|
|
185
|
+
rpcIdentityReadFileSync = hooks.readFileSync ?? readFileSync;
|
|
186
|
+
rpcIdentityExecFileSync = hooks.execFileSync ?? execFileSync;
|
|
187
|
+
rpcIdentityPlatform = hooks.platform ?? process.platform;
|
|
188
|
+
rpcIdentityNowMs = hooks.nowMs ?? (() => Date.now());
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
export function __resetRpcIdentityTestHooks(): void {
|
|
192
|
+
rpcIdentityReadFileSync = readFileSync;
|
|
193
|
+
rpcIdentityExecFileSync = execFileSync;
|
|
194
|
+
rpcIdentityPlatform = process.platform;
|
|
195
|
+
rpcIdentityNowMs = () => Date.now();
|
|
196
|
+
}
|
|
197
|
+
|
|
61
198
|
export function parseRpcPortFile(content: string, fallbackPid = 0): RpcPortFileRecord | null {
|
|
62
199
|
const trimmed = content.trim();
|
|
63
200
|
if (!trimmed) return null;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Process-wide storage permission policy resolved from trusted user config.
|
|
3
|
+
*
|
|
4
|
+
* Storage is shared by OpenCode and Pi, so all writers consult one setting before
|
|
5
|
+
* applying POSIX modes. The default preserves the historical owner-only policy.
|
|
6
|
+
*/
|
|
7
|
+
let enforcePrivateStoragePermissions = true;
|
|
8
|
+
|
|
9
|
+
export function setStoragePrivatePermissionEnforcement(enforce: boolean): void {
|
|
10
|
+
enforcePrivateStoragePermissions = enforce;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export function shouldEnforcePrivateStoragePermissions(): boolean {
|
|
14
|
+
return enforcePrivateStoragePermissions;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/** Test-only reset for suites that exercise both permission policies in one process. */
|
|
18
|
+
export function __resetStoragePrivatePermissionEnforcementForTests(): void {
|
|
19
|
+
enforcePrivateStoragePermissions = true;
|
|
20
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The module specifiers the compiled TUI is allowed to resolve through OpenCode's
|
|
3
|
+
* process-wide OpenTUI runtime registry (`opentui:runtime-module:<encoded>`).
|
|
4
|
+
*
|
|
5
|
+
* Single source of truth, shared by `scripts/build-tui.ts` (which rewrites these
|
|
6
|
+
* specifiers during the Solid transform) and
|
|
7
|
+
* `src/tui/tui-compiled-runtime-imports.test.ts` (which verifies every emitted
|
|
8
|
+
* specifier names a real export). Keeping one list means the guard test cannot
|
|
9
|
+
* drift into skipping a specifier the build actually rewrites — a skipped
|
|
10
|
+
* specifier is exactly the silent hole the test exists to close.
|
|
11
|
+
*
|
|
12
|
+
* Lives in `src/shared/` rather than `src/tui/` on purpose: `build-tui.ts` copies
|
|
13
|
+
* every file under `src/tui/` into the shipped `src/tui-compiled/` bundle, and
|
|
14
|
+
* this list is build/test tooling that the runtime bundle must not carry.
|
|
15
|
+
*/
|
|
16
|
+
export const TUI_RUNTIME_SPECIFIERS = [
|
|
17
|
+
"@opentui/core",
|
|
18
|
+
"@opentui/core/testing",
|
|
19
|
+
"@opentui/solid",
|
|
20
|
+
"@opentui/solid/components",
|
|
21
|
+
"@opentui/solid/jsx-runtime",
|
|
22
|
+
"@opentui/solid/jsx-dev-runtime",
|
|
23
|
+
"solid-js",
|
|
24
|
+
"solid-js/store",
|
|
25
|
+
] as const;
|
|
26
|
+
|
|
27
|
+
export type TuiRuntimeSpecifier = (typeof TUI_RUNTIME_SPECIFIERS)[number];
|
|
28
|
+
|
|
29
|
+
/** Virtual module id OpenCode registers for a runtime specifier. */
|
|
30
|
+
export function runtimeModuleId(specifier: string): string {
|
|
31
|
+
return `opentui:runtime-module:${encodeURIComponent(specifier)}`;
|
|
32
|
+
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { expect, test } from "bun:test";
|
|
2
|
+
import type { SidebarSnapshot } from "../shared/rpc-types";
|
|
3
|
+
import { compactionOffSidebarRows, nativeCompactionContextLabel } from "./compaction-off";
|
|
4
|
+
|
|
5
|
+
function snapshot(overrides: Partial<SidebarSnapshot> = {}): SidebarSnapshot {
|
|
6
|
+
return {
|
|
7
|
+
sessionId: "ses-native-ui",
|
|
8
|
+
usagePercentage: 63.1,
|
|
9
|
+
inputTokens: 41_000,
|
|
10
|
+
contextLimit: 100_000,
|
|
11
|
+
native_context_usage_percentage: 41,
|
|
12
|
+
compaction_enabled: false,
|
|
13
|
+
systemPromptTokens: 0,
|
|
14
|
+
compartmentCount: 12,
|
|
15
|
+
archivedCompartmentCount: 3,
|
|
16
|
+
memoryCount: 5,
|
|
17
|
+
memoryBlockCount: 2,
|
|
18
|
+
pendingOpsCount: 4,
|
|
19
|
+
historianRunning: true,
|
|
20
|
+
compartmentInProgress: true,
|
|
21
|
+
sessionNoteCount: 2,
|
|
22
|
+
readySmartNoteCount: 1,
|
|
23
|
+
cacheTtl: "5m",
|
|
24
|
+
lastDreamerRunAt: null,
|
|
25
|
+
projectIdentity: null,
|
|
26
|
+
compartmentTokens: 0,
|
|
27
|
+
factTokens: 0,
|
|
28
|
+
memoryTokens: 0,
|
|
29
|
+
docsTokens: 0,
|
|
30
|
+
profileTokens: 0,
|
|
31
|
+
conversationTokens: 0,
|
|
32
|
+
toolCallTokens: 0,
|
|
33
|
+
toolDefinitionTokens: 0,
|
|
34
|
+
executeThreshold: 65,
|
|
35
|
+
...overrides,
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
test("renders raw input-versus-model usage rather than threshold-relative fill", () => {
|
|
40
|
+
const value = nativeCompactionContextLabel(snapshot());
|
|
41
|
+
|
|
42
|
+
expect(value).toBe("Context: 41.0% · native compaction");
|
|
43
|
+
expect(value).not.toBe("Context: 63.1% · native compaction");
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
test("keeps historical compartments as a static archived row", () => {
|
|
47
|
+
const initialRows = compactionOffSidebarRows(snapshot());
|
|
48
|
+
const activeCountChangedRows = compactionOffSidebarRows(snapshot({ compartmentCount: 99 }));
|
|
49
|
+
|
|
50
|
+
expect(initialRows).toEqual([
|
|
51
|
+
{ label: "Memories", value: "5" },
|
|
52
|
+
{ label: "Notes", value: "2" },
|
|
53
|
+
{ label: "Archived compartments", value: "3" },
|
|
54
|
+
]);
|
|
55
|
+
expect(activeCountChangedRows).toEqual(initialRows);
|
|
56
|
+
});
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import type { SidebarSnapshot } from "../shared/rpc-types";
|
|
2
|
+
|
|
3
|
+
export interface CompactionOffSidebarRow {
|
|
4
|
+
label: "Memories" | "Notes" | "Archived compartments";
|
|
5
|
+
value: string;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Formats context pressure from the current wire input and the model context
|
|
10
|
+
* limit. It deliberately does not use the stored Magic Context threshold
|
|
11
|
+
* percentage, because native compaction acts on the model window instead.
|
|
12
|
+
*/
|
|
13
|
+
export function nativeCompactionContextLabel(snapshot: SidebarSnapshot): string {
|
|
14
|
+
if (snapshot.contextLimit <= 0) return "Context: unknown · native compaction";
|
|
15
|
+
const percentage = (snapshot.inputTokens / snapshot.contextLimit) * 100;
|
|
16
|
+
return `Context: ${percentage.toFixed(1)}% · native compaction`;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export function compactionOffSidebarRows(snapshot: SidebarSnapshot): CompactionOffSidebarRow[] {
|
|
20
|
+
const rows: CompactionOffSidebarRow[] = [
|
|
21
|
+
{ label: "Memories", value: String(snapshot.memoryCount) },
|
|
22
|
+
{ label: "Notes", value: String(snapshot.sessionNoteCount) },
|
|
23
|
+
];
|
|
24
|
+
const archivedCount = snapshot.archivedCompartmentCount ?? 0;
|
|
25
|
+
if (archivedCount > 0) {
|
|
26
|
+
rows.push({ label: "Archived compartments", value: String(archivedCount) });
|
|
27
|
+
}
|
|
28
|
+
return rows;
|
|
29
|
+
}
|
|
@@ -65,6 +65,7 @@ const EMPTY_SNAPSHOT: SidebarSnapshot = {
|
|
|
65
65
|
sessionNoteCount: 0,
|
|
66
66
|
readySmartNoteCount: 0,
|
|
67
67
|
cacheTtl: "5m",
|
|
68
|
+
lastTransformError: null,
|
|
68
69
|
lastDreamerRunAt: null,
|
|
69
70
|
projectIdentity: null,
|
|
70
71
|
compartmentTokens: 0,
|
|
@@ -194,6 +195,7 @@ export async function loadStatusDetail(
|
|
|
194
195
|
cacheTtlMs: 0,
|
|
195
196
|
cacheRemainingMs: 0,
|
|
196
197
|
cacheExpired: false,
|
|
198
|
+
cacheNeverExpires: false,
|
|
197
199
|
executeThreshold: 65,
|
|
198
200
|
executeThresholdMode: "percentage",
|
|
199
201
|
protectedTagCount: 20,
|
|
@@ -202,6 +204,10 @@ export async function loadStatusDetail(
|
|
|
202
204
|
compressionBudget: null,
|
|
203
205
|
compressionUsage: null,
|
|
204
206
|
toastDurationMs: 5000,
|
|
207
|
+
storage_versions: {
|
|
208
|
+
context_db_schema_version: null,
|
|
209
|
+
plugin_supported_version: 0,
|
|
210
|
+
},
|
|
205
211
|
};
|
|
206
212
|
|
|
207
213
|
if (!rpcClient) return emptyDetail;
|