@tarquinen/opencode-dcp 3.2.5-beta0 → 3.2.7-beta0
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/dist/lib/token-utils.js +2 -2
- package/dist/lib/token-utils.js.map +1 -1
- package/index.ts +141 -0
- package/lib/analysis/tokens.ts +225 -0
- package/lib/auth.ts +37 -0
- package/lib/commands/compression-targets.ts +137 -0
- package/lib/commands/context.ts +132 -0
- package/lib/commands/decompress.ts +275 -0
- package/lib/commands/help.ts +76 -0
- package/lib/commands/index.ts +11 -0
- package/lib/commands/manual.ts +125 -0
- package/lib/commands/recompress.ts +224 -0
- package/lib/commands/stats.ts +148 -0
- package/lib/commands/sweep.ts +268 -0
- package/lib/compress/index.ts +3 -0
- package/lib/compress/message-utils.ts +250 -0
- package/lib/compress/message.ts +137 -0
- package/lib/compress/pipeline.ts +106 -0
- package/lib/compress/protected-content.ts +154 -0
- package/lib/compress/range-utils.ts +308 -0
- package/lib/compress/range.ts +180 -0
- package/lib/compress/search.ts +267 -0
- package/lib/compress/state.ts +268 -0
- package/lib/compress/timing.ts +77 -0
- package/lib/compress/types.ts +108 -0
- package/lib/compress-permission.ts +25 -0
- package/lib/config.ts +1071 -0
- package/lib/hooks.ts +378 -0
- package/lib/host-permissions.ts +101 -0
- package/lib/logger.ts +235 -0
- package/lib/message-ids.ts +172 -0
- package/lib/messages/index.ts +8 -0
- package/lib/messages/inject/inject.ts +215 -0
- package/lib/messages/inject/subagent-results.ts +82 -0
- package/lib/messages/inject/utils.ts +374 -0
- package/lib/messages/priority.ts +102 -0
- package/lib/messages/prune.ts +238 -0
- package/lib/messages/query.ts +56 -0
- package/lib/messages/reasoning-strip.ts +40 -0
- package/lib/messages/sync.ts +124 -0
- package/lib/messages/utils.ts +187 -0
- package/lib/prompts/compress-message.ts +42 -0
- package/lib/prompts/compress-range.ts +60 -0
- package/lib/prompts/context-limit-nudge.ts +18 -0
- package/lib/prompts/extensions/nudge.ts +43 -0
- package/lib/prompts/extensions/system.ts +32 -0
- package/lib/prompts/extensions/tool.ts +35 -0
- package/lib/prompts/index.ts +29 -0
- package/lib/prompts/iteration-nudge.ts +6 -0
- package/lib/prompts/store.ts +467 -0
- package/lib/prompts/system.ts +33 -0
- package/lib/prompts/turn-nudge.ts +10 -0
- package/lib/protected-patterns.ts +128 -0
- package/lib/state/index.ts +4 -0
- package/lib/state/persistence.ts +256 -0
- package/lib/state/state.ts +190 -0
- package/lib/state/tool-cache.ts +98 -0
- package/lib/state/types.ts +112 -0
- package/lib/state/utils.ts +334 -0
- package/lib/strategies/deduplication.ts +127 -0
- package/lib/strategies/index.ts +2 -0
- package/lib/strategies/purge-errors.ts +88 -0
- package/lib/subagents/subagent-results.ts +74 -0
- package/lib/token-utils.ts +162 -0
- package/lib/ui/notification.ts +346 -0
- package/lib/ui/utils.ts +287 -0
- package/package.json +12 -3
- package/tui/data/context.ts +177 -0
- package/tui/index.tsx +34 -0
- package/tui/routes/summary.tsx +175 -0
- package/tui/shared/names.ts +9 -0
- package/tui/shared/theme.ts +58 -0
- package/tui/shared/types.ts +38 -0
- package/tui/slots/sidebar-content.tsx +502 -0
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
/** @jsxImportSource @opentui/solid */
|
|
2
|
+
import { createMemo, createSignal, For, Show } from "solid-js"
|
|
3
|
+
import { useKeyboard } from "@opentui/solid"
|
|
4
|
+
import type { TuiPluginApi } from "@opencode-ai/plugin/tui"
|
|
5
|
+
import { getPalette, type DcpPalette } from "../shared/theme"
|
|
6
|
+
import { LABEL, NAMES } from "../shared/names"
|
|
7
|
+
|
|
8
|
+
const SINGLE_BORDER = { type: "single" } as any
|
|
9
|
+
|
|
10
|
+
interface SummaryRouteParams {
|
|
11
|
+
topic?: string
|
|
12
|
+
summary?: string
|
|
13
|
+
sessionID?: string
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
interface CollapsibleSection {
|
|
17
|
+
label: string
|
|
18
|
+
content: string
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
interface ParsedSummary {
|
|
22
|
+
body: string
|
|
23
|
+
sections: CollapsibleSection[]
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
// These patterns must stay in sync with the exact headings produced by
|
|
27
|
+
// lib/compress (compactSummary output). If compression wording changes,
|
|
28
|
+
// update these patterns accordingly.
|
|
29
|
+
const SECTION_HEADINGS: { pattern: RegExp; label: string }[] = [
|
|
30
|
+
{
|
|
31
|
+
pattern: /\n*The following user messages were sent in this conversation verbatim:/,
|
|
32
|
+
label: "Protected User Messages",
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
pattern: /\n*The following protected tools were used in this conversation as well:/,
|
|
36
|
+
label: "Protected Tools",
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
pattern:
|
|
40
|
+
/\n*The following previously compressed summaries were also part of this conversation section:/,
|
|
41
|
+
label: "Included Compressed Summaries",
|
|
42
|
+
},
|
|
43
|
+
]
|
|
44
|
+
|
|
45
|
+
function parseSummary(raw: string): ParsedSummary {
|
|
46
|
+
if (!raw) return { body: "", sections: [] }
|
|
47
|
+
|
|
48
|
+
const matches: { index: number; length: number; label: string }[] = []
|
|
49
|
+
for (const heading of SECTION_HEADINGS) {
|
|
50
|
+
const match = raw.match(heading.pattern)
|
|
51
|
+
if (match && match.index !== undefined) {
|
|
52
|
+
matches.push({ index: match.index, length: match[0].length, label: heading.label })
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
if (matches.length === 0) {
|
|
57
|
+
return { body: raw, sections: [] }
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
matches.sort((a, b) => a.index - b.index)
|
|
61
|
+
|
|
62
|
+
const body = raw.slice(0, matches[0].index).trimEnd()
|
|
63
|
+
const sections: CollapsibleSection[] = []
|
|
64
|
+
|
|
65
|
+
for (let i = 0; i < matches.length; i++) {
|
|
66
|
+
const start = matches[i].index + matches[i].length
|
|
67
|
+
const end = i + 1 < matches.length ? matches[i + 1].index : raw.length
|
|
68
|
+
const content = raw.slice(start, end).trim()
|
|
69
|
+
if (content) {
|
|
70
|
+
sections.push({ label: matches[i].label, content })
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
return { body, sections }
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function CollapsibleSectionRow(props: { section: CollapsibleSection; palette: DcpPalette }) {
|
|
78
|
+
const [expanded, setExpanded] = createSignal(false)
|
|
79
|
+
|
|
80
|
+
return (
|
|
81
|
+
<box flexDirection="column" width="100%" marginTop={1}>
|
|
82
|
+
<box flexDirection="row" width="100%" height={1}>
|
|
83
|
+
<box
|
|
84
|
+
backgroundColor={props.palette.base}
|
|
85
|
+
height={1}
|
|
86
|
+
onMouseUp={() => setExpanded(!expanded())}
|
|
87
|
+
>
|
|
88
|
+
<text fg={props.palette.accent}>{expanded() ? " ▼ " : " ▶ "}</text>
|
|
89
|
+
</box>
|
|
90
|
+
<box height={1} paddingLeft={1}>
|
|
91
|
+
<text fg={props.palette.muted} onMouseUp={() => setExpanded(!expanded())}>
|
|
92
|
+
{props.section.label}
|
|
93
|
+
</text>
|
|
94
|
+
</box>
|
|
95
|
+
</box>
|
|
96
|
+
<Show when={expanded()}>
|
|
97
|
+
<box width="100%" marginTop={1} paddingLeft={2} flexDirection="column">
|
|
98
|
+
<text fg={props.palette.text}>{props.section.content}</text>
|
|
99
|
+
</box>
|
|
100
|
+
</Show>
|
|
101
|
+
</box>
|
|
102
|
+
)
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
function SummaryScreen(props: { api: TuiPluginApi; params: SummaryRouteParams }) {
|
|
106
|
+
const palette = createMemo(() => getPalette(props.api.theme.current as Record<string, unknown>))
|
|
107
|
+
const parsed = createMemo(() => parseSummary(props.params.summary || ""))
|
|
108
|
+
|
|
109
|
+
const keys = props.api.keybind.create({ close: "escape" })
|
|
110
|
+
|
|
111
|
+
useKeyboard((evt: any) => {
|
|
112
|
+
if (props.api.route.current.name !== NAMES.routes.summary) return
|
|
113
|
+
if (props.api.ui.dialog.open) return
|
|
114
|
+
const matched = keys.match("close", evt)
|
|
115
|
+
if (!matched) return
|
|
116
|
+
evt.preventDefault()
|
|
117
|
+
evt.stopPropagation()
|
|
118
|
+
const sessionID = props.params.sessionID
|
|
119
|
+
if (sessionID) {
|
|
120
|
+
props.api.route.navigate("session", { sessionID })
|
|
121
|
+
} else {
|
|
122
|
+
props.api.route.navigate("home")
|
|
123
|
+
}
|
|
124
|
+
})
|
|
125
|
+
|
|
126
|
+
return (
|
|
127
|
+
<box
|
|
128
|
+
flexDirection="column"
|
|
129
|
+
width="100%"
|
|
130
|
+
height="100%"
|
|
131
|
+
padding={1}
|
|
132
|
+
backgroundColor={palette().surface}
|
|
133
|
+
>
|
|
134
|
+
<box flexDirection="row" gap={1} alignItems="center">
|
|
135
|
+
<box paddingLeft={1} paddingRight={1} backgroundColor={palette().accent}>
|
|
136
|
+
<text fg={palette().panel}>
|
|
137
|
+
<b>{LABEL}</b>
|
|
138
|
+
</text>
|
|
139
|
+
</box>
|
|
140
|
+
<text fg={palette().accent}>
|
|
141
|
+
<b>{props.params.topic || "Compression Summary"}</b>
|
|
142
|
+
</text>
|
|
143
|
+
</box>
|
|
144
|
+
|
|
145
|
+
<box
|
|
146
|
+
flexGrow={1}
|
|
147
|
+
width="100%"
|
|
148
|
+
marginTop={1}
|
|
149
|
+
border={SINGLE_BORDER}
|
|
150
|
+
borderColor={palette().border}
|
|
151
|
+
padding={1}
|
|
152
|
+
flexDirection="column"
|
|
153
|
+
>
|
|
154
|
+
<text fg={palette().text}>{parsed().body || "(no summary available)"}</text>
|
|
155
|
+
|
|
156
|
+
<For each={parsed().sections}>
|
|
157
|
+
{(section) => <CollapsibleSectionRow section={section} palette={palette()} />}
|
|
158
|
+
</For>
|
|
159
|
+
</box>
|
|
160
|
+
|
|
161
|
+
<box marginTop={1}>
|
|
162
|
+
<text {...({ dim: true } as any)} fg={palette().muted}>
|
|
163
|
+
Press Escape to return
|
|
164
|
+
</text>
|
|
165
|
+
</box>
|
|
166
|
+
</box>
|
|
167
|
+
)
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
export const createSummaryRoute = (api: TuiPluginApi) => ({
|
|
171
|
+
name: NAMES.routes.summary,
|
|
172
|
+
render: (input: { params?: Record<string, unknown> }) => (
|
|
173
|
+
<SummaryScreen api={api} params={(input.params ?? {}) as SummaryRouteParams} />
|
|
174
|
+
),
|
|
175
|
+
})
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import type { RGBA } from "@opentui/core"
|
|
2
|
+
|
|
3
|
+
export type DcpColor = RGBA | string
|
|
4
|
+
|
|
5
|
+
export interface DcpPalette {
|
|
6
|
+
panel: DcpColor
|
|
7
|
+
base: DcpColor
|
|
8
|
+
surface: DcpColor
|
|
9
|
+
border: DcpColor
|
|
10
|
+
text: DcpColor
|
|
11
|
+
muted: DcpColor
|
|
12
|
+
accent: DcpColor
|
|
13
|
+
success: DcpColor
|
|
14
|
+
warning: DcpColor
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export type DcpTone = "text" | "muted" | "accent" | "success" | "warning"
|
|
18
|
+
|
|
19
|
+
const defaults = {
|
|
20
|
+
panel: "#111111",
|
|
21
|
+
base: "#1d1d1d",
|
|
22
|
+
surface: "#171717",
|
|
23
|
+
border: "#4a4a4a",
|
|
24
|
+
text: "#f0f0f0",
|
|
25
|
+
muted: "#a5a5a5",
|
|
26
|
+
accent: "#5f87ff",
|
|
27
|
+
success: "#67b95f",
|
|
28
|
+
warning: "#d7a94b",
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export const getPalette = (theme: Record<string, unknown>): DcpPalette => {
|
|
32
|
+
const get = (name: string, fallback: string): DcpColor => {
|
|
33
|
+
const value = theme[name]
|
|
34
|
+
if (typeof value === "string") return value
|
|
35
|
+
if (value && typeof value === "object") return value as RGBA
|
|
36
|
+
return fallback
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
return {
|
|
40
|
+
panel: get("backgroundPanel", defaults.panel),
|
|
41
|
+
base: get("backgroundElement", defaults.base),
|
|
42
|
+
surface: get("background", defaults.surface),
|
|
43
|
+
border: get("border", defaults.border),
|
|
44
|
+
text: get("text", defaults.text),
|
|
45
|
+
muted: get("textMuted", defaults.muted),
|
|
46
|
+
accent: get("primary", defaults.accent),
|
|
47
|
+
success: get("success", defaults.success),
|
|
48
|
+
warning: get("warning", defaults.warning),
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export const toneColor = (palette: DcpPalette, tone: DcpTone = "text") => {
|
|
53
|
+
if (tone === "accent") return palette.accent
|
|
54
|
+
if (tone === "success") return palette.success
|
|
55
|
+
if (tone === "warning") return palette.warning
|
|
56
|
+
if (tone === "muted") return palette.muted
|
|
57
|
+
return palette.text
|
|
58
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import type { TuiPluginApi } from "@opencode-ai/plugin/tui"
|
|
2
|
+
import type {
|
|
3
|
+
MessageStatus as DcpMessageStatus,
|
|
4
|
+
TokenBreakdown as DcpContextBreakdown,
|
|
5
|
+
} from "../../lib/analysis/tokens"
|
|
6
|
+
|
|
7
|
+
export type DcpTuiApi = TuiPluginApi
|
|
8
|
+
export type DcpTuiClient = DcpTuiApi["client"]
|
|
9
|
+
|
|
10
|
+
export type { DcpMessageStatus }
|
|
11
|
+
|
|
12
|
+
export interface DcpActiveBlockInfo {
|
|
13
|
+
topic: string
|
|
14
|
+
summary: string
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export interface DcpPersistedSummary {
|
|
18
|
+
available: boolean
|
|
19
|
+
activeBlockCount: number
|
|
20
|
+
activeBlocks: DcpActiveBlockInfo[]
|
|
21
|
+
lastUpdated?: string
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export interface DcpAllTimeStats {
|
|
25
|
+
totalTokensSaved: number
|
|
26
|
+
sessionCount: number
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export interface DcpContextSnapshot {
|
|
30
|
+
sessionID?: string
|
|
31
|
+
breakdown: DcpContextBreakdown
|
|
32
|
+
activeSummaryTokens: number
|
|
33
|
+
persisted: DcpPersistedSummary
|
|
34
|
+
messageStatuses: DcpMessageStatus[]
|
|
35
|
+
allTimeStats: DcpAllTimeStats
|
|
36
|
+
notes: string[]
|
|
37
|
+
loadedAt: number
|
|
38
|
+
}
|