@corenel/harness 0.10.0 → 0.11.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 +61 -5
- 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 +37 -6
- package/dist/guardrail/permission-service.d.ts.map +1 -1
- package/dist/guardrail/permission-service.js +111 -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 +102 -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 +15 -0
- package/dist/orchestrator/planDoc.d.ts.map +1 -0
- package/dist/orchestrator/planDoc.js +59 -0
- package/dist/orchestrator/planDoc.js.map +1 -0
- package/dist/orchestrator/record.d.ts +103 -0
- package/dist/orchestrator/record.d.ts.map +1 -0
- package/dist/orchestrator/record.js +168 -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/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 +178 -1
- package/dist/sidecar/host.d.ts.map +1 -1
- package/dist/sidecar/host.js +363 -3
- 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 +245 -2
- package/dist/sidecar/server.d.ts.map +1 -1
- package/dist/sidecar/server.js +833 -45
- 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, 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,48 @@ 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
|
+
});
|
|
165
219
|
Object.defineProperty(this, "hostsSeq", {
|
|
166
220
|
enumerable: true,
|
|
167
221
|
configurable: true,
|
|
@@ -281,6 +335,24 @@ export class SidecarHost {
|
|
|
281
335
|
writable: true,
|
|
282
336
|
value: []
|
|
283
337
|
});
|
|
338
|
+
Object.defineProperty(this, "vapidKey", {
|
|
339
|
+
enumerable: true,
|
|
340
|
+
configurable: true,
|
|
341
|
+
writable: true,
|
|
342
|
+
value: null
|
|
343
|
+
});
|
|
344
|
+
Object.defineProperty(this, "pushSeq", {
|
|
345
|
+
enumerable: true,
|
|
346
|
+
configurable: true,
|
|
347
|
+
writable: true,
|
|
348
|
+
value: 0
|
|
349
|
+
});
|
|
350
|
+
Object.defineProperty(this, "pushPending", {
|
|
351
|
+
enumerable: true,
|
|
352
|
+
configurable: true,
|
|
353
|
+
writable: true,
|
|
354
|
+
value: new Map()
|
|
355
|
+
});
|
|
284
356
|
/** The synced directory name the daemon echoed for THIS connection
|
|
285
357
|
* (`welcome.dirName`), or null against an older daemon that sends none. */
|
|
286
358
|
Object.defineProperty(this, "dirName", {
|
|
@@ -289,6 +361,16 @@ export class SidecarHost {
|
|
|
289
361
|
writable: true,
|
|
290
362
|
value: null
|
|
291
363
|
});
|
|
364
|
+
/** The daemon's verdict on the identity challenge/assert exchange, from
|
|
365
|
+
* `welcome.identity`, runtime-validated the same way `dirName`/`info` are:
|
|
366
|
+
* a malformed value is dropped to undefined (=> `identityVerified()`
|
|
367
|
+
* reports null, "not told"), never coerced. */
|
|
368
|
+
Object.defineProperty(this, "identity", {
|
|
369
|
+
enumerable: true,
|
|
370
|
+
configurable: true,
|
|
371
|
+
writable: true,
|
|
372
|
+
value: void 0
|
|
373
|
+
});
|
|
292
374
|
Object.defineProperty(this, "info", {
|
|
293
375
|
enumerable: true,
|
|
294
376
|
configurable: true,
|
|
@@ -342,9 +424,29 @@ export class SidecarHost {
|
|
|
342
424
|
...(this.opts.clientId ? { clientId: this.opts.clientId } : {}),
|
|
343
425
|
...(this.opts.clientName ? { clientName: this.opts.clientName } : {}),
|
|
344
426
|
...(this.opts.accounts?.length ? { accounts: this.opts.accounts } : {}),
|
|
427
|
+
...(this.opts.wantsIdentity ? { wantsIdentity: true } : {}),
|
|
428
|
+
// What THIS client understands, so a daemon can tell an older app
|
|
429
|
+
// (whose agents sync but never fire until deployed) from this one.
|
|
430
|
+
capabilities: [CREW_DEPLOY_FEATURE],
|
|
345
431
|
});
|
|
346
432
|
});
|
|
347
433
|
}
|
|
434
|
+
/** Answers the daemon's `identity-challenge`. `onIdentityChallenge` resolving
|
|
435
|
+
* a string sends it as the assertion; resolving `null`, having no handler
|
|
436
|
+
* at all, or THROWING all send `identity-assert` with no assertion -- every
|
|
437
|
+
* one of those is a considered "no identity to offer", never a reason to
|
|
438
|
+
* fail the handshake. The Clerk token itself never reaches this class: only
|
|
439
|
+
* whatever assertion the callback hands back does. */
|
|
440
|
+
async answerIdentityChallenge(m) {
|
|
441
|
+
let assertion = null;
|
|
442
|
+
try {
|
|
443
|
+
assertion = (await this.opts.onIdentityChallenge?.({ nonce: m.nonce, sidecarId: m.sidecarId })) ?? null;
|
|
444
|
+
}
|
|
445
|
+
catch {
|
|
446
|
+
assertion = null;
|
|
447
|
+
}
|
|
448
|
+
this.ch.send(assertion ? { t: 'identity-assert', assertion } : { t: 'identity-assert' });
|
|
449
|
+
}
|
|
348
450
|
/** Drop the connection: unregister imported tools and fail anything in flight. */
|
|
349
451
|
disconnect(reason = 'disconnected') {
|
|
350
452
|
for (const name of this.imported)
|
|
@@ -359,6 +461,12 @@ export class SidecarHost {
|
|
|
359
461
|
for (const p of this.runPending.values())
|
|
360
462
|
p.reject(new Error(reason));
|
|
361
463
|
this.runPending.clear();
|
|
464
|
+
// A cancel in flight when the connection drops did not get a considered
|
|
465
|
+
// answer -- resolve `unsupported` (not reject) so a Stop click during a
|
|
466
|
+
// drop does not hang the caller awaiting an ok/reason pair.
|
|
467
|
+
for (const p of this.cancelPending.values())
|
|
468
|
+
p({ ok: false, reason: 'unsupported' });
|
|
469
|
+
this.cancelPending.clear();
|
|
362
470
|
for (const p of this.acpPending.values())
|
|
363
471
|
p.reject(new Error(reason));
|
|
364
472
|
this.acpPending.clear();
|
|
@@ -397,6 +505,18 @@ export class SidecarHost {
|
|
|
397
505
|
for (const p of this.pausePending.values())
|
|
398
506
|
p.reject(new Error(reason));
|
|
399
507
|
this.pausePending.clear();
|
|
508
|
+
for (const p of this.crewDeployPending.values())
|
|
509
|
+
p.reject(new Error(reason));
|
|
510
|
+
this.crewDeployPending.clear();
|
|
511
|
+
for (const p of this.approvalsPending.values())
|
|
512
|
+
p.reject(new Error(reason));
|
|
513
|
+
this.approvalsPending.clear();
|
|
514
|
+
for (const p of this.checksPending.values())
|
|
515
|
+
p.reject(new Error(reason));
|
|
516
|
+
this.checksPending.clear();
|
|
517
|
+
for (const p of this.releasePending.values())
|
|
518
|
+
p.reject(new Error(reason));
|
|
519
|
+
this.releasePending.clear();
|
|
400
520
|
for (const p of this.hostsPending.values())
|
|
401
521
|
p.reject(new Error(reason));
|
|
402
522
|
this.hostsPending.clear();
|
|
@@ -434,6 +554,9 @@ export class SidecarHost {
|
|
|
434
554
|
for (const p of this.mcpAdminPending.values())
|
|
435
555
|
p.reject(new Error(reason));
|
|
436
556
|
this.mcpAdminPending.clear();
|
|
557
|
+
for (const p of this.pushPending.values())
|
|
558
|
+
p.reject(new Error(reason));
|
|
559
|
+
this.pushPending.clear();
|
|
437
560
|
/* The rejections above already drop each login's waiter via the wrapper
|
|
438
561
|
* mcpLogin installs. Cleared here too so that stays true if sendMcpAdmin
|
|
439
562
|
* is ever the one carrying a login -- a stale waiter would fire a consent
|
|
@@ -447,12 +570,19 @@ export class SidecarHost {
|
|
|
447
570
|
switch (m.t) {
|
|
448
571
|
case 'welcome': {
|
|
449
572
|
this.features = m.features ?? [];
|
|
573
|
+
// Runtime-validated: an untrusted/older daemon's `vapidPublicKey` is
|
|
574
|
+
// just a JSON-parsed `string`, and a malformed one must never be
|
|
575
|
+
// handed to the Push API. Anything that isn't the expected
|
|
576
|
+
// URL-safe-base64, 87-char shape is treated as "no key offered".
|
|
577
|
+
this.vapidKey = typeof m.vapidPublicKey === 'string' && /^[A-Za-z0-9_-]{87}$/.test(m.vapidPublicKey) ? m.vapidPublicKey : null;
|
|
450
578
|
// Runtime-validated like `info.id`/`info.name` next door: the welcome
|
|
451
579
|
// crossed the wire as `JSON.parse(...) as SidecarMsg`, so the declared
|
|
452
580
|
// `string` guarantees nothing, and this value decides which
|
|
453
581
|
// registrations a management surface treats as this browser's own.
|
|
454
582
|
// Anything malformed is dropped to "not told", never coerced.
|
|
455
583
|
this.dirName = typeof m.dirName === 'string' && m.dirName ? m.dirName : null;
|
|
584
|
+
this.identity = m.identity && typeof m.identity === 'object' && typeof m.identity.verified === 'boolean'
|
|
585
|
+
? m.identity : undefined;
|
|
456
586
|
this.info = m.info ?? {};
|
|
457
587
|
for (const decl of m.tools) {
|
|
458
588
|
const tool = { ...decl, run: (args) => this.invoke(decl.name, args) };
|
|
@@ -468,6 +598,10 @@ export class SidecarHost {
|
|
|
468
598
|
this.welcome = null;
|
|
469
599
|
return;
|
|
470
600
|
}
|
|
601
|
+
case 'identity-challenge': {
|
|
602
|
+
void this.answerIdentityChallenge(m);
|
|
603
|
+
return;
|
|
604
|
+
}
|
|
471
605
|
case 'invoke-result': {
|
|
472
606
|
const p = this.invokePending.get(m.id);
|
|
473
607
|
if (!p)
|
|
@@ -570,8 +704,10 @@ export class SidecarHost {
|
|
|
570
704
|
}
|
|
571
705
|
case 'run-accepted': {
|
|
572
706
|
const p = this.runPending.get(m.id);
|
|
573
|
-
if (p)
|
|
707
|
+
if (p) {
|
|
574
708
|
p.runId = m.runId;
|
|
709
|
+
p.onAccepted?.(m.runId);
|
|
710
|
+
}
|
|
575
711
|
return;
|
|
576
712
|
}
|
|
577
713
|
case 'event': {
|
|
@@ -594,6 +730,12 @@ export class SidecarHost {
|
|
|
594
730
|
p.reject(new Error(m.error || 'run failed'));
|
|
595
731
|
return;
|
|
596
732
|
}
|
|
733
|
+
case 'cancel-run-result': {
|
|
734
|
+
const p = this.cancelPending.get(m.id);
|
|
735
|
+
this.cancelPending.delete(m.id);
|
|
736
|
+
p?.(m.ok ? { ok: true } : { ok: false, ...(m.reason ? { reason: m.reason } : {}) });
|
|
737
|
+
return;
|
|
738
|
+
}
|
|
597
739
|
case 'config-result': {
|
|
598
740
|
const p = this.configPending.get(m.id);
|
|
599
741
|
if (!p)
|
|
@@ -616,6 +758,17 @@ export class SidecarHost {
|
|
|
616
758
|
p.reject(new Error(m.error || 'host sync failed'));
|
|
617
759
|
return;
|
|
618
760
|
}
|
|
761
|
+
case 'push-result': {
|
|
762
|
+
const p = this.pushPending.get(m.id);
|
|
763
|
+
if (!p)
|
|
764
|
+
return;
|
|
765
|
+
this.pushPending.delete(m.id);
|
|
766
|
+
if (m.ok)
|
|
767
|
+
p.resolve();
|
|
768
|
+
else
|
|
769
|
+
p.reject(new Error(m.error));
|
|
770
|
+
return;
|
|
771
|
+
}
|
|
619
772
|
case 'permission-ask': {
|
|
620
773
|
const { t: _t, ...ask } = m;
|
|
621
774
|
this.opts.onPermissionAsk?.(ask);
|
|
@@ -849,6 +1002,25 @@ export class SidecarHost {
|
|
|
849
1002
|
this.opts.onRegistrations?.(m.registrations);
|
|
850
1003
|
return;
|
|
851
1004
|
}
|
|
1005
|
+
case 'approvals-suggest': {
|
|
1006
|
+
const { t: _t, ...offer } = m;
|
|
1007
|
+
this.opts.onApprovalSuggestion?.(offer);
|
|
1008
|
+
return;
|
|
1009
|
+
}
|
|
1010
|
+
case 'approvals-accept-result':
|
|
1011
|
+
case 'approvals-list-result':
|
|
1012
|
+
case 'approvals-revoke-result': {
|
|
1013
|
+
const p = this.approvalsPending.get(m.id);
|
|
1014
|
+
if (!p)
|
|
1015
|
+
return;
|
|
1016
|
+
this.approvalsPending.delete(m.id);
|
|
1017
|
+
if (!m.ok) {
|
|
1018
|
+
p.resolve({ ok: false, error: m.error });
|
|
1019
|
+
return;
|
|
1020
|
+
}
|
|
1021
|
+
p.resolve(m.t === 'approvals-accept-result' ? { ok: true } : { ok: true, state: m.state });
|
|
1022
|
+
return;
|
|
1023
|
+
}
|
|
852
1024
|
case 'room-open-result': {
|
|
853
1025
|
const p = this.roomOpenPending.get(m.id);
|
|
854
1026
|
if (!p)
|
|
@@ -881,6 +1053,33 @@ export class SidecarHost {
|
|
|
881
1053
|
p.resolve(m.ok ? { ok: true, pauses: m.pauses } : { ok: false, error: m.error });
|
|
882
1054
|
return;
|
|
883
1055
|
}
|
|
1056
|
+
case 'crew-deploy-result': {
|
|
1057
|
+
// One pending map for all six crew-* requests: they share one reply shape.
|
|
1058
|
+
const p = this.crewDeployPending.get(m.id);
|
|
1059
|
+
if (!p)
|
|
1060
|
+
return;
|
|
1061
|
+
this.crewDeployPending.delete(m.id);
|
|
1062
|
+
p.resolve(m.reply);
|
|
1063
|
+
return;
|
|
1064
|
+
}
|
|
1065
|
+
case 'checks-get-result':
|
|
1066
|
+
case 'checks-pin-result': {
|
|
1067
|
+
// One map for both: each answers with the complete state.
|
|
1068
|
+
const p = this.checksPending.get(m.id);
|
|
1069
|
+
if (!p)
|
|
1070
|
+
return;
|
|
1071
|
+
this.checksPending.delete(m.id);
|
|
1072
|
+
p.resolve(m.ok ? { ok: true, state: m.state } : { ok: false, error: m.error });
|
|
1073
|
+
return;
|
|
1074
|
+
}
|
|
1075
|
+
case 'workspace-release-result': {
|
|
1076
|
+
const p = this.releasePending.get(m.id);
|
|
1077
|
+
if (!p)
|
|
1078
|
+
return;
|
|
1079
|
+
this.releasePending.delete(m.id);
|
|
1080
|
+
p.resolve(m.ok ? { ok: true, released: m.released } : { ok: false, error: m.error });
|
|
1081
|
+
return;
|
|
1082
|
+
}
|
|
884
1083
|
case 'room-list-result': {
|
|
885
1084
|
const p = this.roomListPending.get(m.id);
|
|
886
1085
|
if (!p)
|
|
@@ -1211,6 +1410,12 @@ export class SidecarHost {
|
|
|
1211
1410
|
/** The sidecar's advertised self-description (root/platform), or {} if it sent
|
|
1212
1411
|
* none. Valid only after connect() resolves. */
|
|
1213
1412
|
sidecarInfo() { return this.info; }
|
|
1413
|
+
/** The daemon's verdict on the identity challenge/assert exchange for this
|
|
1414
|
+
* connection. `null` means the daemon does not verify at all (no `identity`
|
|
1415
|
+
* on `welcome` -- no verifier configured, or an older daemon), which is
|
|
1416
|
+
* distinct from `false` (the exchange ran and was rejected, or the
|
|
1417
|
+
* assertion never arrived). Valid only after connect() resolves. */
|
|
1418
|
+
identityVerified() { return this.identity ? this.identity.verified : null; }
|
|
1214
1419
|
/** Fetch a (loopback) URL THROUGH the sidecar — a server-side fetch on the user's
|
|
1215
1420
|
* machine, so the browser dodges CORS. Returns a streaming Response: it resolves
|
|
1216
1421
|
* as soon as headers arrive (http-res) and the body streams in via http-chunk. */
|
|
@@ -1251,7 +1456,7 @@ export class SidecarHost {
|
|
|
1251
1456
|
run(agent, input, onEvent, opts) {
|
|
1252
1457
|
const id = ++this.runSeq;
|
|
1253
1458
|
return new Promise((resolve, reject) => {
|
|
1254
|
-
this.runPending.set(id, { resolve, reject, onEvent });
|
|
1459
|
+
this.runPending.set(id, { resolve, reject, onEvent, onAccepted: opts?.onAccepted });
|
|
1255
1460
|
// `sessionId` asks for the run to happen INSIDE that session. A daemon
|
|
1256
1461
|
// that cannot honour it answers run-error rather than running it on the
|
|
1257
1462
|
// host, so the caller learns rather than being quietly downgraded.
|
|
@@ -1261,9 +1466,52 @@ export class SidecarHost {
|
|
|
1261
1466
|
t: 'run', id, agent, input,
|
|
1262
1467
|
...(opts?.sessionId ? { sessionId: opts.sessionId } : {}),
|
|
1263
1468
|
...(opts?.keepTranscript ? { keepTranscript: true } : {}),
|
|
1469
|
+
...(opts?.task ? { task: opts.task } : {}),
|
|
1470
|
+
...(opts?.autonomy ? { autonomy: opts.autonomy } : {}),
|
|
1264
1471
|
});
|
|
1265
1472
|
});
|
|
1266
1473
|
}
|
|
1474
|
+
/** WATCH a run this client did not start (a trigger-fired crew run). Rides
|
|
1475
|
+
* the same pending map as `run`, so `event`/`run-done`/`run-error` route here
|
|
1476
|
+
* unchanged; the server replays what already happened first. */
|
|
1477
|
+
watchRun(runId, onEvent) {
|
|
1478
|
+
const id = ++this.runSeq;
|
|
1479
|
+
let rejectDone;
|
|
1480
|
+
const done = new Promise((resolve, reject) => {
|
|
1481
|
+
rejectDone = reject;
|
|
1482
|
+
this.runPending.set(id, {
|
|
1483
|
+
resolve: (r) => resolve({ text: r.text, stopReason: r.stopReason }),
|
|
1484
|
+
reject,
|
|
1485
|
+
onEvent,
|
|
1486
|
+
});
|
|
1487
|
+
this.ch.send({ t: 'subscribe', id, runId });
|
|
1488
|
+
});
|
|
1489
|
+
return {
|
|
1490
|
+
done,
|
|
1491
|
+
// Client-side only: the server keeps the subscription until the run ends
|
|
1492
|
+
// or the socket drops, and events for a closed id are ignored.
|
|
1493
|
+
close: () => { if (this.runPending.delete(id))
|
|
1494
|
+
rejectDone(new Error('closed')); },
|
|
1495
|
+
};
|
|
1496
|
+
}
|
|
1497
|
+
/** Ask the daemon to cancel a running crew run. `ok: false, reason:
|
|
1498
|
+
* 'no-such-run'` means the daemon has no such run (already finished, or
|
|
1499
|
+
* never existed); `'unsupported'` means the daemon's runner has no `cancel`,
|
|
1500
|
+
* or the connection dropped before the daemon answered. */
|
|
1501
|
+
cancelRun(runId) {
|
|
1502
|
+
const id = ++this.cancelSeq;
|
|
1503
|
+
return new Promise((resolve) => {
|
|
1504
|
+
this.cancelPending.set(id, resolve);
|
|
1505
|
+
this.ch.send({ t: 'cancel-run', id, runId });
|
|
1506
|
+
});
|
|
1507
|
+
}
|
|
1508
|
+
/** True when the connected sidecar's runner supports cancelling a run. */
|
|
1509
|
+
supportsRunCancel() { return this.features.includes('run-cancel'); }
|
|
1510
|
+
/** True when the connected sidecar understands `run`'s `task`/`autonomy`
|
|
1511
|
+
* fields. An older sidecar without this feature would silently ignore
|
|
1512
|
+
* them, so callers must check this before sending either — never send and
|
|
1513
|
+
* hope. */
|
|
1514
|
+
supportsTasks() { return this.features.includes(RUN_TASK_FEATURE); }
|
|
1267
1515
|
/** Start a proxied external ACP agent on the sidecar. The returned promise
|
|
1268
1516
|
* settles on the handshake; `onUpdate` keeps receiving the agent's stream for
|
|
1269
1517
|
* the life of the session. Permission asks do NOT come through here — they use
|
|
@@ -1339,6 +1587,30 @@ export class SidecarHost {
|
|
|
1339
1587
|
this.ch.send({ t: 'host-sync-delete', id, path });
|
|
1340
1588
|
});
|
|
1341
1589
|
}
|
|
1590
|
+
/** The daemon's VAPID public key from `welcome`, or null when it offered
|
|
1591
|
+
* none (older daemon, or an offered value that failed validation). */
|
|
1592
|
+
vapidPublicKey() { return this.vapidKey; }
|
|
1593
|
+
/** True when this daemon advertises web push AND handed over a usable key. */
|
|
1594
|
+
supportsWebPush() { return this.features.includes('web-push') && this.vapidKey !== null; }
|
|
1595
|
+
/** Send a browser push subscription to the daemon to persist. Resolves on
|
|
1596
|
+
* `push-result { ok: true }`; rejects with the daemon's error, or on
|
|
1597
|
+
* disconnect while in flight. */
|
|
1598
|
+
pushSubscribe(subscription) {
|
|
1599
|
+
const id = ++this.pushSeq;
|
|
1600
|
+
return new Promise((resolve, reject) => {
|
|
1601
|
+
this.pushPending.set(id, { resolve, reject });
|
|
1602
|
+
this.ch.send({ t: 'push-subscribe', id, subscription });
|
|
1603
|
+
});
|
|
1604
|
+
}
|
|
1605
|
+
/** Tell the daemon to drop a subscription by endpoint. Same resolve/reject
|
|
1606
|
+
* contract as `pushSubscribe`. */
|
|
1607
|
+
pushUnsubscribe(endpoint) {
|
|
1608
|
+
const id = ++this.pushSeq;
|
|
1609
|
+
return new Promise((resolve, reject) => {
|
|
1610
|
+
this.pushPending.set(id, { resolve, reject });
|
|
1611
|
+
this.ch.send({ t: 'push-unsubscribe', id, endpoint });
|
|
1612
|
+
});
|
|
1613
|
+
}
|
|
1342
1614
|
/** True when this daemon serves its OWN Harness Config (`config-*`). Feature-
|
|
1343
1615
|
* detected from the welcome, so a client standing on a daemon too old to serve
|
|
1344
1616
|
* one says the config is unreachable instead of sending a message that will be
|
|
@@ -1504,6 +1776,94 @@ export class SidecarHost {
|
|
|
1504
1776
|
this.ch.send({ t: 'agent-pause-set', id, owner, agent, paused });
|
|
1505
1777
|
});
|
|
1506
1778
|
}
|
|
1779
|
+
/** True when the connected daemon keeps a deploy list (crew deploy per
|
|
1780
|
+
* machine). Feature-detected from the welcome; without it a client shows
|
|
1781
|
+
* the old read-only view. */
|
|
1782
|
+
supportsCrewDeploy() { return this.features.includes(CREW_DEPLOY_FEATURE); }
|
|
1783
|
+
/** One crew-* request. `ok: false` is the daemon's considered answer (a
|
|
1784
|
+
* refusal names its rule) and resolves; only a dropped connection rejects.
|
|
1785
|
+
* Against a daemon without the feature nothing is sent. */
|
|
1786
|
+
crewDeployRequest(send) {
|
|
1787
|
+
if (!this.supportsCrewDeploy())
|
|
1788
|
+
return Promise.resolve({ ok: false, error: 'this daemon does not keep a deploy list' });
|
|
1789
|
+
const id = ++this.crewDeploySeq;
|
|
1790
|
+
return new Promise((resolve, reject) => {
|
|
1791
|
+
this.crewDeployPending.set(id, { resolve, reject });
|
|
1792
|
+
this.ch.send(send(id));
|
|
1793
|
+
});
|
|
1794
|
+
}
|
|
1795
|
+
/** The machine's deploy list, statuses and audit. */
|
|
1796
|
+
crewDeployments() { return this.crewDeployRequest((id) => ({ t: 'crew-deployments', id })); }
|
|
1797
|
+
crewDeploy(ref, reason) { return this.crewDeployRequest((id) => ({ t: 'crew-deploy', id, ref, ...(reason ? { reason } : {}) })); }
|
|
1798
|
+
crewUndeploy(ref, reason) { return this.crewDeployRequest((id) => ({ t: 'crew-undeploy', id, ref, ...(reason ? { reason } : {}) })); }
|
|
1799
|
+
crewPause(ref, reason) { return this.crewDeployRequest((id) => ({ t: 'crew-pause', id, ref, reason })); }
|
|
1800
|
+
crewResume(ref, reason) { return this.crewDeployRequest((id) => ({ t: 'crew-resume', id, ref, ...(reason ? { reason } : {}) })); }
|
|
1801
|
+
/** Stop a run AND record who stopped it and why (the plain `cancelRun` records nothing). */
|
|
1802
|
+
crewStop(ref, runId, reason) { return this.crewDeployRequest((id) => ({ t: 'crew-stop', id, ref, runId, reason })); }
|
|
1803
|
+
/** True when the connected daemon keeps "This machine" standing approvals
|
|
1804
|
+
* (plan 3). Feature-detected from the welcome. */
|
|
1805
|
+
supportsApprovals() { return this.features.includes('approvals'); }
|
|
1806
|
+
approvalsRequest(send) {
|
|
1807
|
+
if (!this.supportsApprovals())
|
|
1808
|
+
return Promise.resolve({ ok: false, error: 'this daemon does not keep standing approvals' });
|
|
1809
|
+
const id = ++this.approvalsSeq;
|
|
1810
|
+
return new Promise((resolve, reject) => {
|
|
1811
|
+
this.approvalsPending.set(id, { resolve, reject });
|
|
1812
|
+
this.ch.send(send(id));
|
|
1813
|
+
});
|
|
1814
|
+
}
|
|
1815
|
+
/** Accept an offer this client received. The daemon checks that THIS
|
|
1816
|
+
* connection is the one it offered to; `ok: false` is its refusal (expired,
|
|
1817
|
+
* already used, offered to another connection), a real answer that
|
|
1818
|
+
* resolves. Only a dropped connection rejects. */
|
|
1819
|
+
async acceptApprovalSuggestion(suggestionId, choice) {
|
|
1820
|
+
const r = await this.approvalsRequest((id) => ({ t: 'approvals-accept', id, suggestionId, choice }));
|
|
1821
|
+
return r.ok ? { ok: true } : r;
|
|
1822
|
+
}
|
|
1823
|
+
/** The daemon's standing rules and its (daemon.json) approval settings. */
|
|
1824
|
+
async listDaemonApprovals() {
|
|
1825
|
+
return this.withState(await this.approvalsRequest((id) => ({ t: 'approvals-list', id })));
|
|
1826
|
+
}
|
|
1827
|
+
/** Remove one daemon standing rule; resolves with the complete state after. */
|
|
1828
|
+
async revokeDaemonApproval(tool) {
|
|
1829
|
+
return this.withState(await this.approvalsRequest((id) => ({ t: 'approvals-revoke', id, tool })));
|
|
1830
|
+
}
|
|
1831
|
+
withState(r) {
|
|
1832
|
+
if (!r.ok)
|
|
1833
|
+
return r;
|
|
1834
|
+
return r.state ? { ok: true, state: r.state } : { ok: false, error: 'the daemon answered without its approvals state' };
|
|
1835
|
+
}
|
|
1836
|
+
/** True when the daemon runs confirmed checks and holds the orchestrator's
|
|
1837
|
+
* workspace lock (it was started with --allow-shell, and its state dir is
|
|
1838
|
+
* not inside an approved root). */
|
|
1839
|
+
supportsChecks() { return this.features.includes('checks'); }
|
|
1840
|
+
/** The folder's checks file, whether it is confirmed, and proposals. `ok:
|
|
1841
|
+
* false` is the daemon's answer (no checks here, a bad path); only a dropped
|
|
1842
|
+
* connection rejects. */
|
|
1843
|
+
checksGet(root, stateDir) {
|
|
1844
|
+
const id = ++this.checksSeq;
|
|
1845
|
+
return new Promise((resolve, reject) => {
|
|
1846
|
+
this.checksPending.set(id, { resolve, reject });
|
|
1847
|
+
this.ch.send({ t: 'checks-get', id, root, stateDir });
|
|
1848
|
+
});
|
|
1849
|
+
}
|
|
1850
|
+
/** Write the content the user confirmed and pin its hash. */
|
|
1851
|
+
checksPin(root, stateDir, content) {
|
|
1852
|
+
const id = ++this.checksSeq;
|
|
1853
|
+
return new Promise((resolve, reject) => {
|
|
1854
|
+
this.checksPending.set(id, { resolve, reject });
|
|
1855
|
+
this.ch.send({ t: 'checks-pin', id, root, stateDir, content });
|
|
1856
|
+
});
|
|
1857
|
+
}
|
|
1858
|
+
/** Hand back a run's workspace lock. `released: false` means this run did
|
|
1859
|
+
* not hold it (already released, or held by another run). */
|
|
1860
|
+
workspaceRelease(ref) {
|
|
1861
|
+
const id = ++this.checksSeq;
|
|
1862
|
+
return new Promise((resolve, reject) => {
|
|
1863
|
+
this.releasePending.set(id, { resolve, reject });
|
|
1864
|
+
this.ch.send({ t: 'workspace-release', id, root: ref.root, stateDir: ref.stateDir, runId: ref.runId });
|
|
1865
|
+
});
|
|
1866
|
+
}
|
|
1507
1867
|
/** Ask the daemon to open a room. `ok: false` here is the daemon's CONSIDERED
|
|
1508
1868
|
* refusal (validation problems, or an unreachable participant) — a real answer,
|
|
1509
1869
|
* not a transport failure, so it resolves rather than rejects. Only a dropped
|