@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
package/src/tui/index.tsx
CHANGED
|
@@ -6,7 +6,8 @@ import { createMemo } from "solid-js"
|
|
|
6
6
|
import type { TuiPlugin, TuiPluginApi, TuiThemeCurrent } from "@opencode-ai/plugin/tui"
|
|
7
7
|
import { createSidebarContentSlot } from "./slots/sidebar-content"
|
|
8
8
|
import packageJson from "../../package.json"
|
|
9
|
-
import { closeRpc, consumeTuiMessages, getCompartmentCount, initRpcClient, loadStatusDetail, requestRecomp, type StatusDetail } from "./data/context-db"
|
|
9
|
+
import { closeRpc, consumeTuiMessages, getAnnouncement, getCompartmentCount, initRpcClient, loadStatusDetail, markAnnounced, requestRecomp, type StatusDetail } from "./data/context-db"
|
|
10
|
+
import { formatThresholdPercent } from "../shared/format-threshold"
|
|
10
11
|
import { detectConflicts } from "../shared/conflict-detector"
|
|
11
12
|
import { fixConflicts } from "../shared/conflict-fixer"
|
|
12
13
|
import { readJsoncFile } from "../shared/jsonc-parser"
|
|
@@ -290,11 +291,16 @@ const StatusDialog = (props: { api: TuiPluginApi; s: StatusDetail }) => {
|
|
|
290
291
|
<text fg={t().textMuted}>v{packageJson.version}</text>
|
|
291
292
|
</box>
|
|
292
293
|
|
|
293
|
-
{/* Context summary line
|
|
294
|
+
{/* Context summary line. Mirrors the sidebar header layout
|
|
295
|
+
("47.5% / 65% 475K / 1.0M") so users can recognize the
|
|
296
|
+
same shape in the status dialog. The execute threshold tells
|
|
297
|
+
them how close they are to compaction triggering. */}
|
|
294
298
|
<box flexDirection="row" justifyContent="space-between" width="100%">
|
|
295
|
-
<text fg={t().text}>Context</text>
|
|
296
299
|
<text fg={s().usagePercentage >= 80 ? t().error : s().usagePercentage >= 65 ? t().warning : t().accent}>
|
|
297
|
-
<b>{s().usagePercentage.toFixed(1)}%</b>
|
|
300
|
+
<b>{s().usagePercentage.toFixed(1)}%</b> / {formatThresholdPercent(s().executeThreshold)}%
|
|
301
|
+
</text>
|
|
302
|
+
<text fg={s().usagePercentage >= 80 ? t().error : s().usagePercentage >= 65 ? t().warning : t().accent}>
|
|
303
|
+
{fmt(s().inputTokens)} / {contextLimit() > 0 ? fmt(contextLimit()) : "?"} tokens
|
|
298
304
|
</text>
|
|
299
305
|
</box>
|
|
300
306
|
|
|
@@ -336,7 +342,7 @@ const StatusDialog = (props: { api: TuiPluginApi; s: StatusDetail }) => {
|
|
|
336
342
|
<R t={t()} l="Configured" v={s().cacheTtl} />
|
|
337
343
|
<R t={t()} l="Last response" v={s().lastResponseTime > 0 ? `${Math.round(elapsed() / 1000)}s ago` : "never"} />
|
|
338
344
|
<R t={t()} l="Remaining" v={s().cacheExpired ? "expired" : `${Math.round(s().cacheRemainingMs / 1000)}s`} fg={s().cacheExpired ? t().warning : t().textMuted} />
|
|
339
|
-
<R t={t()} l="Auto-execute" v={s().cacheExpired ? "yes (expired)" : `at TTL or ≥${s().executeThreshold}%`} fg={t().textMuted} />
|
|
345
|
+
<R t={t()} l="Auto-execute" v={s().cacheExpired ? "yes (expired)" : `at TTL or ≥${formatThresholdPercent(s().executeThreshold)}%`} fg={t().textMuted} />
|
|
340
346
|
<box marginTop={1}>
|
|
341
347
|
<text fg={t().text}><b>Memory</b></text>
|
|
342
348
|
</box>
|
|
@@ -346,7 +352,7 @@ const StatusDialog = (props: { api: TuiPluginApi; s: StatusDetail }) => {
|
|
|
346
352
|
{/* Right column */}
|
|
347
353
|
<box flexDirection="column" flexGrow={1} flexBasis={0}>
|
|
348
354
|
<text fg={t().text}><b>Rolling Nudges</b></text>
|
|
349
|
-
<R t={t()} l="Execute threshold" v={`${s().executeThreshold}%`} />
|
|
355
|
+
<R t={t()} l="Execute threshold" v={`${formatThresholdPercent(s().executeThreshold)}%`} />
|
|
350
356
|
<R t={t()} l="Nudge anchor" v={`${fmt(s().lastNudgeTokens)} tok`} />
|
|
351
357
|
<R t={t()} l="Interval" v={`${fmt(s().nudgeInterval)} tok`} fg={t().textMuted} />
|
|
352
358
|
<R t={t()} l="Next nudge after" v={`${fmt(s().nextNudgeAfter)} tok`} />
|
|
@@ -558,6 +564,63 @@ function registerCommandPaletteEntries(api: TuiPluginApi): void {
|
|
|
558
564
|
// via RPC.
|
|
559
565
|
}
|
|
560
566
|
|
|
567
|
+
/**
|
|
568
|
+
* Show the one-shot "What's new" dialog on TUI startup if the server tells us
|
|
569
|
+
* to. The server is the source of truth: it has the version + features
|
|
570
|
+
* constants AND owns the persistence file. We just render and report back.
|
|
571
|
+
*
|
|
572
|
+
* Failure-tolerant by design — if the server isn't ready or the RPC fails,
|
|
573
|
+
* we silently skip (the next TUI launch will retry).
|
|
574
|
+
*/
|
|
575
|
+
/**
|
|
576
|
+
* URLs render as plain text. Modern terminals (iTerm2, kitty, WezTerm, Ghostty,
|
|
577
|
+
* recent macOS Terminal) auto-detect URLs and let users Cmd-click; older
|
|
578
|
+
* terminals require manual copy. We tried opentui's `<a href>` JSX intrinsic
|
|
579
|
+
* for application-level OSC 8 clickability, but it's a span-like element that
|
|
580
|
+
* forced text out of opentui's word-wrap mode, causing bullets to bleed past
|
|
581
|
+
* the dialog border. Pure-string children of `<text>` wrap correctly, so the
|
|
582
|
+
* AFT-style DialogAlert + plain string is the right surface here.
|
|
583
|
+
*/
|
|
584
|
+
async function showStartupAnnouncement(api: TuiPluginApi): Promise<void> {
|
|
585
|
+
try {
|
|
586
|
+
const ann = await getAnnouncement()
|
|
587
|
+
if (!ann.show || !ann.version || !ann.features || ann.features.length === 0) return
|
|
588
|
+
|
|
589
|
+
const title = `Magic Context v${ann.version}`
|
|
590
|
+
const lines: string[] = [
|
|
591
|
+
"What's new:",
|
|
592
|
+
"",
|
|
593
|
+
...ann.features.map((line) => ` • ${line}`),
|
|
594
|
+
]
|
|
595
|
+
if (ann.footer && ann.footer.trim().length > 0) {
|
|
596
|
+
// Blank-line separator keeps the persistent footer (Discord invite,
|
|
597
|
+
// etc.) visually distinct from the version-specific bullets.
|
|
598
|
+
lines.push("", ann.footer)
|
|
599
|
+
}
|
|
600
|
+
const message = lines.join("\n")
|
|
601
|
+
|
|
602
|
+
api.ui.dialog.replace(
|
|
603
|
+
() => (
|
|
604
|
+
<api.ui.DialogAlert
|
|
605
|
+
title={title}
|
|
606
|
+
message={message}
|
|
607
|
+
onConfirm={() => {
|
|
608
|
+
void markAnnounced()
|
|
609
|
+
}}
|
|
610
|
+
/>
|
|
611
|
+
),
|
|
612
|
+
() => {
|
|
613
|
+
// User dismissed via Escape rather than confirming. Mark
|
|
614
|
+
// dismissed anyway — they saw the dialog, that's the contract.
|
|
615
|
+
void markAnnounced()
|
|
616
|
+
},
|
|
617
|
+
)
|
|
618
|
+
} catch {
|
|
619
|
+
// RPC not ready yet (port file missing or transient HTTP failure) —
|
|
620
|
+
// silently skip. The next TUI start re-checks.
|
|
621
|
+
}
|
|
622
|
+
}
|
|
623
|
+
|
|
561
624
|
const tui: TuiPlugin = async (api, _options, meta) => {
|
|
562
625
|
// Initialize RPC client for server communication
|
|
563
626
|
const directory = api.state.path.directory ?? ""
|
|
@@ -618,6 +681,12 @@ const tui: TuiPlugin = async (api, _options, meta) => {
|
|
|
618
681
|
return
|
|
619
682
|
}
|
|
620
683
|
|
|
684
|
+
// Show one-shot release announcement after conflict gate.
|
|
685
|
+
// Fire-and-forget: if the server isn't ready or RPC fails, the next TUI
|
|
686
|
+
// launch will retry. Dialog only appears once per ANNOUNCEMENT_VERSION
|
|
687
|
+
// (persisted via mark-announced RPC writing last_announced_version).
|
|
688
|
+
void showStartupAnnouncement(api)
|
|
689
|
+
|
|
621
690
|
// Note: if TUI plugin is loaded, tui.json already has our entry.
|
|
622
691
|
// But if the user added it manually and later removes it, or if they
|
|
623
692
|
// use setup/doctor which handles tui.json, this code is already running.
|
|
@@ -3,6 +3,7 @@ import { createEffect, createMemo, createSignal, on, onCleanup } from "solid-js"
|
|
|
3
3
|
import type { TuiSlotPlugin, TuiPluginApi, TuiThemeCurrent } from "@opencode-ai/plugin/tui"
|
|
4
4
|
import packageJson from "../../../package.json"
|
|
5
5
|
import { loadSidebarSnapshot, type SidebarSnapshot } from "../data/context-db"
|
|
6
|
+
import { formatThresholdPercent } from "../../shared/format-threshold"
|
|
6
7
|
|
|
7
8
|
const SINGLE_BORDER = { type: "single" } as any
|
|
8
9
|
const REFRESH_DEBOUNCE_MS = 150
|
|
@@ -47,7 +48,15 @@ const TokenBreakdown = (props: {
|
|
|
47
48
|
theme: TuiThemeCurrent
|
|
48
49
|
snapshot: SidebarSnapshot
|
|
49
50
|
}) => {
|
|
50
|
-
|
|
51
|
+
// Bar width is hardcoded because the @opencode-ai/plugin/tui slot API does
|
|
52
|
+
// not expose the rendered sidebar width to plugins. 24 chars is the safe
|
|
53
|
+
// floor that fits every realistic sidebar configuration we've observed —
|
|
54
|
+
// OpenCode TUI's sidebar narrows with the terminal, and 36 (the previous
|
|
55
|
+
// value) overflowed users' actual layouts (issue #90), wrapping the bar
|
|
56
|
+
// onto a second line. If/when the slot API surfaces a real width, this
|
|
57
|
+
// should become Math.max(20, providedWidth) like the Pi status dialog
|
|
58
|
+
// already does (`Math.max(20, innerWidth)`).
|
|
59
|
+
const barWidth = 24
|
|
51
60
|
|
|
52
61
|
const segments = createMemo<TokenSegment[]>(() => {
|
|
53
62
|
const s = props.snapshot
|
|
@@ -363,9 +372,21 @@ const SidebarContent = (props: {
|
|
|
363
372
|
{s() && s()!.inputTokens > 0 && (
|
|
364
373
|
<box marginTop={1} flexDirection="column">
|
|
365
374
|
{(s()?.contextLimit ?? 0) > 0 && (
|
|
366
|
-
<box width="100%" flexDirection="row" justifyContent="
|
|
375
|
+
<box width="100%" flexDirection="row" justifyContent="space-between">
|
|
376
|
+
{/* Left: current usage vs the per-model execute
|
|
377
|
+
threshold (the value Magic Context compares
|
|
378
|
+
against when scheduling historian / drops).
|
|
379
|
+
"47.5% / 65%" tells the user how close they
|
|
380
|
+
are to the next compaction trigger. */}
|
|
367
381
|
<text fg={contextSummaryColor()}>
|
|
368
|
-
<b>{s()!.usagePercentage.toFixed(1)}%</b>
|
|
382
|
+
<b>{s()!.usagePercentage.toFixed(1)}%</b> / {formatThresholdPercent(s()!.executeThreshold)}%
|
|
383
|
+
</text>
|
|
384
|
+
{/* Right: absolute token usage vs the model's
|
|
385
|
+
full context window (separate from the
|
|
386
|
+
execute threshold so users still know how
|
|
387
|
+
much headroom remains beyond compaction). */}
|
|
388
|
+
<text fg={contextSummaryColor()}>
|
|
389
|
+
{compactTokens(s()!.inputTokens)} / {compactTokens(s()!.contextLimit)}
|
|
369
390
|
</text>
|
|
370
391
|
</box>
|
|
371
392
|
)}
|