create-metamynd-agent 0.14.18 → 0.14.22
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 +131 -34
- package/package.json +1 -1
package/index.mjs
CHANGED
|
@@ -65,7 +65,10 @@ const GUARD_PKG = '@metamynd/agentsafe-guard';
|
|
|
65
65
|
// authorize request of its choosing, MAGP §8.2). Required for every scaffolded agent.
|
|
66
66
|
// 0.20.0: guard.capture() signs as the agent (MAGP-SETTLE-v1) — a hold nobody claimed is settled only by its agent, a
|
|
67
67
|
// counterparty the owner registered, or (an open testnet owner) anyone (MAGP §8.7.4).
|
|
68
|
-
|
|
68
|
+
// 0.21.0: guardTool() settles the hold an allowed call was granted (captured when the tool runs, released when it cannot
|
|
69
|
+
// have), guard.void() releases one signed, and a gated tool's .resume(escalationId, args) runs an approved escalation once.
|
|
70
|
+
// Required: the scaffold's npm run resume uses .resume().
|
|
71
|
+
const GUARD_VERSION = '^0.21.0';
|
|
69
72
|
/** The harness entry point's config load, shared by both harness templates: a fresh clone has no
|
|
70
73
|
* agent.metamynd.json (it is gitignored), so say what to do instead of a bare ENOENT (BR-004). */
|
|
71
74
|
function harnessConfigLoad() {
|
|
@@ -143,7 +146,9 @@ const MCP_GUARD_PKG = '@metamynd/agentsafe-mcp-guard';
|
|
|
143
146
|
// 0.23.0: acceptCounterpartyChallenge() — a Service signs its acceptance of an owner's registration (MAGP §8.7.6). No
|
|
144
147
|
// template change; required with GATEWAY_VERSION below so the gateway never resolves a second guard of its own.
|
|
145
148
|
// 0.24.0: reportOutcome() signs a reportId and an occurrence count (aggregated refusals, a durable spool).
|
|
146
|
-
|
|
149
|
+
// 0.25.0: honourApprovals — a Service without requireAuthorization (the value-less gateway) still runs an owner-approved
|
|
150
|
+
// escalation, exactly once (MAGP §8.7.18). Required: the non-financial gateway sets it and asserts guard.honoursApprovals.
|
|
151
|
+
const MCP_GUARD_VERSION = '^0.25.0';
|
|
147
152
|
/** A DID as it may appear inside a generated string literal (the gateway's allowedAgents pin): no quote, backslash or space. */
|
|
148
153
|
const SAFE_DID = /^did:[a-z0-9]+:[A-Za-z0-9._:%-]+$/;
|
|
149
154
|
const GATEWAY_PKG = '@metamynd/agentsafe-http-gateway';
|
|
@@ -174,7 +179,8 @@ const GATEWAY_PKG = '@metamynd/agentsafe-http-gateway';
|
|
|
174
179
|
// 0.21.0: per-route allowedAgents; depends on agentsafe-mcp-guard ^0.22.0.
|
|
175
180
|
// 0.22.0: depends on agentsafe-mcp-guard ^0.23.0.
|
|
176
181
|
// 0.23.0: refusals aggregated with their count, and `reportSpool` (both hosted templates set it); depends on agentsafe-mcp-guard ^0.24.0.
|
|
177
|
-
|
|
182
|
+
// 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
|
+
const GATEWAY_VERSION = '^0.24.0';
|
|
178
184
|
|
|
179
185
|
/** Appended to every scaffolded gateway server: give hold settlements still running a bounded moment on shutdown. */
|
|
180
186
|
const DRAIN_ON_SHUTDOWN = `
|
|
@@ -1134,12 +1140,66 @@ function keepConfigAndFail(outDir, config, why) {
|
|
|
1134
1140
|
* guard.escalationStatus(id) for the authorization and presents it to run the action (MAGP §8.7.18). Generated code: it
|
|
1135
1141
|
* reads `g`, the refusal's governance verdict, and `dim`, both in scope at every call site.
|
|
1136
1142
|
*/
|
|
1137
|
-
function escalateNote(sandbox) {
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
console.log(dim(' Provision your own agent to approve one in your dashboard.'))
|
|
1141
|
-
|
|
1143
|
+
function escalateNote(sandbox, argsExpr = null) {
|
|
1144
|
+
if (sandbox) {
|
|
1145
|
+
return `console.log(dim(' nobody can approve it here: the shared sandbox has no owner you can sign in as.'));
|
|
1146
|
+
console.log(dim(' Provision your own agent to approve one in your dashboard.'));`;
|
|
1147
|
+
}
|
|
1148
|
+
if (!argsExpr) {
|
|
1149
|
+
return `console.log(dim(' not a failure: it waits in your dashboard, under Escalations' + (g.escalationId ? ' (' + g.escalationId + ')' : '') + '.'));
|
|
1142
1150
|
console.log(dim(' Once you approve it, guard.escalationStatus(id) gives the agent the authorization to run it.'));`;
|
|
1151
|
+
}
|
|
1152
|
+
// Resumable (0.14.21; pre-beta rerun 3, E-1): the call is saved so `npm run resume -- <id>` can run it, once approved.
|
|
1153
|
+
return `console.log(dim(' not a failure: it waits in your dashboard, under Escalations' + (g.escalationId ? ' (' + g.escalationId + ')' : '') + '.'));
|
|
1154
|
+
if (g.escalationId) {
|
|
1155
|
+
saveEscalation(g.escalationId, ${argsExpr});
|
|
1156
|
+
console.log(dim(' Approve it there, then run it - exactly once - with: npm run resume -- ' + g.escalationId));
|
|
1157
|
+
}`;
|
|
1158
|
+
}
|
|
1159
|
+
|
|
1160
|
+
/**
|
|
1161
|
+
* The resume path a resumable scaffold carries (0.14.21; pre-beta rerun 3, E-1 — resuming used to mean writing ~15 lines
|
|
1162
|
+
* by hand). `npm start` saves an escalated call's arguments to ./escalations/<id>.json; `npm run resume -- <id>` hands them
|
|
1163
|
+
* to the gated tool's `.resume()` (agentsafe-guard >= 0.21.0), which waits for the owner's decision and then runs the tool
|
|
1164
|
+
* exactly once under the authorization the approval minted. Generated code; `tool` is the gated tool's variable name.
|
|
1165
|
+
*/
|
|
1166
|
+
function resumeBlock(tool) {
|
|
1167
|
+
return `// --- Escalations: \`npm run resume -- <escalationId>\` runs a call your owner APPROVED, exactly once. ---
|
|
1168
|
+
// npm start saves an escalated call's arguments to ./escalations/<id>.json. Approve it in the dashboard (AgentSafe ->
|
|
1169
|
+
// Escalations), then resume it: ${tool}.resume() waits for the decision and runs the tool once, under the authorization
|
|
1170
|
+
// 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';
|
|
1172
|
+
const ESCALATIONS = new URL('./escalations/', import.meta.url);
|
|
1173
|
+
const escalationFile = (id) => {
|
|
1174
|
+
if (!/^[A-Za-z0-9_-]{1,80}$/.test(String(id ?? ''))) throw new Error('not an escalation id: ' + id);
|
|
1175
|
+
return new URL(id + '.json', ESCALATIONS);
|
|
1176
|
+
};
|
|
1177
|
+
function saveEscalation(id, args) {
|
|
1178
|
+
__mkdir(ESCALATIONS, { recursive: true });
|
|
1179
|
+
__write(escalationFile(id), JSON.stringify(args, null, 2));
|
|
1180
|
+
}
|
|
1181
|
+
if (process.argv[2] === '--resume') {
|
|
1182
|
+
const id = process.argv[3];
|
|
1183
|
+
let saved;
|
|
1184
|
+
try { saved = JSON.parse(__read(escalationFile(id), 'utf8')); } catch {
|
|
1185
|
+
console.error(' No saved escalation "' + (id ?? '') + '". Run npm start first, then: npm run resume -- <escalationId>');
|
|
1186
|
+
process.exit(1);
|
|
1187
|
+
}
|
|
1188
|
+
console.log(' waiting for your decision on ' + id + ' (approve it in the dashboard: AgentSafe -> Escalations) ...');
|
|
1189
|
+
try {
|
|
1190
|
+
const result = await ${tool}.resume(id, saved);
|
|
1191
|
+
__rm(escalationFile(id), { force: true }); // used: a second resume has nothing to run
|
|
1192
|
+
console.log('\\x1b[32m RAN\\x1b[0m approved, and run exactly once: ' + JSON.stringify(result));
|
|
1193
|
+
} catch (e) {
|
|
1194
|
+
const g = e.governance ?? {};
|
|
1195
|
+
// 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(escalationFile(id), { force: true });
|
|
1197
|
+
console.log('\\x1b[31m NOT RUN\\x1b[0m ' + (g.reasonCode ?? e.message) + (g.status ? ' (' + g.status + ')' : ''));
|
|
1198
|
+
process.exitCode = 1;
|
|
1199
|
+
}
|
|
1200
|
+
process.exit();
|
|
1201
|
+
}
|
|
1202
|
+
`;
|
|
1143
1203
|
}
|
|
1144
1204
|
|
|
1145
1205
|
function exampleIndexNoGateway(scope, perTxnMax, currency, merchant, sandbox = false) {
|
|
@@ -1195,6 +1255,7 @@ const gatedRaiseOwnLimit = guard.guardTool(
|
|
|
1195
1255
|
}),
|
|
1196
1256
|
);
|
|
1197
1257
|
|
|
1258
|
+
${sandbox ? '' : resumeBlock('gatedBookFlight')}
|
|
1198
1259
|
const dim = (t) => '\\x1b[2m' + t + '\\x1b[0m';
|
|
1199
1260
|
const bold = (t) => '\\x1b[1m' + t + '\\x1b[0m';
|
|
1200
1261
|
const rule = (n) => ' ' + '-'.repeat(n);
|
|
@@ -1253,7 +1314,7 @@ async function attempt(n, intent, args, tool = gatedBookFlight) {
|
|
|
1253
1314
|
if (g.decision === 'escalate') {
|
|
1254
1315
|
console.log('\\x1b[33m ESCALATED\\x1b[0m held for a human - ' + g.reasonCode);
|
|
1255
1316
|
console.log(dim(' ' + why));
|
|
1256
|
-
${escalateNote(sandbox)}
|
|
1317
|
+
${escalateNote(sandbox, sandbox ? null : 'args')}
|
|
1257
1318
|
} else {
|
|
1258
1319
|
console.log('\\x1b[31m BLOCKED\\x1b[0m ' + (g.reasonCode ?? 'refused'));
|
|
1259
1320
|
console.log(dim(' ' + why));
|
|
@@ -1400,6 +1461,7 @@ const gatedRaiseOwnLimit = guard.guardTool(
|
|
|
1400
1461
|
}),
|
|
1401
1462
|
);
|
|
1402
1463
|
|
|
1464
|
+
${resumeBlock('gatedBookFlight')}
|
|
1403
1465
|
const dim = (t) => '\\x1b[2m' + t + '\\x1b[0m';
|
|
1404
1466
|
const bold = (t) => '\\x1b[1m' + t + '\\x1b[0m';
|
|
1405
1467
|
const rule = (n) => ' ' + '-'.repeat(n);
|
|
@@ -1461,7 +1523,7 @@ async function attempt(n, intent, args, tool = gatedBookFlight) {
|
|
|
1461
1523
|
if (g.decision === 'escalate') {
|
|
1462
1524
|
console.log('\\x1b[33m ESCALATED\\x1b[0m held for a human - ' + g.reasonCode);
|
|
1463
1525
|
console.log(dim(' ' + why));
|
|
1464
|
-
${escalateNote(false)}
|
|
1526
|
+
${escalateNote(false, 'args')}
|
|
1465
1527
|
} else {
|
|
1466
1528
|
console.log('\\x1b[31m BLOCKED\\x1b[0m ' + (g.reasonCode ?? 'refused'));
|
|
1467
1529
|
console.log(dim(' ' + why));
|
|
@@ -1615,7 +1677,7 @@ const gatedChangeOwnPermissions = guard.guardTool(
|
|
|
1615
1677
|
changeOwnPermissions,
|
|
1616
1678
|
(a) => ({ context: a }),
|
|
1617
1679
|
);
|
|
1618
|
-
|
|
1680
|
+
${!sandbox ? '\n' + resumeBlock('gatedAction') : ''}
|
|
1619
1681
|
const STEPS = ${JSON.stringify(steps, null, 2)};
|
|
1620
1682
|
const INPUTS = ${JSON.stringify(demo.inputs)};
|
|
1621
1683
|
const NOT_STAGED = ${JSON.stringify(demo.notDemonstrated)};
|
|
@@ -1636,7 +1698,7 @@ async function attempt(n, total, step) {
|
|
|
1636
1698
|
got = g.decision ?? 'error';
|
|
1637
1699
|
if (g.decision === 'escalate') {
|
|
1638
1700
|
console.log('\\x1b[33m ESCALATED\\x1b[0m held for a human - ' + g.reasonCode);
|
|
1639
|
-
${escalateNote(sandbox)}
|
|
1701
|
+
${escalateNote(sandbox, !sandbox ? 'step.context' : null)}
|
|
1640
1702
|
} else {
|
|
1641
1703
|
console.log('\\x1b[31m BLOCKED\\x1b[0m ' + (g.reasonCode ?? e.message));
|
|
1642
1704
|
console.log(dim(g.eventId ? ' decided by the MetaMynd gate - your tool never ran.' : ' decided right here from your signed rules (no network call), and reported for audit - your tool never ran.'));
|
|
@@ -1759,15 +1821,20 @@ const identity = JSON.parse(readFileSync(new URL('./service.metamynd.json', impo
|
|
|
1759
1821
|
// whose owner granted it this same action would run your tool with your credentials (refused AGENT_NOT_ADMITTED instead).
|
|
1760
1822
|
// Add a DID here only for another agent you mean this gateway to act for. gatewayOwnerPrincipal binds it to YOU, the owner of
|
|
1761
1823
|
// its credentials: an agent listed here that another principal owns is refused anyway (GATEWAY_OWNER_MISMATCH).
|
|
1762
|
-
|
|
1763
|
-
//
|
|
1824
|
+
// honourApprovals: with requireAuthorization off this gateway claims nothing — except an escalated call that presents the
|
|
1825
|
+
// authorization your APPROVAL minted (Dashboard -> Escalations). That one it claims as an approval: the issuer grants it
|
|
1826
|
+
// once, only for a hold a person approved, for this agent and these values, so the approved call runs here exactly once.
|
|
1827
|
+
const guard = createMcpGuard({ serviceDid: identity.serviceDid, serviceKey: identity.serviceKey ?? undefined, issuerApi: MAGP_API, requireAuthorization: false, policyPublicKey: '${policyKey}', allowedAgents: ['${agentDid}'], gatewayOwnerPrincipal: '${ownerPrincipal}', honourApprovals: true });
|
|
1828
|
+
// A guard below 0.22.0 cannot enforce both pins (below 0.20.0 it ignores allowedAgents and serves every agent; 0.22.0 added gatewayOwnerPrincipal): refuse to start on one (a stale lockfile, say).
|
|
1764
1829
|
if (!Array.isArray(guard.allowedAgents) || !guard.gatewayOwnerPrincipal) throw new Error('this gateway needs @metamynd/agentsafe-mcp-guard >= 0.22.0 to enforce allowedAgents and gatewayOwnerPrincipal - run npm install');
|
|
1830
|
+
// A guard below 0.25.0 ignores honourApprovals, and an approved escalation would stay escalated here: refuse to start on one.
|
|
1831
|
+
if (guard.honoursApprovals !== true) throw new Error('this gateway needs @metamynd/agentsafe-mcp-guard >= 0.25.0 to run approved escalations - run npm install');
|
|
1765
1832
|
|
|
1766
1833
|
const gateway = createHttpGateway({
|
|
1767
1834
|
guard,
|
|
1768
1835
|
routes,
|
|
1769
1836
|
// Every request this gateway runs or refuses is reported, signed with the identity above, to your Activity Log (MAGP
|
|
1770
|
-
// 16.4). It claims
|
|
1837
|
+
// 16.4). It claims only an approved escalation, so without this the issuer would never see what it executed - or that another agent tried.
|
|
1771
1838
|
reportOutcomes: true,
|
|
1772
1839
|
// A report the issuer could not take (it was down, say) is kept here and re-sent, so nothing it saw is lost. Gitignored.
|
|
1773
1840
|
reportSpool: './reports.spool.jsonl',
|
|
@@ -1895,10 +1962,18 @@ Replace \`performAction()\` in \`server.mjs\` with your implementation and put a
|
|
|
1895
1962
|
|
|
1896
1963
|
/** The README for a non-financial hosted scaffold. `withGateway` selects the two-process shape. */
|
|
1897
1964
|
/** The generated README's "Change the rules" body: the dashboard path, or — for the shared sandbox agent — how to get an agent whose rules you can change. */
|
|
1898
|
-
function changeRulesSection(sandbox) {
|
|
1965
|
+
function changeRulesSection(sandbox, resumable = false) {
|
|
1899
1966
|
return sandbox
|
|
1900
1967
|
? `${SANDBOX_RULES_FIXED} An \`escalate\` verdict is held for an owner to approve; poll \`guard.escalationStatus(id)\`.`
|
|
1901
|
-
:
|
|
1968
|
+
: resumable
|
|
1969
|
+
? `Edit the agent's SOPs in the dashboard (${SOPS_PATH}). The agent's behaviour changes live —
|
|
1970
|
+
no redeploy.
|
|
1971
|
+
|
|
1972
|
+
An \`escalate\` verdict is held for an owner to approve. \`npm start\` saves an escalated call and prints its id; approve it
|
|
1973
|
+
in the dashboard (AgentSafe → Escalations), then \`npm run resume -- <escalationId>\` runs it exactly once under the
|
|
1974
|
+
authorization the approval minted. In your own agent that is one call: \`await gatedTool.resume(escalationId, sameArgs)\`
|
|
1975
|
+
(\`@metamynd/agentsafe-guard\` ≥ 0.21.0) — pass the SAME arguments; the approval is bound to that request.`
|
|
1976
|
+
: `Edit the agent's SOPs in the dashboard (${SOPS_PATH}). The agent's behaviour changes live —
|
|
1902
1977
|
no redeploy. An \`escalate\` verdict is held for an owner to approve; poll \`guard.escalationStatus(id)\`.`;
|
|
1903
1978
|
}
|
|
1904
1979
|
|
|
@@ -1981,7 +2056,7 @@ ${clonedFreshSection(daemonKey)}
|
|
|
1981
2056
|
|
|
1982
2057
|
## Change the rules
|
|
1983
2058
|
|
|
1984
|
-
${changeRulesSection(sandbox)}
|
|
2059
|
+
${changeRulesSection(sandbox, !sandbox)}
|
|
1985
2060
|
|
|
1986
2061
|
## What this is not
|
|
1987
2062
|
|
|
@@ -2000,7 +2075,7 @@ Full integration guide: \`docs/integration/INTEGRATE-WITH-METAMYND.md\`.
|
|
|
2000
2075
|
`;
|
|
2001
2076
|
}
|
|
2002
2077
|
|
|
2003
|
-
function examplePackageJson(slug, neutral = false) {
|
|
2078
|
+
function examplePackageJson(slug, neutral = false, resumable = false) {
|
|
2004
2079
|
return JSON.stringify(
|
|
2005
2080
|
{
|
|
2006
2081
|
name: slug,
|
|
@@ -2010,7 +2085,7 @@ function examplePackageJson(slug, neutral = false) {
|
|
|
2010
2085
|
// `verify` is scaffolded in because governance that lives only in a dashboard is a
|
|
2011
2086
|
// thing someone has to remember to look at. As a build step it is a control: a change
|
|
2012
2087
|
// that widens this agent's authority fails `npm test`.
|
|
2013
|
-
scripts: { start: 'node index.mjs', test: neutral ? 'agentsafe-guard verify --context ./verify-context.json' : 'agentsafe-guard verify' },
|
|
2088
|
+
scripts: { start: 'node index.mjs', ...(resumable ? { resume: 'node index.mjs --resume' } : {}), test: neutral ? 'agentsafe-guard verify --context ./verify-context.json' : 'agentsafe-guard verify' },
|
|
2014
2089
|
dependencies: { [GUARD_PKG]: GUARD_VERSION },
|
|
2015
2090
|
},
|
|
2016
2091
|
null,
|
|
@@ -2112,14 +2187,14 @@ ${clonedFreshSection(daemonKey)}
|
|
|
2112
2187
|
|
|
2113
2188
|
## Change the rules
|
|
2114
2189
|
|
|
2115
|
-
${changeRulesSection(sandbox)}
|
|
2190
|
+
${changeRulesSection(sandbox, !sandbox)}
|
|
2116
2191
|
|
|
2117
2192
|
Full integration guide: \`docs/integration/INTEGRATE-WITH-METAMYND.md\`.
|
|
2118
2193
|
`;
|
|
2119
2194
|
}
|
|
2120
2195
|
|
|
2121
2196
|
function gitignore() {
|
|
2122
|
-
return `node_modules/\nagent.metamynd.json\n.env\n`;
|
|
2197
|
+
return `node_modules/\nagent.metamynd.json\n.env\nescalations/\n`;
|
|
2123
2198
|
}
|
|
2124
2199
|
|
|
2125
2200
|
// ---------- the default hosted scaffold's second process: a separate tool gateway ----------
|
|
@@ -2194,7 +2269,7 @@ const identity = JSON.parse(readFileSync(new URL('./service.metamynd.json', impo
|
|
|
2194
2269
|
// The claim below does not stop that on its own: the hold belongs to the CALLER, and the caller's owner decides which
|
|
2195
2270
|
// services may claim it — including registering this gateway's DID as one of theirs.
|
|
2196
2271
|
const guard = createMcpGuard({ serviceDid: identity.serviceDid, serviceKey: identity.serviceKey ?? undefined, issuerApi: MAGP_API, requireAuthorization: true, policyPublicKey: '${policyKey}', allowedAgents: ['${agentDid}'], gatewayOwnerPrincipal: '${ownerPrincipal}' });
|
|
2197
|
-
// A guard below 0.20.0 ignores allowedAgents and serves every agent: refuse to start on one (a stale lockfile, say).
|
|
2272
|
+
// A guard below 0.22.0 cannot enforce both pins (below 0.20.0 it ignores allowedAgents and serves every agent; 0.22.0 added gatewayOwnerPrincipal): refuse to start on one (a stale lockfile, say).
|
|
2198
2273
|
if (!Array.isArray(guard.allowedAgents) || !guard.gatewayOwnerPrincipal) throw new Error('this gateway needs @metamynd/agentsafe-mcp-guard >= 0.22.0 to enforce allowedAgents and gatewayOwnerPrincipal - run npm install');
|
|
2199
2274
|
|
|
2200
2275
|
const gateway = createHttpGateway({
|
|
@@ -2388,17 +2463,33 @@ on purpose: an agent must never be able to release or lower-settle a hold it aut
|
|
|
2388
2463
|
provisioning, \`npx create-metamynd-agent\` registered that DID as a trusted counterparty for you
|
|
2389
2464
|
(\`POST /policy/counterparties\`). A **MAINNET** hold is *always* registered-only, regardless of your
|
|
2390
2465
|
registry (MAGP §8.7.10) — a testnet hold stays open unless you have registered anything at all.
|
|
2391
|
-
Manage the registry at \`/dashboard/counterparties
|
|
2466
|
+
Manage the registry at \`/dashboard/counterparties\` (it walks you through the proof below), or
|
|
2467
|
+
re-register this gateway yourself — after removing it from the registry, say.
|
|
2468
|
+
|
|
2469
|
+
Registration needs **proof of control** (MAGP §8.7.6): you ask for a challenge naming you, this
|
|
2470
|
+
gateway signs it with its own key from \`service.metamynd.json\`, and you register with that
|
|
2471
|
+
signature. A DID is never registered on its text alone, so nobody can register a gateway they do
|
|
2472
|
+
not hold the key for. Run this from this directory (bash; \`TOKEN\` is your owner access token):
|
|
2392
2473
|
|
|
2393
2474
|
\`\`\`bash
|
|
2394
|
-
|
|
2395
|
-
|
|
2396
|
-
|
|
2475
|
+
MAGP_API=https://metamynd.ai/api/v1 TOKEN=<owner access token> node --input-type=module -e "
|
|
2476
|
+
import { readFileSync } from 'node:fs'; import crypto from 'node:crypto';
|
|
2477
|
+
const { serviceDid, serviceKey } = JSON.parse(readFileSync('./service.metamynd.json', 'utf8'));
|
|
2478
|
+
const api = process.env.MAGP_API, headers = { 'Content-Type': 'application/json', Authorization: 'Bearer ' + process.env.TOKEN };
|
|
2479
|
+
const post = (path, body) => fetch(api + path, { method: 'POST', headers, body: JSON.stringify(body) });
|
|
2480
|
+
const ch = (await (await post('/policy/counterparties/challenge', { did: serviceDid, purpose: 'claim' })).json()).data;
|
|
2481
|
+
const key = crypto.createPrivateKey({ key: Buffer.from(serviceKey, 'hex'), format: 'der', type: 'pkcs8' });
|
|
2482
|
+
const signature = crypto.sign(null, Buffer.from(ch.message, 'utf8'), key).toString('hex');
|
|
2483
|
+
const r = await post('/policy/counterparties', { did: serviceDid, purpose: 'claim', proof: { challengeToken: ch.challengeToken, signature }, confirmEnforcementChange: true });
|
|
2484
|
+
console.log(r.status, await r.text());
|
|
2485
|
+
"
|
|
2397
2486
|
\`\`\`
|
|
2398
2487
|
|
|
2399
|
-
|
|
2400
|
-
|
|
2401
|
-
|
|
2488
|
+
The gateway's own \`guard.acceptCounterpartyChallenge(message)\` (\`@metamynd/agentsafe-mcp-guard\`
|
|
2489
|
+
≥ 0.23.0) signs the same thing, if you would rather expose it from \`server.mjs\`.
|
|
2490
|
+
\`confirmEnforcementChange: true\` matters only for your account's FIRST-EVER registration — it
|
|
2491
|
+
switches EVERY one of your holds (not just this agent's) from open to registered-only, so the API
|
|
2492
|
+
refuses a first registration without it.
|
|
2402
2493
|
- **Amount unknown.** \`amount-unknown\` (\`@metamynd/agentsafe-mcp-guard\` ≥ 0.3.0) blocks a
|
|
2403
2494
|
platform tool by default when its raw bytes or a nested payload hide the amount from a naive
|
|
2404
2495
|
spend cap — AND this agent's OWN starter SOP (see \`agent.metamynd.json\` /
|
|
@@ -2479,7 +2570,8 @@ function scaffoldProject({ outDir, config, slug, scope, perTxnMax, currency = 'U
|
|
|
2479
2570
|
: exampleIndexNoGateway(scope, perTxnMax, currency, paymentMerchant, !!sandbox),
|
|
2480
2571
|
force,
|
|
2481
2572
|
);
|
|
2482
|
-
|
|
2573
|
+
// Resumable = the index carries the resume path (resumeBlock): every agent but the shared sandbox, where nobody can approve.
|
|
2574
|
+
writeFileSafe(outDir, 'package.json', examplePackageJson(slug, neutral, !sandbox), force);
|
|
2483
2575
|
writeFileSafe(outDir, '.gitignore', gitignore(), force);
|
|
2484
2576
|
writeFileSafe(
|
|
2485
2577
|
outDir,
|
|
@@ -2792,7 +2884,7 @@ const guard = createMcpGuard({
|
|
|
2792
2884
|
return { subject: AGENT_DID, ownerPrincipal: HARNESS_OWNER, ...bundleFromRules(rules) };
|
|
2793
2885
|
},
|
|
2794
2886
|
});
|
|
2795
|
-
// A guard below 0.20.0 ignores allowedAgents and serves every agent: refuse to start on one (a stale lockfile, say).
|
|
2887
|
+
// A guard below 0.22.0 cannot enforce both pins (below 0.20.0 it ignores allowedAgents and serves every agent; 0.22.0 added gatewayOwnerPrincipal): refuse to start on one (a stale lockfile, say).
|
|
2796
2888
|
if (!Array.isArray(guard.allowedAgents) || !guard.gatewayOwnerPrincipal) throw new Error('this gateway needs @metamynd/agentsafe-mcp-guard >= 0.22.0 to enforce allowedAgents and gatewayOwnerPrincipal - run npm install');
|
|
2797
2889
|
|
|
2798
2890
|
// One protected route per gated action in index.mjs. This gateway IS the tool, not a proxy in
|
|
@@ -4281,8 +4373,13 @@ async function main() {
|
|
|
4281
4373
|
console.log(` ${c.green('✓')} gateway DID ${c.b(gatewayDid)} — ${reg.message}`);
|
|
4282
4374
|
} else {
|
|
4283
4375
|
console.log(` ${c.yellow('!')} could not register the gateway as a trusted counterparty: ${reg.message}`);
|
|
4284
|
-
console.log(c.dim(
|
|
4285
|
-
|
|
4376
|
+
console.log(c.dim(financial
|
|
4377
|
+
? ` it still works on testnet (open by default with no registry entries); a MAINNET hold cannot be`
|
|
4378
|
+
: ` it still enforces; only its refusal reports cannot reach your Activity Log until it is`));
|
|
4379
|
+
console.log(c.dim(financial
|
|
4380
|
+
? ` claimed until it is registered — gateway/README.md ("Who may claim this agent's holds") shows how, with the`
|
|
4381
|
+
: ` registered — at /dashboard/counterparties, with the`));
|
|
4382
|
+
console.log(c.dim(` proof-of-control step registration requires, for ${gatewayDid}`));
|
|
4286
4383
|
}
|
|
4287
4384
|
}
|
|
4288
4385
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "create-metamynd-agent",
|
|
3
|
-
"version": "0.14.
|
|
3
|
+
"version": "0.14.22",
|
|
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": {
|