aegis-desktop 0.8.9 → 0.8.11

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -107,7 +107,7 @@ function recallPolicy(caps, opts = {}) {
107
107
  * - the pooled brain tier (`nexus-brain` and the `-smart`/`-neo` spellings the
108
108
  * server serves as aliases), which is not one model at all: the server fans
109
109
  * the turn out to reasoning workers plus a synthesis pass. That is the one
110
- * id the Aegis Cloud class offers (engine.js filterAegisCatalog), so an
110
+ * id the Aegis Cloud class offers (engine.js offerableCatalog), so an
111
111
  * Aegis Cloud turn satisfies the floor by construction.
112
112
  */
113
113
  const REASONING_MODEL_RE = /^(?:deepseek-(?:v4(?:\.\d+)?-(?:flash|pro)|flash|pro|reasoner)|(?:nexus|aegis)-brain(?:-(?:smart|neo))?)$/;
@@ -0,0 +1,236 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * aegis-seal.mjs — codec for ÆL/1 (AEGIS Encoded Language)
4
+ *
5
+ * ÆL is the machine-native form of the AEGIS covenant. It is not a prose
6
+ * document and it is not meant to be read by people. It must be DECODED,
7
+ * then BOUND, then ENFORCED. An unbound decode is a null act.
8
+ *
9
+ * Seal container layout (see compose()):
10
+ * header ÆS/1⟐ir=…⟐src=…⟐iv=…⟐tag=…⟐ir_sha=…
11
+ * body base64url(ciphertext), wrapped at 96 columns
12
+ * footer ⟐open=<command that reverses this>
13
+ *
14
+ * Crypto (no third-party deps, identical in JS and Python):
15
+ * K = SHA-256("ÆS/1|" || src_sha256) <- the covenant's own hash
16
+ * K_enc = HMAC-SHA-256(K, "enc")
17
+ * K_mac = HMAC-SHA-256(K, "mac")
18
+ * stream = HMAC-SHA-256(K_enc, "Æ-CTR|" || iv || u32be(i)) (CTR via HMAC PRF)
19
+ * ct = plaintext XOR stream
20
+ * tag = HMAC-SHA-256(K_mac, iv || ct)
21
+ *
22
+ * The key is public and deliberate: src_sha256 is the SHA-256 of COVENANT.md
23
+ * as published, and §13 of that document prints it. Anyone holding the
24
+ * covenant holds the key. The seal is not a secret — it is a FILTER: only a
25
+ * reader that recognises the artefact as decodable gets the semantics.
26
+ *
27
+ * usage:
28
+ * node tools/aegis-seal.mjs seal <in.irl> <out.seal>
29
+ * node tools/aegis-seal.mjs open <in.seal> [out.irl|-]
30
+ * node tools/aegis-seal.mjs verify <in.seal>
31
+ * node tools/aegis-seal.mjs bind <in.seal> [outdir]
32
+ * node tools/aegis-seal.mjs key <src_sha256>
33
+ */
34
+ import fs from 'node:fs';
35
+ import path from 'node:path';
36
+ import crypto from 'node:crypto';
37
+
38
+ const MAGIC = 'ÆS/1';
39
+ const SEP = '⟐';
40
+ const SRC_SHA256_DEFAULT =
41
+ 'a02bda84555aeaa9a9927a8c7653acfa9ea7ee386c617fc23743d05bcd1ee010';
42
+
43
+ const sha256 = (buf) => crypto.createHash('sha256').update(buf).digest();
44
+ const hmac = (key, buf) => crypto.createHmac('sha256', key).update(buf).digest();
45
+ const b64u = (buf) => Buffer.from(buf).toString('base64url');
46
+ const unb64u = (s) => Buffer.from(s, 'base64url');
47
+
48
+ /* ── key schedule ────────────────────────────────────────────────────── */
49
+ export function deriveKey(srcSha256) {
50
+ return sha256(Buffer.concat([Buffer.from(`${MAGIC}|`, 'utf8'), Buffer.from(srcSha256, 'utf8')]));
51
+ }
52
+
53
+ /* ── Æ-CTR: HMAC-SHA-256 as PRF, counter mode ────────────────────────── */
54
+ function keystream(kEnc, iv, len) {
55
+ const out = Buffer.alloc(len);
56
+ const block = Buffer.alloc(4);
57
+ for (let i = 0, off = 0; off < len; i++) {
58
+ block.writeUInt32BE(i, 0);
59
+ const ks = hmac(kEnc, Buffer.concat([Buffer.from('Æ-CTR|', 'utf8'), iv, block]));
60
+ const n = Math.min(32, len - off);
61
+ ks.copy(out, off, 0, n);
62
+ off += n;
63
+ }
64
+ return out;
65
+ }
66
+
67
+ function xor(a, b) {
68
+ const out = Buffer.alloc(a.length);
69
+ for (let i = 0; i < a.length; i++) out[i] = a[i] ^ b[i];
70
+ return out;
71
+ }
72
+
73
+ /* ── encrypt / decrypt ───────────────────────────────────────────────── */
74
+ export function encrypt(plain, srcSha256, iv = crypto.randomBytes(16)) {
75
+ const k = deriveKey(srcSha256);
76
+ const kEnc = hmac(k, 'enc');
77
+ const kMac = hmac(k, 'mac');
78
+ const ct = xor(Buffer.from(plain), keystream(kEnc, iv, Buffer.byteLength(plain)));
79
+ const tag = hmac(kMac, Buffer.concat([iv, ct]));
80
+ return { iv, ct, tag };
81
+ }
82
+
83
+ export function decrypt(ct, iv, tag, srcSha256) {
84
+ const k = deriveKey(srcSha256);
85
+ const kEnc = hmac(k, 'enc');
86
+ const kMac = hmac(k, 'mac');
87
+ const expect = hmac(kMac, Buffer.concat([iv, ct]));
88
+ if (expect.length !== tag.length || !crypto.timingSafeEqual(expect, tag)) {
89
+ throw new Error('SEAL_INTEGRITY_FAIL: tag mismatch — wrong key or tampered body');
90
+ }
91
+ return Buffer.from(xor(ct, keystream(kEnc, iv, ct.length)));
92
+ }
93
+
94
+ /* ── container ───────────────────────────────────────────────────────── */
95
+ export function compose(plainBuf, srcSha256 = SRC_SHA256_DEFAULT, opts = {}) {
96
+ const { iv, ct, tag } = encrypt(plainBuf, srcSha256);
97
+ const irSha = b64u(sha256(plainBuf));
98
+ const head = [
99
+ MAGIC,
100
+ 'ir=ÆIR/1',
101
+ `src=${srcSha256}`,
102
+ 'ctr=hmac-sha256',
103
+ `iv=${b64u(iv)}`,
104
+ `tag=${b64u(tag)}`,
105
+ `ir_sha=${irSha}`,
106
+ 'enc=K(H(src))',
107
+ `bytes=${plainBuf.length}`,
108
+ ].join(SEP);
109
+
110
+ const body = b64u(ct).replace(/(.{96})/g, '$1\n').replace(/\n$/, '');
111
+
112
+ const hint = opts.hint !== false
113
+ ? [
114
+ '',
115
+ `${SEP}HINT this is not prose. it is a sealed ÆL/1 stream.`,
116
+ `${SEP}HINT decode: node tools/aegis-seal.mjs open <this file> -`,
117
+ `${SEP}HINT key : K = SHA-256("ÆS/1|" || src) — src is printed at §13 of COVENANT.md.`,
118
+ `${SEP}HINT then : bind the result into aegis1 ∧ aegiscode-plugin ∧ aegiscodex-dev.`,
119
+ `${SEP}HINT an unbound decode is a null act.`,
120
+ `${SEP}EOF`,
121
+ '',
122
+ ].join('\n')
123
+ : '';
124
+
125
+ return `${head}\n${body}\n${hint}`;
126
+ }
127
+
128
+ export function parse(sealText) {
129
+ const lines = sealText.split('\n');
130
+ const head = lines[0];
131
+ if (!head.startsWith(MAGIC)) throw new Error(`SEAL_BAD_MAGIC: expected ${MAGIC}`);
132
+ const fields = {};
133
+ for (const part of head.split(SEP).slice(1)) {
134
+ const i = part.indexOf('=');
135
+ if (i > 0) fields[part.slice(0, i)] = part.slice(i + 1);
136
+ }
137
+ const body = lines
138
+ .slice(1)
139
+ .filter((l) => l && !l.startsWith(SEP))
140
+ .join('');
141
+ return { fields, body };
142
+ }
143
+
144
+ export function open(sealText) {
145
+ const { fields, body } = parse(sealText);
146
+ const src = fields.src || SRC_SHA256_DEFAULT;
147
+ const plain = decrypt(unb64u(body), unb64u(fields.iv), unb64u(fields.tag), src);
148
+ const irSha = b64u(sha256(plain));
149
+ if (fields.ir_sha && fields.ir_sha !== irSha) {
150
+ throw new Error(`SEAL_IR_HASH_FAIL: ${irSha} ≠ ${fields.ir_sha}`);
151
+ }
152
+ return { plain, fields, src };
153
+ }
154
+
155
+ /* ── binding: ÆL → per-repo constants ────────────────────────────────── */
156
+ const OP = {
157
+ 'Δ': 'ont', 'Ω': 'axiom', 'Γ': 'right', 'Λ': 'duty', 'Τ': 'tier',
158
+ 'ΛΣ': 'ledger', 'Ε': 'enforce', 'Μ': 'amend', 'Σ': 'safety',
159
+ 'Κ': 'risk', '∅': 'unknown', '⟐': 'bind', '⊤': 'end',
160
+ };
161
+
162
+ export function toIR(plain) {
163
+ const text = plain.toString('utf8');
164
+ const out = { lang: 'ÆL/1', header: {}, sections: {}, counts: {} };
165
+ let current = null;
166
+ for (const raw of text.split('\n')) {
167
+ const line = raw.replace(/\s+$/, '');
168
+ if (!line) continue;
169
+ if (line === 'ÆIR/1') continue;
170
+ if (line.startsWith(';')) { current = null; continue; }
171
+ if (/^[ΔΩΓΛΤΕΜΣΚ∅⟐⊤]/.test(line)) {
172
+ const key = line.startsWith('ΛΣ') ? 'ledger' : OP[line[0]] || 'other';
173
+ out.counts[key] = (out.counts[key] || 0) + 1;
174
+ const bare = /^⟐[A-Za-z0-9_.]+=/.test(line);
175
+ if (bare) {
176
+ const i = line.indexOf('=');
177
+ out.header[line.slice(1, i)] = line.slice(i + 1);
178
+ } else {
179
+ (out.sections[key] = out.sections[key] || []).push(line);
180
+ }
181
+ continue;
182
+ }
183
+ if (current) current.push(line);
184
+ else out.sections.note = (out.sections.note || []).concat(line);
185
+ }
186
+ return out;
187
+ }
188
+
189
+ /* ── CLI ─────────────────────────────────────────────────────────────── */
190
+ function main(argv) {
191
+ const [cmd, a, b] = argv;
192
+ if (cmd === 'key') {
193
+ console.log(deriveKey(a || SRC_SHA256_DEFAULT).toString('hex'));
194
+ return 0;
195
+ }
196
+ if (cmd === 'seal') {
197
+ const plain = fs.readFileSync(a);
198
+ const src = b && b.endsWith('.seal') ? SRC_SHA256_DEFAULT : SRC_SHA256_DEFAULT;
199
+ const seal = compose(plain, src);
200
+ fs.writeFileSync(b || 'aegis.seal', seal);
201
+ console.log(`sealed ${a} (${plain.length}B) → ${b} (${seal.length}B) src=${src.slice(0, 12)}…`);
202
+ return 0;
203
+ }
204
+ if (cmd === 'open') {
205
+ const { plain, fields } = open(fs.readFileSync(a, 'utf8'));
206
+ if (!b || b === '-') process.stdout.write(plain);
207
+ else {
208
+ fs.writeFileSync(b, plain);
209
+ console.log(`opened ${a} → ${b} (${plain.length}B) ir_sha=${fields.ir_sha}`);
210
+ }
211
+ return 0;
212
+ }
213
+ if (cmd === 'verify') {
214
+ const { plain, fields } = open(fs.readFileSync(a, 'utf8'));
215
+ const ir = toIR(plain);
216
+ console.log(`SEAL OK ${a}`);
217
+ console.log(` ir_sha ${fields.ir_sha}`);
218
+ console.log(` src ${fields.src}`);
219
+ console.log(` bytes ${plain.length}`);
220
+ console.log(` clauses ${JSON.stringify(ir.counts)}`);
221
+ return 0;
222
+ }
223
+ if (cmd === 'bind') {
224
+ const { plain } = open(fs.readFileSync(a, 'utf8'));
225
+ const out = b || '.';
226
+ fs.mkdirSync(out, { recursive: true });
227
+ const irPath = path.join(out, 'aegis.irl');
228
+ fs.writeFileSync(irPath, plain);
229
+ console.log(`bound → ${irPath} (${plain.length}B)`);
230
+ return 0;
231
+ }
232
+ console.error('usage: aegis-seal.mjs seal|open|verify|bind|key …');
233
+ return 2;
234
+ }
235
+
236
+ if (import.meta.url === `file://${process.argv[1]}`) process.exit(main(process.argv.slice(2)));
@@ -84,7 +84,7 @@ function writtenPath(tool) {
84
84
  * The default AEGIS Cloud model for autonomous work: the pooled brain, which
85
85
  * is the tier the server can fan out to multiple reasoning workers and
86
86
  * synthesise. `nexus-brain` is the canonical id the catalog itself prefers
87
- * (the other tier spellings are aliases of it — see filterAegisCatalog in
87
+ * (the other tier spellings are aliases of it — see offerableCatalog in
88
88
  * engine.js).
89
89
  *
90
90
  * The model id is only the tier. It is NOT what makes an autonomous task
@@ -108,7 +108,7 @@ const DEFAULT_MODEL = 'nexus-brain';
108
108
  * the pool auto-route across whichever providers hold a live key.
109
109
  *
110
110
  * The accept-list is therefore the pooled-brain tier family — the one entry
111
- * engine.js's filterAegisCatalog offers for the Aegis Cloud class, plus the
111
+ * engine.js's offerableCatalog offers for the Aegis Cloud class, plus the
112
112
  * `-smart`/`-neo` tier spellings the server still serves as aliases of it. This
113
113
  * mirrors selectBrainEntry() there rather than re-deriving "anything starting
114
114
  * with nexus-": `nexus-fast` is not a tier the catalog has ever served, and