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