@adhdev/daemon-core 1.0.18-rc.19 → 1.0.18-rc.2
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/cli-adapters/cli-state-engine.d.ts +0 -77
- package/dist/cli-adapters/provider-cli-shared.d.ts +0 -33
- package/dist/cli-adapters/pty-transport.d.ts +1 -2
- package/dist/commands/cli-manager.d.ts +0 -9
- package/dist/commands/upgrade-helper.d.ts +0 -1
- package/dist/commands/windows-atomic-upgrade.d.ts +1 -17
- package/dist/config/mesh-json-config.d.ts +0 -62
- package/dist/index.d.ts +2 -4
- package/dist/index.js +4143 -6578
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +4138 -6570
- package/dist/index.mjs.map +1 -1
- package/dist/mesh/coordinator-prompt.d.ts +0 -76
- package/dist/mesh/mesh-active-work.d.ts +1 -1
- package/dist/mesh/mesh-ledger.d.ts +1 -28
- package/dist/mesh/mesh-node-identity.d.ts +0 -23
- package/dist/mesh/mesh-refine-gates.d.ts +0 -89
- package/dist/mesh/mesh-remote-event-pull.d.ts +0 -3
- package/dist/mesh/mesh-runtime-store.d.ts +0 -11
- package/dist/mesh/mesh-work-queue.d.ts +1 -24
- package/dist/providers/chat-message-normalization.d.ts +0 -22
- package/dist/providers/cli-provider-instance-types.d.ts +0 -4
- package/dist/providers/cli-provider-instance.d.ts +0 -78
- package/dist/providers/contracts.d.ts +0 -17
- package/dist/providers/provider-schema.d.ts +0 -1
- package/dist/providers/sdk/v1/builders/cli/detect-status.d.ts +0 -14
- package/dist/providers/types/interactive-prompt.d.ts +0 -18
- package/dist/repo-mesh-types.d.ts +6 -38
- package/dist/shared-types.d.ts +2 -4
- package/package.json +3 -3
- package/src/boot/daemon-lifecycle.ts +0 -10
- package/src/cli-adapters/cli-state-engine.ts +3 -220
- package/src/cli-adapters/provider-cli-adapter.ts +11 -41
- package/src/cli-adapters/provider-cli-shared.ts +0 -41
- package/src/cli-adapters/pty-transport.d.ts +1 -2
- package/src/cli-adapters/pty-transport.ts +1 -1
- package/src/cli-adapters/session-host-transport.ts +3 -8
- package/src/commands/cli-manager.ts +8 -72
- package/src/commands/high-family/mesh-coordinator-launch.ts +2 -17
- package/src/commands/high-family/mesh-events.ts +2 -13
- package/src/commands/med-family/mesh-crud.ts +0 -155
- package/src/commands/med-family/mesh-queue.ts +1 -74
- package/src/commands/router-refine.ts +4 -71
- package/src/commands/router.ts +0 -8
- package/src/commands/upgrade-helper.ts +82 -106
- package/src/commands/windows-atomic-upgrade.ts +35 -281
- package/src/config/mesh-json-config.ts +0 -111
- package/src/index.ts +1 -21
- package/src/mesh/coordinator-prompt.ts +11 -258
- package/src/mesh/mesh-active-work.ts +1 -11
- package/src/mesh/mesh-completion-synthesis.ts +1 -12
- package/src/mesh/mesh-event-classify.ts +0 -19
- package/src/mesh/mesh-event-forwarding.ts +6 -64
- package/src/mesh/mesh-events-utils.ts +0 -42
- package/src/mesh/mesh-ledger.ts +0 -77
- package/src/mesh/mesh-node-identity.ts +0 -49
- package/src/mesh/mesh-queue-assignment.ts +11 -210
- package/src/mesh/mesh-reconcile-loop.ts +3 -306
- package/src/mesh/mesh-refine-gates.ts +0 -300
- package/src/mesh/mesh-remote-event-pull.ts +47 -68
- package/src/mesh/mesh-runtime-store.ts +0 -21
- package/src/mesh/mesh-work-queue.ts +2 -85
- package/src/providers/chat-message-normalization.ts +0 -53
- package/src/providers/cli-provider-instance-types.ts +0 -53
- package/src/providers/cli-provider-instance.ts +15 -497
- package/src/providers/contracts.ts +1 -25
- package/src/providers/provider-schema.ts +0 -83
- package/src/providers/sdk/v1/builders/cli/detect-status.ts +0 -23
- package/src/providers/sdk/v1/builders/cli/parse-approval.ts +0 -20
- package/src/providers/sdk/v1/schemas/cli/provider.schema.json +0 -44
- package/src/providers/types/interactive-prompt.ts +0 -77
- package/src/repo-mesh-types.ts +12 -112
- package/src/session-host/managed-host.ts +0 -34
- package/src/shared-types.ts +1 -9
- package/src/status/reporter.ts +0 -1
- package/src/status/snapshot.ts +0 -2
- package/dist/commands/process-lifecycle.d.ts +0 -43
- package/dist/mesh/mesh-disk-retention.d.ts +0 -105
- package/dist/providers/auto-approve-modes.d.ts +0 -14
- package/src/commands/process-lifecycle.ts +0 -248
- package/src/mesh/mesh-disk-retention.ts +0 -370
- package/src/providers/auto-approve-modes.ts +0 -97
|
@@ -2,68 +2,11 @@ import { execFileSync, spawn, spawnSync, type ChildProcess } from 'child_process
|
|
|
2
2
|
import * as fs from 'fs';
|
|
3
3
|
import * as http from 'http';
|
|
4
4
|
import * as path from 'path';
|
|
5
|
-
import { stopOwnedProcessesForPrefixes } from './process-lifecycle.js';
|
|
6
5
|
|
|
7
6
|
const POINTER_NAME = '.adhdev-current';
|
|
8
7
|
const STABLE_FILES = [POINTER_NAME, 'adhdev.cmd', 'adhdev.ps1', 'adhdev'] as const;
|
|
9
8
|
const DEFAULT_HEALTH_PORT = 19222;
|
|
10
9
|
|
|
11
|
-
// How long to wait for the replacement daemon to report the target version
|
|
12
|
-
// before deterministically rolling back. status.version only appears AFTER the
|
|
13
|
-
// daemon fully boots its components — a separate session-host process, node-pty/
|
|
14
|
-
// conpty, CDP, and providers — which on a Windows cold self-upgrade routinely
|
|
15
|
-
// exceeds the old 30s ceiling. Live Windows logs showed every rollback clustering
|
|
16
|
-
// at 33–35s (30s timeout + poll overhead), so the gate was tripping on slow-boot,
|
|
17
|
-
// not on genuine failure. 120s reflects real Windows self-upgrade cold-start time.
|
|
18
|
-
export const DEFAULT_HEALTH_TIMEOUT_MS = 120_000;
|
|
19
|
-
|
|
20
|
-
function fetchLocalJson(port: number, pathname: string): Promise<{ ok: boolean; body: string }> {
|
|
21
|
-
return new Promise((resolve) => {
|
|
22
|
-
const req = http.get(`http://127.0.0.1:${port}${pathname}`, { timeout: 1500 }, (res) => {
|
|
23
|
-
let body = '';
|
|
24
|
-
res.setEncoding('utf8');
|
|
25
|
-
res.on('data', (chunk) => { body += chunk; });
|
|
26
|
-
res.on('end', () => resolve({ ok: res.statusCode === 200, body }));
|
|
27
|
-
});
|
|
28
|
-
req.on('timeout', () => req.destroy());
|
|
29
|
-
req.on('error', () => resolve({ ok: false, body: '' }));
|
|
30
|
-
});
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
// Liveness + pid identity: GET /health returns {ok, pid, wsPath, port}.
|
|
34
|
-
async function fetchLocalHealth(port: number): Promise<{ ok: boolean; pid?: number }> {
|
|
35
|
-
const { ok, body } = await fetchLocalJson(port, '/health');
|
|
36
|
-
if (!ok) return { ok: false };
|
|
37
|
-
try {
|
|
38
|
-
const pid = Number(JSON.parse(body)?.pid);
|
|
39
|
-
return { ok: true, pid: Number.isFinite(pid) ? pid : undefined };
|
|
40
|
-
} catch {
|
|
41
|
-
return { ok: false };
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
// Running version: GET /api/v1/status exposes it at payload.status.version.
|
|
46
|
-
// /health carries no version, so the upgrade version gate must read this.
|
|
47
|
-
async function fetchLocalStatusVersion(port: number): Promise<string | undefined> {
|
|
48
|
-
const { ok, body } = await fetchLocalJson(port, '/api/v1/status');
|
|
49
|
-
if (!ok) return undefined;
|
|
50
|
-
try {
|
|
51
|
-
const version = (JSON.parse(body) as { status?: { version?: unknown } })?.status?.version;
|
|
52
|
-
return typeof version === 'string' ? version : undefined;
|
|
53
|
-
} catch {
|
|
54
|
-
return undefined;
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
// Markers that identify a Node process as ADHDev-owned when its command line
|
|
59
|
-
// also lives under a versioned install prefix. This deliberately excludes
|
|
60
|
-
// arbitrary user scripts that happen to be located under ~/.adhdev.
|
|
61
|
-
export const ADHDEV_OWNED_MARKERS = [
|
|
62
|
-
'session-host-daemon',
|
|
63
|
-
'node_modules/adhdev',
|
|
64
|
-
'node_modules/@adhdev/daemon-standalone',
|
|
65
|
-
] as const;
|
|
66
|
-
|
|
67
10
|
export interface WindowsInstallerLayout {
|
|
68
11
|
homeDir: string;
|
|
69
12
|
installRoot: string;
|
|
@@ -79,7 +22,7 @@ export interface WindowsAtomicUpgradeHooks {
|
|
|
79
22
|
restartOld: (portableNode: string) => void;
|
|
80
23
|
waitForHealth: (pid: number, targetVersion: string) => Promise<boolean>;
|
|
81
24
|
stopProcess: (pid: number) => void;
|
|
82
|
-
cleanup: (layout: WindowsInstallerLayout, activePrefix: string) => void
|
|
25
|
+
cleanup: (layout: WindowsInstallerLayout, activePrefix: string) => void;
|
|
83
26
|
log: (message: string) => void;
|
|
84
27
|
}
|
|
85
28
|
|
|
@@ -89,8 +32,6 @@ export interface WindowsAtomicUpgradeOptions {
|
|
|
89
32
|
targetVersion: string;
|
|
90
33
|
portableNode: string;
|
|
91
34
|
hooks: WindowsAtomicUpgradeHooks;
|
|
92
|
-
/** PIDs that must never be terminated during prefix sweeps (helper + parent daemon). */
|
|
93
|
-
excludePids?: number[];
|
|
94
35
|
}
|
|
95
36
|
|
|
96
37
|
export interface WindowsAtomicUpgradeResult {
|
|
@@ -161,28 +102,6 @@ function packageRootForPrefix(prefix: string, packageName: string): string {
|
|
|
161
102
|
return path.join(prefix, 'node_modules', ...packageName.split('/'));
|
|
162
103
|
}
|
|
163
104
|
|
|
164
|
-
// node-pty ships a Windows x64 prebuild. If npm rebuilds from source (because a
|
|
165
|
-
// .npmrc sets build-from-source=true), the install script deletes the prebuild
|
|
166
|
-
// and may leave no conpty.node on machines without build tools. Verify it
|
|
167
|
-
// survived before we ever activate the staged prefix.
|
|
168
|
-
const CONPTY_PREBUILD_RELATIVE_PATH = path.join(
|
|
169
|
-
'node_modules', 'adhdev', 'node_modules', 'node-pty', 'prebuilds', 'win32-x64', 'conpty.node'
|
|
170
|
-
);
|
|
171
|
-
|
|
172
|
-
function resolveStagedConptyPrebuildPath(stagedPrefix: string): string {
|
|
173
|
-
return path.join(stagedPrefix, CONPTY_PREBUILD_RELATIVE_PATH);
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
function verifyStagedConptyPrebuild(stagedPrefix: string): void {
|
|
177
|
-
const conptyPath = resolveStagedConptyPrebuildPath(stagedPrefix);
|
|
178
|
-
if (!fs.existsSync(conptyPath)) {
|
|
179
|
-
throw new Error(
|
|
180
|
-
`Staged install is missing required native addon: ${conptyPath}. ` +
|
|
181
|
-
'Aborting activation to prevent a daemon boot crash.'
|
|
182
|
-
);
|
|
183
|
-
}
|
|
184
|
-
}
|
|
185
|
-
|
|
186
105
|
function readPackageCliEntry(prefix: string, packageName: string, targetVersion: string): string {
|
|
187
106
|
const packageRoot = packageRootForPrefix(prefix, packageName);
|
|
188
107
|
const packageJsonPath = path.join(packageRoot, 'package.json');
|
|
@@ -209,31 +128,13 @@ function quotePowerShellLiteral(value: string): string {
|
|
|
209
128
|
function pinStagedShims(prefix: string, portableNode: string, cliEntry: string): void {
|
|
210
129
|
const cmdPath = path.join(prefix, 'adhdev.cmd');
|
|
211
130
|
const ps1Path = path.join(prefix, 'adhdev.ps1');
|
|
212
|
-
// The no-extension `adhdev` shim is what `where.exe adhdev` can resolve first
|
|
213
|
-
// and what git-bash / MSYS / WSL and `spawnSync('adhdev')` invoke. npm generates
|
|
214
|
-
// it as a POSIX `sh` shim whose ELSE branch falls back to the FIRST `node` on
|
|
215
|
-
// PATH (`else exec node ...`). On a box where system PATH node is v24, that
|
|
216
|
-
// fallback trips adhdev's own Node-24 guard and `adhdev doctor` reports the
|
|
217
|
-
// runtime surface broken. Rewrite it too so it hard-codes the portable Node 22
|
|
218
|
-
// absolute path with NO system-node fallback — mirroring the .cmd/.ps1 pins.
|
|
219
|
-
const noExtPath = path.join(prefix, 'adhdev');
|
|
220
131
|
const cmd = `@echo off\r\n"${portableNode}" "${cliEntry}" %*\r\n`;
|
|
221
132
|
const ps1 = `#!/usr/bin/env pwsh\r\n& ${quotePowerShellLiteral(portableNode)} ${quotePowerShellLiteral(cliEntry)} @args\r\nexit $LASTEXITCODE\r\n`;
|
|
222
|
-
// sh shim: single unconditional exec of the pinned node — no `if -x node` /
|
|
223
|
-
// `else exec node` branch, so PATH ordering can never shadow the runtime.
|
|
224
|
-
const noExt = `#!/bin/sh\nexec "${portableNode}" "${cliEntry}" "$@"\n`;
|
|
225
133
|
fs.writeFileSync(cmdPath, cmd, 'ascii');
|
|
226
134
|
fs.writeFileSync(ps1Path, ps1, 'utf8');
|
|
227
|
-
fs.writeFileSync(noExtPath, noExt, 'ascii');
|
|
228
135
|
const cmdReadback = fs.readFileSync(cmdPath, 'utf8');
|
|
229
136
|
const ps1Readback = fs.readFileSync(ps1Path, 'utf8');
|
|
230
|
-
|
|
231
|
-
if (
|
|
232
|
-
!cmdReadback.includes(portableNode)
|
|
233
|
-
|| !ps1Readback.includes(portableNode)
|
|
234
|
-
|| !noExtReadback.includes(portableNode)
|
|
235
|
-
|| /(^|\s)exec\s+node(\s|$)/m.test(noExtReadback)
|
|
236
|
-
) {
|
|
137
|
+
if (!cmdReadback.includes(portableNode) || !ps1Readback.includes(portableNode)) {
|
|
237
138
|
throw new Error('portable Node 22 pin validation failed');
|
|
238
139
|
}
|
|
239
140
|
}
|
|
@@ -319,39 +220,10 @@ function snapshotStableFiles(stablePrefix: string): Map<string, FileSnapshot> {
|
|
|
319
220
|
return snapshots;
|
|
320
221
|
}
|
|
321
222
|
|
|
322
|
-
|
|
323
|
-
// "restore-or-delete" logic dangerous:
|
|
324
|
-
// 1. If a stable shim (adhdev.cmd/.ps1/no-ext) did not exist at snapshot time —
|
|
325
|
-
// a first/partial install, or a stable tree that never had the launcher —
|
|
326
|
-
// deleting it leaves `where.exe adhdev` / `spawnSync('adhdev')` resolving to
|
|
327
|
-
// nothing (ENOENT). `adhdev doctor` then reports the runtime surface broken.
|
|
328
|
-
// 2. If the pointer (.adhdev-current) was absent at snapshot time, deleting it
|
|
329
|
-
// strands the re-published shims with no version to redirect to.
|
|
330
|
-
// So rollback (re)guarantees a valid launcher surface: existing snapshots restore
|
|
331
|
-
// their original bytes atomically; missing shims are re-issued from the canonical
|
|
332
|
-
// pointer-redirect launcher contents; and the pointer, when it has no snapshot,
|
|
333
|
-
// is re-written to the last-known-good active version instead of removed.
|
|
334
|
-
function restoreStableFiles(snapshots: Map<string, FileSnapshot>, layout: WindowsInstallerLayout): void {
|
|
335
|
-
const shims = stableShimContents();
|
|
223
|
+
function restoreStableFiles(snapshots: Map<string, FileSnapshot>): void {
|
|
336
224
|
for (const [target, snapshot] of snapshots) {
|
|
337
|
-
if (snapshot.exists && snapshot.data)
|
|
338
|
-
|
|
339
|
-
continue;
|
|
340
|
-
}
|
|
341
|
-
const name = path.basename(target);
|
|
342
|
-
if (name === 'adhdev.cmd' || name === 'adhdev.ps1' || name === 'adhdev') {
|
|
343
|
-
// Re-issue a valid pointer-redirect launcher rather than deleting it, so
|
|
344
|
-
// PATH `adhdev` always resolves after a rollback.
|
|
345
|
-
atomicWrite(target, shims[name].content, shims[name].encoding);
|
|
346
|
-
} else if (name === POINTER_NAME) {
|
|
347
|
-
// Preserve the last-successful (currently active) version so the redirect
|
|
348
|
-
// launchers still reach a real prefix. Only fall back to deleting when we
|
|
349
|
-
// have no active version to point at.
|
|
350
|
-
if (layout.activeVersionName) atomicWrite(target, layout.activeVersionName, 'ascii');
|
|
351
|
-
else try { fs.unlinkSync(target); } catch { /* noop */ }
|
|
352
|
-
} else {
|
|
353
|
-
try { fs.unlinkSync(target); } catch { /* noop */ }
|
|
354
|
-
}
|
|
225
|
+
if (snapshot.exists && snapshot.data) atomicWrite(target, snapshot.data.toString('binary'), 'binary');
|
|
226
|
+
else try { fs.unlinkSync(target); } catch { /* noop */ }
|
|
355
227
|
}
|
|
356
228
|
}
|
|
357
229
|
|
|
@@ -375,31 +247,11 @@ export async function performWindowsAtomicUpgrade(options: WindowsAtomicUpgradeO
|
|
|
375
247
|
try {
|
|
376
248
|
hooks.log(`Installing ${packageName}@${targetVersion} into inactive prefix ${stagedPrefix}`);
|
|
377
249
|
await hooks.install(stagedPrefix, portableNode);
|
|
378
|
-
verifyStagedConptyPrebuild(stagedPrefix);
|
|
379
250
|
const stagedCliEntry = readPackageCliEntry(stagedPrefix, packageName, targetVersion);
|
|
380
251
|
pinStagedShims(stagedPrefix, portableNode, stagedCliEntry);
|
|
381
252
|
validateStagedCli(portableNode, stagedCliEntry, targetVersion);
|
|
382
253
|
hooks.log(`Validated staged CLI and portable Node 22 shims in ${stagedPrefix}`);
|
|
383
254
|
|
|
384
|
-
// Stop every ADHDev-owned process still executing from the current active
|
|
385
|
-
// prefix or the legacy stable shim tree before moving the pointer. A stale
|
|
386
|
-
// session-host left running here keeps node-pty's native addon mapped from
|
|
387
|
-
// the old tree and resolves lazy requires against a deleted prefix after
|
|
388
|
-
// activation. Survivors block activation so the pointer is never corrupted.
|
|
389
|
-
const excludedPids = new Set([process.pid, ...(options.excludePids ?? [])].filter((n) => Number.isFinite(n) && n > 0));
|
|
390
|
-
const preStop = await stopOwnedProcessesForPrefixes({
|
|
391
|
-
prefixes: [layout.activePrefix, layout.stablePrefix],
|
|
392
|
-
excludePids: Array.from(excludedPids),
|
|
393
|
-
markers: Array.from(ADHDEV_OWNED_MARKERS),
|
|
394
|
-
waitMs: 15_000,
|
|
395
|
-
log: hooks.log,
|
|
396
|
-
});
|
|
397
|
-
if (preStop.survivors.length > 0) {
|
|
398
|
-
throw new Error(
|
|
399
|
-
`Cannot activate ${versionName}: ${preStop.survivors.length} owned process(es) still running under the current prefix`
|
|
400
|
-
);
|
|
401
|
-
}
|
|
402
|
-
|
|
403
255
|
activated = true;
|
|
404
256
|
publishStableShimsAndPointer(layout, versionName);
|
|
405
257
|
hooks.log(`Atomically activated ${versionName}`);
|
|
@@ -409,20 +261,20 @@ export async function performWindowsAtomicUpgrade(options: WindowsAtomicUpgradeO
|
|
|
409
261
|
throw new Error('replacement daemon did not pass the health/version gate');
|
|
410
262
|
}
|
|
411
263
|
hooks.log(`Replacement daemon pid ${daemonPid} passed health for ${targetVersion}`);
|
|
412
|
-
|
|
264
|
+
hooks.cleanup(layout, stagedPrefix);
|
|
413
265
|
return { stagedPrefix, stagedCliEntry, daemonPid };
|
|
414
266
|
} catch (error) {
|
|
415
267
|
if (restarted?.pid) hooks.stopProcess(restarted.pid);
|
|
416
268
|
if (activated) {
|
|
417
269
|
try {
|
|
418
|
-
restoreStableFiles(snapshots
|
|
270
|
+
restoreStableFiles(snapshots);
|
|
419
271
|
hooks.log(`Rolled back activation to ${layout.activeVersionName}`);
|
|
420
272
|
} catch (rollbackError: any) {
|
|
421
273
|
hooks.log(`Stable-file rollback failed: ${rollbackError?.message || String(rollbackError)}`);
|
|
422
274
|
}
|
|
423
275
|
}
|
|
424
276
|
try { hooks.restartOld(portableNode); } catch { hooks.log('Failed to restart the previous daemon during rollback'); }
|
|
425
|
-
try {
|
|
277
|
+
try { hooks.cleanup(layout, layout.activePrefix); } catch { /* failure path must preserve original error */ }
|
|
426
278
|
throw error;
|
|
427
279
|
}
|
|
428
280
|
}
|
|
@@ -435,24 +287,13 @@ export function createDefaultWindowsAtomicHooks(options: {
|
|
|
435
287
|
cwd: string;
|
|
436
288
|
env: NodeJS.ProcessEnv;
|
|
437
289
|
log: (message: string) => void;
|
|
438
|
-
/** Loopback IPC port to probe for health/version. Defaults to the daemon's local IPC port. */
|
|
439
|
-
healthPort?: number;
|
|
440
|
-
/** How long to poll for the replacement daemon to report the target version. */
|
|
441
|
-
healthTimeoutMs?: number;
|
|
442
290
|
}): WindowsAtomicUpgradeHooks {
|
|
443
|
-
const healthPort = options.healthPort ?? DEFAULT_HEALTH_PORT;
|
|
444
|
-
const healthTimeoutMs = options.healthTimeoutMs ?? DEFAULT_HEALTH_TIMEOUT_MS;
|
|
445
291
|
return {
|
|
446
292
|
install: (stagedPrefix, portableNode) => {
|
|
447
|
-
const env: NodeJS.ProcessEnv = {
|
|
448
|
-
...options.env,
|
|
449
|
-
ADHDEV_BOOTSTRAP: '1',
|
|
450
|
-
npm_config_build_from_source: 'false',
|
|
451
|
-
'npm_config_build-from-source': 'false',
|
|
452
|
-
};
|
|
293
|
+
const env: NodeJS.ProcessEnv = { ...options.env, ADHDEV_BOOTSTRAP: '1' };
|
|
453
294
|
const pathKey = Object.keys(env).find((key) => key.toLowerCase() === 'path') || 'Path';
|
|
454
295
|
env[pathKey] = `${path.dirname(portableNode)};${env[pathKey] || ''}`;
|
|
455
|
-
|
|
296
|
+
execFileSync(portableNode, [
|
|
456
297
|
options.npmCliPath,
|
|
457
298
|
'install', '-g', `${options.packageName}@${options.targetVersion}`, '--force', '--prefer-online', '--prefix', stagedPrefix,
|
|
458
299
|
], {
|
|
@@ -461,11 +302,7 @@ export function createDefaultWindowsAtomicHooks(options: {
|
|
|
461
302
|
maxBuffer: 20 * 1024 * 1024,
|
|
462
303
|
windowsHide: true,
|
|
463
304
|
env,
|
|
464
|
-
})
|
|
465
|
-
// On failure execFileSync throws with stdout attached to the Error; on
|
|
466
|
-
// success it was previously discarded. Surface the npm output either way so
|
|
467
|
-
// a silently-succeeding-then-rolled-back upgrade leaves a diagnosable trail.
|
|
468
|
-
if (installOutput.trim()) options.log(installOutput.trim());
|
|
305
|
+
});
|
|
469
306
|
},
|
|
470
307
|
restart: (portableNode, stagedCliEntry) => {
|
|
471
308
|
const restartArgv = options.restartArgv.map((arg, index) => index === 0 ? stagedCliEntry : arg);
|
|
@@ -492,55 +329,31 @@ export function createDefaultWindowsAtomicHooks(options: {
|
|
|
492
329
|
child.unref();
|
|
493
330
|
},
|
|
494
331
|
waitForHealth: async (pid, targetVersion) => {
|
|
495
|
-
const
|
|
496
|
-
const deadline = startedAt + healthTimeoutMs;
|
|
497
|
-
let attempt = 0;
|
|
498
|
-
// Log the transition milestones exactly once so the daemon log shows how far
|
|
499
|
-
// the replacement got before the gate resolved: not-yet-alive → alive but
|
|
500
|
-
// version-not-yet-ready (components still booting) → version matched. This is
|
|
501
|
-
// what pins down which boot stage the full-boot budget is being spent in.
|
|
502
|
-
let loggedAlive = false;
|
|
503
|
-
let loggedVersionPending = false;
|
|
332
|
+
const deadline = Date.now() + 30000;
|
|
504
333
|
while (Date.now() < deadline) {
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
if (alive && !loggedAlive) {
|
|
521
|
-
loggedAlive = true;
|
|
522
|
-
options.log(`Health gate: replacement pid ${pid} is alive at ${elapsedMs}ms; awaiting status.version (components still booting)`);
|
|
523
|
-
}
|
|
524
|
-
if (alive && version && version !== targetVersion && !loggedVersionPending) {
|
|
525
|
-
loggedVersionPending = true;
|
|
526
|
-
options.log(`Health gate: replacement reports version ${version} (want ${targetVersion}) at ${elapsedMs}ms`);
|
|
527
|
-
}
|
|
334
|
+
const result = await new Promise<{ ok: boolean; pid?: number; body?: string }>((resolve) => {
|
|
335
|
+
const req = http.get(`http://127.0.0.1:${DEFAULT_HEALTH_PORT}/health`, { timeout: 1500 }, (res) => {
|
|
336
|
+
let body = '';
|
|
337
|
+
res.setEncoding('utf8');
|
|
338
|
+
res.on('data', (chunk) => { body += chunk; });
|
|
339
|
+
res.on('end', () => {
|
|
340
|
+
let responsePid: number | undefined;
|
|
341
|
+
try { responsePid = Number(JSON.parse(body)?.pid); } catch { /* noop */ }
|
|
342
|
+
resolve({ ok: res.statusCode === 200, pid: responsePid, body });
|
|
343
|
+
});
|
|
344
|
+
});
|
|
345
|
+
req.on('timeout', () => req.destroy());
|
|
346
|
+
req.on('error', () => resolve({ ok: false }));
|
|
347
|
+
});
|
|
348
|
+
if (result.ok && result.pid === pid && result.body?.includes(targetVersion)) return true;
|
|
528
349
|
await new Promise((resolve) => setTimeout(resolve, 500));
|
|
529
350
|
}
|
|
530
|
-
options.log(`Health gate timed out after ${Date.now() - startedAt}ms (${attempt} probe(s), budget ${healthTimeoutMs}ms) waiting for pid ${pid} to report ${targetVersion}`);
|
|
531
351
|
return false;
|
|
532
352
|
},
|
|
533
353
|
stopProcess: (pid) => {
|
|
534
354
|
try { execFileSync('taskkill', ['/PID', String(pid), '/T', '/F'], { stdio: 'ignore', windowsHide: true }); } catch { /* noop */ }
|
|
535
355
|
},
|
|
536
|
-
cleanup:
|
|
537
|
-
layout,
|
|
538
|
-
activePrefix,
|
|
539
|
-
excludePids: [process.pid],
|
|
540
|
-
markers: Array.from(ADHDEV_OWNED_MARKERS),
|
|
541
|
-
waitMs: 15_000,
|
|
542
|
-
log: options.log,
|
|
543
|
-
}),
|
|
356
|
+
cleanup: (layout, activePrefix) => boundedCleanupInactivePrefixes(layout, activePrefix, options.log),
|
|
544
357
|
log: options.log,
|
|
545
358
|
};
|
|
546
359
|
}
|
|
@@ -562,70 +375,11 @@ export function boundedCleanupInactivePrefixes(
|
|
|
562
375
|
return;
|
|
563
376
|
}
|
|
564
377
|
if (candidates.length === 0) return;
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
fs.rmSync(candidate, { recursive: true, force: true, maxRetries: 5, retryDelay: 200 });
|
|
573
|
-
} catch {
|
|
574
|
-
incomplete = true;
|
|
575
|
-
}
|
|
576
|
-
}
|
|
577
|
-
if (incomplete) log('Bounded inactive-prefix cleanup was incomplete; future updates will retry');
|
|
578
|
-
}
|
|
579
|
-
|
|
580
|
-
export async function cleanupInactivePrefixesWithGuard(options: {
|
|
581
|
-
layout: WindowsInstallerLayout;
|
|
582
|
-
activePrefix: string;
|
|
583
|
-
excludePids?: number[];
|
|
584
|
-
markers?: readonly string[];
|
|
585
|
-
waitMs?: number;
|
|
586
|
-
log?: (message: string) => void;
|
|
587
|
-
}): Promise<void> {
|
|
588
|
-
const { layout, activePrefix, log } = options;
|
|
589
|
-
let candidates: string[] = [];
|
|
590
|
-
try {
|
|
591
|
-
candidates = fs.readdirSync(layout.installRoot, { withFileTypes: true })
|
|
592
|
-
.filter((entry) => entry.isDirectory() && entry.name.startsWith('version-'))
|
|
593
|
-
.map((entry) => path.join(layout.installRoot, entry.name))
|
|
594
|
-
.filter((entry) => normalizeForCompare(entry) !== normalizeForCompare(activePrefix))
|
|
595
|
-
.sort()
|
|
596
|
-
.slice(0, 8);
|
|
597
|
-
} catch {
|
|
598
|
-
return;
|
|
599
|
-
}
|
|
600
|
-
if (candidates.length === 0) return;
|
|
601
|
-
|
|
602
|
-
for (const candidate of candidates) {
|
|
603
|
-
const stopResult = await stopOwnedProcessesForPrefixes({
|
|
604
|
-
prefixes: [candidate],
|
|
605
|
-
excludePids: options.excludePids,
|
|
606
|
-
markers: options.markers,
|
|
607
|
-
waitMs: options.waitMs ?? 15_000,
|
|
608
|
-
log,
|
|
609
|
-
});
|
|
610
|
-
if (stopResult.survivors.length > 0) {
|
|
611
|
-
log?.(`Skipping cleanup of ${candidate}: ${stopResult.survivors.length} owned process(es) could not be stopped`);
|
|
612
|
-
continue;
|
|
613
|
-
}
|
|
614
|
-
removeInactivePrefix(candidate, log);
|
|
615
|
-
}
|
|
616
|
-
}
|
|
617
|
-
|
|
618
|
-
function removeInactivePrefix(target: string, log?: (message: string) => void): void {
|
|
619
|
-
// Delete in-process with fs.rmSync instead of shelling out to powershell.exe.
|
|
620
|
-
// A version prefix holds thousands of small files (node_modules, node-pty
|
|
621
|
-
// prebuilds); Remove-Item -Recurse -Force over that, plus PowerShell 5.1's
|
|
622
|
-
// cold-start, routinely blew past the old 5000ms spawnSync timeout on Windows
|
|
623
|
-
// — every candidate then failed with ETIMEDOUT and orphan version-* dirs
|
|
624
|
-
// accumulated. fs.rmSync has no process spawn, no timeout, and retries the
|
|
625
|
-
// transient EBUSY/EPERM/ENOTEMPTY that a just-stopped process can leave behind.
|
|
626
|
-
try {
|
|
627
|
-
fs.rmSync(target, { recursive: true, force: true, maxRetries: 5, retryDelay: 200 });
|
|
628
|
-
} catch (error: any) {
|
|
629
|
-
log?.(`Failed to remove inactive prefix ${target}: ${error?.message || String(error)}`);
|
|
630
|
-
}
|
|
378
|
+
const escaped = candidates.map((candidate) => quotePowerShellLiteral(candidate)).join(',');
|
|
379
|
+
const script = `$ErrorActionPreference='Stop'; @(${escaped}) | ForEach-Object { if (Test-Path -LiteralPath $_) { Remove-Item -LiteralPath $_ -Recurse -Force } }`;
|
|
380
|
+
const encoded = Buffer.from(script, 'utf16le').toString('base64');
|
|
381
|
+
const result = spawnSync('powershell.exe', [
|
|
382
|
+
'-NoLogo', '-NoProfile', '-NonInteractive', '-ExecutionPolicy', 'Bypass', '-EncodedCommand', encoded,
|
|
383
|
+
], { timeout: 5000, windowsHide: true, stdio: 'ignore' });
|
|
384
|
+
if (result.error || result.status !== 0) log('Bounded inactive-prefix cleanup was incomplete; future updates will retry');
|
|
631
385
|
}
|
|
@@ -19,18 +19,6 @@
|
|
|
19
19
|
* machine-local policy directly. The repo file shapes the coordinator prompt and
|
|
20
20
|
* operating notes, nothing else.
|
|
21
21
|
*
|
|
22
|
-
* `providerDefaults` IS NOT POLICY EITHER. It is a repo-shared DECLARATIVE
|
|
23
|
-
* "requested auto-approve mode" per providerType — the answer to "WHEN a delegated
|
|
24
|
-
* worker of type X is auto-approved, WHICH mode should it use by default". It has
|
|
25
|
-
* NO say over WHETHER auto-approve is enabled: the ENABLE decision (and the
|
|
26
|
-
* dangerous-mode opt-in) remains 100% machine-local (meshes.json /
|
|
27
|
-
* RepoMeshPolicy). A repo can declare `providerDefaults` freely; a machine that
|
|
28
|
-
* has delegatedWorkerAutoApprove=false still gets no auto-approve, and a dangerous
|
|
29
|
-
* requested mode is still downgraded to PTY parsing unless the machine opts in.
|
|
30
|
-
* The requested mode ID is validated at runtime against the provider spec — an
|
|
31
|
-
* unknown mode ID is IGNORED (fall back to the provider's own default), never
|
|
32
|
-
* silently coerced into a dangerous mode.
|
|
33
|
-
*
|
|
34
22
|
* The coordinator/operatingNotes merge is **in-memory only**: the on-disk
|
|
35
23
|
* machine-local `meshes.json` is never mutated by this module.
|
|
36
24
|
*
|
|
@@ -72,19 +60,6 @@ export interface RepoMeshDeclarativeLimits {
|
|
|
72
60
|
maxNotes?: number;
|
|
73
61
|
}
|
|
74
62
|
|
|
75
|
-
/**
|
|
76
|
-
* Repo-shared declarative per-provider defaults. NOT policy — see the file header.
|
|
77
|
-
* `autoApproveModes` maps a providerType (e.g. "claude-cli") to the auto-approve
|
|
78
|
-
* mode ID that a delegated worker of that type should REQUEST when it is
|
|
79
|
-
* auto-approved. The map only influences WHICH mode is used, never WHETHER
|
|
80
|
-
* auto-approve is enabled (that stays machine-local). Mode IDs are validated
|
|
81
|
-
* against the live provider spec at resolve time; an unknown ID is ignored.
|
|
82
|
-
*/
|
|
83
|
-
export interface RepoMeshDeclarativeProviderDefaults {
|
|
84
|
-
/** providerType → requested auto-approve mode ID. */
|
|
85
|
-
autoApproveModes?: Record<string, string>;
|
|
86
|
-
}
|
|
87
|
-
|
|
88
63
|
/**
|
|
89
64
|
* Parsed + normalized `.adhdev/mesh.json` shape. Every field is optional except
|
|
90
65
|
* version so a repo can declare only the zone(s) it cares about. Policy is NOT a
|
|
@@ -95,8 +70,6 @@ export interface RepoMeshDeclarativeConfig {
|
|
|
95
70
|
coordinator?: RepoMeshDeclarativeCoordinatorConfig;
|
|
96
71
|
operatingNotes?: CoordinatorOperatingNote[];
|
|
97
72
|
limits?: RepoMeshDeclarativeLimits;
|
|
98
|
-
/** Repo-shared per-provider defaults (requested auto-approve mode). NOT policy. */
|
|
99
|
-
providerDefaults?: RepoMeshDeclarativeProviderDefaults;
|
|
100
73
|
}
|
|
101
74
|
|
|
102
75
|
export interface RepoMeshJsonConfigLoadResult {
|
|
@@ -159,20 +132,6 @@ export const MESH_JSON_CONFIG_SCHEMA = {
|
|
|
159
132
|
maxNotes: { type: 'number', minimum: 1 },
|
|
160
133
|
},
|
|
161
134
|
},
|
|
162
|
-
providerDefaults: {
|
|
163
|
-
type: 'object',
|
|
164
|
-
additionalProperties: false,
|
|
165
|
-
properties: {
|
|
166
|
-
// providerType → requested auto-approve mode ID. Mode IDs are
|
|
167
|
-
// validated against the live provider spec at resolve time; an
|
|
168
|
-
// unknown ID is ignored (provider default is used), so the schema
|
|
169
|
-
// only constrains the shape (string→string), not the ID values.
|
|
170
|
-
autoApproveModes: {
|
|
171
|
-
type: 'object',
|
|
172
|
-
additionalProperties: { type: 'string' },
|
|
173
|
-
},
|
|
174
|
-
},
|
|
175
|
-
},
|
|
176
135
|
},
|
|
177
136
|
} as const;
|
|
178
137
|
|
|
@@ -201,14 +160,6 @@ function normalizeOperatingNote(value: unknown): CoordinatorOperatingNote | null
|
|
|
201
160
|
...(category ? { category } : {}),
|
|
202
161
|
...(typeof value.createdAt === 'string' ? { createdAt: value.createdAt } : {}),
|
|
203
162
|
...(typeof value.sourceCoordinator === 'string' ? { sourceCoordinator: value.sourceCoordinator } : {}),
|
|
204
|
-
// Operating-notes lifecycle: a repo-declared note may pin itself or set an
|
|
205
|
-
// explicit expiry, same as a runtime-recorded one. Phase 2 (b)/(c): it may
|
|
206
|
-
// also declare supersedes/subjectKey to retire an earlier note or group
|
|
207
|
-
// same-subject notes for folding.
|
|
208
|
-
...(value.pinned === true ? { pinned: true } : {}),
|
|
209
|
-
...(typeof value.expiresAt === 'string' ? { expiresAt: value.expiresAt } : {}),
|
|
210
|
-
...(typeof value.supersedes === 'string' ? { supersedes: value.supersedes } : {}),
|
|
211
|
-
...(typeof value.subjectKey === 'string' ? { subjectKey: value.subjectKey } : {}),
|
|
212
163
|
};
|
|
213
164
|
}
|
|
214
165
|
|
|
@@ -267,38 +218,6 @@ export function normalizeRepoMeshDeclarativeConfig(parsed: unknown): {
|
|
|
267
218
|
}
|
|
268
219
|
}
|
|
269
220
|
|
|
270
|
-
// providerDefaults: shape-only normalization. We keep every string→string
|
|
271
|
-
// (providerType → requested mode ID) entry after trimming; we deliberately do
|
|
272
|
-
// NOT validate the mode IDs against any provider spec here — the normalizer has
|
|
273
|
-
// no provider registry, and (crucially) validity depends on the LIVE provider
|
|
274
|
-
// at resolve time. The runtime resolver (resolveDelegatedWorkerAutoApprove)
|
|
275
|
-
// checks the requested ID against provider.autoApproveModes.modes and falls
|
|
276
|
-
// back to the provider's own default when the ID is unknown. This keeps the
|
|
277
|
-
// config fail-closed: a stale/typo'd mode ID never coerces into a dangerous
|
|
278
|
-
// mode, it just means "no repo override → provider default".
|
|
279
|
-
if (parsed.providerDefaults !== undefined) {
|
|
280
|
-
if (isRecord(parsed.providerDefaults)) {
|
|
281
|
-
const pd: RepoMeshDeclarativeProviderDefaults = {};
|
|
282
|
-
const rawModes = (parsed.providerDefaults as Record<string, unknown>).autoApproveModes;
|
|
283
|
-
if (rawModes !== undefined) {
|
|
284
|
-
if (isRecord(rawModes)) {
|
|
285
|
-
const modes: Record<string, string> = {};
|
|
286
|
-
for (const [providerType, modeId] of Object.entries(rawModes)) {
|
|
287
|
-
const type = typeof providerType === 'string' ? providerType.trim() : '';
|
|
288
|
-
const id = typeof modeId === 'string' ? modeId.trim() : '';
|
|
289
|
-
if (type && id) modes[type] = id;
|
|
290
|
-
}
|
|
291
|
-
if (Object.keys(modes).length) pd.autoApproveModes = modes;
|
|
292
|
-
} else {
|
|
293
|
-
errors.push('providerDefaults.autoApproveModes must be an object when provided');
|
|
294
|
-
}
|
|
295
|
-
}
|
|
296
|
-
if (Object.keys(pd).length) config.providerDefaults = pd;
|
|
297
|
-
} else {
|
|
298
|
-
errors.push('providerDefaults must be an object when provided');
|
|
299
|
-
}
|
|
300
|
-
}
|
|
301
|
-
|
|
302
221
|
return { valid: true, config, errors };
|
|
303
222
|
}
|
|
304
223
|
|
|
@@ -437,21 +356,6 @@ export function applyRepoMeshConfig<T extends Pick<LocalMeshEntry, 'coordinator'
|
|
|
437
356
|
* exported — it is machine-local only and has no place in mesh.json. Operating
|
|
438
357
|
* notes are intentionally NOT exported either: those are runtime ledger lessons,
|
|
439
358
|
* and a repo should declare baseline notes deliberately.
|
|
440
|
-
*
|
|
441
|
-
* `providerDefaults` is NOT auto-exported from the machine-local mesh (there is no
|
|
442
|
-
* machine-local source for it — it is a repo-authored declaration). To help an
|
|
443
|
-
* operator hand-author it, the scaffold carries a commented example shape:
|
|
444
|
-
*
|
|
445
|
-
* "providerDefaults": {
|
|
446
|
-
* "autoApproveModes": {
|
|
447
|
-
* "claude-cli": "accept-edits", // requested mode WHEN auto-approve is on
|
|
448
|
-
* "codex-cli": "auto" // (enable/dangerous opt-in stays machine-local)
|
|
449
|
-
* }
|
|
450
|
-
* }
|
|
451
|
-
*
|
|
452
|
-
* The example is surfaced via the scaffold's `_providerDefaultsExample` hint (JSON
|
|
453
|
-
* has no comments) rather than a live `providerDefaults` value, so serializing the
|
|
454
|
-
* scaffold never writes an unwanted requested-mode map into the repo file.
|
|
455
359
|
*/
|
|
456
360
|
export function buildMeshJsonConfigScaffold(
|
|
457
361
|
mesh: Pick<LocalMeshEntry, 'coordinator'>,
|
|
@@ -466,21 +370,6 @@ export function buildMeshJsonConfigScaffold(
|
|
|
466
370
|
return scaffold;
|
|
467
371
|
}
|
|
468
372
|
|
|
469
|
-
/**
|
|
470
|
-
* A copy-paste example of the `providerDefaults` zone for operators hand-authoring
|
|
471
|
-
* a repo `.adhdev/mesh.json`. Returned by the export/write commands as a separate
|
|
472
|
-
* hint field (never merged into the serialized scaffold) so the repo file stays
|
|
473
|
-
* clean. The mode IDs shown are illustrative — a repo should use IDs that exist in
|
|
474
|
-
* its own providers' specs; an unknown ID is ignored at resolve time and the
|
|
475
|
-
* provider's own default is used.
|
|
476
|
-
*/
|
|
477
|
-
export const MESH_JSON_PROVIDER_DEFAULTS_EXAMPLE: RepoMeshDeclarativeProviderDefaults = {
|
|
478
|
-
autoApproveModes: {
|
|
479
|
-
'claude-cli': 'accept-edits',
|
|
480
|
-
'codex-cli': 'auto',
|
|
481
|
-
},
|
|
482
|
-
};
|
|
483
|
-
|
|
484
373
|
/** Serialize a scaffold to the canonical 2-space JSON draft text. */
|
|
485
374
|
export function serializeMeshJsonConfigScaffold(config: RepoMeshDeclarativeConfig): string {
|
|
486
375
|
return JSON.stringify(config, null, 2);
|
package/src/index.ts
CHANGED
|
@@ -143,8 +143,6 @@ export type {
|
|
|
143
143
|
export {
|
|
144
144
|
DEFAULT_MESH_POLICY,
|
|
145
145
|
resolveDelegatedWorkerAutoApprove,
|
|
146
|
-
delegatedWorkerAutoApproveSettings,
|
|
147
|
-
resolveDelegatedWorkerDangerousModeAllow,
|
|
148
146
|
resolveAllowSendKeysDestructive,
|
|
149
147
|
resolveMagiSessionCleanupMode,
|
|
150
148
|
magiAutoLaunchedSessionCleanupDecision,
|
|
@@ -163,24 +161,6 @@ export {
|
|
|
163
161
|
resolveAutoConvergeCodeChange,
|
|
164
162
|
} from './repo-mesh-types.js';
|
|
165
163
|
|
|
166
|
-
// ── Repo-shared declarative mesh config (.adhdev/mesh.json) ──
|
|
167
|
-
export {
|
|
168
|
-
loadRepoMeshJsonConfig,
|
|
169
|
-
normalizeRepoMeshDeclarativeConfig,
|
|
170
|
-
buildMeshJsonConfigScaffold,
|
|
171
|
-
serializeMeshJsonConfigScaffold,
|
|
172
|
-
MESH_JSON_CONFIG_LOCATIONS,
|
|
173
|
-
MESH_JSON_CONFIG_SCHEMA,
|
|
174
|
-
MESH_JSON_PROVIDER_DEFAULTS_EXAMPLE,
|
|
175
|
-
} from './config/mesh-json-config.js';
|
|
176
|
-
export type {
|
|
177
|
-
RepoMeshDeclarativeConfig,
|
|
178
|
-
RepoMeshDeclarativeCoordinatorConfig,
|
|
179
|
-
RepoMeshDeclarativeLimits,
|
|
180
|
-
RepoMeshDeclarativeProviderDefaults,
|
|
181
|
-
RepoMeshJsonConfigLoadResult,
|
|
182
|
-
} from './config/mesh-json-config.js';
|
|
183
|
-
|
|
184
164
|
// ── Git Surface ──
|
|
185
165
|
export * from './git/index.js';
|
|
186
166
|
|
|
@@ -509,7 +489,7 @@ export { ProviderInstanceManager } from './providers/provider-instance-manager.j
|
|
|
509
489
|
export { IdeProviderInstance } from './providers/ide-provider-instance.js';
|
|
510
490
|
export { CliProviderInstance } from './providers/cli-provider-instance.js';
|
|
511
491
|
export { AcpProviderInstance } from './providers/acp-provider-instance.js';
|
|
512
|
-
export type { ProviderModule,
|
|
492
|
+
export type { ProviderModule, CdpTargetFilter, ProviderResumeCapability, InputEnvelope, InputPart, MessagePart, ReadChatTurnStatus, ControlListResult, ControlSetResult, ControlInvokeResult } from './providers/contracts.js';
|
|
513
493
|
export type { ProviderSourceConfigSnapshot, ProviderSourceConfigUpdate } from './config/provider-source-config.js';
|
|
514
494
|
export { parseProviderSourceConfigUpdate } from './config/provider-source-config.js';
|
|
515
495
|
export { normalizeInputEnvelope, normalizeMessageParts, flattenMessageParts } from './providers/io-contracts.js';
|