@kyaki/agents 0.1.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.d.ts +35 -0
- package/dist/approvals.d.ts.map +1 -0
- package/dist/approvals.js +93 -0
- package/dist/approvals.js.map +1 -0
- package/dist/auditor.d.ts +104 -0
- package/dist/auditor.d.ts.map +1 -0
- package/dist/auditor.js +250 -0
- package/dist/auditor.js.map +1 -0
- package/dist/continuous-auditor.d.ts +38 -0
- package/dist/continuous-auditor.d.ts.map +1 -0
- package/dist/continuous-auditor.js +52 -0
- package/dist/continuous-auditor.js.map +1 -0
- package/dist/index.d.ts +20 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +8 -0
- package/dist/index.js.map +1 -0
- package/dist/market.d.ts +10 -0
- package/dist/market.d.ts.map +1 -0
- package/dist/market.js +10 -0
- package/dist/market.js.map +1 -0
- package/dist/procurer.d.ts +54 -0
- package/dist/procurer.d.ts.map +1 -0
- package/dist/procurer.js +142 -0
- package/dist/procurer.js.map +1 -0
- package/dist/steward.d.ts +89 -0
- package/dist/steward.d.ts.map +1 -0
- package/dist/steward.js +121 -0
- package/dist/steward.js.map +1 -0
- package/dist/treasury.d.ts +134 -0
- package/dist/treasury.d.ts.map +1 -0
- package/dist/treasury.js +346 -0
- package/dist/treasury.js.map +1 -0
- package/dist/types.d.ts +47 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/package.json +34 -0
- package/src/approvals.ts +111 -0
- package/src/auditor.ts +342 -0
- package/src/continuous-auditor.ts +68 -0
- package/src/index.ts +31 -0
- package/src/market.ts +11 -0
- package/src/procurer.ts +183 -0
- package/src/steward.ts +168 -0
- package/src/treasury.ts +458 -0
- package/src/types.ts +57 -0
package/dist/treasury.js
ADDED
|
@@ -0,0 +1,346 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* treasury.ts — The Treasurer, KYA's second autonomous agent.
|
|
3
|
+
*
|
|
4
|
+
* Where the Procurer spends OUTWARD, the Treasurer manages the BALANCE SHEET
|
|
5
|
+
* and proves, movement by movement, that it stayed inside its mandate. A full
|
|
6
|
+
* cycle (runCycle) pays due obligations FIRST (due-date order), then sweeps
|
|
7
|
+
* idle cash above a configured buffer into reserve/yield.
|
|
8
|
+
*
|
|
9
|
+
* Reconstructed from the canonical Notion source and rebased onto the on-disk
|
|
10
|
+
* hardened seam: every movement runs through evaluateWithPolicy(), which
|
|
11
|
+
* atomically does kernel-verify -> escalation gate -> org-exposure check ->
|
|
12
|
+
* kernel COMMIT. Account balances mutate ONLY after a committed allow, so a
|
|
13
|
+
* denied or escalated movement can never leave a negative balance or a phantom
|
|
14
|
+
* debit.
|
|
15
|
+
*
|
|
16
|
+
* - Escalated movements park in a TreasuryApprovalInbox that uses the same
|
|
17
|
+
* crash-safe two-phase exactly-once protocol as the Procurer:
|
|
18
|
+
* approved -> claimExecution() -> 'executing' -> confirm / revert / fail.
|
|
19
|
+
* A signed CFO Approval is required to finalize; the agent cannot approve
|
|
20
|
+
* its own movement.
|
|
21
|
+
* - The FX-funding leg is denominated in a currency the org actually HOLDS,
|
|
22
|
+
* so L0/L1 always evaluate a real in-mandate-currency amount rather than
|
|
23
|
+
* tripping a single-currency mandate.
|
|
24
|
+
*/
|
|
25
|
+
import { randomUUID } from 'node:crypto';
|
|
26
|
+
import { createTransactionIntent, } from '@kyaki/core';
|
|
27
|
+
import { evaluateWithPolicy, isSecurityAnomaly, } from '@kyaki/policy';
|
|
28
|
+
import { signApproval, verifyApproval } from './approvals.js';
|
|
29
|
+
/** A deterministic FX desk for demos and tests. */
|
|
30
|
+
export class StaticFX {
|
|
31
|
+
table;
|
|
32
|
+
constructor(table) {
|
|
33
|
+
this.table = table;
|
|
34
|
+
}
|
|
35
|
+
rate(from, to) {
|
|
36
|
+
if (from === to)
|
|
37
|
+
return 1;
|
|
38
|
+
const direct = this.table[`${from}/${to}`];
|
|
39
|
+
if (direct !== undefined)
|
|
40
|
+
return direct;
|
|
41
|
+
const inverse = this.table[`${to}/${from}`];
|
|
42
|
+
if (inverse !== undefined && inverse !== 0)
|
|
43
|
+
return 1 / inverse;
|
|
44
|
+
throw new Error(`NO_FX_RATE: ${from}->${to}`);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Amounts are integer minor units; FX products carry IEEE-754 noise
|
|
49
|
+
* (100000 * 1.1 === 110000.00000000001). Snap to whole minor units before
|
|
50
|
+
* rounding so the kernel always evaluates a clean integer.
|
|
51
|
+
*/
|
|
52
|
+
function roundMinorUnits(x) {
|
|
53
|
+
return Math.round(x);
|
|
54
|
+
}
|
|
55
|
+
function ceilMinorUnits(x) {
|
|
56
|
+
// Strip float noise to 6 decimals, then round up to the next whole unit.
|
|
57
|
+
return Math.ceil(Math.round(x * 1e6) / 1e6);
|
|
58
|
+
}
|
|
59
|
+
export class TreasuryApprovalInbox {
|
|
60
|
+
movements = new Map();
|
|
61
|
+
submit(input) {
|
|
62
|
+
const movement = {
|
|
63
|
+
...input,
|
|
64
|
+
id: `mov_${randomUUID().slice(0, 8)}`,
|
|
65
|
+
submittedAt: new Date().toISOString(),
|
|
66
|
+
status: 'pending',
|
|
67
|
+
};
|
|
68
|
+
this.movements.set(movement.id, movement);
|
|
69
|
+
return movement;
|
|
70
|
+
}
|
|
71
|
+
get(id) { return this.movements.get(id); }
|
|
72
|
+
pending() { return [...this.movements.values()].filter((m) => m.status === 'pending'); }
|
|
73
|
+
all() { return [...this.movements.values()]; }
|
|
74
|
+
/** The CFO countersigns a parked movement with their own key. */
|
|
75
|
+
approve(id, approver) {
|
|
76
|
+
const m = this.movements.get(id);
|
|
77
|
+
if (!m)
|
|
78
|
+
throw new Error(`No movement ${id}`);
|
|
79
|
+
if (m.status !== 'pending')
|
|
80
|
+
throw new Error(`Movement ${id} is ${m.status}, not pending`);
|
|
81
|
+
const approval = signApproval(m.intent.id, approver);
|
|
82
|
+
m.status = 'approved';
|
|
83
|
+
m.decidedBy = approval.approvedBy;
|
|
84
|
+
m.decidedAt = approval.at;
|
|
85
|
+
return approval;
|
|
86
|
+
}
|
|
87
|
+
reject(id, approver) {
|
|
88
|
+
const m = this.movements.get(id);
|
|
89
|
+
if (!m)
|
|
90
|
+
throw new Error(`No movement ${id}`);
|
|
91
|
+
if (m.status !== 'pending')
|
|
92
|
+
throw new Error(`Movement ${id} is ${m.status}, not pending`);
|
|
93
|
+
m.status = 'rejected';
|
|
94
|
+
m.decidedBy = approver.did;
|
|
95
|
+
m.decidedAt = new Date().toISOString();
|
|
96
|
+
return m;
|
|
97
|
+
}
|
|
98
|
+
/** Atomic exactly-once gate: 'approved' -> 'executing'. True iff WE won. */
|
|
99
|
+
claimExecution(id) {
|
|
100
|
+
const m = this.movements.get(id);
|
|
101
|
+
if (!m || m.status !== 'approved')
|
|
102
|
+
return false;
|
|
103
|
+
m.status = 'executing';
|
|
104
|
+
return true;
|
|
105
|
+
}
|
|
106
|
+
confirmExecuted(id) {
|
|
107
|
+
const m = this.movements.get(id);
|
|
108
|
+
if (m && m.status === 'executing')
|
|
109
|
+
m.status = 'executed';
|
|
110
|
+
}
|
|
111
|
+
/** Transient deny: stays retryable. */
|
|
112
|
+
revertToApproved(id, reasons) {
|
|
113
|
+
const m = this.movements.get(id);
|
|
114
|
+
if (m && m.status === 'executing') {
|
|
115
|
+
m.status = 'approved';
|
|
116
|
+
m.failureReasons = reasons;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
/** Security failure: terminal AND visible. */
|
|
120
|
+
markFailed(id, reasons) {
|
|
121
|
+
const m = this.movements.get(id);
|
|
122
|
+
if (m && m.status === 'executing') {
|
|
123
|
+
m.status = 'failed';
|
|
124
|
+
m.failureReasons = reasons;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
export class Treasurer {
|
|
129
|
+
cfg;
|
|
130
|
+
accounts;
|
|
131
|
+
constructor(cfg) {
|
|
132
|
+
this.cfg = cfg;
|
|
133
|
+
this.accounts = new Map(cfg.accounts.map((a) => [a.id, { ...a }]));
|
|
134
|
+
}
|
|
135
|
+
account(id) {
|
|
136
|
+
const a = this.accounts.get(id);
|
|
137
|
+
return a ? { ...a } : undefined;
|
|
138
|
+
}
|
|
139
|
+
/** The signed {intent, mandate} pairs this agent committed — Auditor input. */
|
|
140
|
+
committedSpends() {
|
|
141
|
+
return [...this.committed];
|
|
142
|
+
}
|
|
143
|
+
committed = [];
|
|
144
|
+
operatingFor(currency) {
|
|
145
|
+
return [...this.accounts.values()].find((a) => a.type === 'operating' && a.currency === currency);
|
|
146
|
+
}
|
|
147
|
+
intentFor(source, payee, category, amount, currency, now) {
|
|
148
|
+
return createTransactionIntent({
|
|
149
|
+
agent: this.cfg.agentKeys,
|
|
150
|
+
mandateId: this.cfg.mandate.id,
|
|
151
|
+
merchant: payee,
|
|
152
|
+
amount,
|
|
153
|
+
currency,
|
|
154
|
+
category,
|
|
155
|
+
description: `treasury:${source.id}`,
|
|
156
|
+
...(now ? { now } : {}),
|
|
157
|
+
});
|
|
158
|
+
}
|
|
159
|
+
/**
|
|
160
|
+
* The shared movement pipeline. Balance is debited ONLY on a committed
|
|
161
|
+
* allow. On escalate the movement parks (nothing debited); on deny it
|
|
162
|
+
* aborts (nothing debited). evaluateWithPolicy handles the org-exposure
|
|
163
|
+
* reserve/release and the atomic kernel commit internally.
|
|
164
|
+
*/
|
|
165
|
+
async execute(kind, source, payee, category, amount, currency, obligationId, now) {
|
|
166
|
+
const live = this.accounts.get(source.id);
|
|
167
|
+
// Balance check up front: never sign a movement we cannot fund.
|
|
168
|
+
if (live.balance < amount) {
|
|
169
|
+
return {
|
|
170
|
+
kind, status: 'skipped', accountId: source.id, payee, category, amount, currency,
|
|
171
|
+
...(obligationId ? { obligationId } : {}), reasons: ['INSUFFICIENT_FUNDS'],
|
|
172
|
+
};
|
|
173
|
+
}
|
|
174
|
+
const intent = this.intentFor(live, payee, category, amount, currency, now);
|
|
175
|
+
const result = await evaluateWithPolicy({
|
|
176
|
+
policy: this.cfg.policy, mandate: this.cfg.mandate, intent,
|
|
177
|
+
...(this.cfg.approvalAbove !== undefined ? { approvalAbove: this.cfg.approvalAbove } : {}),
|
|
178
|
+
...(this.cfg.ladder ? { ladder: this.cfg.ladder } : {}),
|
|
179
|
+
...(this.cfg.orgLedger ? { orgLedger: this.cfg.orgLedger } : {}),
|
|
180
|
+
kernelCtx: this.cfg.kernelCtx,
|
|
181
|
+
});
|
|
182
|
+
if (result.decision === 'allow') {
|
|
183
|
+
live.balance -= amount;
|
|
184
|
+
this.committed.push({ intent, mandate: this.cfg.mandate });
|
|
185
|
+
this.cfg.audit?.append({
|
|
186
|
+
type: 'treasury.committed', kind, accountId: source.id, payee, category,
|
|
187
|
+
amount, currency, intentId: intent.id, ...(obligationId ? { obligationId } : {}),
|
|
188
|
+
});
|
|
189
|
+
return {
|
|
190
|
+
kind, status: 'committed', accountId: source.id, payee, category, amount, currency,
|
|
191
|
+
intentId: intent.id, ...(obligationId ? { obligationId } : {}),
|
|
192
|
+
};
|
|
193
|
+
}
|
|
194
|
+
if (result.decision === 'escalate') {
|
|
195
|
+
const movement = this.cfg.approvals.submit({
|
|
196
|
+
intent, mandate: this.cfg.mandate, kind, accountId: source.id, payee, category,
|
|
197
|
+
amount, currency, ...(obligationId ? { obligationId } : {}), reason: result.policy.reasons.join(','),
|
|
198
|
+
});
|
|
199
|
+
this.cfg.audit?.append({
|
|
200
|
+
type: 'treasury.escalated', kind, accountId: source.id, payee, category,
|
|
201
|
+
amount, currency, approvalId: movement.id, intentId: intent.id, ...(obligationId ? { obligationId } : {}),
|
|
202
|
+
});
|
|
203
|
+
return {
|
|
204
|
+
kind, status: 'pending_approval', accountId: source.id, payee, category, amount,
|
|
205
|
+
currency, approvalId: movement.id, intentId: intent.id, ...(obligationId ? { obligationId } : {}),
|
|
206
|
+
};
|
|
207
|
+
}
|
|
208
|
+
const reasons = [...result.kernel.reasons, ...result.policy.reasons];
|
|
209
|
+
this.cfg.audit?.append({
|
|
210
|
+
type: 'treasury.denied', kind, accountId: source.id, payee, category,
|
|
211
|
+
amount, currency, reasons, ...(obligationId ? { obligationId } : {}),
|
|
212
|
+
});
|
|
213
|
+
return {
|
|
214
|
+
kind, status: 'denied', accountId: source.id, payee, category, amount, currency,
|
|
215
|
+
reasons, ...(obligationId ? { obligationId } : {}),
|
|
216
|
+
};
|
|
217
|
+
}
|
|
218
|
+
/** Fund a single obligation, routing through FX if we don't hold its currency. */
|
|
219
|
+
async fundObligation(ob, now) {
|
|
220
|
+
// 1) We hold the obligation's currency directly. Pay if it can cover it;
|
|
221
|
+
// if underfunded, SKIP — converting into a currency we already hold is
|
|
222
|
+
// nonsense, so this is a genuine funding gap.
|
|
223
|
+
const native = this.operatingFor(ob.currency);
|
|
224
|
+
if (native) {
|
|
225
|
+
if (native.balance >= ob.amount) {
|
|
226
|
+
return this.execute('obligation_payment', native, ob.payee, ob.category, ob.amount, ob.currency, ob.id, now);
|
|
227
|
+
}
|
|
228
|
+
return {
|
|
229
|
+
kind: 'obligation_payment', status: 'skipped', accountId: native.id, payee: ob.payee,
|
|
230
|
+
category: ob.category, amount: ob.amount, currency: ob.currency, obligationId: ob.id,
|
|
231
|
+
reasons: ['INSUFFICIENT_FUNDS'],
|
|
232
|
+
};
|
|
233
|
+
}
|
|
234
|
+
// 2) We do NOT hold the obligation's currency: route ONE mandate-governed
|
|
235
|
+
// movement in a currency we hold (prefer base), FX desk settles abroad.
|
|
236
|
+
const candidates = [...this.accounts.values()].filter((a) => a.type === 'operating' && a.currency !== ob.currency);
|
|
237
|
+
const source = candidates.find((a) => a.currency === this.cfg.baseCurrency) ?? candidates[0];
|
|
238
|
+
if (!source) {
|
|
239
|
+
return {
|
|
240
|
+
kind: 'obligation_payment', status: 'skipped', accountId: '—', payee: ob.payee,
|
|
241
|
+
category: ob.category, amount: ob.amount, currency: ob.currency, obligationId: ob.id,
|
|
242
|
+
reasons: ['NO_FUNDING_ACCOUNT'],
|
|
243
|
+
};
|
|
244
|
+
}
|
|
245
|
+
const sourceAmount = ceilMinorUnits(ob.amount * this.cfg.fx.rate(ob.currency, source.currency));
|
|
246
|
+
return this.execute('fx_conversion', source, ob.payee, ob.category, sourceAmount, source.currency, ob.id, now);
|
|
247
|
+
}
|
|
248
|
+
/** A full treasury cycle: pay due obligations FIRST, then sweep idle cash. */
|
|
249
|
+
async runCycle(obligations, now) {
|
|
250
|
+
const at = (now ?? new Date()).getTime();
|
|
251
|
+
const actions = [];
|
|
252
|
+
const due = obligations.filter((o) => o.dueAt <= at).sort((a, b) => a.dueAt - b.dueAt);
|
|
253
|
+
for (const ob of due)
|
|
254
|
+
actions.push(await this.fundObligation(ob, now));
|
|
255
|
+
// Liquidity sweep AFTER obligations, so we never sweep money we owe.
|
|
256
|
+
for (const acct of this.accounts.values()) {
|
|
257
|
+
if (acct.type !== 'operating' || acct.minBuffer === undefined)
|
|
258
|
+
continue;
|
|
259
|
+
const excess = acct.balance - acct.minBuffer;
|
|
260
|
+
if (excess <= 0)
|
|
261
|
+
continue;
|
|
262
|
+
const reserve = [...this.accounts.values()].find((a) => (a.type === 'reserve' || a.type === 'yield') && a.currency === acct.currency);
|
|
263
|
+
if (!reserve)
|
|
264
|
+
continue;
|
|
265
|
+
const action = await this.execute('liquidity_sweep', acct, reserve.id, 'treasury_sweep', excess, acct.currency, undefined, now);
|
|
266
|
+
if (action.status === 'committed')
|
|
267
|
+
this.accounts.get(reserve.id).balance += excess;
|
|
268
|
+
actions.push(action);
|
|
269
|
+
}
|
|
270
|
+
return actions;
|
|
271
|
+
}
|
|
272
|
+
/** Finalize an escalated movement with a signed CFO approval (two-phase). */
|
|
273
|
+
async applyApproval(approvalId, approval) {
|
|
274
|
+
const movement = this.cfg.approvals.get(approvalId);
|
|
275
|
+
if (!movement)
|
|
276
|
+
throw new Error(`No movement ${approvalId}`);
|
|
277
|
+
const base = {
|
|
278
|
+
kind: movement.kind, accountId: movement.accountId, payee: movement.payee,
|
|
279
|
+
category: movement.category, amount: movement.amount, currency: movement.currency,
|
|
280
|
+
...(movement.obligationId ? { obligationId: movement.obligationId } : {}),
|
|
281
|
+
};
|
|
282
|
+
if (!verifyApproval(approval) || approval.intentId !== movement.intent.id) {
|
|
283
|
+
return { ...base, status: 'denied', reasons: ['APPROVAL_SIGNATURE_INVALID'] };
|
|
284
|
+
}
|
|
285
|
+
// Separation of duties: the agent that initiated the movement can never be
|
|
286
|
+
// the human that countersigns it. Approval is authority from ANOTHER key.
|
|
287
|
+
if (approval.approvedBy === this.cfg.agentKeys.did) {
|
|
288
|
+
return { ...base, status: 'denied', reasons: ['SELF_APPROVAL_FORBIDDEN'] };
|
|
289
|
+
}
|
|
290
|
+
// Exactly-once gate: a double-submit dies here, never reaching the kernel.
|
|
291
|
+
if (!this.cfg.approvals.claimExecution(approvalId)) {
|
|
292
|
+
return { ...base, status: 'denied', reasons: ['ALREADY_EXECUTED'] };
|
|
293
|
+
}
|
|
294
|
+
const result = await evaluateWithPolicy({
|
|
295
|
+
policy: this.cfg.policy, mandate: this.cfg.mandate, intent: movement.intent,
|
|
296
|
+
...(this.cfg.approvalAbove !== undefined ? { approvalAbove: this.cfg.approvalAbove } : {}),
|
|
297
|
+
humanApproval: approval,
|
|
298
|
+
...(this.cfg.ladder ? { ladder: this.cfg.ladder } : {}),
|
|
299
|
+
...(this.cfg.orgLedger ? { orgLedger: this.cfg.orgLedger } : {}),
|
|
300
|
+
kernelCtx: this.cfg.kernelCtx,
|
|
301
|
+
});
|
|
302
|
+
if (result.decision !== 'allow') {
|
|
303
|
+
const reasons = [...result.kernel.reasons, ...result.policy.reasons];
|
|
304
|
+
if (isSecurityAnomaly(reasons))
|
|
305
|
+
this.cfg.approvals.markFailed(approvalId, reasons);
|
|
306
|
+
else
|
|
307
|
+
this.cfg.approvals.revertToApproved(approvalId, reasons);
|
|
308
|
+
this.cfg.audit?.append({ type: 'treasury.approval_denied', approvalId, intentId: movement.intent.id, reasons });
|
|
309
|
+
return { ...base, status: 'denied', reasons };
|
|
310
|
+
}
|
|
311
|
+
this.cfg.approvals.confirmExecuted(approvalId);
|
|
312
|
+
const src = this.accounts.get(movement.accountId);
|
|
313
|
+
if (src)
|
|
314
|
+
src.balance -= movement.amount;
|
|
315
|
+
if (movement.kind === 'liquidity_sweep') {
|
|
316
|
+
const reserve = [...this.accounts.values()].find((a) => (a.type === 'reserve' || a.type === 'yield') && a.currency === movement.currency);
|
|
317
|
+
if (reserve)
|
|
318
|
+
reserve.balance += movement.amount;
|
|
319
|
+
}
|
|
320
|
+
this.committed.push({ intent: movement.intent, mandate: movement.mandate });
|
|
321
|
+
this.cfg.audit?.append({
|
|
322
|
+
type: 'treasury.approval_executed', approvalId, intentId: movement.intent.id,
|
|
323
|
+
approvedBy: approval.approvedBy, approvalSignature: approval.signature,
|
|
324
|
+
amount: movement.amount, currency: movement.currency, payee: movement.payee,
|
|
325
|
+
});
|
|
326
|
+
return { ...base, status: 'committed', intentId: movement.intent.id, approvalId };
|
|
327
|
+
}
|
|
328
|
+
/** Multi-currency cash position, converted to base, net of near-term obligations. */
|
|
329
|
+
cashPosition(obligations, horizonMs = 30 * 24 * 3600 * 1000, now) {
|
|
330
|
+
const at = (now ?? new Date()).getTime();
|
|
331
|
+
const byCurrency = {};
|
|
332
|
+
let totalInBase = 0;
|
|
333
|
+
for (const a of this.accounts.values()) {
|
|
334
|
+
byCurrency[a.currency] = (byCurrency[a.currency] ?? 0) + a.balance;
|
|
335
|
+
totalInBase += roundMinorUnits(a.balance * this.cfg.fx.rate(a.currency, this.cfg.baseCurrency));
|
|
336
|
+
}
|
|
337
|
+
const nearTermObligationsInBase = obligations
|
|
338
|
+
.filter((o) => o.dueAt <= at + horizonMs)
|
|
339
|
+
.reduce((s, o) => s + roundMinorUnits(o.amount * this.cfg.fx.rate(o.currency, this.cfg.baseCurrency)), 0);
|
|
340
|
+
return {
|
|
341
|
+
baseCurrency: this.cfg.baseCurrency, byCurrency, totalInBase,
|
|
342
|
+
nearTermObligationsInBase, netLiquidityInBase: totalInBase - nearTermObligationsInBase,
|
|
343
|
+
};
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
//# sourceMappingURL=treasury.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"treasury.js","sourceRoot":"","sources":["../src/treasury.ts"],"names":[],"mappings":"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"}
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* types.ts — L2 agent runtime data model (procurement domain).
|
|
3
|
+
*/
|
|
4
|
+
import type { SpendMandate, TransactionIntent } from '@kyaki/core';
|
|
5
|
+
export interface ProcurementNeed {
|
|
6
|
+
id: string;
|
|
7
|
+
description: string;
|
|
8
|
+
category?: string;
|
|
9
|
+
maxBudget?: number;
|
|
10
|
+
}
|
|
11
|
+
export interface VendorOffer {
|
|
12
|
+
needId: string;
|
|
13
|
+
vendor: string;
|
|
14
|
+
price: number;
|
|
15
|
+
currency: string;
|
|
16
|
+
}
|
|
17
|
+
export interface Market {
|
|
18
|
+
offersFor(needId: string): Promise<VendorOffer[]> | VendorOffer[];
|
|
19
|
+
}
|
|
20
|
+
export type OutcomeStatus = 'purchased' | 'pending_approval' | 'no_compliant_vendor' | 'deferred';
|
|
21
|
+
export interface SkippedOffer {
|
|
22
|
+
vendor: string;
|
|
23
|
+
price: number;
|
|
24
|
+
reason: string;
|
|
25
|
+
}
|
|
26
|
+
export interface ProcurementOutcome {
|
|
27
|
+
need: ProcurementNeed;
|
|
28
|
+
status: OutcomeStatus;
|
|
29
|
+
offer?: VendorOffer;
|
|
30
|
+
txnId?: string;
|
|
31
|
+
approvalId?: string;
|
|
32
|
+
skippedNonCompliant?: SkippedOffer[];
|
|
33
|
+
reasons?: string[];
|
|
34
|
+
}
|
|
35
|
+
export interface ApprovalRequest {
|
|
36
|
+
id: string;
|
|
37
|
+
intent: TransactionIntent;
|
|
38
|
+
mandate: SpendMandate;
|
|
39
|
+
need: ProcurementNeed;
|
|
40
|
+
offer: VendorOffer;
|
|
41
|
+
reason: string;
|
|
42
|
+
submittedAt: string;
|
|
43
|
+
status: 'pending' | 'approved' | 'rejected' | 'executed';
|
|
44
|
+
decidedBy?: string;
|
|
45
|
+
decidedAt?: string;
|
|
46
|
+
}
|
|
47
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,OAAO,KAAK,EAAE,YAAY,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAEnE,MAAM,WAAW,eAAe;IAC9B,EAAE,EAAE,MAAM,CAAC;IACX,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,WAAW;IAC1B,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,MAAM;IACrB,SAAS,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,EAAE,CAAC,GAAG,WAAW,EAAE,CAAC;CACnE;AAED,MAAM,MAAM,aAAa,GACrB,WAAW,GACX,kBAAkB,GAClB,qBAAqB,GACrB,UAAU,CAAC;AAEf,MAAM,WAAW,YAAY;IAC3B,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,kBAAkB;IACjC,IAAI,EAAE,eAAe,CAAC;IACtB,MAAM,EAAE,aAAa,CAAC;IACtB,KAAK,CAAC,EAAE,WAAW,CAAC;IACpB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,mBAAmB,CAAC,EAAE,YAAY,EAAE,CAAC;IACrC,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;CACpB;AAED,MAAM,WAAW,eAAe;IAC9B,EAAE,EAAE,MAAM,CAAC;IACX,MAAM,EAAE,iBAAiB,CAAC;IAC1B,OAAO,EAAE,YAAY,CAAC;IACtB,IAAI,EAAE,eAAe,CAAC;IACtB,KAAK,EAAE,WAAW,CAAC;IACnB,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,EAAE,SAAS,GAAG,UAAU,GAAG,UAAU,GAAG,UAAU,CAAC;IACzD,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB"}
|
package/dist/types.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":""}
|
package/package.json
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@kyaki/agents",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "KYA agent runtime — the Procurer, Treasurer, Auditor, and Steward over the fail-closed kernel.",
|
|
5
|
+
"license": "Apache-2.0",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"types": "./src/index.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"types": "./src/index.ts",
|
|
11
|
+
"import": "./src/index.ts",
|
|
12
|
+
"default": "./src/index.ts"
|
|
13
|
+
}
|
|
14
|
+
},
|
|
15
|
+
"files": ["dist", "src"],
|
|
16
|
+
"scripts": {
|
|
17
|
+
"build": "tsc -p tsconfig.build.json"
|
|
18
|
+
},
|
|
19
|
+
"publishConfig": {
|
|
20
|
+
"access": "public",
|
|
21
|
+
"types": "./dist/index.d.ts",
|
|
22
|
+
"exports": {
|
|
23
|
+
".": {
|
|
24
|
+
"types": "./dist/index.d.ts",
|
|
25
|
+
"import": "./dist/index.js",
|
|
26
|
+
"default": "./dist/index.js"
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
},
|
|
30
|
+
"dependencies": {
|
|
31
|
+
"@kyaki/core": "^0.1.0",
|
|
32
|
+
"@kyaki/policy": "^0.1.0"
|
|
33
|
+
}
|
|
34
|
+
}
|
package/src/approvals.ts
ADDED
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* approvals.ts — The human countersignature workflow.
|
|
3
|
+
*
|
|
4
|
+
* Approval is not a button click recorded in a database row — it is an
|
|
5
|
+
* Ed25519 SIGNATURE by the approver's key over a canonical approval document
|
|
6
|
+
* binding (intentId, approvedBy, at). Exactly-once execution is enforced by
|
|
7
|
+
* markExecuted, which flips state before the kernel is touched.
|
|
8
|
+
*/
|
|
9
|
+
import { randomUUID } from 'node:crypto';
|
|
10
|
+
import {
|
|
11
|
+
canonicalBytes, fromBase58, publicKeyFromDid, sign, toBase58, verifySignature,
|
|
12
|
+
type Identity, type SpendMandate, type TransactionIntent,
|
|
13
|
+
} from '@kyaki/core';
|
|
14
|
+
import type { HumanApproval } from '@kyaki/policy';
|
|
15
|
+
import type { ApprovalRequest, ProcurementNeed, VendorOffer } from './types.js';
|
|
16
|
+
|
|
17
|
+
export interface SignedApproval extends HumanApproval {
|
|
18
|
+
at: string;
|
|
19
|
+
signature: string;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function approvalDocument(intentId: string, approvedBy: string, at: string) {
|
|
23
|
+
return { type: 'KyaHumanApproval', intentId, approvedBy, at };
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/** Produce a CFO countersignature over the canonical approval document. The
|
|
27
|
+
* single signer used by every approval inbox (procurement + treasury), so the
|
|
28
|
+
* signed shape never diverges from what verifyApproval expects. */
|
|
29
|
+
export function signApproval(
|
|
30
|
+
intentId: string,
|
|
31
|
+
approver: Identity,
|
|
32
|
+
at: string = new Date().toISOString(),
|
|
33
|
+
): SignedApproval {
|
|
34
|
+
const doc = approvalDocument(intentId, approver.did, at);
|
|
35
|
+
return { intentId, approvedBy: approver.did, at, signature: toBase58(sign(canonicalBytes(doc), approver.privateKey)) };
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export function verifyApproval(approval: SignedApproval): boolean {
|
|
39
|
+
try {
|
|
40
|
+
const publicKey = publicKeyFromDid(approval.approvedBy);
|
|
41
|
+
const doc = approvalDocument(approval.intentId, approval.approvedBy, approval.at);
|
|
42
|
+
return verifySignature(fromBase58(approval.signature), canonicalBytes(doc), publicKey);
|
|
43
|
+
} catch {
|
|
44
|
+
return false;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export class ApprovalInbox {
|
|
49
|
+
private requests = new Map<string, ApprovalRequest>();
|
|
50
|
+
|
|
51
|
+
submit(input: {
|
|
52
|
+
intent: TransactionIntent; mandate: SpendMandate; need: ProcurementNeed;
|
|
53
|
+
offer: VendorOffer; reason: string;
|
|
54
|
+
}): ApprovalRequest {
|
|
55
|
+
const request: ApprovalRequest = {
|
|
56
|
+
id: `apr_${randomUUID().slice(0, 8)}`,
|
|
57
|
+
intent: input.intent, mandate: input.mandate, need: input.need,
|
|
58
|
+
offer: input.offer, reason: input.reason,
|
|
59
|
+
submittedAt: new Date().toISOString(), status: 'pending',
|
|
60
|
+
};
|
|
61
|
+
this.requests.set(request.id, request);
|
|
62
|
+
return request;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
get(id: string): ApprovalRequest | undefined { return this.requests.get(id); }
|
|
66
|
+
pending(): ApprovalRequest[] { return [...this.requests.values()].filter((r) => r.status === 'pending'); }
|
|
67
|
+
all(): ApprovalRequest[] { return [...this.requests.values()]; }
|
|
68
|
+
|
|
69
|
+
approve(id: string, approver: Identity): SignedApproval {
|
|
70
|
+
const request = this.requests.get(id);
|
|
71
|
+
if (!request) throw new Error(`No approval request ${id}`);
|
|
72
|
+
if (request.status !== 'pending') throw new Error(`Approval ${id} is ${request.status}, not pending`);
|
|
73
|
+
const signed = signApproval(request.intent.id, approver);
|
|
74
|
+
request.status = 'approved';
|
|
75
|
+
request.decidedBy = signed.approvedBy;
|
|
76
|
+
request.decidedAt = signed.at;
|
|
77
|
+
return signed;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
reject(id: string, approver: Identity): ApprovalRequest {
|
|
81
|
+
const request = this.requests.get(id);
|
|
82
|
+
if (!request) throw new Error(`No approval request ${id}`);
|
|
83
|
+
if (request.status !== 'pending') throw new Error(`Approval ${id} is ${request.status}, not pending`);
|
|
84
|
+
request.status = 'rejected';
|
|
85
|
+
request.decidedBy = approver.did;
|
|
86
|
+
request.decidedAt = new Date().toISOString();
|
|
87
|
+
return request;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/** Accept a countersignature produced OUTSIDE the inbox — e.g. signed in the
|
|
91
|
+
* CFO's browser with their own key — as the approval decision. The caller is
|
|
92
|
+
* expected to have already run verifyApproval(); this only binds the decision
|
|
93
|
+
* to the request (and refuses if the signature is for a different intent). */
|
|
94
|
+
acceptSignature(id: string, signed: SignedApproval): boolean {
|
|
95
|
+
const request = this.requests.get(id);
|
|
96
|
+
if (!request || request.status !== 'pending') return false;
|
|
97
|
+
if (signed.intentId !== request.intent.id) return false;
|
|
98
|
+
request.status = 'approved';
|
|
99
|
+
request.decidedBy = signed.approvedBy;
|
|
100
|
+
request.decidedAt = signed.at;
|
|
101
|
+
return true;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/** Exactly-once gate. Returns true the FIRST time only. */
|
|
105
|
+
markExecuted(id: string): boolean {
|
|
106
|
+
const request = this.requests.get(id);
|
|
107
|
+
if (!request || request.status !== 'approved') return false;
|
|
108
|
+
request.status = 'executed';
|
|
109
|
+
return true;
|
|
110
|
+
}
|
|
111
|
+
}
|