@jmoyers/harness 0.1.11 → 0.1.20

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 (232) hide show
  1. package/README.md +31 -39
  2. package/package.json +31 -11
  3. package/packages/harness-ai/src/anthropic-protocol.ts +68 -68
  4. package/packages/harness-ai/src/stream-text.ts +13 -91
  5. package/packages/harness-ui/src/frame-primitives.ts +158 -0
  6. package/packages/harness-ui/src/index.ts +18 -0
  7. package/packages/harness-ui/src/interaction/conversation-input-forwarder.ts +221 -0
  8. package/packages/harness-ui/src/interaction/conversation-selection-input.ts +213 -0
  9. package/packages/harness-ui/src/interaction/global-shortcut-input.ts +172 -0
  10. package/{src/ui → packages/harness-ui/src/interaction}/input-preflight.ts +10 -12
  11. package/{src/ui → packages/harness-ui/src/interaction}/input-token-router.ts +120 -69
  12. package/packages/harness-ui/src/interaction/input.ts +420 -0
  13. package/packages/harness-ui/src/interaction/left-nav-input.ts +166 -0
  14. package/{src/ui → packages/harness-ui/src/interaction}/main-pane-pointer-input.ts +91 -23
  15. package/{src/ui → packages/harness-ui/src/interaction}/pointer-routing-input.ts +112 -48
  16. package/packages/harness-ui/src/interaction/rail-pointer-input.ts +62 -0
  17. package/packages/harness-ui/src/interaction/repository-fold-input.ts +118 -0
  18. package/packages/harness-ui/src/kit.ts +476 -0
  19. package/packages/harness-ui/src/layout.ts +238 -0
  20. package/packages/harness-ui/src/modal-manager.ts +222 -0
  21. package/{src/ui → packages/harness-ui/src}/screen.ts +53 -26
  22. package/packages/harness-ui/src/surface.ts +252 -0
  23. package/packages/harness-ui/src/text-layout.ts +210 -0
  24. package/packages/nim-core/src/contracts.ts +239 -0
  25. package/packages/nim-core/src/event-store.ts +299 -0
  26. package/packages/nim-core/src/events.ts +53 -0
  27. package/packages/nim-core/src/index.ts +9 -0
  28. package/packages/nim-core/src/provider-router.ts +129 -0
  29. package/packages/nim-core/src/providers/anthropic-driver.ts +291 -0
  30. package/packages/nim-core/src/runtime-factory.ts +49 -0
  31. package/packages/nim-core/src/runtime.ts +1797 -0
  32. package/packages/nim-core/src/session-store.ts +516 -0
  33. package/packages/nim-core/src/telemetry.ts +48 -0
  34. package/packages/nim-test-tui/src/index.ts +150 -0
  35. package/packages/nim-ui-core/src/index.ts +1 -0
  36. package/packages/nim-ui-core/src/projection.ts +87 -0
  37. package/scripts/codex-live-mux-runtime.ts +2 -3872
  38. package/scripts/control-plane-daemon.ts +11 -0
  39. package/scripts/harness-bin.js +5 -0
  40. package/scripts/harness-commands.ts +300 -0
  41. package/scripts/harness-runtime.ts +82 -0
  42. package/scripts/harness.ts +33 -3019
  43. package/scripts/nim-tui-smoke.ts +748 -0
  44. package/src/cli/auth/runtime.ts +948 -0
  45. package/src/cli/gateway/runtime.ts +1872 -0
  46. package/src/cli/parsing/flags.ts +23 -0
  47. package/src/cli/parsing/session.ts +42 -0
  48. package/src/cli/runtime/context.ts +193 -0
  49. package/src/cli/runtime-app/application.ts +392 -0
  50. package/src/cli/runtime-infra/gateway-control.ts +729 -0
  51. package/{scripts/harness-inspector.ts → src/cli/workflows/inspector.ts} +14 -11
  52. package/src/cli/workflows/runtime.ts +965 -0
  53. package/src/clients/tui/left-rail-interactions.ts +519 -0
  54. package/src/clients/tui/main-pane-interactions.ts +509 -0
  55. package/src/clients/tui/modal-input-routing.ts +71 -0
  56. package/src/clients/tui/render-snapshot-adapter.ts +88 -0
  57. package/src/clients/web/synced-selectors.ts +132 -0
  58. package/src/codex/live-session.ts +82 -29
  59. package/src/config/config-core.ts +348 -8
  60. package/src/config/harness.config.template.jsonc +33 -0
  61. package/src/control-plane/agent-realtime-api.ts +82 -427
  62. package/src/control-plane/session-summary.ts +10 -81
  63. package/src/control-plane/status/reducer-base.ts +12 -12
  64. package/src/control-plane/status/reducers/claude-status-reducer.ts +3 -3
  65. package/src/control-plane/status/reducers/codex-status-reducer.ts +4 -4
  66. package/src/control-plane/status/reducers/cursor-status-reducer.ts +3 -3
  67. package/src/control-plane/stream-client.ts +12 -2
  68. package/src/control-plane/stream-command-parser.ts +83 -143
  69. package/src/control-plane/stream-protocol.ts +53 -37
  70. package/src/control-plane/stream-server-command.ts +376 -69
  71. package/src/control-plane/stream-server-session-runtime.ts +3 -2
  72. package/src/control-plane/stream-server.ts +864 -70
  73. package/src/control-plane/stream-session-runtime-types.ts +41 -0
  74. package/src/{mux/live-mux/control-plane-records.ts → core/contracts/records.ts} +24 -97
  75. package/src/core/state/observed-stream-cursor.ts +43 -0
  76. package/src/core/state/synced-observed-state.ts +273 -0
  77. package/src/core/store/harness-synced-store.ts +81 -0
  78. package/src/diff/budget.ts +136 -0
  79. package/src/diff/build.ts +289 -0
  80. package/src/diff/chunker.ts +146 -0
  81. package/src/diff/git-invoke.ts +315 -0
  82. package/src/diff/git-parse.ts +472 -0
  83. package/src/diff/hash.ts +70 -0
  84. package/src/diff/index.ts +24 -0
  85. package/src/diff/normalize.ts +134 -0
  86. package/src/diff/types.ts +178 -0
  87. package/src/diff-ui/args.ts +346 -0
  88. package/src/diff-ui/commands.ts +123 -0
  89. package/src/diff-ui/finder.ts +94 -0
  90. package/src/diff-ui/highlight.ts +127 -0
  91. package/src/diff-ui/index.ts +2 -0
  92. package/src/diff-ui/model.ts +141 -0
  93. package/src/diff-ui/pager.ts +412 -0
  94. package/src/diff-ui/render.ts +337 -0
  95. package/src/diff-ui/runtime.ts +379 -0
  96. package/src/diff-ui/state.ts +224 -0
  97. package/src/diff-ui/types.ts +236 -0
  98. package/src/domain/workspace.ts +68 -5
  99. package/src/mux/control-plane-op-queue.ts +93 -7
  100. package/src/mux/conversation-rail.ts +28 -71
  101. package/src/mux/dual-pane-core.ts +13 -13
  102. package/src/mux/harness-core-ui.ts +313 -42
  103. package/src/mux/input-shortcuts.ts +13 -131
  104. package/src/mux/keybinding-catalog.ts +340 -0
  105. package/src/mux/keybinding-registry.ts +103 -0
  106. package/src/mux/live-mux/command-menu-open-in.ts +280 -0
  107. package/src/mux/live-mux/command-menu.ts +167 -4
  108. package/src/mux/live-mux/conversation-state.ts +13 -0
  109. package/src/mux/live-mux/directory-resolution.ts +1 -1
  110. package/src/mux/live-mux/git-snapshot.ts +33 -2
  111. package/src/mux/live-mux/global-shortcut-handlers.ts +6 -0
  112. package/src/mux/live-mux/home-pane-drop.ts +1 -1
  113. package/src/mux/live-mux/home-pane-pointer.ts +10 -0
  114. package/src/mux/live-mux/input-forwarding.ts +59 -2
  115. package/src/mux/live-mux/left-nav-activation.ts +124 -7
  116. package/src/mux/live-mux/left-nav.ts +35 -0
  117. package/src/mux/live-mux/link-click.ts +292 -0
  118. package/src/mux/live-mux/modal-command-menu-handler.ts +46 -9
  119. package/src/mux/live-mux/modal-conversation-handlers.ts +5 -1
  120. package/src/mux/live-mux/modal-input-reducers.ts +77 -12
  121. package/src/mux/live-mux/modal-overlays.ts +168 -34
  122. package/src/mux/live-mux/modal-pointer.ts +3 -7
  123. package/src/mux/live-mux/modal-prompt-handlers.ts +23 -2
  124. package/src/mux/live-mux/modal-release-notes-handler.ts +111 -0
  125. package/src/mux/live-mux/modal-task-editor-handler.ts +16 -11
  126. package/src/mux/live-mux/pointer-routing.ts +5 -2
  127. package/src/mux/live-mux/project-pane-pointer.ts +8 -0
  128. package/src/mux/live-mux/rail-layout.ts +33 -30
  129. package/src/mux/live-mux/release-notes.ts +383 -0
  130. package/src/mux/live-mux/render-trace-analysis.ts +52 -7
  131. package/src/mux/live-mux/repository-folding.ts +3 -0
  132. package/src/mux/live-mux/selection.ts +0 -4
  133. package/src/mux/live-mux/session-diagnostics-paths.ts +21 -0
  134. package/src/mux/project-pane-github-review.ts +271 -0
  135. package/src/mux/render-frame.ts +4 -0
  136. package/src/mux/runtime-app/codex-live-mux-runtime.ts +5191 -0
  137. package/src/mux/task-composer.ts +21 -14
  138. package/src/mux/task-focused-pane.ts +118 -117
  139. package/src/mux/task-screen-keybindings.ts +10 -101
  140. package/src/mux/workspace-rail-model.ts +270 -104
  141. package/src/mux/workspace-rail.ts +45 -22
  142. package/src/pty/session-broker.ts +1 -1
  143. package/{scripts → src/recording}/terminal-recording-gif-lib.ts +2 -2
  144. package/src/services/control-plane.ts +50 -32
  145. package/src/services/conversation-lifecycle.ts +118 -87
  146. package/src/services/conversation-startup-hydration.ts +20 -12
  147. package/src/services/directory-hydration.ts +21 -16
  148. package/src/services/event-persistence.ts +7 -0
  149. package/src/services/left-rail-pointer-handler.ts +329 -0
  150. package/src/services/mux-ui-state-persistence.ts +5 -1
  151. package/src/services/recording.ts +34 -26
  152. package/src/services/runtime-command-menu-agent-tools.ts +1 -1
  153. package/src/services/runtime-control-actions.ts +79 -61
  154. package/src/services/runtime-control-plane-ops.ts +122 -83
  155. package/src/services/runtime-conversation-actions.ts +40 -26
  156. package/src/services/runtime-conversation-activation.ts +73 -46
  157. package/src/services/runtime-conversation-starter.ts +53 -45
  158. package/src/services/runtime-conversation-title-edit.ts +91 -80
  159. package/src/services/runtime-envelope-handler.ts +107 -105
  160. package/src/services/runtime-git-state.ts +42 -29
  161. package/src/services/runtime-layout-resize.ts +3 -1
  162. package/src/services/runtime-left-rail-render.ts +99 -63
  163. package/src/services/runtime-nim-cli-session.ts +438 -0
  164. package/src/services/runtime-nim-session.ts +705 -0
  165. package/src/services/runtime-nim-tool-bridge.ts +141 -0
  166. package/src/services/runtime-observed-event-projection-pipeline.ts +45 -0
  167. package/src/services/runtime-process-wiring.ts +29 -36
  168. package/src/services/runtime-project-pane-github-review-cache.ts +164 -0
  169. package/src/services/runtime-render-flush.ts +63 -70
  170. package/src/services/runtime-render-lifecycle.ts +65 -64
  171. package/src/services/runtime-render-orchestrator.ts +55 -45
  172. package/src/services/runtime-render-pipeline.ts +106 -103
  173. package/src/services/runtime-render-state.ts +62 -49
  174. package/src/services/runtime-repository-actions.ts +97 -72
  175. package/src/services/runtime-right-pane-render.ts +80 -53
  176. package/src/services/runtime-shutdown.ts +38 -35
  177. package/src/services/runtime-stream-subscriptions.ts +35 -27
  178. package/src/services/runtime-task-composer-persistence.ts +71 -59
  179. package/src/services/runtime-task-composer-snapshot.ts +14 -0
  180. package/src/services/runtime-task-editor-actions.ts +46 -29
  181. package/src/services/runtime-task-pane-actions.ts +220 -134
  182. package/src/services/runtime-task-pane-shortcuts.ts +323 -123
  183. package/src/services/runtime-workspace-observed-effect-queue.ts +25 -0
  184. package/src/services/runtime-workspace-observed-events.ts +33 -184
  185. package/src/services/runtime-workspace-observed-transition-policy.ts +228 -0
  186. package/src/services/session-diagnostics-store.ts +217 -0
  187. package/src/services/startup-background-resume.ts +26 -21
  188. package/src/services/startup-orchestrator.ts +16 -13
  189. package/src/services/startup-paint-tracker.ts +29 -21
  190. package/src/services/startup-persisted-conversation-queue.ts +19 -13
  191. package/src/services/startup-settled-gate.ts +25 -15
  192. package/src/services/startup-shutdown.ts +18 -22
  193. package/src/services/startup-state-hydration.ts +44 -34
  194. package/src/services/startup-visibility.ts +12 -4
  195. package/src/services/task-pane-selection-actions.ts +89 -72
  196. package/src/services/task-planning-hydration.ts +24 -18
  197. package/src/services/task-planning-observed-events.ts +50 -52
  198. package/src/services/workspace-observed-events.ts +66 -63
  199. package/src/storage/storage-lifecycle-core.ts +438 -0
  200. package/src/store/control-plane-store-normalize.ts +33 -242
  201. package/src/store/control-plane-store-types.ts +1 -35
  202. package/src/store/control-plane-store.ts +360 -56
  203. package/src/store/event-store.ts +366 -8
  204. package/src/terminal/snapshot-oracle.ts +207 -94
  205. package/src/ui/mux-theme.ts +112 -8
  206. package/src/ui/panes/home-gridfire.ts +40 -31
  207. package/src/ui/panes/home.ts +10 -2
  208. package/src/ui/panes/nim.ts +315 -0
  209. package/src/mux/live-mux/actions-task.ts +0 -115
  210. package/src/mux/live-mux/left-rail-actions.ts +0 -118
  211. package/src/mux/live-mux/left-rail-conversation-click.ts +0 -85
  212. package/src/mux/live-mux/left-rail-pointer.ts +0 -74
  213. package/src/mux/live-mux/task-pane-shortcuts.ts +0 -206
  214. package/src/services/runtime-directory-actions.ts +0 -164
  215. package/src/services/runtime-input-pipeline.ts +0 -50
  216. package/src/services/runtime-input-router.ts +0 -195
  217. package/src/services/runtime-main-pane-input.ts +0 -230
  218. package/src/services/runtime-modal-input.ts +0 -137
  219. package/src/services/runtime-navigation-input.ts +0 -197
  220. package/src/services/runtime-rail-input.ts +0 -279
  221. package/src/services/runtime-task-pane.ts +0 -62
  222. package/src/services/runtime-workspace-actions.ts +0 -158
  223. package/src/ui/conversation-input-forwarder.ts +0 -114
  224. package/src/ui/conversation-selection-input.ts +0 -103
  225. package/src/ui/global-shortcut-input.ts +0 -89
  226. package/src/ui/input.ts +0 -269
  227. package/src/ui/kit.ts +0 -509
  228. package/src/ui/left-nav-input.ts +0 -80
  229. package/src/ui/left-rail-pointer-input.ts +0 -148
  230. package/src/ui/modals/manager.ts +0 -218
  231. package/src/ui/repository-fold-input.ts +0 -91
  232. package/src/ui/surface.ts +0 -224
