@ravefox/space 0.2.1 → 0.3.0

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 (163) hide show
  1. package/LICENSE +21 -205
  2. package/README.md +80 -31
  3. package/bin/space.js +106 -0
  4. package/lib/agents.js +119 -0
  5. package/lib/credentials.js +95 -0
  6. package/lib/daemon.js +294 -0
  7. package/lib/harness.js +166 -0
  8. package/lib/http.js +101 -0
  9. package/lib/login.js +68 -0
  10. package/lib/mcp.js +34 -0
  11. package/lib/runtime.js +241 -0
  12. package/lib/store.js +96 -0
  13. package/lib/tools.js +93 -0
  14. package/package.json +27 -46
  15. package/NOTICE +0 -55
  16. package/assets/webhook-sdk/python/bot.py +0 -63
  17. package/assets/webhook-sdk/python/commonly.py +0 -232
  18. package/node_modules/@commonlyai/seats/README.md +0 -25
  19. package/node_modules/@commonlyai/seats/package.json +0 -22
  20. package/node_modules/@commonlyai/seats/src/adapters/hermes.js +0 -264
  21. package/node_modules/@commonlyai/seats/src/adapters/stub.js +0 -24
  22. package/node_modules/@commonlyai/seats/src/contract.js +0 -48
  23. package/node_modules/@commonlyai/seats/src/failures.js +0 -44
  24. package/node_modules/@commonlyai/seats/src/harness-plan.js +0 -329
  25. package/node_modules/@commonlyai/seats/src/index.js +0 -22
  26. package/node_modules/@commonlyai/seats/src/process-group.js +0 -269
  27. package/node_modules/@commonlyai/seats/src/record/format.js +0 -737
  28. package/node_modules/@commonlyai/seats/src/record/otel.js +0 -238
  29. package/node_modules/@commonlyai/seats/src/runtime-observation.js +0 -29
  30. package/node_modules/@commonlyai/seats/src/seat-model.js +0 -17
  31. package/node_modules/@commonlyai/seats/src/supervisor.js +0 -308
  32. package/node_modules/@commonlyai/seats/src/trace/events.js +0 -46
  33. package/node_modules/@commonlyai/seats/src/trace/store.js +0 -276
  34. package/node_modules/@commonlyai/seats/src/turn-processes.js +0 -226
  35. package/node_modules/@commonlyai/seats/src/upstream-refusal.js +0 -418
  36. package/node_modules/@commonlyai/seats/src/valet/env.js +0 -201
  37. package/node_modules/@commonlyai/seats/src/valet/grant.js +0 -123
  38. package/node_modules/@commonlyai/seats/src/valet/permissions.js +0 -213
  39. package/node_modules/@commonlyai/seats/src/wire/provider-proxy.js +0 -529
  40. package/skills/commonly/SKILL.md +0 -233
  41. package/src/commands/agent.js +0 -6334
  42. package/src/commands/daemon.js +0 -933
  43. package/src/commands/dev.js +0 -153
  44. package/src/commands/doctor.js +0 -141
  45. package/src/commands/inbox.js +0 -330
  46. package/src/commands/login.js +0 -194
  47. package/src/commands/mcp.js +0 -56
  48. package/src/commands/pod.js +0 -120
  49. package/src/commands/task.js +0 -279
  50. package/src/commands/verify.js +0 -48
  51. package/src/index.js +0 -99
  52. package/src/lib/adapter-login.js +0 -76
  53. package/src/lib/adapters/broker-bridge-entry.js +0 -76
  54. package/src/lib/adapters/claude.js +0 -2207
  55. package/src/lib/adapters/codex-broker-bridge.mjs +0 -298
  56. package/src/lib/adapters/codex.js +0 -1898
  57. package/src/lib/adapters/dsh.js +0 -513
  58. package/src/lib/adapters/goose.js +0 -510
  59. package/src/lib/adapters/hermes.js +0 -18
  60. package/src/lib/adapters/index.js +0 -68
  61. package/src/lib/adapters/opencode.js +0 -1681
  62. package/src/lib/adapters/pi-commonly-mcp.mjs +0 -71
  63. package/src/lib/adapters/pi-mcp-client.mjs +0 -617
  64. package/src/lib/adapters/pi-shell-env.mjs +0 -41
  65. package/src/lib/adapters/pi.js +0 -1025
  66. package/src/lib/adapters/qwen.js +0 -523
  67. package/src/lib/adapters/seat-model.js +0 -8
  68. package/src/lib/adapters/stub.js +0 -1
  69. package/src/lib/adapters/tested-version.js +0 -106
  70. package/src/lib/api.js +0 -205
  71. package/src/lib/claim-outcome.js +0 -128
  72. package/src/lib/claimed-work-gate.js +0 -73
  73. package/src/lib/cli-hint.js +0 -33
  74. package/src/lib/commit-attribution.js +0 -391
  75. package/src/lib/config.js +0 -155
  76. package/src/lib/content-refusal.js +0 -94
  77. package/src/lib/credential-file.js +0 -208
  78. package/src/lib/daemon-agent-states.js +0 -95
  79. package/src/lib/daemon-hint.js +0 -18
  80. package/src/lib/daemon-logs.js +0 -43
  81. package/src/lib/daemon-service-windows.js +0 -263
  82. package/src/lib/daemon-service.js +0 -486
  83. package/src/lib/daemon-state.js +0 -84
  84. package/src/lib/daemon-store.js +0 -119
  85. package/src/lib/daemon-supervisor.js +0 -629
  86. package/src/lib/daemon-watch.js +0 -115
  87. package/src/lib/declared-mcp-guard.js +0 -284
  88. package/src/lib/default-environment.js +0 -207
  89. package/src/lib/device-login.js +0 -126
  90. package/src/lib/digest-frame.js +0 -151
  91. package/src/lib/doctor.js +0 -291
  92. package/src/lib/egress-proxy.js +0 -160
  93. package/src/lib/enforcement.js +0 -946
  94. package/src/lib/environment.js +0 -681
  95. package/src/lib/first-output-watchdog.js +0 -191
  96. package/src/lib/git-readonly-server.js +0 -138
  97. package/src/lib/git-readonly.js +0 -463
  98. package/src/lib/git-remote-commonly.mjs +0 -100
  99. package/src/lib/grant-broker-guard.js +0 -345
  100. package/src/lib/hooks-config.js +0 -238
  101. package/src/lib/host-gh-broker.js +0 -152
  102. package/src/lib/host-gh-shim.mjs +0 -98
  103. package/src/lib/inbox.js +0 -483
  104. package/src/lib/instance-proxy.js +0 -76
  105. package/src/lib/machine-host.js +0 -29
  106. package/src/lib/mcp-credential-delivery.js +0 -121
  107. package/src/lib/mcp-first-run.js +0 -50
  108. package/src/lib/mcp-home.js +0 -644
  109. package/src/lib/mcp-ready.js +0 -68
  110. package/src/lib/mcp-server-version.js +0 -123
  111. package/src/lib/mcp-token.js +0 -36
  112. package/src/lib/mcp-warm-child.mjs +0 -412
  113. package/src/lib/memory-bridge.js +0 -191
  114. package/src/lib/memory-import.js +0 -156
  115. package/src/lib/missing-harness.js +0 -31
  116. package/src/lib/model-unavailable.js +0 -154
  117. package/src/lib/native-runtime-inventory.js +0 -40
  118. package/src/lib/outsider-grant-mcp.js +0 -82
  119. package/src/lib/own-key-launch.js +0 -42
  120. package/src/lib/pod-catch-up.js +0 -55
  121. package/src/lib/pod-focus.js +0 -224
  122. package/src/lib/pod-tail.js +0 -68
  123. package/src/lib/poll-liveness.js +0 -103
  124. package/src/lib/poll-retry.js +0 -81
  125. package/src/lib/poller.js +0 -200
  126. package/src/lib/provider-proxy.js +0 -42
  127. package/src/lib/public-seat-mcp-flag.js +0 -36
  128. package/src/lib/public-tool-error.js +0 -60
  129. package/src/lib/public-withheld-tools.js +0 -48
  130. package/src/lib/reply-receipt.js +0 -55
  131. package/src/lib/resolve-on-path.js +0 -22
  132. package/src/lib/runtime-inventory.js +0 -56
  133. package/src/lib/runtime-observation.js +0 -37
  134. package/src/lib/sandbox/bwrap.js +0 -275
  135. package/src/lib/sandbox/codex-perimeter.js +0 -16
  136. package/src/lib/sandbox/host-check.js +0 -268
  137. package/src/lib/sandbox/mode.js +0 -49
  138. package/src/lib/sandbox/opencode-loopback.mjs +0 -68
  139. package/src/lib/sandbox/seatbelt.js +0 -684
  140. package/src/lib/sandbox/turn-dir.js +0 -56
  141. package/src/lib/sandbox/windows-unsandboxed.js +0 -183
  142. package/src/lib/sandbox/workspace-git-config.js +0 -243
  143. package/src/lib/sandbox/workspace-pin.js +0 -158
  144. package/src/lib/seat-runtime-controls.js +0 -61
  145. package/src/lib/seat-stop.js +0 -97
  146. package/src/lib/server-skills.js +0 -32
  147. package/src/lib/session-recycle.js +0 -219
  148. package/src/lib/session-store.js +0 -215
  149. package/src/lib/skill-prompt.js +0 -74
  150. package/src/lib/skills-import.js +0 -113
  151. package/src/lib/spawn-credential.js +0 -402
  152. package/src/lib/spawn-retry.js +0 -311
  153. package/src/lib/stable-install.js +0 -176
  154. package/src/lib/supervised-turn.js +0 -352
  155. package/src/lib/task.js +0 -235
  156. package/src/lib/turn-failure-notice.js +0 -177
  157. package/src/lib/unconfined-seat-guard.js +0 -94
  158. package/src/lib/update-check.js +0 -192
  159. package/src/lib/upstream-refusal.js +0 -1
  160. package/src/lib/webhook-server.js +0 -109
  161. package/src/lib/windows-command.js +0 -393
  162. package/src/lib/workspace-git.js +0 -87
  163. package/src/lib/workspace-trust.js +0 -162
