claude-flow 3.61.1 → 3.63.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 (37) hide show
  1. package/README.md +1 -1
  2. package/package.json +1 -1
  3. package/v3/@claude-flow/cli/README.md +1 -1
  4. package/v3/@claude-flow/cli/catalog-manifest.json +3 -3
  5. package/v3/@claude-flow/cli/dist/src/mcp-tools/managed-agent-tools.d.ts +1 -20
  6. package/v3/@claude-flow/cli/dist/src/mcp-tools/managed-agent-tools.js +344 -108
  7. package/v3/@claude-flow/cli/dist/src/mcp-tools/managed-learning-tool.d.ts +4 -0
  8. package/v3/@claude-flow/cli/dist/src/mcp-tools/managed-learning-tool.js +46 -0
  9. package/v3/@claude-flow/cli/dist/src/mcp-tools/memory-tools.js +6 -3
  10. package/v3/@claude-flow/cli/dist/src/mcp-tools/rure-tools.js +59 -0
  11. package/v3/@claude-flow/cli/dist/src/missions/schemas.d.ts +12 -12
  12. package/v3/@claude-flow/cli/dist/src/rure/cuda-cubin.d.ts +7 -0
  13. package/v3/@claude-flow/cli/dist/src/rure/cuda-cubin.js +17 -2
  14. package/v3/@claude-flow/cli/dist/src/rure/cuda-occupancy.d.ts +59 -0
  15. package/v3/@claude-flow/cli/dist/src/rure/cuda-occupancy.js +69 -0
  16. package/v3/@claude-flow/cli/dist/src/rure/cuda-plan.d.ts +84 -0
  17. package/v3/@claude-flow/cli/dist/src/rure/cuda-plan.js +197 -0
  18. package/v3/@claude-flow/cli/dist/src/services/headless-worker-executor.js +56 -19
  19. package/v3/@claude-flow/cli/dist/src/services/managed-learning-loop.d.ts +79 -0
  20. package/v3/@claude-flow/cli/dist/src/services/managed-learning-loop.js +76 -0
  21. package/v3/@claude-flow/cli/dist/src/services/managed-learning-store.d.ts +187 -0
  22. package/v3/@claude-flow/cli/dist/src/services/managed-learning-store.js +238 -0
  23. package/v3/@claude-flow/cli/dist/src/services/managed-learning.d.ts +108 -0
  24. package/v3/@claude-flow/cli/dist/src/services/managed-learning.js +197 -0
  25. package/v3/@claude-flow/cli/dist/src/services/managed-memory-client.d.ts +72 -0
  26. package/v3/@claude-flow/cli/dist/src/services/managed-memory-client.js +236 -0
  27. package/v3/@claude-flow/cli/dist/src/services/managed-memory-worker.d.ts +56 -0
  28. package/v3/@claude-flow/cli/dist/src/services/managed-memory-worker.js +161 -0
  29. package/v3/@claude-flow/cli/dist/src/services/managed-run-collector.d.ts +93 -0
  30. package/v3/@claude-flow/cli/dist/src/services/managed-run-collector.js +181 -0
  31. package/v3/@claude-flow/cli/dist/src/services/managed-run-store.d.ts +52 -0
  32. package/v3/@claude-flow/cli/dist/src/services/managed-run-store.js +119 -0
  33. package/v3/@claude-flow/cli/dist/src/services/managed-workflows.d.ts +101 -0
  34. package/v3/@claude-flow/cli/dist/src/services/managed-workflows.js +183 -0
  35. package/v3/@claude-flow/cli/dist/src/services/worker-daemon.d.ts +1 -0
  36. package/v3/@claude-flow/cli/dist/src/services/worker-daemon.js +11 -2
  37. package/v3/@claude-flow/cli/package.json +1 -1
