@jmoyers/harness 0.1.10 → 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 (239) hide show
  1. package/README.md +31 -35
  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/{src/ui/modals/manager.ts → packages/harness-ui/src/modal-manager.ts} +94 -64
  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 -3721
  38. package/scripts/control-plane-daemon.ts +24 -2
  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 -3007
  43. package/scripts/nim-tui-smoke.ts +748 -0
  44. package/src/cli/auth/runtime.ts +948 -0
  45. package/src/cli/default-gateway-pointer.ts +193 -0
  46. package/src/cli/gateway/runtime.ts +1872 -0
  47. package/src/cli/parsing/flags.ts +23 -0
  48. package/src/cli/parsing/session.ts +42 -0
  49. package/src/cli/runtime/context.ts +193 -0
  50. package/src/cli/runtime-app/application.ts +392 -0
  51. package/src/cli/runtime-infra/gateway-control.ts +729 -0
  52. package/{scripts/harness-inspector.ts → src/cli/workflows/inspector.ts} +14 -11
  53. package/src/cli/workflows/runtime.ts +965 -0
  54. package/src/clients/tui/left-rail-interactions.ts +519 -0
  55. package/src/clients/tui/main-pane-interactions.ts +509 -0
  56. package/src/clients/tui/modal-input-routing.ts +71 -0
  57. package/src/clients/tui/render-snapshot-adapter.ts +88 -0
  58. package/src/clients/web/synced-selectors.ts +132 -0
  59. package/src/codex/live-session.ts +82 -29
  60. package/src/config/config-core.ts +361 -10
  61. package/src/config/harness-paths.ts +4 -7
  62. package/src/config/harness-runtime-migration.ts +142 -19
  63. package/src/config/harness.config.template.jsonc +33 -0
  64. package/src/config/secrets-core.ts +92 -4
  65. package/src/control-plane/agent-realtime-api.ts +82 -427
  66. package/src/control-plane/prompt/thread-title-namer.ts +49 -23
  67. package/src/control-plane/session-summary.ts +10 -81
  68. package/src/control-plane/status/reducer-base.ts +12 -12
  69. package/src/control-plane/status/reducers/claude-status-reducer.ts +3 -3
  70. package/src/control-plane/status/reducers/codex-status-reducer.ts +4 -4
  71. package/src/control-plane/status/reducers/cursor-status-reducer.ts +3 -3
  72. package/src/control-plane/stream-client.ts +12 -2
  73. package/src/control-plane/stream-command-parser.ts +83 -143
  74. package/src/control-plane/stream-protocol.ts +53 -37
  75. package/src/control-plane/stream-server-background.ts +18 -2
  76. package/src/control-plane/stream-server-command.ts +376 -69
  77. package/src/control-plane/stream-server-session-runtime.ts +3 -2
  78. package/src/control-plane/stream-server.ts +943 -80
  79. package/src/control-plane/stream-session-runtime-types.ts +41 -0
  80. package/src/{mux/live-mux/control-plane-records.ts → core/contracts/records.ts} +24 -97
  81. package/src/core/state/observed-stream-cursor.ts +43 -0
  82. package/src/core/state/synced-observed-state.ts +273 -0
  83. package/src/core/store/harness-synced-store.ts +81 -0
  84. package/src/diff/budget.ts +136 -0
  85. package/src/diff/build.ts +289 -0
  86. package/src/diff/chunker.ts +146 -0
  87. package/src/diff/git-invoke.ts +315 -0
  88. package/src/diff/git-parse.ts +472 -0
  89. package/src/diff/hash.ts +70 -0
  90. package/src/diff/index.ts +24 -0
  91. package/src/diff/normalize.ts +134 -0
  92. package/src/diff/types.ts +178 -0
  93. package/src/diff-ui/args.ts +346 -0
  94. package/src/diff-ui/commands.ts +123 -0
  95. package/src/diff-ui/finder.ts +94 -0
  96. package/src/diff-ui/highlight.ts +127 -0
  97. package/src/diff-ui/index.ts +2 -0
  98. package/src/diff-ui/model.ts +141 -0
  99. package/src/diff-ui/pager.ts +412 -0
  100. package/src/diff-ui/render.ts +337 -0
  101. package/src/diff-ui/runtime.ts +379 -0
  102. package/src/diff-ui/state.ts +224 -0
  103. package/src/diff-ui/types.ts +236 -0
  104. package/src/domain/conversations.ts +11 -7
  105. package/src/domain/workspace.ts +76 -4
  106. package/src/mux/control-plane-op-queue.ts +93 -7
  107. package/src/mux/conversation-rail.ts +28 -71
  108. package/src/mux/dual-pane-core.ts +13 -13
  109. package/src/mux/harness-core-ui.ts +313 -42
  110. package/src/mux/input-shortcuts.ts +22 -112
  111. package/src/mux/keybinding-catalog.ts +340 -0
  112. package/src/mux/keybinding-registry.ts +103 -0
  113. package/src/mux/live-mux/command-menu-open-in.ts +280 -0
  114. package/src/mux/live-mux/command-menu.ts +167 -4
  115. package/src/mux/live-mux/conversation-state.ts +13 -0
  116. package/src/mux/live-mux/directory-resolution.ts +1 -1
  117. package/src/mux/live-mux/git-parsing.ts +16 -0
  118. package/src/mux/live-mux/git-snapshot.ts +33 -2
  119. package/src/mux/live-mux/global-shortcut-handlers.ts +6 -0
  120. package/src/mux/live-mux/home-pane-drop.ts +1 -1
  121. package/src/mux/live-mux/home-pane-pointer.ts +10 -0
  122. package/src/mux/live-mux/input-forwarding.ts +59 -2
  123. package/src/mux/live-mux/left-nav-activation.ts +124 -7
  124. package/src/mux/live-mux/left-nav.ts +35 -0
  125. package/src/mux/live-mux/link-click.ts +292 -0
  126. package/src/mux/live-mux/modal-command-menu-handler.ts +46 -9
  127. package/src/mux/live-mux/modal-conversation-handlers.ts +5 -1
  128. package/src/mux/live-mux/modal-input-reducers.ts +106 -8
  129. package/src/mux/live-mux/modal-overlays.ts +210 -31
  130. package/src/mux/live-mux/modal-pointer.ts +3 -7
  131. package/src/mux/live-mux/modal-prompt-handlers.ts +107 -1
  132. package/src/mux/live-mux/modal-release-notes-handler.ts +111 -0
  133. package/src/mux/live-mux/modal-task-editor-handler.ts +16 -11
  134. package/src/mux/live-mux/pointer-routing.ts +5 -2
  135. package/src/mux/live-mux/project-pane-pointer.ts +8 -0
  136. package/src/mux/live-mux/rail-layout.ts +33 -30
  137. package/src/mux/live-mux/release-notes.ts +383 -0
  138. package/src/mux/live-mux/render-trace-analysis.ts +52 -7
  139. package/src/mux/live-mux/repository-folding.ts +3 -0
  140. package/src/mux/live-mux/selection.ts +0 -4
  141. package/src/mux/live-mux/session-diagnostics-paths.ts +21 -0
  142. package/src/mux/project-pane-github-review.ts +271 -0
  143. package/src/mux/render-frame.ts +4 -0
  144. package/src/mux/runtime-app/codex-live-mux-runtime.ts +5191 -0
  145. package/src/mux/task-composer.ts +21 -14
  146. package/src/mux/task-focused-pane.ts +118 -117
  147. package/src/mux/task-screen-keybindings.ts +19 -82
  148. package/src/mux/workspace-rail-model.ts +270 -104
  149. package/src/mux/workspace-rail.ts +45 -22
  150. package/src/pty/session-broker.ts +1 -1
  151. package/{scripts → src/recording}/terminal-recording-gif-lib.ts +2 -2
  152. package/src/services/control-plane.ts +50 -32
  153. package/src/services/conversation-lifecycle.ts +118 -87
  154. package/src/services/conversation-startup-hydration.ts +20 -12
  155. package/src/services/directory-hydration.ts +21 -16
  156. package/src/services/event-persistence.ts +7 -0
  157. package/src/services/left-rail-pointer-handler.ts +329 -0
  158. package/src/services/mux-ui-state-persistence.ts +5 -1
  159. package/src/services/recording.ts +34 -26
  160. package/src/services/runtime-command-menu-agent-tools.ts +1 -1
  161. package/src/services/runtime-control-actions.ts +79 -61
  162. package/src/services/runtime-control-plane-ops.ts +122 -83
  163. package/src/services/runtime-conversation-actions.ts +40 -26
  164. package/src/services/runtime-conversation-activation.ts +82 -30
  165. package/src/services/runtime-conversation-starter.ts +80 -48
  166. package/src/services/runtime-conversation-title-edit.ts +91 -80
  167. package/src/services/runtime-envelope-handler.ts +107 -105
  168. package/src/services/runtime-git-state.ts +42 -29
  169. package/src/services/runtime-layout-resize.ts +3 -1
  170. package/src/services/runtime-left-rail-render.ts +99 -63
  171. package/src/services/runtime-nim-cli-session.ts +438 -0
  172. package/src/services/runtime-nim-session.ts +705 -0
  173. package/src/services/runtime-nim-tool-bridge.ts +141 -0
  174. package/src/services/runtime-observed-event-projection-pipeline.ts +45 -0
  175. package/src/services/runtime-process-wiring.ts +29 -36
  176. package/src/services/runtime-project-pane-github-review-cache.ts +164 -0
  177. package/src/services/runtime-render-flush.ts +63 -70
  178. package/src/services/runtime-render-lifecycle.ts +65 -64
  179. package/src/services/runtime-render-orchestrator.ts +55 -45
  180. package/src/services/runtime-render-pipeline.ts +106 -103
  181. package/src/services/runtime-render-state.ts +62 -49
  182. package/src/services/runtime-repository-actions.ts +97 -70
  183. package/src/services/runtime-right-pane-render.ts +80 -53
  184. package/src/services/runtime-shutdown.ts +38 -35
  185. package/src/services/runtime-stream-subscriptions.ts +35 -27
  186. package/src/services/runtime-task-composer-persistence.ts +71 -59
  187. package/src/services/runtime-task-composer-snapshot.ts +14 -0
  188. package/src/services/runtime-task-editor-actions.ts +46 -29
  189. package/src/services/runtime-task-pane-actions.ts +220 -134
  190. package/src/services/runtime-task-pane-shortcuts.ts +323 -123
  191. package/src/services/runtime-workspace-observed-effect-queue.ts +25 -0
  192. package/src/services/runtime-workspace-observed-events.ts +33 -184
  193. package/src/services/runtime-workspace-observed-transition-policy.ts +228 -0
  194. package/src/services/session-diagnostics-store.ts +217 -0
  195. package/src/services/startup-background-resume.ts +26 -21
  196. package/src/services/startup-orchestrator.ts +16 -13
  197. package/src/services/startup-paint-tracker.ts +29 -21
  198. package/src/services/startup-persisted-conversation-queue.ts +19 -13
  199. package/src/services/startup-settled-gate.ts +25 -15
  200. package/src/services/startup-shutdown.ts +18 -22
  201. package/src/services/startup-state-hydration.ts +44 -34
  202. package/src/services/startup-visibility.ts +12 -4
  203. package/src/services/task-pane-selection-actions.ts +89 -72
  204. package/src/services/task-planning-hydration.ts +24 -18
  205. package/src/services/task-planning-observed-events.ts +50 -52
  206. package/src/services/workspace-observed-events.ts +66 -63
  207. package/src/storage/storage-lifecycle-core.ts +438 -0
  208. package/src/store/control-plane-store-normalize.ts +33 -242
  209. package/src/store/control-plane-store-types.ts +1 -35
  210. package/src/store/control-plane-store.ts +396 -56
  211. package/src/store/event-store.ts +397 -3
  212. package/src/terminal/snapshot-oracle.ts +207 -94
  213. package/src/ui/mux-theme.ts +112 -8
  214. package/src/ui/panes/home-gridfire.ts +40 -31
  215. package/src/ui/panes/home.ts +10 -2
  216. package/src/ui/panes/nim.ts +315 -0
  217. package/src/mux/live-mux/actions-task.ts +0 -115
  218. package/src/mux/live-mux/left-rail-actions.ts +0 -118
  219. package/src/mux/live-mux/left-rail-conversation-click.ts +0 -82
  220. package/src/mux/live-mux/left-rail-pointer.ts +0 -74
  221. package/src/mux/live-mux/task-pane-shortcuts.ts +0 -206
  222. package/src/services/runtime-directory-actions.ts +0 -164
  223. package/src/services/runtime-input-pipeline.ts +0 -50
  224. package/src/services/runtime-input-router.ts +0 -189
  225. package/src/services/runtime-main-pane-input.ts +0 -230
  226. package/src/services/runtime-modal-input.ts +0 -119
  227. package/src/services/runtime-navigation-input.ts +0 -197
  228. package/src/services/runtime-rail-input.ts +0 -278
  229. package/src/services/runtime-task-pane.ts +0 -62
  230. package/src/services/runtime-workspace-actions.ts +0 -158
  231. package/src/ui/conversation-input-forwarder.ts +0 -114
  232. package/src/ui/conversation-selection-input.ts +0 -103
  233. package/src/ui/global-shortcut-input.ts +0 -89
  234. package/src/ui/input.ts +0 -238
  235. package/src/ui/kit.ts +0 -509
  236. package/src/ui/left-nav-input.ts +0 -80
  237. package/src/ui/left-rail-pointer-input.ts +0 -148
  238. package/src/ui/repository-fold-input.ts +0 -91
  239. 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
+ }