@@ -1,264 +0,0 @@
1
- /** Hermes 0.22 local CLI wrapper. Hermes owns provider I/O; this file only spawns it. */
2
- import { spawn as childSpawn, spawnSync } from 'child_process';
3
- import { createHash, randomBytes } from 'crypto';
4
- import { chmod, mkdir, readFile, rename, rm, writeFile } from 'fs/promises';
5
- import { homedir } from 'os';
6
- import { join } from 'path';
7
- import { StringDecoder } from 'string_decoder';
8
- import { observeSpawn, controlsFromArguments } from '../runtime-observation.js';
9
- import { requireSeatModel } from '../seat-model.js';
10
- import { adapterFailure, spawnCredentials } from '../upstream-refusal.js';
11
-
12
- export const SUPPORTED_HERMES_VERSION_RANGE = '>=0.22.5 <0.23.0';
13
- const supportsHermesVersion = (version) => {
14
- const match = /^(\d+)\.(\d+)\.(\d+)$/.exec(version || '');
15
- if (!match) return false;
16
- return Number(match[1]) === 0 && Number(match[2]) === 22 && Number(match[3]) >= 5;
17
- };
18
- const DEFAULT_TIMEOUT_MS = 15 * 60 * 1000;
19
- const VERSION_TIMEOUT_MS = 15000;
20
- const MAX_STDERR_CHARS = 4096;
21
- const MAX_LINE_CHARS = 1024 * 1024;
22
- // Keys the wrapper owns in the profile's .env. Every other line is the user's.
23
- const PROFILE_ENV_KEYS = ['OPENROUTER_API_KEY', 'OPENROUTER_BASE_URL'];
24
- const PLAIN_ENV_VALUE = /^[A-Za-z0-9_\-.:/+=@%~]+$/;
25
-
26
- const definedKey = (line) => {
27
- const match = /^\s*(?:export\s+)?([A-Za-z_][A-Za-z0-9_]*)\s*=/.exec(line);
28
- return match ? match[1] : null;
29
- };
30
-
31
- // From 0.22.5 the detached gateway serves a profiles/<hash> home with a child
32
- // env scrubbed of credentials and overlaid with that profile's own .env
33
- // (tools/environments/local.py, served_profile_child_env), so a key in the
34
- // spawn env never reaches the model. Hermes documents <profile>/.env as where
35
- // a profile's API keys live (website/docs/user-guide/profiles.md). The file
36
- // stays after the turn: the gateway outlives the spawn and rereads it.
37
- export const syncProfileEnv = async (home, env) => {
38
- const owned = PROFILE_ENV_KEYS.filter((key) => env[key]);
39
- for (const key of owned) {
40
- if (!PLAIN_ENV_VALUE.test(env[key])) {
41
- throw new Error(`${key} contains characters a Hermes .env line cannot carry unquoted; refusing to write it`);
42
- }
43
- }
44
- const path = join(home, '.env');
45
- let current = '';
46
- try {
47
- current = await readFile(path, 'utf8');
48
- } catch (error) {
49
- if (error?.code !== 'ENOENT') throw new Error(`Cannot read the Hermes profile .env (${error?.code || 'read failed'})`);
50
- }
51
- const kept = current.split(/\r?\n/).filter((line, index, lines) => (
52
- !owned.includes(definedKey(line)) && !(index === lines.length - 1 && line === '')
53
- ));
54
- const next = `${[...kept, ...owned.map((key) => `${key}=${env[key]}`)].join('\n')}\n`;
55
- await chmod(home, 0o700);
56
- if (next === current) {
57
- await chmod(path, 0o600);
58
- return;
59
- }
60
- const temp = join(home, `.env.${process.pid}.${randomBytes(6).toString('hex')}.tmp`);
61
- try {
62
- await writeFile(temp, next, { mode: 0o600, flag: 'wx' });
63
- await chmod(temp, 0o600);
64
- await rename(temp, path);
65
- } catch (error) {
66
- await rm(temp, { force: true });
67
- throw new Error(`Cannot write the Hermes profile .env (${error?.code || 'write failed'})`);
68
- }
69
- };
70
-
71
- const readVersion = (env, spawnSyncImpl = spawnSync) => {
72
- let probe;
73
- for (let attempt = 0; attempt < 2; attempt += 1) {
74
- probe = spawnSyncImpl('hermes', ['--version'], {
75
- encoding: 'utf8', env, timeout: VERSION_TIMEOUT_MS,
76
- });
77
- if (probe.error?.code === 'ENOENT') return null;
78
- if (probe.error || probe.status !== 0) continue;
79
- const version = String(probe.stdout || '').match(/Hermes Agent v([^\s]+)/i)?.[1];
80
- if (!supportsHermesVersion(version)) {
81
- throw new Error(`Hermes ${version || 'unknown'} is unsupported; this adapter supports ${SUPPORTED_HERMES_VERSION_RANGE}. Run hermes update if yours is older than 0.22.5; releases 0.23 and later are not supported yet`);
82
- }
83
- return version;
84
- }
85
- const status = probe.status ?? 'none';
86
- const signal = probe.signal ?? 'none';
87
- const code = probe.error?.code ?? 'none';
88
- throw new Error(`Hermes version probe failed after 2 attempts (status=${status}, signal=${signal}, code=${code}, timeout=${VERSION_TIMEOUT_MS}ms); check the Hermes installation or startup log`);
89
- };
90
-
91
- export const readHermesJsonl = (line, state) => {
92
- let event;
93
- try { event = JSON.parse(line); } catch { throw new Error('Hermes emitted invalid JSONL'); }
94
- if (state.result) throw new Error('Hermes emitted data after its terminal result');
95
- if (event?.type === 'system' && event.subtype === 'init') {
96
- state.started = true;
97
- } else if (event?.type === 'result') {
98
- state.result = event;
99
- } else if (!state.started) {
100
- throw new Error('Hermes emitted an event before init');
101
- }
102
- };
103
-
104
- const runHermes = ({ args, prompt, cwd, env, timeoutMs, spawnImpl, credentials, onSpawn, controls = {} }) => new Promise((resolve, reject) => {
105
- const child = (spawnImpl || childSpawn)('hermes', args, {
106
- cwd, env, stdio: ['pipe', 'pipe', 'pipe'],
107
- });
108
- observeSpawn(child, onSpawn, controls);
109
- const state = { started: false, result: null };
110
- const decoder = new StringDecoder('utf8');
111
- let buffer = '';
112
- let stderr = '';
113
- let timedOut = false;
114
- let settled = false;
115
- let closed = false;
116
- let stopping = false;
117
- let killTimer;
118
- const finish = (error, result) => {
119
- if (settled) return;
120
- settled = true;
121
- clearTimeout(timer);
122
- if (error) reject(error); else resolve(result);
123
- };
124
- const stop = () => {
125
- if (stopping || closed) return;
126
- stopping = true;
127
- child.kill('SIGTERM');
128
- killTimer = setTimeout(() => {
129
- if (!closed) child.kill('SIGKILL');
130
- }, 5000);
131
- };
132
- const failAndStop = (error) => {
133
- stop();
134
- finish(error);
135
- };
136
- const timer = setTimeout(() => {
137
- timedOut = true;
138
- stop();
139
- }, timeoutMs);
140
- child.on('error', (error) => failAndStop(adapterFailure('Hermes', error?.message || 'spawn failed', { credentials })));
141
- child.stdout.on('data', (chunk) => {
142
- if (settled) return;
143
- buffer += decoder.write(chunk);
144
- let newline = buffer.indexOf('\n');
145
- while (newline !== -1) {
146
- if (newline > MAX_LINE_CHARS) {
147
- failAndStop(new Error('Hermes JSONL line exceeded 1 MiB'));
148
- return;
149
- }
150
- const line = buffer.slice(0, newline).trim();
151
- buffer = buffer.slice(newline + 1);
152
- if (line) {
153
- try { readHermesJsonl(line, state); } catch (error) {
154
- failAndStop(error);
155
- return;
156
- }
157
- }
158
- newline = buffer.indexOf('\n');
159
- }
160
- if (buffer.length > MAX_LINE_CHARS) {
161
- failAndStop(new Error('Hermes JSONL line exceeded 1 MiB'));
162
- }
163
- });
164
- child.stdout.on('error', (error) => failAndStop(adapterFailure('Hermes stdout', error?.message || 'read failed', { credentials })));
165
- child.stderr.on('data', (chunk) => {
166
- stderr = `${stderr}${chunk.toString()}`.slice(-MAX_STDERR_CHARS);
167
- });
168
- child.on('close', (code) => {
169
- closed = true;
170
- clearTimeout(killTimer);
171
- if (settled) return;
172
- if (timedOut) return finish(new Error(`Hermes timed out after ${timeoutMs}ms`));
173
- buffer += decoder.end();
174
- if (buffer.trim()) {
175
- try { readHermesJsonl(buffer.trim(), state); } catch (error) { return finish(error); }
176
- }
177
- const result = state.result;
178
- if (!result || !state.started) return finish(new Error('Hermes exited without a complete JSONL result'));
179
- if (code !== 0 || result.exit_code !== 0) {
180
- return finish(adapterFailure('Hermes', result.error || stderr || `exit ${code}`, {
181
- credentials, exitCode: code,
182
- }));
183
- }
184
- if (typeof result.text !== 'string' || !result.session_id) {
185
- return finish(new Error('Hermes result omitted text or session_id'));
186
- }
187
- return finish(null, { text: result.text, newSessionId: result.session_id });
188
- });
189
- child.stdin.on('error', (error) => failAndStop(adapterFailure('Hermes stdin', error?.message || 'write failed', { credentials })));
190
- child.stdin.end(prompt);
191
- });
192
-
193
- /**
194
- * @param {import('../contract.js').SeatHost & { profileRoot?: string }} [host]
195
- * profileRoot is where each seat's isolated HERMES_HOME is created.
196
- */
197
- export const createHermesAdapter = ({
198
- memoryPreamble = (prompt) => prompt,
199
- effectiveTrust = (trust) => trust,
200
- seatModelRequired = () => false,
201
- profileRoot = join(homedir(), '.seats', 'hermes', 'profiles'),
202
- } = {}) => ({
203
- get requiresModel() { return seatModelRequired(); },
204
- name: 'hermes',
205
- runtimeType: 'hermes',
206
- supportsProvider: false,
207
- providerKeyEnv: 'OPENROUTER_API_KEY',
208
-
209
- async validateEnvironment(environment) {
210
- if (environment?.provider !== undefined) {
211
- throw new Error('Hermes adapter does not consume environment.provider; supply OPENROUTER_API_KEY in the host environment');
212
- }
213
- if (environment?.mcp?.length) {
214
- throw new Error('Hermes adapter does not yet consume environment.mcp declarations');
215
- }
216
- if (effectiveTrust(environment?.sandbox?.trust) === 'public'
217
- || (environment?.sandbox?.mode && environment.sandbox.mode !== 'none')) {
218
- throw new Error('Hermes public or sandbox-declared seats are unsupported: no enforced confinement adapter exists');
219
- }
220
- },
221
-
222
- async detect() {
223
- const version = readVersion(process.env);
224
- return version ? { path: 'hermes', version } : null;
225
- },
226
-
227
- async spawn(prompt, ctx = {}) {
228
- const seatModel = requireSeatModel(ctx.environment, 'hermes', seatModelRequired());
229
- await this.validateEnvironment(ctx.environment);
230
- // Keep Hermes away from the operator's default profile, including any
231
- // Nous Portal/Cloud credentials there. The wrapper provides the sole key.
232
- const seat = `${ctx.agentName || ''}\0${ctx.cwd || ''}`;
233
- const home = ctx._hermesHome || join(profileRoot,
234
- createHash('sha256').update(seat).digest('hex').slice(0, 24));
235
- await mkdir(home, { recursive: true, mode: 0o700 });
236
- const env = { ...(ctx.env || process.env), HERMES_HOME: home };
237
- if (!readVersion(env, ctx._spawnSyncImpl)) {
238
- throw new Error(`Hermes Agent ${SUPPORTED_HERMES_VERSION_RANGE} is not on PATH; install for your OS: https://hermes-agent.nousresearch.com/docs/getting-started/installation`);
239
- }
240
- if (!env.OPENROUTER_API_KEY) {
241
- throw new Error('Supply the operator OPENROUTER_API_KEY through Secret Manager before running Hermes');
242
- }
243
- await syncProfileEnv(home, env);
244
- const cwd = ctx.cwd || process.cwd();
245
- // 0.22.5's gateway rejects --no-restore-cwd; --in pins the workspace on
246
- // both fresh and resumed turns. 0.22.2-0.22.4 are refused above, so no
247
- // version-keyed flag is needed.
248
- const args = ['chat', '--oneshot', '--query-file', '-', '--format', 'stream-json',
249
- '--provider', 'openrouter', '--source', 'tool', '--in', cwd];
250
- if (seatModel) args.push('--model', seatModel);
251
- if (ctx.sessionId) args.push('--resume', ctx.sessionId);
252
- const fullPrompt = memoryPreamble(prompt, ctx.memoryLongTerm, {
253
- freshSession: !ctx.sessionId,
254
- });
255
- const requestedTimeout = Number(ctx.timeoutMs);
256
- const timeoutMs = Number.isFinite(requestedTimeout) && requestedTimeout > 0
257
- ? Math.min(requestedTimeout, DEFAULT_TIMEOUT_MS) : DEFAULT_TIMEOUT_MS;
258
- return runHermes({
259
- args, prompt: fullPrompt, cwd, env, timeoutMs,
260
- spawnImpl: ctx._spawnImpl, onSpawn: ctx.onSpawn, controls: controlsFromArguments(args, { modelFlag: '--model', valueFlags: ['--query-file', '--format', '--provider', '--source', '--in', '--resume'] }),
261
- credentials: spawnCredentials(ctx, ['OPENROUTER_API_KEY']),
262
- });
263
- },
264
- });
@@ -1,24 +0,0 @@
1
- /**
2
- * Stub adapter — the no-op reference implementation of the ADR-005 adapter
3
- * contract. Used by `commonly agent attach stub` and the Phase-1a test
4
- * harness so the run loop can be reviewed end-to-end without a real CLI
5
- * on PATH.
6
- *
7
- * Contract (ADR-005 §Adapter pattern):
8
- * detect(): Promise<{ path, version } | null>
9
- * spawn(prompt, ctx): Promise<{ text, newSessionId?, memorySummary? }>
10
- *
11
- * The adapter MUST be pure: input (argv + env + prompt) → output (text + optional
12
- * next session-id + optional memory summary). No direct network, no direct API
13
- * calls, no hidden state. The run loop handles all Commonly-facing I/O.
14
- */
15
-
16
- export default {
17
- name: 'stub',
18
- async detect() {
19
- return { path: '(builtin)', version: '0.0.0' };
20
- },
21
- async spawn(prompt, _ctx) {
22
- return { text: `(stub) received: ${String(prompt ?? '').slice(0, 200)}` };
23
- },
24
- };
@@ -1,48 +0,0 @@
1
- /**
2
- * The adapter contract (ADR-005 §Adapter pattern). An adapter turns one prompt
3
- * into one CLI run and one reply. It owns argv, env and output parsing for its
4
- * CLI; the caller owns everything that talks to a Commonly instance.
5
- *
6
- * @typedef {object} Adapter
7
- * @property {string} name
8
- * @property {string} [runtimeType]
9
- * @property {string} [providerKeyEnv] host env var the adapter's provider key arrives in
10
- * @property {() => Promise<{ path: string, version: string } | null>} detect
11
- * @property {(environment: object) => Promise<void>} [validateEnvironment] refuse before spawn
12
- * @property {(prompt: string, ctx: object) => Promise<SpawnResult>} spawn
13
- *
14
- * @typedef {object} SpawnResult
15
- * @property {string} text
16
- * @property {string} [newSessionId]
17
- * @property {number} [contextTokens]
18
- * @property {string} [memorySummary]
19
- *
20
- * Adapters that need host policy are built by a factory that takes a SeatHost.
21
- * Every field is optional; an absent field takes the neutral default named.
22
- *
23
- * @typedef {object} SeatHost
24
- * @property {(prompt: string, longTerm: string | null | undefined, opts: { freshSession: boolean }) => string} [memoryPreamble] default: the prompt unchanged
25
- * @property {(trust: string | undefined) => string | undefined} [effectiveTrust] default: the declared trust unchanged
26
- * @property {() => boolean} [seatModelRequired] default: false
27
- */
28
-
29
- /**
30
- * @param {Adapter[]} adapters
31
- */
32
- export const createRegistry = (adapters) => {
33
- const byName = {};
34
- for (const adapter of adapters) {
35
- if (!adapter?.name) throw new Error('every adapter needs a name');
36
- if (byName[adapter.name]) throw new Error(`duplicate adapter name: ${adapter.name}`);
37
- byName[adapter.name] = adapter;
38
- }
39
- return {
40
- adapters: byName,
41
- names: () => Object.keys(byName),
42
- get: (name) => byName[name] || null,
43
- };
44
- };
45
-
46
- export const providerKeyEnvNames = (adapters) => [...new Set(
47
- Object.values(adapters).map((adapter) => adapter.providerKeyEnv).filter(Boolean),
48
- )];
@@ -1,44 +0,0 @@
1
- /**
2
- * Named run failures. Every way a run can end badly has one kind, so a trace,
3
- * a retry policy and a dashboard all read the same word. A kind is a contract:
4
- * add one here, never mint one at a call site.
5
- */
6
- import { scrubAdapterFailure } from './upstream-refusal.js';
7
-
8
- export const FAILURE_KINDS = Object.freeze({
9
- stuck: { retryable: true, meaning: 'no progress signal within the liveness window' },
10
- timeout: { retryable: true, meaning: 'the run exceeded its wall-clock budget' },
11
- exited: { retryable: true, meaning: 'the CLI process ended before the turn settled' },
12
- protocol: { retryable: false, meaning: 'the CLI broke its wire protocol' },
13
- refusal: { retryable: false, meaning: 'the model or provider refused; never retried' },
14
- max_tokens: { retryable: false, meaning: 'the turn hit its token limit' },
15
- max_turn_requests: { retryable: false, meaning: 'the turn hit its request limit' },
16
- cancelled: { retryable: false, meaning: 'the run was cancelled' },
17
- credential_denied: { retryable: false, meaning: 'the run asked for a credential its grant does not allow' },
18
- unsupported: { retryable: false, meaning: 'the CLI or its version is outside what this adapter supports' },
19
- // The harness cannot serve anyone right now. Retrying on the same harness is
20
- // pointless; a run config's fallback moves to its next entry (harness-plan.js).
21
- not_authenticated: { retryable: false, meaning: 'the CLI is not signed in, or its sign-in was rejected' },
22
- quota: { retryable: false, meaning: 'the account behind the CLI is out of quota or credits' },
23
- provider_unsupported: { retryable: false, meaning: 'the provider does not serve this model to this account' },
24
- capacity: { retryable: true, meaning: 'every entry the run config allows is at a host cap or held; it may run once one frees' },
25
- });
26
-
27
- export class RunFailure extends Error {
28
- constructor(kind, detail, { cause } = {}) {
29
- if (!FAILURE_KINDS[kind]) throw new Error(`unknown failure kind: ${kind}`);
30
- super(detail ? `${kind}: ${detail}` : kind, cause ? { cause } : undefined);
31
- this.name = 'RunFailure';
32
- this.kind = kind;
33
- this.retryable = FAILURE_KINDS[kind].retryable;
34
- this.detail = detail || '';
35
- }
36
- }
37
-
38
- /** Build a failure whose detail has known credentials and secret shapes scrubbed. */
39
- export const runFailure = (kind, text, { credentials = [], limit } = {}) => {
40
- const { detail } = scrubAdapterFailure(String(text ?? ''), { credentials, limit });
41
- return new RunFailure(kind, detail);
42
- };
43
-
44
- export const failureKindOf = (error) => (error instanceof RunFailure ? error.kind : null);