create-metamynd-agent 0.14.22 → 0.14.24
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/index.mjs +46 -12
- package/package.json +1 -1
package/index.mjs
CHANGED
|
@@ -68,7 +68,11 @@ const GUARD_PKG = '@metamynd/agentsafe-guard';
|
|
|
68
68
|
// 0.21.0: guardTool() settles the hold an allowed call was granted (captured when the tool runs, released when it cannot
|
|
69
69
|
// have), guard.void() releases one signed, and a gated tool's .resume(escalationId, args) runs an approved escalation once.
|
|
70
70
|
// Required: the scaffold's npm run resume uses .resume().
|
|
71
|
-
|
|
71
|
+
// 0.22.0: a concurrent resume of one escalation in a process is refused AUTHORIZATION_IN_USE, and a hold minted for an
|
|
72
|
+
// authorize whose answer never arrived is found by nonce and released. Required: npm run resume relies on the lock.
|
|
73
|
+
// 0.23.0: the guard derives the same risk floor as the gate (owner tier + a payment at or above the owner's share of the
|
|
74
|
+
// per-transaction cap is HIGH), so a local check and the gate agree on a large payment's review. No template change.
|
|
75
|
+
const GUARD_VERSION = '^0.23.0';
|
|
72
76
|
/** The harness entry point's config load, shared by both harness templates: a fresh clone has no
|
|
73
77
|
* agent.metamynd.json (it is gitignored), so say what to do instead of a bare ENOENT (BR-004). */
|
|
74
78
|
function harnessConfigLoad() {
|
|
@@ -148,7 +152,8 @@ const MCP_GUARD_PKG = '@metamynd/agentsafe-mcp-guard';
|
|
|
148
152
|
// 0.24.0: reportOutcome() signs a reportId and an occurrence count (aggregated refusals, a durable spool).
|
|
149
153
|
// 0.25.0: honourApprovals — a Service without requireAuthorization (the value-less gateway) still runs an owner-approved
|
|
150
154
|
// escalation, exactly once (MAGP §8.7.18). Required: the non-financial gateway sets it and asserts guard.honoursApprovals.
|
|
151
|
-
|
|
155
|
+
// 0.26.0: a gateway judges the same derived risk floor as the gate (owner tier + amount share of the cap).
|
|
156
|
+
const MCP_GUARD_VERSION = '^0.26.0';
|
|
152
157
|
/** A DID as it may appear inside a generated string literal (the gateway's allowedAgents pin): no quote, backslash or space. */
|
|
153
158
|
const SAFE_DID = /^did:[a-z0-9]+:[A-Za-z0-9._:%-]+$/;
|
|
154
159
|
const GATEWAY_PKG = '@metamynd/agentsafe-http-gateway';
|
|
@@ -180,7 +185,8 @@ const GATEWAY_PKG = '@metamynd/agentsafe-http-gateway';
|
|
|
180
185
|
// 0.22.0: depends on agentsafe-mcp-guard ^0.23.0.
|
|
181
186
|
// 0.23.0: refusals aggregated with their count, and `reportSpool` (both hosted templates set it); depends on agentsafe-mcp-guard ^0.24.0.
|
|
182
187
|
// 0.24.0: depends on agentsafe-mcp-guard ^0.25.0 (one guard in the tree with MCP_GUARD_VERSION above). No template change.
|
|
183
|
-
|
|
188
|
+
// 0.25.0: depends on agentsafe-mcp-guard ^0.26.0 (one guard in the tree). No template change.
|
|
189
|
+
const GATEWAY_VERSION = '^0.25.0';
|
|
184
190
|
|
|
185
191
|
/** Appended to every scaffolded gateway server: give hold settlements still running a bounded moment on shutdown. */
|
|
186
192
|
const DRAIN_ON_SHUTDOWN = `
|
|
@@ -267,6 +273,11 @@ ${c.b('Options')}
|
|
|
267
273
|
--max-amount <n> Total mandate budget (default 10000)
|
|
268
274
|
--currency <cur> Currency (default USD)
|
|
269
275
|
--merchants <a,b> Allowed merchants, comma-separated (optional)
|
|
276
|
+
--risk-tier <level> The owner's risk floor for the action: low | medium | high | critical. The agent's own
|
|
277
|
+
riskLevel can raise it, never lower it (high = every call is reviewed by a person).
|
|
278
|
+
--amount-review <f> A single payment at or above this share of the per-transaction cap is high risk (reviewed),
|
|
279
|
+
whatever the agent claims. A fraction in (0, 1] (default 0.7), or "off". Without --merchants,
|
|
280
|
+
the first payment to each merchant is reviewed too.
|
|
270
281
|
--byok Bring-your-own-key: generate the keypair locally, provision + prove control
|
|
271
282
|
(MetaMynd never sees the private key). Overridden by --public-key.
|
|
272
283
|
--public-key <hex> BYOK with a key you already hold (SPKI/raw hex); you prove control yourself
|
|
@@ -1024,7 +1035,7 @@ async function resolveHostedRulePack(base, key, { financial, perTxnMax, maxAmoun
|
|
|
1024
1035
|
* dashboard registration shows its own dialog before setting the flag); see the printed message
|
|
1025
1036
|
* either way.
|
|
1026
1037
|
*/
|
|
1027
|
-
async function registerGatewayCounterparty(base, token, did, label, purpose = 'claim', privateKeyHex = null) {
|
|
1038
|
+
async function registerGatewayCounterparty(base, token, did, label, purpose = 'claim', privateKeyHex = null, agents = null) {
|
|
1028
1039
|
try {
|
|
1029
1040
|
// The gateway accepts its own registration (MAGP §8.7.6): ask for a challenge naming this owner, sign it with the gateway's
|
|
1030
1041
|
// key (generated here, so this process holds it), and register with that proof. A DID is never registered on its text.
|
|
@@ -1044,7 +1055,9 @@ async function registerGatewayCounterparty(base, token, did, label, purpose = 'c
|
|
|
1044
1055
|
headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${token}` },
|
|
1045
1056
|
// `report` (a non-financial gateway, which never claims): it may only report to this owner, and it changes nothing about
|
|
1046
1057
|
// who may claim their holds, so it needs no enforcement confirmation (MAGP §8.7.6, §16.4).
|
|
1047
|
-
|
|
1058
|
+
// `agents` (report only): the agents this gateway serves — the issuer lets it claim an approved value-less hold of THOSE
|
|
1059
|
+
// agents only, never another agent of the same owner (MAGP §8.7.6).
|
|
1060
|
+
body: JSON.stringify({ did, label, purpose, ...(proof ? { proof } : {}), ...(agents ? { agents } : {}), ...(purpose === 'claim' ? { confirmEnforcementChange: true } : {}) }),
|
|
1048
1061
|
});
|
|
1049
1062
|
const json = await res.json().catch(() => null);
|
|
1050
1063
|
if (!res.ok || json?.success === false) return { ok: false, message: json?.message ?? `HTTP ${res.status}` };
|
|
@@ -1168,7 +1181,7 @@ function resumeBlock(tool) {
|
|
|
1168
1181
|
// npm start saves an escalated call's arguments to ./escalations/<id>.json. Approve it in the dashboard (AgentSafe ->
|
|
1169
1182
|
// Escalations), then resume it: ${tool}.resume() waits for the decision and runs the tool once, under the authorization
|
|
1170
1183
|
// the approval minted. Your own agent does the same with the escalationId from the refusal (e.governance.escalationId).
|
|
1171
|
-
import { mkdirSync as __mkdir, writeFileSync as __write, readFileSync as __read, rmSync as __rm } from 'node:fs';
|
|
1184
|
+
import { mkdirSync as __mkdir, writeFileSync as __write, readFileSync as __read, rmSync as __rm, renameSync as __rename } from 'node:fs';
|
|
1172
1185
|
const ESCALATIONS = new URL('./escalations/', import.meta.url);
|
|
1173
1186
|
const escalationFile = (id) => {
|
|
1174
1187
|
if (!/^[A-Za-z0-9_-]{1,80}$/.test(String(id ?? ''))) throw new Error('not an escalation id: ' + id);
|
|
@@ -1180,20 +1193,28 @@ function saveEscalation(id, args) {
|
|
|
1180
1193
|
}
|
|
1181
1194
|
if (process.argv[2] === '--resume') {
|
|
1182
1195
|
const id = process.argv[3];
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1196
|
+
// Take the saved call ATOMICALLY (a rename): a second npm run resume of the same escalation started meanwhile finds
|
|
1197
|
+
// nothing to take, so two processes cannot both run it. Put back if it is still undecided; removed once it is final.
|
|
1198
|
+
let saved, taken;
|
|
1199
|
+
try {
|
|
1200
|
+
const file = escalationFile(id);
|
|
1201
|
+
taken = new URL(file.href + '.resuming');
|
|
1202
|
+
__rename(file, taken);
|
|
1203
|
+
saved = JSON.parse(__read(taken, 'utf8'));
|
|
1204
|
+
} catch {
|
|
1205
|
+
console.error(' No saved escalation "' + (id ?? '') + '" to resume (or another npm run resume is running it). Run npm start first, then: npm run resume -- <escalationId>');
|
|
1186
1206
|
process.exit(1);
|
|
1187
1207
|
}
|
|
1188
1208
|
console.log(' waiting for your decision on ' + id + ' (approve it in the dashboard: AgentSafe -> Escalations) ...');
|
|
1189
1209
|
try {
|
|
1190
1210
|
const result = await ${tool}.resume(id, saved);
|
|
1191
|
-
__rm(
|
|
1211
|
+
__rm(taken, { force: true }); // used: a second resume has nothing to run
|
|
1192
1212
|
console.log('\\x1b[32m RAN\\x1b[0m approved, and run exactly once: ' + JSON.stringify(result));
|
|
1193
1213
|
} catch (e) {
|
|
1194
1214
|
const g = e.governance ?? {};
|
|
1195
1215
|
// A final answer (rejected, already used, expired) retires the saved call; still pending or unreachable keeps it.
|
|
1196
|
-
if (g.decision === 'block' && g.reasonCode !== 'GATE_UNREACHABLE') __rm(
|
|
1216
|
+
if (g.decision === 'block' && g.reasonCode !== 'GATE_UNREACHABLE') __rm(taken, { force: true });
|
|
1217
|
+
else __rename(taken, escalationFile(id)); // still undecided, or the gate was unreachable: keep it for the next try
|
|
1197
1218
|
console.log('\\x1b[31m NOT RUN\\x1b[0m ' + (g.reasonCode ?? e.message) + (g.status ? ' (' + g.status + ')' : ''));
|
|
1198
1219
|
process.exitCode = 1;
|
|
1199
1220
|
}
|
|
@@ -4244,6 +4265,13 @@ async function main() {
|
|
|
4244
4265
|
Array.isArray(fileConfig?.merchants) ? fileConfig.merchants.join(',') : '',
|
|
4245
4266
|
);
|
|
4246
4267
|
const merchants = String(merchantsRaw).split(',').map((s) => s.trim()).filter(Boolean);
|
|
4268
|
+
// Risk the agent cannot lower (MAGP 6.4.3): the owner's floor, and the amount share of the cap that is reviewed.
|
|
4269
|
+
const riskTier = typeof args['risk-tier'] === 'string' ? args['risk-tier'].trim().toLowerCase() : undefined;
|
|
4270
|
+
if (riskTier !== undefined && !['low', 'medium', 'high', 'critical'].includes(riskTier)) fail('--risk-tier must be low, medium, high or critical');
|
|
4271
|
+
const amountReviewRaw = typeof args['amount-review'] === 'string' ? args['amount-review'].trim().toLowerCase() : undefined;
|
|
4272
|
+
const amountShare = amountReviewRaw === undefined ? undefined : amountReviewRaw === 'off' ? false : Number(amountReviewRaw);
|
|
4273
|
+
if (typeof amountShare === 'number' && !(amountShare > 0 && amountShare <= 1)) fail('--amount-review must be a fraction in (0, 1], or "off"');
|
|
4274
|
+
const riskFields = { ...(riskTier ? { riskTier } : {}), ...(amountShare !== undefined && financial ? { riskSignals: { amountShare } } : {}) };
|
|
4247
4275
|
|
|
4248
4276
|
// BYOK: --byok generates a keypair on THIS machine (MetaMynd never sees the private key) —
|
|
4249
4277
|
// either locally in this process (default) or, opt-in, via an already-running agentsafe-signer
|
|
@@ -4314,6 +4342,7 @@ async function main() {
|
|
|
4314
4342
|
network: 'testnet',
|
|
4315
4343
|
...(financial ? { currency, maxAmount, perTxnMax } : {}),
|
|
4316
4344
|
merchants,
|
|
4345
|
+
...riskFields,
|
|
4317
4346
|
...(publicKey ? { publicKey } : {}),
|
|
4318
4347
|
...sopFields,
|
|
4319
4348
|
};
|
|
@@ -4322,6 +4351,11 @@ async function main() {
|
|
|
4322
4351
|
if (!config?.agentDid) fail('Provisioning did not return a config with an agentDid.');
|
|
4323
4352
|
console.log(` ${c.green('✓')} agent DID ${c.b(config.agentDid)}`);
|
|
4324
4353
|
if (config.standards?.length) console.log(` ${c.green('✓')} enforced Standards: ${config.standards.join(', ')}`);
|
|
4354
|
+
if (financial && merchants.length === 0) {
|
|
4355
|
+
console.log(c.dim(' no --merchants list: the FIRST payment to each merchant is held for your review (a risk the agent cannot'));
|
|
4356
|
+
console.log(c.dim(' lower). Pass --merchants a,b to pre-approve them, or register them as payees in the dashboard.'));
|
|
4357
|
+
}
|
|
4358
|
+
if (riskTier) console.log(` ${c.green('✓')} risk floor: ${riskTier} (the agent's own claim can raise it, never lower it)`);
|
|
4325
4359
|
|
|
4326
4360
|
// 3b. BYOK: prove control of the key (verify-key), else the gate blocks with AGENT_KEY_UNVERIFIED.
|
|
4327
4361
|
if (daemonPublicKeyHex) {
|
|
@@ -4368,7 +4402,7 @@ async function main() {
|
|
|
4368
4402
|
const gatewayDid = buildDidKey(rawPublicKeyFromSpkiHex(gwKeypair.publicKeyHex));
|
|
4369
4403
|
gatewayIdentity = { did: gatewayDid, keyHex: gwKeypair.privateKeyHex };
|
|
4370
4404
|
console.log(c.dim(`\n → registering the gateway's identity as a trusted counterparty (${financial ? "so it can claim this agent's holds and report to you" : 'so its reports reach your Activity Log'}) …`));
|
|
4371
|
-
const reg = await registerGatewayCounterparty(base, token, gatewayDid, `${scope}-gateway`, financial ? 'claim' : 'report', gwKeypair.privateKeyHex);
|
|
4405
|
+
const reg = await registerGatewayCounterparty(base, token, gatewayDid, `${scope}-gateway`, financial ? 'claim' : 'report', gwKeypair.privateKeyHex, financial ? null : [config.agentDid]);
|
|
4372
4406
|
if (reg.ok) {
|
|
4373
4407
|
console.log(` ${c.green('✓')} gateway DID ${c.b(gatewayDid)} — ${reg.message}`);
|
|
4374
4408
|
} else {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "create-metamynd-agent",
|
|
3
|
-
"version": "0.14.
|
|
3
|
+
"version": "0.14.24",
|
|
4
4
|
"description": "Scaffold a MetaMynd/AgentSafe-governed AI agent in one command — logs in, provisions the agent (identity + mandate + SOP + Standards) in a single call, writes agent.metamynd.json plus a runnable agent + separate tool-gateway process that closes direct-call, confused-deputy, replay, and cumulative-spend bypasses. --harness scaffolds a free, local, zero-network governance harness instead.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|