@@ -0,0 +1,299 @@
1
+ import { mkdirSync } from 'node:fs';
2
+ import { createRequire } from 'node:module';
3
+ import { dirname } from 'node:path';
4
+ import { parseNimEventEnvelope, type NimEventEnvelope } from './events.ts';
5
+
6
+ export type NimEventStoreQuery = {
7
+ readonly tenantId: string;
8
+ readonly sessionId?: string;
9
+ readonly runId?: string;
10
+ };
11
+
12
+ export interface NimEventStore {
13
+ append(event: NimEventEnvelope): void;
14
+ getById(eventId: string): NimEventEnvelope | undefined;
15
+ list(input: NimEventStoreQuery): readonly NimEventEnvelope[];
16
+ }
17
+
18
+ export class InMemoryNimEventStore implements NimEventStore {
19
+ private events: NimEventEnvelope[] = [];
20
+ private eventById = new Map<string, NimEventEnvelope>();
21
+
22
+ public append(event: NimEventEnvelope): void {
23
+ this.events.push(event);
24
+ this.eventById.set(event.event_id, event);
25
+ }
26
+
27
+ public getById(eventId: string): NimEventEnvelope | undefined {
28
+ return this.eventById.get(eventId);
29
+ }
30
+
31
+ public list(input: NimEventStoreQuery): readonly NimEventEnvelope[] {
32
+ return this.events.filter((event) => {
33
+ if (event.tenant_id !== input.tenantId) {
34
+ return false;
35
+ }
36
+ if (input.sessionId !== undefined && event.session_id !== input.sessionId) {
37
+ return false;
38
+ }
39
+ if (input.runId !== undefined && event.run_id !== input.runId) {
40
+ return false;
41
+ }
42
+ return true;
43
+ });
44
+ }
45
+ }
46
+
47
+ interface StatementLike {
48
+ run: (...params: unknown[]) => unknown;
49
+ get: (...params: unknown[]) => unknown;
50
+ all: (...params: unknown[]) => unknown[];
51
+ }
52
+
53
+ class WrappedStatement {
54
+ private readonly statement: StatementLike;
55
+
56
+ public constructor(statement: StatementLike) {
57
+ this.statement = statement;
58
+ }
59
+
60
+ public run(...params: unknown[]): unknown {
61
+ return this.statement.run(...params);
62
+ }
63
+
64
+ public get(...params: unknown[]): unknown {
65
+ const value = this.statement.get(...params);
66
+ return value === null ? undefined : value;
67
+ }
68
+
69
+ public all(...params: unknown[]): unknown[] {
70
+ return this.statement.all(...params);
71
+ }
72
+ }
73
+
74
+ interface SqliteDatabaseLike {
75
+ close: () => void;
76
+ exec: (sql: string) => unknown;
77
+ prepare: (sql: string) => StatementLike;
78
+ }
79
+
80
+ type BunSqliteModule = {
81
+ Database: new (path: string) => SqliteDatabaseLike;
82
+ };
83
+
84
+ interface SqliteRuntime {
85
+ readonly bunVersion: string | undefined;
86
+ readonly loadModule: (specifier: 'bun:sqlite') => unknown;
87
+ }
88
+
89
+ const require = createRequire(import.meta.url);
90
+ const defaultRuntime: SqliteRuntime = {
91
+ bunVersion: process.versions.bun,
92
+ loadModule: (specifier) => require(specifier) as unknown,
93
+ };
94
+
95
+ function createDatabaseForRuntime(
96
+ path: string,
97
+ runtime: SqliteRuntime = defaultRuntime,
98
+ ): SqliteDatabaseLike {
99
+ if (runtime.bunVersion === undefined) {
100
+ throw new Error('bun runtime is required for sqlite access');
101
+ }
102
+ const module = runtime.loadModule('bun:sqlite') as BunSqliteModule;
103
+ return new module.Database(path);
104
+ }
105
+
106
+ class DatabaseSync {
107
+ private readonly database: SqliteDatabaseLike;
108
+
109
+ public constructor(path: string, runtime: SqliteRuntime = defaultRuntime) {
110
+ this.database = createDatabaseForRuntime(path, runtime);
111
+ }
112
+
113
+ public close(): void {
114
+ this.database.close();
115
+ }
116
+
117
+ public exec(sql: string): void {
118
+ this.database.exec(sql);
119
+ }
120
+
121
+ public prepare(sql: string): WrappedStatement {
122
+ return new WrappedStatement(this.database.prepare(sql));
123
+ }
124
+ }
125
+
126
+ const NIM_EVENT_STORE_SCHEMA_VERSION = 1;
127
+
128
+ function asRecord(value: unknown): Record<string, unknown> {
129
+ if (typeof value !== 'object' || value === null) {
130
+ throw new Error('expected sqlite row object');
131
+ }
132
+ return value as Record<string, unknown>;
133
+ }
134
+
135
+ function asString(value: unknown, field: string): string {
136
+ if (typeof value !== 'string') {
137
+ throw new Error(`expected string for ${field}`);
138
+ }
139
+ return value;
140
+ }
141
+
142
+ function readEventJsonFromRow(row: unknown): string {
143
+ return asString(asRecord(row).event_json, 'event_json');
144
+ }
145
+
146
+ function parseEventRow(row: unknown): NimEventEnvelope {
147
+ const eventJson = readEventJsonFromRow(row);
148
+ return parseNimEventEnvelope(JSON.parse(eventJson) as unknown);
149
+ }
150
+
151
+ function preparePath(filePath: string): string {
152
+ if (filePath === ':memory:') {
153
+ return filePath;
154
+ }
155
+ mkdirSync(dirname(filePath), { recursive: true });
156
+ return filePath;
157
+ }
158
+
159
+ export class NimSqliteEventStore implements NimEventStore {
160
+ private readonly db: DatabaseSync;
161
+
162
+ public constructor(filePath = ':memory:') {
163
+ this.db = new DatabaseSync(preparePath(filePath));
164
+ this.configureConnection();
165
+ this.initializeSchema();
166
+ }
167
+
168
+ public close(): void {
169
+ this.db.close();
170
+ }
171
+
172
+ public append(event: NimEventEnvelope): void {
173
+ this.db
174
+ .prepare(
175
+ `
176
+ INSERT INTO nim_events (
177
+ event_id,
178
+ tenant_id,
179
+ session_id,
180
+ run_id,
181
+ event_seq,
182
+ ts,
183
+ event_json
184
+ ) VALUES (?, ?, ?, ?, ?, ?, ?)
185
+ `,
186
+ )
187
+ .run(
188
+ event.event_id,
189
+ event.tenant_id,
190
+ event.session_id,
191
+ event.run_id,
192
+ event.event_seq,
193
+ event.ts,
194
+ JSON.stringify(event),
195
+ );
196
+ }
197
+
198
+ public getById(eventId: string): NimEventEnvelope | undefined {
199
+ const row = this.db
200
+ .prepare(
201
+ `
202
+ SELECT event_json
203
+ FROM nim_events
204
+ WHERE event_id = ?
205
+ LIMIT 1
206
+ `,
207
+ )
208
+ .get(eventId);
209
+ if (row === undefined) {
210
+ return undefined;
211
+ }
212
+ return parseEventRow(row);
213
+ }
214
+
215
+ public list(input: NimEventStoreQuery): readonly NimEventEnvelope[] {
216
+ const clauses = ['tenant_id = ?'];
217
+ const args: Array<number | string> = [input.tenantId];
218
+
219
+ if (input.sessionId !== undefined) {
220
+ clauses.push('session_id = ?');
221
+ args.push(input.sessionId);
222
+ }
223
+ if (input.runId !== undefined) {
224
+ clauses.push('run_id = ?');
225
+ args.push(input.runId);
226
+ }
227
+
228
+ const rows = this.db
229
+ .prepare(
230
+ `
231
+ SELECT event_json
232
+ FROM nim_events
233
+ WHERE ${clauses.join(' AND ')}
234
+ ORDER BY row_id ASC
235
+ `,
236
+ )
237
+ .all(...args);
238
+ return rows.map((row) => parseEventRow(row));
239
+ }
240
+
241
+ private configureConnection(): void {
242
+ this.db.exec('PRAGMA journal_mode = WAL;');
243
+ this.db.exec('PRAGMA synchronous = NORMAL;');
244
+ this.db.exec('PRAGMA busy_timeout = 5000;');
245
+ }
246
+
247
+ private initializeSchema(): void {
248
+ this.db.exec('BEGIN IMMEDIATE TRANSACTION');
249
+ try {
250
+ const currentVersion = this.readSchemaVersion();
251
+ if (currentVersion > NIM_EVENT_STORE_SCHEMA_VERSION) {
252
+ throw new Error(
253
+ `nim event store schema version ${String(currentVersion)} is newer than supported version ${String(NIM_EVENT_STORE_SCHEMA_VERSION)}`,
254
+ );
255
+ }
256
+ this.applySchemaV1();
257
+ this.writeSchemaVersion(NIM_EVENT_STORE_SCHEMA_VERSION);
258
+ this.db.exec('COMMIT');
259
+ } catch (error) {
260
+ this.db.exec('ROLLBACK');
261
+ throw error;
262
+ }
263
+ }
264
+
265
+ private applySchemaV1(): void {
266
+ this.db.exec(`
267
+ CREATE TABLE IF NOT EXISTS nim_events (
268
+ row_id INTEGER PRIMARY KEY AUTOINCREMENT,
269
+ event_id TEXT NOT NULL UNIQUE,
270
+ tenant_id TEXT NOT NULL,
271
+ session_id TEXT NOT NULL,
272
+ run_id TEXT NOT NULL,
273
+ event_seq INTEGER NOT NULL,
274
+ ts TEXT NOT NULL,
275
+ event_json TEXT NOT NULL
276
+ );
277
+ `);
278
+ this.db.exec(`
279
+ CREATE INDEX IF NOT EXISTS idx_nim_events_scope
280
+ ON nim_events (tenant_id, session_id, run_id, row_id);
281
+ `);
282
+ }
283
+
284
+ private readSchemaVersion(): number {
285
+ const row = this.db.prepare('PRAGMA user_version;').get();
286
+ if (row === undefined) {
287
+ throw new Error('failed to read nim event store schema version');
288
+ }
289
+ const version = asRecord(row).user_version;
290
+ if (typeof version !== 'number' || !Number.isInteger(version) || version < 0) {
291
+ throw new Error(`invalid nim event store schema version value: ${String(version)}`);
292
+ }
293
+ return version;
294
+ }
295
+
296
+ private writeSchemaVersion(version: number): void {
297
+ this.db.exec(`PRAGMA user_version = ${String(version)};`);
298
+ }
299
+ }
@@ -0,0 +1,53 @@
1
+ import { z } from 'zod';
2
+
3
+ export const nimEventSourceSchema = z.enum([
4
+ 'provider',
5
+ 'tool',
6
+ 'memory',
7
+ 'skill',
8
+ 'soul',
9
+ 'system',
10
+ ]);
11
+
12
+ export const nimEventStateSchema = z.enum(['thinking', 'tool-calling', 'responding', 'idle']);
13
+
14
+ export const nimEventEnvelopeSchema = z
15
+ .object({
16
+ event_id: z.string().min(1),
17
+ event_seq: z.number().int().nonnegative(),
18
+ ts: z.string().min(1),
19
+ tenant_id: z.string().min(1),
20
+ user_id: z.string().min(1),
21
+ workspace_id: z.string().min(1),
22
+ session_id: z.string().min(1),
23
+ run_id: z.string(),
24
+ turn_id: z.string(),
25
+ step_id: z.string().min(1),
26
+ tool_call_id: z.string().min(1).optional(),
27
+ source: nimEventSourceSchema,
28
+ type: z.string().min(1),
29
+ payload_ref: z.string().min(1).optional(),
30
+ payload_hash: z.string().min(1),
31
+ idempotency_key: z.string().min(1),
32
+ lane: z.string().min(1),
33
+ queue_id: z.string().min(1).optional(),
34
+ queue_position: z.number().int().nonnegative().optional(),
35
+ steer_strategy: z.enum(['append']).optional(),
36
+ strategy_phase: z.string().min(1).optional(),
37
+ provider_event_index: z.number().int().nonnegative().optional(),
38
+ state: nimEventStateSchema.optional(),
39
+ policy_hash: z.string().min(1),
40
+ skills_snapshot_version: z.number().int().nonnegative().optional(),
41
+ soul_hash: z.string().min(1).optional(),
42
+ trace_id: z.string().min(1),
43
+ span_id: z.string().min(1),
44
+ parent_span_id: z.string().min(1).optional(),
45
+ data: z.record(z.string(), z.unknown()).optional(),
46
+ })
47
+ .strict();
48
+
49
+ export type NimEventEnvelope = z.infer<typeof nimEventEnvelopeSchema>;
50
+
51
+ export function parseNimEventEnvelope(input: unknown): NimEventEnvelope {
52
+ return nimEventEnvelopeSchema.parse(input);
53
+ }
@@ -0,0 +1,9 @@
1
+ export * from './contracts.ts';
2
+ export * from './event-store.ts';
3
+ export * from './events.ts';
4
+ export * from './provider-router.ts';
5
+ export * from './providers/anthropic-driver.ts';
6
+ export * from './runtime.ts';
7
+ export * from './runtime-factory.ts';
8
+ export * from './session-store.ts';
9
+ export * from './telemetry.ts';
@@ -0,0 +1,129 @@
1
+ import type { NimModelRef, NimProvider, NimProviderId, NimToolDefinition } from './contracts.ts';
2
+
3
+ export type ParsedNimModelRef = {
4
+ readonly providerId: NimProviderId;
5
+ readonly providerModelId: string;
6
+ };
7
+
8
+ export type NimProviderTurnInput = {
9
+ readonly modelRef: NimModelRef;
10
+ readonly providerModelId: string;
11
+ readonly input: string;
12
+ readonly tools: readonly NimToolDefinition[];
13
+ readonly abortSignal?: AbortSignal;
14
+ };
15
+
16
+ export type NimProviderTurnEvent =
17
+ | {
18
+ readonly type: 'provider.thinking.started';
19
+ }
20
+ | {
21
+ readonly type: 'provider.thinking.delta';
22
+ readonly text: string;
23
+ }
24
+ | {
25
+ readonly type: 'provider.thinking.completed';
26
+ }
27
+ | {
28
+ readonly type: 'tool.call.started';
29
+ readonly toolCallId: string;
30
+ readonly toolName: string;
31
+ }
32
+ | {
33
+ readonly type: 'tool.call.arguments.delta';
34
+ readonly toolCallId: string;
35
+ readonly delta: string;
36
+ }
37
+ | {
38
+ readonly type: 'tool.call.completed';
39
+ readonly toolCallId: string;
40
+ readonly toolName: string;
41
+ }
42
+ | {
43
+ readonly type: 'tool.call.failed';
44
+ readonly toolCallId: string;
45
+ readonly toolName: string;
46
+ readonly error: string;
47
+ }
48
+ | {
49
+ readonly type: 'tool.result.emitted';
50
+ readonly toolCallId: string;
51
+ readonly toolName: string;
52
+ readonly output?: unknown;
53
+ }
54
+ | {
55
+ readonly type: 'assistant.output.delta';
56
+ readonly text: string;
57
+ }
58
+ | {
59
+ readonly type: 'assistant.output.completed';
60
+ }
61
+ | {
62
+ readonly type: 'provider.turn.finished';
63
+ readonly finishReason: string;
64
+ }
65
+ | {
66
+ readonly type: 'provider.turn.error';
67
+ readonly message: string;
68
+ };
69
+
70
+ export interface NimProviderDriver {
71
+ readonly providerId: NimProviderId;
72
+ runTurn(input: NimProviderTurnInput): AsyncIterable<NimProviderTurnEvent>;
73
+ }
74
+
75
+ export function parseNimModelRef(modelRef: NimModelRef): ParsedNimModelRef {
76
+ const slashIndex = modelRef.indexOf('/');
77
+ if (slashIndex <= 0 || slashIndex === modelRef.length - 1) {
78
+ throw new Error(`invalid model ref: ${modelRef}`);
79
+ }
80
+
81
+ const providerId = modelRef.slice(0, slashIndex);
82
+ const providerModelId = modelRef.slice(slashIndex + 1);
83
+ if (providerId.trim().length === 0 || providerModelId.trim().length === 0) {
84
+ throw new Error(`invalid model ref: ${modelRef}`);
85
+ }
86
+
87
+ return {
88
+ providerId,
89
+ providerModelId,
90
+ };
91
+ }
92
+
93
+ export type ResolvedNimProvider = {
94
+ readonly provider: NimProvider;
95
+ readonly parsedModel: ParsedNimModelRef;
96
+ readonly driver?: NimProviderDriver;
97
+ };
98
+
99
+ export class NimProviderRouter {
100
+ private providers = new Map<NimProviderId, NimProvider>();
101
+ private drivers = new Map<NimProviderId, NimProviderDriver>();
102
+
103
+ public registerProvider(provider: NimProvider): void {
104
+ this.providers.set(provider.id, provider);
105
+ }
106
+
107
+ public registerDriver(driver: NimProviderDriver): void {
108
+ this.drivers.set(driver.providerId, driver);
109
+ }
110
+
111
+ public resolveModel(modelRef: NimModelRef): ResolvedNimProvider {
112
+ const parsedModel = parseNimModelRef(modelRef);
113
+ const provider = this.providers.get(parsedModel.providerId);
114
+ if (provider === undefined) {
115
+ throw new Error(`provider not registered: ${parsedModel.providerId}`);
116
+ }
117
+
118
+ if (!provider.models.includes(modelRef)) {
119
+ throw new Error(`model not registered for provider ${provider.id}: ${modelRef}`);
120
+ }
121
+
122
+ const driver = this.drivers.get(parsedModel.providerId);
123
+ return {
124
+ provider,
125
+ parsedModel,
126
+ ...(driver !== undefined ? { driver } : {}),
127
+ };
128
+ }
129
+ }