@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.
- package/README.md +31 -35
- package/package.json +31 -11
- package/packages/harness-ai/src/anthropic-protocol.ts +68 -68
- package/packages/harness-ai/src/stream-text.ts +13 -91
- package/packages/harness-ui/src/frame-primitives.ts +158 -0
- package/packages/harness-ui/src/index.ts +18 -0
- package/packages/harness-ui/src/interaction/conversation-input-forwarder.ts +221 -0
- package/packages/harness-ui/src/interaction/conversation-selection-input.ts +213 -0
- package/packages/harness-ui/src/interaction/global-shortcut-input.ts +172 -0
- package/{src/ui → packages/harness-ui/src/interaction}/input-preflight.ts +10 -12
- package/{src/ui → packages/harness-ui/src/interaction}/input-token-router.ts +120 -69
- package/packages/harness-ui/src/interaction/input.ts +420 -0
- package/packages/harness-ui/src/interaction/left-nav-input.ts +166 -0
- package/{src/ui → packages/harness-ui/src/interaction}/main-pane-pointer-input.ts +91 -23
- package/{src/ui → packages/harness-ui/src/interaction}/pointer-routing-input.ts +112 -48
- package/packages/harness-ui/src/interaction/rail-pointer-input.ts +62 -0
- package/packages/harness-ui/src/interaction/repository-fold-input.ts +118 -0
- package/packages/harness-ui/src/kit.ts +476 -0
- package/packages/harness-ui/src/layout.ts +238 -0
- package/{src/ui/modals/manager.ts → packages/harness-ui/src/modal-manager.ts} +94 -64
- package/{src/ui → packages/harness-ui/src}/screen.ts +53 -26
- package/packages/harness-ui/src/surface.ts +252 -0
- package/packages/harness-ui/src/text-layout.ts +210 -0
- package/packages/nim-core/src/contracts.ts +239 -0
- package/packages/nim-core/src/event-store.ts +299 -0
- package/packages/nim-core/src/events.ts +53 -0
- package/packages/nim-core/src/index.ts +9 -0
- package/packages/nim-core/src/provider-router.ts +129 -0
- package/packages/nim-core/src/providers/anthropic-driver.ts +291 -0
- package/packages/nim-core/src/runtime-factory.ts +49 -0
- package/packages/nim-core/src/runtime.ts +1797 -0
- package/packages/nim-core/src/session-store.ts +516 -0
- package/packages/nim-core/src/telemetry.ts +48 -0
- package/packages/nim-test-tui/src/index.ts +150 -0
- package/packages/nim-ui-core/src/index.ts +1 -0
- package/packages/nim-ui-core/src/projection.ts +87 -0
- package/scripts/codex-live-mux-runtime.ts +2 -3721
- package/scripts/control-plane-daemon.ts +24 -2
- package/scripts/harness-bin.js +5 -0
- package/scripts/harness-commands.ts +300 -0
- package/scripts/harness-runtime.ts +82 -0
- package/scripts/harness.ts +33 -3007
- package/scripts/nim-tui-smoke.ts +748 -0
- package/src/cli/auth/runtime.ts +948 -0
- package/src/cli/default-gateway-pointer.ts +193 -0
- package/src/cli/gateway/runtime.ts +1872 -0
- package/src/cli/parsing/flags.ts +23 -0
- package/src/cli/parsing/session.ts +42 -0
- package/src/cli/runtime/context.ts +193 -0
- package/src/cli/runtime-app/application.ts +392 -0
- package/src/cli/runtime-infra/gateway-control.ts +729 -0
- package/{scripts/harness-inspector.ts → src/cli/workflows/inspector.ts} +14 -11
- package/src/cli/workflows/runtime.ts +965 -0
- package/src/clients/tui/left-rail-interactions.ts +519 -0
- package/src/clients/tui/main-pane-interactions.ts +509 -0
- package/src/clients/tui/modal-input-routing.ts +71 -0
- package/src/clients/tui/render-snapshot-adapter.ts +88 -0
- package/src/clients/web/synced-selectors.ts +132 -0
- package/src/codex/live-session.ts +82 -29
- package/src/config/config-core.ts +361 -10
- package/src/config/harness-paths.ts +4 -7
- package/src/config/harness-runtime-migration.ts +142 -19
- package/src/config/harness.config.template.jsonc +33 -0
- package/src/config/secrets-core.ts +92 -4
- package/src/control-plane/agent-realtime-api.ts +82 -427
- package/src/control-plane/prompt/thread-title-namer.ts +49 -23
- package/src/control-plane/session-summary.ts +10 -81
- package/src/control-plane/status/reducer-base.ts +12 -12
- package/src/control-plane/status/reducers/claude-status-reducer.ts +3 -3
- package/src/control-plane/status/reducers/codex-status-reducer.ts +4 -4
- package/src/control-plane/status/reducers/cursor-status-reducer.ts +3 -3
- package/src/control-plane/stream-client.ts +12 -2
- package/src/control-plane/stream-command-parser.ts +83 -143
- package/src/control-plane/stream-protocol.ts +53 -37
- package/src/control-plane/stream-server-background.ts +18 -2
- package/src/control-plane/stream-server-command.ts +376 -69
- package/src/control-plane/stream-server-session-runtime.ts +3 -2
- package/src/control-plane/stream-server.ts +943 -80
- package/src/control-plane/stream-session-runtime-types.ts +41 -0
- package/src/{mux/live-mux/control-plane-records.ts → core/contracts/records.ts} +24 -97
- package/src/core/state/observed-stream-cursor.ts +43 -0
- package/src/core/state/synced-observed-state.ts +273 -0
- package/src/core/store/harness-synced-store.ts +81 -0
- package/src/diff/budget.ts +136 -0
- package/src/diff/build.ts +289 -0
- package/src/diff/chunker.ts +146 -0
- package/src/diff/git-invoke.ts +315 -0
- package/src/diff/git-parse.ts +472 -0
- package/src/diff/hash.ts +70 -0
- package/src/diff/index.ts +24 -0
- package/src/diff/normalize.ts +134 -0
- package/src/diff/types.ts +178 -0
- package/src/diff-ui/args.ts +346 -0
- package/src/diff-ui/commands.ts +123 -0
- package/src/diff-ui/finder.ts +94 -0
- package/src/diff-ui/highlight.ts +127 -0
- package/src/diff-ui/index.ts +2 -0
- package/src/diff-ui/model.ts +141 -0
- package/src/diff-ui/pager.ts +412 -0
- package/src/diff-ui/render.ts +337 -0
- package/src/diff-ui/runtime.ts +379 -0
- package/src/diff-ui/state.ts +224 -0
- package/src/diff-ui/types.ts +236 -0
- package/src/domain/conversations.ts +11 -7
- package/src/domain/workspace.ts +76 -4
- package/src/mux/control-plane-op-queue.ts +93 -7
- package/src/mux/conversation-rail.ts +28 -71
- package/src/mux/dual-pane-core.ts +13 -13
- package/src/mux/harness-core-ui.ts +313 -42
- package/src/mux/input-shortcuts.ts +22 -112
- package/src/mux/keybinding-catalog.ts +340 -0
- package/src/mux/keybinding-registry.ts +103 -0
- package/src/mux/live-mux/command-menu-open-in.ts +280 -0
- package/src/mux/live-mux/command-menu.ts +167 -4
- package/src/mux/live-mux/conversation-state.ts +13 -0
- package/src/mux/live-mux/directory-resolution.ts +1 -1
- package/src/mux/live-mux/git-parsing.ts +16 -0
- package/src/mux/live-mux/git-snapshot.ts +33 -2
- package/src/mux/live-mux/global-shortcut-handlers.ts +6 -0
- package/src/mux/live-mux/home-pane-drop.ts +1 -1
- package/src/mux/live-mux/home-pane-pointer.ts +10 -0
- package/src/mux/live-mux/input-forwarding.ts +59 -2
- package/src/mux/live-mux/left-nav-activation.ts +124 -7
- package/src/mux/live-mux/left-nav.ts +35 -0
- package/src/mux/live-mux/link-click.ts +292 -0
- package/src/mux/live-mux/modal-command-menu-handler.ts +46 -9
- package/src/mux/live-mux/modal-conversation-handlers.ts +5 -1
- package/src/mux/live-mux/modal-input-reducers.ts +106 -8
- package/src/mux/live-mux/modal-overlays.ts +210 -31
- package/src/mux/live-mux/modal-pointer.ts +3 -7
- package/src/mux/live-mux/modal-prompt-handlers.ts +107 -1
- package/src/mux/live-mux/modal-release-notes-handler.ts +111 -0
- package/src/mux/live-mux/modal-task-editor-handler.ts +16 -11
- package/src/mux/live-mux/pointer-routing.ts +5 -2
- package/src/mux/live-mux/project-pane-pointer.ts +8 -0
- package/src/mux/live-mux/rail-layout.ts +33 -30
- package/src/mux/live-mux/release-notes.ts +383 -0
- package/src/mux/live-mux/render-trace-analysis.ts +52 -7
- package/src/mux/live-mux/repository-folding.ts +3 -0
- package/src/mux/live-mux/selection.ts +0 -4
- package/src/mux/live-mux/session-diagnostics-paths.ts +21 -0
- package/src/mux/project-pane-github-review.ts +271 -0
- package/src/mux/render-frame.ts +4 -0
- package/src/mux/runtime-app/codex-live-mux-runtime.ts +5191 -0
- package/src/mux/task-composer.ts +21 -14
- package/src/mux/task-focused-pane.ts +118 -117
- package/src/mux/task-screen-keybindings.ts +19 -82
- package/src/mux/workspace-rail-model.ts +270 -104
- package/src/mux/workspace-rail.ts +45 -22
- package/src/pty/session-broker.ts +1 -1
- package/{scripts → src/recording}/terminal-recording-gif-lib.ts +2 -2
- package/src/services/control-plane.ts +50 -32
- package/src/services/conversation-lifecycle.ts +118 -87
- package/src/services/conversation-startup-hydration.ts +20 -12
- package/src/services/directory-hydration.ts +21 -16
- package/src/services/event-persistence.ts +7 -0
- package/src/services/left-rail-pointer-handler.ts +329 -0
- package/src/services/mux-ui-state-persistence.ts +5 -1
- package/src/services/recording.ts +34 -26
- package/src/services/runtime-command-menu-agent-tools.ts +1 -1
- package/src/services/runtime-control-actions.ts +79 -61
- package/src/services/runtime-control-plane-ops.ts +122 -83
- package/src/services/runtime-conversation-actions.ts +40 -26
- package/src/services/runtime-conversation-activation.ts +82 -30
- package/src/services/runtime-conversation-starter.ts +80 -48
- package/src/services/runtime-conversation-title-edit.ts +91 -80
- package/src/services/runtime-envelope-handler.ts +107 -105
- package/src/services/runtime-git-state.ts +42 -29
- package/src/services/runtime-layout-resize.ts +3 -1
- package/src/services/runtime-left-rail-render.ts +99 -63
- package/src/services/runtime-nim-cli-session.ts +438 -0
- package/src/services/runtime-nim-session.ts +705 -0
- package/src/services/runtime-nim-tool-bridge.ts +141 -0
- package/src/services/runtime-observed-event-projection-pipeline.ts +45 -0
- package/src/services/runtime-process-wiring.ts +29 -36
- package/src/services/runtime-project-pane-github-review-cache.ts +164 -0
- package/src/services/runtime-render-flush.ts +63 -70
- package/src/services/runtime-render-lifecycle.ts +65 -64
- package/src/services/runtime-render-orchestrator.ts +55 -45
- package/src/services/runtime-render-pipeline.ts +106 -103
- package/src/services/runtime-render-state.ts +62 -49
- package/src/services/runtime-repository-actions.ts +97 -70
- package/src/services/runtime-right-pane-render.ts +80 -53
- package/src/services/runtime-shutdown.ts +38 -35
- package/src/services/runtime-stream-subscriptions.ts +35 -27
- package/src/services/runtime-task-composer-persistence.ts +71 -59
- package/src/services/runtime-task-composer-snapshot.ts +14 -0
- package/src/services/runtime-task-editor-actions.ts +46 -29
- package/src/services/runtime-task-pane-actions.ts +220 -134
- package/src/services/runtime-task-pane-shortcuts.ts +323 -123
- package/src/services/runtime-workspace-observed-effect-queue.ts +25 -0
- package/src/services/runtime-workspace-observed-events.ts +33 -184
- package/src/services/runtime-workspace-observed-transition-policy.ts +228 -0
- package/src/services/session-diagnostics-store.ts +217 -0
- package/src/services/startup-background-resume.ts +26 -21
- package/src/services/startup-orchestrator.ts +16 -13
- package/src/services/startup-paint-tracker.ts +29 -21
- package/src/services/startup-persisted-conversation-queue.ts +19 -13
- package/src/services/startup-settled-gate.ts +25 -15
- package/src/services/startup-shutdown.ts +18 -22
- package/src/services/startup-state-hydration.ts +44 -34
- package/src/services/startup-visibility.ts +12 -4
- package/src/services/task-pane-selection-actions.ts +89 -72
- package/src/services/task-planning-hydration.ts +24 -18
- package/src/services/task-planning-observed-events.ts +50 -52
- package/src/services/workspace-observed-events.ts +66 -63
- package/src/storage/storage-lifecycle-core.ts +438 -0
- package/src/store/control-plane-store-normalize.ts +33 -242
- package/src/store/control-plane-store-types.ts +1 -35
- package/src/store/control-plane-store.ts +396 -56
- package/src/store/event-store.ts +397 -3
- package/src/terminal/snapshot-oracle.ts +207 -94
- package/src/ui/mux-theme.ts +112 -8
- package/src/ui/panes/home-gridfire.ts +40 -31
- package/src/ui/panes/home.ts +10 -2
- package/src/ui/panes/nim.ts +315 -0
- package/src/mux/live-mux/actions-task.ts +0 -115
- package/src/mux/live-mux/left-rail-actions.ts +0 -118
- package/src/mux/live-mux/left-rail-conversation-click.ts +0 -82
- package/src/mux/live-mux/left-rail-pointer.ts +0 -74
- package/src/mux/live-mux/task-pane-shortcuts.ts +0 -206
- package/src/services/runtime-directory-actions.ts +0 -164
- package/src/services/runtime-input-pipeline.ts +0 -50
- package/src/services/runtime-input-router.ts +0 -189
- package/src/services/runtime-main-pane-input.ts +0 -230
- package/src/services/runtime-modal-input.ts +0 -119
- package/src/services/runtime-navigation-input.ts +0 -197
- package/src/services/runtime-rail-input.ts +0 -278
- package/src/services/runtime-task-pane.ts +0 -62
- package/src/services/runtime-workspace-actions.ts +0 -158
- package/src/ui/conversation-input-forwarder.ts +0 -114
- package/src/ui/conversation-selection-input.ts +0 -103
- package/src/ui/global-shortcut-input.ts +0 -89
- package/src/ui/input.ts +0 -238
- package/src/ui/kit.ts +0 -509
- package/src/ui/left-nav-input.ts +0 -80
- package/src/ui/left-rail-pointer-input.ts +0 -148
- package/src/ui/repository-fold-input.ts +0 -91
- 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
|
+
}
|