@@ -0,0 +1,181 @@
1
+ /** One explicit, bounded event collector per managed session. No background traffic. */
2
+ import { isDeepStrictEqual } from 'node:util';
3
+ import { eventPage, ManagedProgress } from './managed-workflows.js';
4
+ export const COLLECTOR_LIMITS = { sessions: 6, events: 10_000, bytes: 16_000_000, shownThreads: 60, staleMs: 300_000 };
5
+ const idOf = (v) => typeof v === 'string' && /^[A-Za-z0-9_-]{1,128}$/.test(v) ? v : undefined;
6
+ const record = (v) => v && typeof v === 'object' && !Array.isArray(v) ? v : undefined;
7
+ const clone = (v) => JSON.parse(JSON.stringify(v));
8
+ const cents = (v) => {
9
+ const r = record(v);
10
+ return r && (r.currency === undefined || r.currency === 'USD') && typeof r.amount === 'string' && /^(?:0|[1-9][0-9]{0,8})$/.test(r.amount) ? Number(r.amount) : null;
11
+ };
12
+ const phaseName = (v, fallback) => {
13
+ if (typeof v !== 'string')
14
+ return fallback;
15
+ if (/sk-|ghp_|github_pat_|hf_|npm_|xox|AKIA|AIza|Bearer|PRIVATE KEY|password|secret|credential|[A-Za-z0-9_=-]{24,}|token["']?\s*[:=]/i.test(v))
16
+ return '[redacted phase]';
17
+ return v.replace(/[\u0000-\u001f\u007f-\u009f\u200b-\u200f\u202a-\u202e\u2060-\u206f]/g, '').slice(0, 80) || fallback;
18
+ };
19
+ /** This cache is an observation aid. Cached facts never authorize an action. */
20
+ export class ManagedRunCollector {
21
+ states = new Map();
22
+ now;
23
+ ttlMs;
24
+ constructor(options = {}) {
25
+ this.now = options.now ?? Date.now;
26
+ this.ttlMs = options.ttlMs ?? 1000;
27
+ if (!Number.isFinite(this.ttlMs) || this.ttlMs < 0 || this.ttlMs > 60_000)
28
+ throw new Error('Invalid collector TTL');
29
+ }
30
+ /** Singleflight includes event and session reads; a failed refresh is never cached as success. */
31
+ refresh(sessionId, transport) {
32
+ if (!idOf(sessionId))
33
+ return Promise.reject(new Error('Invalid managed session ID'));
34
+ let state = this.states.get(sessionId);
35
+ if (!state) {
36
+ if (this.states.size >= COLLECTOR_LIMITS.sessions) {
37
+ const oldest = [...this.states.entries()].filter(([, s]) => !s.flight && (this.now() - s.lastUsed >= COLLECTOR_LIMITS.staleMs || ['idle', 'terminated', 'error'].includes(s.result?.snapshot.status ?? 'unknown'))).sort((a, b) => a[1].lastUsed - b[1].lastUsed)[0];
38
+ if (!oldest)
39
+ return Promise.reject(new Error('Managed collector session limit reached'));
40
+ this.states.delete(oldest[0]);
41
+ }
42
+ state = { events: new Map(), bytes: 0, progress: new ManagedProgress(), threads: new Map(), phases: new Map(), blocked: new Set(), session: {}, lastRead: -Infinity, lastUsed: this.now() };
43
+ this.states.set(sessionId, state);
44
+ }
45
+ state.lastUsed = this.now();
46
+ if (state.flight)
47
+ return state.flight.then(clone);
48
+ if (state.result && this.now() - state.lastRead < this.ttlMs)
49
+ return Promise.resolve(clone(state.result));
50
+ const s = state;
51
+ // Start transport in a microtask so reentrant callers also see the flight.
52
+ s.flight = Promise.resolve().then(async () => {
53
+ const [events, session] = await Promise.all([transport.readEvents(s.progress.watermark()), transport.readSession()]);
54
+ if (!record(session))
55
+ throw new Error('Invalid managed session response');
56
+ const validated = eventPage({ data: events }).data;
57
+ const additions = [];
58
+ let bytes = s.bytes, total = s.events.size;
59
+ const batch = new Map();
60
+ for (const input of validated) {
61
+ const e = clone(input), previous = batch.get(e.id) ?? s.events.get(e.id);
62
+ if (previous?.processed_at && !isDeepStrictEqual(previous, e))
63
+ throw new Error('Conflicting managed event replay');
64
+ if (previous && isDeepStrictEqual(previous, e))
65
+ continue;
66
+ bytes += Buffer.byteLength(JSON.stringify(e)) - (previous ? Buffer.byteLength(JSON.stringify(previous)) : 0);
67
+ if (!previous)
68
+ total++;
69
+ if (total > COLLECTOR_LIMITS.events || bytes > COLLECTOR_LIMITS.bytes)
70
+ throw new Error('Managed collector event limit exceeded');
71
+ batch.set(e.id, e);
72
+ }
73
+ for (const e of batch.values())
74
+ additions.push(e);
75
+ s.progress.accept(additions);
76
+ s.bytes = bytes;
77
+ for (const e of additions) {
78
+ s.events.set(e.id, e);
79
+ if (e.processed_at)
80
+ this.observe(s, e);
81
+ }
82
+ s.session = clone(session);
83
+ s.lastRead = this.now();
84
+ const p = s.progress.snapshot();
85
+ const threadCounts = { running: 0, ended: 0, failed: 0, idle: 0 };
86
+ for (const t of s.threads.values()) {
87
+ if (t.status === 'running')
88
+ threadCounts.running++;
89
+ else if (t.status === 'failed')
90
+ threadCounts.failed++;
91
+ else if (t.status === 'ended' || t.status === 'terminated')
92
+ threadCounts.ended++;
93
+ else
94
+ threadCounts.idle++;
95
+ }
96
+ const snapshot = {
97
+ sessionId, status: ['idle', 'running', 'rescheduling', 'terminated', 'error'].includes(session.status ?? '') ? session.status : p.status,
98
+ stopReason: p.stopReason === null || ['end_turn', 'budget_reached', 'requires_action'].includes(p.stopReason) ? p.stopReason : 'unknown', budgetCents: cents(session.budget?.max_list_cost), listCostCents: cents(session.usage?.list_cost) ?? cents(p.listCost),
99
+ providerFinished: p.finished && session.status !== 'running' && session.status !== 'rescheduling' && session.status !== 'terminated',
100
+ workersInspected: false, acceptancePassed: false, verification: 'unverified',
101
+ runs: p.workflowRuns.map(r => ({ ...r, phases: [...(s.phases.get(r.id)?.values() ?? [])] })),
102
+ threads: [...s.threads.values()].slice(0, COLLECTOR_LIMITS.shownThreads), threadOffset: 0, threadCount: s.threads.size, threadCounts,
103
+ blockedEventIds: [...s.blocked], observedFailures: Math.max(p.observedFailures, threadCounts.failed), updatedMs: s.lastRead,
104
+ };
105
+ s.result = { events: [...s.events.values()], snapshot };
106
+ return s.result;
107
+ }).finally(() => { s.flight = undefined; });
108
+ return s.flight.then(clone);
109
+ }
110
+ /** Pure observation, suitable for disk snapshot publication. Never invokes a transport. */
111
+ snapshots() { return [...this.states.values()].flatMap(s => s.result ? [clone(s.result.snapshot)] : []); }
112
+ /** A local worker page only. Aggregate counts and observation timestamp do not change. */
113
+ page(sessionId, threadOffset = 0) {
114
+ if (!Number.isSafeInteger(threadOffset) || threadOffset < 0 || threadOffset >= COLLECTOR_LIMITS.events)
115
+ throw new Error('Invalid managed thread offset');
116
+ const state = this.states.get(sessionId);
117
+ if (!state?.result)
118
+ return undefined;
119
+ return { ...clone(state.result.snapshot), threadOffset, threads: clone([...state.threads.values()].slice(threadOffset, threadOffset + COLLECTOR_LIMITS.shownThreads)) };
120
+ }
121
+ /** A collector doing work cannot be forgotten midflight. No remote work is interrupted. */
122
+ forget(sessionId) {
123
+ const s = this.states.get(sessionId);
124
+ return s?.flight ? false : this.states.delete(sessionId);
125
+ }
126
+ observe(s, e) {
127
+ const runId = idOf(e.workflow_run_id);
128
+ if (runId && e.type === 'workflow_run.created' && Array.isArray(e.phases)) {
129
+ const phases = new Map();
130
+ for (const value of e.phases.slice(0, 100)) {
131
+ const phase = record(value), id = idOf(phase?.id);
132
+ if (id)
133
+ phases.set(id, { id, name: phaseName(phase?.name, id), status: 'queued' });
134
+ }
135
+ s.phases.set(runId, phases);
136
+ }
137
+ if (runId && (e.type === 'workflow_run.phase_started' || e.type === 'workflow_run.phase_ended')) {
138
+ const id = idOf(e.workflow_run_phase_id);
139
+ if (id) {
140
+ const phases = s.phases.get(runId) ?? new Map();
141
+ if (phases.size < 100 || phases.has(id))
142
+ phases.set(id, { id, name: phases.get(id)?.name ?? id, status: e.type.endsWith('started') ? 'running' : 'ended' });
143
+ s.phases.set(runId, phases);
144
+ }
145
+ }
146
+ const threadId = idOf(e.session_thread_id);
147
+ if (threadId && e.type.startsWith('session.thread_')) {
148
+ const t = s.threads.get(threadId) ?? { id: threadId, runId: runId ?? null, status: 'idle' };
149
+ if (runId)
150
+ t.runId = runId;
151
+ const phaseId = idOf(e.workflow_run_phase_id);
152
+ if (phaseId)
153
+ t.phaseId = phaseId;
154
+ if (e.type === 'session.thread_status_running')
155
+ t.status = 'running';
156
+ else if (e.type === 'session.thread_status_idle')
157
+ t.status = 'idle';
158
+ else if (e.type === 'session.thread_status_terminated')
159
+ t.status = e.error || t.status === 'failed' ? 'failed' : 'ended';
160
+ s.threads.set(threadId, t);
161
+ }
162
+ if (threadId && e.type === 'session.error') {
163
+ const t = s.threads.get(threadId) ?? { id: threadId, runId: runId ?? null, status: 'failed' };
164
+ t.status = 'failed';
165
+ s.threads.set(threadId, t);
166
+ }
167
+ const permission = typeof e.evaluated_permission === 'string' ? e.evaluated_permission : record(e.evaluated_permission)?.type;
168
+ if (permission === 'ask' || e.type === 'agent.custom_tool_use')
169
+ s.blocked.add(e.id);
170
+ const answered = idOf(e.tool_use_id) ?? idOf(e.custom_tool_use_id);
171
+ if (answered && ['user.tool_confirmation', 'user.custom_tool_result', 'user.tool_result'].includes(e.type))
172
+ s.blocked.delete(answered);
173
+ const stop = record(e.stop_reason);
174
+ if (stop?.type === 'requires_action' && Array.isArray(stop.event_ids))
175
+ for (const id of stop.event_ids)
176
+ if (idOf(id))
177
+ s.blocked.add(id);
178
+ }
179
+ }
180
+ export const sharedManagedCollector = new ManagedRunCollector();
181
+ //# sourceMappingURL=managed-run-collector.js.map
@@ -0,0 +1,52 @@
1
+ import type { ManagedRunSnapshot } from './managed-run-collector.js';
2
+ /** Other processes fail rather than poll twice. Never automatically steal a lease. */
3
+ export declare function collectWithLease<T>(root: string, sessionId: string, action: () => Promise<T>, scope?: string): Promise<T>;
4
+ export declare function publishManagedSnapshot(root: string, snapshot: ManagedRunSnapshot): void;
5
+ export type ControlAction = 'inspect' | 'interrupt' | 'terminate';
6
+ /** Host settings are read again immediately before side effects, never from console facts. */
7
+ export declare function executeManagedControl(input: Record<string, unknown>, deps: {
8
+ policy: () => {
9
+ enabled: boolean;
10
+ sessions: string[];
11
+ };
12
+ read: (sessionId: string) => Promise<{
13
+ status?: string;
14
+ }>;
15
+ inspect: (sessionId: string, offset: number) => Promise<unknown>;
16
+ interrupt: (sessionId: string) => Promise<unknown>;
17
+ terminate: (sessionId: string) => Promise<unknown>;
18
+ }): Promise<{
19
+ success: boolean;
20
+ currentStatus: string | undefined;
21
+ snapshot: unknown;
22
+ cancellationConfirmed?: undefined;
23
+ interruptRequested?: undefined;
24
+ result?: undefined;
25
+ } | {
26
+ success: boolean;
27
+ currentStatus: string;
28
+ cancellationConfirmed: boolean;
29
+ snapshot?: undefined;
30
+ interruptRequested?: undefined;
31
+ result?: undefined;
32
+ } | {
33
+ success: boolean;
34
+ currentStatus: string | undefined;
35
+ interruptRequested: boolean;
36
+ cancellationConfirmed: boolean;
37
+ result: unknown;
38
+ snapshot?: undefined;
39
+ } | {
40
+ success: boolean;
41
+ currentStatus: string | undefined;
42
+ cancellationConfirmed: boolean;
43
+ result: unknown;
44
+ snapshot?: undefined;
45
+ interruptRequested?: undefined;
46
+ }>;
47
+ /** Successful provider deletion is a session fact, never an acceptance verdict. */
48
+ export declare function publishManagedTermination(root: string, sessionId: string, collector: {
49
+ page: (id: string) => ManagedRunSnapshot | undefined;
50
+ forget: (id: string) => boolean;
51
+ }): void;
52
+ //# sourceMappingURL=managed-run-store.d.ts.map
@@ -0,0 +1,119 @@
1
+ /** Bounded console projections and a fail-closed cross-process collector lease. */
2
+ import { closeSync, constants, existsSync, fsyncSync, lstatSync, mkdirSync, openSync, readSync, renameSync, unlinkSync, writeFileSync } from 'node:fs';
3
+ import { join, resolve } from 'node:path';
4
+ import { randomUUID } from 'node:crypto';
5
+ const flights = new Map();
6
+ function folder(root) {
7
+ let p = resolve(root);
8
+ if (!lstatSync(p).isDirectory())
9
+ throw new Error('Invalid managed project root');
10
+ for (const name of ['.claude-flow', 'managed-workflows']) {
11
+ p = join(p, name);
12
+ if (!existsSync(p))
13
+ mkdirSync(p, { mode: 0o700 });
14
+ if (!lstatSync(p).isDirectory() || lstatSync(p).isSymbolicLink())
15
+ throw new Error('Managed snapshot directory must be regular');
16
+ }
17
+ return p;
18
+ }
19
+ /** Other processes fail rather than poll twice. Never automatically steal a lease. */
20
+ export function collectWithLease(root, sessionId, action, scope = '') {
21
+ if (!/^sesn_[A-Za-z0-9]+$/.test(sessionId))
22
+ return Promise.reject(new Error('Invalid managed session ID'));
23
+ const path = join(folder(root), `${sessionId}.collector.lock`);
24
+ const pending = flights.get(path);
25
+ if (pending)
26
+ return pending.scope === scope ? pending.promise.then(v => structuredClone(v)) : Promise.reject(new Error('Managed collector belongs to another credential scope'));
27
+ let fd;
28
+ try {
29
+ fd = openSync(path, 'wx', 0o600);
30
+ }
31
+ catch {
32
+ return Promise.reject(new Error('Managed collector is leased by another process; inspect before recovering a stale lease'));
33
+ }
34
+ const flight = Promise.resolve().then(action).finally(() => { closeSync(fd); unlinkSync(path); flights.delete(path); });
35
+ flights.set(path, { scope, promise: flight });
36
+ return flight.then(v => structuredClone(v));
37
+ }
38
+ /** Read only the fixed, capped projection. It has no authorization role. */
39
+ function previous(path) {
40
+ if (!existsSync(path))
41
+ return [];
42
+ if (!lstatSync(path).isFile() || lstatSync(path).isSymbolicLink())
43
+ throw new Error('Invalid managed snapshot file');
44
+ const fd = openSync(path, constants.O_RDONLY | (constants.O_NOFOLLOW ?? 0));
45
+ try {
46
+ const buffer = Buffer.alloc(128_001);
47
+ const count = readSync(fd, buffer, 0, buffer.length, 0);
48
+ if (count > 128_000)
49
+ throw new Error('Managed snapshot exceeds limit');
50
+ const value = JSON.parse(buffer.subarray(0, count).toString('utf8'));
51
+ if (value.version !== 1 || !Array.isArray(value.sessions) || value.sessions.length > 6)
52
+ throw new Error('Invalid managed snapshot');
53
+ return value.sessions;
54
+ }
55
+ finally {
56
+ closeSync(fd);
57
+ }
58
+ }
59
+ export function publishManagedSnapshot(root, snapshot) {
60
+ const dir = folder(root), lockPath = join(dir, 'snapshot.lock');
61
+ const fd = openSync(lockPath, 'wx', 0o600), temp = join(dir, `${randomUUID()}.tmp`);
62
+ try {
63
+ const path = join(dir, 'snapshot.json');
64
+ const old = previous(path);
65
+ const sessions = [snapshot, ...old.filter(s => s.sessionId !== snapshot.sessionId)].slice(0, 6);
66
+ if (JSON.stringify(sessions) === JSON.stringify(old))
67
+ return;
68
+ const text = JSON.stringify({ version: 1, updatedMs: Date.now(), sessions });
69
+ if (Buffer.byteLength(text) > 128_000)
70
+ throw new Error('Managed snapshot exceeds limit');
71
+ const out = openSync(temp, 'wx', 0o600);
72
+ try {
73
+ writeFileSync(out, text);
74
+ fsyncSync(out);
75
+ }
76
+ finally {
77
+ closeSync(out);
78
+ }
79
+ renameSync(temp, path);
80
+ }
81
+ finally {
82
+ if (existsSync(temp))
83
+ unlinkSync(temp);
84
+ closeSync(fd);
85
+ unlinkSync(lockPath);
86
+ }
87
+ }
88
+ /** Host settings are read again immediately before side effects, never from console facts. */
89
+ export async function executeManagedControl(input, deps) {
90
+ const sessionId = input.sessionId, action = input.action;
91
+ if (Object.keys(input).some(k => !['sessionId', 'action', 'threadOffset'].includes(k)) || typeof sessionId !== 'string' || !/^sesn_[A-Za-z0-9]+$/.test(sessionId) || !['inspect', 'interrupt', 'terminate'].includes(String(action)))
92
+ throw new Error('Invalid managed control request');
93
+ const offset = input.threadOffset ?? 0;
94
+ if (!Number.isSafeInteger(offset) || offset < 0 || offset >= 10_000 || (action !== 'inspect' && input.threadOffset !== undefined))
95
+ throw new Error('Invalid managed worker page');
96
+ const allowed = () => { const p = deps.policy(); return p.enabled && p.sessions.includes(sessionId); };
97
+ if (!allowed())
98
+ throw new Error('Host managed control policy denies this session');
99
+ const current = await deps.read(sessionId);
100
+ if (!current || !['idle', 'running', 'rescheduling', 'terminated'].includes(current.status ?? ''))
101
+ throw new Error('Unknown provider session state');
102
+ if (!allowed())
103
+ throw new Error('Host managed control policy changed before dispatch');
104
+ if (action === 'inspect')
105
+ return { success: true, currentStatus: current.status, snapshot: await deps.inspect(sessionId, offset) };
106
+ if (current.status === 'terminated')
107
+ return { success: true, currentStatus: 'terminated', cancellationConfirmed: true };
108
+ if (action === 'interrupt')
109
+ return { success: true, currentStatus: current.status, interruptRequested: true, cancellationConfirmed: false, result: await deps.interrupt(sessionId) };
110
+ return { success: true, currentStatus: current.status, cancellationConfirmed: true, result: await deps.terminate(sessionId) };
111
+ }
112
+ /** Successful provider deletion is a session fact, never an acceptance verdict. */
113
+ export function publishManagedTermination(root, sessionId, collector) {
114
+ const snapshot = collector.page(sessionId);
115
+ if (snapshot)
116
+ publishManagedSnapshot(root, { ...snapshot, status: 'terminated', providerFinished: false, updatedMs: Date.now() });
117
+ collector.forget(sessionId);
118
+ }
119
+ //# sourceMappingURL=managed-run-store.js.map
@@ -0,0 +1,101 @@
1
+ /** Provider event semantics for ADR-115 / dynamic workflows. No model calls. */
2
+ export interface ManagedEvent {
3
+ id: string;
4
+ type: string;
5
+ processed_at?: string | null;
6
+ content?: Array<{
7
+ type: string;
8
+ text?: string;
9
+ }>;
10
+ name?: string;
11
+ input?: unknown;
12
+ stop_reason?: {
13
+ type: string;
14
+ };
15
+ workflow_run_id?: string | null;
16
+ session_thread_id?: string;
17
+ result?: {
18
+ type: string;
19
+ } | string;
20
+ [key: string]: unknown;
21
+ }
22
+ export interface EventPage {
23
+ data: ManagedEvent[];
24
+ next_page?: string | null;
25
+ }
26
+ /** Validate untrusted pages before deriving lifecycle claims. */
27
+ export declare function eventPage(value: unknown): EventPage;
28
+ /** Reads complete history, bounded and deduplicated. Cursor is opaque, never a URL. */
29
+ export declare function readManagedEvents(read: (query: string) => Promise<unknown>, since?: string): Promise<ManagedEvent[]>;
30
+ /** Incremental reducer. Provider completion is always unverified. */
31
+ export declare class ManagedProgress {
32
+ private seen;
33
+ private text;
34
+ private tools;
35
+ private runs;
36
+ private failures;
37
+ private status;
38
+ private stopReason;
39
+ private cost;
40
+ private since;
41
+ private bytes;
42
+ private finished;
43
+ private interrupted;
44
+ accept(events: ManagedEvent[]): void;
45
+ watermark(): string | undefined;
46
+ snapshot(): {
47
+ assistantText: string;
48
+ toolUses: {
49
+ name: string;
50
+ input: unknown;
51
+ }[];
52
+ status: string;
53
+ stopReason: string | null;
54
+ eventCount: number;
55
+ workflowRuns: {
56
+ id: string;
57
+ status: string;
58
+ result?: string;
59
+ }[];
60
+ openRuns: number;
61
+ finished: boolean;
62
+ interrupted: boolean;
63
+ blocked: boolean;
64
+ observedFailures: number;
65
+ listCost: unknown;
66
+ verification: "unverified";
67
+ needsThreadInspection: boolean;
68
+ };
69
+ }
70
+ /** Build only the supported configuration; do not pass arbitrary multiagent JSON. */
71
+ export declare function dynamicConfig(input: Record<string, unknown>): {
72
+ enabled: boolean;
73
+ budget: {
74
+ type: string;
75
+ max_list_cost: {
76
+ amount: string;
77
+ currency: string;
78
+ };
79
+ } | undefined;
80
+ multiagent: {
81
+ type: string;
82
+ subagents: {
83
+ type: string;
84
+ };
85
+ workflows: {
86
+ inline_agents?: {
87
+ type: string;
88
+ } | undefined;
89
+ predefined_agents?: any[] | undefined;
90
+ type: string;
91
+ };
92
+ } | undefined;
93
+ networking: {
94
+ type: string;
95
+ allowed_hosts: any[];
96
+ } | {
97
+ type: {};
98
+ allowed_hosts?: undefined;
99
+ };
100
+ };
101
+ //# sourceMappingURL=managed-workflows.d.ts.map
@@ -0,0 +1,183 @@
1
+ /** Validate untrusted pages before deriving lifecycle claims. */
2
+ export function eventPage(value) {
3
+ if (!value || typeof value !== 'object')
4
+ throw new Error('Invalid Managed Agents event page');
5
+ const page = value;
6
+ if (!Array.isArray(page.data) || page.data.some(e => !e || typeof e.id !== 'string' || !e.id || typeof e.type !== 'string')) {
7
+ throw new Error('Invalid Managed Agents events');
8
+ }
9
+ for (const e of page.data) {
10
+ if (e.processed_at != null && (typeof e.processed_at !== 'string' || !Number.isFinite(Date.parse(e.processed_at))))
11
+ throw new Error('Invalid event timestamp');
12
+ if (e.type === 'agent.message' && e.content !== undefined && (!Array.isArray(e.content) || e.content.some(b => !b || typeof b.type !== 'string' || (b.text !== undefined && typeof b.text !== 'string'))))
13
+ throw new Error('Invalid event content');
14
+ if (e.stop_reason != null && typeof e.stop_reason.type !== 'string')
15
+ throw new Error('Invalid event stop reason');
16
+ if (e.type === 'workflow_run.status_ended' && (!e.result || typeof e.result !== 'object' || typeof e.result.type !== 'string'))
17
+ throw new Error('Invalid workflow result');
18
+ if (e.type.startsWith('workflow_run.') && e.type !== 'workflow_run.error' && (typeof e.workflow_run_id !== 'string' || !e.workflow_run_id))
19
+ throw new Error('Invalid workflow ID');
20
+ if (e.workflow_run_id != null && typeof e.workflow_run_id !== 'string')
21
+ throw new Error('Invalid workflow ID');
22
+ }
23
+ if (page.next_page != null && (typeof page.next_page !== 'string' || !page.next_page || page.next_page.length > 4096)) {
24
+ throw new Error('Invalid Managed Agents cursor');
25
+ }
26
+ return page;
27
+ }
28
+ /** Reads complete history, bounded and deduplicated. Cursor is opaque, never a URL. */
29
+ export async function readManagedEvents(read, since) {
30
+ const events = new Map();
31
+ const cursors = new Set();
32
+ let bytes = 0;
33
+ let cursor;
34
+ for (let count = 0; count < 100; count++) {
35
+ const query = new URLSearchParams({ order: 'asc', limit: '100' });
36
+ if (cursor)
37
+ query.set('page', cursor);
38
+ if (since)
39
+ query.set('created_at[gte]', since);
40
+ const page = eventPage(await read(query.toString()));
41
+ bytes += Buffer.byteLength(JSON.stringify(page), 'utf8');
42
+ if (bytes > 16_000_000)
43
+ throw new Error('Managed Agents transcript limit exceeded');
44
+ for (const e of page.data)
45
+ events.set(e.id, e);
46
+ if (events.size > 10_000)
47
+ throw new Error('Managed Agents event limit exceeded');
48
+ if (!page.next_page)
49
+ return [...events.values()];
50
+ if (cursors.has(page.next_page))
51
+ throw new Error('Repeated Managed Agents cursor');
52
+ cursors.add(page.next_page);
53
+ cursor = page.next_page;
54
+ }
55
+ throw new Error('Managed Agents page limit exceeded');
56
+ }
57
+ /** Incremental reducer. Provider completion is always unverified. */
58
+ export class ManagedProgress {
59
+ seen = new Set();
60
+ text = [];
61
+ tools = [];
62
+ runs = new Map();
63
+ failures = 0;
64
+ status = 'unknown';
65
+ stopReason = null;
66
+ cost = null;
67
+ since;
68
+ bytes = 0;
69
+ finished = false;
70
+ interrupted = false;
71
+ accept(events) {
72
+ for (const e of events) {
73
+ // Queued user messages may be returned again after processing.
74
+ if (!e.processed_at)
75
+ continue;
76
+ if (this.seen.has(e.id))
77
+ continue;
78
+ if (this.seen.size >= 10_000)
79
+ throw new Error('Managed Agents event limit exceeded');
80
+ this.bytes += Buffer.byteLength(JSON.stringify(e), 'utf8');
81
+ if (this.bytes > 16_000_000)
82
+ throw new Error('Managed Agents transcript limit exceeded');
83
+ this.seen.add(e.id);
84
+ if (!this.since || e.processed_at > this.since)
85
+ this.since = e.processed_at;
86
+ if (e.type === 'user.message') {
87
+ this.finished = false;
88
+ this.interrupted = false;
89
+ }
90
+ else if (e.type === 'user.interrupt') {
91
+ this.finished = false;
92
+ this.interrupted = true;
93
+ }
94
+ else if (e.type === 'agent.message' && Array.isArray(e.content)) {
95
+ this.text.push(e.content.filter(b => b?.type === 'text' && typeof b.text === 'string').map(b => b.text).join(''));
96
+ }
97
+ else if (e.type === 'agent.tool_use' || e.type === 'agent.mcp_tool_use') {
98
+ this.tools.push({ name: e.name ?? '?', input: e.input });
99
+ }
100
+ else if (e.type === 'session.status_running') {
101
+ this.status = 'running';
102
+ this.stopReason = null;
103
+ this.finished = false;
104
+ }
105
+ else if (e.type === 'session.status_idle') {
106
+ this.status = 'idle';
107
+ this.stopReason = e.stop_reason?.type ?? null;
108
+ this.finished = this.stopReason === 'end_turn' && !this.interrupted && [...this.runs.values()].every(r => r.status === 'ended');
109
+ }
110
+ else if (e.type === 'session.status_terminated') {
111
+ this.status = 'terminated';
112
+ this.finished = false;
113
+ }
114
+ else if (e.type === 'session.error' || e.type === 'session.status_error' || e.type === 'session.status_failed') {
115
+ this.failures++;
116
+ }
117
+ else if (e.type === 'session.usage') {
118
+ this.cost = e.usage && typeof e.usage === 'object' ? e.usage.list_cost ?? null : null;
119
+ }
120
+ if (e.type === 'workflow_run.error')
121
+ this.failures++;
122
+ if (e.workflow_run_id && e.type.startsWith('workflow_run.')) {
123
+ this.finished = false;
124
+ const run = this.runs.get(e.workflow_run_id) ?? { id: e.workflow_run_id, status: 'running' };
125
+ if (e.type === 'workflow_run.status_ended') {
126
+ run.status = 'ended';
127
+ run.result = typeof e.result === 'object' ? e.result.type : 'unknown';
128
+ if (run.result !== 'completed')
129
+ this.failures++;
130
+ }
131
+ else if (e.type === 'workflow_run.status_idle' && run.status !== 'ended')
132
+ run.status = 'idle';
133
+ else if (e.type === 'workflow_run.status_running' && run.status !== 'ended')
134
+ run.status = 'running';
135
+ this.runs.set(run.id, run);
136
+ }
137
+ }
138
+ }
139
+ watermark() { return this.since; }
140
+ snapshot() {
141
+ const workflowRuns = [...this.runs.values()].map(r => ({ ...r }));
142
+ const openRuns = workflowRuns.filter(r => r.status !== 'ended').length;
143
+ const blocked = this.status === 'idle' && ['budget_reached', 'requires_action'].includes(this.stopReason ?? '');
144
+ return {
145
+ assistantText: this.text.join(''), toolUses: [...this.tools], status: this.status,
146
+ stopReason: this.stopReason, eventCount: this.seen.size, workflowRuns, openRuns,
147
+ finished: this.finished, interrupted: this.interrupted, blocked, observedFailures: this.failures, listCost: this.cost,
148
+ verification: 'unverified',
149
+ needsThreadInspection: workflowRuns.length > 0,
150
+ };
151
+ }
152
+ }
153
+ /** Build only the supported configuration; do not pass arbitrary multiagent JSON. */
154
+ export function dynamicConfig(input) {
155
+ if (input.dynamicWorkflows !== undefined && typeof input.dynamicWorkflows !== 'boolean')
156
+ throw new Error('dynamicWorkflows must be boolean');
157
+ const enabled = input.dynamicWorkflows === true;
158
+ if (input.budgetCents !== undefined || enabled) {
159
+ if (typeof input.budgetCents !== 'string' || !/^[1-9][0-9]{0,8}$/.test(input.budgetCents)) {
160
+ throw new Error('budgetCents must be a positive integer cents string (maximum 999999999)');
161
+ }
162
+ }
163
+ if (enabled && input.networking !== undefined && input.networking !== 'limited')
164
+ throw new Error('Dynamic workflows require limited networking');
165
+ const hosts = input.allowedHosts ?? [];
166
+ if (!Array.isArray(hosts) || hosts.length > 100 || hosts.some(h => typeof h !== 'string' || h.length > 253 || !/^[a-z0-9]+(?:[.-][a-z0-9]+)*$/i.test(h))) {
167
+ throw new Error('allowedHosts must contain at most 100 exact hostnames');
168
+ }
169
+ const agents = input.predefinedAgents;
170
+ if (agents !== undefined && (!Array.isArray(agents) || agents.length < 1 || agents.length > 20 || agents.some(a => typeof a !== 'string' || !/^agent_[A-Za-z0-9]+$/.test(a)))) {
171
+ throw new Error('predefinedAgents must contain 1 to 20 agent IDs');
172
+ }
173
+ return {
174
+ enabled,
175
+ budget: input.budgetCents === undefined ? undefined : { type: 'limit', max_list_cost: { amount: input.budgetCents, currency: 'USD' } },
176
+ multiagent: enabled ? {
177
+ type: 'multiagent_20261001', subagents: { type: 'disabled' },
178
+ workflows: { type: 'enabled', ...(agents ? { inline_agents: { type: 'disabled' }, predefined_agents: agents } : {}) },
179
+ } : undefined,
180
+ networking: enabled || input.networking === 'limited' ? { type: 'limited', allowed_hosts: hosts } : { type: input.networking ?? 'unrestricted' },
181
+ };
182
+ }
183
+ //# sourceMappingURL=managed-workflows.js.map
@@ -90,6 +90,7 @@ export declare class WorkerDaemon extends EventEmitter {
90
90
  private headlessInitPromise;
91
91
  private originalConfig?;
92
92
  private fileConfigResourceThresholds?;
93
+ private fileConfigScalars?;
93
94
  private gitIdentity;
94
95
  private lastSupervisorRenewalMs;
95
96
  constructor(projectRoot: string, config?: Partial<DaemonConfig>);