@kognai/build 0.6.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/README.md +91 -0
- package/dist/bin/kognai-build.js +774 -0
- package/dist/bin/kognai.js +323 -0
- package/dist/lib/boot-env.js +30 -0
- package/dist/lib/cost-estimator.js +54 -0
- package/dist/lib/knowledge.js +256 -0
- package/dist/lib/mandate.js +360 -0
- package/dist/lib/memory.js +243 -0
- package/dist/lib/registry-overrides.js +40 -0
- package/dist/lib/router.js +119 -0
- package/dist/lib/skill-bank-stub.js +214 -0
- package/dist/lib/skill-bank.js +248 -0
- package/dist/lib/swarm-coder-prompt.js +39 -0
- package/dist/lib/swarm.js +89 -0
- package/dist/lib/tool-layer-stub.js +214 -0
- package/dist/lib/tool-layer.js +249 -0
- package/dist/lib/workspace.js +221 -0
- package/dist/templates/index.js +184 -0
- package/dist/templates/registry.js +82 -0
- package/package.json +45 -0
|
@@ -0,0 +1,360 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.buildMandate = buildMandate;
|
|
4
|
+
exports.signMandate = signMandate;
|
|
5
|
+
exports.verifyMandateSignature = verifyMandateSignature;
|
|
6
|
+
exports.renderMandateMarkdown = renderMandateMarkdown;
|
|
7
|
+
exports.persistMandate = persistMandate;
|
|
8
|
+
exports.loadMandate = loadMandate;
|
|
9
|
+
exports.loadActiveMandate = loadActiveMandate;
|
|
10
|
+
exports.listMandates = listMandates;
|
|
11
|
+
exports.checkEnvelope = checkEnvelope;
|
|
12
|
+
exports.updateBurn = updateBurn;
|
|
13
|
+
exports.revokeMandate = revokeMandate;
|
|
14
|
+
const node_crypto_1 = require("node:crypto");
|
|
15
|
+
const node_fs_1 = require("node:fs");
|
|
16
|
+
const node_os_1 = require("node:os");
|
|
17
|
+
const node_path_1 = require("node:path");
|
|
18
|
+
const STATE_ROOT = '.swarm-state/mandates';
|
|
19
|
+
const ACTIVE_POINTER = (0, node_path_1.join)(STATE_ROOT, 'active.json');
|
|
20
|
+
const SECRET_PATH = (0, node_path_1.join)((0, node_os_1.homedir)(), '.kognai', 'mandate-secret');
|
|
21
|
+
function ensureDir(path) {
|
|
22
|
+
if (!(0, node_fs_1.existsSync)(path)) {
|
|
23
|
+
(0, node_fs_1.mkdirSync)(path, { recursive: true });
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
function getMandateSecret() {
|
|
27
|
+
const envSecret = process.env.KOGNAI_MANDATE_SECRET;
|
|
28
|
+
if (envSecret && envSecret.length > 0) {
|
|
29
|
+
return envSecret;
|
|
30
|
+
}
|
|
31
|
+
if ((0, node_fs_1.existsSync)(SECRET_PATH)) {
|
|
32
|
+
const value = (0, node_fs_1.readFileSync)(SECRET_PATH, 'utf8').trim();
|
|
33
|
+
if (value.length > 0) {
|
|
34
|
+
return value;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
const generated = (0, node_crypto_1.randomBytes)(32).toString('hex');
|
|
38
|
+
ensureDir((0, node_path_1.dirname)(SECRET_PATH));
|
|
39
|
+
(0, node_fs_1.writeFileSync)(SECRET_PATH, generated, { mode: 0o600 });
|
|
40
|
+
return generated;
|
|
41
|
+
}
|
|
42
|
+
function canonicalSignaturePayload(unsigned, signed_at) {
|
|
43
|
+
return JSON.stringify({
|
|
44
|
+
objective: unsigned.objective,
|
|
45
|
+
success_criteria: unsigned.success_criteria,
|
|
46
|
+
scope_boundaries: unsigned.scope_boundaries,
|
|
47
|
+
template_id: unsigned.template_id,
|
|
48
|
+
profile: unsigned.profile,
|
|
49
|
+
capabilities: unsigned.capabilities,
|
|
50
|
+
cost_envelope: unsigned.cost_envelope,
|
|
51
|
+
deadline: unsigned.deadline,
|
|
52
|
+
signed_at,
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
function deriveMandateId(objective, template_id, estimated_usdc, signed_at) {
|
|
56
|
+
const seed = `${objective}${template_id}${estimated_usdc}${signed_at}`;
|
|
57
|
+
return (0, node_crypto_1.createHash)('sha256').update(seed).digest('hex').slice(0, 16);
|
|
58
|
+
}
|
|
59
|
+
function computeSignature(payload, secret) {
|
|
60
|
+
return (0, node_crypto_1.createHmac)('sha256', secret).update(payload).digest('hex');
|
|
61
|
+
}
|
|
62
|
+
function buildMandate(input) {
|
|
63
|
+
if (!input.objective || input.objective.trim().length === 0) {
|
|
64
|
+
throw new Error('Mandate objective is required');
|
|
65
|
+
}
|
|
66
|
+
if (!input.template_id || input.template_id.trim().length === 0) {
|
|
67
|
+
throw new Error('Mandate template_id is required');
|
|
68
|
+
}
|
|
69
|
+
// deadline is optional (string | null) — a mandate may run open-ended within its
|
|
70
|
+
// budget/scope envelope (TICKET-202 spec: deadline string | null).
|
|
71
|
+
if (!Array.isArray(input.success_criteria) || input.success_criteria.length === 0) {
|
|
72
|
+
throw new Error('Mandate success_criteria must be a non-empty array');
|
|
73
|
+
}
|
|
74
|
+
if (!Array.isArray(input.scope_boundaries)) {
|
|
75
|
+
throw new Error('Mandate scope_boundaries must be an array');
|
|
76
|
+
}
|
|
77
|
+
if (!input.profile || !input.profile.primary) {
|
|
78
|
+
throw new Error('Mandate profile.primary is required');
|
|
79
|
+
}
|
|
80
|
+
if (!input.capabilities) {
|
|
81
|
+
throw new Error('Mandate capabilities are required');
|
|
82
|
+
}
|
|
83
|
+
if (!input.cost_envelope) {
|
|
84
|
+
throw new Error('Mandate cost_envelope is required');
|
|
85
|
+
}
|
|
86
|
+
if (input.cost_envelope.max_usdc < input.cost_envelope.estimated_usdc) {
|
|
87
|
+
throw new Error('max_usdc must be >= estimated_usdc');
|
|
88
|
+
}
|
|
89
|
+
return {
|
|
90
|
+
objective: input.objective,
|
|
91
|
+
success_criteria: [...input.success_criteria],
|
|
92
|
+
scope_boundaries: [...input.scope_boundaries],
|
|
93
|
+
template_id: input.template_id,
|
|
94
|
+
profile: {
|
|
95
|
+
primary: input.profile.primary,
|
|
96
|
+
secondary: [...input.profile.secondary],
|
|
97
|
+
hard_constraints: [...input.profile.hard_constraints],
|
|
98
|
+
},
|
|
99
|
+
capabilities: {
|
|
100
|
+
skills: [...input.capabilities.skills],
|
|
101
|
+
tools: [...input.capabilities.tools],
|
|
102
|
+
agents: [...input.capabilities.agents],
|
|
103
|
+
},
|
|
104
|
+
cost_envelope: { ...input.cost_envelope },
|
|
105
|
+
deadline: input.deadline,
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
function signMandate(unsigned, signer = 'founder') {
|
|
109
|
+
const signed_at = new Date().toISOString();
|
|
110
|
+
const payload = canonicalSignaturePayload(unsigned, signed_at);
|
|
111
|
+
const secret = getMandateSecret();
|
|
112
|
+
const signature = computeSignature(payload, secret);
|
|
113
|
+
const mandate_id = deriveMandateId(unsigned.objective, unsigned.template_id, unsigned.cost_envelope.estimated_usdc, signed_at);
|
|
114
|
+
return {
|
|
115
|
+
mandate_id,
|
|
116
|
+
objective: unsigned.objective,
|
|
117
|
+
success_criteria: [...unsigned.success_criteria],
|
|
118
|
+
scope_boundaries: [...unsigned.scope_boundaries],
|
|
119
|
+
template_id: unsigned.template_id,
|
|
120
|
+
profile: {
|
|
121
|
+
primary: unsigned.profile.primary,
|
|
122
|
+
secondary: [...unsigned.profile.secondary],
|
|
123
|
+
hard_constraints: [...unsigned.profile.hard_constraints],
|
|
124
|
+
},
|
|
125
|
+
capabilities: {
|
|
126
|
+
skills: [...unsigned.capabilities.skills],
|
|
127
|
+
tools: [...unsigned.capabilities.tools],
|
|
128
|
+
agents: [...unsigned.capabilities.agents],
|
|
129
|
+
},
|
|
130
|
+
cost_envelope: { ...unsigned.cost_envelope },
|
|
131
|
+
deadline: unsigned.deadline,
|
|
132
|
+
signed_at,
|
|
133
|
+
signed_by: signer,
|
|
134
|
+
signature,
|
|
135
|
+
status: 'active',
|
|
136
|
+
burn: {
|
|
137
|
+
usdc_consumed: 0,
|
|
138
|
+
tasks_completed: 0,
|
|
139
|
+
last_updated: signed_at,
|
|
140
|
+
},
|
|
141
|
+
};
|
|
142
|
+
}
|
|
143
|
+
function verifyMandateSignature(m) {
|
|
144
|
+
const payload = canonicalSignaturePayload({
|
|
145
|
+
objective: m.objective,
|
|
146
|
+
success_criteria: m.success_criteria,
|
|
147
|
+
scope_boundaries: m.scope_boundaries,
|
|
148
|
+
template_id: m.template_id,
|
|
149
|
+
profile: m.profile,
|
|
150
|
+
capabilities: m.capabilities,
|
|
151
|
+
cost_envelope: m.cost_envelope,
|
|
152
|
+
deadline: m.deadline,
|
|
153
|
+
}, m.signed_at);
|
|
154
|
+
const secret = getMandateSecret();
|
|
155
|
+
const expected = computeSignature(payload, secret);
|
|
156
|
+
return expected === m.signature;
|
|
157
|
+
}
|
|
158
|
+
function renderMandateMarkdown(m) {
|
|
159
|
+
const shortId = m.mandate_id.slice(0, 8);
|
|
160
|
+
const lines = [];
|
|
161
|
+
lines.push(`# ⟢ Mandate ${shortId}`);
|
|
162
|
+
lines.push('');
|
|
163
|
+
lines.push(`**Objective:** ${m.objective}`);
|
|
164
|
+
lines.push(`**Template:** ${m.template_id}`);
|
|
165
|
+
lines.push(`**Status:** ${m.status}`);
|
|
166
|
+
lines.push(`**Signed by:** ${m.signed_by} at ${m.signed_at}`);
|
|
167
|
+
lines.push(`**Deadline:** ${m.deadline ?? 'none'}`);
|
|
168
|
+
lines.push('');
|
|
169
|
+
lines.push('## ⟡ Success Criteria');
|
|
170
|
+
for (const sc of m.success_criteria) {
|
|
171
|
+
lines.push(`- ${sc}`);
|
|
172
|
+
}
|
|
173
|
+
lines.push('');
|
|
174
|
+
lines.push('## ⟡ Scope Boundaries');
|
|
175
|
+
if (m.scope_boundaries.length === 0) {
|
|
176
|
+
lines.push('- _(none specified)_');
|
|
177
|
+
}
|
|
178
|
+
else {
|
|
179
|
+
for (const sb of m.scope_boundaries) {
|
|
180
|
+
lines.push(`- ${sb}`);
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
lines.push('');
|
|
184
|
+
lines.push('## ⟡ Profile');
|
|
185
|
+
lines.push(`- **Primary:** ${m.profile.primary}`);
|
|
186
|
+
lines.push(`- **Secondary:** ${m.profile.secondary.length > 0 ? m.profile.secondary.join(', ') : '_(none)_'}`);
|
|
187
|
+
lines.push(`- **Hard Constraints:** ${m.profile.hard_constraints.length > 0
|
|
188
|
+
? m.profile.hard_constraints.join(', ')
|
|
189
|
+
: '_(none)_'}`);
|
|
190
|
+
lines.push('');
|
|
191
|
+
lines.push('## ⟡ Capabilities');
|
|
192
|
+
lines.push(`- **Skills:** ${m.capabilities.skills.length > 0 ? m.capabilities.skills.join(', ') : '_(none)_'}`);
|
|
193
|
+
lines.push(`- **Tools:** ${m.capabilities.tools.length > 0 ? m.capabilities.tools.join(', ') : '_(none)_'}`);
|
|
194
|
+
lines.push(`- **Agents:** ${m.capabilities.agents.length > 0 ? m.capabilities.agents.join(', ') : '_(none)_'}`);
|
|
195
|
+
lines.push('');
|
|
196
|
+
lines.push('## ⟡ Cost Envelope');
|
|
197
|
+
lines.push(`- **Estimated:** ${m.cost_envelope.estimated_usdc.toFixed(4)} USDC`);
|
|
198
|
+
lines.push(`- **Max (hard ceiling):** ${m.cost_envelope.max_usdc.toFixed(4)} USDC`);
|
|
199
|
+
lines.push(`- **Per-task budget:** ${m.cost_envelope.per_task_budget.toFixed(4)} USDC`);
|
|
200
|
+
lines.push('');
|
|
201
|
+
lines.push('## ⟡ Burn');
|
|
202
|
+
lines.push(`- **Consumed:** ${m.burn.usdc_consumed.toFixed(4)} USDC`);
|
|
203
|
+
lines.push(`- **Tasks Completed:** ${m.burn.tasks_completed}`);
|
|
204
|
+
lines.push(`- **Last Updated:** ${m.burn.last_updated}`);
|
|
205
|
+
lines.push('');
|
|
206
|
+
lines.push(`_signature: ${m.signature.slice(0, 16)}…_`);
|
|
207
|
+
return lines.join('\n');
|
|
208
|
+
}
|
|
209
|
+
function mandatePath(mandate_id) {
|
|
210
|
+
return (0, node_path_1.join)(STATE_ROOT, `${mandate_id}.json`);
|
|
211
|
+
}
|
|
212
|
+
function persistMandate(m) {
|
|
213
|
+
ensureDir(STATE_ROOT);
|
|
214
|
+
const path = mandatePath(m.mandate_id);
|
|
215
|
+
(0, node_fs_1.writeFileSync)(path, JSON.stringify(m, null, 2), 'utf8');
|
|
216
|
+
if (m.status === 'active') {
|
|
217
|
+
(0, node_fs_1.writeFileSync)(ACTIVE_POINTER, JSON.stringify({ mandate_id: m.mandate_id, updated_at: new Date().toISOString() }, null, 2), 'utf8');
|
|
218
|
+
}
|
|
219
|
+
return path;
|
|
220
|
+
}
|
|
221
|
+
function loadMandate(mandate_id) {
|
|
222
|
+
const path = mandatePath(mandate_id);
|
|
223
|
+
if (!(0, node_fs_1.existsSync)(path)) {
|
|
224
|
+
return null;
|
|
225
|
+
}
|
|
226
|
+
try {
|
|
227
|
+
const raw = (0, node_fs_1.readFileSync)(path, 'utf8');
|
|
228
|
+
const parsed = JSON.parse(raw);
|
|
229
|
+
return parsed;
|
|
230
|
+
}
|
|
231
|
+
catch (err) {
|
|
232
|
+
throw new Error(`Failed to load mandate ${mandate_id}: ${err instanceof Error ? err.message : String(err)}`);
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
function loadActiveMandate() {
|
|
236
|
+
if ((0, node_fs_1.existsSync)(ACTIVE_POINTER)) {
|
|
237
|
+
try {
|
|
238
|
+
const raw = (0, node_fs_1.readFileSync)(ACTIVE_POINTER, 'utf8');
|
|
239
|
+
const pointer = JSON.parse(raw);
|
|
240
|
+
if (pointer.mandate_id) {
|
|
241
|
+
const m = loadMandate(pointer.mandate_id);
|
|
242
|
+
if (m && m.status === 'active') {
|
|
243
|
+
return m;
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
catch {
|
|
248
|
+
// fall through to directory scan
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
if (!(0, node_fs_1.existsSync)(STATE_ROOT)) {
|
|
252
|
+
return null;
|
|
253
|
+
}
|
|
254
|
+
const entries = (0, node_fs_1.readdirSync)(STATE_ROOT).filter((f) => f.endsWith('.json') && f !== 'active.json');
|
|
255
|
+
let latest = null;
|
|
256
|
+
for (const entry of entries) {
|
|
257
|
+
try {
|
|
258
|
+
const raw = (0, node_fs_1.readFileSync)((0, node_path_1.join)(STATE_ROOT, entry), 'utf8');
|
|
259
|
+
const m = JSON.parse(raw);
|
|
260
|
+
if (m.status !== 'active')
|
|
261
|
+
continue;
|
|
262
|
+
if (!latest || m.signed_at > latest.signed_at) {
|
|
263
|
+
latest = m;
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
catch {
|
|
267
|
+
continue;
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
return latest;
|
|
271
|
+
}
|
|
272
|
+
/** All persisted mandates, newest first. Skips the active.json pointer and any
|
|
273
|
+
* unreadable file. Used by the REPL `/mandates` listing. */
|
|
274
|
+
function listMandates() {
|
|
275
|
+
if (!(0, node_fs_1.existsSync)(STATE_ROOT)) {
|
|
276
|
+
return [];
|
|
277
|
+
}
|
|
278
|
+
const entries = (0, node_fs_1.readdirSync)(STATE_ROOT).filter((f) => f.endsWith('.json') && f !== 'active.json');
|
|
279
|
+
const out = [];
|
|
280
|
+
for (const entry of entries) {
|
|
281
|
+
try {
|
|
282
|
+
out.push(JSON.parse((0, node_fs_1.readFileSync)((0, node_path_1.join)(STATE_ROOT, entry), 'utf8')));
|
|
283
|
+
}
|
|
284
|
+
catch {
|
|
285
|
+
continue;
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
out.sort((a, b) => String(b.signed_at).localeCompare(String(a.signed_at)));
|
|
289
|
+
return out;
|
|
290
|
+
}
|
|
291
|
+
function checkEnvelope(m) {
|
|
292
|
+
if (m.status === 'revoked') {
|
|
293
|
+
return { reason: 'revoked', detail: 'Mandate has been revoked' };
|
|
294
|
+
}
|
|
295
|
+
if (m.burn.usdc_consumed > m.cost_envelope.max_usdc) {
|
|
296
|
+
return {
|
|
297
|
+
reason: 'budget',
|
|
298
|
+
detail: `Burn ${m.burn.usdc_consumed.toFixed(4)} USDC exceeds hard ceiling ${m.cost_envelope.max_usdc.toFixed(4)} USDC`,
|
|
299
|
+
};
|
|
300
|
+
}
|
|
301
|
+
if (m.deadline) {
|
|
302
|
+
const deadlineMs = Date.parse(m.deadline);
|
|
303
|
+
if (!Number.isNaN(deadlineMs) && Date.now() > deadlineMs) {
|
|
304
|
+
return {
|
|
305
|
+
reason: 'deadline',
|
|
306
|
+
detail: `Deadline ${m.deadline} has passed`,
|
|
307
|
+
};
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
if (m.profile.hard_constraints.length === 0 && m.scope_boundaries.length === 0) {
|
|
311
|
+
// no explicit scope rules to evaluate
|
|
312
|
+
}
|
|
313
|
+
return null;
|
|
314
|
+
}
|
|
315
|
+
function updateBurn(mandate_id, delta_usdc, task_completed) {
|
|
316
|
+
if (!Number.isFinite(delta_usdc) || delta_usdc < 0) {
|
|
317
|
+
throw new Error('delta_usdc must be a non-negative finite number');
|
|
318
|
+
}
|
|
319
|
+
const m = loadMandate(mandate_id);
|
|
320
|
+
if (!m) {
|
|
321
|
+
throw new Error(`Mandate ${mandate_id} not found`);
|
|
322
|
+
}
|
|
323
|
+
m.burn.usdc_consumed = Number((m.burn.usdc_consumed + delta_usdc).toFixed(6));
|
|
324
|
+
if (task_completed) {
|
|
325
|
+
m.burn.tasks_completed += 1;
|
|
326
|
+
}
|
|
327
|
+
m.burn.last_updated = new Date().toISOString();
|
|
328
|
+
const breach = checkEnvelope(m);
|
|
329
|
+
if (breach && breach.reason === 'budget') {
|
|
330
|
+
m.status = 'breached';
|
|
331
|
+
}
|
|
332
|
+
persistMandate(m);
|
|
333
|
+
return m;
|
|
334
|
+
}
|
|
335
|
+
function revokeMandate(mandate_id, reason) {
|
|
336
|
+
if (!reason || reason.trim().length === 0) {
|
|
337
|
+
throw new Error('Revocation reason is required');
|
|
338
|
+
}
|
|
339
|
+
const m = loadMandate(mandate_id);
|
|
340
|
+
if (!m) {
|
|
341
|
+
throw new Error(`Mandate ${mandate_id} not found`);
|
|
342
|
+
}
|
|
343
|
+
m.status = 'revoked';
|
|
344
|
+
m.burn.last_updated = new Date().toISOString();
|
|
345
|
+
persistMandate(m);
|
|
346
|
+
// Clear active pointer if this was the active mandate
|
|
347
|
+
if ((0, node_fs_1.existsSync)(ACTIVE_POINTER)) {
|
|
348
|
+
try {
|
|
349
|
+
const raw = (0, node_fs_1.readFileSync)(ACTIVE_POINTER, 'utf8');
|
|
350
|
+
const pointer = JSON.parse(raw);
|
|
351
|
+
if (pointer.mandate_id === mandate_id) {
|
|
352
|
+
(0, node_fs_1.writeFileSync)(ACTIVE_POINTER, JSON.stringify({ mandate_id: null, revoked: mandate_id, reason, revoked_at: m.burn.last_updated }, null, 2), 'utf8');
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
catch {
|
|
356
|
+
// ignore pointer cleanup errors
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
return m;
|
|
360
|
+
}
|
|
@@ -0,0 +1,243 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.loadMemory = loadMemory;
|
|
37
|
+
exports.renderMemoryPrefix = renderMemoryPrefix;
|
|
38
|
+
exports.writeSessionSummary = writeSessionSummary;
|
|
39
|
+
const fs = __importStar(require("fs"));
|
|
40
|
+
const path = __importStar(require("path"));
|
|
41
|
+
const crypto = __importStar(require("crypto"));
|
|
42
|
+
const DEFAULT_MAX_BYTES = 100_000;
|
|
43
|
+
function walkDir(root, source) {
|
|
44
|
+
const out = [];
|
|
45
|
+
if (!root)
|
|
46
|
+
return out;
|
|
47
|
+
let stat;
|
|
48
|
+
try {
|
|
49
|
+
stat = fs.statSync(root);
|
|
50
|
+
}
|
|
51
|
+
catch {
|
|
52
|
+
return out;
|
|
53
|
+
}
|
|
54
|
+
if (!stat.isDirectory())
|
|
55
|
+
return out;
|
|
56
|
+
const stack = [root];
|
|
57
|
+
while (stack.length > 0) {
|
|
58
|
+
const dir = stack.pop();
|
|
59
|
+
let entries;
|
|
60
|
+
try {
|
|
61
|
+
entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
62
|
+
}
|
|
63
|
+
catch {
|
|
64
|
+
continue;
|
|
65
|
+
}
|
|
66
|
+
for (const entry of entries) {
|
|
67
|
+
if (entry.name.startsWith('.'))
|
|
68
|
+
continue;
|
|
69
|
+
const abs = path.join(dir, entry.name);
|
|
70
|
+
if (entry.isDirectory()) {
|
|
71
|
+
stack.push(abs);
|
|
72
|
+
}
|
|
73
|
+
else if (entry.isFile()) {
|
|
74
|
+
let s;
|
|
75
|
+
try {
|
|
76
|
+
s = fs.statSync(abs);
|
|
77
|
+
}
|
|
78
|
+
catch {
|
|
79
|
+
continue;
|
|
80
|
+
}
|
|
81
|
+
out.push({
|
|
82
|
+
absPath: abs,
|
|
83
|
+
relPath: path.relative(root, abs),
|
|
84
|
+
source,
|
|
85
|
+
size: s.size,
|
|
86
|
+
mtimeMs: s.mtimeMs,
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
return out;
|
|
92
|
+
}
|
|
93
|
+
function loadMemory(workspace, maxBytes) {
|
|
94
|
+
const cap = typeof maxBytes === 'number' && maxBytes >= 0 ? maxBytes : DEFAULT_MAX_BYTES;
|
|
95
|
+
const userDir = workspace.memory_user_dir || '';
|
|
96
|
+
const kognaiDir = workspace.memory_kognai_dir || '';
|
|
97
|
+
const userEntries = walkDir(userDir, 'user');
|
|
98
|
+
const kognaiEntries = walkDir(kognaiDir, 'kognai');
|
|
99
|
+
// Most-recent first across both dirs.
|
|
100
|
+
const all = [...userEntries, ...kognaiEntries].sort((a, b) => b.mtimeMs - a.mtimeMs);
|
|
101
|
+
const filesRead = [];
|
|
102
|
+
const filesSkipped = [];
|
|
103
|
+
const chunks = [];
|
|
104
|
+
let totalBytes = 0;
|
|
105
|
+
for (const entry of all) {
|
|
106
|
+
const sourceLabel = entry.source === 'user' ? 'user' : 'kognai';
|
|
107
|
+
const header = `MEMORY: ${sourceLabel}/${entry.relPath}\n`;
|
|
108
|
+
const footer = `\nMEMORY: end ${sourceLabel}/${entry.relPath}\n`;
|
|
109
|
+
let content;
|
|
110
|
+
try {
|
|
111
|
+
content = fs.readFileSync(entry.absPath, 'utf8');
|
|
112
|
+
}
|
|
113
|
+
catch {
|
|
114
|
+
filesSkipped.push(entry.absPath);
|
|
115
|
+
continue;
|
|
116
|
+
}
|
|
117
|
+
const block = header + content + footer;
|
|
118
|
+
const blockBytes = Buffer.byteLength(block, 'utf8');
|
|
119
|
+
if (totalBytes + blockBytes > cap) {
|
|
120
|
+
filesSkipped.push(entry.absPath);
|
|
121
|
+
continue;
|
|
122
|
+
}
|
|
123
|
+
chunks.push(block);
|
|
124
|
+
totalBytes += blockBytes;
|
|
125
|
+
filesRead.push(entry.absPath);
|
|
126
|
+
}
|
|
127
|
+
return {
|
|
128
|
+
text: chunks.join('\n'),
|
|
129
|
+
files_read: filesRead,
|
|
130
|
+
files_skipped: filesSkipped,
|
|
131
|
+
total_bytes: totalBytes,
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
function renderMemoryPrefix(block) {
|
|
135
|
+
if (!block.files_read || block.files_read.length === 0) {
|
|
136
|
+
return '';
|
|
137
|
+
}
|
|
138
|
+
return ('# Workspace Memory\n\nPrior session memories (most-recent first). Use these for context.\n\n' +
|
|
139
|
+
block.text +
|
|
140
|
+
'\n# End of Workspace Memory\n\n');
|
|
141
|
+
}
|
|
142
|
+
function escapeMarkdown(text) {
|
|
143
|
+
// Escape characters that could break Markdown rendering or list structure.
|
|
144
|
+
return text
|
|
145
|
+
.replace(/\\/g, '\\\\')
|
|
146
|
+
.replace(/\r\n/g, '\n')
|
|
147
|
+
.replace(/\n/g, ' ')
|
|
148
|
+
.replace(/`/g, '\\`')
|
|
149
|
+
.replace(/\*/g, '\\*')
|
|
150
|
+
.replace(/_/g, '\\_')
|
|
151
|
+
.replace(/\[/g, '\\[')
|
|
152
|
+
.replace(/\]/g, '\\]');
|
|
153
|
+
}
|
|
154
|
+
function shortIdFromMandate(mandateId) {
|
|
155
|
+
if (mandateId && mandateId.length > 0) {
|
|
156
|
+
return mandateId.slice(0, 8);
|
|
157
|
+
}
|
|
158
|
+
// Random base36 short id.
|
|
159
|
+
const bytes = crypto.randomBytes(6);
|
|
160
|
+
let n = 0n;
|
|
161
|
+
for (const b of bytes) {
|
|
162
|
+
n = (n << 8n) | BigInt(b);
|
|
163
|
+
}
|
|
164
|
+
return n.toString(36).slice(0, 8).padStart(8, '0');
|
|
165
|
+
}
|
|
166
|
+
function datePartISO(iso) {
|
|
167
|
+
// Extract YYYY-MM-DD from an ISO timestamp; fall back to today (UTC).
|
|
168
|
+
const m = /^(\d{4}-\d{2}-\d{2})/.exec(iso || '');
|
|
169
|
+
if (m)
|
|
170
|
+
return m[1];
|
|
171
|
+
const now = new Date();
|
|
172
|
+
const yyyy = now.getUTCFullYear().toString().padStart(4, '0');
|
|
173
|
+
const mm = (now.getUTCMonth() + 1).toString().padStart(2, '0');
|
|
174
|
+
const dd = now.getUTCDate().toString().padStart(2, '0');
|
|
175
|
+
return `${yyyy}-${mm}-${dd}`;
|
|
176
|
+
}
|
|
177
|
+
function atomicWriteFile(targetPath, contents) {
|
|
178
|
+
const dir = path.dirname(targetPath);
|
|
179
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
180
|
+
const tmpName = `.${path.basename(targetPath)}.${process.pid}.${crypto
|
|
181
|
+
.randomBytes(4)
|
|
182
|
+
.toString('hex')}.tmp`;
|
|
183
|
+
const tmpPath = path.join(dir, tmpName);
|
|
184
|
+
fs.writeFileSync(tmpPath, contents, { encoding: 'utf8' });
|
|
185
|
+
try {
|
|
186
|
+
fs.renameSync(tmpPath, targetPath);
|
|
187
|
+
}
|
|
188
|
+
catch (err) {
|
|
189
|
+
try {
|
|
190
|
+
fs.unlinkSync(tmpPath);
|
|
191
|
+
}
|
|
192
|
+
catch {
|
|
193
|
+
// ignore
|
|
194
|
+
}
|
|
195
|
+
throw err;
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
function writeSessionSummary(workspace, summary) {
|
|
199
|
+
const kognaiDir = workspace.memory_kognai_dir;
|
|
200
|
+
if (!kognaiDir) {
|
|
201
|
+
throw new Error('writeSessionSummary: workspace.memory_kognai_dir is not configured');
|
|
202
|
+
}
|
|
203
|
+
fs.mkdirSync(kognaiDir, { recursive: true });
|
|
204
|
+
const datePart = datePartISO(summary.ended_at || summary.started_at);
|
|
205
|
+
const shortId = shortIdFromMandate(summary.mandate_id);
|
|
206
|
+
const baseName = `session-${datePart}-${shortId}`;
|
|
207
|
+
let candidate = path.join(kognaiDir, `${baseName}.md`);
|
|
208
|
+
let suffix = 1;
|
|
209
|
+
while (fs.existsSync(candidate)) {
|
|
210
|
+
candidate = path.join(kognaiDir, `${baseName}-${suffix}.md`);
|
|
211
|
+
suffix += 1;
|
|
212
|
+
}
|
|
213
|
+
const profile = summary.profile && summary.profile.length > 0 ? summary.profile : 'none';
|
|
214
|
+
const mandate = summary.mandate_id && summary.mandate_id.length > 0 ? summary.mandate_id : 'unsigned';
|
|
215
|
+
const goalEscaped = escapeMarkdown(summary.goal || '');
|
|
216
|
+
const costStr = (() => {
|
|
217
|
+
const n = Number(summary.cost_usdc);
|
|
218
|
+
if (!Number.isFinite(n))
|
|
219
|
+
return '0.0000';
|
|
220
|
+
return n.toFixed(4);
|
|
221
|
+
})();
|
|
222
|
+
const deliverables = summary.files && summary.files.length > 0
|
|
223
|
+
? summary.files.map((f) => `- \`${f}\``).join('\n')
|
|
224
|
+
: '- _none_';
|
|
225
|
+
const md = `# Session ${shortId}\n` +
|
|
226
|
+
`\n` +
|
|
227
|
+
`- **Started:** ${summary.started_at}\n` +
|
|
228
|
+
`- **Ended:** ${summary.ended_at}\n` +
|
|
229
|
+
`- **Goal:** ${goalEscaped}\n` +
|
|
230
|
+
`- **Profile:** ${profile}\n` +
|
|
231
|
+
`- **Mandate:** ${mandate}\n` +
|
|
232
|
+
`- **Tasks shipped:** ${summary.tasks_shipped} / ${summary.tasks_total}\n` +
|
|
233
|
+
`- **Cost:** \\$${costStr} USDC\n` +
|
|
234
|
+
`\n` +
|
|
235
|
+
`## Deliverables\n` +
|
|
236
|
+
`\n` +
|
|
237
|
+
`${deliverables}\n` +
|
|
238
|
+
`\n` +
|
|
239
|
+
`---\n` +
|
|
240
|
+
`*Auto-generated by Kognai orchestrator. Phase 2 will extract agent decisions + key learnings from the session transcript.*\n`;
|
|
241
|
+
atomicWriteFile(candidate, md);
|
|
242
|
+
return candidate;
|
|
243
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.applyRegistryOverrides = applyRegistryOverrides;
|
|
4
|
+
/**
|
|
5
|
+
* registry-overrides.ts — merge optional `.kognai/<file>.json` into a base
|
|
6
|
+
* registry (tool-layer / skill-bank) at module load, so the registries are
|
|
7
|
+
* real and extensible per workspace rather than frozen catalogs (TICKET-233).
|
|
8
|
+
*
|
|
9
|
+
* Resolves the workspace root from KOGNAI_ROOT (set by boot-env) or cwd. A
|
|
10
|
+
* missing or malformed override file is a no-op — it must never break the CLI.
|
|
11
|
+
* Entries override the base by `id`; unknown ids are appended.
|
|
12
|
+
*/
|
|
13
|
+
const node_fs_1 = require("node:fs");
|
|
14
|
+
const node_path_1 = require("node:path");
|
|
15
|
+
function applyRegistryOverrides(base, filename) {
|
|
16
|
+
try {
|
|
17
|
+
const root = process.env.KOGNAI_ROOT || process.cwd();
|
|
18
|
+
const path = (0, node_path_1.join)(root, '.kognai', filename);
|
|
19
|
+
if (!(0, node_fs_1.existsSync)(path))
|
|
20
|
+
return;
|
|
21
|
+
const raw = JSON.parse((0, node_fs_1.readFileSync)(path, 'utf8'));
|
|
22
|
+
const extra = Array.isArray(raw)
|
|
23
|
+
? raw
|
|
24
|
+
: Array.isArray(raw.entries)
|
|
25
|
+
? raw.entries
|
|
26
|
+
: [];
|
|
27
|
+
for (const e of extra) {
|
|
28
|
+
if (!e || typeof e.id !== 'string')
|
|
29
|
+
continue;
|
|
30
|
+
const i = base.findIndex((b) => b.id === e.id);
|
|
31
|
+
if (i >= 0)
|
|
32
|
+
base[i] = e;
|
|
33
|
+
else
|
|
34
|
+
base.push(e);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
catch {
|
|
38
|
+
/* best-effort: a malformed override file must never break the CLI */
|
|
39
|
+
}
|
|
40
|
+
}
|