@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.
Files changed (74) hide show
  1. package/dist/lib/token-utils.js +2 -2
  2. package/dist/lib/token-utils.js.map +1 -1
  3. package/index.ts +141 -0
  4. package/lib/analysis/tokens.ts +225 -0
  5. package/lib/auth.ts +37 -0
  6. package/lib/commands/compression-targets.ts +137 -0
  7. package/lib/commands/context.ts +132 -0
  8. package/lib/commands/decompress.ts +275 -0
  9. package/lib/commands/help.ts +76 -0
  10. package/lib/commands/index.ts +11 -0
  11. package/lib/commands/manual.ts +125 -0
  12. package/lib/commands/recompress.ts +224 -0
  13. package/lib/commands/stats.ts +148 -0
  14. package/lib/commands/sweep.ts +268 -0
  15. package/lib/compress/index.ts +3 -0
  16. package/lib/compress/message-utils.ts +250 -0
  17. package/lib/compress/message.ts +137 -0
  18. package/lib/compress/pipeline.ts +106 -0
  19. package/lib/compress/protected-content.ts +154 -0
  20. package/lib/compress/range-utils.ts +308 -0
  21. package/lib/compress/range.ts +180 -0
  22. package/lib/compress/search.ts +267 -0
  23. package/lib/compress/state.ts +268 -0
  24. package/lib/compress/timing.ts +77 -0
  25. package/lib/compress/types.ts +108 -0
  26. package/lib/compress-permission.ts +25 -0
  27. package/lib/config.ts +1071 -0
  28. package/lib/hooks.ts +378 -0
  29. package/lib/host-permissions.ts +101 -0
  30. package/lib/logger.ts +235 -0
  31. package/lib/message-ids.ts +172 -0
  32. package/lib/messages/index.ts +8 -0
  33. package/lib/messages/inject/inject.ts +215 -0
  34. package/lib/messages/inject/subagent-results.ts +82 -0
  35. package/lib/messages/inject/utils.ts +374 -0
  36. package/lib/messages/priority.ts +102 -0
  37. package/lib/messages/prune.ts +238 -0
  38. package/lib/messages/query.ts +56 -0
  39. package/lib/messages/reasoning-strip.ts +40 -0
  40. package/lib/messages/sync.ts +124 -0
  41. package/lib/messages/utils.ts +187 -0
  42. package/lib/prompts/compress-message.ts +42 -0
  43. package/lib/prompts/compress-range.ts +60 -0
  44. package/lib/prompts/context-limit-nudge.ts +18 -0
  45. package/lib/prompts/extensions/nudge.ts +43 -0
  46. package/lib/prompts/extensions/system.ts +32 -0
  47. package/lib/prompts/extensions/tool.ts +35 -0
  48. package/lib/prompts/index.ts +29 -0
  49. package/lib/prompts/iteration-nudge.ts +6 -0
  50. package/lib/prompts/store.ts +467 -0
  51. package/lib/prompts/system.ts +33 -0
  52. package/lib/prompts/turn-nudge.ts +10 -0
  53. package/lib/protected-patterns.ts +128 -0
  54. package/lib/state/index.ts +4 -0
  55. package/lib/state/persistence.ts +256 -0
  56. package/lib/state/state.ts +190 -0
  57. package/lib/state/tool-cache.ts +98 -0
  58. package/lib/state/types.ts +112 -0
  59. package/lib/state/utils.ts +334 -0
  60. package/lib/strategies/deduplication.ts +127 -0
  61. package/lib/strategies/index.ts +2 -0
  62. package/lib/strategies/purge-errors.ts +88 -0
  63. package/lib/subagents/subagent-results.ts +74 -0
  64. package/lib/token-utils.ts +162 -0
  65. package/lib/ui/notification.ts +346 -0
  66. package/lib/ui/utils.ts +287 -0
  67. package/package.json +12 -3
  68. package/tui/data/context.ts +177 -0
  69. package/tui/index.tsx +34 -0
  70. package/tui/routes/summary.tsx +175 -0
  71. package/tui/shared/names.ts +9 -0
  72. package/tui/shared/theme.ts +58 -0
  73. package/tui/shared/types.ts +38 -0
  74. 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,9 @@
1
+ export const LABEL = "DCP"
2
+
3
+ export const NAMES = {
4
+ routes: {
5
+ summary: "dcp.summary",
6
+ },
7
+ } as const
8
+
9
+ export type DcpRouteNames = typeof NAMES
@@ -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
+ }