@shardworks/nexus-core 0.1.19 → 0.1.21
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/dist/clockworks.d.ts +0 -20
- package/dist/clockworks.d.ts.map +1 -1
- package/dist/clockworks.js +104 -19
- package/dist/clockworks.js.map +1 -1
- package/dist/events.d.ts.map +1 -1
- package/dist/events.js +1 -0
- package/dist/events.js.map +1 -1
- package/dist/index.d.ts +5 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +5 -1
- package/dist/index.js.map +1 -1
- package/dist/manifest.d.ts +132 -0
- package/dist/manifest.d.ts.map +1 -0
- package/dist/manifest.js +321 -0
- package/dist/manifest.js.map +1 -0
- package/dist/migrate.d.ts +63 -0
- package/dist/migrate.d.ts.map +1 -0
- package/dist/migrate.js +141 -0
- package/dist/migrate.js.map +1 -0
- package/dist/session.d.ts +214 -0
- package/dist/session.d.ts.map +1 -0
- package/dist/session.js +349 -0
- package/dist/session.js.map +1 -0
- package/dist/worktree.d.ts +69 -0
- package/dist/worktree.d.ts.map +1 -0
- package/dist/worktree.js +116 -0
- package/dist/worktree.js.map +1 -0
- package/package.json +1 -1
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Session infrastructure — the unified session funnel.
|
|
3
|
+
*
|
|
4
|
+
* ALL sessions (interactive, commissioned, briefed) flow through
|
|
5
|
+
* `launchSession()`. This provides unified logging, events, metrics,
|
|
6
|
+
* workspace lifecycle, and session record persistence.
|
|
7
|
+
*
|
|
8
|
+
* Session providers (e.g. claude-code, claude-api) implement the
|
|
9
|
+
* `SessionProvider` interface and are registered at startup.
|
|
10
|
+
*/
|
|
11
|
+
import type { ManifestResult } from './manifest.ts';
|
|
12
|
+
/** What a session provider must implement. */
|
|
13
|
+
export interface SessionProvider {
|
|
14
|
+
/** Provider identifier (e.g. "claude-code", "claude-api", "bedrock"). */
|
|
15
|
+
name: string;
|
|
16
|
+
/** Launch a session and return when it completes. */
|
|
17
|
+
launch(options: SessionProviderLaunchOptions): Promise<SessionProviderResult>;
|
|
18
|
+
}
|
|
19
|
+
/** Options passed to the provider's launch() — provider-specific subset. */
|
|
20
|
+
export interface SessionProviderLaunchOptions {
|
|
21
|
+
/** Guild root path. */
|
|
22
|
+
home: string;
|
|
23
|
+
/** The manifest result — system prompt + resolved tools. */
|
|
24
|
+
manifest: ManifestResult;
|
|
25
|
+
/** The user-facing prompt (commission spec, consultation topic, brief). */
|
|
26
|
+
prompt: string | null;
|
|
27
|
+
/** Whether the session is interactive (human at keyboard) or autonomous. */
|
|
28
|
+
interactive: boolean;
|
|
29
|
+
/** Resolved working directory for the session. */
|
|
30
|
+
cwd: string;
|
|
31
|
+
/** Display name for tracking. */
|
|
32
|
+
name?: string;
|
|
33
|
+
/** Budget cap, if any. */
|
|
34
|
+
maxBudgetUsd?: number;
|
|
35
|
+
}
|
|
36
|
+
/** What comes back from the provider (before the funnel adds its own fields). */
|
|
37
|
+
export interface SessionProviderResult {
|
|
38
|
+
exitCode: number;
|
|
39
|
+
/** Provider-reported token usage, if available. */
|
|
40
|
+
tokenUsage?: {
|
|
41
|
+
inputTokens: number;
|
|
42
|
+
outputTokens: number;
|
|
43
|
+
cacheReadTokens?: number;
|
|
44
|
+
cacheWriteTokens?: number;
|
|
45
|
+
};
|
|
46
|
+
/** Provider-reported cost in USD, if available. */
|
|
47
|
+
costUsd?: number;
|
|
48
|
+
/** Wall-clock duration in milliseconds. */
|
|
49
|
+
durationMs: number;
|
|
50
|
+
/** Session ID from the provider, if available (e.g. claude session ID). */
|
|
51
|
+
providerSessionId?: string;
|
|
52
|
+
/**
|
|
53
|
+
* Full conversation transcript — raw provider output, minimally typed.
|
|
54
|
+
* Stored as-is in the session record; typed normalization deferred.
|
|
55
|
+
*/
|
|
56
|
+
transcript?: Record<string, unknown>[];
|
|
57
|
+
}
|
|
58
|
+
/** Everything needed to launch a session through the funnel. */
|
|
59
|
+
export interface SessionLaunchOptions {
|
|
60
|
+
/** Guild root path. */
|
|
61
|
+
home: string;
|
|
62
|
+
/** The manifest result — system prompt + resolved tools. */
|
|
63
|
+
manifest: ManifestResult;
|
|
64
|
+
/** The user-facing prompt (commission spec, consultation topic, brief). */
|
|
65
|
+
prompt: string | null;
|
|
66
|
+
/** Whether the session is interactive (human at keyboard) or autonomous. */
|
|
67
|
+
interactive: boolean;
|
|
68
|
+
/** Workspace context. */
|
|
69
|
+
workspace: ResolvedWorkspace;
|
|
70
|
+
/** What triggered this session. */
|
|
71
|
+
trigger: 'consult' | 'summon' | 'brief';
|
|
72
|
+
/** Display name for tracking. */
|
|
73
|
+
name?: string;
|
|
74
|
+
/** Budget cap, if any. */
|
|
75
|
+
maxBudgetUsd?: number;
|
|
76
|
+
}
|
|
77
|
+
/** What the funnel returns to callers. */
|
|
78
|
+
export interface SessionResult {
|
|
79
|
+
/** Ledger row ID — written by the funnel before provider launch. */
|
|
80
|
+
sessionId: number;
|
|
81
|
+
exitCode: number;
|
|
82
|
+
/** Provider-reported token usage, if available. */
|
|
83
|
+
tokenUsage?: {
|
|
84
|
+
inputTokens: number;
|
|
85
|
+
outputTokens: number;
|
|
86
|
+
cacheReadTokens?: number;
|
|
87
|
+
cacheWriteTokens?: number;
|
|
88
|
+
};
|
|
89
|
+
/** Provider-reported cost in USD, if available. */
|
|
90
|
+
costUsd?: number;
|
|
91
|
+
/** Wall-clock duration in milliseconds. */
|
|
92
|
+
durationMs: number;
|
|
93
|
+
/** Session ID from the provider, if available. */
|
|
94
|
+
providerSessionId?: string;
|
|
95
|
+
/** Raw transcript from the provider. */
|
|
96
|
+
transcript?: Record<string, unknown>[];
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* Standard optional fields on event payloads. Any event can carry these.
|
|
100
|
+
* The session launcher inspects the triggering event's payload and uses
|
|
101
|
+
* them to determine the working directory for the session.
|
|
102
|
+
*/
|
|
103
|
+
export interface WorkspaceContext {
|
|
104
|
+
/** Workshop name — session gets a fresh temporary worktree of main. */
|
|
105
|
+
workshop?: string;
|
|
106
|
+
/** Explicit worktree path — used as-is (caller owns the lifecycle). */
|
|
107
|
+
worktreePath?: string;
|
|
108
|
+
}
|
|
109
|
+
/** The resolved working directory for a session. */
|
|
110
|
+
export type ResolvedWorkspace = {
|
|
111
|
+
kind: 'guildhall';
|
|
112
|
+
} | {
|
|
113
|
+
kind: 'workshop-temp';
|
|
114
|
+
workshop: string;
|
|
115
|
+
worktreePath: string;
|
|
116
|
+
} | {
|
|
117
|
+
kind: 'workshop-managed';
|
|
118
|
+
workshop: string;
|
|
119
|
+
worktreePath: string;
|
|
120
|
+
};
|
|
121
|
+
/** Full session record written to disk as JSON. */
|
|
122
|
+
export interface SessionRecord {
|
|
123
|
+
/** Ledger session row ID (for cross-reference). */
|
|
124
|
+
sessionId: number;
|
|
125
|
+
/** The anima that ran this session, with full composition provenance. */
|
|
126
|
+
anima: {
|
|
127
|
+
id: number;
|
|
128
|
+
name: string;
|
|
129
|
+
roles: string[];
|
|
130
|
+
codex: string;
|
|
131
|
+
roleInstructions: string;
|
|
132
|
+
curriculum: {
|
|
133
|
+
name: string;
|
|
134
|
+
version: string;
|
|
135
|
+
content: string;
|
|
136
|
+
} | null;
|
|
137
|
+
temperament: {
|
|
138
|
+
name: string;
|
|
139
|
+
version: string;
|
|
140
|
+
content: string;
|
|
141
|
+
} | null;
|
|
142
|
+
toolInstructions: Array<{
|
|
143
|
+
toolName: string;
|
|
144
|
+
instructions: string;
|
|
145
|
+
}>;
|
|
146
|
+
};
|
|
147
|
+
/** The final assembled system prompt. */
|
|
148
|
+
systemPrompt: string;
|
|
149
|
+
/** Tools available to the anima. */
|
|
150
|
+
tools: Array<{
|
|
151
|
+
name: string;
|
|
152
|
+
}>;
|
|
153
|
+
/** Tools that were resolved but failed preconditions. */
|
|
154
|
+
unavailableTools: Array<{
|
|
155
|
+
name: string;
|
|
156
|
+
reasons: string[];
|
|
157
|
+
}>;
|
|
158
|
+
/** The user-facing prompt. */
|
|
159
|
+
userPrompt: string | null;
|
|
160
|
+
/** Raw conversation transcript from the provider. */
|
|
161
|
+
transcript: Record<string, unknown>[];
|
|
162
|
+
}
|
|
163
|
+
/**
|
|
164
|
+
* Register a session provider. Called once at startup.
|
|
165
|
+
*/
|
|
166
|
+
export declare function registerSessionProvider(provider: SessionProvider): void;
|
|
167
|
+
/** Get the registered session provider. */
|
|
168
|
+
export declare function getSessionProvider(): SessionProvider | null;
|
|
169
|
+
/**
|
|
170
|
+
* Resolve workspace context from an event payload's standard fields.
|
|
171
|
+
*/
|
|
172
|
+
export declare function resolveWorkspace(payload: Record<string, unknown> | null): ResolvedWorkspace;
|
|
173
|
+
/**
|
|
174
|
+
* Create a temporary worktree from a workshop's bare repo, checked out to main.
|
|
175
|
+
*
|
|
176
|
+
* Uses a crypto-safe random hash for the directory name. The worktree is
|
|
177
|
+
* a fresh snapshot — no branch management, no merge-back lifecycle.
|
|
178
|
+
*
|
|
179
|
+
* @returns Absolute path to the worktree directory.
|
|
180
|
+
*/
|
|
181
|
+
export declare function createTempWorktree(home: string, workshop: string): string;
|
|
182
|
+
/**
|
|
183
|
+
* Remove a temporary worktree.
|
|
184
|
+
*
|
|
185
|
+
* Logs but does not throw on failure — stale worktrees are assumed to be
|
|
186
|
+
* reaped by a separate mechanism.
|
|
187
|
+
*/
|
|
188
|
+
export declare function removeTempWorktree(home: string, workshop: string, worktreePath: string): void;
|
|
189
|
+
/**
|
|
190
|
+
* Launch a session through the registered provider.
|
|
191
|
+
*
|
|
192
|
+
* This is THE code path for all sessions. It:
|
|
193
|
+
* 1. If workspace is workshop-temp: create fresh worktree from main
|
|
194
|
+
* 2. Records session.started in the Ledger → gets sessionId
|
|
195
|
+
* 3. Signals session.started event
|
|
196
|
+
* 4. Delegates to the provider (passing resolved cwd)
|
|
197
|
+
* 5. Records session.ended in the Ledger (with metrics)
|
|
198
|
+
* 6. Writes the SessionRecord JSON to .nexus/sessions/{uuid}.json
|
|
199
|
+
* 7. Signals session.ended event (with full metrics + sessionId in payload)
|
|
200
|
+
* 8. If workspace is workshop-temp AND session is autonomous: tear down the worktree
|
|
201
|
+
* (interactive sessions leave the worktree for manual cleanup)
|
|
202
|
+
* 9. Returns the result (including sessionId)
|
|
203
|
+
*
|
|
204
|
+
* Error handling guarantee: Steps 5–8 MUST execute even if the provider
|
|
205
|
+
* throws. The funnel wraps step 4 in try/finally. If the provider crashes,
|
|
206
|
+
* the session row still gets ended_at, exit_code, and the session.ended
|
|
207
|
+
* event still fires (with error details in the payload). If the funnel
|
|
208
|
+
* itself fails during recording (e.g. Ledger locked), it signals
|
|
209
|
+
* session.record-failed as a core event and continues with remaining
|
|
210
|
+
* cleanup steps. Worktree teardown failures are logged but do not throw —
|
|
211
|
+
* stale worktrees are assumed to be reaped by a separate mechanism.
|
|
212
|
+
*/
|
|
213
|
+
export declare function launchSession(options: SessionLaunchOptions): Promise<SessionResult>;
|
|
214
|
+
//# sourceMappingURL=session.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"session.d.ts","sourceRoot":"","sources":["../src/session.ts"],"names":[],"mappings":"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"}
|
package/dist/session.js
ADDED
|
@@ -0,0 +1,349 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Session infrastructure — the unified session funnel.
|
|
3
|
+
*
|
|
4
|
+
* ALL sessions (interactive, commissioned, briefed) flow through
|
|
5
|
+
* `launchSession()`. This provides unified logging, events, metrics,
|
|
6
|
+
* workspace lifecycle, and session record persistence.
|
|
7
|
+
*
|
|
8
|
+
* Session providers (e.g. claude-code, claude-api) implement the
|
|
9
|
+
* `SessionProvider` interface and are registered at startup.
|
|
10
|
+
*/
|
|
11
|
+
import crypto from 'node:crypto';
|
|
12
|
+
import { execFileSync } from 'node:child_process';
|
|
13
|
+
import fs from 'node:fs';
|
|
14
|
+
import path from 'node:path';
|
|
15
|
+
import Database from 'better-sqlite3';
|
|
16
|
+
import { ledgerPath, nexusDir, workshopBarePath } from "./nexus-home.js";
|
|
17
|
+
import { signalEvent } from "./events.js";
|
|
18
|
+
// ── Provider Registration ──────────────────────────────────────────────
|
|
19
|
+
let _provider = null;
|
|
20
|
+
/**
|
|
21
|
+
* Register a session provider. Called once at startup.
|
|
22
|
+
*/
|
|
23
|
+
export function registerSessionProvider(provider) {
|
|
24
|
+
_provider = provider;
|
|
25
|
+
}
|
|
26
|
+
/** Get the registered session provider. */
|
|
27
|
+
export function getSessionProvider() {
|
|
28
|
+
return _provider;
|
|
29
|
+
}
|
|
30
|
+
// ── Workspace Helpers ──────────────────────────────────────────────────
|
|
31
|
+
/**
|
|
32
|
+
* Resolve workspace context from an event payload's standard fields.
|
|
33
|
+
*/
|
|
34
|
+
export function resolveWorkspace(payload) {
|
|
35
|
+
if (!payload)
|
|
36
|
+
return { kind: 'guildhall' };
|
|
37
|
+
const worktreePath = payload.worktreePath;
|
|
38
|
+
const workshop = payload.workshop;
|
|
39
|
+
if (worktreePath && workshop) {
|
|
40
|
+
return { kind: 'workshop-managed', workshop, worktreePath };
|
|
41
|
+
}
|
|
42
|
+
if (workshop) {
|
|
43
|
+
// workshop-temp: worktreePath gets set by createTempWorktree before launch
|
|
44
|
+
// We return a placeholder — the funnel fills in the actual path
|
|
45
|
+
return { kind: 'workshop-temp', workshop, worktreePath: '' };
|
|
46
|
+
}
|
|
47
|
+
return { kind: 'guildhall' };
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Create a temporary worktree from a workshop's bare repo, checked out to main.
|
|
51
|
+
*
|
|
52
|
+
* Uses a crypto-safe random hash for the directory name. The worktree is
|
|
53
|
+
* a fresh snapshot — no branch management, no merge-back lifecycle.
|
|
54
|
+
*
|
|
55
|
+
* @returns Absolute path to the worktree directory.
|
|
56
|
+
*/
|
|
57
|
+
export function createTempWorktree(home, workshop) {
|
|
58
|
+
const hash = crypto.randomBytes(8).toString('hex');
|
|
59
|
+
const worktreeDir = path.join(nexusDir(home), 'worktrees', workshop, hash);
|
|
60
|
+
const barePath = workshopBarePath(home, workshop);
|
|
61
|
+
if (!fs.existsSync(barePath)) {
|
|
62
|
+
throw new Error(`Workshop "${workshop}" bare repo not found at ${barePath}. ` +
|
|
63
|
+
`Has the workshop been added with 'nsg workshop add'?`);
|
|
64
|
+
}
|
|
65
|
+
fs.mkdirSync(path.dirname(worktreeDir), { recursive: true });
|
|
66
|
+
execFileSync('git', ['worktree', 'add', '--detach', worktreeDir, 'main'], {
|
|
67
|
+
cwd: barePath,
|
|
68
|
+
stdio: 'pipe',
|
|
69
|
+
});
|
|
70
|
+
return worktreeDir;
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Remove a temporary worktree.
|
|
74
|
+
*
|
|
75
|
+
* Logs but does not throw on failure — stale worktrees are assumed to be
|
|
76
|
+
* reaped by a separate mechanism.
|
|
77
|
+
*/
|
|
78
|
+
export function removeTempWorktree(home, workshop, worktreePath) {
|
|
79
|
+
const barePath = workshopBarePath(home, workshop);
|
|
80
|
+
try {
|
|
81
|
+
execFileSync('git', ['worktree', 'remove', '--force', worktreePath], {
|
|
82
|
+
cwd: barePath,
|
|
83
|
+
stdio: 'pipe',
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
catch (err) {
|
|
87
|
+
console.error(`[session] Warning: failed to remove temp worktree ${worktreePath}: ` +
|
|
88
|
+
`${err instanceof Error ? err.message : String(err)}`);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
// ── Session Record Helpers ─────────────────────────────────────────────
|
|
92
|
+
/** Path to the sessions directory. */
|
|
93
|
+
function sessionsDir(home) {
|
|
94
|
+
return path.join(nexusDir(home), 'sessions');
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* Build a SessionRecord from the manifest and provider result.
|
|
98
|
+
*/
|
|
99
|
+
function buildSessionRecord(sessionId, manifest, prompt, providerResult) {
|
|
100
|
+
return {
|
|
101
|
+
sessionId,
|
|
102
|
+
anima: {
|
|
103
|
+
id: manifest.anima.id,
|
|
104
|
+
name: manifest.anima.name,
|
|
105
|
+
roles: manifest.anima.roles,
|
|
106
|
+
codex: manifest.composition.codex,
|
|
107
|
+
roleInstructions: manifest.composition.roleInstructions,
|
|
108
|
+
curriculum: manifest.composition.curriculum,
|
|
109
|
+
temperament: manifest.composition.temperament,
|
|
110
|
+
toolInstructions: manifest.composition.toolInstructions,
|
|
111
|
+
},
|
|
112
|
+
systemPrompt: manifest.systemPrompt,
|
|
113
|
+
tools: manifest.tools.map(t => ({ name: t.name })),
|
|
114
|
+
unavailableTools: manifest.unavailable,
|
|
115
|
+
userPrompt: prompt,
|
|
116
|
+
transcript: providerResult?.transcript ?? [],
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* Write a session record to disk.
|
|
121
|
+
* @returns The relative record_path (relative to guild root).
|
|
122
|
+
*/
|
|
123
|
+
function writeSessionRecord(home, record) {
|
|
124
|
+
const dir = sessionsDir(home);
|
|
125
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
126
|
+
const uuid = crypto.randomUUID();
|
|
127
|
+
const filename = `${uuid}.json`;
|
|
128
|
+
const fullPath = path.join(dir, filename);
|
|
129
|
+
const relativePath = path.relative(home, fullPath);
|
|
130
|
+
fs.writeFileSync(fullPath, JSON.stringify(record, null, 2));
|
|
131
|
+
return relativePath;
|
|
132
|
+
}
|
|
133
|
+
// ── Ledger Helpers ─────────────────────────────────────────────────────
|
|
134
|
+
/**
|
|
135
|
+
* Insert a session.started row in the Ledger.
|
|
136
|
+
* @returns The session row ID.
|
|
137
|
+
*/
|
|
138
|
+
function insertSessionRow(home, opts) {
|
|
139
|
+
const db = new Database(ledgerPath(home));
|
|
140
|
+
db.pragma('foreign_keys = ON');
|
|
141
|
+
try {
|
|
142
|
+
const result = db.prepare(`INSERT INTO sessions (anima_id, provider, trigger, workshop, workspace_kind,
|
|
143
|
+
curriculum_name, curriculum_version, temperament_name, temperament_version,
|
|
144
|
+
roles, started_at)
|
|
145
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`).run(opts.animaId, opts.provider, opts.trigger, opts.workshop, opts.workspaceKind, opts.curriculumName, opts.curriculumVersion, opts.temperamentName, opts.temperamentVersion, JSON.stringify(opts.roles), opts.startedAt);
|
|
146
|
+
return Number(result.lastInsertRowid);
|
|
147
|
+
}
|
|
148
|
+
finally {
|
|
149
|
+
db.close();
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
/**
|
|
153
|
+
* Update a session row with end-of-session data.
|
|
154
|
+
*/
|
|
155
|
+
function updateSessionRow(home, sessionId, opts) {
|
|
156
|
+
const db = new Database(ledgerPath(home));
|
|
157
|
+
db.pragma('foreign_keys = ON');
|
|
158
|
+
try {
|
|
159
|
+
db.prepare(`UPDATE sessions SET
|
|
160
|
+
ended_at = ?, exit_code = ?,
|
|
161
|
+
input_tokens = ?, output_tokens = ?,
|
|
162
|
+
cache_read_tokens = ?, cache_write_tokens = ?,
|
|
163
|
+
cost_usd = ?, duration_ms = ?,
|
|
164
|
+
provider_session_id = ?, record_path = ?
|
|
165
|
+
WHERE id = ?`).run(opts.endedAt, opts.exitCode, opts.inputTokens ?? null, opts.outputTokens ?? null, opts.cacheReadTokens ?? null, opts.cacheWriteTokens ?? null, opts.costUsd ?? null, opts.durationMs ?? null, opts.providerSessionId ?? null, opts.recordPath ?? null, sessionId);
|
|
166
|
+
}
|
|
167
|
+
finally {
|
|
168
|
+
db.close();
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
// ── The Session Funnel ─────────────────────────────────────────────────
|
|
172
|
+
/**
|
|
173
|
+
* Launch a session through the registered provider.
|
|
174
|
+
*
|
|
175
|
+
* This is THE code path for all sessions. It:
|
|
176
|
+
* 1. If workspace is workshop-temp: create fresh worktree from main
|
|
177
|
+
* 2. Records session.started in the Ledger → gets sessionId
|
|
178
|
+
* 3. Signals session.started event
|
|
179
|
+
* 4. Delegates to the provider (passing resolved cwd)
|
|
180
|
+
* 5. Records session.ended in the Ledger (with metrics)
|
|
181
|
+
* 6. Writes the SessionRecord JSON to .nexus/sessions/{uuid}.json
|
|
182
|
+
* 7. Signals session.ended event (with full metrics + sessionId in payload)
|
|
183
|
+
* 8. If workspace is workshop-temp AND session is autonomous: tear down the worktree
|
|
184
|
+
* (interactive sessions leave the worktree for manual cleanup)
|
|
185
|
+
* 9. Returns the result (including sessionId)
|
|
186
|
+
*
|
|
187
|
+
* Error handling guarantee: Steps 5–8 MUST execute even if the provider
|
|
188
|
+
* throws. The funnel wraps step 4 in try/finally. If the provider crashes,
|
|
189
|
+
* the session row still gets ended_at, exit_code, and the session.ended
|
|
190
|
+
* event still fires (with error details in the payload). If the funnel
|
|
191
|
+
* itself fails during recording (e.g. Ledger locked), it signals
|
|
192
|
+
* session.record-failed as a core event and continues with remaining
|
|
193
|
+
* cleanup steps. Worktree teardown failures are logged but do not throw —
|
|
194
|
+
* stale worktrees are assumed to be reaped by a separate mechanism.
|
|
195
|
+
*/
|
|
196
|
+
export async function launchSession(options) {
|
|
197
|
+
if (!_provider) {
|
|
198
|
+
throw new Error('No session provider registered. Call registerSessionProvider() at startup.');
|
|
199
|
+
}
|
|
200
|
+
const { home, manifest, prompt, interactive, trigger, name, maxBudgetUsd } = options;
|
|
201
|
+
let { workspace } = options;
|
|
202
|
+
// Step 1: If workshop-temp, create fresh worktree
|
|
203
|
+
if (workspace.kind === 'workshop-temp') {
|
|
204
|
+
const worktreePath = createTempWorktree(home, workspace.workshop);
|
|
205
|
+
workspace = { ...workspace, worktreePath };
|
|
206
|
+
}
|
|
207
|
+
// Resolve cwd from workspace
|
|
208
|
+
const cwd = workspace.kind === 'guildhall'
|
|
209
|
+
? home
|
|
210
|
+
: workspace.worktreePath;
|
|
211
|
+
const startedAt = new Date().toISOString();
|
|
212
|
+
// Derive workshop name from workspace
|
|
213
|
+
const workshopName = workspace.kind === 'guildhall' ? null : workspace.workshop;
|
|
214
|
+
// Step 2: Record session.started in the Ledger
|
|
215
|
+
let sessionId;
|
|
216
|
+
try {
|
|
217
|
+
sessionId = insertSessionRow(home, {
|
|
218
|
+
animaId: manifest.anima.id,
|
|
219
|
+
provider: _provider.name,
|
|
220
|
+
trigger,
|
|
221
|
+
workshop: workshopName,
|
|
222
|
+
workspaceKind: workspace.kind,
|
|
223
|
+
curriculumName: manifest.composition.curriculum?.name ?? null,
|
|
224
|
+
curriculumVersion: manifest.composition.curriculum?.version ?? null,
|
|
225
|
+
temperamentName: manifest.composition.temperament?.name ?? null,
|
|
226
|
+
temperamentVersion: manifest.composition.temperament?.version ?? null,
|
|
227
|
+
roles: manifest.anima.roles,
|
|
228
|
+
startedAt,
|
|
229
|
+
});
|
|
230
|
+
}
|
|
231
|
+
catch (err) {
|
|
232
|
+
// If we can't even write the session row, signal failure and abort
|
|
233
|
+
try {
|
|
234
|
+
signalEvent(home, 'session.record-failed', {
|
|
235
|
+
error: err instanceof Error ? err.message : String(err),
|
|
236
|
+
phase: 'insert',
|
|
237
|
+
anima: manifest.anima.name,
|
|
238
|
+
}, 'framework');
|
|
239
|
+
}
|
|
240
|
+
catch { /* swallow — best effort */ }
|
|
241
|
+
throw err;
|
|
242
|
+
}
|
|
243
|
+
// Step 3: Signal session.started event
|
|
244
|
+
try {
|
|
245
|
+
signalEvent(home, 'session.started', {
|
|
246
|
+
sessionId,
|
|
247
|
+
anima: manifest.anima.name,
|
|
248
|
+
trigger,
|
|
249
|
+
workshop: workshopName,
|
|
250
|
+
workspaceKind: workspace.kind,
|
|
251
|
+
}, 'framework');
|
|
252
|
+
}
|
|
253
|
+
catch { /* swallow — event signalling is best-effort */ }
|
|
254
|
+
// Step 4: Delegate to the provider (wrapped in try/finally for guarantees)
|
|
255
|
+
let providerResult = null;
|
|
256
|
+
let providerError = null;
|
|
257
|
+
try {
|
|
258
|
+
providerResult = await _provider.launch({
|
|
259
|
+
home,
|
|
260
|
+
manifest,
|
|
261
|
+
prompt,
|
|
262
|
+
interactive,
|
|
263
|
+
cwd,
|
|
264
|
+
name,
|
|
265
|
+
maxBudgetUsd,
|
|
266
|
+
});
|
|
267
|
+
}
|
|
268
|
+
catch (err) {
|
|
269
|
+
providerError = err instanceof Error ? err : new Error(String(err));
|
|
270
|
+
}
|
|
271
|
+
// Steps 5–8: Always execute, even if the provider threw
|
|
272
|
+
const endedAt = new Date().toISOString();
|
|
273
|
+
const exitCode = providerResult?.exitCode ?? 1;
|
|
274
|
+
const durationMs = providerResult?.durationMs ?? (Date.now() - new Date(startedAt).getTime());
|
|
275
|
+
// Step 5: Update session row in Ledger
|
|
276
|
+
let recordPath;
|
|
277
|
+
try {
|
|
278
|
+
// Step 6: Write SessionRecord JSON
|
|
279
|
+
const record = buildSessionRecord(sessionId, manifest, prompt, providerResult);
|
|
280
|
+
recordPath = writeSessionRecord(home, record);
|
|
281
|
+
}
|
|
282
|
+
catch (err) {
|
|
283
|
+
try {
|
|
284
|
+
signalEvent(home, 'session.record-failed', {
|
|
285
|
+
sessionId,
|
|
286
|
+
error: err instanceof Error ? err.message : String(err),
|
|
287
|
+
phase: 'write-record',
|
|
288
|
+
}, 'framework');
|
|
289
|
+
}
|
|
290
|
+
catch { /* swallow */ }
|
|
291
|
+
}
|
|
292
|
+
try {
|
|
293
|
+
updateSessionRow(home, sessionId, {
|
|
294
|
+
endedAt,
|
|
295
|
+
exitCode,
|
|
296
|
+
inputTokens: providerResult?.tokenUsage?.inputTokens,
|
|
297
|
+
outputTokens: providerResult?.tokenUsage?.outputTokens,
|
|
298
|
+
cacheReadTokens: providerResult?.tokenUsage?.cacheReadTokens,
|
|
299
|
+
cacheWriteTokens: providerResult?.tokenUsage?.cacheWriteTokens,
|
|
300
|
+
costUsd: providerResult?.costUsd,
|
|
301
|
+
durationMs,
|
|
302
|
+
providerSessionId: providerResult?.providerSessionId,
|
|
303
|
+
recordPath,
|
|
304
|
+
});
|
|
305
|
+
}
|
|
306
|
+
catch (err) {
|
|
307
|
+
try {
|
|
308
|
+
signalEvent(home, 'session.record-failed', {
|
|
309
|
+
sessionId,
|
|
310
|
+
error: err instanceof Error ? err.message : String(err),
|
|
311
|
+
phase: 'update-row',
|
|
312
|
+
}, 'framework');
|
|
313
|
+
}
|
|
314
|
+
catch { /* swallow */ }
|
|
315
|
+
}
|
|
316
|
+
// Step 7: Signal session.ended event
|
|
317
|
+
try {
|
|
318
|
+
signalEvent(home, 'session.ended', {
|
|
319
|
+
sessionId,
|
|
320
|
+
anima: manifest.anima.name,
|
|
321
|
+
trigger,
|
|
322
|
+
workshop: workshopName,
|
|
323
|
+
exitCode,
|
|
324
|
+
durationMs,
|
|
325
|
+
costUsd: providerResult?.costUsd ?? null,
|
|
326
|
+
error: providerError?.message ?? null,
|
|
327
|
+
}, 'framework');
|
|
328
|
+
}
|
|
329
|
+
catch { /* swallow */ }
|
|
330
|
+
// Step 8: Teardown temp worktree (autonomous only)
|
|
331
|
+
if (workspace.kind === 'workshop-temp' && !interactive) {
|
|
332
|
+
removeTempWorktree(home, workspace.workshop, workspace.worktreePath);
|
|
333
|
+
}
|
|
334
|
+
// Step 9: Return result
|
|
335
|
+
if (providerError && !providerResult) {
|
|
336
|
+
// Provider threw without returning — re-throw so caller knows
|
|
337
|
+
throw providerError;
|
|
338
|
+
}
|
|
339
|
+
return {
|
|
340
|
+
sessionId,
|
|
341
|
+
exitCode,
|
|
342
|
+
tokenUsage: providerResult?.tokenUsage,
|
|
343
|
+
costUsd: providerResult?.costUsd,
|
|
344
|
+
durationMs,
|
|
345
|
+
providerSessionId: providerResult?.providerSessionId,
|
|
346
|
+
transcript: providerResult?.transcript,
|
|
347
|
+
};
|
|
348
|
+
}
|
|
349
|
+
//# sourceMappingURL=session.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"session.js","sourceRoot":"","sources":["../src/session.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Commission worktree management — creates isolated git worktrees for
|
|
3
|
+
* commission sessions.
|
|
4
|
+
*
|
|
5
|
+
* When an anima is dispatched to work on a commission, this module:
|
|
6
|
+
* 1. Creates a new git branch for the commission
|
|
7
|
+
* 2. Sets up a git worktree pointing at that branch
|
|
8
|
+
* 3. Returns the worktree path so the session can launch in the correct cwd
|
|
9
|
+
*
|
|
10
|
+
* Worktrees provide isolation — each commission gets its own working copy
|
|
11
|
+
* of the repo, so multiple animas can work concurrently without conflicts
|
|
12
|
+
* in the working directory.
|
|
13
|
+
*
|
|
14
|
+
* Absorbed from the former `engine-worktree-setup` package.
|
|
15
|
+
*
|
|
16
|
+
* ## Directory layout
|
|
17
|
+
*
|
|
18
|
+
* Commission worktrees are created from workshop bare clones:
|
|
19
|
+
*
|
|
20
|
+
* GUILD_ROOT/
|
|
21
|
+
* .nexus/
|
|
22
|
+
* workshops/
|
|
23
|
+
* workshop-a.git/ ← bare clone (source for worktrees)
|
|
24
|
+
* worktrees/
|
|
25
|
+
* workshop-a/
|
|
26
|
+
* commission-42/ ← worktree for commission #42
|
|
27
|
+
* workshop-b/
|
|
28
|
+
* commission-17/ ← worktree for commission #17
|
|
29
|
+
*/
|
|
30
|
+
export interface WorktreeConfig {
|
|
31
|
+
/** Absolute path to the guild root. */
|
|
32
|
+
home: string;
|
|
33
|
+
/** Workshop name — the bare clone source for the worktree. */
|
|
34
|
+
workshop: string;
|
|
35
|
+
/** Commission ID — used to derive branch name and worktree directory. */
|
|
36
|
+
commissionId: number;
|
|
37
|
+
/** Base branch to create the worktree from (default: 'main'). */
|
|
38
|
+
baseBranch?: string;
|
|
39
|
+
}
|
|
40
|
+
export interface WorktreeResult {
|
|
41
|
+
/** Absolute path to the created worktree. */
|
|
42
|
+
path: string;
|
|
43
|
+
/** Branch name created for the worktree. */
|
|
44
|
+
branch: string;
|
|
45
|
+
/** Commission ID this worktree serves. */
|
|
46
|
+
commissionId: number;
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Create a git worktree for a commission session.
|
|
50
|
+
*
|
|
51
|
+
* Creates a new branch from the base branch and sets up a worktree in
|
|
52
|
+
* .nexus/worktrees/{workshop}/commission-{id}/.
|
|
53
|
+
*
|
|
54
|
+
* @throws If the worktree or branch already exists.
|
|
55
|
+
*/
|
|
56
|
+
export declare function setupWorktree(config: WorktreeConfig): WorktreeResult;
|
|
57
|
+
/**
|
|
58
|
+
* Remove a worktree after a commission session completes.
|
|
59
|
+
*
|
|
60
|
+
* Removes the worktree directory and prunes it from git's tracking.
|
|
61
|
+
* Does NOT delete the branch — the branch should be kept for history
|
|
62
|
+
* until explicitly merged or pruned.
|
|
63
|
+
*/
|
|
64
|
+
export declare function teardownWorktree(home: string, workshop: string, commissionId: number): void;
|
|
65
|
+
/**
|
|
66
|
+
* List active commission worktrees.
|
|
67
|
+
*/
|
|
68
|
+
export declare function listWorktrees(home: string, workshop?: string): WorktreeResult[];
|
|
69
|
+
//# sourceMappingURL=worktree.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"worktree.d.ts","sourceRoot":"","sources":["../src/worktree.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AAOH,MAAM,WAAW,cAAc;IAC7B,uCAAuC;IACvC,IAAI,EAAE,MAAM,CAAC;IACb,8DAA8D;IAC9D,QAAQ,EAAE,MAAM,CAAC;IACjB,yEAAyE;IACzE,YAAY,EAAE,MAAM,CAAC;IACrB,iEAAiE;IACjE,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,cAAc;IAC7B,6CAA6C;IAC7C,IAAI,EAAE,MAAM,CAAC;IACb,4CAA4C;IAC5C,MAAM,EAAE,MAAM,CAAC;IACf,0CAA0C;IAC1C,YAAY,EAAE,MAAM,CAAC;CACtB;AAMD;;;;;;;GAOG;AACH,wBAAgB,aAAa,CAAC,MAAM,EAAE,cAAc,GAAG,cAAc,CAqBpE;AAED;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,GAAG,IAAI,CAmB3F;AAED;;GAEG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,cAAc,EAAE,CAiC/E"}
|