@cortexkit/opencode-magic-context 0.24.1 → 0.25.0
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 +4 -2
- package/dist/agents/magic-context-prompt.d.ts.map +1 -1
- package/dist/features/magic-context/compartment-chunk-embedding.d.ts +8 -0
- package/dist/features/magic-context/compartment-chunk-embedding.d.ts.map +1 -1
- package/dist/features/magic-context/memory/embedding-local.d.ts +4 -0
- package/dist/features/magic-context/memory/embedding-local.d.ts.map +1 -1
- package/dist/features/magic-context/memory/storage-memory-embeddings.d.ts +6 -0
- package/dist/features/magic-context/memory/storage-memory-embeddings.d.ts.map +1 -1
- package/dist/features/magic-context/project-embedding-registry.d.ts +38 -0
- package/dist/features/magic-context/project-embedding-registry.d.ts.map +1 -1
- package/dist/features/magic-context/storage-db.d.ts.map +1 -1
- package/dist/features/magic-context/storage-meta-session.d.ts +1 -0
- package/dist/features/magic-context/storage-meta-session.d.ts.map +1 -1
- package/dist/features/magic-context/storage-meta-shared.d.ts +2 -1
- package/dist/features/magic-context/storage-meta-shared.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-tags.d.ts +10 -0
- package/dist/features/magic-context/storage-tags.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/types.d.ts +1 -0
- package/dist/features/magic-context/types.d.ts.map +1 -1
- package/dist/hooks/magic-context/apply-operations.d.ts +3 -25
- package/dist/hooks/magic-context/apply-operations.d.ts.map +1 -1
- package/dist/hooks/magic-context/caveman-cleanup.d.ts +1 -0
- package/dist/hooks/magic-context/caveman-cleanup.d.ts.map +1 -1
- package/dist/hooks/magic-context/channel2-delivery.d.ts +2 -0
- package/dist/hooks/magic-context/channel2-delivery.d.ts.map +1 -1
- package/dist/hooks/magic-context/command-handler.d.ts +7 -5
- package/dist/hooks/magic-context/command-handler.d.ts.map +1 -1
- package/dist/hooks/magic-context/ctx-reduce-nudge.d.ts +7 -2
- package/dist/hooks/magic-context/ctx-reduce-nudge.d.ts.map +1 -1
- package/dist/hooks/magic-context/embed-session-state.d.ts +14 -0
- package/dist/hooks/magic-context/embed-session-state.d.ts.map +1 -0
- package/dist/hooks/magic-context/event-handler.d.ts.map +1 -1
- package/dist/hooks/magic-context/format-embed-status.d.ts +9 -0
- package/dist/hooks/magic-context/format-embed-status.d.ts.map +1 -0
- package/dist/hooks/magic-context/heuristic-cleanup.d.ts +1 -0
- package/dist/hooks/magic-context/heuristic-cleanup.d.ts.map +1 -1
- package/dist/hooks/magic-context/hook-handlers.d.ts.map +1 -1
- package/dist/hooks/magic-context/hook.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-true-raw-tokens.d.ts +1 -1
- package/dist/hooks/magic-context/read-session-true-raw-tokens.d.ts.map +1 -1
- package/dist/hooks/magic-context/strip-content.d.ts +0 -1
- package/dist/hooks/magic-context/strip-content.d.ts.map +1 -1
- package/dist/hooks/magic-context/tag-content-primitives.d.ts +2 -0
- package/dist/hooks/magic-context/tag-content-primitives.d.ts.map +1 -1
- package/dist/hooks/magic-context/tag-messages.d.ts.map +1 -1
- package/dist/hooks/magic-context/tool-drop-target.d.ts +1 -1
- package/dist/hooks/magic-context/tool-drop-target.d.ts.map +1 -1
- package/dist/hooks/magic-context/tool-reclaim.d.ts +12 -0
- package/dist/hooks/magic-context/tool-reclaim.d.ts.map +1 -0
- package/dist/hooks/magic-context/transform-operations.d.ts +1 -1
- package/dist/hooks/magic-context/transform-operations.d.ts.map +1 -1
- 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 +1103 -408
- package/dist/plugin/conflict-warning-hook.d.ts.map +1 -1
- package/dist/plugin/hooks/create-session-hooks.d.ts.map +1 -1
- package/dist/plugin/rpc-handlers.d.ts.map +1 -1
- package/dist/shared/announcement.d.ts +1 -1
- package/dist/shared/model-suggestion-retry.d.ts.map +1 -1
- package/dist/shared/rpc-types.d.ts +20 -0
- package/dist/shared/rpc-types.d.ts.map +1 -1
- package/dist/shared/sqlite.d.ts +5 -1
- package/dist/shared/sqlite.d.ts.map +1 -1
- package/dist/tools/ctx-expand/constants.d.ts +1 -1
- package/dist/tools/ctx-expand/constants.d.ts.map +1 -1
- package/dist/tools/ctx-expand/render.d.ts +43 -0
- package/dist/tools/ctx-expand/render.d.ts.map +1 -0
- package/dist/tools/ctx-expand/tools.d.ts.map +1 -1
- package/dist/tools/ctx-expand/types.d.ts +6 -2
- package/dist/tools/ctx-expand/types.d.ts.map +1 -1
- package/dist/tools/ctx-reduce/constants.d.ts +1 -1
- package/dist/tools/ctx-reduce/constants.d.ts.map +1 -1
- package/dist/tui/data/context-db.d.ts +4 -2
- package/dist/tui/data/context-db.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/shared/announcement.ts +6 -6
- package/src/shared/model-suggestion-retry.test.ts +61 -1
- package/src/shared/model-suggestion-retry.ts +22 -0
- package/src/shared/rpc-types.ts +11 -0
- package/src/shared/sqlite-bind-style.test.ts +82 -0
- package/src/shared/sqlite.ts +30 -1
- package/src/shared/tag-transcript.test.ts +3 -1
- package/src/shared/tag-transcript.ts +19 -17
- package/src/tui/data/context-db.ts +34 -2
- package/src/tui/index.tsx +58 -4
- package/src/tui/slots/sidebar-content.tsx +7 -2
|
@@ -611,7 +611,7 @@ function tagToolPart(args: TagToolPartArgs): void {
|
|
|
611
611
|
args.part.setText(tagged);
|
|
612
612
|
}
|
|
613
613
|
|
|
614
|
-
args.targets.set(tagId, buildToolTarget(args.part, args.message));
|
|
614
|
+
args.targets.set(tagId, buildToolTarget(args.part, args.message, tagId));
|
|
615
615
|
}
|
|
616
616
|
|
|
617
617
|
function setToolContentOrText(part: TranscriptPart, content: string): boolean {
|
|
@@ -679,10 +679,11 @@ function buildAggregateTarget(tagId: number, occurrences: ToolOccurrence[]): Tag
|
|
|
679
679
|
return any ? "removed" : "absent";
|
|
680
680
|
},
|
|
681
681
|
truncate(): "truncated" | "absent" {
|
|
682
|
-
//
|
|
683
|
-
//
|
|
684
|
-
//
|
|
685
|
-
|
|
682
|
+
// Skeleton-drop: replace BOTH halves' content with the one
|
|
683
|
+
// canonical `[dropped §N§]` placeholder (byte-identical to a full
|
|
684
|
+
// drop and to OpenCode). Frozen by the dropMode column → replays
|
|
685
|
+
// the same string every pass. The tool_use call survives intact.
|
|
686
|
+
const sentinel = `[dropped \u00a7${tagId}\u00a7]`;
|
|
686
687
|
let any = false;
|
|
687
688
|
for (const occ of occurrences) {
|
|
688
689
|
if (setToolContentOrText(occ.part, sentinel)) {
|
|
@@ -739,16 +740,17 @@ function buildTextTarget(
|
|
|
739
740
|
}
|
|
740
741
|
|
|
741
742
|
/**
|
|
742
|
-
* TagTarget for a tag-eligible tool part. Tool parts get full-drop
|
|
743
|
-
*
|
|
744
|
-
* `
|
|
745
|
-
*
|
|
746
|
-
*
|
|
747
|
-
*
|
|
743
|
+
* TagTarget for a tag-eligible tool part. Tool parts get full-drop or
|
|
744
|
+
* skeleton-drop treatment from `applyFlushedStatuses` based on the stored
|
|
745
|
+
* `drop_mode` column. Both render the SAME canonical `[dropped §N§]`
|
|
746
|
+
* placeholder — full-drop replaces the whole pair, skeleton-drop keeps the
|
|
747
|
+
* tool_use call and replaces only its output. One placeholder string,
|
|
748
|
+
* byte-identical across passes and across harnesses.
|
|
748
749
|
*/
|
|
749
750
|
function buildToolTarget(
|
|
750
751
|
part: TranscriptPart,
|
|
751
752
|
message: { info: { id?: string; role: string } },
|
|
753
|
+
tagId: number,
|
|
752
754
|
): TagTarget {
|
|
753
755
|
return {
|
|
754
756
|
setContent(content: string): boolean {
|
|
@@ -765,15 +767,15 @@ function buildToolTarget(
|
|
|
765
767
|
// For Pi the current Transcript contract treats both
|
|
766
768
|
// invocation and result parts symmetrically — both expose
|
|
767
769
|
// setText / setToolOutput.
|
|
768
|
-
const replaced = part.replaceWithSentinel(`[dropped \u00a7${
|
|
770
|
+
const replaced = part.replaceWithSentinel(`[dropped \u00a7${tagId}\u00a7]`);
|
|
769
771
|
return replaced ? "removed" : "absent";
|
|
770
772
|
},
|
|
771
773
|
truncate(): "truncated" | "absent" {
|
|
772
|
-
//
|
|
773
|
-
//
|
|
774
|
-
//
|
|
775
|
-
//
|
|
776
|
-
const ok = setToolContentOrText(part,
|
|
774
|
+
// Skeleton-drop: replace the tool output with the one canonical
|
|
775
|
+
// `[dropped §N§]` placeholder (byte-identical to a full drop and to
|
|
776
|
+
// OpenCode). Frozen by the dropMode column, so it replays the same
|
|
777
|
+
// string every pass. The tool_use call itself survives intact.
|
|
778
|
+
const ok = setToolContentOrText(part, `[dropped \u00a7${tagId}\u00a7]`);
|
|
777
779
|
return ok ? "truncated" : "absent";
|
|
778
780
|
},
|
|
779
781
|
message: {
|
|
@@ -5,9 +5,14 @@
|
|
|
5
5
|
import os from "node:os";
|
|
6
6
|
import path from "node:path";
|
|
7
7
|
import { MagicContextRpcClient } from "../../shared/rpc-client";
|
|
8
|
-
import type {
|
|
8
|
+
import type {
|
|
9
|
+
EmbedDetail,
|
|
10
|
+
RpcNotificationMessage,
|
|
11
|
+
SidebarSnapshot,
|
|
12
|
+
StatusDetail,
|
|
13
|
+
} from "../../shared/rpc-types";
|
|
9
14
|
|
|
10
|
-
export type { SidebarSnapshot, StatusDetail };
|
|
15
|
+
export type { EmbedDetail, SidebarSnapshot, StatusDetail };
|
|
11
16
|
|
|
12
17
|
let rpcClient: MagicContextRpcClient | null = null;
|
|
13
18
|
let rpcGeneration = 0;
|
|
@@ -218,6 +223,33 @@ export async function loadStatusDetail(
|
|
|
218
223
|
}
|
|
219
224
|
}
|
|
220
225
|
|
|
226
|
+
const EMPTY_EMBED_DETAIL: EmbedDetail = {
|
|
227
|
+
enabled: false,
|
|
228
|
+
model: "off",
|
|
229
|
+
provider: "off",
|
|
230
|
+
session: { embedded: 0, total: 0 },
|
|
231
|
+
memories: { embedded: 0, total: 0 },
|
|
232
|
+
commits: { embedded: 0, total: 0, gitEnabled: false },
|
|
233
|
+
statusText: "Embedding is off (no provider configured).",
|
|
234
|
+
};
|
|
235
|
+
|
|
236
|
+
/** Fetch embedding coverage status for `/ctx-embed` via RPC. */
|
|
237
|
+
export async function loadEmbedDetail(sessionId: string, directory: string): Promise<EmbedDetail> {
|
|
238
|
+
if (!rpcClient) return EMPTY_EMBED_DETAIL;
|
|
239
|
+
try {
|
|
240
|
+
const result = await rpcClient.call<EmbedDetail>("embed-detail", {
|
|
241
|
+
sessionId,
|
|
242
|
+
directory,
|
|
243
|
+
});
|
|
244
|
+
if ((result as unknown as Record<string, unknown>).error) {
|
|
245
|
+
return EMPTY_EMBED_DETAIL;
|
|
246
|
+
}
|
|
247
|
+
return result;
|
|
248
|
+
} catch {
|
|
249
|
+
return EMPTY_EMBED_DETAIL;
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
|
|
221
253
|
/** Get compartment count via RPC. */
|
|
222
254
|
export async function getCompartmentCount(sessionId: string): Promise<number> {
|
|
223
255
|
if (!rpcClient) return 0;
|
package/src/tui/index.tsx
CHANGED
|
@@ -6,7 +6,7 @@ import { createMemo } from "solid-js"
|
|
|
6
6
|
import type { TuiPlugin, TuiPluginApi, TuiThemeCurrent } from "@opencode-ai/plugin/tui"
|
|
7
7
|
import { createSidebarContentSlot, kickRecompProgressRefresh } from "./slots/sidebar-content"
|
|
8
8
|
import packageJson from "../../package.json"
|
|
9
|
-
import { closeRpc, consumeTuiMessages, dismissUpgradeReminder, getAnnouncement, getCompartmentCount, getRpcGeneration, initRpcClient, loadStatusDetail, markAnnounced, markTuiMessagesHandled, requestRecomp, requestUpgrade, type TuiMessage, type StatusDetail } from "./data/context-db"
|
|
9
|
+
import { closeRpc, consumeTuiMessages, dismissUpgradeReminder, getAnnouncement, getCompartmentCount, getRpcGeneration, initRpcClient, loadEmbedDetail, loadStatusDetail, markAnnounced, markTuiMessagesHandled, requestRecomp, requestUpgrade, type EmbedDetail, type TuiMessage, type StatusDetail } from "./data/context-db"
|
|
10
10
|
import { formatThresholdPercent } from "../shared/format-threshold"
|
|
11
11
|
import { detectConflicts } from "../shared/conflict-detector"
|
|
12
12
|
import { fixConflicts } from "../shared/conflict-fixer"
|
|
@@ -353,7 +353,7 @@ const StatusDialog = (props: { api: TuiPluginApi; s: StatusDetail }) => {
|
|
|
353
353
|
}
|
|
354
354
|
if (p.phase === "migration") return <R t={t()} l="Status" v={p.note ?? "Migrating memories ⟳"} fg={t().warning} />
|
|
355
355
|
if (p.phase === "done") return <R t={t()} l="Status" v={`✓ ${verb} complete`} fg={t().accent} />
|
|
356
|
-
if (p.phase === "skipped") return <R t={t()} l="Status" v={
|
|
356
|
+
if (p.phase === "skipped") return <R t={t()} l="Status" v={p.message ?? `${verb} stopped early`} fg={t().textMuted} />
|
|
357
357
|
return <R t={t()} l="Status" v={`✗ ${verb} failed${p.message ? `: ${p.message}` : ""}`} fg={t().error} />
|
|
358
358
|
})()}
|
|
359
359
|
</box>
|
|
@@ -580,14 +580,53 @@ async function showStatusDialog(api: TuiPluginApi, targetSessionId = getSessionI
|
|
|
580
580
|
const directory = api.state.path.directory ?? ""
|
|
581
581
|
const modelKey = getModelKeyFromMessages(api, sessionId)
|
|
582
582
|
const detail = await loadStatusDetail(sessionId, directory, modelKey)
|
|
583
|
-
// Ack only after the dialog is actually shown for the same active session;
|
|
584
|
-
// route switches while the RPC detail load is in flight must leave it pending.
|
|
585
583
|
if (getSessionId(api) !== sessionId) return false
|
|
586
584
|
|
|
587
585
|
api.ui.dialog.replace(() => <StatusDialog api={api} s={detail} />)
|
|
588
586
|
return true
|
|
589
587
|
}
|
|
590
588
|
|
|
589
|
+
const EmbedDialog = (props: { api: TuiPluginApi; detail: EmbedDetail }) => {
|
|
590
|
+
const theme = createMemo(() => (props.api as any).theme.current)
|
|
591
|
+
const t = () => theme()
|
|
592
|
+
const lines = () => props.detail.statusText.split("\n")
|
|
593
|
+
return (
|
|
594
|
+
<box flexDirection="column" width="100%" paddingLeft={2} paddingRight={2} paddingTop={1} paddingBottom={1}>
|
|
595
|
+
<box justifyContent="center" width="100%" marginBottom={1}>
|
|
596
|
+
<text fg={t().accent}><b>Embedding</b></text>
|
|
597
|
+
</box>
|
|
598
|
+
{lines().map((line) => (
|
|
599
|
+
<text fg={t().text}>{line}</text>
|
|
600
|
+
))}
|
|
601
|
+
</box>
|
|
602
|
+
)
|
|
603
|
+
}
|
|
604
|
+
|
|
605
|
+
async function showEmbedDialog(api: TuiPluginApi, targetSessionId = getSessionId(api)): Promise<boolean> {
|
|
606
|
+
const sessionId = targetSessionId
|
|
607
|
+
if (!sessionId) {
|
|
608
|
+
api.ui.toast({ message: "No active session", variant: "warning" })
|
|
609
|
+
return false
|
|
610
|
+
}
|
|
611
|
+
const directory = api.state.path.directory ?? ""
|
|
612
|
+
const detail = await loadEmbedDetail(sessionId, directory)
|
|
613
|
+
if (getSessionId(api) !== sessionId) return false
|
|
614
|
+
api.ui.dialog.replace(() => <EmbedDialog api={api} detail={detail} />)
|
|
615
|
+
return true
|
|
616
|
+
}
|
|
617
|
+
|
|
618
|
+
function showResultDialog(api: TuiPluginApi, title: string, message: string): boolean {
|
|
619
|
+
api.ui.dialog.replace(() => (
|
|
620
|
+
<api.ui.DialogAlert
|
|
621
|
+
title={title}
|
|
622
|
+
message={message}
|
|
623
|
+
onConfirm={() => {}}
|
|
624
|
+
/>
|
|
625
|
+
))
|
|
626
|
+
return true
|
|
627
|
+
}
|
|
628
|
+
|
|
629
|
+
|
|
591
630
|
/**
|
|
592
631
|
* Register Magic Context command palette entries, preferring the v1.14.42+
|
|
593
632
|
* `keymap.registerLayer` API and falling back to the legacy
|
|
@@ -842,6 +881,21 @@ const tui: TuiPlugin = async (api, _options, meta) => {
|
|
|
842
881
|
if (showUpgradeDialog(api, resume, requestedSessionId)) {
|
|
843
882
|
handledMessageIds.add(msg.id)
|
|
844
883
|
}
|
|
884
|
+
} else if (action === "show-embed-dialog") {
|
|
885
|
+
if (await showEmbedDialog(api, requestedSessionId)) {
|
|
886
|
+
handledMessageIds.add(msg.id)
|
|
887
|
+
}
|
|
888
|
+
} else if (action === "show-flush-dialog") {
|
|
889
|
+
const flushMsg = String(msg.payload?.message ?? "Flushed.")
|
|
890
|
+
if (showResultDialog(api, "Flush", flushMsg)) {
|
|
891
|
+
handledMessageIds.add(msg.id)
|
|
892
|
+
}
|
|
893
|
+
} else if (action === "show-result-dialog") {
|
|
894
|
+
const title = String(msg.payload?.title ?? "Magic Context")
|
|
895
|
+
const body = String(msg.payload?.message ?? "")
|
|
896
|
+
if (showResultDialog(api, title, body)) {
|
|
897
|
+
handledMessageIds.add(msg.id)
|
|
898
|
+
}
|
|
845
899
|
}
|
|
846
900
|
}
|
|
847
901
|
}
|
|
@@ -394,8 +394,13 @@ const RecompProgressSection = (props: {
|
|
|
394
394
|
case "done":
|
|
395
395
|
return { text: `✓ ${verb()} complete`, color: props.theme.success ?? props.theme.accent }
|
|
396
396
|
case "skipped":
|
|
397
|
-
//
|
|
398
|
-
|
|
397
|
+
// Neutral terse status next to the bold verb header; the full,
|
|
398
|
+
// self-contained reason (lease-busy "try again shortly" vs a
|
|
399
|
+
// partial-stall "run /ctx-embed start again") renders on its own
|
|
400
|
+
// line below. Don't re-prepend verb here (it's already the bold
|
|
401
|
+
// header — doing so read as "EmbedEmbed"), and don't hardcode
|
|
402
|
+
// "retry shortly" (wrong for a partial stall).
|
|
403
|
+
return { text: "stopped", color: props.theme.textMuted }
|
|
399
404
|
case "failed":
|
|
400
405
|
return { text: `✗ ${verb()} failed`, color: props.theme.error }
|
|
401
406
|
}
|