@haven_ai/cli 0.8.0-alpha.0 → 0.9.0-alpha.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/cli.js CHANGED
@@ -1,8 +1,9 @@
1
1
  #!/usr/bin/env node
2
- import { rm, mkdir, writeFile, chmod, readFile } from 'fs/promises';
2
+ import { rm, mkdir, writeFile, chmod, readFile, readdir } from 'fs/promises';
3
3
  import { homedir } from 'os';
4
4
  import { join, resolve } from 'path';
5
5
  import { spawn } from 'child_process';
6
+ import { createECDH } from 'crypto';
6
7
  import { emitKeypressEvents } from 'readline';
7
8
 
8
9
  // src/args.ts
@@ -38,7 +39,8 @@ function parseArgs(argv) {
38
39
  noWait: false,
39
40
  run: false,
40
41
  wait: false,
41
- hashes: false
42
+ hashes: false,
43
+ keepBudget: false
42
44
  };
43
45
  for (let i = 0; i < argv.length; i += 1) {
44
46
  const arg = argv[i];
@@ -46,6 +48,7 @@ function parseArgs(argv) {
46
48
  else if (arg === "--help" || arg === "-h") flags.help = true;
47
49
  else if (arg === "--version" || arg === "-v") flags.version = true;
48
50
  else if (arg === "--yes" || arg === "-y") flags.yes = true;
51
+ else if (arg === "--keep-budget") flags.keepBudget = true;
49
52
  else if (arg === "--no-wait") flags.noWait = true;
50
53
  else if (arg === "--run") flags.run = true;
51
54
  else if (arg === "--wait") flags.wait = true;
@@ -147,10 +150,19 @@ function helpText() {
147
150
  " catalog list List payable services",
148
151
  " contacts list List your address book",
149
152
  "",
153
+ "Feedback:",
154
+ ' feedback submit "<text>" Send feedback or a bug report. Needs',
155
+ " `haven login`. It refuses to send text that",
156
+ " looks like a credential, but cannot catch",
157
+ " every secret shape \u2014 never include one.",
158
+ "",
150
159
  "Manage (backend-only \u2014 no on-chain signing):",
151
- " agents pause <id> Stop the agent spending; keeps its budget",
152
- " agents resume <id> Let it spend again",
153
- " agents revoke <id> --yes Permanently revoke an agent",
160
+ " agents pause <id> Block payments through Haven; the budget",
161
+ " stays live on-chain (end it: budget revoke)",
162
+ " agents resume <id> Allow payments through Haven again",
163
+ " agents revoke <id> --yes Retire the agent in Haven (terminal). Ends no",
164
+ " budget on-chain: run budget revoke first; refuses",
165
+ " while a budget is still live unless --keep-budget",
154
166
  " agents rotate-key <id> Issue a new API key (shown once)",
155
167
  " agents rename <id> <name>",
156
168
  " wallets rename <id> <name>",
@@ -367,11 +379,20 @@ var UsageError = class extends Error {
367
379
  var HavenCliError = class extends Error {
368
380
  exit;
369
381
  hint;
370
- constructor(message, exit, hint) {
382
+ /**
383
+ * #3597: additional typed fields merged into the `--json` error object
384
+ * (`error.layer`, `error.reason`, …) — for a refusal where the FREE-TEXT
385
+ * hint is not enough to branch on. `feedback submit`'s secret check is the
386
+ * first caller: an agent needs `error.layer` (which of the four checks
387
+ * refused) as a number, not a string it has to parse out of a sentence.
388
+ */
389
+ extra;
390
+ constructor(message, exit, hint, extra) {
371
391
  super(message);
372
392
  this.name = "HavenCliError";
373
393
  this.exit = exit;
374
394
  this.hint = hint;
395
+ this.extra = extra;
375
396
  }
376
397
  };
377
398
  function toFailure(err) {
@@ -380,7 +401,7 @@ function toFailure(err) {
380
401
  }
381
402
  if (err instanceof HavenCliError) {
382
403
  const code = err.exit === EXIT.notAuthenticated ? "not_authenticated" : err.exit === EXIT.refused ? "refused" : err.exit === EXIT.network ? "network" : "failed";
383
- return { code, exit: err.exit, message: err.message, hint: err.hint };
404
+ return { code, exit: err.exit, message: err.message, hint: err.hint, extra: err.extra };
384
405
  }
385
406
  if (err instanceof CliApiError) {
386
407
  if (err.status === 0) {
@@ -430,6 +451,11 @@ function createOutput(json, out, err) {
430
451
  const body = {
431
452
  ok: false,
432
453
  error: {
454
+ // `extra` spreads FIRST (#3597 round 2, N-a): it is per-command
455
+ // (e.g. the secret check's `layer`/`reason`), so it must never be
456
+ // able to clobber the contract fields every command shares —
457
+ // `code`, `message`, `hint` are spread AFTER and always win.
458
+ ...failure.extra ?? {},
433
459
  code: failure.code,
434
460
  message: failure.message,
435
461
  ...failure.hint ? { hint: failure.hint } : {}
@@ -443,7 +469,7 @@ ${failure.hint}` : failure.message);
443
469
  }
444
470
 
445
471
  // src/agent-guidance-text.ts
446
- var HAVEN_AGENT_RUNBOOK_MD = "# Haven for agents \u2014 set your user up to let you pay\n\nYou are an AI agent whose user has no Haven account yet, or has one but has not connected you.\n\n> Links below are paths on this same host: resolve them against the URL you fetched this file from.\n\n## What Haven is, in three sentences\n\nYour user gives you a **budget on their own account** \u2014 not their wallet, and not a key to their funds. The budget is a delegation they sign, enforced on-chain: a payment over it, to the wrong recipient, or past its expiry is refused at execution time, not by a dashboard promise. Haven constructs and relays the payments; you get an agent credential and a signing key made on your machine, and your user can revoke the budget without you and without Haven.\n\n## The sequence\n\nFour of the six steps are your user's \u2014 each needs a human signature or a human decision. The rest is yours. If they already have a funded account, start at step 3.\n\n1. **HUMAN \u2014 create the account.** Name, email, password, then a passkey (Face ID / Touch ID) or a wallet. Never offer to enter any of it: you must not have their password, and the passkey is bound to their device. With a terminal, `<packages.cli.one_liner> login --api <api-url>` afterwards gets you a scoped session for steps 3-4 (that flag or `HAVEN_API_URL`, on the first command \u2014 the session then remembers the backend; **the CLI's built-in default is Haven's hosted production backend**, so on any other deployment an omitted flag connects you somewhere real and wrong rather than failing) \u2014 they approve a code in the browser, you never hold their password. That command is the manifest's `packages.cli.one_liner` (`/.well-known/haven.json`) \u2014 run it as given: `packages.cli.channel` serves the full spec. Do not hold the process open while you wait: under `--json`, pass `--no-wait` to get the link object back at once, then poll it with `haven login --poll <device_code>` \u2014 one round per invocation, exit 3 while it is still pending, 0 once approved. It can set up agents and read the account; it cannot sign, approve a budget, move funds, or rotate any agent's keys.\n2. **HUMAN \u2014 fund it.** USDC only, no ETH: Haven sponsors the gas. `/.well-known/haven.json` names `chains.default` as the deployment's expected chain, but after login `haven wallets funding` prints the address, the amount and which chain in one place; confirm that chain before you message your user. Without a CLI session, the dashboard's funding card shows the address and amount and its Receive-funds screen names the chain; never assume one: a testnet deployment and production both call themselves Haven. Before you write that message, read the manifest: `environment` says whether this deployment is `production`, and each `chains.supported` entry says whether that chain is a `testnet`. Real money is at stake only on a non-testnet chain of a `production` deployment \u2014 tell your user which case theirs is.\n3. **HUMAN \u2014 create the agent, set its budget**, and paste you the **setup prompt** it hands back. With a CLI session (step 1) you can do this step yourself: `haven agents connect --name <n> --budget <amount> --token USDC --period <minutes>` prints the same connector command and approval link; add `--run` to do step 4 too.\n4. **YOU \u2014 run the connector command** in that prompt (see What you run). It makes your signing key locally, registering only the public address.\n5. **HUMAN \u2014 approve the budget** with their passkey, in the Haven tab they created the agent in: it advances to the approval step by itself once your run registers.\n6. **YOU \u2014 verify, then pay.**\n\n## Budget changes later (second token, raise, revoke)\n\nThe first budget rides the setup in The sequence. After the agent exists, a CLI session can construct the LATER changes \u2014 a second token, a bigger amount, a recipient pin, a stop \u2014 and hand your user a link; the human still signs, every time:\n\n```\nhaven budget grant <agentId> --amount <n> --token USDC --period <minutes> [--recipient <address>] [--wait]\nhaven budget revoke <agentId> <delegationHash> [--wait]\n```\n\nThe CLI never signs: it prints a dashboard link, your user opens it and signs with their passkey or wallet. `--wait` polls until the human's signature lands. The hash for `revoke` is in `haven agents show <agentId>`.\n\n## Hand-off scripts\n\nSend these as your own message, `<host>` replaced by the host you fetched this file from. Say what you cannot do, not only what they must.\n\n**Before signup** (step 1):\n\n> I can do everything except the parts that need your signature. Please open `<host>/signup?next=/agents&via=agent` \u2014 name, email, password, then a passkey (Face ID / Touch ID) or a wallet. That is your account's key: I should not have it, and I will never ask for your password. Already have one? `<host>/login?next=/agents`.\n\n**At the passkey step:**\n\n> The passkey must be made on your own device \u2014 it is what keeps the account yours, so nobody, Haven included, can move your funds without you. If this browser cannot, open `<host>/onboarding?next=/agents` on your phone and finish there.\n\n**At funding** (step 2):\n\n> Your Haven account needs USDC before I can pay for anything \u2014 USDC only, no ETH: Haven sponsors the gas. Before you send anything, let me get you the exact address **and network** \u2014 `haven wallets funding` prints both, and the funding card on `<host>/dashboard` shows the same. Please do not send to an address or a chain I have not confirmed with you; a small amount first is fine.\n\n**At the budget** (step 3):\n\n> On `<host>/agents`, create an agent for me and set a budget \u2014 say 25 USDC per day. That is the limit I cannot exceed. It hands back a setup prompt: paste it to me and I run it here.\n\n**At budget approval** (step 5) \u2014 the moment your run reports that approval is required, before anything else. Send the first if your run carried `approval.url`, the second if it did not.\n\n> Setup is done on my side. Approve the budget here: <approval.url>. Approve it with your passkey; nothing can be spent until you do.\n\n> Setup is done on my side. Go back to the Haven tab where you created the agent \u2014 it should now be asking you to approve the budget. Approve it with your passkey; nothing can be spent until you do.\n\n## What you run\n\nThe setup prompt carries a **connector command** shaped like this:\n\n```\nnpx -y @haven_ai/connect@<channel> --setup EXAMPLE-SETUP-TOKEN-NOT-REAL --api <api-url> --ack-local-tools\n```\n\nRun the prompt's version, not this one: the token is one-time, the API URL is the backend's own, and `<channel>` is the npm tag your prompt names \u2014 never a tag you pick. The rules below come from that prompt, so \"me\" in them is your user, not Haven.\n\n- If you are an AI agent running this command yourself rather than a human pasting it, you should append --json: the connector then emits one machine-readable, secret-free result object on stdout with progress on stderr, and returns promptly instead of blocking while it waits for the budget approval.\n- When a --json outcome reports approval.required: true, your first action must be to relay the approval instruction to me in your own reply \u2014 if the outcome carries approval.url, give me that link; otherwise tell me to return to Haven and approve this agent's budget \u2014 before verifying the connection, restarting anything, or any other step. Never build that link yourself: relay the one the outcome gave you or none at all. Any restart the outcome asks for is a separate instruction to give me afterwards, once the approval is done.\n- If you ran the command without --json, the connector waits for the approval itself and prints its next steps when it finishes: relay the budget-approval instruction to me \u2014 the approval link if those steps printed one, otherwise that you need to return to Haven and approve this agent's budget \u2014 only if those printed next steps still ask for it. If they report the budget as already approved, there is nothing for me to approve.\n- Only two changes to the command above are permitted, and no others: appending --json, and \u2014 only if the connector refuses because it could not determine the agent runtime \u2014 re-running it once with --runtime <name> added, naming the harness you are running in, using one of the values that refusal lists. Never invent a runtime name and never change anything else.\n- Do not print private keys, API keys, credential file contents, or config secrets in chat or logs.\n\nIf the approval wait times out, nothing is lost \u2014 your agent is registered and the budget is still waiting to be approved. Send your user the `approval.url` your run reported, or, if it carried none, ask them to finish it in that same Haven tab. The outcome carries no setup id, so never assemble an approval link out of parts \u2014 relay the whole one it gave you or none at all.\n\n## How to verify\n\nCall `haven_get_agent`, one of the Haven MCP tools the connector wires into your runtime in step 4. It returns identity plus `spend_authority_readiness`:\n\n- `ready` \u2014 a budget is live; you can pay.\n- `needs_approval` \u2014 the connector finished, nobody approved yet. Ask your user again, in their Haven tab; there is no queue to wait in.\n- `revoked` \u2014 the credential is not active; ask your user to create a new agent.\n\n`ready` covers hosted identity and the budget only, not your local signer. Check that with `npx -y @haven_ai/connect@<channel> --doctor`, the same tag your prompt named \u2014 a separate command, so the two-changes rule does not bind it.\n\n## If you cannot open a browser\n\nNothing here needs you to. Steps 1-3 \u2014 account, funding, budget \u2014 are links: hand your user the full `<host>/\u2026` URL and ask them to say when it is done. Step 5 is a link only when your run reported one in `approval.url` \u2014 otherwise it is the tab they already have open. Then poll `haven_get_agent` until it reads `ready`. Do not route around the sign-in wall \u2014 it makes the account theirs, not yours.\n\n## If something breaks\n\nIf a Haven result carries `client_update`, that client is out of date: run its `upgrade_command` as given, then any repair line it prints, then retry. `required: true` means payments are refused until you do. What changed: [/releases](/releases).\n\n## Vocabulary\n\n| Term | What it is |\n|---|---|\n| **setup prompt** | The text the dashboard hands your user to paste to you. Carries the command and its rules. |\n| **connector command** | The `npx -y @haven_ai/connect@\u2026` line you run. One-time token, one use. |\n| **agent credential** | Your API key (`sk_agent_\u2026`), written to `~/.haven`. It identifies you; alone it cannot move money. |\n| **delegate key** | Your signing key, made on this machine and never sent anywhere. |\n| **budget** | The on-chain delegation your user signed. It authorises the payment; Haven constructs and relays it. |\n\nNext: [your agent hit a 402](/402.md) \xB7 [everything agent-readable](/llms.txt)\n";
472
+ var HAVEN_AGENT_RUNBOOK_MD = "# Haven for agents \u2014 set your user up to let you pay\n\nYou are an AI agent whose user has no Haven account yet, or has one but has not connected you.\n\n> Links below are paths on this same host: resolve them against the URL you fetched this file from.\n\n## What Haven is, in three sentences\n\nYour user gives you a **budget on their own account** \u2014 not their wallet, and not a key to their funds. The budget is a delegation they sign, enforced on-chain: a payment over it, to the wrong recipient, or past its expiry is refused at execution time, not by a dashboard promise. Haven constructs and relays the payments; you get an agent credential and a signing key made on your machine, and your user can revoke the budget without you and without Haven.\n\n## The sequence\n\nFour of the six steps are your user's \u2014 each needs a human signature or a human decision. The rest is yours. If they already have a funded account, start at step 3.\n\n1. **HUMAN \u2014 create the account.** Name, email, password, then a passkey (Face ID / Touch ID) or a wallet. Never offer to enter any of it: you must not have their password, and the passkey is bound to their device. With a terminal, `<packages.cli.one_liner> login --api <api-url>` afterwards gets you a scoped session for steps 3-4 (that flag or `HAVEN_API_URL`, on the first command \u2014 the session then remembers the backend; **the CLI's built-in default is Haven's hosted production backend**, so on any other deployment an omitted flag connects you somewhere real and wrong rather than failing) \u2014 they approve a code in the browser, you never hold their password. That command is the manifest's `packages.cli.one_liner` (`/.well-known/haven.json`) \u2014 run it as given: `packages.cli.channel` serves the full spec. Do not hold the process open while you wait: under `--json`, pass `--no-wait` to get the link object back at once, then poll it with `haven login --poll <device_code>` \u2014 one round per invocation, exit 3 while it is still pending, 0 once approved. It can set up agents and read the account; it cannot sign, approve a budget, move funds, or rotate any agent's keys.\n2. **HUMAN \u2014 fund it.** USDC only, no ETH: Haven sponsors the gas. `/.well-known/haven.json` names `chains.default` as the deployment's expected chain, but after login `haven wallets funding` prints the address, the amount and which chain in one place; confirm that chain before you message your user. Without a CLI session, the dashboard's funding card shows the address and amount and its Receive-funds screen names the chain; never assume one: a testnet deployment and production both call themselves Haven. Before you write that message, read the manifest: `environment` says whether this deployment is `production`, and each `chains.supported` entry says whether that chain is a `testnet`. Real money is at stake only on a non-testnet chain of a `production` deployment \u2014 tell your user which case theirs is.\n3. **HUMAN \u2014 create the agent, set its budget**, and paste you the **setup prompt** it hands back. With a CLI session (step 1) you can do this step yourself: `haven agents connect --name <n> --budget <amount> --token USDC --period <minutes>` prints the same connector command and approval link; add `--run` to do step 4 too.\n4. **YOU \u2014 run the connector command** in that prompt (see What you run). It makes your signing key locally, registering only the public address.\n5. **HUMAN \u2014 approve the budget** with their passkey, in the Haven tab they created the agent in: it advances to the approval step by itself once your run registers.\n6. **YOU \u2014 verify, then pay.**\n\n## Budget changes later (second token, raise, revoke)\n\nThe first budget rides the setup in The sequence. After the agent exists, a CLI session can construct the LATER changes \u2014 a second token, a bigger amount, a recipient pin, a stop \u2014 and hand your user a link; the human still signs, every time:\n\n```\nhaven budget grant <agentId> --amount <n> --token USDC --period <minutes> [--recipient <address>] [--wait]\nhaven budget revoke <agentId> <delegationHash> [--wait]\n```\n\nThe CLI never signs: it prints a dashboard link, your user opens it and signs with their passkey or wallet. `--wait` polls until the human's signature lands. The hash for `revoke` is in `haven agents show <agentId>`.\n\n## Hand-off scripts\n\nSend these as your own message, `<host>` replaced by the host you fetched this file from. Say what you cannot do, not only what they must.\n\n**Before signup** (step 1):\n\n> I can do everything except the parts that need your signature. Please open `<host>/signup?next=/agents&via=agent` \u2014 name, email, password, then a passkey (Face ID / Touch ID) or a wallet. That is your account's key: I should not have it, and I will never ask for your password. Already have one? `<host>/login?next=/agents`.\n\n**At the passkey step:**\n\n> The passkey must be made on your own device \u2014 it is what keeps the account yours, so nobody, Haven included, can move your funds without you. If this browser cannot, open `<host>/onboarding?next=/agents` on your phone and finish there.\n\n**At funding** (step 2):\n\n> Your Haven account needs USDC before I can pay for anything \u2014 USDC only, no ETH: Haven sponsors the gas. Before you send anything, let me get you the exact address **and network** \u2014 `haven wallets funding` prints both, and the funding card on `<host>/dashboard` shows the same. Please do not send to an address or a chain I have not confirmed with you; a small amount first is fine.\n\n**At the budget** (step 3):\n\n> On `<host>/agents`, create an agent for me and set a budget \u2014 say 25 USDC per day. That is the limit I cannot exceed. It hands back a setup prompt: paste it to me and I run it here.\n\n**At budget approval** (step 5) \u2014 the moment your run reports that approval is required, before anything else. Send the first if your run carried `approval.url`, the second if it did not.\n\n> Setup is done on my side. Approve the budget here: <approval.url>. Approve it with your passkey; nothing can be spent until you do.\n\n> Setup is done on my side. Go back to the Haven tab where you created the agent \u2014 it should now be asking you to approve the budget. Approve it with your passkey; nothing can be spent until you do.\n\n## What you run\n\nThe setup prompt carries a **connector command** shaped like this:\n\n```\nnpx -y @haven_ai/connect@<channel> --setup EXAMPLE-SETUP-TOKEN-NOT-REAL --api <api-url> --ack-local-tools\n```\n\nRun the prompt's version, not this one: the token is one-time, the API URL is the backend's own, and `<channel>` is the npm tag your prompt names \u2014 never a tag you pick. The rules below come from that prompt, so \"me\" in them is your user, not Haven.\n\n- If you are an AI agent running this command yourself rather than a human pasting it, you should append --json: the connector then emits one machine-readable, secret-free result object on stdout with progress on stderr, and returns promptly instead of blocking while it waits for the budget approval.\n- When a --json outcome reports approval.required: true, your first action must be to relay the approval instruction to me in your own reply \u2014 if the outcome carries approval.url, give me that link; otherwise tell me to return to Haven and approve this agent's budget \u2014 before verifying the connection, restarting anything, or any other step. Never build that link yourself: relay the one the outcome gave you or none at all. Any restart the outcome asks for is a separate instruction to give me afterwards, once the approval is done.\n- If you ran the command without --json, the connector waits for the approval itself and prints its next steps when it finishes: relay the budget-approval instruction to me \u2014 the approval link if those steps printed one, otherwise that you need to return to Haven and approve this agent's budget \u2014 only if those printed next steps still ask for it. If they report the budget as already approved, there is nothing for me to approve.\n- Only three changes to the command above are permitted, and no others: appending --json; re-running it once with --runtime <name> added, only if the connector refuses because it could not determine the agent runtime, naming the harness you are running in and using one of the values that refusal lists; and re-running it once with the one flag I chose added, only after I have answered a wiring_collision relay \u2014 --name <the suggested_name or a name I gave> to add alongside, or --replace to replace \u2014 keeping any change already made. If that re-run is refused too, relay the refusal to me and stop. Never invent a runtime name and never change anything else.\n- Do not print private keys, API keys, credential file contents, or config secrets in chat or logs.\n\nIf the approval wait times out, nothing is lost \u2014 your agent is registered and the budget is still waiting to be approved. Send your user the `approval.url` your run reported, or, if it carried none, ask them to finish it in that same Haven tab. The outcome carries no setup id, so never assemble an approval link out of parts \u2014 relay the whole one it gave you or none at all.\n\n## How to verify\n\nCall `haven_get_agent`, one of the Haven MCP tools the connector wires into your runtime in step 4. It returns identity plus `spend_authority_readiness`:\n\n- `ready` \u2014 a budget is live: you have the authority to pay. It does not say the account holds funds \u2014 the `allowances[]` rows carry `funds_cover_remaining`, and `false` there is a heads-up to mention to your user, not a refusal.\n- `needs_approval` \u2014 the connector finished, nobody approved yet. Ask your user again, in their Haven tab; there is no queue to wait in.\n- `revoked` \u2014 the credential is not active; ask your user to create a new agent.\n\n`ready` covers hosted identity and the budget only, not your local signer. Check that with `npx -y @haven_ai/connect@<channel> --doctor`, the same tag your prompt named \u2014 a separate command, so the command-modification rule does not bind it.\n\n## If you cannot open a browser\n\nNothing here needs you to. Steps 1-3 \u2014 account, funding, budget \u2014 are links: hand your user the full `<host>/\u2026` URL and ask them to say when it is done. Step 5 is a link only when your run reported one in `approval.url` \u2014 otherwise it is the tab they already have open. Then poll `haven_get_agent` until it reads `ready`. Do not route around the sign-in wall \u2014 it makes the account theirs, not yours.\n\n## If something breaks\n\nIf a Haven result carries `client_update`, that client is out of date: run its `upgrade_command` as given, then any repair line it prints, then retry. `required: true` means payments are refused until you do. What changed: [/releases](/releases).\n\nSomething else wrong? `haven feedback submit \"<text>\"` sends it to Haven \u2014 it needs `haven login` first. Never put a credential in that text: no API key, no delegate key, no session token, no recovery phrase.\n\n## Vocabulary\n\n| Term | What it is |\n|---|---|\n| **setup prompt** | The text the dashboard hands your user to paste to you. Carries the command and its rules. |\n| **connector command** | The `npx -y @haven_ai/connect@\u2026` line you run. One-time token, one use. |\n| **agent credential** | Your API key (`sk_agent_\u2026`), written to `~/.haven`. It identifies you; alone it cannot move money. |\n| **delegate key** | Your signing key, made on this machine and never sent anywhere. |\n| **budget** | The on-chain delegation your user signed. It authorises the payment; Haven constructs and relays it. |\n\nNext: [your agent hit a 402](/402.md) \xB7 [everything agent-readable](/llms.txt)\n";
447
473
 
448
474
  // src/token.ts
449
475
  function sessionExpiry(token) {
@@ -616,9 +642,2515 @@ async function runConnector(connectorCommand, spawner, onStderr) {
616
642
  return { outcome, exitCode, stderr, stdoutNoise: noise };
617
643
  }
618
644
 
645
+ // ../../node_modules/@noble/hashes/esm/_u64.js
646
+ var U32_MASK64 = /* @__PURE__ */ BigInt(2 ** 32 - 1);
647
+ var _32n = /* @__PURE__ */ BigInt(32);
648
+ function fromBig(n, le = false) {
649
+ if (le)
650
+ return { h: Number(n & U32_MASK64), l: Number(n >> _32n & U32_MASK64) };
651
+ return { h: Number(n >> _32n & U32_MASK64) | 0, l: Number(n & U32_MASK64) | 0 };
652
+ }
653
+ function split(lst, le = false) {
654
+ const len = lst.length;
655
+ let Ah = new Uint32Array(len);
656
+ let Al = new Uint32Array(len);
657
+ for (let i = 0; i < len; i++) {
658
+ const { h, l } = fromBig(lst[i], le);
659
+ [Ah[i], Al[i]] = [h, l];
660
+ }
661
+ return [Ah, Al];
662
+ }
663
+ var rotlSH = (h, l, s) => h << s | l >>> 32 - s;
664
+ var rotlSL = (h, l, s) => l << s | h >>> 32 - s;
665
+ var rotlBH = (h, l, s) => l << s - 32 | h >>> 64 - s;
666
+ var rotlBL = (h, l, s) => h << s - 32 | l >>> 64 - s;
667
+
668
+ // ../../node_modules/@noble/hashes/esm/utils.js
669
+ function isBytes(a) {
670
+ return a instanceof Uint8Array || ArrayBuffer.isView(a) && a.constructor.name === "Uint8Array";
671
+ }
672
+ function anumber(n) {
673
+ if (!Number.isSafeInteger(n) || n < 0)
674
+ throw new Error("positive integer expected, got " + n);
675
+ }
676
+ function abytes(b, ...lengths) {
677
+ if (!isBytes(b))
678
+ throw new Error("Uint8Array expected");
679
+ if (lengths.length > 0 && !lengths.includes(b.length))
680
+ throw new Error("Uint8Array expected of length " + lengths + ", got length=" + b.length);
681
+ }
682
+ function aexists(instance, checkFinished = true) {
683
+ if (instance.destroyed)
684
+ throw new Error("Hash instance has been destroyed");
685
+ if (checkFinished && instance.finished)
686
+ throw new Error("Hash#digest() has already been called");
687
+ }
688
+ function aoutput(out, instance) {
689
+ abytes(out);
690
+ const min = instance.outputLen;
691
+ if (out.length < min) {
692
+ throw new Error("digestInto() expects output buffer of length at least " + min);
693
+ }
694
+ }
695
+ function u32(arr) {
696
+ return new Uint32Array(arr.buffer, arr.byteOffset, Math.floor(arr.byteLength / 4));
697
+ }
698
+ function clean(...arrays) {
699
+ for (let i = 0; i < arrays.length; i++) {
700
+ arrays[i].fill(0);
701
+ }
702
+ }
703
+ var isLE = /* @__PURE__ */ (() => new Uint8Array(new Uint32Array([287454020]).buffer)[0] === 68)();
704
+ function byteSwap(word) {
705
+ return word << 24 & 4278190080 | word << 8 & 16711680 | word >>> 8 & 65280 | word >>> 24 & 255;
706
+ }
707
+ function byteSwap32(arr) {
708
+ for (let i = 0; i < arr.length; i++) {
709
+ arr[i] = byteSwap(arr[i]);
710
+ }
711
+ return arr;
712
+ }
713
+ var swap32IfBE = isLE ? (u) => u : byteSwap32;
714
+ function utf8ToBytes(str) {
715
+ if (typeof str !== "string")
716
+ throw new Error("string expected");
717
+ return new Uint8Array(new TextEncoder().encode(str));
718
+ }
719
+ function toBytes(data) {
720
+ if (typeof data === "string")
721
+ data = utf8ToBytes(data);
722
+ abytes(data);
723
+ return data;
724
+ }
725
+ var Hash = class {
726
+ };
727
+ function createHasher(hashCons) {
728
+ const hashC = (msg) => hashCons().update(toBytes(msg)).digest();
729
+ const tmp = hashCons();
730
+ hashC.outputLen = tmp.outputLen;
731
+ hashC.blockLen = tmp.blockLen;
732
+ hashC.create = () => hashCons();
733
+ return hashC;
734
+ }
735
+
736
+ // ../../node_modules/@noble/hashes/esm/sha3.js
737
+ var _0n = BigInt(0);
738
+ var _1n = BigInt(1);
739
+ var _2n = BigInt(2);
740
+ var _7n = BigInt(7);
741
+ var _256n = BigInt(256);
742
+ var _0x71n = BigInt(113);
743
+ var SHA3_PI = [];
744
+ var SHA3_ROTL = [];
745
+ var _SHA3_IOTA = [];
746
+ for (let round = 0, R = _1n, x = 1, y = 0; round < 24; round++) {
747
+ [x, y] = [y, (2 * x + 3 * y) % 5];
748
+ SHA3_PI.push(2 * (5 * y + x));
749
+ SHA3_ROTL.push((round + 1) * (round + 2) / 2 % 64);
750
+ let t = _0n;
751
+ for (let j = 0; j < 7; j++) {
752
+ R = (R << _1n ^ (R >> _7n) * _0x71n) % _256n;
753
+ if (R & _2n)
754
+ t ^= _1n << (_1n << /* @__PURE__ */ BigInt(j)) - _1n;
755
+ }
756
+ _SHA3_IOTA.push(t);
757
+ }
758
+ var IOTAS = split(_SHA3_IOTA, true);
759
+ var SHA3_IOTA_H = IOTAS[0];
760
+ var SHA3_IOTA_L = IOTAS[1];
761
+ var rotlH = (h, l, s) => s > 32 ? rotlBH(h, l, s) : rotlSH(h, l, s);
762
+ var rotlL = (h, l, s) => s > 32 ? rotlBL(h, l, s) : rotlSL(h, l, s);
763
+ function keccakP(s, rounds = 24) {
764
+ const B = new Uint32Array(5 * 2);
765
+ for (let round = 24 - rounds; round < 24; round++) {
766
+ for (let x = 0; x < 10; x++)
767
+ B[x] = s[x] ^ s[x + 10] ^ s[x + 20] ^ s[x + 30] ^ s[x + 40];
768
+ for (let x = 0; x < 10; x += 2) {
769
+ const idx1 = (x + 8) % 10;
770
+ const idx0 = (x + 2) % 10;
771
+ const B0 = B[idx0];
772
+ const B1 = B[idx0 + 1];
773
+ const Th = rotlH(B0, B1, 1) ^ B[idx1];
774
+ const Tl = rotlL(B0, B1, 1) ^ B[idx1 + 1];
775
+ for (let y = 0; y < 50; y += 10) {
776
+ s[x + y] ^= Th;
777
+ s[x + y + 1] ^= Tl;
778
+ }
779
+ }
780
+ let curH = s[2];
781
+ let curL = s[3];
782
+ for (let t = 0; t < 24; t++) {
783
+ const shift = SHA3_ROTL[t];
784
+ const Th = rotlH(curH, curL, shift);
785
+ const Tl = rotlL(curH, curL, shift);
786
+ const PI = SHA3_PI[t];
787
+ curH = s[PI];
788
+ curL = s[PI + 1];
789
+ s[PI] = Th;
790
+ s[PI + 1] = Tl;
791
+ }
792
+ for (let y = 0; y < 50; y += 10) {
793
+ for (let x = 0; x < 10; x++)
794
+ B[x] = s[y + x];
795
+ for (let x = 0; x < 10; x++)
796
+ s[y + x] ^= ~B[(x + 2) % 10] & B[(x + 4) % 10];
797
+ }
798
+ s[0] ^= SHA3_IOTA_H[round];
799
+ s[1] ^= SHA3_IOTA_L[round];
800
+ }
801
+ clean(B);
802
+ }
803
+ var Keccak = class _Keccak extends Hash {
804
+ // NOTE: we accept arguments in bytes instead of bits here.
805
+ constructor(blockLen, suffix, outputLen, enableXOF = false, rounds = 24) {
806
+ super();
807
+ this.pos = 0;
808
+ this.posOut = 0;
809
+ this.finished = false;
810
+ this.destroyed = false;
811
+ this.enableXOF = false;
812
+ this.blockLen = blockLen;
813
+ this.suffix = suffix;
814
+ this.outputLen = outputLen;
815
+ this.enableXOF = enableXOF;
816
+ this.rounds = rounds;
817
+ anumber(outputLen);
818
+ if (!(0 < blockLen && blockLen < 200))
819
+ throw new Error("only keccak-f1600 function is supported");
820
+ this.state = new Uint8Array(200);
821
+ this.state32 = u32(this.state);
822
+ }
823
+ clone() {
824
+ return this._cloneInto();
825
+ }
826
+ keccak() {
827
+ swap32IfBE(this.state32);
828
+ keccakP(this.state32, this.rounds);
829
+ swap32IfBE(this.state32);
830
+ this.posOut = 0;
831
+ this.pos = 0;
832
+ }
833
+ update(data) {
834
+ aexists(this);
835
+ data = toBytes(data);
836
+ abytes(data);
837
+ const { blockLen, state } = this;
838
+ const len = data.length;
839
+ for (let pos = 0; pos < len; ) {
840
+ const take = Math.min(blockLen - this.pos, len - pos);
841
+ for (let i = 0; i < take; i++)
842
+ state[this.pos++] ^= data[pos++];
843
+ if (this.pos === blockLen)
844
+ this.keccak();
845
+ }
846
+ return this;
847
+ }
848
+ finish() {
849
+ if (this.finished)
850
+ return;
851
+ this.finished = true;
852
+ const { state, suffix, pos, blockLen } = this;
853
+ state[pos] ^= suffix;
854
+ if ((suffix & 128) !== 0 && pos === blockLen - 1)
855
+ this.keccak();
856
+ state[blockLen - 1] ^= 128;
857
+ this.keccak();
858
+ }
859
+ writeInto(out) {
860
+ aexists(this, false);
861
+ abytes(out);
862
+ this.finish();
863
+ const bufferOut = this.state;
864
+ const { blockLen } = this;
865
+ for (let pos = 0, len = out.length; pos < len; ) {
866
+ if (this.posOut >= blockLen)
867
+ this.keccak();
868
+ const take = Math.min(blockLen - this.posOut, len - pos);
869
+ out.set(bufferOut.subarray(this.posOut, this.posOut + take), pos);
870
+ this.posOut += take;
871
+ pos += take;
872
+ }
873
+ return out;
874
+ }
875
+ xofInto(out) {
876
+ if (!this.enableXOF)
877
+ throw new Error("XOF is not possible for this instance");
878
+ return this.writeInto(out);
879
+ }
880
+ xof(bytes) {
881
+ anumber(bytes);
882
+ return this.xofInto(new Uint8Array(bytes));
883
+ }
884
+ digestInto(out) {
885
+ aoutput(out, this);
886
+ if (this.finished)
887
+ throw new Error("digest() was already called");
888
+ this.writeInto(out);
889
+ this.destroy();
890
+ return out;
891
+ }
892
+ digest() {
893
+ return this.digestInto(new Uint8Array(this.outputLen));
894
+ }
895
+ destroy() {
896
+ this.destroyed = true;
897
+ clean(this.state);
898
+ }
899
+ _cloneInto(to) {
900
+ const { blockLen, suffix, outputLen, rounds, enableXOF } = this;
901
+ to || (to = new _Keccak(blockLen, suffix, outputLen, enableXOF, rounds));
902
+ to.state32.set(this.state32);
903
+ to.pos = this.pos;
904
+ to.posOut = this.posOut;
905
+ to.finished = this.finished;
906
+ to.rounds = rounds;
907
+ to.suffix = suffix;
908
+ to.outputLen = outputLen;
909
+ to.enableXOF = enableXOF;
910
+ to.destroyed = this.destroyed;
911
+ return to;
912
+ }
913
+ };
914
+ var gen = (suffix, blockLen, outputLen) => createHasher(() => new Keccak(blockLen, suffix, outputLen));
915
+ var keccak_256 = /* @__PURE__ */ (() => gen(1, 136, 256 / 8))();
916
+
917
+ // ../../node_modules/@scure/bip39/esm/wordlists/english.js
918
+ var wordlist = `abandon
919
+ ability
920
+ able
921
+ about
922
+ above
923
+ absent
924
+ absorb
925
+ abstract
926
+ absurd
927
+ abuse
928
+ access
929
+ accident
930
+ account
931
+ accuse
932
+ achieve
933
+ acid
934
+ acoustic
935
+ acquire
936
+ across
937
+ act
938
+ action
939
+ actor
940
+ actress
941
+ actual
942
+ adapt
943
+ add
944
+ addict
945
+ address
946
+ adjust
947
+ admit
948
+ adult
949
+ advance
950
+ advice
951
+ aerobic
952
+ affair
953
+ afford
954
+ afraid
955
+ again
956
+ age
957
+ agent
958
+ agree
959
+ ahead
960
+ aim
961
+ air
962
+ airport
963
+ aisle
964
+ alarm
965
+ album
966
+ alcohol
967
+ alert
968
+ alien
969
+ all
970
+ alley
971
+ allow
972
+ almost
973
+ alone
974
+ alpha
975
+ already
976
+ also
977
+ alter
978
+ always
979
+ amateur
980
+ amazing
981
+ among
982
+ amount
983
+ amused
984
+ analyst
985
+ anchor
986
+ ancient
987
+ anger
988
+ angle
989
+ angry
990
+ animal
991
+ ankle
992
+ announce
993
+ annual
994
+ another
995
+ answer
996
+ antenna
997
+ antique
998
+ anxiety
999
+ any
1000
+ apart
1001
+ apology
1002
+ appear
1003
+ apple
1004
+ approve
1005
+ april
1006
+ arch
1007
+ arctic
1008
+ area
1009
+ arena
1010
+ argue
1011
+ arm
1012
+ armed
1013
+ armor
1014
+ army
1015
+ around
1016
+ arrange
1017
+ arrest
1018
+ arrive
1019
+ arrow
1020
+ art
1021
+ artefact
1022
+ artist
1023
+ artwork
1024
+ ask
1025
+ aspect
1026
+ assault
1027
+ asset
1028
+ assist
1029
+ assume
1030
+ asthma
1031
+ athlete
1032
+ atom
1033
+ attack
1034
+ attend
1035
+ attitude
1036
+ attract
1037
+ auction
1038
+ audit
1039
+ august
1040
+ aunt
1041
+ author
1042
+ auto
1043
+ autumn
1044
+ average
1045
+ avocado
1046
+ avoid
1047
+ awake
1048
+ aware
1049
+ away
1050
+ awesome
1051
+ awful
1052
+ awkward
1053
+ axis
1054
+ baby
1055
+ bachelor
1056
+ bacon
1057
+ badge
1058
+ bag
1059
+ balance
1060
+ balcony
1061
+ ball
1062
+ bamboo
1063
+ banana
1064
+ banner
1065
+ bar
1066
+ barely
1067
+ bargain
1068
+ barrel
1069
+ base
1070
+ basic
1071
+ basket
1072
+ battle
1073
+ beach
1074
+ bean
1075
+ beauty
1076
+ because
1077
+ become
1078
+ beef
1079
+ before
1080
+ begin
1081
+ behave
1082
+ behind
1083
+ believe
1084
+ below
1085
+ belt
1086
+ bench
1087
+ benefit
1088
+ best
1089
+ betray
1090
+ better
1091
+ between
1092
+ beyond
1093
+ bicycle
1094
+ bid
1095
+ bike
1096
+ bind
1097
+ biology
1098
+ bird
1099
+ birth
1100
+ bitter
1101
+ black
1102
+ blade
1103
+ blame
1104
+ blanket
1105
+ blast
1106
+ bleak
1107
+ bless
1108
+ blind
1109
+ blood
1110
+ blossom
1111
+ blouse
1112
+ blue
1113
+ blur
1114
+ blush
1115
+ board
1116
+ boat
1117
+ body
1118
+ boil
1119
+ bomb
1120
+ bone
1121
+ bonus
1122
+ book
1123
+ boost
1124
+ border
1125
+ boring
1126
+ borrow
1127
+ boss
1128
+ bottom
1129
+ bounce
1130
+ box
1131
+ boy
1132
+ bracket
1133
+ brain
1134
+ brand
1135
+ brass
1136
+ brave
1137
+ bread
1138
+ breeze
1139
+ brick
1140
+ bridge
1141
+ brief
1142
+ bright
1143
+ bring
1144
+ brisk
1145
+ broccoli
1146
+ broken
1147
+ bronze
1148
+ broom
1149
+ brother
1150
+ brown
1151
+ brush
1152
+ bubble
1153
+ buddy
1154
+ budget
1155
+ buffalo
1156
+ build
1157
+ bulb
1158
+ bulk
1159
+ bullet
1160
+ bundle
1161
+ bunker
1162
+ burden
1163
+ burger
1164
+ burst
1165
+ bus
1166
+ business
1167
+ busy
1168
+ butter
1169
+ buyer
1170
+ buzz
1171
+ cabbage
1172
+ cabin
1173
+ cable
1174
+ cactus
1175
+ cage
1176
+ cake
1177
+ call
1178
+ calm
1179
+ camera
1180
+ camp
1181
+ can
1182
+ canal
1183
+ cancel
1184
+ candy
1185
+ cannon
1186
+ canoe
1187
+ canvas
1188
+ canyon
1189
+ capable
1190
+ capital
1191
+ captain
1192
+ car
1193
+ carbon
1194
+ card
1195
+ cargo
1196
+ carpet
1197
+ carry
1198
+ cart
1199
+ case
1200
+ cash
1201
+ casino
1202
+ castle
1203
+ casual
1204
+ cat
1205
+ catalog
1206
+ catch
1207
+ category
1208
+ cattle
1209
+ caught
1210
+ cause
1211
+ caution
1212
+ cave
1213
+ ceiling
1214
+ celery
1215
+ cement
1216
+ census
1217
+ century
1218
+ cereal
1219
+ certain
1220
+ chair
1221
+ chalk
1222
+ champion
1223
+ change
1224
+ chaos
1225
+ chapter
1226
+ charge
1227
+ chase
1228
+ chat
1229
+ cheap
1230
+ check
1231
+ cheese
1232
+ chef
1233
+ cherry
1234
+ chest
1235
+ chicken
1236
+ chief
1237
+ child
1238
+ chimney
1239
+ choice
1240
+ choose
1241
+ chronic
1242
+ chuckle
1243
+ chunk
1244
+ churn
1245
+ cigar
1246
+ cinnamon
1247
+ circle
1248
+ citizen
1249
+ city
1250
+ civil
1251
+ claim
1252
+ clap
1253
+ clarify
1254
+ claw
1255
+ clay
1256
+ clean
1257
+ clerk
1258
+ clever
1259
+ click
1260
+ client
1261
+ cliff
1262
+ climb
1263
+ clinic
1264
+ clip
1265
+ clock
1266
+ clog
1267
+ close
1268
+ cloth
1269
+ cloud
1270
+ clown
1271
+ club
1272
+ clump
1273
+ cluster
1274
+ clutch
1275
+ coach
1276
+ coast
1277
+ coconut
1278
+ code
1279
+ coffee
1280
+ coil
1281
+ coin
1282
+ collect
1283
+ color
1284
+ column
1285
+ combine
1286
+ come
1287
+ comfort
1288
+ comic
1289
+ common
1290
+ company
1291
+ concert
1292
+ conduct
1293
+ confirm
1294
+ congress
1295
+ connect
1296
+ consider
1297
+ control
1298
+ convince
1299
+ cook
1300
+ cool
1301
+ copper
1302
+ copy
1303
+ coral
1304
+ core
1305
+ corn
1306
+ correct
1307
+ cost
1308
+ cotton
1309
+ couch
1310
+ country
1311
+ couple
1312
+ course
1313
+ cousin
1314
+ cover
1315
+ coyote
1316
+ crack
1317
+ cradle
1318
+ craft
1319
+ cram
1320
+ crane
1321
+ crash
1322
+ crater
1323
+ crawl
1324
+ crazy
1325
+ cream
1326
+ credit
1327
+ creek
1328
+ crew
1329
+ cricket
1330
+ crime
1331
+ crisp
1332
+ critic
1333
+ crop
1334
+ cross
1335
+ crouch
1336
+ crowd
1337
+ crucial
1338
+ cruel
1339
+ cruise
1340
+ crumble
1341
+ crunch
1342
+ crush
1343
+ cry
1344
+ crystal
1345
+ cube
1346
+ culture
1347
+ cup
1348
+ cupboard
1349
+ curious
1350
+ current
1351
+ curtain
1352
+ curve
1353
+ cushion
1354
+ custom
1355
+ cute
1356
+ cycle
1357
+ dad
1358
+ damage
1359
+ damp
1360
+ dance
1361
+ danger
1362
+ daring
1363
+ dash
1364
+ daughter
1365
+ dawn
1366
+ day
1367
+ deal
1368
+ debate
1369
+ debris
1370
+ decade
1371
+ december
1372
+ decide
1373
+ decline
1374
+ decorate
1375
+ decrease
1376
+ deer
1377
+ defense
1378
+ define
1379
+ defy
1380
+ degree
1381
+ delay
1382
+ deliver
1383
+ demand
1384
+ demise
1385
+ denial
1386
+ dentist
1387
+ deny
1388
+ depart
1389
+ depend
1390
+ deposit
1391
+ depth
1392
+ deputy
1393
+ derive
1394
+ describe
1395
+ desert
1396
+ design
1397
+ desk
1398
+ despair
1399
+ destroy
1400
+ detail
1401
+ detect
1402
+ develop
1403
+ device
1404
+ devote
1405
+ diagram
1406
+ dial
1407
+ diamond
1408
+ diary
1409
+ dice
1410
+ diesel
1411
+ diet
1412
+ differ
1413
+ digital
1414
+ dignity
1415
+ dilemma
1416
+ dinner
1417
+ dinosaur
1418
+ direct
1419
+ dirt
1420
+ disagree
1421
+ discover
1422
+ disease
1423
+ dish
1424
+ dismiss
1425
+ disorder
1426
+ display
1427
+ distance
1428
+ divert
1429
+ divide
1430
+ divorce
1431
+ dizzy
1432
+ doctor
1433
+ document
1434
+ dog
1435
+ doll
1436
+ dolphin
1437
+ domain
1438
+ donate
1439
+ donkey
1440
+ donor
1441
+ door
1442
+ dose
1443
+ double
1444
+ dove
1445
+ draft
1446
+ dragon
1447
+ drama
1448
+ drastic
1449
+ draw
1450
+ dream
1451
+ dress
1452
+ drift
1453
+ drill
1454
+ drink
1455
+ drip
1456
+ drive
1457
+ drop
1458
+ drum
1459
+ dry
1460
+ duck
1461
+ dumb
1462
+ dune
1463
+ during
1464
+ dust
1465
+ dutch
1466
+ duty
1467
+ dwarf
1468
+ dynamic
1469
+ eager
1470
+ eagle
1471
+ early
1472
+ earn
1473
+ earth
1474
+ easily
1475
+ east
1476
+ easy
1477
+ echo
1478
+ ecology
1479
+ economy
1480
+ edge
1481
+ edit
1482
+ educate
1483
+ effort
1484
+ egg
1485
+ eight
1486
+ either
1487
+ elbow
1488
+ elder
1489
+ electric
1490
+ elegant
1491
+ element
1492
+ elephant
1493
+ elevator
1494
+ elite
1495
+ else
1496
+ embark
1497
+ embody
1498
+ embrace
1499
+ emerge
1500
+ emotion
1501
+ employ
1502
+ empower
1503
+ empty
1504
+ enable
1505
+ enact
1506
+ end
1507
+ endless
1508
+ endorse
1509
+ enemy
1510
+ energy
1511
+ enforce
1512
+ engage
1513
+ engine
1514
+ enhance
1515
+ enjoy
1516
+ enlist
1517
+ enough
1518
+ enrich
1519
+ enroll
1520
+ ensure
1521
+ enter
1522
+ entire
1523
+ entry
1524
+ envelope
1525
+ episode
1526
+ equal
1527
+ equip
1528
+ era
1529
+ erase
1530
+ erode
1531
+ erosion
1532
+ error
1533
+ erupt
1534
+ escape
1535
+ essay
1536
+ essence
1537
+ estate
1538
+ eternal
1539
+ ethics
1540
+ evidence
1541
+ evil
1542
+ evoke
1543
+ evolve
1544
+ exact
1545
+ example
1546
+ excess
1547
+ exchange
1548
+ excite
1549
+ exclude
1550
+ excuse
1551
+ execute
1552
+ exercise
1553
+ exhaust
1554
+ exhibit
1555
+ exile
1556
+ exist
1557
+ exit
1558
+ exotic
1559
+ expand
1560
+ expect
1561
+ expire
1562
+ explain
1563
+ expose
1564
+ express
1565
+ extend
1566
+ extra
1567
+ eye
1568
+ eyebrow
1569
+ fabric
1570
+ face
1571
+ faculty
1572
+ fade
1573
+ faint
1574
+ faith
1575
+ fall
1576
+ false
1577
+ fame
1578
+ family
1579
+ famous
1580
+ fan
1581
+ fancy
1582
+ fantasy
1583
+ farm
1584
+ fashion
1585
+ fat
1586
+ fatal
1587
+ father
1588
+ fatigue
1589
+ fault
1590
+ favorite
1591
+ feature
1592
+ february
1593
+ federal
1594
+ fee
1595
+ feed
1596
+ feel
1597
+ female
1598
+ fence
1599
+ festival
1600
+ fetch
1601
+ fever
1602
+ few
1603
+ fiber
1604
+ fiction
1605
+ field
1606
+ figure
1607
+ file
1608
+ film
1609
+ filter
1610
+ final
1611
+ find
1612
+ fine
1613
+ finger
1614
+ finish
1615
+ fire
1616
+ firm
1617
+ first
1618
+ fiscal
1619
+ fish
1620
+ fit
1621
+ fitness
1622
+ fix
1623
+ flag
1624
+ flame
1625
+ flash
1626
+ flat
1627
+ flavor
1628
+ flee
1629
+ flight
1630
+ flip
1631
+ float
1632
+ flock
1633
+ floor
1634
+ flower
1635
+ fluid
1636
+ flush
1637
+ fly
1638
+ foam
1639
+ focus
1640
+ fog
1641
+ foil
1642
+ fold
1643
+ follow
1644
+ food
1645
+ foot
1646
+ force
1647
+ forest
1648
+ forget
1649
+ fork
1650
+ fortune
1651
+ forum
1652
+ forward
1653
+ fossil
1654
+ foster
1655
+ found
1656
+ fox
1657
+ fragile
1658
+ frame
1659
+ frequent
1660
+ fresh
1661
+ friend
1662
+ fringe
1663
+ frog
1664
+ front
1665
+ frost
1666
+ frown
1667
+ frozen
1668
+ fruit
1669
+ fuel
1670
+ fun
1671
+ funny
1672
+ furnace
1673
+ fury
1674
+ future
1675
+ gadget
1676
+ gain
1677
+ galaxy
1678
+ gallery
1679
+ game
1680
+ gap
1681
+ garage
1682
+ garbage
1683
+ garden
1684
+ garlic
1685
+ garment
1686
+ gas
1687
+ gasp
1688
+ gate
1689
+ gather
1690
+ gauge
1691
+ gaze
1692
+ general
1693
+ genius
1694
+ genre
1695
+ gentle
1696
+ genuine
1697
+ gesture
1698
+ ghost
1699
+ giant
1700
+ gift
1701
+ giggle
1702
+ ginger
1703
+ giraffe
1704
+ girl
1705
+ give
1706
+ glad
1707
+ glance
1708
+ glare
1709
+ glass
1710
+ glide
1711
+ glimpse
1712
+ globe
1713
+ gloom
1714
+ glory
1715
+ glove
1716
+ glow
1717
+ glue
1718
+ goat
1719
+ goddess
1720
+ gold
1721
+ good
1722
+ goose
1723
+ gorilla
1724
+ gospel
1725
+ gossip
1726
+ govern
1727
+ gown
1728
+ grab
1729
+ grace
1730
+ grain
1731
+ grant
1732
+ grape
1733
+ grass
1734
+ gravity
1735
+ great
1736
+ green
1737
+ grid
1738
+ grief
1739
+ grit
1740
+ grocery
1741
+ group
1742
+ grow
1743
+ grunt
1744
+ guard
1745
+ guess
1746
+ guide
1747
+ guilt
1748
+ guitar
1749
+ gun
1750
+ gym
1751
+ habit
1752
+ hair
1753
+ half
1754
+ hammer
1755
+ hamster
1756
+ hand
1757
+ happy
1758
+ harbor
1759
+ hard
1760
+ harsh
1761
+ harvest
1762
+ hat
1763
+ have
1764
+ hawk
1765
+ hazard
1766
+ head
1767
+ health
1768
+ heart
1769
+ heavy
1770
+ hedgehog
1771
+ height
1772
+ hello
1773
+ helmet
1774
+ help
1775
+ hen
1776
+ hero
1777
+ hidden
1778
+ high
1779
+ hill
1780
+ hint
1781
+ hip
1782
+ hire
1783
+ history
1784
+ hobby
1785
+ hockey
1786
+ hold
1787
+ hole
1788
+ holiday
1789
+ hollow
1790
+ home
1791
+ honey
1792
+ hood
1793
+ hope
1794
+ horn
1795
+ horror
1796
+ horse
1797
+ hospital
1798
+ host
1799
+ hotel
1800
+ hour
1801
+ hover
1802
+ hub
1803
+ huge
1804
+ human
1805
+ humble
1806
+ humor
1807
+ hundred
1808
+ hungry
1809
+ hunt
1810
+ hurdle
1811
+ hurry
1812
+ hurt
1813
+ husband
1814
+ hybrid
1815
+ ice
1816
+ icon
1817
+ idea
1818
+ identify
1819
+ idle
1820
+ ignore
1821
+ ill
1822
+ illegal
1823
+ illness
1824
+ image
1825
+ imitate
1826
+ immense
1827
+ immune
1828
+ impact
1829
+ impose
1830
+ improve
1831
+ impulse
1832
+ inch
1833
+ include
1834
+ income
1835
+ increase
1836
+ index
1837
+ indicate
1838
+ indoor
1839
+ industry
1840
+ infant
1841
+ inflict
1842
+ inform
1843
+ inhale
1844
+ inherit
1845
+ initial
1846
+ inject
1847
+ injury
1848
+ inmate
1849
+ inner
1850
+ innocent
1851
+ input
1852
+ inquiry
1853
+ insane
1854
+ insect
1855
+ inside
1856
+ inspire
1857
+ install
1858
+ intact
1859
+ interest
1860
+ into
1861
+ invest
1862
+ invite
1863
+ involve
1864
+ iron
1865
+ island
1866
+ isolate
1867
+ issue
1868
+ item
1869
+ ivory
1870
+ jacket
1871
+ jaguar
1872
+ jar
1873
+ jazz
1874
+ jealous
1875
+ jeans
1876
+ jelly
1877
+ jewel
1878
+ job
1879
+ join
1880
+ joke
1881
+ journey
1882
+ joy
1883
+ judge
1884
+ juice
1885
+ jump
1886
+ jungle
1887
+ junior
1888
+ junk
1889
+ just
1890
+ kangaroo
1891
+ keen
1892
+ keep
1893
+ ketchup
1894
+ key
1895
+ kick
1896
+ kid
1897
+ kidney
1898
+ kind
1899
+ kingdom
1900
+ kiss
1901
+ kit
1902
+ kitchen
1903
+ kite
1904
+ kitten
1905
+ kiwi
1906
+ knee
1907
+ knife
1908
+ knock
1909
+ know
1910
+ lab
1911
+ label
1912
+ labor
1913
+ ladder
1914
+ lady
1915
+ lake
1916
+ lamp
1917
+ language
1918
+ laptop
1919
+ large
1920
+ later
1921
+ latin
1922
+ laugh
1923
+ laundry
1924
+ lava
1925
+ law
1926
+ lawn
1927
+ lawsuit
1928
+ layer
1929
+ lazy
1930
+ leader
1931
+ leaf
1932
+ learn
1933
+ leave
1934
+ lecture
1935
+ left
1936
+ leg
1937
+ legal
1938
+ legend
1939
+ leisure
1940
+ lemon
1941
+ lend
1942
+ length
1943
+ lens
1944
+ leopard
1945
+ lesson
1946
+ letter
1947
+ level
1948
+ liar
1949
+ liberty
1950
+ library
1951
+ license
1952
+ life
1953
+ lift
1954
+ light
1955
+ like
1956
+ limb
1957
+ limit
1958
+ link
1959
+ lion
1960
+ liquid
1961
+ list
1962
+ little
1963
+ live
1964
+ lizard
1965
+ load
1966
+ loan
1967
+ lobster
1968
+ local
1969
+ lock
1970
+ logic
1971
+ lonely
1972
+ long
1973
+ loop
1974
+ lottery
1975
+ loud
1976
+ lounge
1977
+ love
1978
+ loyal
1979
+ lucky
1980
+ luggage
1981
+ lumber
1982
+ lunar
1983
+ lunch
1984
+ luxury
1985
+ lyrics
1986
+ machine
1987
+ mad
1988
+ magic
1989
+ magnet
1990
+ maid
1991
+ mail
1992
+ main
1993
+ major
1994
+ make
1995
+ mammal
1996
+ man
1997
+ manage
1998
+ mandate
1999
+ mango
2000
+ mansion
2001
+ manual
2002
+ maple
2003
+ marble
2004
+ march
2005
+ margin
2006
+ marine
2007
+ market
2008
+ marriage
2009
+ mask
2010
+ mass
2011
+ master
2012
+ match
2013
+ material
2014
+ math
2015
+ matrix
2016
+ matter
2017
+ maximum
2018
+ maze
2019
+ meadow
2020
+ mean
2021
+ measure
2022
+ meat
2023
+ mechanic
2024
+ medal
2025
+ media
2026
+ melody
2027
+ melt
2028
+ member
2029
+ memory
2030
+ mention
2031
+ menu
2032
+ mercy
2033
+ merge
2034
+ merit
2035
+ merry
2036
+ mesh
2037
+ message
2038
+ metal
2039
+ method
2040
+ middle
2041
+ midnight
2042
+ milk
2043
+ million
2044
+ mimic
2045
+ mind
2046
+ minimum
2047
+ minor
2048
+ minute
2049
+ miracle
2050
+ mirror
2051
+ misery
2052
+ miss
2053
+ mistake
2054
+ mix
2055
+ mixed
2056
+ mixture
2057
+ mobile
2058
+ model
2059
+ modify
2060
+ mom
2061
+ moment
2062
+ monitor
2063
+ monkey
2064
+ monster
2065
+ month
2066
+ moon
2067
+ moral
2068
+ more
2069
+ morning
2070
+ mosquito
2071
+ mother
2072
+ motion
2073
+ motor
2074
+ mountain
2075
+ mouse
2076
+ move
2077
+ movie
2078
+ much
2079
+ muffin
2080
+ mule
2081
+ multiply
2082
+ muscle
2083
+ museum
2084
+ mushroom
2085
+ music
2086
+ must
2087
+ mutual
2088
+ myself
2089
+ mystery
2090
+ myth
2091
+ naive
2092
+ name
2093
+ napkin
2094
+ narrow
2095
+ nasty
2096
+ nation
2097
+ nature
2098
+ near
2099
+ neck
2100
+ need
2101
+ negative
2102
+ neglect
2103
+ neither
2104
+ nephew
2105
+ nerve
2106
+ nest
2107
+ net
2108
+ network
2109
+ neutral
2110
+ never
2111
+ news
2112
+ next
2113
+ nice
2114
+ night
2115
+ noble
2116
+ noise
2117
+ nominee
2118
+ noodle
2119
+ normal
2120
+ north
2121
+ nose
2122
+ notable
2123
+ note
2124
+ nothing
2125
+ notice
2126
+ novel
2127
+ now
2128
+ nuclear
2129
+ number
2130
+ nurse
2131
+ nut
2132
+ oak
2133
+ obey
2134
+ object
2135
+ oblige
2136
+ obscure
2137
+ observe
2138
+ obtain
2139
+ obvious
2140
+ occur
2141
+ ocean
2142
+ october
2143
+ odor
2144
+ off
2145
+ offer
2146
+ office
2147
+ often
2148
+ oil
2149
+ okay
2150
+ old
2151
+ olive
2152
+ olympic
2153
+ omit
2154
+ once
2155
+ one
2156
+ onion
2157
+ online
2158
+ only
2159
+ open
2160
+ opera
2161
+ opinion
2162
+ oppose
2163
+ option
2164
+ orange
2165
+ orbit
2166
+ orchard
2167
+ order
2168
+ ordinary
2169
+ organ
2170
+ orient
2171
+ original
2172
+ orphan
2173
+ ostrich
2174
+ other
2175
+ outdoor
2176
+ outer
2177
+ output
2178
+ outside
2179
+ oval
2180
+ oven
2181
+ over
2182
+ own
2183
+ owner
2184
+ oxygen
2185
+ oyster
2186
+ ozone
2187
+ pact
2188
+ paddle
2189
+ page
2190
+ pair
2191
+ palace
2192
+ palm
2193
+ panda
2194
+ panel
2195
+ panic
2196
+ panther
2197
+ paper
2198
+ parade
2199
+ parent
2200
+ park
2201
+ parrot
2202
+ party
2203
+ pass
2204
+ patch
2205
+ path
2206
+ patient
2207
+ patrol
2208
+ pattern
2209
+ pause
2210
+ pave
2211
+ payment
2212
+ peace
2213
+ peanut
2214
+ pear
2215
+ peasant
2216
+ pelican
2217
+ pen
2218
+ penalty
2219
+ pencil
2220
+ people
2221
+ pepper
2222
+ perfect
2223
+ permit
2224
+ person
2225
+ pet
2226
+ phone
2227
+ photo
2228
+ phrase
2229
+ physical
2230
+ piano
2231
+ picnic
2232
+ picture
2233
+ piece
2234
+ pig
2235
+ pigeon
2236
+ pill
2237
+ pilot
2238
+ pink
2239
+ pioneer
2240
+ pipe
2241
+ pistol
2242
+ pitch
2243
+ pizza
2244
+ place
2245
+ planet
2246
+ plastic
2247
+ plate
2248
+ play
2249
+ please
2250
+ pledge
2251
+ pluck
2252
+ plug
2253
+ plunge
2254
+ poem
2255
+ poet
2256
+ point
2257
+ polar
2258
+ pole
2259
+ police
2260
+ pond
2261
+ pony
2262
+ pool
2263
+ popular
2264
+ portion
2265
+ position
2266
+ possible
2267
+ post
2268
+ potato
2269
+ pottery
2270
+ poverty
2271
+ powder
2272
+ power
2273
+ practice
2274
+ praise
2275
+ predict
2276
+ prefer
2277
+ prepare
2278
+ present
2279
+ pretty
2280
+ prevent
2281
+ price
2282
+ pride
2283
+ primary
2284
+ print
2285
+ priority
2286
+ prison
2287
+ private
2288
+ prize
2289
+ problem
2290
+ process
2291
+ produce
2292
+ profit
2293
+ program
2294
+ project
2295
+ promote
2296
+ proof
2297
+ property
2298
+ prosper
2299
+ protect
2300
+ proud
2301
+ provide
2302
+ public
2303
+ pudding
2304
+ pull
2305
+ pulp
2306
+ pulse
2307
+ pumpkin
2308
+ punch
2309
+ pupil
2310
+ puppy
2311
+ purchase
2312
+ purity
2313
+ purpose
2314
+ purse
2315
+ push
2316
+ put
2317
+ puzzle
2318
+ pyramid
2319
+ quality
2320
+ quantum
2321
+ quarter
2322
+ question
2323
+ quick
2324
+ quit
2325
+ quiz
2326
+ quote
2327
+ rabbit
2328
+ raccoon
2329
+ race
2330
+ rack
2331
+ radar
2332
+ radio
2333
+ rail
2334
+ rain
2335
+ raise
2336
+ rally
2337
+ ramp
2338
+ ranch
2339
+ random
2340
+ range
2341
+ rapid
2342
+ rare
2343
+ rate
2344
+ rather
2345
+ raven
2346
+ raw
2347
+ razor
2348
+ ready
2349
+ real
2350
+ reason
2351
+ rebel
2352
+ rebuild
2353
+ recall
2354
+ receive
2355
+ recipe
2356
+ record
2357
+ recycle
2358
+ reduce
2359
+ reflect
2360
+ reform
2361
+ refuse
2362
+ region
2363
+ regret
2364
+ regular
2365
+ reject
2366
+ relax
2367
+ release
2368
+ relief
2369
+ rely
2370
+ remain
2371
+ remember
2372
+ remind
2373
+ remove
2374
+ render
2375
+ renew
2376
+ rent
2377
+ reopen
2378
+ repair
2379
+ repeat
2380
+ replace
2381
+ report
2382
+ require
2383
+ rescue
2384
+ resemble
2385
+ resist
2386
+ resource
2387
+ response
2388
+ result
2389
+ retire
2390
+ retreat
2391
+ return
2392
+ reunion
2393
+ reveal
2394
+ review
2395
+ reward
2396
+ rhythm
2397
+ rib
2398
+ ribbon
2399
+ rice
2400
+ rich
2401
+ ride
2402
+ ridge
2403
+ rifle
2404
+ right
2405
+ rigid
2406
+ ring
2407
+ riot
2408
+ ripple
2409
+ risk
2410
+ ritual
2411
+ rival
2412
+ river
2413
+ road
2414
+ roast
2415
+ robot
2416
+ robust
2417
+ rocket
2418
+ romance
2419
+ roof
2420
+ rookie
2421
+ room
2422
+ rose
2423
+ rotate
2424
+ rough
2425
+ round
2426
+ route
2427
+ royal
2428
+ rubber
2429
+ rude
2430
+ rug
2431
+ rule
2432
+ run
2433
+ runway
2434
+ rural
2435
+ sad
2436
+ saddle
2437
+ sadness
2438
+ safe
2439
+ sail
2440
+ salad
2441
+ salmon
2442
+ salon
2443
+ salt
2444
+ salute
2445
+ same
2446
+ sample
2447
+ sand
2448
+ satisfy
2449
+ satoshi
2450
+ sauce
2451
+ sausage
2452
+ save
2453
+ say
2454
+ scale
2455
+ scan
2456
+ scare
2457
+ scatter
2458
+ scene
2459
+ scheme
2460
+ school
2461
+ science
2462
+ scissors
2463
+ scorpion
2464
+ scout
2465
+ scrap
2466
+ screen
2467
+ script
2468
+ scrub
2469
+ sea
2470
+ search
2471
+ season
2472
+ seat
2473
+ second
2474
+ secret
2475
+ section
2476
+ security
2477
+ seed
2478
+ seek
2479
+ segment
2480
+ select
2481
+ sell
2482
+ seminar
2483
+ senior
2484
+ sense
2485
+ sentence
2486
+ series
2487
+ service
2488
+ session
2489
+ settle
2490
+ setup
2491
+ seven
2492
+ shadow
2493
+ shaft
2494
+ shallow
2495
+ share
2496
+ shed
2497
+ shell
2498
+ sheriff
2499
+ shield
2500
+ shift
2501
+ shine
2502
+ ship
2503
+ shiver
2504
+ shock
2505
+ shoe
2506
+ shoot
2507
+ shop
2508
+ short
2509
+ shoulder
2510
+ shove
2511
+ shrimp
2512
+ shrug
2513
+ shuffle
2514
+ shy
2515
+ sibling
2516
+ sick
2517
+ side
2518
+ siege
2519
+ sight
2520
+ sign
2521
+ silent
2522
+ silk
2523
+ silly
2524
+ silver
2525
+ similar
2526
+ simple
2527
+ since
2528
+ sing
2529
+ siren
2530
+ sister
2531
+ situate
2532
+ six
2533
+ size
2534
+ skate
2535
+ sketch
2536
+ ski
2537
+ skill
2538
+ skin
2539
+ skirt
2540
+ skull
2541
+ slab
2542
+ slam
2543
+ sleep
2544
+ slender
2545
+ slice
2546
+ slide
2547
+ slight
2548
+ slim
2549
+ slogan
2550
+ slot
2551
+ slow
2552
+ slush
2553
+ small
2554
+ smart
2555
+ smile
2556
+ smoke
2557
+ smooth
2558
+ snack
2559
+ snake
2560
+ snap
2561
+ sniff
2562
+ snow
2563
+ soap
2564
+ soccer
2565
+ social
2566
+ sock
2567
+ soda
2568
+ soft
2569
+ solar
2570
+ soldier
2571
+ solid
2572
+ solution
2573
+ solve
2574
+ someone
2575
+ song
2576
+ soon
2577
+ sorry
2578
+ sort
2579
+ soul
2580
+ sound
2581
+ soup
2582
+ source
2583
+ south
2584
+ space
2585
+ spare
2586
+ spatial
2587
+ spawn
2588
+ speak
2589
+ special
2590
+ speed
2591
+ spell
2592
+ spend
2593
+ sphere
2594
+ spice
2595
+ spider
2596
+ spike
2597
+ spin
2598
+ spirit
2599
+ split
2600
+ spoil
2601
+ sponsor
2602
+ spoon
2603
+ sport
2604
+ spot
2605
+ spray
2606
+ spread
2607
+ spring
2608
+ spy
2609
+ square
2610
+ squeeze
2611
+ squirrel
2612
+ stable
2613
+ stadium
2614
+ staff
2615
+ stage
2616
+ stairs
2617
+ stamp
2618
+ stand
2619
+ start
2620
+ state
2621
+ stay
2622
+ steak
2623
+ steel
2624
+ stem
2625
+ step
2626
+ stereo
2627
+ stick
2628
+ still
2629
+ sting
2630
+ stock
2631
+ stomach
2632
+ stone
2633
+ stool
2634
+ story
2635
+ stove
2636
+ strategy
2637
+ street
2638
+ strike
2639
+ strong
2640
+ struggle
2641
+ student
2642
+ stuff
2643
+ stumble
2644
+ style
2645
+ subject
2646
+ submit
2647
+ subway
2648
+ success
2649
+ such
2650
+ sudden
2651
+ suffer
2652
+ sugar
2653
+ suggest
2654
+ suit
2655
+ summer
2656
+ sun
2657
+ sunny
2658
+ sunset
2659
+ super
2660
+ supply
2661
+ supreme
2662
+ sure
2663
+ surface
2664
+ surge
2665
+ surprise
2666
+ surround
2667
+ survey
2668
+ suspect
2669
+ sustain
2670
+ swallow
2671
+ swamp
2672
+ swap
2673
+ swarm
2674
+ swear
2675
+ sweet
2676
+ swift
2677
+ swim
2678
+ swing
2679
+ switch
2680
+ sword
2681
+ symbol
2682
+ symptom
2683
+ syrup
2684
+ system
2685
+ table
2686
+ tackle
2687
+ tag
2688
+ tail
2689
+ talent
2690
+ talk
2691
+ tank
2692
+ tape
2693
+ target
2694
+ task
2695
+ taste
2696
+ tattoo
2697
+ taxi
2698
+ teach
2699
+ team
2700
+ tell
2701
+ ten
2702
+ tenant
2703
+ tennis
2704
+ tent
2705
+ term
2706
+ test
2707
+ text
2708
+ thank
2709
+ that
2710
+ theme
2711
+ then
2712
+ theory
2713
+ there
2714
+ they
2715
+ thing
2716
+ this
2717
+ thought
2718
+ three
2719
+ thrive
2720
+ throw
2721
+ thumb
2722
+ thunder
2723
+ ticket
2724
+ tide
2725
+ tiger
2726
+ tilt
2727
+ timber
2728
+ time
2729
+ tiny
2730
+ tip
2731
+ tired
2732
+ tissue
2733
+ title
2734
+ toast
2735
+ tobacco
2736
+ today
2737
+ toddler
2738
+ toe
2739
+ together
2740
+ toilet
2741
+ token
2742
+ tomato
2743
+ tomorrow
2744
+ tone
2745
+ tongue
2746
+ tonight
2747
+ tool
2748
+ tooth
2749
+ top
2750
+ topic
2751
+ topple
2752
+ torch
2753
+ tornado
2754
+ tortoise
2755
+ toss
2756
+ total
2757
+ tourist
2758
+ toward
2759
+ tower
2760
+ town
2761
+ toy
2762
+ track
2763
+ trade
2764
+ traffic
2765
+ tragic
2766
+ train
2767
+ transfer
2768
+ trap
2769
+ trash
2770
+ travel
2771
+ tray
2772
+ treat
2773
+ tree
2774
+ trend
2775
+ trial
2776
+ tribe
2777
+ trick
2778
+ trigger
2779
+ trim
2780
+ trip
2781
+ trophy
2782
+ trouble
2783
+ truck
2784
+ true
2785
+ truly
2786
+ trumpet
2787
+ trust
2788
+ truth
2789
+ try
2790
+ tube
2791
+ tuition
2792
+ tumble
2793
+ tuna
2794
+ tunnel
2795
+ turkey
2796
+ turn
2797
+ turtle
2798
+ twelve
2799
+ twenty
2800
+ twice
2801
+ twin
2802
+ twist
2803
+ two
2804
+ type
2805
+ typical
2806
+ ugly
2807
+ umbrella
2808
+ unable
2809
+ unaware
2810
+ uncle
2811
+ uncover
2812
+ under
2813
+ undo
2814
+ unfair
2815
+ unfold
2816
+ unhappy
2817
+ uniform
2818
+ unique
2819
+ unit
2820
+ universe
2821
+ unknown
2822
+ unlock
2823
+ until
2824
+ unusual
2825
+ unveil
2826
+ update
2827
+ upgrade
2828
+ uphold
2829
+ upon
2830
+ upper
2831
+ upset
2832
+ urban
2833
+ urge
2834
+ usage
2835
+ use
2836
+ used
2837
+ useful
2838
+ useless
2839
+ usual
2840
+ utility
2841
+ vacant
2842
+ vacuum
2843
+ vague
2844
+ valid
2845
+ valley
2846
+ valve
2847
+ van
2848
+ vanish
2849
+ vapor
2850
+ various
2851
+ vast
2852
+ vault
2853
+ vehicle
2854
+ velvet
2855
+ vendor
2856
+ venture
2857
+ venue
2858
+ verb
2859
+ verify
2860
+ version
2861
+ very
2862
+ vessel
2863
+ veteran
2864
+ viable
2865
+ vibrant
2866
+ vicious
2867
+ victory
2868
+ video
2869
+ view
2870
+ village
2871
+ vintage
2872
+ violin
2873
+ virtual
2874
+ virus
2875
+ visa
2876
+ visit
2877
+ visual
2878
+ vital
2879
+ vivid
2880
+ vocal
2881
+ voice
2882
+ void
2883
+ volcano
2884
+ volume
2885
+ vote
2886
+ voyage
2887
+ wage
2888
+ wagon
2889
+ wait
2890
+ walk
2891
+ wall
2892
+ walnut
2893
+ want
2894
+ warfare
2895
+ warm
2896
+ warrior
2897
+ wash
2898
+ wasp
2899
+ waste
2900
+ water
2901
+ wave
2902
+ way
2903
+ wealth
2904
+ weapon
2905
+ wear
2906
+ weasel
2907
+ weather
2908
+ web
2909
+ wedding
2910
+ weekend
2911
+ weird
2912
+ welcome
2913
+ west
2914
+ wet
2915
+ whale
2916
+ what
2917
+ wheat
2918
+ wheel
2919
+ when
2920
+ where
2921
+ whip
2922
+ whisper
2923
+ wide
2924
+ width
2925
+ wife
2926
+ wild
2927
+ will
2928
+ win
2929
+ window
2930
+ wine
2931
+ wing
2932
+ wink
2933
+ winner
2934
+ winter
2935
+ wire
2936
+ wisdom
2937
+ wise
2938
+ wish
2939
+ witness
2940
+ wolf
2941
+ woman
2942
+ wonder
2943
+ wood
2944
+ wool
2945
+ word
2946
+ work
2947
+ world
2948
+ worry
2949
+ worth
2950
+ wrap
2951
+ wreck
2952
+ wrestle
2953
+ wrist
2954
+ write
2955
+ wrong
2956
+ yard
2957
+ year
2958
+ yellow
2959
+ you
2960
+ young
2961
+ youth
2962
+ zebra
2963
+ zero
2964
+ zone
2965
+ zoo`.split("\n");
2966
+
2967
+ // src/secret-check.ts
2968
+ var MAX_FEEDBACK_TEXT_LENGTH = 4e3;
2969
+ function refusal(layer, reason, message) {
2970
+ return { layer, reason, message };
2971
+ }
2972
+ var LABELLED_PATTERNS = [
2973
+ { reason: "agent_api_key", re: /sk_agent_[A-Za-z0-9]/ },
2974
+ { reason: "setup_token", re: /hv_setup_[A-Za-z0-9]/ },
2975
+ // A session JWT: three dot-separated base64url parts, each non-empty.
2976
+ // `\beyJ` (not the looser `\bey`), which is the base64url encoding of `{"`
2977
+ // — every real JWT header starts there, and the tighter anchor stops
2978
+ // ordinary words like "eyebrow" or "eyelet" from matching their own tail.
2979
+ { reason: "session_jwt", re: /\beyJ[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+\b/ },
2980
+ // key= / api_key= / api-key= / apikey= / token= / secret=, `\b`-bounded —
2981
+ // the same boundary `redact-vendor-secrets.ts:19` uses.
2982
+ { reason: "labelled_parameter", re: /\b(api[_-]?key|key|token|secret)=[^&\s"'\\)]+/i },
2983
+ { reason: "url_credentials", re: /https?:\/\/[^\s/@]+:[^\s@]+@/i },
2984
+ { reason: "rpc_path_key", re: /\/(?:rpc|v2)\/[A-Za-z0-9_-]{16,}/ }
2985
+ ];
2986
+ function checkLabelledSecrets(text) {
2987
+ for (const { reason, re } of LABELLED_PATTERNS) {
2988
+ if (re.test(text)) {
2989
+ return refusal(
2990
+ 1,
2991
+ reason,
2992
+ "Refusing to send: the text looks like it contains a secret (a credential label or prefix matched). Remove it and try again."
2993
+ );
2994
+ }
2995
+ }
2996
+ return null;
2997
+ }
2998
+ function normalize(value) {
2999
+ return value.trim().toLowerCase().replace(/^0x/, "");
3000
+ }
3001
+ async function readJsonStringField(path, field) {
3002
+ try {
3003
+ const raw = await readFile(path, "utf8");
3004
+ const parsed = JSON.parse(raw);
3005
+ const value = parsed[field];
3006
+ return typeof value === "string" && value.length > 0 ? value : null;
3007
+ } catch {
3008
+ return null;
3009
+ }
3010
+ }
3011
+ async function readLocalSecrets(sources = {}) {
3012
+ const secrets = /* @__PURE__ */ new Set();
3013
+ const env = sources.env ?? process.env;
3014
+ const envKey = env.HAVEN_DELEGATE_KEY;
3015
+ if (envKey) secrets.add(normalize(envKey));
3016
+ if (sources.sessionToken) secrets.add(normalize(sources.sessionToken));
3017
+ const root = sources.baseDir ?? join(homedir(), ".haven", "agents");
3018
+ let entries = [];
3019
+ try {
3020
+ entries = await readdir(root);
3021
+ } catch {
3022
+ return secrets;
3023
+ }
3024
+ for (const entry of entries) {
3025
+ const dir = join(root, entry);
3026
+ const delegateKey = await readJsonStringField(join(dir, "signer.json"), "delegate_key");
3027
+ if (delegateKey) secrets.add(normalize(delegateKey));
3028
+ const apiKey = await readJsonStringField(join(dir, "identity.json"), "api_key");
3029
+ if (apiKey) secrets.add(normalize(apiKey));
3030
+ const pendingKey = await readJsonStringField(join(dir, "rekey-pending.json"), "new_delegate_key");
3031
+ if (pendingKey) secrets.add(normalize(pendingKey));
3032
+ }
3033
+ return secrets;
3034
+ }
3035
+ function checkExactSecretMatch(text, secrets) {
3036
+ if (secrets.size === 0) return null;
3037
+ const haystack = text.toLowerCase();
3038
+ for (const secret of secrets) {
3039
+ if (secret.length >= 8 && haystack.includes(secret)) {
3040
+ return refusal(
3041
+ 2,
3042
+ "local_credential",
3043
+ "Refusing to send: the text contains a credential this machine holds (an agent delegate key, an agent API key, a pending re-key, or your own session token). Remove it and try again."
3044
+ );
3045
+ }
3046
+ }
3047
+ return null;
3048
+ }
3049
+ function findHexTokenCandidates(text) {
3050
+ const matches = text.matchAll(/(?<![0-9a-fA-F])(0x)?([0-9a-fA-F]{64})(?![0-9a-fA-F])/g);
3051
+ return [...matches].map((m) => m[2]);
3052
+ }
3053
+ function deriveAddressFromHexToken(token) {
3054
+ try {
3055
+ const ecdh = createECDH("secp256k1");
3056
+ ecdh.setPrivateKey(Buffer.from(token, "hex"));
3057
+ const uncompressed = ecdh.getPublicKey();
3058
+ const hash = keccak_256(uncompressed.subarray(1));
3059
+ return `0x${Buffer.from(hash.subarray(12)).toString("hex")}`;
3060
+ } catch {
3061
+ return null;
3062
+ }
3063
+ }
3064
+ async function collectKeyBackedAddresses(api) {
3065
+ const addresses = /* @__PURE__ */ new Set();
3066
+ const { agents } = await api.get("/agents");
3067
+ for (const agent of agents) {
3068
+ if (agent.delegate_address) addresses.add(agent.delegate_address.toLowerCase());
3069
+ }
3070
+ const { accounts } = await api.get("/user/accounts");
3071
+ for (const account of accounts) {
3072
+ if (!account.account_address) continue;
3073
+ const signers = await api.get(
3074
+ `/accounts/hybrid/${account.account_address}/signers?chain_id=${account.chain_id}`
3075
+ );
3076
+ if (signers.owner_address) addresses.add(signers.owner_address.toLowerCase());
3077
+ }
3078
+ return addresses;
3079
+ }
3080
+ async function checkKeyBackedAddresses(text, api) {
3081
+ const candidates = findHexTokenCandidates(text);
3082
+ if (candidates.length === 0) return null;
3083
+ let keyBacked;
3084
+ try {
3085
+ keyBacked = await collectKeyBackedAddresses(api);
3086
+ } catch (err) {
3087
+ if (err instanceof CliApiError && (err.status === 401 || err.status === 403)) {
3088
+ return refusal(
3089
+ 3,
3090
+ "address_check_unavailable",
3091
+ "Refusing to send: could not verify whether the text contains a private key \u2014 your session cannot read the data this check needs. Run `haven login` again; retrying this submission will not help."
3092
+ );
3093
+ }
3094
+ if (err instanceof CliApiError && err.status === 409) {
3095
+ return refusal(
3096
+ 3,
3097
+ "address_check_unavailable",
3098
+ "Refusing to send: could not verify whether the text contains a private key \u2014 one of your accounts' signer configuration is unknown, so there is nothing to compare against. Retrying this submission will not help; contact support."
3099
+ );
3100
+ }
3101
+ return refusal(
3102
+ 3,
3103
+ "address_check_unavailable",
3104
+ "Refusing to send: could not verify whether the text contains a private key (the address check failed). Retry once Haven is reachable."
3105
+ );
3106
+ }
3107
+ for (const token of candidates) {
3108
+ const address = deriveAddressFromHexToken(token);
3109
+ if (address && keyBacked.has(address.toLowerCase())) {
3110
+ return refusal(
3111
+ 3,
3112
+ "private_key",
3113
+ "Refusing to send: the text contains what looks like the private key for one of your agents or accounts. Remove it and try again."
3114
+ );
3115
+ }
3116
+ }
3117
+ return null;
3118
+ }
3119
+ var BIP39_WORDS = new Set(wordlist.map((word) => word.toLowerCase()));
3120
+ var MIN_RECOVERY_PHRASE_WORDS = 12;
3121
+ function checkRecoveryPhrase(text) {
3122
+ const tokens = text.split(/\s+/).map((t) => t.replace(/^[^A-Za-z]+|[^A-Za-z]+$/g, "").toLowerCase());
3123
+ let run2 = 0;
3124
+ for (const token of tokens) {
3125
+ if (token && BIP39_WORDS.has(token)) {
3126
+ run2 += 1;
3127
+ if (run2 >= MIN_RECOVERY_PHRASE_WORDS) {
3128
+ return refusal(
3129
+ 4,
3130
+ "recovery_phrase",
3131
+ "Refusing to send: the text looks like it contains a recovery phrase (12 or more consecutive seed words). Remove it and try again."
3132
+ );
3133
+ }
3134
+ } else {
3135
+ run2 = 0;
3136
+ }
3137
+ }
3138
+ return null;
3139
+ }
3140
+ async function checkTextForSecrets(text, deps) {
3141
+ const layer1 = checkLabelledSecrets(text);
3142
+ if (layer1) return layer1;
3143
+ const localSecrets = await readLocalSecrets(deps.localSecrets);
3144
+ const layer2 = checkExactSecretMatch(text, localSecrets);
3145
+ if (layer2) return layer2;
3146
+ const layer3 = await checkKeyBackedAddresses(text, deps.api);
3147
+ if (layer3) return layer3;
3148
+ return checkRecoveryPhrase(text);
3149
+ }
3150
+
619
3151
  // src/commands.ts
620
3152
  var DEFAULT_API = "https://havenbackend-production-8a00.up.railway.app";
621
- var CLI_VERSION = "0.8.0-alpha.0";
3153
+ var CLI_VERSION = "0.9.0-alpha.0";
622
3154
  var CLI_CLIENT_IDENTITY = `@haven_ai/cli/${CLI_VERSION}`;
623
3155
  function accountAddressOf(s) {
624
3156
  return s.account_address ?? "";
@@ -639,6 +3171,7 @@ async function run(argv, deps = {}) {
639
3171
  out,
640
3172
  err,
641
3173
  env: deps.env ?? process.env,
3174
+ credentialsBaseDir: deps.credentialsBaseDir,
642
3175
  o
643
3176
  };
644
3177
  let args;
@@ -719,6 +3252,8 @@ async function dispatch(args, d) {
719
3252
  return cmdContactsAdd(args, d);
720
3253
  case "contacts remove":
721
3254
  return cmdContactsRemove(args, d);
3255
+ case "feedback submit":
3256
+ return cmdFeedbackSubmit(args, d);
722
3257
  default:
723
3258
  throw new UsageError(`Unknown command: ${key}.`, "Run `haven --help` for the command list.");
724
3259
  }
@@ -1169,12 +3704,6 @@ async function cmdBudgetRevoke(args, d) {
1169
3704
  if (row.status === "revoked") {
1170
3705
  throw new HavenCliError(`Delegation ${hash} is already revoked.`, EXIT.refused);
1171
3706
  }
1172
- if (row.status === "replaced") {
1173
- throw new HavenCliError(
1174
- `Delegation ${hash} was already replaced by a newer grant \u2014 nothing to revoke.`,
1175
- EXIT.refused
1176
- );
1177
- }
1178
3707
  const prepared = await api.post(`/agents/${id}/delegations/${hash}/revoke`, {});
1179
3708
  const revokeUrl = prepared.revocation_url;
1180
3709
  if (!revokeUrl) {
@@ -1225,25 +3754,63 @@ async function cmdAgentLifecycle(args, d, action) {
1225
3754
  const { api } = await authed(args, d);
1226
3755
  await api.post(`/agents/${id}/${action}`);
1227
3756
  const status = action === "pause" ? "paused" : "resumed";
1228
- emit(d, args.flags.json, { ok: true, agent_id: id, status }, () => `Agent ${id} ${status}.`);
3757
+ emit(
3758
+ d,
3759
+ args.flags.json,
3760
+ { ok: true, agent_id: id, status },
3761
+ () => action === "pause" ? `Agent ${id} paused: payments through Haven are blocked, but its budget stays live on-chain (end it with \`haven budget revoke\`).` : `Agent ${id} resumed: payments through Haven are allowed again.`
3762
+ );
1229
3763
  return EXIT.ok;
1230
3764
  }
1231
3765
  async function cmdAgentRevoke(args, d) {
1232
3766
  const id = args.positionals[0];
1233
- if (!id) throw new UsageError("Usage: haven agents revoke <id> --yes");
3767
+ if (!id) throw new UsageError("Usage: haven agents revoke <id> [--keep-budget] --yes");
1234
3768
  if (!args.flags.yes) {
1235
3769
  throw new UsageError(
1236
- `This permanently revokes agent ${id}.`,
1237
- "Re-run with --yes to confirm. Revoke is terminal \u2014 the agent cannot go back to active."
3770
+ `This permanently retires agent ${id} in Haven.`,
3771
+ "Re-run with --yes to confirm. Revoke is terminal \u2014 the agent cannot go back to active. It ends no budget on-chain: end each live budget with `haven budget revoke <agentId> <delegationHash>`."
1238
3772
  );
1239
3773
  }
1240
3774
  const { api } = await authed(args, d);
3775
+ let live = [];
3776
+ try {
3777
+ const agent = await api.get(`/agents/${id}`);
3778
+ if ((agent.live_delegation_count ?? 0) > 0) {
3779
+ const { delegations } = await api.get(`/agents/${id}/delegations`);
3780
+ live = delegations.filter((r) => r.status === "pending" || r.status === "active" || r.status === "replaced");
3781
+ }
3782
+ } catch (err) {
3783
+ d.o.note(`Couldn't check budgets for agent ${id}, so the revocation was not attempted.`);
3784
+ throw err;
3785
+ }
3786
+ if (live.length > 0 && !args.flags.keepBudget) {
3787
+ throw new HavenCliError(
3788
+ `Agent ${id} still has a live on-chain budget. Revoking now would leave it redeemable by the delegate key.`,
3789
+ EXIT.refused,
3790
+ [
3791
+ ...live.map((r) => `haven budget revoke ${id} ${r.delegation_hash} # ${r.status}`),
3792
+ "Remove agent\u2026 in the dashboard ends every live budget with one signature, before revoking.",
3793
+ "To revoke the agent anyway and leave the budget live on-chain, re-run with --keep-budget."
3794
+ ].join("\n"),
3795
+ // The machine branch: a script skips the prose and reads the rows.
3796
+ { live_delegations: live.map(({ delegation_hash, status }) => ({ delegation_hash, status })) }
3797
+ );
3798
+ }
1241
3799
  await api.post(`/agents/${id}/revoke`);
1242
3800
  emit(
1243
3801
  d,
1244
3802
  args.flags.json,
1245
- { ok: true, agent_id: id, status: "revoked" },
1246
- () => `Agent ${id} revoked. To also remove its on-chain allowance, use the dashboard.`
3803
+ live.length > 0 ? {
3804
+ ok: true,
3805
+ agent_id: id,
3806
+ status: "revoked",
3807
+ live_delegations: live.map(({ delegation_hash, status }) => ({ delegation_hash, status }))
3808
+ } : { ok: true, agent_id: id, status: "revoked" },
3809
+ () => live.length > 0 ? [
3810
+ `Agent ${id} revoked.`,
3811
+ `Its budget is still live on-chain: the delegate key can keep spending until each delegation is revoked.`,
3812
+ ...live.map((r) => `haven budget revoke ${id} ${r.delegation_hash} # ${r.status}`)
3813
+ ].join("\n") : `Agent ${id} revoked in Haven. It had no live budget on-chain.`
1247
3814
  );
1248
3815
  return EXIT.ok;
1249
3816
  }
@@ -1535,6 +4102,30 @@ async function cmdContactsRemove(args, d) {
1535
4102
  emit(d, args.flags.json, { ok: true, contact_id: id, removed: true }, () => `Contact ${id} removed.`);
1536
4103
  return EXIT.ok;
1537
4104
  }
4105
+ async function cmdFeedbackSubmit(args, d) {
4106
+ const text = args.positionals.join(" ").trim();
4107
+ if (!text) throw new UsageError('Usage: haven feedback submit "<text>"');
4108
+ if ([...text].length > MAX_FEEDBACK_TEXT_LENGTH) {
4109
+ throw new UsageError(`Feedback text must be ${MAX_FEEDBACK_TEXT_LENGTH} characters (code points) or fewer.`);
4110
+ }
4111
+ const { session, api } = await authed(args, d);
4112
+ const refusal2 = await checkTextForSecrets(text, {
4113
+ api,
4114
+ localSecrets: { sessionToken: session.token, env: d.env, baseDir: d.credentialsBaseDir }
4115
+ });
4116
+ if (refusal2) {
4117
+ throw new HavenCliError(refusal2.message, EXIT.refused, `layer ${refusal2.layer}: ${refusal2.reason}`, {
4118
+ layer: refusal2.layer,
4119
+ reason: refusal2.reason
4120
+ });
4121
+ }
4122
+ d.o.note(
4123
+ "Reminder: never include a password or any other credential in feedback text \u2014 this check cannot recognise one by shape."
4124
+ );
4125
+ const result = await api.post("/feedback", { text });
4126
+ emit(d, args.flags.json, { ok: true, ...result }, () => `Feedback submitted (id ${result.id}).`);
4127
+ return EXIT.ok;
4128
+ }
1538
4129
  function promptPasswordWith(streams) {
1539
4130
  return new Promise((resolve2, reject) => {
1540
4131
  const { stdin, stderr } = streams;
@@ -1590,5 +4181,10 @@ run(process.argv.slice(2), { promptPassword }).then((code) => process.exit(code)
1590
4181
  }
1591
4182
  process.exit(1);
1592
4183
  });
4184
+ /*! Bundled license information:
4185
+
4186
+ @noble/hashes/esm/utils.js:
4187
+ (*! noble-hashes - MIT License (c) 2022 Paul Miller (paulmillr.com) *)
4188
+ */
1593
4189
  //# sourceMappingURL=cli.js.map
1594
4190
  //# sourceMappingURL=cli.js.map