@corenel/harness 0.10.0 → 0.12.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.
- package/dist/approvals/decisionLog.d.ts +21 -0
- package/dist/approvals/decisionLog.d.ts.map +1 -0
- package/dist/approvals/decisionLog.js +129 -0
- package/dist/approvals/decisionLog.js.map +1 -0
- package/dist/approvals/hooks.d.ts +18 -0
- package/dist/approvals/hooks.d.ts.map +1 -0
- package/dist/approvals/hooks.js +34 -0
- package/dist/approvals/hooks.js.map +1 -0
- package/dist/approvals/index.d.ts +10 -0
- package/dist/approvals/index.d.ts.map +1 -0
- package/dist/approvals/index.js +10 -0
- package/dist/approvals/index.js.map +1 -0
- package/dist/approvals/service.d.ts +46 -0
- package/dist/approvals/service.d.ts.map +1 -0
- package/dist/approvals/service.js +181 -0
- package/dist/approvals/service.js.map +1 -0
- package/dist/approvals/settings.d.ts +20 -0
- package/dist/approvals/settings.d.ts.map +1 -0
- package/dist/approvals/settings.js +32 -0
- package/dist/approvals/settings.js.map +1 -0
- package/dist/approvals/shellTools.d.ts +10 -0
- package/dist/approvals/shellTools.d.ts.map +1 -0
- package/dist/approvals/shellTools.js +17 -0
- package/dist/approvals/shellTools.js.map +1 -0
- package/dist/approvals/standingStore.d.ts +15 -0
- package/dist/approvals/standingStore.d.ts.map +1 -0
- package/dist/approvals/standingStore.js +50 -0
- package/dist/approvals/standingStore.js.map +1 -0
- package/dist/approvals/suggest.d.ts +15 -0
- package/dist/approvals/suggest.d.ts.map +1 -0
- package/dist/approvals/suggest.js +23 -0
- package/dist/approvals/suggest.js.map +1 -0
- package/dist/approvals/summarize.d.ts +7 -0
- package/dist/approvals/summarize.d.ts.map +1 -0
- package/dist/approvals/summarize.js +70 -0
- package/dist/approvals/summarize.js.map +1 -0
- package/dist/approvals/testTextStore.d.ts +6 -0
- package/dist/approvals/testTextStore.d.ts.map +1 -0
- package/dist/approvals/testTextStore.js +11 -0
- package/dist/approvals/testTextStore.js.map +1 -0
- package/dist/approvals/textStore.d.ts +9 -0
- package/dist/approvals/textStore.d.ts.map +1 -0
- package/dist/approvals/textStore.js +2 -0
- package/dist/approvals/textStore.js.map +1 -0
- package/dist/audit/canonical.d.ts +5 -0
- package/dist/audit/canonical.d.ts.map +1 -0
- package/dist/audit/canonical.js +15 -0
- package/dist/audit/canonical.js.map +1 -0
- package/dist/audit/ids.d.ts +3 -0
- package/dist/audit/ids.d.ts.map +1 -0
- package/dist/audit/ids.js +11 -0
- package/dist/audit/ids.js.map +1 -0
- package/dist/audit/index.d.ts +7 -0
- package/dist/audit/index.d.ts.map +1 -0
- package/dist/audit/index.js +7 -0
- package/dist/audit/index.js.map +1 -0
- package/dist/audit/log.d.ts +64 -0
- package/dist/audit/log.d.ts.map +1 -0
- package/dist/audit/log.js +457 -0
- package/dist/audit/log.js.map +1 -0
- package/dist/audit/recorder.d.ts +31 -0
- package/dist/audit/recorder.d.ts.map +1 -0
- package/dist/audit/recorder.js +185 -0
- package/dist/audit/recorder.js.map +1 -0
- package/dist/audit/runAudit.d.ts +38 -0
- package/dist/audit/runAudit.d.ts.map +1 -0
- package/dist/audit/runAudit.js +99 -0
- package/dist/audit/runAudit.js.map +1 -0
- package/dist/audit/verify.d.ts +14 -0
- package/dist/audit/verify.d.ts.map +1 -0
- package/dist/audit/verify.js +47 -0
- package/dist/audit/verify.js.map +1 -0
- package/dist/conversation.d.ts +31 -0
- package/dist/conversation.d.ts.map +1 -1
- package/dist/conversation.js +33 -0
- package/dist/conversation.js.map +1 -1
- package/dist/core/hash.d.ts +2 -0
- package/dist/core/hash.d.ts.map +1 -0
- package/dist/core/hash.js +9 -0
- package/dist/core/hash.js.map +1 -0
- package/dist/core/loop.d.ts +18 -0
- package/dist/core/loop.d.ts.map +1 -1
- package/dist/core/loop.js +67 -6
- package/dist/core/loop.js.map +1 -1
- package/dist/core/spawn.d.ts +59 -4
- package/dist/core/spawn.d.ts.map +1 -1
- package/dist/core/spawn.js +83 -5
- package/dist/core/spawn.js.map +1 -1
- package/dist/core/turnVerify.d.ts +25 -0
- package/dist/core/turnVerify.d.ts.map +1 -0
- package/dist/core/turnVerify.js +177 -0
- package/dist/core/turnVerify.js.map +1 -0
- package/dist/dag/executeNode.d.ts +7 -0
- package/dist/dag/executeNode.d.ts.map +1 -1
- package/dist/dag/executeNode.js +3 -0
- package/dist/dag/executeNode.js.map +1 -1
- package/dist/guardrail/guardrail.d.ts +7 -0
- package/dist/guardrail/guardrail.d.ts.map +1 -1
- package/dist/guardrail/guardrail.js +8 -0
- package/dist/guardrail/guardrail.js.map +1 -1
- package/dist/guardrail/index.d.ts +2 -2
- package/dist/guardrail/index.d.ts.map +1 -1
- package/dist/guardrail/index.js +2 -2
- package/dist/guardrail/index.js.map +1 -1
- package/dist/guardrail/permission-service.d.ts +41 -6
- package/dist/guardrail/permission-service.d.ts.map +1 -1
- package/dist/guardrail/permission-service.js +118 -11
- package/dist/guardrail/permission-service.js.map +1 -1
- package/dist/guardrail/serialize.d.ts.map +1 -1
- package/dist/guardrail/serialize.js +2 -0
- package/dist/guardrail/serialize.js.map +1 -1
- package/dist/guardrail/validate.d.ts.map +1 -1
- package/dist/guardrail/validate.js +3 -1
- package/dist/guardrail/validate.js.map +1 -1
- package/dist/install/lockfile.d.ts +13 -5
- package/dist/install/lockfile.d.ts.map +1 -1
- package/dist/install/lockfile.js +31 -2
- package/dist/install/lockfile.js.map +1 -1
- package/dist/orchestrator/agentSearch.d.ts +15 -0
- package/dist/orchestrator/agentSearch.d.ts.map +1 -0
- package/dist/orchestrator/agentSearch.js +112 -0
- package/dist/orchestrator/agentSearch.js.map +1 -0
- package/dist/orchestrator/agentTags.d.ts +13 -0
- package/dist/orchestrator/agentTags.d.ts.map +1 -0
- package/dist/orchestrator/agentTags.js +48 -0
- package/dist/orchestrator/agentTags.js.map +1 -0
- package/dist/orchestrator/branch.d.ts +9 -0
- package/dist/orchestrator/branch.d.ts.map +1 -0
- package/dist/orchestrator/branch.js +26 -0
- package/dist/orchestrator/branch.js.map +1 -0
- package/dist/orchestrator/checks.d.ts +16 -0
- package/dist/orchestrator/checks.d.ts.map +1 -0
- package/dist/orchestrator/checks.js +66 -0
- package/dist/orchestrator/checks.js.map +1 -0
- package/dist/orchestrator/planDoc.d.ts +41 -0
- package/dist/orchestrator/planDoc.d.ts.map +1 -0
- package/dist/orchestrator/planDoc.js +181 -0
- package/dist/orchestrator/planDoc.js.map +1 -0
- package/dist/orchestrator/record.d.ts +110 -0
- package/dist/orchestrator/record.d.ts.map +1 -0
- package/dist/orchestrator/record.js +221 -0
- package/dist/orchestrator/record.js.map +1 -0
- package/dist/orchestrator/sidecarTools.d.ts +7 -0
- package/dist/orchestrator/sidecarTools.d.ts.map +1 -0
- package/dist/orchestrator/sidecarTools.js +141 -0
- package/dist/orchestrator/sidecarTools.js.map +1 -0
- package/dist/prompts/config.d.ts.map +1 -1
- package/dist/prompts/config.js +11 -0
- package/dist/prompts/config.js.map +1 -1
- package/dist/prompts/index.d.ts +0 -6
- package/dist/prompts/index.d.ts.map +1 -1
- package/dist/prompts/index.js +10 -17
- package/dist/prompts/index.js.map +1 -1
- package/dist/prompts/init.d.ts +12 -0
- package/dist/prompts/init.d.ts.map +1 -0
- package/dist/prompts/init.js +48 -0
- package/dist/prompts/init.js.map +1 -0
- package/dist/prompts/library/defaults.generated.d.ts.map +1 -1
- package/dist/prompts/library/defaults.generated.js +29 -12
- package/dist/prompts/library/defaults.generated.js.map +1 -1
- package/dist/redact.d.ts.map +1 -1
- package/dist/redact.js +1 -0
- package/dist/redact.js.map +1 -1
- package/dist/rooms/findings.d.ts +14 -0
- package/dist/rooms/findings.d.ts.map +1 -1
- package/dist/rooms/findings.js +37 -0
- package/dist/rooms/findings.js.map +1 -1
- package/dist/sessions/blobGc.d.ts.map +1 -1
- package/dist/sessions/blobGc.js +2 -5
- package/dist/sessions/blobGc.js.map +1 -1
- package/dist/sessions/childSessions.d.ts +12 -0
- package/dist/sessions/childSessions.d.ts.map +1 -0
- package/dist/sessions/childSessions.js +43 -0
- package/dist/sessions/childSessions.js.map +1 -0
- package/dist/sessions/fileRecallStore.d.ts.map +1 -1
- package/dist/sessions/fileRecallStore.js +1 -3
- package/dist/sessions/fileRecallStore.js.map +1 -1
- package/dist/sessions/fileSessionStore.d.ts +11 -7
- package/dist/sessions/fileSessionStore.d.ts.map +1 -1
- package/dist/sessions/fileSessionStore.js +27 -16
- package/dist/sessions/fileSessionStore.js.map +1 -1
- package/dist/sessions/inflight.js +0 -0
- package/dist/sessions/inflight.js.map +1 -1
- package/dist/sessions/jsonl.d.ts +4 -0
- package/dist/sessions/jsonl.d.ts.map +1 -1
- package/dist/sessions/jsonl.js +1 -0
- package/dist/sessions/jsonl.js.map +1 -1
- package/dist/sessions/testFs.d.ts.map +1 -1
- package/dist/sessions/testFs.js +1 -1
- package/dist/sessions/testFs.js.map +1 -1
- package/dist/sidecar/host.d.ts +194 -3
- package/dist/sidecar/host.d.ts.map +1 -1
- package/dist/sidecar/host.js +401 -6
- package/dist/sidecar/host.js.map +1 -1
- package/dist/sidecar/identity.d.ts +44 -0
- package/dist/sidecar/identity.d.ts.map +1 -0
- package/dist/sidecar/identity.js +45 -0
- package/dist/sidecar/identity.js.map +1 -0
- package/dist/sidecar/server.d.ts +250 -4
- package/dist/sidecar/server.d.ts.map +1 -1
- package/dist/sidecar/server.js +866 -46
- package/dist/sidecar/server.js.map +1 -1
- package/dist/skillSource.js +1 -1
- package/dist/skillSource.js.map +1 -1
- package/dist/skills.d.ts +23 -7
- package/dist/skills.d.ts.map +1 -1
- package/dist/skills.js +62 -37
- package/dist/skills.js.map +1 -1
- package/dist/tools/callAgent.d.ts +29 -12
- package/dist/tools/callAgent.d.ts.map +1 -1
- package/dist/tools/callAgent.js +120 -14
- package/dist/tools/callAgent.js.map +1 -1
- package/dist/tools/crewList.d.ts +33 -0
- package/dist/tools/crewList.d.ts.map +1 -0
- package/dist/tools/crewList.js +66 -0
- package/dist/tools/crewList.js.map +1 -0
- package/dist/tools/files.d.ts.map +1 -1
- package/dist/tools/files.js +11 -5
- package/dist/tools/files.js.map +1 -1
- package/dist/tools/findAgent.d.ts +23 -0
- package/dist/tools/findAgent.d.ts.map +1 -0
- package/dist/tools/findAgent.js +58 -0
- package/dist/tools/findAgent.js.map +1 -0
- package/dist/tools/namespaces.d.ts.map +1 -1
- package/dist/tools/namespaces.js +7 -0
- package/dist/tools/namespaces.js.map +1 -1
- package/dist/tools/orchestrate.d.ts +4 -0
- package/dist/tools/orchestrate.d.ts.map +1 -0
- package/dist/tools/orchestrate.js +38 -0
- package/dist/tools/orchestrate.js.map +1 -0
- package/dist/tools/orchestration.d.ts +24 -0
- package/dist/tools/orchestration.d.ts.map +1 -0
- package/dist/tools/orchestration.js +173 -0
- package/dist/tools/orchestration.js.map +1 -0
- package/dist/tools/orchestrationSet.d.ts +6 -0
- package/dist/tools/orchestrationSet.d.ts.map +1 -0
- package/dist/tools/orchestrationSet.js +12 -0
- package/dist/tools/orchestrationSet.js.map +1 -0
- package/dist/tools/roomTools.d.ts +6 -0
- package/dist/tools/roomTools.d.ts.map +1 -1
- package/dist/tools/roomTools.js +26 -2
- package/dist/tools/roomTools.js.map +1 -1
- package/dist/tools/skillTool.d.ts +15 -0
- package/dist/tools/skillTool.d.ts.map +1 -0
- package/dist/tools/skillTool.js +39 -0
- package/dist/tools/skillTool.js.map +1 -0
- package/dist/tools/urlGuard.d.ts +7 -0
- package/dist/tools/urlGuard.d.ts.map +1 -1
- package/dist/tools/urlGuard.js +54 -18
- package/dist/tools/urlGuard.js.map +1 -1
- package/package.json +6 -2
package/dist/sidecar/host.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { SIDECAR_PROTOCOL, HOST_METHODS, errMessage, } from '@corenel/protocol';
|
|
1
|
+
import { SIDECAR_PROTOCOL, HOST_METHODS, errMessage, RUN_TASK_FEATURE, RUN_PLAN_FEATURE, CREW_DEPLOY_FEATURE, } from '@corenel/protocol';
|
|
2
2
|
/** Decode a base64 chunk to bytes (atob is available in the browser and Node 18+). */
|
|
3
3
|
function b64ToBytes(b64) {
|
|
4
4
|
const bin = atob(b64);
|
|
@@ -57,6 +57,18 @@ export class SidecarHost {
|
|
|
57
57
|
writable: true,
|
|
58
58
|
value: new Map()
|
|
59
59
|
});
|
|
60
|
+
Object.defineProperty(this, "cancelSeq", {
|
|
61
|
+
enumerable: true,
|
|
62
|
+
configurable: true,
|
|
63
|
+
writable: true,
|
|
64
|
+
value: 0
|
|
65
|
+
});
|
|
66
|
+
Object.defineProperty(this, "cancelPending", {
|
|
67
|
+
enumerable: true,
|
|
68
|
+
configurable: true,
|
|
69
|
+
writable: true,
|
|
70
|
+
value: new Map()
|
|
71
|
+
});
|
|
60
72
|
Object.defineProperty(this, "acpSeq", {
|
|
61
73
|
enumerable: true,
|
|
62
74
|
configurable: true,
|
|
@@ -162,6 +174,60 @@ export class SidecarHost {
|
|
|
162
174
|
writable: true,
|
|
163
175
|
value: new Map()
|
|
164
176
|
});
|
|
177
|
+
Object.defineProperty(this, "crewDeploySeq", {
|
|
178
|
+
enumerable: true,
|
|
179
|
+
configurable: true,
|
|
180
|
+
writable: true,
|
|
181
|
+
value: 0
|
|
182
|
+
});
|
|
183
|
+
Object.defineProperty(this, "crewDeployPending", {
|
|
184
|
+
enumerable: true,
|
|
185
|
+
configurable: true,
|
|
186
|
+
writable: true,
|
|
187
|
+
value: new Map()
|
|
188
|
+
});
|
|
189
|
+
Object.defineProperty(this, "approvalsSeq", {
|
|
190
|
+
enumerable: true,
|
|
191
|
+
configurable: true,
|
|
192
|
+
writable: true,
|
|
193
|
+
value: 0
|
|
194
|
+
});
|
|
195
|
+
Object.defineProperty(this, "approvalsPending", {
|
|
196
|
+
enumerable: true,
|
|
197
|
+
configurable: true,
|
|
198
|
+
writable: true,
|
|
199
|
+
value: new Map()
|
|
200
|
+
});
|
|
201
|
+
Object.defineProperty(this, "checksSeq", {
|
|
202
|
+
enumerable: true,
|
|
203
|
+
configurable: true,
|
|
204
|
+
writable: true,
|
|
205
|
+
value: 0
|
|
206
|
+
});
|
|
207
|
+
Object.defineProperty(this, "checksPending", {
|
|
208
|
+
enumerable: true,
|
|
209
|
+
configurable: true,
|
|
210
|
+
writable: true,
|
|
211
|
+
value: new Map()
|
|
212
|
+
});
|
|
213
|
+
Object.defineProperty(this, "releasePending", {
|
|
214
|
+
enumerable: true,
|
|
215
|
+
configurable: true,
|
|
216
|
+
writable: true,
|
|
217
|
+
value: new Map()
|
|
218
|
+
});
|
|
219
|
+
Object.defineProperty(this, "runPlanSeq", {
|
|
220
|
+
enumerable: true,
|
|
221
|
+
configurable: true,
|
|
222
|
+
writable: true,
|
|
223
|
+
value: 0
|
|
224
|
+
});
|
|
225
|
+
Object.defineProperty(this, "runPlanPending", {
|
|
226
|
+
enumerable: true,
|
|
227
|
+
configurable: true,
|
|
228
|
+
writable: true,
|
|
229
|
+
value: new Map()
|
|
230
|
+
});
|
|
165
231
|
Object.defineProperty(this, "hostsSeq", {
|
|
166
232
|
enumerable: true,
|
|
167
233
|
configurable: true,
|
|
@@ -281,6 +347,24 @@ export class SidecarHost {
|
|
|
281
347
|
writable: true,
|
|
282
348
|
value: []
|
|
283
349
|
});
|
|
350
|
+
Object.defineProperty(this, "vapidKey", {
|
|
351
|
+
enumerable: true,
|
|
352
|
+
configurable: true,
|
|
353
|
+
writable: true,
|
|
354
|
+
value: null
|
|
355
|
+
});
|
|
356
|
+
Object.defineProperty(this, "pushSeq", {
|
|
357
|
+
enumerable: true,
|
|
358
|
+
configurable: true,
|
|
359
|
+
writable: true,
|
|
360
|
+
value: 0
|
|
361
|
+
});
|
|
362
|
+
Object.defineProperty(this, "pushPending", {
|
|
363
|
+
enumerable: true,
|
|
364
|
+
configurable: true,
|
|
365
|
+
writable: true,
|
|
366
|
+
value: new Map()
|
|
367
|
+
});
|
|
284
368
|
/** The synced directory name the daemon echoed for THIS connection
|
|
285
369
|
* (`welcome.dirName`), or null against an older daemon that sends none. */
|
|
286
370
|
Object.defineProperty(this, "dirName", {
|
|
@@ -289,6 +373,16 @@ export class SidecarHost {
|
|
|
289
373
|
writable: true,
|
|
290
374
|
value: null
|
|
291
375
|
});
|
|
376
|
+
/** The daemon's verdict on the identity challenge/assert exchange, from
|
|
377
|
+
* `welcome.identity`, runtime-validated the same way `dirName`/`info` are:
|
|
378
|
+
* a malformed value is dropped to undefined (=> `identityVerified()`
|
|
379
|
+
* reports null, "not told"), never coerced. */
|
|
380
|
+
Object.defineProperty(this, "identity", {
|
|
381
|
+
enumerable: true,
|
|
382
|
+
configurable: true,
|
|
383
|
+
writable: true,
|
|
384
|
+
value: void 0
|
|
385
|
+
});
|
|
292
386
|
Object.defineProperty(this, "info", {
|
|
293
387
|
enumerable: true,
|
|
294
388
|
configurable: true,
|
|
@@ -342,9 +436,29 @@ export class SidecarHost {
|
|
|
342
436
|
...(this.opts.clientId ? { clientId: this.opts.clientId } : {}),
|
|
343
437
|
...(this.opts.clientName ? { clientName: this.opts.clientName } : {}),
|
|
344
438
|
...(this.opts.accounts?.length ? { accounts: this.opts.accounts } : {}),
|
|
439
|
+
...(this.opts.wantsIdentity ? { wantsIdentity: true } : {}),
|
|
440
|
+
// What THIS client understands, so a daemon can tell an older app
|
|
441
|
+
// (whose agents sync but never fire until deployed) from this one.
|
|
442
|
+
capabilities: [CREW_DEPLOY_FEATURE],
|
|
345
443
|
});
|
|
346
444
|
});
|
|
347
445
|
}
|
|
446
|
+
/** Answers the daemon's `identity-challenge`. `onIdentityChallenge` resolving
|
|
447
|
+
* a string sends it as the assertion; resolving `null`, having no handler
|
|
448
|
+
* at all, or THROWING all send `identity-assert` with no assertion -- every
|
|
449
|
+
* one of those is a considered "no identity to offer", never a reason to
|
|
450
|
+
* fail the handshake. The Clerk token itself never reaches this class: only
|
|
451
|
+
* whatever assertion the callback hands back does. */
|
|
452
|
+
async answerIdentityChallenge(m) {
|
|
453
|
+
let assertion = null;
|
|
454
|
+
try {
|
|
455
|
+
assertion = (await this.opts.onIdentityChallenge?.({ nonce: m.nonce, sidecarId: m.sidecarId })) ?? null;
|
|
456
|
+
}
|
|
457
|
+
catch {
|
|
458
|
+
assertion = null;
|
|
459
|
+
}
|
|
460
|
+
this.ch.send(assertion ? { t: 'identity-assert', assertion } : { t: 'identity-assert' });
|
|
461
|
+
}
|
|
348
462
|
/** Drop the connection: unregister imported tools and fail anything in flight. */
|
|
349
463
|
disconnect(reason = 'disconnected') {
|
|
350
464
|
for (const name of this.imported)
|
|
@@ -359,6 +473,12 @@ export class SidecarHost {
|
|
|
359
473
|
for (const p of this.runPending.values())
|
|
360
474
|
p.reject(new Error(reason));
|
|
361
475
|
this.runPending.clear();
|
|
476
|
+
// A cancel in flight when the connection drops did not get a considered
|
|
477
|
+
// answer -- resolve `unsupported` (not reject) so a Stop click during a
|
|
478
|
+
// drop does not hang the caller awaiting an ok/reason pair.
|
|
479
|
+
for (const p of this.cancelPending.values())
|
|
480
|
+
p({ ok: false, reason: 'unsupported' });
|
|
481
|
+
this.cancelPending.clear();
|
|
362
482
|
for (const p of this.acpPending.values())
|
|
363
483
|
p.reject(new Error(reason));
|
|
364
484
|
this.acpPending.clear();
|
|
@@ -397,6 +517,21 @@ export class SidecarHost {
|
|
|
397
517
|
for (const p of this.pausePending.values())
|
|
398
518
|
p.reject(new Error(reason));
|
|
399
519
|
this.pausePending.clear();
|
|
520
|
+
for (const p of this.crewDeployPending.values())
|
|
521
|
+
p.reject(new Error(reason));
|
|
522
|
+
this.crewDeployPending.clear();
|
|
523
|
+
for (const p of this.approvalsPending.values())
|
|
524
|
+
p.reject(new Error(reason));
|
|
525
|
+
this.approvalsPending.clear();
|
|
526
|
+
for (const p of this.checksPending.values())
|
|
527
|
+
p.reject(new Error(reason));
|
|
528
|
+
this.checksPending.clear();
|
|
529
|
+
for (const p of this.releasePending.values())
|
|
530
|
+
p.reject(new Error(reason));
|
|
531
|
+
this.releasePending.clear();
|
|
532
|
+
for (const p of this.runPlanPending.values())
|
|
533
|
+
p.reject(new Error(reason));
|
|
534
|
+
this.runPlanPending.clear();
|
|
400
535
|
for (const p of this.hostsPending.values())
|
|
401
536
|
p.reject(new Error(reason));
|
|
402
537
|
this.hostsPending.clear();
|
|
@@ -434,6 +569,9 @@ export class SidecarHost {
|
|
|
434
569
|
for (const p of this.mcpAdminPending.values())
|
|
435
570
|
p.reject(new Error(reason));
|
|
436
571
|
this.mcpAdminPending.clear();
|
|
572
|
+
for (const p of this.pushPending.values())
|
|
573
|
+
p.reject(new Error(reason));
|
|
574
|
+
this.pushPending.clear();
|
|
437
575
|
/* The rejections above already drop each login's waiter via the wrapper
|
|
438
576
|
* mcpLogin installs. Cleared here too so that stays true if sendMcpAdmin
|
|
439
577
|
* is ever the one carrying a login -- a stale waiter would fire a consent
|
|
@@ -447,12 +585,19 @@ export class SidecarHost {
|
|
|
447
585
|
switch (m.t) {
|
|
448
586
|
case 'welcome': {
|
|
449
587
|
this.features = m.features ?? [];
|
|
588
|
+
// Runtime-validated: an untrusted/older daemon's `vapidPublicKey` is
|
|
589
|
+
// just a JSON-parsed `string`, and a malformed one must never be
|
|
590
|
+
// handed to the Push API. Anything that isn't the expected
|
|
591
|
+
// URL-safe-base64, 87-char shape is treated as "no key offered".
|
|
592
|
+
this.vapidKey = typeof m.vapidPublicKey === 'string' && /^[A-Za-z0-9_-]{87}$/.test(m.vapidPublicKey) ? m.vapidPublicKey : null;
|
|
450
593
|
// Runtime-validated like `info.id`/`info.name` next door: the welcome
|
|
451
594
|
// crossed the wire as `JSON.parse(...) as SidecarMsg`, so the declared
|
|
452
595
|
// `string` guarantees nothing, and this value decides which
|
|
453
596
|
// registrations a management surface treats as this browser's own.
|
|
454
597
|
// Anything malformed is dropped to "not told", never coerced.
|
|
455
598
|
this.dirName = typeof m.dirName === 'string' && m.dirName ? m.dirName : null;
|
|
599
|
+
this.identity = m.identity && typeof m.identity === 'object' && typeof m.identity.verified === 'boolean'
|
|
600
|
+
? m.identity : undefined;
|
|
456
601
|
this.info = m.info ?? {};
|
|
457
602
|
for (const decl of m.tools) {
|
|
458
603
|
const tool = { ...decl, run: (args) => this.invoke(decl.name, args) };
|
|
@@ -468,6 +613,10 @@ export class SidecarHost {
|
|
|
468
613
|
this.welcome = null;
|
|
469
614
|
return;
|
|
470
615
|
}
|
|
616
|
+
case 'identity-challenge': {
|
|
617
|
+
void this.answerIdentityChallenge(m);
|
|
618
|
+
return;
|
|
619
|
+
}
|
|
471
620
|
case 'invoke-result': {
|
|
472
621
|
const p = this.invokePending.get(m.id);
|
|
473
622
|
if (!p)
|
|
@@ -570,8 +719,10 @@ export class SidecarHost {
|
|
|
570
719
|
}
|
|
571
720
|
case 'run-accepted': {
|
|
572
721
|
const p = this.runPending.get(m.id);
|
|
573
|
-
if (p)
|
|
722
|
+
if (p) {
|
|
574
723
|
p.runId = m.runId;
|
|
724
|
+
p.onAccepted?.(m.runId);
|
|
725
|
+
}
|
|
575
726
|
return;
|
|
576
727
|
}
|
|
577
728
|
case 'event': {
|
|
@@ -594,6 +745,12 @@ export class SidecarHost {
|
|
|
594
745
|
p.reject(new Error(m.error || 'run failed'));
|
|
595
746
|
return;
|
|
596
747
|
}
|
|
748
|
+
case 'cancel-run-result': {
|
|
749
|
+
const p = this.cancelPending.get(m.id);
|
|
750
|
+
this.cancelPending.delete(m.id);
|
|
751
|
+
p?.(m.ok ? { ok: true } : { ok: false, ...(m.reason ? { reason: m.reason } : {}) });
|
|
752
|
+
return;
|
|
753
|
+
}
|
|
597
754
|
case 'config-result': {
|
|
598
755
|
const p = this.configPending.get(m.id);
|
|
599
756
|
if (!p)
|
|
@@ -616,6 +773,17 @@ export class SidecarHost {
|
|
|
616
773
|
p.reject(new Error(m.error || 'host sync failed'));
|
|
617
774
|
return;
|
|
618
775
|
}
|
|
776
|
+
case 'push-result': {
|
|
777
|
+
const p = this.pushPending.get(m.id);
|
|
778
|
+
if (!p)
|
|
779
|
+
return;
|
|
780
|
+
this.pushPending.delete(m.id);
|
|
781
|
+
if (m.ok)
|
|
782
|
+
p.resolve();
|
|
783
|
+
else
|
|
784
|
+
p.reject(new Error(m.error));
|
|
785
|
+
return;
|
|
786
|
+
}
|
|
619
787
|
case 'permission-ask': {
|
|
620
788
|
const { t: _t, ...ask } = m;
|
|
621
789
|
this.opts.onPermissionAsk?.(ask);
|
|
@@ -849,6 +1017,25 @@ export class SidecarHost {
|
|
|
849
1017
|
this.opts.onRegistrations?.(m.registrations);
|
|
850
1018
|
return;
|
|
851
1019
|
}
|
|
1020
|
+
case 'approvals-suggest': {
|
|
1021
|
+
const { t: _t, ...offer } = m;
|
|
1022
|
+
this.opts.onApprovalSuggestion?.(offer);
|
|
1023
|
+
return;
|
|
1024
|
+
}
|
|
1025
|
+
case 'approvals-accept-result':
|
|
1026
|
+
case 'approvals-list-result':
|
|
1027
|
+
case 'approvals-revoke-result': {
|
|
1028
|
+
const p = this.approvalsPending.get(m.id);
|
|
1029
|
+
if (!p)
|
|
1030
|
+
return;
|
|
1031
|
+
this.approvalsPending.delete(m.id);
|
|
1032
|
+
if (!m.ok) {
|
|
1033
|
+
p.resolve({ ok: false, error: m.error });
|
|
1034
|
+
return;
|
|
1035
|
+
}
|
|
1036
|
+
p.resolve(m.t === 'approvals-accept-result' ? { ok: true } : { ok: true, state: m.state });
|
|
1037
|
+
return;
|
|
1038
|
+
}
|
|
852
1039
|
case 'room-open-result': {
|
|
853
1040
|
const p = this.roomOpenPending.get(m.id);
|
|
854
1041
|
if (!p)
|
|
@@ -881,6 +1068,41 @@ export class SidecarHost {
|
|
|
881
1068
|
p.resolve(m.ok ? { ok: true, pauses: m.pauses } : { ok: false, error: m.error });
|
|
882
1069
|
return;
|
|
883
1070
|
}
|
|
1071
|
+
case 'crew-deploy-result': {
|
|
1072
|
+
// One pending map for all six crew-* requests: they share one reply shape.
|
|
1073
|
+
const p = this.crewDeployPending.get(m.id);
|
|
1074
|
+
if (!p)
|
|
1075
|
+
return;
|
|
1076
|
+
this.crewDeployPending.delete(m.id);
|
|
1077
|
+
p.resolve(m.reply);
|
|
1078
|
+
return;
|
|
1079
|
+
}
|
|
1080
|
+
case 'checks-get-result':
|
|
1081
|
+
case 'checks-pin-result': {
|
|
1082
|
+
// One map for both: each answers with the complete state.
|
|
1083
|
+
const p = this.checksPending.get(m.id);
|
|
1084
|
+
if (!p)
|
|
1085
|
+
return;
|
|
1086
|
+
this.checksPending.delete(m.id);
|
|
1087
|
+
p.resolve(m.ok ? { ok: true, state: m.state } : { ok: false, error: m.error });
|
|
1088
|
+
return;
|
|
1089
|
+
}
|
|
1090
|
+
case 'workspace-release-result': {
|
|
1091
|
+
const p = this.releasePending.get(m.id);
|
|
1092
|
+
if (!p)
|
|
1093
|
+
return;
|
|
1094
|
+
this.releasePending.delete(m.id);
|
|
1095
|
+
p.resolve(m.ok ? { ok: true, released: m.released } : { ok: false, error: m.error });
|
|
1096
|
+
return;
|
|
1097
|
+
}
|
|
1098
|
+
case 'run-plan-get-result': {
|
|
1099
|
+
const p = this.runPlanPending.get(m.id);
|
|
1100
|
+
if (!p)
|
|
1101
|
+
return;
|
|
1102
|
+
this.runPlanPending.delete(m.id);
|
|
1103
|
+
p.resolve(m.ok ? { ok: true, plan: m.plan } : { ok: false, error: m.error });
|
|
1104
|
+
return;
|
|
1105
|
+
}
|
|
884
1106
|
case 'room-list-result': {
|
|
885
1107
|
const p = this.roomListPending.get(m.id);
|
|
886
1108
|
if (!p)
|
|
@@ -1211,6 +1433,12 @@ export class SidecarHost {
|
|
|
1211
1433
|
/** The sidecar's advertised self-description (root/platform), or {} if it sent
|
|
1212
1434
|
* none. Valid only after connect() resolves. */
|
|
1213
1435
|
sidecarInfo() { return this.info; }
|
|
1436
|
+
/** The daemon's verdict on the identity challenge/assert exchange for this
|
|
1437
|
+
* connection. `null` means the daemon does not verify at all (no `identity`
|
|
1438
|
+
* on `welcome` -- no verifier configured, or an older daemon), which is
|
|
1439
|
+
* distinct from `false` (the exchange ran and was rejected, or the
|
|
1440
|
+
* assertion never arrived). Valid only after connect() resolves. */
|
|
1441
|
+
identityVerified() { return this.identity ? this.identity.verified : null; }
|
|
1214
1442
|
/** Fetch a (loopback) URL THROUGH the sidecar — a server-side fetch on the user's
|
|
1215
1443
|
* machine, so the browser dodges CORS. Returns a streaming Response: it resolves
|
|
1216
1444
|
* as soon as headers arrive (http-res) and the body streams in via http-chunk. */
|
|
@@ -1251,7 +1479,7 @@ export class SidecarHost {
|
|
|
1251
1479
|
run(agent, input, onEvent, opts) {
|
|
1252
1480
|
const id = ++this.runSeq;
|
|
1253
1481
|
return new Promise((resolve, reject) => {
|
|
1254
|
-
this.runPending.set(id, { resolve, reject, onEvent });
|
|
1482
|
+
this.runPending.set(id, { resolve, reject, onEvent, onAccepted: opts?.onAccepted });
|
|
1255
1483
|
// `sessionId` asks for the run to happen INSIDE that session. A daemon
|
|
1256
1484
|
// that cannot honour it answers run-error rather than running it on the
|
|
1257
1485
|
// host, so the caller learns rather than being quietly downgraded.
|
|
@@ -1261,9 +1489,52 @@ export class SidecarHost {
|
|
|
1261
1489
|
t: 'run', id, agent, input,
|
|
1262
1490
|
...(opts?.sessionId ? { sessionId: opts.sessionId } : {}),
|
|
1263
1491
|
...(opts?.keepTranscript ? { keepTranscript: true } : {}),
|
|
1492
|
+
...(opts?.task ? { task: opts.task } : {}),
|
|
1493
|
+
...(opts?.autonomy ? { autonomy: opts.autonomy } : {}),
|
|
1264
1494
|
});
|
|
1265
1495
|
});
|
|
1266
1496
|
}
|
|
1497
|
+
/** WATCH a run this client did not start (a trigger-fired crew run). Rides
|
|
1498
|
+
* the same pending map as `run`, so `event`/`run-done`/`run-error` route here
|
|
1499
|
+
* unchanged; the server replays what already happened first. */
|
|
1500
|
+
watchRun(runId, onEvent) {
|
|
1501
|
+
const id = ++this.runSeq;
|
|
1502
|
+
let rejectDone;
|
|
1503
|
+
const done = new Promise((resolve, reject) => {
|
|
1504
|
+
rejectDone = reject;
|
|
1505
|
+
this.runPending.set(id, {
|
|
1506
|
+
resolve: (r) => resolve({ text: r.text, stopReason: r.stopReason }),
|
|
1507
|
+
reject,
|
|
1508
|
+
onEvent,
|
|
1509
|
+
});
|
|
1510
|
+
this.ch.send({ t: 'subscribe', id, runId });
|
|
1511
|
+
});
|
|
1512
|
+
return {
|
|
1513
|
+
done,
|
|
1514
|
+
// Client-side only: the server keeps the subscription until the run ends
|
|
1515
|
+
// or the socket drops, and events for a closed id are ignored.
|
|
1516
|
+
close: () => { if (this.runPending.delete(id))
|
|
1517
|
+
rejectDone(new Error('closed')); },
|
|
1518
|
+
};
|
|
1519
|
+
}
|
|
1520
|
+
/** Ask the daemon to cancel a running crew run. `ok: false, reason:
|
|
1521
|
+
* 'no-such-run'` means the daemon has no such run (already finished, or
|
|
1522
|
+
* never existed); `'unsupported'` means the daemon's runner has no `cancel`,
|
|
1523
|
+
* or the connection dropped before the daemon answered. */
|
|
1524
|
+
cancelRun(runId) {
|
|
1525
|
+
const id = ++this.cancelSeq;
|
|
1526
|
+
return new Promise((resolve) => {
|
|
1527
|
+
this.cancelPending.set(id, resolve);
|
|
1528
|
+
this.ch.send({ t: 'cancel-run', id, runId });
|
|
1529
|
+
});
|
|
1530
|
+
}
|
|
1531
|
+
/** True when the connected sidecar's runner supports cancelling a run. */
|
|
1532
|
+
supportsRunCancel() { return this.features.includes('run-cancel'); }
|
|
1533
|
+
/** True when the connected sidecar understands `run`'s `task`/`autonomy`
|
|
1534
|
+
* fields. An older sidecar without this feature would silently ignore
|
|
1535
|
+
* them, so callers must check this before sending either — never send and
|
|
1536
|
+
* hope. */
|
|
1537
|
+
supportsTasks() { return this.features.includes(RUN_TASK_FEATURE); }
|
|
1267
1538
|
/** Start a proxied external ACP agent on the sidecar. The returned promise
|
|
1268
1539
|
* settles on the handshake; `onUpdate` keeps receiving the agent's stream for
|
|
1269
1540
|
* the life of the session. Permission asks do NOT come through here — they use
|
|
@@ -1313,9 +1584,10 @@ export class SidecarHost {
|
|
|
1313
1584
|
}
|
|
1314
1585
|
/** Answer an outstanding permission ask. Fire-and-forget over the wire — the
|
|
1315
1586
|
* actual drop of the prompt is driven by the server's `permission-resolved`
|
|
1316
|
-
* broadcast (`onPermissionResolved`), not by this call returning.
|
|
1317
|
-
|
|
1318
|
-
|
|
1587
|
+
* broadcast (`onPermissionResolved`), not by this call returning. `note` is
|
|
1588
|
+
* the person's reason for a deny; the daemon trims and caps it. */
|
|
1589
|
+
answerPermission(id, decision, note) {
|
|
1590
|
+
this.ch.send({ t: 'permission-answer', id, decision, ...(note ? { note } : {}) });
|
|
1319
1591
|
}
|
|
1320
1592
|
/** Push one definition file's content to the daemon, to be persisted under
|
|
1321
1593
|
* this client's own `<stateDir>/hosts/<dirName>/` (Slice 2 host sync).
|
|
@@ -1339,6 +1611,30 @@ export class SidecarHost {
|
|
|
1339
1611
|
this.ch.send({ t: 'host-sync-delete', id, path });
|
|
1340
1612
|
});
|
|
1341
1613
|
}
|
|
1614
|
+
/** The daemon's VAPID public key from `welcome`, or null when it offered
|
|
1615
|
+
* none (older daemon, or an offered value that failed validation). */
|
|
1616
|
+
vapidPublicKey() { return this.vapidKey; }
|
|
1617
|
+
/** True when this daemon advertises web push AND handed over a usable key. */
|
|
1618
|
+
supportsWebPush() { return this.features.includes('web-push') && this.vapidKey !== null; }
|
|
1619
|
+
/** Send a browser push subscription to the daemon to persist. Resolves on
|
|
1620
|
+
* `push-result { ok: true }`; rejects with the daemon's error, or on
|
|
1621
|
+
* disconnect while in flight. */
|
|
1622
|
+
pushSubscribe(subscription) {
|
|
1623
|
+
const id = ++this.pushSeq;
|
|
1624
|
+
return new Promise((resolve, reject) => {
|
|
1625
|
+
this.pushPending.set(id, { resolve, reject });
|
|
1626
|
+
this.ch.send({ t: 'push-subscribe', id, subscription });
|
|
1627
|
+
});
|
|
1628
|
+
}
|
|
1629
|
+
/** Tell the daemon to drop a subscription by endpoint. Same resolve/reject
|
|
1630
|
+
* contract as `pushSubscribe`. */
|
|
1631
|
+
pushUnsubscribe(endpoint) {
|
|
1632
|
+
const id = ++this.pushSeq;
|
|
1633
|
+
return new Promise((resolve, reject) => {
|
|
1634
|
+
this.pushPending.set(id, { resolve, reject });
|
|
1635
|
+
this.ch.send({ t: 'push-unsubscribe', id, endpoint });
|
|
1636
|
+
});
|
|
1637
|
+
}
|
|
1342
1638
|
/** True when this daemon serves its OWN Harness Config (`config-*`). Feature-
|
|
1343
1639
|
* detected from the welcome, so a client standing on a daemon too old to serve
|
|
1344
1640
|
* one says the config is unreachable instead of sending a message that will be
|
|
@@ -1504,6 +1800,105 @@ export class SidecarHost {
|
|
|
1504
1800
|
this.ch.send({ t: 'agent-pause-set', id, owner, agent, paused });
|
|
1505
1801
|
});
|
|
1506
1802
|
}
|
|
1803
|
+
/** True when the connected daemon keeps a deploy list (crew deploy per
|
|
1804
|
+
* machine). Feature-detected from the welcome; without it a client shows
|
|
1805
|
+
* the old read-only view. */
|
|
1806
|
+
supportsCrewDeploy() { return this.features.includes(CREW_DEPLOY_FEATURE); }
|
|
1807
|
+
/** One crew-* request. `ok: false` is the daemon's considered answer (a
|
|
1808
|
+
* refusal names its rule) and resolves; only a dropped connection rejects.
|
|
1809
|
+
* Against a daemon without the feature nothing is sent. */
|
|
1810
|
+
crewDeployRequest(send) {
|
|
1811
|
+
if (!this.supportsCrewDeploy())
|
|
1812
|
+
return Promise.resolve({ ok: false, error: 'this daemon does not keep a deploy list' });
|
|
1813
|
+
const id = ++this.crewDeploySeq;
|
|
1814
|
+
return new Promise((resolve, reject) => {
|
|
1815
|
+
this.crewDeployPending.set(id, { resolve, reject });
|
|
1816
|
+
this.ch.send(send(id));
|
|
1817
|
+
});
|
|
1818
|
+
}
|
|
1819
|
+
/** The machine's deploy list, statuses and audit. */
|
|
1820
|
+
crewDeployments() { return this.crewDeployRequest((id) => ({ t: 'crew-deployments', id })); }
|
|
1821
|
+
crewDeploy(ref, reason) { return this.crewDeployRequest((id) => ({ t: 'crew-deploy', id, ref, ...(reason ? { reason } : {}) })); }
|
|
1822
|
+
crewUndeploy(ref, reason) { return this.crewDeployRequest((id) => ({ t: 'crew-undeploy', id, ref, ...(reason ? { reason } : {}) })); }
|
|
1823
|
+
crewPause(ref, reason) { return this.crewDeployRequest((id) => ({ t: 'crew-pause', id, ref, reason })); }
|
|
1824
|
+
crewResume(ref, reason) { return this.crewDeployRequest((id) => ({ t: 'crew-resume', id, ref, ...(reason ? { reason } : {}) })); }
|
|
1825
|
+
/** Stop a run AND record who stopped it and why (the plain `cancelRun` records nothing). */
|
|
1826
|
+
crewStop(ref, runId, reason) { return this.crewDeployRequest((id) => ({ t: 'crew-stop', id, ref, runId, reason })); }
|
|
1827
|
+
/** True when the connected daemon keeps "This machine" standing approvals
|
|
1828
|
+
* (plan 3). Feature-detected from the welcome. */
|
|
1829
|
+
supportsApprovals() { return this.features.includes('approvals'); }
|
|
1830
|
+
approvalsRequest(send) {
|
|
1831
|
+
if (!this.supportsApprovals())
|
|
1832
|
+
return Promise.resolve({ ok: false, error: 'this daemon does not keep standing approvals' });
|
|
1833
|
+
const id = ++this.approvalsSeq;
|
|
1834
|
+
return new Promise((resolve, reject) => {
|
|
1835
|
+
this.approvalsPending.set(id, { resolve, reject });
|
|
1836
|
+
this.ch.send(send(id));
|
|
1837
|
+
});
|
|
1838
|
+
}
|
|
1839
|
+
/** Accept an offer this client received. The daemon checks that THIS
|
|
1840
|
+
* connection is the one it offered to; `ok: false` is its refusal (expired,
|
|
1841
|
+
* already used, offered to another connection), a real answer that
|
|
1842
|
+
* resolves. Only a dropped connection rejects. */
|
|
1843
|
+
async acceptApprovalSuggestion(suggestionId, choice) {
|
|
1844
|
+
const r = await this.approvalsRequest((id) => ({ t: 'approvals-accept', id, suggestionId, choice }));
|
|
1845
|
+
return r.ok ? { ok: true } : r;
|
|
1846
|
+
}
|
|
1847
|
+
/** The daemon's standing rules and its (daemon.json) approval settings. */
|
|
1848
|
+
async listDaemonApprovals() {
|
|
1849
|
+
return this.withState(await this.approvalsRequest((id) => ({ t: 'approvals-list', id })));
|
|
1850
|
+
}
|
|
1851
|
+
/** Remove one daemon standing rule; resolves with the complete state after. */
|
|
1852
|
+
async revokeDaemonApproval(tool) {
|
|
1853
|
+
return this.withState(await this.approvalsRequest((id) => ({ t: 'approvals-revoke', id, tool })));
|
|
1854
|
+
}
|
|
1855
|
+
withState(r) {
|
|
1856
|
+
if (!r.ok)
|
|
1857
|
+
return r;
|
|
1858
|
+
return r.state ? { ok: true, state: r.state } : { ok: false, error: 'the daemon answered without its approvals state' };
|
|
1859
|
+
}
|
|
1860
|
+
/** True when the daemon runs confirmed checks and holds the orchestrator's
|
|
1861
|
+
* workspace lock (it was started with --allow-shell, and its state dir is
|
|
1862
|
+
* not inside an approved root). */
|
|
1863
|
+
supportsChecks() { return this.features.includes('checks'); }
|
|
1864
|
+
/** The folder's checks file, whether it is confirmed, and proposals. `ok:
|
|
1865
|
+
* false` is the daemon's answer (no checks here, a bad path); only a dropped
|
|
1866
|
+
* connection rejects. */
|
|
1867
|
+
checksGet(root, stateDir) {
|
|
1868
|
+
const id = ++this.checksSeq;
|
|
1869
|
+
return new Promise((resolve, reject) => {
|
|
1870
|
+
this.checksPending.set(id, { resolve, reject });
|
|
1871
|
+
this.ch.send({ t: 'checks-get', id, root, stateDir });
|
|
1872
|
+
});
|
|
1873
|
+
}
|
|
1874
|
+
/** Write the content the user confirmed and pin its hash. */
|
|
1875
|
+
checksPin(root, stateDir, content) {
|
|
1876
|
+
const id = ++this.checksSeq;
|
|
1877
|
+
return new Promise((resolve, reject) => {
|
|
1878
|
+
this.checksPending.set(id, { resolve, reject });
|
|
1879
|
+
this.ch.send({ t: 'checks-pin', id, root, stateDir, content });
|
|
1880
|
+
});
|
|
1881
|
+
}
|
|
1882
|
+
/** Hand back a run's workspace lock. `released: false` means this run did
|
|
1883
|
+
* not hold it (already released, or held by another run). */
|
|
1884
|
+
workspaceRelease(ref) {
|
|
1885
|
+
const id = ++this.checksSeq;
|
|
1886
|
+
return new Promise((resolve, reject) => {
|
|
1887
|
+
this.releasePending.set(id, { resolve, reject });
|
|
1888
|
+
this.ch.send({ t: 'workspace-release', id, root: ref.root, stateDir: ref.stateDir, runId: ref.runId });
|
|
1889
|
+
});
|
|
1890
|
+
}
|
|
1891
|
+
/** True when the daemon answers run-plan-get. */
|
|
1892
|
+
supportsRunPlan() { return this.features.includes(RUN_PLAN_FEATURE); }
|
|
1893
|
+
/** One run's plan from its record. `ok: false` is the daemon's answer; only
|
|
1894
|
+
* a dropped connection rejects. Ask only when supportsRunPlan() is true. */
|
|
1895
|
+
runPlanGet(runId) {
|
|
1896
|
+
const id = ++this.runPlanSeq;
|
|
1897
|
+
return new Promise((resolve, reject) => {
|
|
1898
|
+
this.runPlanPending.set(id, { resolve, reject });
|
|
1899
|
+
this.ch.send({ t: 'run-plan-get', id, runId });
|
|
1900
|
+
});
|
|
1901
|
+
}
|
|
1507
1902
|
/** Ask the daemon to open a room. `ok: false` here is the daemon's CONSIDERED
|
|
1508
1903
|
* refusal (validation problems, or an unreachable participant) — a real answer,
|
|
1509
1904
|
* not a transport failure, so it resolves rather than rejects. Only a dropped
|