opencode-extended-sidebar 0.2.90 → 0.2.91

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 (108) hide show
  1. package/.oesignore +17 -17
  2. package/CHANGELOG.md +96 -95
  3. package/LICENSE +21 -21
  4. package/README.md +150 -235
  5. package/oes.json +13 -11
  6. package/package.json +100 -100
  7. package/scripts/install-git-hooks.mjs +29 -29
  8. package/src/pware.oc.core/constants/index.ts +18 -18
  9. package/src/pware.oc.core/constants/pware.oc.core.constants.eventKind.ts +29 -29
  10. package/src/pware.oc.core/constants/pware.oc.core.constants.eventName.ts +15 -13
  11. package/src/pware.oc.core/constants/pware.oc.core.constants.eventType.ts +241 -241
  12. package/src/pware.oc.core/constants/pware.oc.core.constants.myWork.ts +55 -55
  13. package/src/pware.oc.core/constants/pware.oc.core.constants.partType.ts +65 -65
  14. package/src/pware.oc.core/constants/pware.oc.core.constants.phase.ts +58 -58
  15. package/src/pware.oc.core/constants/pware.oc.core.constants.pulse.ts +63 -63
  16. package/src/pware.oc.core/constants/pware.oc.core.constants.rowKind.ts +34 -34
  17. package/src/pware.oc.core/constants/pware.oc.core.constants.status.ts +74 -74
  18. package/src/pware.oc.core/constants/pware.oc.core.constants.toolName.ts +69 -69
  19. package/src/pware.oc.core/git/index.ts +7 -7
  20. package/src/pware.oc.core/git/pware.oc.core.git.ts +237 -237
  21. package/src/pware.oc.core/git/pware.oc.core.gitignore.ts +87 -87
  22. package/src/pware.oc.core/index.ts +19 -18
  23. package/src/pware.oc.core/pware.oc.core.bus.ts +42 -42
  24. package/src/pware.oc.core/pware.oc.core.cache.ts +48 -48
  25. package/src/pware.oc.core/pware.oc.core.clipboard.ts +97 -97
  26. package/src/pware.oc.core/pware.oc.core.debug.ts +280 -280
  27. package/src/pware.oc.core/pware.oc.core.events.ts +79 -79
  28. package/src/pware.oc.core/pware.oc.core.glyph.ts +135 -135
  29. package/src/pware.oc.core/pware.oc.core.layout.ts +234 -234
  30. package/src/pware.oc.core/pware.oc.core.oes.ts +99 -89
  31. package/src/pware.oc.core/pware.oc.core.paths.ts +240 -240
  32. package/src/pware.oc.core/pware.oc.core.preview.ts +74 -74
  33. package/src/pware.oc.core/pware.oc.core.pulse.ts +726 -726
  34. package/src/pware.oc.core/pware.oc.core.runtimeConfig.ts +41 -0
  35. package/src/pware.oc.core/pware.oc.core.sqlite.ts +164 -175
  36. package/src/pware.oc.core/pware.oc.core.sqliteGateway.ts +89 -0
  37. package/src/pware.oc.core/pware.oc.core.status.ts +214 -214
  38. package/src/pware.oc.core/pware.oc.core.timing.ts +63 -63
  39. package/src/pware.oc.core/pware.oc.core.width.ts +215 -215
  40. package/src/pware.oc.omo/constants/index.ts +15 -15
  41. package/src/pware.oc.omo/constants/pware.oc.omo.constants.backgroundTask.ts +18 -18
  42. package/src/pware.oc.omo/constants/pware.oc.omo.constants.boulderStatus.ts +49 -49
  43. package/src/pware.oc.omo/constants/pware.oc.omo.constants.docKind.ts +29 -29
  44. package/src/pware.oc.omo/constants/pware.oc.omo.constants.eventName.ts +11 -11
  45. package/src/pware.oc.omo/constants/pware.oc.omo.constants.planStatus.ts +60 -60
  46. package/src/pware.oc.omo/constants/pware.oc.omo.constants.reviewStatus.ts +49 -49
  47. package/src/pware.oc.omo/constants/pware.oc.omo.constants.startWork.ts +25 -25
  48. package/src/pware.oc.omo/constants/pware.oc.omo.constants.verdict.ts +38 -38
  49. package/src/pware.oc.omo/index.ts +7 -7
  50. package/src/pware.oc.omo/resolver/index.ts +19 -19
  51. package/src/pware.oc.omo/resolver/pware.oc.omo.resolver.approval.ts +179 -179
  52. package/src/pware.oc.omo/resolver/pware.oc.omo.resolver.approvalGroup.ts +116 -116
  53. package/src/pware.oc.omo/resolver/pware.oc.omo.resolver.approvalState.ts +34 -34
  54. package/src/pware.oc.omo/resolver/pware.oc.omo.resolver.boulder.ts +503 -503
  55. package/src/pware.oc.omo/resolver/pware.oc.omo.resolver.config.ts +40 -40
  56. package/src/pware.oc.omo/resolver/pware.oc.omo.resolver.doc.ts +269 -269
  57. package/src/pware.oc.omo/resolver/pware.oc.omo.resolver.draftFile.ts +18 -18
  58. package/src/pware.oc.omo/resolver/pware.oc.omo.resolver.notepadsFile.ts +18 -18
  59. package/src/pware.oc.omo/resolver/pware.oc.omo.resolver.plan.ts +155 -155
  60. package/src/pware.oc.omo/resolver/pware.oc.omo.resolver.planFile.ts +363 -361
  61. package/src/pware.oc.omo/resolver/pware.oc.omo.resolver.proofFile.ts +18 -18
  62. package/src/pware.oc.omo/resolver/pware.oc.omo.resolver.rulesFile.ts +11 -11
  63. package/src/pware.oc.omo/resolver/pware.oc.omo.resolver.runContinuationFile.ts +14 -14
  64. package/src/pware.oc.opencode/constants/index.ts +10 -10
  65. package/src/pware.oc.opencode/constants/pware.oc.opencode.constants.eventName.ts +15 -15
  66. package/src/pware.oc.opencode/constants/pware.oc.opencode.constants.fileTouch.ts +18 -18
  67. package/src/pware.oc.opencode/constants/pware.oc.opencode.constants.questionKind.ts +45 -45
  68. package/src/pware.oc.opencode/constants/pware.oc.opencode.constants.sessionStatus.ts +58 -58
  69. package/src/pware.oc.opencode/index.ts +8 -8
  70. package/src/pware.oc.opencode/pware.oc.opencode.events.ts +182 -182
  71. package/src/pware.oc.opencode/pware.oc.opencode.files.ts +399 -399
  72. package/src/pware.oc.opencode/resolver/index.ts +223 -170
  73. package/src/pware.oc.opencode/resolver/pware.oc.opencode.resolver.file.ts +150 -162
  74. package/src/pware.oc.opencode/resolver/pware.oc.opencode.resolver.question.ts +149 -185
  75. package/src/pware.oc.opencode/resolver/pware.oc.opencode.resolver.session.ts +248 -211
  76. package/src/pware.oc.opencode/resolver/pware.oc.opencode.resolver.todo.ts +22 -26
  77. package/src/pware.oc.opencode/resolver/pware.oc.opencode.resolver.tool.ts +224 -236
  78. package/src/pware.oc.perf/index.ts +14 -14
  79. package/src/pware.oc.perf/pware.oc.perf.asciichart.d.ts +15 -15
  80. package/src/pware.oc.perf/pware.oc.perf.charts.ts +284 -284
  81. package/src/pware.oc.perf/pware.oc.perf.reader.ts +962 -927
  82. package/src/pware.oc.perf/pware.oc.perf.realtime.ts +88 -88
  83. package/src/pware.oc.perf/pware.oc.perf.realtimeBlock.ts +118 -118
  84. package/src/pware.oc.perf/pware.oc.perf.realtimeCpuRam.ts +79 -79
  85. package/src/pware.oc.perf/pware.oc.perf.realtimeSampler.ts +71 -71
  86. package/src/pware.oc.perf/pware.oc.perf.realtimeTimeline.ts +214 -214
  87. package/src/pware.oc.perf/pware.oc.perf.self.ts +179 -179
  88. package/src/pware.oc.perf/pware.oc.perf.view.tsx +463 -462
  89. package/src/pware.oc.runtime/index.ts +13 -13
  90. package/src/pware.oc.runtime/pware.oc.runtime.monitor.ts +142 -142
  91. package/src/pware.oc.runtime/pware.oc.runtime.mywork-enrich.ts +87 -79
  92. package/src/pware.oc.runtime/pware.oc.runtime.mywork.ts +282 -253
  93. package/src/pware.oc.runtime/pware.oc.runtime.omoRead.ts +37 -0
  94. package/src/pware.oc.runtime/pware.oc.runtime.questions.ts +83 -95
  95. package/src/pware.oc.runtime/pware.oc.runtime.snapshotClient.ts +130 -129
  96. package/src/pware.oc.runtime/pware.oc.runtime.source.ts +105 -105
  97. package/src/pware.oc.runtime/pware.oc.runtime.worker.ts +44 -43
  98. package/src/pware.oc.runtime/resolver/index.ts +268 -224
  99. package/src/pware.oc.runtime/resolver/pware.oc.runtime.resolver.delegate.ts +146 -146
  100. package/src/pware.oc.ui/index.ts +12 -12
  101. package/src/pware.oc.ui/pware.oc.ui.chrome.tsx +381 -339
  102. package/src/pware.oc.ui/pware.oc.ui.glyphs.tsx +144 -144
  103. package/src/pware.oc.ui/pware.oc.ui.host.tsx +111 -111
  104. package/src/pware.oc.ui/pware.oc.ui.live.tsx +82 -82
  105. package/src/pware.oc.ui/pware.oc.ui.menudialogs.tsx +843 -843
  106. package/src/pware.oc.ui/pware.oc.ui.sections.tsx +552 -526
  107. package/src/pware.oc.ui/pware.oc.ui.sidebar.tsx +1718 -1629
  108. package/src/pware.oc.ui.tsx +98 -98
