dsh-creator-mode-plus 0.3.8 → 0.3.11

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.
@@ -0,0 +1,95 @@
1
+ /** Private, per-invocation adapter to the application's public profile APIs. */
2
+ import { randomBytes } from 'node:crypto'
3
+ import { readFileSync, realpathSync } from 'node:fs'
4
+ import { join, resolve, sep } from 'node:path'
5
+ import { pathToFileURL } from 'node:url'
6
+ import { stringify } from 'yaml'
7
+
8
+ const PATH = '/dsh-creator-mode-plus/desktop-profile'
9
+ const REGISTRY = Symbol.for('dsh-creator-mode-plus.desktop-profile.v1')
10
+ const ID = /^[a-z][a-z0-9-]*$/
11
+
12
+ export function createDesktopProfileBridge(ctx, dependencies = {}) {
13
+ const profile = ctx.profileContext
14
+ if (profile?.name !== 'desktop') return {}
15
+ const anchorSuffix = '/node_modules/@deepseek-ai/dsh/package.json'
16
+ if (!profile.installAnchor.endsWith(anchorSuffix)) throw new Error('Unrecognized Desktop runtime install anchor')
17
+ const hostRoot = profile.installAnchor.slice(0, -anchorSuffix.length)
18
+ const server = ctx.webServer
19
+ const hostKey = ctx.root ?? server
20
+ globalThis[REGISTRY] ??= new WeakMap()
21
+ let shared = globalThis[REGISTRY].get(hostKey)
22
+ if (!shared) {
23
+ shared = { tickets: new Map(), owners: 0 }
24
+ shared.dispose = server.register({ kind: 'exact', path: PATH, handler: async (req, res) => {
25
+ const ticket = shared.tickets.get(req.headers['x-dshx-profile-ticket'])
26
+ if (req.method !== 'POST' || !ticket || ticket.signal?.aborted || Date.now() > ticket.expires) {
27
+ res.writeHead(403); res.end('desktop profile access expired'); return
28
+ }
29
+ try {
30
+ let bytes = 0; const chunks = []
31
+ for await (const part of req) {
32
+ bytes += part.length
33
+ if (bytes > 4096) throw new Error('profile request too large')
34
+ chunks.push(part)
35
+ }
36
+ const request = JSON.parse(Buffer.concat(chunks).toString())
37
+ const result = await ticket.run(request)
38
+ res.writeHead(200, { 'content-type': 'application/json', 'cache-control': 'no-store' })
39
+ res.end(JSON.stringify(result))
40
+ } catch (error) {
41
+ res.writeHead(400, { 'content-type': 'application/json' })
42
+ res.end(JSON.stringify({ code: 1, stdout: '', stderr: String(error.message ?? error) }))
43
+ }
44
+ } })
45
+ globalThis[REGISTRY].set(hostKey, shared)
46
+ }
47
+ shared.owners++
48
+ const owned = new Set()
49
+ ctx.effect(() => () => {
50
+ for (const token of owned) shared.tickets.delete(token)
51
+ if (--shared.owners === 0) { shared.dispose(); globalThis[REGISTRY].delete(hostKey) }
52
+ })
53
+ return {
54
+ hostProfile: 'desktop', hostPort: server.port,
55
+ hostRoot, hostHome: profile.home,
56
+ createProfileAccess(args, exec, root) {
57
+ const token = randomBytes(32).toString('hex')
58
+ let mutationUsed = false
59
+ const pluginId = args[0] === 'creator' ? args[2] : args[1]
60
+ const mutation = args[0] === 'activate-new-client' ? 'add'
61
+ : args[0] === 'creator' && args[1] === 'remove' ? 'remove' : undefined
62
+ const run = async request => {
63
+ if (request.operation === 'dump') {
64
+ const boot = dependencies.boot ?? await import('@deepseek-ai/dsh-app-boot')
65
+ const rows = boot.composeEntries([boot.readProfilePatches('dshx', profile)])
66
+ // Configuration values and credential references never leave the Host.
67
+ return { code: 0, stderr: '', stdout: stringify(rows.map(({ id, name, disabled }) => ({ id, ...name ? { name } : {}, ...disabled ? { disabled } : {} }))) }
68
+ }
69
+ if (!mutation || mutationUsed || request.operation !== mutation || request.pluginId !== pluginId || !ID.test(pluginId)) throw new Error('desktop profile operation not granted')
70
+ const claims = dependencies.claims ?? await import(pathToFileURL(join(root, 'tools/dshx/src/internal/creator-claims.mjs')).href)
71
+ claims.assertUnfenced(root, exec.agent.id, pluginId)
72
+ if (!claims.listClaims(root).some(row => row.sessionId === exec.agent.id && row.pluginId === pluginId)) throw new Error('desktop profile claim changed')
73
+ const source = realpathSync(join(root, 'my-plugins', pluginId))
74
+ if (source.split(sep).includes('node_modules') || ['apps', 'packages', 'vendor'].some(dir => source === join(root, dir) || source.startsWith(join(root, dir) + sep))) throw new Error('protected plugin source')
75
+ const pkg = JSON.parse(readFileSync(join(source, 'package.json'), 'utf8'))
76
+ if (pkg.name !== pluginId || pkg.dsh?.bundle !== undefined) throw new Error('desktop watched operation requires a matching plain plugin')
77
+ const manifest = JSON.parse(readFileSync(join(profile.dir, 'package.json'), 'utf8'))
78
+ if (manifest.dsh?.profile?.bundles?.includes(pluginId)) throw new Error('desktop bundle removal is external-only')
79
+ const existing = manifest.dependencies?.[pluginId]
80
+ if (existing && (!existing.startsWith('link:') || realpathSync(resolve(profile.dir, existing.slice(5))) !== source)) throw new Error('desktop profile dependency identity changed')
81
+ mutationUsed = true
82
+ const operations = dependencies.operations ?? await import('@deepseek-ai/dsh-plugin-manager/operations')
83
+ const pm = profile.packageManager
84
+ const result = await operations.runPluginCommand({ profile: profile.name, dir: profile.dir, installAnchor: profile.installAnchor, cwd: source, home: profile.home },
85
+ mutation === 'add' ? ['add', `link:${source}`] : ['remove', pluginId],
86
+ { execution: 'service', ...pm, signal: exec.signal, outputBytes: 16384, activateNewBundles: false, lockWaitMs: 30000, idleTimeoutMs: 60000 })
87
+ return { code: result.exitCode, stdout: result.output, stderr: '' }
88
+ }
89
+ shared.tickets.set(token, { run, signal: exec.signal, expires: Date.now() + 120000 })
90
+ owned.add(token)
91
+ return { env: { DSHX_DESKTOP_PROFILE_ACCESS: JSON.stringify({ token, port: server.port, hostPid: process.pid }) },
92
+ dispose() { shared.tickets.delete(token); owned.delete(token) } }
93
+ },
94
+ }
95
+ }
@@ -0,0 +1,237 @@
1
+ /**
2
+ * Private Creator execute/request provenance, NOT execution permission.
3
+ *
4
+ * TRUSTED WIRING REQUIRED: only reviewed C apply code constructs this object
5
+ * from its own four definition objects and keeps the authority private. Only
6
+ * those real execute closures call enter(exec, args). A receives reader only,
7
+ * through reviewed private wiring, not an arbitrary sourceVerified callback.
8
+ * This public factory cannot prove who called it; same-process plugin code is
9
+ * not sandboxed. Tool names merely select the fixed operation vocabulary.
10
+ * Definition/execute identity, native exec/token identity and closure ownership
11
+ * are the premises. No name, callId, JSON marker or allowed-once proves them.
12
+ *
13
+ * Public seams: ctx.root/fiber.assertActive/effect/on; tools.get(name, agent),
14
+ * agents.get and sessions.get; Agent.ctx/session; tools/pre-execute, execute,
15
+ * result/change; agent/disposed and session/disposed. Inject tools/agents/sessions.
16
+ * Native pre signal is the caller signal. Tools may fuse a wrapper/timeout
17
+ * signal before the body. We retain BOTH and construct a request signal from
18
+ * them plus the private owner lifetime; no arbitrary supplied signal is bound.
19
+ *
20
+ * enter is single-use per native token. Owners are frozen empty objects, all
21
+ * state is private. makeApprovalRequest is single-use per owner and binds the
22
+ * exact frozen request, never its callId. inspect/revalidate/reader lookups are
23
+ * live reads, not transferable grants; repeat lookup immediately before use.
24
+ * Native Approval does NOT deduplicate repeated calls with the same request:
25
+ * question deduplication and one-use grant/ticket consumption belong elsewhere.
26
+ * Task/target binding is also a separate private C composition, not this API.
27
+ * Closing dispatch, cancellation, definition replacement or producer disposal
28
+ * invalidates the owner/request. No rules, approvals, execution method, disk,
29
+ * CLI or activation here. Even activate-new-client provenance says NOTHING
30
+ * about sealed-byte support or whether an operation can skip human review.
31
+ */
32
+
33
+ const OPERATIONS = Object.freeze({
34
+ dshx_check: 'check',
35
+ dshx_activation_plan: 'activation-plan',
36
+ dshx_hot_reload: 'hot-reload',
37
+ dshx_activate_new_client: 'activate-new-client',
38
+ })
39
+ const CHANGES = new Set(['patch', 'manifest', 'preset', 'client', 'new-client', 'server', 'artifact'])
40
+ const object = value => value !== null && typeof value === 'object'
41
+ const signal = value => value instanceof AbortSignal
42
+
43
+ export class DevelopmentExecutionError extends Error {
44
+ constructor(code) { super(code); this.name = 'DevelopmentExecutionError'; this.code = code; this.eligible = false }
45
+ }
46
+ const fail = code => { throw new DevelopmentExecutionError(code) }
47
+
48
+ /** ownedDefinitions is a trusted, private Map<fixed tool name, exact definition>. */
49
+ export function createDevelopmentExecutionAuthority(ctx, ownedDefinitions) {
50
+ if (!ctx?.root || typeof ctx.on !== 'function' || typeof ctx.effect !== 'function'
51
+ || typeof ctx.fiber?.assertActive !== 'function' || typeof ctx.tools?.get !== 'function'
52
+ || typeof ctx.agents?.get !== 'function' || typeof ctx.sessions?.get !== 'function') fail('EXECUTION_SERVICES_UNAVAILABLE')
53
+ if (!(ownedDefinitions instanceof Map) || ownedDefinitions.size !== 4) fail('EXECUTION_DEFINITIONS_INVALID')
54
+ const definitions = new Map()
55
+ for (const [name, definition] of ownedDefinitions) {
56
+ if (!Object.hasOwn(OPERATIONS, name) || !object(definition)
57
+ || definition.name !== name || typeof definition.execute !== 'function') fail('EXECUTION_DEFINITIONS_INVALID')
58
+ definitions.set(name, { definition, execute: definition.execute, operation: OPERATIONS[name] })
59
+ }
60
+ const root = ctx.root
61
+ const observations = new WeakMap()
62
+ const owners = new WeakMap()
63
+ const requests = new WeakMap()
64
+ const active = new Set()
65
+ // Native Tools tokens are non-registered Symbols, valid weak keys in the
66
+ // current Host. Remember replay without retaining every completed call.
67
+ const usedTokens = new WeakSet()
68
+ const disposers = []
69
+ let disposed = false
70
+
71
+ function live(agent, session, agentCtx) {
72
+ return object(agent) && object(session) && agent.ctx === agentCtx && agentCtx?.root === root
73
+ && agent.session === session && ctx.agents.get(agent.id) === agent
74
+ && ctx.sessions.get(session.id) === session && agent.id === session.id
75
+ }
76
+ function close(record, code) {
77
+ if (!record || record.closed) return
78
+ record.closed = code
79
+ record.phase = 'closed'
80
+ active.delete(record)
81
+ record.callerSignal.removeEventListener('abort', record.onAbort)
82
+ record.bodySignal?.removeEventListener('abort', record.onAbort)
83
+ record.controller?.abort(new DevelopmentExecutionError(code))
84
+ }
85
+ function current(record) {
86
+ if (disposed) fail('EXECUTION_PRODUCER_DISPOSED')
87
+ try { ctx.fiber.assertActive() } catch { dispose(); fail('EXECUTION_PRODUCER_DISPOSED') }
88
+ if (!record) fail('EXECUTION_UNOBSERVED')
89
+ if (record.closed) fail(record.closed)
90
+ const { exec, definition, execute, agent, session, agentCtx } = record
91
+ let code
92
+ if (record.phase !== 'dispatch') code = 'EXECUTION_NOT_ACTIVE'
93
+ else if (!live(agent, session, agentCtx)) code = 'EXECUTION_AGENT_CHANGED'
94
+ else if (exec.token !== record.token || exec.agent !== agent || exec.name !== record.name
95
+ || exec.callId !== record.callId || exec.rootCallId !== record.rootCallId || exec.parent !== record.parent
96
+ || exec.arguments !== record.args) code = 'EXECUTION_IDENTITY_CHANGED'
97
+ else if (ctx.tools.get(record.name, agent) !== definition || definition.name !== record.name
98
+ || definition.execute !== execute) code = 'EXECUTION_DEFINITION_CHANGED'
99
+ else if (record.callerSignal.aborted || !signal(exec.signal) || exec.signal.aborted
100
+ || record.bodySignal?.aborted || record.controller?.signal.aborted) code = 'EXECUTION_CANCELLED'
101
+ else if (record.bodySignal && exec.signal !== record.bodySignal) code = 'EXECUTION_BODY_SIGNAL_CHANGED'
102
+ if (code) { close(record, code); fail(code) }
103
+ return record
104
+ }
105
+ function owned(owner) {
106
+ const record = object(owner) ? owners.get(owner) : undefined
107
+ if (!record) fail('EXECUTION_OWNER_UNISSUED')
108
+ return current(record)
109
+ }
110
+ function argumentsFor(record, args) {
111
+ if (args !== record.args || !object(args) || Array.isArray(args) || !Object.isFrozen(args)) fail('EXECUTION_ARGUMENTS_INVALID')
112
+ const keys = Reflect.ownKeys(args)
113
+ const expected = record.operation === 'activation-plan' ? ['name', 'change'] : ['name']
114
+ if (keys.length !== expected.length || keys.some(key => !expected.includes(key))) fail('EXECUTION_ARGUMENTS_INVALID')
115
+ for (const key of expected) {
116
+ const descriptor = Object.getOwnPropertyDescriptor(args, key)
117
+ if (!descriptor || !Object.hasOwn(descriptor, 'value') || typeof descriptor.value !== 'string') fail('EXECUTION_ARGUMENTS_INVALID')
118
+ }
119
+ if (!/^[a-z][a-z0-9-]*$/.test(args.name)
120
+ || (record.operation === 'activation-plan' && !CHANGES.has(args.change))) fail('EXECUTION_ARGUMENTS_INVALID')
121
+ return Object.freeze({ operation: record.operation, pluginId: args.name,
122
+ ...(record.operation === 'activation-plan' ? { change: args.change } : {}),
123
+ })
124
+ }
125
+
126
+ function enter(exec, args) {
127
+ const record = current(object(exec) ? observations.get(exec) : undefined)
128
+ if (record.owner || usedTokens.has(record.token)) fail('EXECUTION_ALREADY_ENTERED')
129
+ // Burn the native token even if an attempted closure entry has bad args.
130
+ usedTokens.add(record.token)
131
+ let operation
132
+ try { operation = argumentsFor(record, args) }
133
+ catch (error) { close(record, 'EXECUTION_ARGUMENTS_INVALID'); throw error }
134
+ record.bodySignal = exec.signal
135
+ record.bodySignal.addEventListener('abort', record.onAbort, { once: true })
136
+ record.controller = new AbortController()
137
+ const owner = Object.freeze(Object.create(null))
138
+ record.owner = owner
139
+ record.signal = AbortSignal.any([record.callerSignal, record.bodySignal, record.controller.signal])
140
+ record.view = Object.freeze({
141
+ ...operation, owner, agent: record.agent, session: record.session,
142
+ toolName: record.name, callId: record.callId, rootCallId: record.rootCallId,
143
+ token: record.token, callerSignal: record.callerSignal, bodySignal: record.bodySignal,
144
+ signal: record.signal, evidenceBoundary: 'owned-execution-only-no-authorization',
145
+ })
146
+ owners.set(owner, record)
147
+ current(record)
148
+ return owner
149
+ }
150
+ function inspect(owner) { return owned(owner).view }
151
+ function revalidate(owner) { owned(owner); return owner }
152
+ // Revoke-only private composition: policy/peer loss can cancel the exact
153
+ // already-issued request.signal. It cannot mint a new owner or reopen one.
154
+ function revoke(owner) {
155
+ const record = object(owner) ? owners.get(owner) : undefined
156
+ if (!record) fail('EXECUTION_OWNER_UNISSUED')
157
+ const wasLive = !record.closed
158
+ close(record, 'EXECUTION_REVOKED')
159
+ return wasLive
160
+ }
161
+ function makeApprovalRequest(owner, reason) {
162
+ const record = owned(owner)
163
+ if (record.request) fail('EXECUTION_REQUEST_ALREADY_BOUND')
164
+ if (reason !== undefined && (typeof reason !== 'string' || reason.length > 4096)) fail('EXECUTION_REQUEST_REASON_INVALID')
165
+ const request = Object.freeze({
166
+ agent: record.agent, toolName: record.name, callId: record.callId,
167
+ signal: record.signal, ...(reason === undefined ? {} : { reason }),
168
+ })
169
+ record.request = request
170
+ requests.set(request, record)
171
+ return request
172
+ }
173
+ function lookupRequest(request) {
174
+ const record = object(request) ? requests.get(request) : undefined
175
+ if (!record || record.request !== request) return undefined
176
+ try {
177
+ current(record)
178
+ if (request.agent !== record.agent || request.toolName !== record.name
179
+ || request.callId !== record.callId || request.signal !== record.signal) return undefined
180
+ return record.view
181
+ } catch { return undefined }
182
+ }
183
+ function dispose() {
184
+ if (disposed) return
185
+ disposed = true
186
+ for (const record of [...active]) close(record, 'EXECUTION_PRODUCER_DISPOSED')
187
+ for (const remove of disposers.splice(0).reverse()) remove()
188
+ }
189
+
190
+ try {
191
+ disposers.push(ctx.on('tools/pre-execute', async (exec, next) => {
192
+ const selected = definitions.get(exec.name)
193
+ if (!disposed && selected && object(exec) && !observations.has(exec) && !usedTokens.has(exec.token)
194
+ && typeof exec.token === 'symbol' && Symbol.keyFor(exec.token) === undefined
195
+ && signal(exec.signal) && !exec.signal.aborted
196
+ && live(exec.agent, exec.agent?.session, exec.agent?.ctx)
197
+ && ctx.tools.get(exec.name, exec.agent) === selected.definition
198
+ && selected.definition.execute === selected.execute) {
199
+ const record = {
200
+ ...selected, exec, name: exec.name, token: exec.token, args: exec.arguments,
201
+ callId: exec.callId, rootCallId: exec.rootCallId, parent: exec.parent,
202
+ agent: exec.agent, session: exec.agent.session, agentCtx: exec.agent.ctx,
203
+ callerSignal: exec.signal, phase: 'pre', closed: undefined,
204
+ }
205
+ record.onAbort = () => close(record, 'EXECUTION_CANCELLED')
206
+ exec.signal.addEventListener('abort', record.onAbort, { once: true })
207
+ observations.set(exec, record)
208
+ active.add(record)
209
+ }
210
+ return next() // Observation only: never return allow/ask or alter policy.
211
+ }))
212
+ disposers.push(ctx.on('tools/execute', async (exec, next) => {
213
+ const record = observations.get(exec)
214
+ if (!record || record.closed || record.phase !== 'pre') return next()
215
+ record.phase = 'dispatch'
216
+ try { return await next() }
217
+ finally { close(record, 'EXECUTION_ENDED') }
218
+ }))
219
+ disposers.push(ctx.on('tools/result', exec => close(observations.get(exec), 'EXECUTION_ENDED')))
220
+ disposers.push(ctx.on('tools/change', () => {
221
+ for (const record of [...active]) {
222
+ if (ctx.tools.get(record.name, record.agent) !== record.definition
223
+ || record.definition.execute !== record.execute) close(record, 'EXECUTION_DEFINITION_CHANGED')
224
+ }
225
+ }, { global: true }))
226
+ disposers.push(ctx.on('agent/disposed', ({ agent }) => {
227
+ for (const record of [...active]) if (record.agent === agent) close(record, 'EXECUTION_AGENT_DISPOSED')
228
+ }))
229
+ disposers.push(ctx.on('session/disposed', session => {
230
+ for (const record of [...active]) if (record.session === session) close(record, 'EXECUTION_SESSION_DISPOSED')
231
+ }))
232
+ ctx.effect(() => dispose)
233
+ } catch (error) { dispose(); throw error }
234
+ return Object.freeze({ enter, inspect, revalidate, revoke, makeApprovalRequest, lookupRequest,
235
+ reader: Object.freeze({ lookupRequest }), dispose,
236
+ })
237
+ }
@@ -0,0 +1,267 @@
1
+ /** Private C execute composition. No model/HTTP owner enrollment, grant writer,
2
+ * script runner, argv or automatic approval fallback. Optional creatorExecution
3
+ * is the public Cordis seam; its captured peer is private to one C generation.
4
+ */
5
+ import { types as utilTypes } from 'node:util'
6
+ import { createDevelopmentTargetResolver } from './development-target.js'
7
+ import { DevelopmentTaskError } from './development-tasks.js'
8
+
9
+ const object = value => value !== null && typeof value === 'object'
10
+ const signal = value => value instanceof AbortSignal
11
+ const PROTOCOL = 'creator-owned-execution-v1'
12
+ const SYNC = ['lookupTarget', 'readRegisteredTarget', 'prepare', 'release', 'dispose']
13
+ const METHODS = [...SYNC, 'authorize', 'dispatch']
14
+ export class DevelopmentInvocationError extends Error {
15
+ constructor(code, cause) { super(code, cause ? { cause } : undefined); this.name = 'DevelopmentInvocationError'; this.code = code; this.eligible = false }
16
+ }
17
+ const fail = code => { throw new DevelopmentInvocationError(code) }
18
+ function synchronous(value) {
19
+ if (utilTypes.isPromise(value)) { void Promise.prototype.then.call(value, undefined, () => {}); fail('INVOCATION_ASYNC_PRIVATE_READER') }
20
+ if (typeof value?.then === 'function') fail('INVOCATION_ASYNC_PRIVATE_READER')
21
+ return value
22
+ }
23
+ function waitFor(promise, lifetime) {
24
+ if (lifetime.aborted) { void Promise.resolve(promise).catch(() => {}); return Promise.reject(new DevelopmentInvocationError('INVOCATION_CANCELLED')) }
25
+ return new Promise((resolve, reject) => {
26
+ const abort = () => { detach(); reject(new DevelopmentInvocationError('INVOCATION_CANCELLED')) }
27
+ const detach = () => lifetime.removeEventListener('abort', abort)
28
+ lifetime.addEventListener('abort', abort, { once: true })
29
+ Promise.resolve(promise).then(value => { detach(); resolve(value) }, error => { detach(); reject(error) })
30
+ if (lifetime.aborted) abort()
31
+ })
32
+ }
33
+
34
+ /** Called by real C apply only; exact owner/exec associations never leave here. */
35
+ export function createDevelopmentInvocations(ctx, { authority, tasks, policy, getHarnessRoot } = {}) {
36
+ if (!ctx?.root || typeof ctx.inject !== 'function' || typeof ctx.effect !== 'function' || typeof ctx.on !== 'function'
37
+ || typeof getHarnessRoot !== 'function' || typeof authority?.revoke !== 'function'
38
+ || typeof tasks?.capture !== 'function' || typeof policy?.revalidateFor !== 'function') fail('INVOCATION_SERVICES_UNAVAILABLE')
39
+ const root = ctx.root, producer = new AbortController(), byOwner = new WeakMap(), byExec = new WeakMap(), preBindings = new WeakMap(), opened = new WeakSet(), active = new Set(), connections = new Set()
40
+ let binding, disposed = false
41
+
42
+ function cleanup(frame) {
43
+ // May run again if cancellation occurred synchronously INSIDE prepare and
44
+ // its returned preparation was assigned only afterwards.
45
+ if (frame.preparation && !frame.released) {
46
+ frame.released = true
47
+ try { synchronous(frame.connection.methods.release(frame.owner, frame.preparation)) }
48
+ catch (error) { frame.cleanupError ??= error }
49
+ }
50
+ if (frame.compilation && !frame.inputReleased) {
51
+ frame.inputReleased = true
52
+ frame.targets.releaseCompilation(frame.compilation)
53
+ }
54
+ frame.targets?.dispose()
55
+ }
56
+ function close(frame) {
57
+ if (!frame.closed) {
58
+ frame.closed = true
59
+ active.delete(frame); byOwner.delete(frame.owner); byExec.delete(frame.exec)
60
+ for (const detach of frame.detach.splice(0)) detach()
61
+ authority.revoke(frame.owner) // Never abort the shared Agent/body signal.
62
+ }
63
+ cleanup(frame)
64
+ }
65
+ function watch(frame, lifetime) {
66
+ if (!signal(lifetime) || lifetime.aborted) { close(frame); fail('INVOCATION_CANCELLED') }
67
+ const cancel = () => close(frame)
68
+ lifetime.addEventListener('abort', cancel, { once: true })
69
+ frame.detach.push(() => lifetime.removeEventListener('abort', cancel))
70
+ }
71
+ function connectionLive(connection) {
72
+ if (!connection || !connection.active || disposed || producer.signal.aborted || !connection.binding.active
73
+ || connection.signal.aborted || connection.peer.signal !== connection.signal) fail('INVOCATION_PEER_ENDED')
74
+ connection.binding.scope.fiber.assertActive()
75
+ }
76
+ function check(frame, target = true) {
77
+ if (!frame || frame.closed || disposed || producer.signal.aborted) fail('INVOCATION_CLOSED')
78
+ ctx.fiber.assertActive()
79
+ const source = authority.inspect(frame.owner), mode = policy.inspect(frame.policyHandle)
80
+ // Read-only exact-exec validation consumes no new pre witness. capture
81
+ // itself remains single-use, including for the same real native exec.
82
+ if (policy.revalidateFor(frame.policyHandle, frame.exec) !== frame.policyHandle || source.token !== frame.exec.token
83
+ || source.agent !== frame.exec.agent || source.session !== frame.exec.agent.session
84
+ || source.operation !== frame.operation || source.pluginId !== frame.pluginId
85
+ || mode.policyEpoch !== frame.policyEpoch || mode.route !== frame.route || mode.signal !== frame.policySignal
86
+ || source.signal !== frame.signal || frame.signal.aborted) fail('INVOCATION_BINDING_CHANGED')
87
+ if (frame.task) tasks.revalidate(frame.task, frame.exec)
88
+ if (frame.connection) connectionLive(frame.connection)
89
+ if (target && frame.snapshot) frame.targets.revalidate(frame.snapshot, frame.exec)
90
+ return source
91
+ }
92
+ function inspectOwner(connection, owner) {
93
+ const frame = object(owner) ? byOwner.get(owner) : undefined
94
+ if (!frame || frame.connection !== connection || !frame.request || !frame.snapshot) return undefined
95
+ try {
96
+ check(frame)
97
+ return Object.freeze({ exactNativeRequest: frame.request, binding: frame.snapshot.binding,
98
+ operation: frame.operation, policyEpoch: frame.policyEpoch, signal: frame.signal,
99
+ ...(frame.task ? { taskHandle: frame.task, taskSignal: frame.task.signal } : {}) })
100
+ } catch { close(frame); return undefined }
101
+ }
102
+ function compilationInput(connection, owner, handle) {
103
+ const frame = object(owner) ? byOwner.get(owner) : undefined
104
+ if (!frame || frame.connection !== connection || frame.compilation !== handle || !frame.request) return undefined
105
+ try { check(frame, false); return frame.targets.compilationInput(handle, frame.exec) }
106
+ catch { close(frame); return undefined }
107
+ }
108
+ function endConnection(connection) {
109
+ if (!connection.active) return
110
+ connection.active = false
111
+ connection.signal?.removeEventListener('abort', connection.cancel)
112
+ for (const frame of [...active]) if (frame.connection === connection) close(frame)
113
+ connections.delete(connection)
114
+ try { synchronous(connection.methods?.dispose()) } catch { /* Its signal already prevents reuse. */ }
115
+ }
116
+ function connect(b) {
117
+ if (!b || !b.active || !b.facade || disposed) fail('INVOCATION_EXECUTOR_UNAVAILABLE')
118
+ b.scope.fiber.assertActive()
119
+ if (b.connection?.active && !b.connection.signal.aborted) {
120
+ connectionLive(b.connection)
121
+ return b.connection
122
+ }
123
+ // Reconnection is allowed only while OPENING A NEW invocation. Existing
124
+ // frames and their reader closures retain their original connection.
125
+ const connection = { binding: b, active: true }
126
+ const reader = Object.freeze({ root, signal: producer.signal,
127
+ inspectOwner: owner => inspectOwner(connection, owner),
128
+ compilationInput: (owner, handle) => compilationInput(connection, owner, handle) })
129
+ let peer
130
+ try {
131
+ peer = synchronous(b.connect.call(b.facade, reader))
132
+ if (!object(peer) || !signal(peer.signal) || peer.signal.aborted
133
+ || !Array.isArray(peer.supportedOperations) || peer.supportedOperations.length !== 1
134
+ || peer.supportedOperations[0] !== 'hot-reload') fail('INVOCATION_EXECUTOR_INVALID')
135
+ const methods = Object.create(null)
136
+ for (const name of METHODS) {
137
+ const method = peer[name]
138
+ if (typeof method !== 'function' || (SYNC.includes(name) && utilTypes.isAsyncFunction(method))) fail('INVOCATION_EXECUTOR_INVALID')
139
+ methods[name] = (...args) => method.apply(peer, args)
140
+ }
141
+ Object.assign(connection, { peer, signal: peer.signal, methods, supportedOperations: Object.freeze([...peer.supportedOperations]) })
142
+ connection.cancel = () => endConnection(connection)
143
+ connections.add(connection); b.connection = connection
144
+ connection.signal.addEventListener('abort', connection.cancel, { once: true })
145
+ connectionLive(connection)
146
+ return connection
147
+ } catch (error) {
148
+ connection.active = false
149
+ connections.delete(connection)
150
+ if (peer && typeof peer.dispose === 'function') { try { synchronous(peer.dispose()) } catch {} }
151
+ throw error instanceof DevelopmentInvocationError ? error : new DevelopmentInvocationError('INVOCATION_EXECUTOR_UNAVAILABLE', error)
152
+ }
153
+ }
154
+ const dependency = ctx.inject(['creatorExecution'], scope => {
155
+ if (disposed) return
156
+ const b = { scope, active: true }
157
+ try {
158
+ const facade = scope.creatorExecution
159
+ if (facade?.protocol === PROTOCOL && facade.root === root && typeof facade.connectProducer === 'function'
160
+ && !utilTypes.isAsyncFunction(facade.connectProducer)) Object.assign(b, { facade, connect: facade.connectProducer })
161
+ } catch { /* Invalid optional service closes sealed only, never tool registration. */ }
162
+ binding = b
163
+ scope.effect(() => () => {
164
+ b.active = false
165
+ if (binding === b) binding = undefined
166
+ for (const connection of [...connections]) if (connection.binding === b) endConnection(connection)
167
+ })
168
+ })
169
+ // Observe, but never connect/prepare/request, before an earlier A reviewer
170
+ // unwinds. A body may not borrow a service or existing peer installed while
171
+ // its original native pre was waiting. These are private activation objects,
172
+ // not repeatedly-created Cordis service proxies.
173
+ const stopPre = ctx.on('tools/pre-execute', (exec, next) => {
174
+ if (!disposed && !preBindings.has(exec)) {
175
+ const b = binding, connection = b?.connection
176
+ preBindings.set(exec, { binding: b,
177
+ connection: connection?.active && !connection.signal.aborted ? connection : undefined })
178
+ }
179
+ return next()
180
+ }, { prepend: true })
181
+
182
+ function open(owner, exec, policyHandle) {
183
+ if (disposed || opened.has(exec) || byOwner.has(owner)) fail('INVOCATION_ALREADY_OPENED')
184
+ const source = authority.inspect(owner), mode = policy.inspect(policyHandle)
185
+ if (policy.revalidateFor(policyHandle, exec) !== policyHandle || source.token !== exec.token || source.agent !== exec.agent
186
+ || source.session !== exec.agent.session) fail('INVOCATION_BINDING_CHANGED')
187
+ opened.add(exec)
188
+ const frame = { owner, exec, policyHandle, route: mode.route, operation: source.operation, pluginId: source.pluginId,
189
+ policyEpoch: mode.policyEpoch, policySignal: mode.signal, signal: source.signal, detach: [], phase: 'open', closed: false }
190
+ byOwner.set(owner, frame); byExec.set(exec, frame); active.add(frame)
191
+ try {
192
+ watch(frame, mode.signal); watch(frame, producer.signal); watch(frame, source.signal)
193
+ try { frame.task = tasks.capture(exec) }
194
+ catch (error) { if (!(error instanceof DevelopmentTaskError)) throw error }
195
+ if (frame.task) watch(frame, frame.task.signal)
196
+ if (frame.route === 'sealed') {
197
+ const before = preBindings.get(exec)
198
+ if (!before || !before.binding || before.binding !== binding || !before.binding.active) fail('INVOCATION_PRE_BINDING_ENDED')
199
+ frame.connection = before.connection ?? connect(before.binding)
200
+ connectionLive(frame.connection)
201
+ watch(frame, frame.connection.signal)
202
+ } else if (frame.route !== 'legacy') fail('INVOCATION_POLICY_UNSUPPORTED')
203
+ check(frame, false)
204
+ return Object.freeze({ route: frame.route,
205
+ legacyExecution() {
206
+ check(frame, false)
207
+ if (frame.route !== 'legacy' || frame.phase !== 'open') fail('INVOCATION_LEGACY_FORBIDDEN')
208
+ frame.phase = 'legacy'
209
+ // A signal-replaced exec copy is ONLY an input to the old runner.
210
+ return Object.freeze({ ...exec, signal: frame.signal })
211
+ },
212
+ invokeSealed: () => invokeSealed(frame),
213
+ close() {
214
+ close(frame)
215
+ if (frame.cleanupError) throw new DevelopmentInvocationError('INVOCATION_CLEANUP_FAILED', frame.cleanupError)
216
+ },
217
+ })
218
+ } catch (error) { close(frame); throw error }
219
+ }
220
+ async function invokeSealed(frame) {
221
+ check(frame, false)
222
+ if (frame.route !== 'sealed' || frame.phase !== 'open') fail('INVOCATION_SEALED_REPLAY')
223
+ frame.phase = 'preparing'
224
+ const connection = frame.connection
225
+ if (!connection.supportedOperations.includes(frame.operation)) fail('INVOCATION_OPERATION_UNSUPPORTED')
226
+ frame.lease = synchronous(connection.methods.lookupTarget(frame.pluginId))
227
+ if (!object(frame.lease)) fail('INVOCATION_TARGET_UNREGISTERED')
228
+ check(frame, false)
229
+ frame.targets = createDevelopmentTargetResolver({ getHarnessRoot,
230
+ readRegisteredTarget: lease => {
231
+ check(frame, false)
232
+ if (lease !== frame.lease) return undefined
233
+ return synchronous(connection.methods.readRegisteredTarget(lease))
234
+ },
235
+ readCompilationSignals: original => {
236
+ if (original !== frame.exec || byExec.get(original) !== frame) return undefined
237
+ const source = check(frame, false)
238
+ return { ownerSignal: source.signal, policySignal: frame.policySignal }
239
+ }, producerSignal: producer.signal })
240
+ frame.compilation = frame.targets.captureCompilation(frame.exec, frame.lease)
241
+ frame.snapshot = frame.targets.compilationSnapshot(frame.compilation, frame.exec)
242
+ frame.request = authority.makeApprovalRequest(frame.owner, `Creator ${frame.operation}: ${frame.pluginId}; sealed in-process execution`)
243
+ check(frame)
244
+ frame.preparation = synchronous(connection.methods.prepare(frame.owner, frame.lease, frame.compilation))
245
+ if (!object(frame.preparation)) fail('INVOCATION_PREPARATION_INVALID')
246
+ check(frame)
247
+ frame.phase = 'authorizing'
248
+ const capability = await waitFor(connection.methods.authorize(frame.owner, frame.preparation), frame.signal)
249
+ check(frame)
250
+ if (!object(capability)) fail('INVOCATION_CAPABILITY_INVALID')
251
+ frame.phase = 'dispatching'
252
+ // The fixed peer performs its own final source/policy/capability consume.
253
+ // Its result is an explicit in-process managed receipt, never a CLI claim.
254
+ return await waitFor(connection.methods.dispatch(frame.owner, frame.preparation, capability), frame.signal)
255
+ }
256
+ function dispose() {
257
+ if (disposed) return
258
+ disposed = true
259
+ stopPre()
260
+ producer.abort(new DevelopmentInvocationError('INVOCATION_PRODUCER_DISPOSED'))
261
+ for (const frame of [...active]) close(frame)
262
+ for (const connection of [...connections]) endConnection(connection)
263
+ void Promise.resolve(dependency.dispose()).catch(() => {})
264
+ }
265
+ ctx.effect(() => dispose)
266
+ return Object.freeze({ open, dispose })
267
+ }