@corenel/harness 0.20.0 → 0.22.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/chat/engine.d.ts +53 -0
- package/dist/chat/engine.d.ts.map +1 -0
- package/dist/chat/engine.js +885 -0
- package/dist/chat/engine.js.map +1 -0
- package/dist/chat/events.d.ts +5 -0
- package/dist/chat/events.d.ts.map +1 -0
- package/dist/chat/events.js +100 -0
- package/dist/chat/events.js.map +1 -0
- package/dist/chat/index.d.ts +9 -0
- package/dist/chat/index.d.ts.map +1 -0
- package/dist/chat/index.js +9 -0
- package/dist/chat/index.js.map +1 -0
- package/dist/chat/liveFold.d.ts +10 -0
- package/dist/chat/liveFold.d.ts.map +1 -0
- package/dist/chat/liveFold.js +31 -0
- package/dist/chat/liveFold.js.map +1 -0
- package/dist/chat/messages.d.ts +20 -0
- package/dist/chat/messages.d.ts.map +1 -0
- package/dist/chat/messages.js +56 -0
- package/dist/chat/messages.js.map +1 -0
- package/dist/chat/persist.d.ts +47 -0
- package/dist/chat/persist.d.ts.map +1 -0
- package/dist/chat/persist.js +118 -0
- package/dist/chat/persist.js.map +1 -0
- package/dist/chat/runWatchdog.d.ts +21 -0
- package/dist/chat/runWatchdog.d.ts.map +1 -0
- package/dist/chat/runWatchdog.js +92 -0
- package/dist/chat/runWatchdog.js.map +1 -0
- package/dist/chat/stitch.d.ts +26 -0
- package/dist/chat/stitch.d.ts.map +1 -0
- package/dist/chat/stitch.js +40 -0
- package/dist/chat/stitch.js.map +1 -0
- package/dist/chat/types.d.ts +163 -0
- package/dist/chat/types.d.ts.map +1 -0
- package/dist/chat/types.js +2 -0
- package/dist/chat/types.js.map +1 -0
- package/dist/core/loop.d.ts.map +1 -1
- package/dist/core/loop.js +50 -15
- package/dist/core/loop.js.map +1 -1
- package/dist/core/spawn.d.ts +10 -0
- package/dist/core/spawn.d.ts.map +1 -1
- package/dist/core/spawn.js +28 -5
- package/dist/core/spawn.js.map +1 -1
- package/dist/fileSchema.js +1 -1
- package/dist/guardrail/builtins.d.ts.map +1 -1
- package/dist/guardrail/builtins.js +11 -6
- package/dist/guardrail/builtins.js.map +1 -1
- package/dist/guardrail/guardrail.d.ts +8 -0
- package/dist/guardrail/guardrail.d.ts.map +1 -1
- package/dist/guardrail/guardrail.js +12 -0
- package/dist/guardrail/guardrail.js.map +1 -1
- package/dist/guardrail/index.d.ts +1 -1
- package/dist/guardrail/index.d.ts.map +1 -1
- package/dist/guardrail/index.js +1 -1
- package/dist/guardrail/index.js.map +1 -1
- package/dist/guardrail/serialize.d.ts.map +1 -1
- package/dist/guardrail/serialize.js +3 -0
- package/dist/guardrail/serialize.js.map +1 -1
- package/dist/guardrail/validate.d.ts.map +1 -1
- package/dist/guardrail/validate.js +4 -0
- package/dist/guardrail/validate.js.map +1 -1
- package/dist/orchestrator/sidecarTools.d.ts.map +1 -1
- package/dist/orchestrator/sidecarTools.js +8 -4
- package/dist/orchestrator/sidecarTools.js.map +1 -1
- package/dist/prompts/fitProject.d.ts +6 -0
- package/dist/prompts/fitProject.d.ts.map +1 -0
- package/dist/prompts/fitProject.js +6 -0
- package/dist/prompts/fitProject.js.map +1 -0
- package/dist/prompts/library/defaults.generated.js +9 -9
- package/dist/prompts/library/defaults.generated.js.map +1 -1
- package/dist/prompts/library/index.d.ts +2 -1
- package/dist/prompts/library/index.d.ts.map +1 -1
- package/dist/prompts/library/index.js +11 -16
- package/dist/prompts/library/index.js.map +1 -1
- package/dist/providers/gateway.d.ts +1 -0
- package/dist/providers/gateway.d.ts.map +1 -1
- package/dist/providers/gateway.js +1 -0
- package/dist/providers/gateway.js.map +1 -1
- package/dist/sessions/fileSessionStore.d.ts.map +1 -1
- package/dist/sessions/fileSessionStore.js.map +1 -1
- package/dist/sidecar/askUserBroker.d.ts +40 -0
- package/dist/sidecar/askUserBroker.d.ts.map +1 -0
- package/dist/sidecar/askUserBroker.js +103 -0
- package/dist/sidecar/askUserBroker.js.map +1 -0
- package/dist/sidecar/fleetJwe.d.ts +25 -0
- package/dist/sidecar/fleetJwe.d.ts.map +1 -0
- package/dist/sidecar/fleetJwe.js +115 -0
- package/dist/sidecar/fleetJwe.js.map +1 -0
- package/dist/sidecar/fsChanges.d.ts +23 -0
- package/dist/sidecar/fsChanges.d.ts.map +1 -0
- package/dist/sidecar/fsChanges.js +77 -0
- package/dist/sidecar/fsChanges.js.map +1 -0
- package/dist/sidecar/host.d.ts +91 -1
- package/dist/sidecar/host.d.ts.map +1 -1
- package/dist/sidecar/host.js +269 -6
- package/dist/sidecar/host.js.map +1 -1
- package/dist/sidecar/identity.d.ts +4 -0
- package/dist/sidecar/identity.d.ts.map +1 -1
- package/dist/sidecar/memberGate.d.ts.map +1 -1
- package/dist/sidecar/memberGate.js +5 -0
- package/dist/sidecar/memberGate.js.map +1 -1
- package/dist/sidecar/server.d.ts +61 -4
- package/dist/sidecar/server.d.ts.map +1 -1
- package/dist/sidecar/server.js +583 -13
- package/dist/sidecar/server.js.map +1 -1
- package/dist/systemParts.d.ts +0 -1
- package/dist/systemParts.d.ts.map +1 -1
- package/dist/systemParts.js +6 -2
- package/dist/systemParts.js.map +1 -1
- package/dist/todos.d.ts +8 -0
- package/dist/todos.d.ts.map +1 -1
- package/dist/todos.js +21 -17
- package/dist/todos.js.map +1 -1
- package/dist/tools/builtins.d.ts +14 -1
- package/dist/tools/builtins.d.ts.map +1 -1
- package/dist/tools/builtins.js +27 -3
- package/dist/tools/builtins.js.map +1 -1
- package/dist/tools/namespaces.d.ts +8 -0
- package/dist/tools/namespaces.d.ts.map +1 -1
- package/dist/tools/namespaces.js +18 -0
- package/dist/tools/namespaces.js.map +1 -1
- package/dist/tools/skillTool.d.ts.map +1 -1
- package/dist/tools/skillTool.js +22 -1
- package/dist/tools/skillTool.js.map +1 -1
- package/dist/tools/toolFacts.d.ts +9 -0
- package/dist/tools/toolFacts.d.ts.map +1 -0
- package/dist/tools/toolFacts.js +19 -0
- package/dist/tools/toolFacts.js.map +1 -0
- package/dist/tools/toolSearch.d.ts +25 -6
- package/dist/tools/toolSearch.d.ts.map +1 -1
- package/dist/tools/toolSearch.js +62 -14
- package/dist/tools/toolSearch.js.map +1 -1
- package/dist/tools/workflowTool.d.ts +34 -0
- package/dist/tools/workflowTool.d.ts.map +1 -0
- package/dist/tools/workflowTool.js +122 -0
- package/dist/tools/workflowTool.js.map +1 -0
- package/package.json +6 -3
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
/** One seat's delivery; a dead connection must not stop the others'. */
|
|
2
|
+
function deliver(seat, prompt) {
|
|
3
|
+
try {
|
|
4
|
+
seat.ask(prompt);
|
|
5
|
+
}
|
|
6
|
+
catch {
|
|
7
|
+
// The seat's own connection is gone; its leave follows.
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
export class AskUserBroker {
|
|
11
|
+
constructor() {
|
|
12
|
+
Object.defineProperty(this, "seq", {
|
|
13
|
+
enumerable: true,
|
|
14
|
+
configurable: true,
|
|
15
|
+
writable: true,
|
|
16
|
+
value: 0
|
|
17
|
+
});
|
|
18
|
+
Object.defineProperty(this, "seats", {
|
|
19
|
+
enumerable: true,
|
|
20
|
+
configurable: true,
|
|
21
|
+
writable: true,
|
|
22
|
+
value: new Set()
|
|
23
|
+
});
|
|
24
|
+
Object.defineProperty(this, "open", {
|
|
25
|
+
enumerable: true,
|
|
26
|
+
configurable: true,
|
|
27
|
+
writable: true,
|
|
28
|
+
value: new Map()
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
/** Seat a connection. Asks still open for its user are delivered to it at
|
|
32
|
+
* once, so a reconnecting device picks up a question already showing
|
|
33
|
+
* elsewhere. Returns the leave function. */
|
|
34
|
+
join(seat) {
|
|
35
|
+
this.seats.add(seat);
|
|
36
|
+
for (const a of this.open.values()) {
|
|
37
|
+
if (!a.users.has(seat.userId) || a.shown.has(seat))
|
|
38
|
+
continue;
|
|
39
|
+
a.shown.add(seat);
|
|
40
|
+
deliver(seat, a.prompt);
|
|
41
|
+
}
|
|
42
|
+
return () => {
|
|
43
|
+
this.seats.delete(seat);
|
|
44
|
+
for (const a of this.open.values())
|
|
45
|
+
a.shown.delete(seat);
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
/** Ask `users`. Resolves with the first answer from a seat it was shown to,
|
|
49
|
+
* or null once `signal` ends. Nobody seated yet is not an answer: the ask
|
|
50
|
+
* waits for one of them to join or for the signal. */
|
|
51
|
+
ask(q, signal) {
|
|
52
|
+
if (signal.aborted)
|
|
53
|
+
return Promise.resolve(null);
|
|
54
|
+
const users = new Set(q.users.filter((u) => typeof u === 'string' && u !== ''));
|
|
55
|
+
const id = ++this.seq;
|
|
56
|
+
const prompt = { id, runId: q.runId, agent: q.agent, questions: q.questions, deadline: q.deadline };
|
|
57
|
+
return new Promise((resolve) => {
|
|
58
|
+
const onAbort = () => { this.finish(id, null, 'timeout', null); };
|
|
59
|
+
const entry = {
|
|
60
|
+
prompt, users, shown: new Set(),
|
|
61
|
+
settle: (answers) => {
|
|
62
|
+
signal.removeEventListener('abort', onAbort);
|
|
63
|
+
resolve(answers);
|
|
64
|
+
},
|
|
65
|
+
};
|
|
66
|
+
this.open.set(id, entry);
|
|
67
|
+
signal.addEventListener('abort', onAbort, { once: true });
|
|
68
|
+
for (const seat of this.seats) {
|
|
69
|
+
if (!users.has(seat.userId))
|
|
70
|
+
continue;
|
|
71
|
+
entry.shown.add(seat);
|
|
72
|
+
deliver(seat, prompt);
|
|
73
|
+
}
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
/** `seat` answers ask `id`. Ignored, returning false, for an unknown or
|
|
77
|
+
* settled id or a seat the ask was never shown to. */
|
|
78
|
+
answer(id, seat, answers) {
|
|
79
|
+
const a = this.open.get(id);
|
|
80
|
+
if (!a || !a.shown.has(seat) || !this.seats.has(seat))
|
|
81
|
+
return false;
|
|
82
|
+
this.finish(id, answers, 'client', seat);
|
|
83
|
+
return true;
|
|
84
|
+
}
|
|
85
|
+
finish(id, answers, by, answeredBy) {
|
|
86
|
+
const a = this.open.get(id);
|
|
87
|
+
if (!a)
|
|
88
|
+
return;
|
|
89
|
+
this.open.delete(id);
|
|
90
|
+
a.settle(answers);
|
|
91
|
+
for (const seat of a.shown) {
|
|
92
|
+
if (seat === answeredBy)
|
|
93
|
+
continue;
|
|
94
|
+
try {
|
|
95
|
+
seat.resolved(id, by);
|
|
96
|
+
}
|
|
97
|
+
catch {
|
|
98
|
+
// One dead connection must not keep the others' prompts up.
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
//# sourceMappingURL=askUserBroker.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"askUserBroker.js","sourceRoot":"","sources":["../../sidecar/askUserBroker.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { EcPublicJwk } from '@corenel/protocol';
|
|
2
|
+
export interface EncryptionKeyPair {
|
|
3
|
+
publicJwk: EcPublicJwk;
|
|
4
|
+
privateKey: CryptoKey;
|
|
5
|
+
}
|
|
6
|
+
export declare const JWE_ALG = "ECDH-ES";
|
|
7
|
+
export declare const JWE_ENC = "A256GCM";
|
|
8
|
+
/** The Concat KDF of RFC 7518 4.6.2 (NIST SP 800-56A, SHA-256) for a key of at
|
|
9
|
+
* most 256 bits, so one hash round: SHA-256(counter 1 || Z || OtherInfo). For
|
|
10
|
+
* ECDH-ES direct agreement `algorithmId` is the `enc` value. */
|
|
11
|
+
export declare function concatKdf(z: Uint8Array, algorithmId: string, keyBits: number, apu?: Uint8Array, apv?: Uint8Array): Promise<Uint8Array<ArrayBuffer>>;
|
|
12
|
+
/** A fresh encryption key pair. The private JWK is for the caller to store
|
|
13
|
+
* (mode 0600, like the node signing key); load it back with importEncryptionPrivateKey. */
|
|
14
|
+
export declare function generateEncryptionKey(): Promise<{
|
|
15
|
+
publicJwk: EcPublicJwk;
|
|
16
|
+
privateJwk: JsonWebKey;
|
|
17
|
+
}>;
|
|
18
|
+
/** The stored private JWK as a non-extractable ECDH key that can only derive bits. */
|
|
19
|
+
export declare function importEncryptionPrivateKey(privateJwk: JsonWebKey): Promise<CryptoKey>;
|
|
20
|
+
/** `plaintext` sealed to `recipient`: compact JWE with header { alg: ECDH-ES, enc: A256GCM, typ, epk } and an empty encrypted key. */
|
|
21
|
+
export declare function sealJwe(recipient: EcPublicJwk, typ: string, plaintext: string): Promise<string>;
|
|
22
|
+
/** The plaintext if `jwe` is a compact JWE of purpose `typ` sealed to `privateKey`, else null. Never throws:
|
|
23
|
+
* a wrong key, a wrong typ, any other header member, a changed byte, or a malformed token is null. */
|
|
24
|
+
export declare function openJwe(privateKey: CryptoKey, typ: string, jwe: string): Promise<string | null>;
|
|
25
|
+
//# sourceMappingURL=fleetJwe.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fleetJwe.d.ts","sourceRoot":"","sources":["../../sidecar/fleetJwe.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAGrD,MAAM,WAAW,iBAAiB;IAAG,SAAS,EAAE,WAAW,CAAC;IAAC,UAAU,EAAE,SAAS,CAAA;CAAE;AAEpF,eAAO,MAAM,OAAO,YAAY,CAAC;AACjC,eAAO,MAAM,OAAO,YAAY,CAAC;AA0BjC;;gEAEgE;AAChE,wBAAsB,SAAS,CAC7B,CAAC,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,GAAG,GAAE,UAA8B,EAAE,GAAG,GAAE,UAA8B,GAC5H,OAAO,CAAC,UAAU,CAAC,WAAW,CAAC,CAAC,CAKlC;AAQD;2FAC2F;AAC3F,wBAAsB,qBAAqB,IAAI,OAAO,CAAC;IAAE,SAAS,EAAE,WAAW,CAAC;IAAC,UAAU,EAAE,UAAU,CAAA;CAAE,CAAC,CAMzG;AAED,sFAAsF;AACtF,wBAAsB,0BAA0B,CAAC,UAAU,EAAE,UAAU,GAAG,OAAO,CAAC,SAAS,CAAC,CAO3F;AAED,sIAAsI;AACtI,wBAAsB,OAAO,CAAC,SAAS,EAAE,WAAW,EAAE,GAAG,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAiBrG;AAED;sGACsG;AACtG,wBAAsB,OAAO,CAAC,UAAU,EAAE,SAAS,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAwBrG"}
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import { asEcPublicJwk, b64urlToBytes, bytesToB64url } from './deviceKeys.js';
|
|
2
|
+
export const JWE_ALG = 'ECDH-ES';
|
|
3
|
+
export const JWE_ENC = 'A256GCM';
|
|
4
|
+
const ECDH = { name: 'ECDH', namedCurve: 'P-256' };
|
|
5
|
+
const HEADER_KEYS = ['alg', 'enc', 'typ', 'epk'];
|
|
6
|
+
const IV_BYTES = 12;
|
|
7
|
+
const TAG_BYTES = 16;
|
|
8
|
+
const B64URL = /^[A-Za-z0-9_-]+$/;
|
|
9
|
+
const te = new TextEncoder();
|
|
10
|
+
const isObj = (v) => typeof v === 'object' && v !== null && !Array.isArray(v);
|
|
11
|
+
function concat(...parts) {
|
|
12
|
+
const out = new Uint8Array(parts.reduce((n, p) => n + p.length, 0));
|
|
13
|
+
let at = 0;
|
|
14
|
+
for (const p of parts) {
|
|
15
|
+
out.set(p, at);
|
|
16
|
+
at += p.length;
|
|
17
|
+
}
|
|
18
|
+
return out;
|
|
19
|
+
}
|
|
20
|
+
function u32(n) {
|
|
21
|
+
const out = new Uint8Array(4);
|
|
22
|
+
new DataView(out.buffer).setUint32(0, n);
|
|
23
|
+
return out;
|
|
24
|
+
}
|
|
25
|
+
const lengthPrefixed = (b) => concat(u32(b.length), b);
|
|
26
|
+
/** The Concat KDF of RFC 7518 4.6.2 (NIST SP 800-56A, SHA-256) for a key of at
|
|
27
|
+
* most 256 bits, so one hash round: SHA-256(counter 1 || Z || OtherInfo). For
|
|
28
|
+
* ECDH-ES direct agreement `algorithmId` is the `enc` value. */
|
|
29
|
+
export async function concatKdf(z, algorithmId, keyBits, apu = new Uint8Array(0), apv = new Uint8Array(0)) {
|
|
30
|
+
if (!Number.isInteger(keyBits) || keyBits < 8 || keyBits > 256 || keyBits % 8 !== 0)
|
|
31
|
+
throw new Error('concatKdf derives 8 to 256 bits, in whole bytes');
|
|
32
|
+
const otherInfo = concat(lengthPrefixed(te.encode(algorithmId)), lengthPrefixed(apu), lengthPrefixed(apv), u32(keyBits));
|
|
33
|
+
const digest = new Uint8Array(await crypto.subtle.digest('SHA-256', concat(u32(1), z, otherInfo)));
|
|
34
|
+
return digest.slice(0, keyBits / 8);
|
|
35
|
+
}
|
|
36
|
+
async function contentKey(privateKey, peer, usage) {
|
|
37
|
+
const pub = await crypto.subtle.importKey('jwk', { kty: peer.kty, crv: peer.crv, x: peer.x, y: peer.y }, ECDH, false, []);
|
|
38
|
+
const z = new Uint8Array(await crypto.subtle.deriveBits({ name: 'ECDH', public: pub }, privateKey, 256));
|
|
39
|
+
return crypto.subtle.importKey('raw', await concatKdf(z, JWE_ENC, 256), { name: 'AES-GCM' }, false, [usage]);
|
|
40
|
+
}
|
|
41
|
+
/** A fresh encryption key pair. The private JWK is for the caller to store
|
|
42
|
+
* (mode 0600, like the node signing key); load it back with importEncryptionPrivateKey. */
|
|
43
|
+
export async function generateEncryptionKey() {
|
|
44
|
+
const pair = await crypto.subtle.generateKey(ECDH, true, ['deriveBits']);
|
|
45
|
+
const privateJwk = { ...(await crypto.subtle.exportKey('jwk', pair.privateKey)), key_ops: ['deriveBits'] };
|
|
46
|
+
const publicJwk = asEcPublicJwk({ kty: privateJwk.kty, crv: privateJwk.crv, x: privateJwk.x, y: privateJwk.y });
|
|
47
|
+
if (!publicJwk || typeof privateJwk.d !== 'string')
|
|
48
|
+
throw new Error('not a P-256 key');
|
|
49
|
+
return { publicJwk, privateJwk };
|
|
50
|
+
}
|
|
51
|
+
/** The stored private JWK as a non-extractable ECDH key that can only derive bits. */
|
|
52
|
+
export async function importEncryptionPrivateKey(privateJwk) {
|
|
53
|
+
const pub = asEcPublicJwk({ kty: privateJwk.kty, crv: privateJwk.crv, x: privateJwk.x, y: privateJwk.y });
|
|
54
|
+
if (!pub || typeof privateJwk.d !== 'string')
|
|
55
|
+
throw new Error('not a P-256 private key');
|
|
56
|
+
/* Only a key stored as an encryption key imports. The node signing key is stored as bare { kty, crv, x, y, d } with
|
|
57
|
+
* no key_ops, so a missing key_ops is refused too: a signing key never doubles as a key-agreement key. */
|
|
58
|
+
if (!privateJwk.key_ops?.includes('deriveBits'))
|
|
59
|
+
throw new Error('not an encryption key');
|
|
60
|
+
return crypto.subtle.importKey('jwk', { kty: 'EC', crv: 'P-256', x: pub.x, y: pub.y, d: privateJwk.d }, ECDH, false, ['deriveBits']);
|
|
61
|
+
}
|
|
62
|
+
/** `plaintext` sealed to `recipient`: compact JWE with header { alg: ECDH-ES, enc: A256GCM, typ, epk } and an empty encrypted key. */
|
|
63
|
+
export async function sealJwe(recipient, typ, plaintext) {
|
|
64
|
+
const to = asEcPublicJwk(recipient);
|
|
65
|
+
if (!to)
|
|
66
|
+
throw new Error('the recipient is not a P-256 public key');
|
|
67
|
+
const eph = await crypto.subtle.generateKey(ECDH, true, ['deriveBits']);
|
|
68
|
+
const e = await crypto.subtle.exportKey('jwk', eph.publicKey);
|
|
69
|
+
const epk = asEcPublicJwk({ kty: e.kty, crv: e.crv, x: e.x, y: e.y });
|
|
70
|
+
if (!epk)
|
|
71
|
+
throw new Error('not a P-256 key');
|
|
72
|
+
const header = bytesToB64url(te.encode(JSON.stringify({ alg: JWE_ALG, enc: JWE_ENC, typ, epk })));
|
|
73
|
+
const iv = new Uint8Array(IV_BYTES);
|
|
74
|
+
crypto.getRandomValues(iv);
|
|
75
|
+
const key = await contentKey(eph.privateKey, to, 'encrypt');
|
|
76
|
+
const sealed = new Uint8Array(await crypto.subtle.encrypt({ name: 'AES-GCM', iv, additionalData: te.encode(header), tagLength: TAG_BYTES * 8 }, key, te.encode(plaintext)));
|
|
77
|
+
const ct = sealed.slice(0, sealed.length - TAG_BYTES);
|
|
78
|
+
const tag = sealed.slice(sealed.length - TAG_BYTES);
|
|
79
|
+
return `${header}..${bytesToB64url(iv)}.${bytesToB64url(ct)}.${bytesToB64url(tag)}`;
|
|
80
|
+
}
|
|
81
|
+
/** The plaintext if `jwe` is a compact JWE of purpose `typ` sealed to `privateKey`, else null. Never throws:
|
|
82
|
+
* a wrong key, a wrong typ, any other header member, a changed byte, or a malformed token is null. */
|
|
83
|
+
export async function openJwe(privateKey, typ, jwe) {
|
|
84
|
+
try {
|
|
85
|
+
if (typeof jwe !== 'string')
|
|
86
|
+
return null;
|
|
87
|
+
const parts = jwe.split('.');
|
|
88
|
+
if (parts.length !== 5)
|
|
89
|
+
return null;
|
|
90
|
+
const [h, encryptedKey, ivPart, ctPart, tagPart] = parts;
|
|
91
|
+
if (encryptedKey !== '' || !B64URL.test(h) || !B64URL.test(ivPart) || !B64URL.test(tagPart))
|
|
92
|
+
return null;
|
|
93
|
+
if (ctPart !== '' && !B64URL.test(ctPart))
|
|
94
|
+
return null;
|
|
95
|
+
const header = JSON.parse(new TextDecoder().decode(b64urlToBytes(h)));
|
|
96
|
+
if (!isObj(header) || Object.keys(header).some((k) => !HEADER_KEYS.includes(k)))
|
|
97
|
+
return null;
|
|
98
|
+
if (header.alg !== JWE_ALG || header.enc !== JWE_ENC || header.typ !== typ)
|
|
99
|
+
return null;
|
|
100
|
+
const epk = asEcPublicJwk(header.epk);
|
|
101
|
+
if (!epk)
|
|
102
|
+
return null;
|
|
103
|
+
const iv = new Uint8Array(b64urlToBytes(ivPart));
|
|
104
|
+
const tag = b64urlToBytes(tagPart);
|
|
105
|
+
if (iv.length !== IV_BYTES || tag.length !== TAG_BYTES)
|
|
106
|
+
return null;
|
|
107
|
+
const key = await contentKey(privateKey, epk, 'decrypt');
|
|
108
|
+
const plain = await crypto.subtle.decrypt({ name: 'AES-GCM', iv, additionalData: te.encode(h), tagLength: TAG_BYTES * 8 }, key, concat(b64urlToBytes(ctPart), tag));
|
|
109
|
+
return new TextDecoder('utf-8', { fatal: true }).decode(plain);
|
|
110
|
+
}
|
|
111
|
+
catch {
|
|
112
|
+
return null;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
//# sourceMappingURL=fleetJwe.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fleetJwe.js","sourceRoot":"","sources":["../../sidecar/fleetJwe.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { SidecarFsChange } from '@corenel/protocol';
|
|
2
|
+
/** One published change: what changed, and which connection changed it, so
|
|
3
|
+
* that connection is not told about its own write. */
|
|
4
|
+
export interface FsChangeEvent extends SidecarFsChange {
|
|
5
|
+
by: string;
|
|
6
|
+
}
|
|
7
|
+
export interface SidecarFsChanges {
|
|
8
|
+
publish(change: FsChangeEvent): void;
|
|
9
|
+
subscribe(cb: (change: FsChangeEvent) => void): () => void;
|
|
10
|
+
}
|
|
11
|
+
export declare class FsChangeBus implements SidecarFsChanges {
|
|
12
|
+
private readonly subs;
|
|
13
|
+
publish(change: FsChangeEvent): void;
|
|
14
|
+
subscribe(cb: (change: FsChangeEvent) => void): () => void;
|
|
15
|
+
}
|
|
16
|
+
/** What a file tool's `{ root?, path }` changed, said the way every client
|
|
17
|
+
* reads it: the path is resolved the way the tools confine it (against the
|
|
18
|
+
* named root, or the primary one, so `../other/x` and an absolute path both
|
|
19
|
+
* land where the tool wrote), then reported for each shared root that holds
|
|
20
|
+
* it, with the daemon's spelling of that root and the path under it in
|
|
21
|
+
* forward slashes. Nothing when no root holds it. */
|
|
22
|
+
export declare function fsChangesFor(roots: string[], primary: string | undefined, rootArg: unknown, path: string): SidecarFsChange[];
|
|
23
|
+
//# sourceMappingURL=fsChanges.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fsChanges.d.ts","sourceRoot":"","sources":["../../sidecar/fsChanges.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AAEzD;sDACsD;AACtD,MAAM,WAAW,aAAc,SAAQ,eAAe;IACpD,EAAE,EAAE,MAAM,CAAC;CACZ;AAED,MAAM,WAAW,gBAAgB;IAC/B,OAAO,CAAC,MAAM,EAAE,aAAa,GAAG,IAAI,CAAC;IACrC,SAAS,CAAC,EAAE,EAAE,CAAC,MAAM,EAAE,aAAa,KAAK,IAAI,GAAG,MAAM,IAAI,CAAC;CAC5D;AAED,qBAAa,WAAY,YAAW,gBAAgB;IAClD,OAAO,CAAC,QAAQ,CAAC,IAAI,CAA8C;IAEnE,OAAO,CAAC,MAAM,EAAE,aAAa,GAAG,IAAI;IAQpC,SAAS,CAAC,EAAE,EAAE,CAAC,MAAM,EAAE,aAAa,KAAK,IAAI,GAAG,MAAM,IAAI;CAI3D;AA6BD;;;;;qDAKqD;AACrD,wBAAgB,YAAY,CAAC,KAAK,EAAE,MAAM,EAAE,EAAE,OAAO,EAAE,MAAM,GAAG,SAAS,EAAE,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,GAAG,eAAe,EAAE,CAgB5H"}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
export class FsChangeBus {
|
|
2
|
+
constructor() {
|
|
3
|
+
Object.defineProperty(this, "subs", {
|
|
4
|
+
enumerable: true,
|
|
5
|
+
configurable: true,
|
|
6
|
+
writable: true,
|
|
7
|
+
value: new Set()
|
|
8
|
+
});
|
|
9
|
+
}
|
|
10
|
+
publish(change) {
|
|
11
|
+
// A copy, so a subscriber that unsubscribes (a connection closing) while
|
|
12
|
+
// this runs cannot skip the one after it.
|
|
13
|
+
for (const cb of [...this.subs]) {
|
|
14
|
+
try {
|
|
15
|
+
cb(change);
|
|
16
|
+
}
|
|
17
|
+
catch { /* one connection's failure must not stop the rest hearing it */ }
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
subscribe(cb) {
|
|
21
|
+
this.subs.add(cb);
|
|
22
|
+
return () => { this.subs.delete(cb); };
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
function lexical(p) {
|
|
26
|
+
const s = p.replace(/\\/g, '/');
|
|
27
|
+
const drive = /^([A-Za-z]):(\/|$)/.exec(s);
|
|
28
|
+
const head = drive ? `${drive[1].toUpperCase()}:` : s.startsWith('//') ? '//' : s.startsWith('/') ? '/' : null;
|
|
29
|
+
if (head === null)
|
|
30
|
+
return null;
|
|
31
|
+
const parts = [];
|
|
32
|
+
for (const seg of s.slice(drive ? 2 : head.length).split('/')) {
|
|
33
|
+
if (seg === '' || seg === '.')
|
|
34
|
+
continue;
|
|
35
|
+
if (seg === '..')
|
|
36
|
+
parts.pop();
|
|
37
|
+
else
|
|
38
|
+
parts.push(seg);
|
|
39
|
+
}
|
|
40
|
+
return { head, parts, drive: drive !== null };
|
|
41
|
+
}
|
|
42
|
+
/** `abs` lies inside `root`: the same head and root's segments as its prefix,
|
|
43
|
+
* without regard to letter case on a drive (a Windows root). */
|
|
44
|
+
function inside(root, abs) {
|
|
45
|
+
if (root.head !== abs.head || abs.parts.length <= root.parts.length)
|
|
46
|
+
return false;
|
|
47
|
+
const same = root.drive ? (a, b) => a.toLowerCase() === b.toLowerCase() : (a, b) => a === b;
|
|
48
|
+
return root.parts.every((seg, i) => same(seg, abs.parts[i]));
|
|
49
|
+
}
|
|
50
|
+
/** What a file tool's `{ root?, path }` changed, said the way every client
|
|
51
|
+
* reads it: the path is resolved the way the tools confine it (against the
|
|
52
|
+
* named root, or the primary one, so `../other/x` and an absolute path both
|
|
53
|
+
* land where the tool wrote), then reported for each shared root that holds
|
|
54
|
+
* it, with the daemon's spelling of that root and the path under it in
|
|
55
|
+
* forward slashes. Nothing when no root holds it. */
|
|
56
|
+
export function fsChangesFor(roots, primary, rootArg, path) {
|
|
57
|
+
if (!path)
|
|
58
|
+
return [];
|
|
59
|
+
let abs = lexical(path);
|
|
60
|
+
if (!abs) {
|
|
61
|
+
const named = typeof rootArg === 'string' && rootArg !== '' ? lexical(rootArg) : null;
|
|
62
|
+
const base = named ?? (primary ? lexical(primary) : null);
|
|
63
|
+
if (!base)
|
|
64
|
+
return [];
|
|
65
|
+
abs = lexical(`${base.head}${base.head.endsWith('/') ? '' : '/'}${[...base.parts, path.replace(/\\/g, '/')].join('/')}`);
|
|
66
|
+
if (!abs)
|
|
67
|
+
return [];
|
|
68
|
+
}
|
|
69
|
+
const out = [];
|
|
70
|
+
for (const root of roots) {
|
|
71
|
+
const r = lexical(root);
|
|
72
|
+
if (r && inside(r, abs))
|
|
73
|
+
out.push({ root, path: abs.parts.slice(r.parts.length).join('/') });
|
|
74
|
+
}
|
|
75
|
+
return out;
|
|
76
|
+
}
|
|
77
|
+
//# sourceMappingURL=fsChanges.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fsChanges.js","sourceRoot":"","sources":["../../sidecar/fsChanges.ts"],"names":[],"mappings":"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"}
|
package/dist/sidecar/host.d.ts
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
|
-
import type { AgentTool, IMemoryProvider, IKeyValueStore, ISearchProvider, ISessionStore, PermissionRequest, AskQuestion, AskAnswers, AgentEvent, PermissionDecision, DaemonRunActivity, DaemonRegistration, AgentPause, HostSummary, ProcInfo, RunRecordInfo, RunHistoryCrew, SessionInfo, SessionRepoSpec, SessionSnapshotInfo, CrewMemoryInfo, SidecarConfigEntry, WebPushSubscription, ChecksState, WorkspaceLockRef, TaskSpec, TaskAutonomy, RunPlanView, DaemonApprovalChoice, DaemonApprovalOffer, DaemonApprovalsState, CrewAgentRef, CrewDeployReply, DeviceSummary, AccessReply, NodeEvent, PeerMcpToolDecl, MemberReply, AccessOverviewReply, AgentCredReply, AgentCredStatusReply, AgentRunsReply, PeerReply, AgentCredHeader, McpBinding } from '@corenel/protocol';
|
|
1
|
+
import type { AgentTool, IMemoryProvider, IKeyValueStore, ISearchProvider, ISessionStore, PermissionRequest, AskQuestion, AskAnswers, AgentEvent, PermissionDecision, DaemonRunActivity, DaemonRegistration, AgentPause, HostSummary, ProcInfo, RunRecordInfo, RunHistoryCrew, SessionInfo, SessionRepoSpec, SessionSnapshotInfo, CrewMemoryInfo, SidecarConfigEntry, WebPushSubscription, ChecksState, WorkspaceLockRef, TaskSpec, TaskAutonomy, RunPlanView, DaemonApprovalChoice, DaemonApprovalOffer, DaemonApprovalsState, CrewAgentRef, CrewDeployReply, DeviceSummary, AccessReply, NodeEvent, PeerMcpToolDecl, MemberReply, AccessOverviewReply, AgentCredReply, AgentCredStatusReply, AgentRunsReply, PeerReply, AgentCredHeader, McpBinding, ChatSummary, ChatTurnSpec, StoredChat } from '@corenel/protocol';
|
|
2
|
+
import { type SidecarFsChange } from '@corenel/protocol';
|
|
3
|
+
import type { ChatTurnResult } from '../chat/types';
|
|
2
4
|
import type { DontAsk, FileService } from '@corenel/protocol';
|
|
3
5
|
import { type KeySigner } from './deviceKeys';
|
|
4
6
|
import { type SpeechStreamMsg, type SidecarChannel, type SidecarMsg, type SidecarInfo, type AcpPromptPart, type AcpModeInfo, type AcpStartOptions, type RoomOpenRequest, type RoomSummary, type RoomFinding, type RoomFindingEdit, type DeployRef, type WorkflowRef, type WorkflowPackagePut, type WorkflowPackageReply, type WorkflowResultsListReply, type WorkflowResultsTakeReply, type WorkflowResultsAckReply, type WorkflowStopReply } from '@corenel/protocol';
|
|
@@ -176,6 +178,24 @@ export interface SidecarHostOptions {
|
|
|
176
178
|
meta?: Record<string, string>;
|
|
177
179
|
}) => void;
|
|
178
180
|
}
|
|
181
|
+
/** One chat ask_user prompt from the daemon (Harness Chat Plan 2, R8). */
|
|
182
|
+
export interface SidecarAskUser {
|
|
183
|
+
id: number;
|
|
184
|
+
runId: string;
|
|
185
|
+
agent: string;
|
|
186
|
+
questions: AskQuestion[];
|
|
187
|
+
deadline: number;
|
|
188
|
+
}
|
|
189
|
+
/** How one daemon chat turn ended. A refused or failed turn resolves too;
|
|
190
|
+
* only a dropped connection rejects. */
|
|
191
|
+
export interface SidecarChatRunResult {
|
|
192
|
+
runId: string | null;
|
|
193
|
+
conversationId: string | null;
|
|
194
|
+
text: string;
|
|
195
|
+
stopReason: string;
|
|
196
|
+
outcome: ChatTurnResult['outcome'];
|
|
197
|
+
error?: string;
|
|
198
|
+
}
|
|
179
199
|
/** `mcp-call` resolves `ok: false` for a policy refusal (`mcp-error`) rather
|
|
180
200
|
* than rejecting -- see the field comment on `mcpCallPending` above for why. A
|
|
181
201
|
* reject here is reserved for the transport dropping mid-call. */
|
|
@@ -220,6 +240,9 @@ export interface ProcWatcher {
|
|
|
220
240
|
signal?: string;
|
|
221
241
|
}) => void;
|
|
222
242
|
}
|
|
243
|
+
/** An `fs-changed` message's change, or null when it is not one: a root and a
|
|
244
|
+
* path, each a non-empty string within MAX_FS_CHANGE_LEN with no NUL. */
|
|
245
|
+
export declare function fsChangeOf(m: unknown): SidecarFsChange | null;
|
|
223
246
|
/** Prefix of the error a connection rejects with when the daemon's key is not
|
|
224
247
|
* the one this client pinned (or it cannot prove it holds it). A caller shows
|
|
225
248
|
* this state as its own, never as a generic connection failure, and never
|
|
@@ -251,6 +274,9 @@ export declare function isSidecarAccountRefusal(e: unknown): boolean;
|
|
|
251
274
|
* (slice 4). The device is fine and stays paired; retrying changes nothing
|
|
252
275
|
* until a fleet admin changes the policy. */
|
|
253
276
|
export declare function isFleetLoginsRefusal(e: unknown): boolean;
|
|
277
|
+
/** The sidecar's fleet sign-in is paused for a reason that clears by itself (SIGN_IN_PAUSED_DENIED). The device is
|
|
278
|
+
* fine and stays paired, and unlike every other account refusal a retry is expected to succeed later. */
|
|
279
|
+
export declare function isSignInPausedRefusal(e: unknown): boolean;
|
|
254
280
|
export declare class SidecarHost {
|
|
255
281
|
private readonly ch;
|
|
256
282
|
private readonly opts;
|
|
@@ -262,6 +288,16 @@ export declare class SidecarHost {
|
|
|
262
288
|
private readonly runPending;
|
|
263
289
|
private cancelSeq;
|
|
264
290
|
private readonly cancelPending;
|
|
291
|
+
/** Daemon chat (Harness Chat Plan 2): run-inject, chat-list and chat-get replies, by request id. */
|
|
292
|
+
private chatSeq;
|
|
293
|
+
private readonly injectPending;
|
|
294
|
+
private readonly chatListPending;
|
|
295
|
+
private readonly chatGetPending;
|
|
296
|
+
/** Chat ask_user prompts not yet answered or resolved, by the daemon's ask id,
|
|
297
|
+
* so a handler registered after one arrived still sees it. */
|
|
298
|
+
private readonly askUserOpen;
|
|
299
|
+
private readonly askUserHandlers;
|
|
300
|
+
private readonly askUserResolvedHandlers;
|
|
265
301
|
private acpSeq;
|
|
266
302
|
private readonly acpPending;
|
|
267
303
|
private syncSeq;
|
|
@@ -330,6 +366,9 @@ export declare class SidecarHost {
|
|
|
330
366
|
/** Per-request consent-URL listeners, cleared when the request settles. */
|
|
331
367
|
private readonly mcpLoginUrlWaiters;
|
|
332
368
|
private features;
|
|
369
|
+
/** Who hears another client's file changes (`fs-changed`), for the life of
|
|
370
|
+
* this connection. */
|
|
371
|
+
private readonly fsChangeSubs;
|
|
333
372
|
private sttSeq;
|
|
334
373
|
private readonly sttStreams;
|
|
335
374
|
private vapidKey;
|
|
@@ -655,6 +694,50 @@ export declare class SidecarHost {
|
|
|
655
694
|
ok: boolean;
|
|
656
695
|
reason?: 'no-such-run' | 'unsupported' | 'not-allowed';
|
|
657
696
|
}>;
|
|
697
|
+
/** True when the connected daemon holds chats (Harness Chat Plan 2):
|
|
698
|
+
* `runChat`, `injectRun`, `listChats`, `getChat` and ask_user prompts. An
|
|
699
|
+
* older daemon would run a chat turn as a plain one-shot run, so check this
|
|
700
|
+
* before calling any of them. */
|
|
701
|
+
supportsChat(): boolean;
|
|
702
|
+
/** One turn of a daemon chat. `chat.conversationId` absent starts a new
|
|
703
|
+
* chat; the daemon mints its id and hands it to `onAccepted` with the run
|
|
704
|
+
* id. Resolves with how the turn ended, a refused or failed one included
|
|
705
|
+
* (with `error`); rejects only when the connection drops mid-turn. */
|
|
706
|
+
runChat(agent: string, text: string, chat: ChatTurnSpec, onEvent: (e: AgentEvent) => void, opts?: {
|
|
707
|
+
onAccepted?: (runId: string, conversationId: string) => void;
|
|
708
|
+
}): Promise<SidecarChatRunResult>;
|
|
709
|
+
/** Messages typed while a chat turn runs; `hard` interrupts it. Never
|
|
710
|
+
* rejects: a refusal or a dropped connection answers `ok: false`. */
|
|
711
|
+
injectRun(runId: string, messages: {
|
|
712
|
+
id: string;
|
|
713
|
+
text: string;
|
|
714
|
+
}[], hard: boolean): Promise<{
|
|
715
|
+
ok: boolean;
|
|
716
|
+
reason?: string;
|
|
717
|
+
}>;
|
|
718
|
+
/** This user's chats with `agent`; the admin may name a `member`. Rejects
|
|
719
|
+
* with the daemon's refusal. */
|
|
720
|
+
listChats(agent: string, opts?: {
|
|
721
|
+
ref?: CrewAgentRef;
|
|
722
|
+
member?: string;
|
|
723
|
+
}): Promise<ChatSummary[]>;
|
|
724
|
+
/** One conversation, to its member or the admin. Rejects with the daemon's
|
|
725
|
+
* refusal (CHAT_NOT_YOURS, CHAT_NOT_FOUND). */
|
|
726
|
+
getChat(agent: string, conversationId: string, opts?: {
|
|
727
|
+
ref?: CrewAgentRef;
|
|
728
|
+
member?: string;
|
|
729
|
+
}): Promise<StoredChat>;
|
|
730
|
+
/** Chat ask_user prompts for this user. Prompts still open when the handler
|
|
731
|
+
* is added are handed to it at once. Returns the unsubscribe function. */
|
|
732
|
+
onAskUser(handler: (ask: SidecarAskUser) => void): () => void;
|
|
733
|
+
/** A prompt was answered on another connection, or timed out: drop it. */
|
|
734
|
+
onAskUserResolved(handler: (r: {
|
|
735
|
+
id: number;
|
|
736
|
+
by: 'client' | 'timeout';
|
|
737
|
+
}) => void): () => void;
|
|
738
|
+
/** Answer prompt `id`; null declines. The first answer from any of this
|
|
739
|
+
* user's or the admin's connections wins; a late one is ignored. */
|
|
740
|
+
answerAskUser(id: number, answers: AskAnswers | null): void;
|
|
658
741
|
/** True when the connected sidecar's runner supports cancelling a run. */
|
|
659
742
|
supportsRunCancel(): boolean;
|
|
660
743
|
/** True when the connected daemon fans a delegated callee's own node events
|
|
@@ -746,6 +829,13 @@ export declare class SidecarHost {
|
|
|
746
829
|
* and every room-* message is refused server-side; feature-detecting here means
|
|
747
830
|
* the browser never sends one it already knows will be refused. */
|
|
748
831
|
supportsRooms(): boolean;
|
|
832
|
+
/** True when the sidecar announces other clients' file changes (`fs-changed`).
|
|
833
|
+
* An older one never does, and a listener here simply never fires. */
|
|
834
|
+
supportsFsChanged(): boolean;
|
|
835
|
+
/** Hear every file change another client of this daemon makes through its
|
|
836
|
+
* file tools: the root the tool used and the root-relative path. Never this
|
|
837
|
+
* client's own changes (the daemon leaves the writer out). */
|
|
838
|
+
onFsChanged(cb: (change: SidecarFsChange) => void): () => void;
|
|
749
839
|
/** True when the connected daemon HOLDS agent pauses. Feature-detected from
|
|
750
840
|
* the welcome, so a client offers "Pause" only where pausing would actually
|
|
751
841
|
* stop the agent rather than reporting a success the daemon ignores. */
|