@@ -1,13 +1,13 @@
1
- /**
2
- * pware.oc.runtime
3
- *
4
- * Runtime composition: the only layer that combines opencode + omo into one
5
- * snapshot, plus the monitor that keeps it fresh and the My work queue.
6
- */
7
- export * from "./pware.oc.runtime.monitor.js"
8
- export * from "./pware.oc.runtime.source.js"
9
- export * from "./pware.oc.runtime.snapshotClient.js"
10
- export * from "./pware.oc.runtime.mywork.js"
11
- export * from "./pware.oc.runtime.mywork-enrich.js"
12
- export * from "./pware.oc.runtime.questions.js"
13
- export * from "./resolver/index.js"
1
+ /**
2
+ * pware.oc.runtime
3
+ *
4
+ * Runtime composition: the only layer that combines opencode + omo into one
5
+ * snapshot, plus the monitor that keeps it fresh and the My work queue.
6
+ */
7
+ export * from "./pware.oc.runtime.monitor.js"
8
+ export * from "./pware.oc.runtime.source.js"
9
+ export * from "./pware.oc.runtime.snapshotClient.js"
10
+ export * from "./pware.oc.runtime.mywork.js"
11
+ export * from "./pware.oc.runtime.mywork-enrich.js"
12
+ export * from "./pware.oc.runtime.questions.js"
13
+ export * from "./resolver/index.js"
@@ -1,142 +1,142 @@
1
- /**
2
- * Watch boulder.json + poll SQLite stamps. Invokes onChange when fingerprint changes.
3
- */
4
- import fs from "node:fs"
5
- import path from "node:path"
6
- import type { PwareEvent } from "../pware.oc.core/pware.oc.core.bus.js"
7
- import { EV_OES_SNAPSHOT } from "../pware.oc.core/constants/pware.oc.core.constants.eventName.js"
8
- import { EV_OMO_BOULDER_CHANGED } from "../pware.oc.omo/constants/pware.oc.omo.constants.eventName.js"
9
- import { findBoulder } from "../pware.oc.omo/resolver/index.js"
10
- import { computeFingerprint, type RuntimeSnapshot } from "./resolver/index.js"
11
- import { readRuntimeSnapshotAsync } from "./pware.oc.runtime.snapshotClient.js"
12
- import { getOpenCodeDbPath } from "../pware.oc.core/pware.oc.core.paths.js"
13
- import { MONITOR_POLL_MS, MONITOR_WATCH_DEBOUNCE_MS } from "../pware.oc.core/pware.oc.core.timing.js"
14
- import { dbg, profile } from "../pware.oc.core/pware.oc.core.debug.js"
15
-
16
- export type MonitorOptions = {
17
- sessionId: string
18
- projectRoot: string | null
19
- dbPath?: string
20
- pollMs?: number
21
- onChange?: (snap: RuntimeSnapshot) => void
22
- emit?: (evt: PwareEvent) => void
23
- }
24
-
25
- export type MonitorHandle = {
26
- stop: () => void
27
- refresh: () => void
28
- question: (sessionId: string) => void
29
- }
30
-
31
- export function startMonitor(opts: MonitorOptions): MonitorHandle {
32
- const dbPath = opts.dbPath || getOpenCodeDbPath(process.env, undefined, opts.projectRoot)
33
- dbg("monitor", "start", { dbPath, sessionId: opts.sessionId, projectRoot: opts.projectRoot ?? null })
34
- const pollMs = opts.pollMs ?? MONITOR_POLL_MS
35
- let lastFp = ""
36
- let stopped = false
37
- let emitGen = 0
38
- let debounce: ReturnType<typeof setTimeout> | null = null
39
- let pendingQuestionHint: string | null = null
40
- const watchers: fs.FSWatcher[] = []
41
-
42
- const emit = (force = false) =>
43
- profile("monitor.emit", () => {
44
- if (stopped) return
45
- const fp = computeFingerprint({
46
- dbPath,
47
- projectRoot: opts.projectRoot,
48
- sessionId: opts.sessionId,
49
- })
50
- if (!force && fp === lastFp) return
51
- dbg("monitor", "emit", { force, fp: fp.slice(0, 40) })
52
- lastFp = fp
53
- const gen = ++emitGen
54
- const questionHint = pendingQuestionHint ?? undefined
55
- pendingQuestionHint = null
56
- void readRuntimeSnapshotAsync({
57
- sessionId: opts.sessionId,
58
- projectRoot: opts.projectRoot,
59
- dbPath,
60
- questionHint,
61
- }).then((snapshot) => {
62
- if (stopped || gen !== emitGen) return
63
- opts.onChange?.(snapshot)
64
- opts.emit?.({
65
- type: EV_OES_SNAPSHOT,
66
- ts: Date.now(),
67
- data: { snapshot },
68
- })
69
- })
70
- })
71
-
72
- const schedule = () => {
73
- if (stopped) return
74
- if (debounce) clearTimeout(debounce)
75
- debounce = setTimeout(() => {
76
- debounce = null
77
- emit()
78
- }, MONITOR_WATCH_DEBOUNCE_MS)
79
- }
80
-
81
- // Initial
82
- emit(true)
83
-
84
- const interval = setInterval(schedule, pollMs)
85
-
86
- const root = opts.projectRoot
87
- if (root) {
88
- const boulderPath = findBoulder(root)
89
- if (boulderPath) {
90
- try {
91
- watchers.push(
92
- fs.watch(boulderPath, { persistent: false }, () => {
93
- opts.emit?.({
94
- type: EV_OMO_BOULDER_CHANGED,
95
- ts: Date.now(),
96
- data: {},
97
- })
98
- schedule()
99
- }),
100
- )
101
- } catch {
102
- // poll covers it
103
- }
104
- }
105
- }
106
-
107
- // SQLite: watching the file is flaky under WAL — poll stamps instead.
108
- // Still try a non-recursive watch on the data dir for companions.
109
- try {
110
- const dataDir = path.dirname(dbPath)
111
- if (fs.existsSync(dataDir)) {
112
- watchers.push(
113
- fs.watch(dataDir, { persistent: false }, (event, filename) => {
114
- const name = filename?.toString() || ""
115
- if (!name || name.includes("opencode.db")) schedule()
116
- }),
117
- )
118
- }
119
- } catch {
120
- // poll only
121
- }
122
-
123
- return {
124
- refresh: () => emit(true),
125
- question: (sessionId: string) => {
126
- pendingQuestionHint = sessionId
127
- emit(true)
128
- },
129
- stop: () => {
130
- stopped = true
131
- if (debounce) clearTimeout(debounce)
132
- clearInterval(interval)
133
- for (const w of watchers) {
134
- try {
135
- w.close()
136
- } catch {
137
- // ignore
138
- }
139
- }
140
- },
141
- }
142
- }
1
+ /**
2
+ * Watch boulder.json + poll SQLite stamps. Invokes onChange when fingerprint changes.
3
+ */
4
+ import fs from "node:fs"
5
+ import path from "node:path"
6
+ import type { PwareEvent } from "../pware.oc.core/pware.oc.core.bus.js"
7
+ import { EV_OES_SNAPSHOT } from "../pware.oc.core/constants/pware.oc.core.constants.eventName.js"
8
+ import { EV_OMO_BOULDER_CHANGED } from "../pware.oc.omo/constants/pware.oc.omo.constants.eventName.js"
9
+ import { findBoulder } from "../pware.oc.omo/resolver/index.js"
10
+ import { computeFingerprint, type RuntimeSnapshot } from "./resolver/index.js"
11
+ import { readRuntimeSnapshotAsync } from "./pware.oc.runtime.snapshotClient.js"
12
+ import { getOpenCodeDbPath } from "../pware.oc.core/pware.oc.core.paths.js"
13
+ import { MONITOR_POLL_MS, MONITOR_WATCH_DEBOUNCE_MS } from "../pware.oc.core/pware.oc.core.timing.js"
14
+ import { dbg, profile } from "../pware.oc.core/pware.oc.core.debug.js"
15
+
16
+ export type MonitorOptions = {
17
+ sessionId: string
18
+ projectRoot: string | null
19
+ dbPath?: string
20
+ pollMs?: number
21
+ onChange?: (snap: RuntimeSnapshot) => void
22
+ emit?: (evt: PwareEvent) => void
23
+ }
24
+
25
+ export type MonitorHandle = {
26
+ stop: () => void
27
+ refresh: () => void
28
+ question: (sessionId: string) => void
29
+ }
30
+
31
+ export function startMonitor(opts: MonitorOptions): MonitorHandle {
32
+ const dbPath = opts.dbPath || getOpenCodeDbPath(process.env, undefined, opts.projectRoot)
33
+ dbg("monitor", "start", { dbPath, sessionId: opts.sessionId, projectRoot: opts.projectRoot ?? null })
34
+ const pollMs = opts.pollMs ?? MONITOR_POLL_MS
35
+ let lastFp = ""
36
+ let stopped = false
37
+ let emitGen = 0
38
+ let debounce: ReturnType<typeof setTimeout> | null = null
39
+ let pendingQuestionHint: string | null = null
40
+ const watchers: fs.FSWatcher[] = []
41
+
42
+ const emit = (force = false) =>
43
+ profile("monitor.emit", () => {
44
+ if (stopped) return
45
+ const fp = computeFingerprint({
46
+ dbPath,
47
+ projectRoot: opts.projectRoot,
48
+ sessionId: opts.sessionId,
49
+ })
50
+ if (!force && fp === lastFp) return
51
+ dbg("monitor", "emit", { force, fp: fp.slice(0, 40) })
52
+ lastFp = fp
53
+ const gen = ++emitGen
54
+ const questionHint = pendingQuestionHint ?? undefined
55
+ pendingQuestionHint = null
56
+ void readRuntimeSnapshotAsync({
57
+ sessionId: opts.sessionId,
58
+ projectRoot: opts.projectRoot,
59
+ dbPath,
60
+ questionHint,
61
+ }).then((snapshot) => {
62
+ if (stopped || gen !== emitGen) return
63
+ opts.onChange?.(snapshot)
64
+ opts.emit?.({
65
+ type: EV_OES_SNAPSHOT,
66
+ ts: Date.now(),
67
+ data: { snapshot },
68
+ })
69
+ })
70
+ })
71
+
72
+ const schedule = () => {
73
+ if (stopped) return
74
+ if (debounce) clearTimeout(debounce)
75
+ debounce = setTimeout(() => {
76
+ debounce = null
77
+ emit()
78
+ }, MONITOR_WATCH_DEBOUNCE_MS)
79
+ }
80
+
81
+ // Initial
82
+ emit(true)
83
+
84
+ const interval = setInterval(schedule, pollMs)
85
+
86
+ const root = opts.projectRoot
87
+ if (root) {
88
+ const boulderPath = findBoulder(root)
89
+ if (boulderPath) {
90
+ try {
91
+ watchers.push(
92
+ fs.watch(boulderPath, { persistent: false }, () => {
93
+ opts.emit?.({
94
+ type: EV_OMO_BOULDER_CHANGED,
95
+ ts: Date.now(),
96
+ data: {},
97
+ })
98
+ schedule()
99
+ }),
100
+ )
101
+ } catch {
102
+ // poll covers it
103
+ }
104
+ }
105
+ }
106
+
107
+ // SQLite: watching the file is flaky under WAL — poll stamps instead.
108
+ // Still try a non-recursive watch on the data dir for companions.
109
+ try {
110
+ const dataDir = path.dirname(dbPath)
111
+ if (fs.existsSync(dataDir)) {
112
+ watchers.push(
113
+ fs.watch(dataDir, { persistent: false }, (event, filename) => {
114
+ const name = filename?.toString() || ""
115
+ if (!name || name.includes("opencode.db")) schedule()
116
+ }),
117
+ )
118
+ }
119
+ } catch {
120
+ // poll only
121
+ }
122
+
123
+ return {
124
+ refresh: () => emit(true),
125
+ question: (sessionId: string) => {
126
+ pendingQuestionHint = sessionId
127
+ emit(true)
128
+ },
129
+ stop: () => {
130
+ stopped = true
131
+ if (debounce) clearTimeout(debounce)
132
+ clearInterval(interval)
133
+ for (const w of watchers) {
134
+ try {
135
+ w.close()
136
+ } catch {
137
+ // ignore
138
+ }
139
+ }
140
+ },
141
+ }
142
+ }
@@ -1,79 +1,87 @@
1
- /**
2
- * pware.oc.runtime.mywork-enrich
3
- *
4
- * Enriches the OMO approval queue with the writer session's activity state:
5
- * builds the plan-file → writer-session index once, then per item resolves the
6
- * writer via `basenameOf(item.rel)` and asks `sessionActivityState` (SQLite
7
- * freshness + the omo `.omo/run-continuation` marker). Soft-fails to
8
- * `sessionState: null` per item — never throws. This is the runtime layer's
9
- * job: it composes omo (`.omo/run-continuation`) with opencode (SQLite session
10
- * activity).
11
- */
12
- import fs from "node:fs"
13
- import {
14
- sessionActivityState,
15
- type SessionActivityState,
16
- } from "../pware.oc.opencode/resolver/pware.oc.opencode.resolver.session.js"
17
- import { listTodos } from "../pware.oc.opencode/resolver/pware.oc.opencode.resolver.todo.js"
18
- import { planSessionIndex } from "../pware.oc.omo/resolver/index.js"
19
- import { openReadonlyDb, withDbRead, type SqlDb } from "../pware.oc.core/pware.oc.core.sqlite.js"
20
- import { profile } from "../pware.oc.core/pware.oc.core.debug.js"
21
- import { basenameOf } from "../pware.oc.core/pware.oc.core.paths.js"
22
- import { readRunContinuationState } from "../pware.oc.omo/resolver/pware.oc.omo.resolver.approvalState.js"
23
- import { BACKGROUND_TASK_ACTIVE } from "../pware.oc.omo/constants/pware.oc.omo.constants.backgroundTask.js"
24
- import { STATUS_COMPLETED } from "../pware.oc.core/constants/pware.oc.core.constants.status.js"
25
- import type { ApprovalItem } from "../pware.oc.omo/resolver/pware.oc.omo.resolver.plan.js"
26
-
27
- /**
28
- * An approval item with its planner-session activity attached. `sessionState`
29
- * is a runtime enrichment: it lives here on the runtime layer's enriched type,
30
- * not on omo's base `ApprovalItem` (omo must not import the opencode session type).
31
- */
32
- export type EnrichedApproval = ApprovalItem & {
33
- sessionState: SessionActivityState | null
34
- /** Writer session's todos all completed (and at least one present). */
35
- todosDone: boolean
36
- }
37
-
38
- function blank(items: readonly ApprovalItem[]): EnrichedApproval[] {
39
- return items.map((item) => ({ ...item, sessionState: null, todosDone: false }))
40
- }
41
-
42
- function sessionTodosDone(db: SqlDb, sessionId: string): boolean {
43
- const todos = listTodos(db, sessionId)
44
- return todos.length > 0 && todos.every((t) => t.status === STATUS_COMPLETED)
45
- }
46
-
47
- export function enrichApprovalSessionStates(
48
- items: readonly ApprovalItem[],
49
- opts: { dbPath: string | null | undefined; projectRoot: string | null | undefined; now?: number },
50
- ): EnrichedApproval[] {
51
- if (items.length === 0) return []
52
- const dbPath = opts.dbPath
53
- const projectRoot = opts.projectRoot
54
- if (!dbPath || !projectRoot || !fs.existsSync(dbPath)) return blank(items)
55
- return profile("mywork.approvals", () =>
56
- withDbRead(
57
- () => {
58
- const db = openReadonlyDb(dbPath)
59
- if (!db) return blank(items)
60
- // Index once; match writers by basename across all sessions (no
61
- // projectId in the opts shape — same scope as sessionForPlanFile).
62
- const index = planSessionIndex(db, null, projectRoot)
63
- return items.map((item) => {
64
- const sessionId = index.fileWriter.get(basenameOf(item.rel))?.sessionId ?? null
65
- const sessionState = sessionId
66
- ? sessionActivityState(db, sessionId, {
67
- backgroundTaskActive:
68
- readRunContinuationState(projectRoot, sessionId) === BACKGROUND_TASK_ACTIVE,
69
- now: opts.now,
70
- })
71
- : null
72
- const todosDone = sessionId ? sessionTodosDone(db, sessionId) : false
73
- return { ...item, sessionState, todosDone }
74
- })
75
- },
76
- () => blank(items),
77
- ),
78
- )
79
- }
1
+ /**
2
+ * pware.oc.runtime.mywork-enrich
3
+ *
4
+ * Enriches the OMO approval queue with the writer session's activity state:
5
+ * builds the plan-file → writer-session index once, then per item resolves the
6
+ * writer via `basenameOf(item.rel)` and asks `sessionActivityState` (SQLite
7
+ * freshness + the omo `.omo/run-continuation` marker). Soft-fails to
8
+ * `sessionState: null` per item — never throws. This is the runtime layer's
9
+ * job: it composes omo (`.omo/run-continuation`) with opencode (SQLite session
10
+ * activity).
11
+ */
12
+ import {
13
+ sessionActivityState,
14
+ type SessionActivityState,
15
+ } from "../pware.oc.opencode/resolver/pware.oc.opencode.resolver.session.js"
16
+ import { listTodos } from "../pware.oc.opencode/resolver/pware.oc.opencode.resolver.todo.js"
17
+ import { planSessionIndex, type PlanSessionIndex } from "../pware.oc.omo/resolver/index.js"
18
+ import type { SqlDb } from "../pware.oc.core/pware.oc.core.sqlite.js"
19
+ import { readonlyReadGateway } from "../pware.oc.core/pware.oc.core.sqliteGateway.js"
20
+ import { profileAsync } from "../pware.oc.core/pware.oc.core.debug.js"
21
+ import { basenameOf } from "../pware.oc.core/pware.oc.core.paths.js"
22
+ import { readRunContinuationState } from "../pware.oc.omo/resolver/pware.oc.omo.resolver.approvalState.js"
23
+ import { BACKGROUND_TASK_ACTIVE } from "../pware.oc.omo/constants/pware.oc.omo.constants.backgroundTask.js"
24
+ import { STATUS_COMPLETED } from "../pware.oc.core/constants/pware.oc.core.constants.status.js"
25
+ import type { ApprovalItem } from "../pware.oc.omo/resolver/pware.oc.omo.resolver.plan.js"
26
+
27
+ /**
28
+ * An approval item with its planner-session activity attached. `sessionState`
29
+ * is a runtime enrichment: it lives here on the runtime layer's enriched type,
30
+ * not on omo's base `ApprovalItem` (omo must not import the opencode session type).
31
+ */
32
+ export type EnrichedApproval = ApprovalItem & {
33
+ sessionState: SessionActivityState | null
34
+ /** Writer session's todos all completed (and at least one present). */
35
+ todosDone: boolean
36
+ }
37
+
38
+ function blank(items: readonly ApprovalItem[]): EnrichedApproval[] {
39
+ return items.map((item) => ({ ...item, sessionState: null, todosDone: false }))
40
+ }
41
+
42
+ function sessionTodosDone(db: SqlDb, sessionId: string): boolean {
43
+ const todos = listTodos(db, sessionId)
44
+ return todos.length > 0 && todos.every((t) => t.status === STATUS_COMPLETED)
45
+ }
46
+
47
+ type EnrichmentRead = {
48
+ readonly index: PlanSessionIndex | null
49
+ readonly approvals: EnrichedApproval[]
50
+ }
51
+
52
+ export async function enrichApprovalSessionStates(
53
+ items: readonly ApprovalItem[],
54
+ opts: { dbPath: string | null | undefined; projectRoot: string | null | undefined; now?: number },
55
+ ): Promise<EnrichedApproval[]> {
56
+ if (items.length === 0) return []
57
+ const dbPath = opts.dbPath
58
+ const projectRoot = opts.projectRoot
59
+ if (!dbPath || !projectRoot) return blank(items)
60
+ const itemStages = items.map((item) => (db: SqlDb, state: EnrichmentRead): EnrichmentRead => {
61
+ const sessionId = state.index?.fileWriter.get(basenameOf(item.rel))?.sessionId ?? null
62
+ const sessionState = sessionId
63
+ ? sessionActivityState(db, sessionId, {
64
+ backgroundTaskActive:
65
+ readRunContinuationState(projectRoot, sessionId) === BACKGROUND_TASK_ACTIVE,
66
+ now: opts.now,
67
+ })
68
+ : null
69
+ const todosDone = sessionId ? sessionTodosDone(db, sessionId) : false
70
+ return {
71
+ ...state,
72
+ approvals: [...state.approvals, { ...item, sessionState, todosDone }],
73
+ }
74
+ })
75
+ const result = await profileAsync("mywork.approvals", () =>
76
+ readonlyReadGateway.run<EnrichmentRead>({
77
+ dbPath,
78
+ initial: () => ({ index: null, approvals: [] }),
79
+ stages: [
80
+ (db, state) => ({ ...state, index: planSessionIndex(db, null, projectRoot) }),
81
+ ...itemStages,
82
+ ],
83
+ fallback: () => ({ index: null, approvals: blank(items) }),
84
+ }),
85
+ )
86
+ return result.approvals
87
+ }