@thirdfy/agent-cli 0.1.38 → 0.1.40
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/CHANGELOG.md +17 -0
- package/README.md +26 -13
- package/bin/thirdfy-agent.mjs +145 -19
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,23 @@ All notable changes to `@thirdfy/agent-cli` are documented here. The format is b
|
|
|
4
4
|
|
|
5
5
|
## [Unreleased]
|
|
6
6
|
|
|
7
|
+
## [0.1.40] - 2026-05-17
|
|
8
|
+
|
|
9
|
+
### Fixed
|
|
10
|
+
|
|
11
|
+
- Email login and bootstrap commands now persist agent credentials from nested API response shapes such as `registration.registration.agentApiKey`, so owner-session onboarding can resolve the hidden execution identity without requiring users to pass a raw agent API key manually.
|
|
12
|
+
|
|
13
|
+
## [0.1.39] - 2026-05-17
|
|
14
|
+
|
|
15
|
+
### Added
|
|
16
|
+
|
|
17
|
+
- Framework guidance for Hermes, OpenClaw, and Claude Managed Agents in `help onboarding`, README, and command reference so agents can choose CLI/MCP rails correctly.
|
|
18
|
+
- Gator-style `delegation grant` alias and clearer external-wallet delegation next steps.
|
|
19
|
+
|
|
20
|
+
### Changed
|
|
21
|
+
|
|
22
|
+
- Fresh solo profiles now default to `agent_wallet`, persist/resolve owner `userDid`, and use owner-matched mirror scope for hybrid validation.
|
|
23
|
+
|
|
7
24
|
## [0.1.38] - 2026-05-17
|
|
8
25
|
|
|
9
26
|
### Fixed
|
package/README.md
CHANGED
|
@@ -9,12 +9,13 @@ Use it to:
|
|
|
9
9
|
- run governance preflight checks
|
|
10
10
|
- queue execute-intent requests with idempotency
|
|
11
11
|
- poll intent status with stable JSON output for automation
|
|
12
|
-
- choose execution topology with one switch: `
|
|
12
|
+
- choose execution topology with one switch: `agent_wallet`, `self`, `hybrid`, or `thirdfy`
|
|
13
13
|
- configure profile-driven defaults (`personal`, `builder`, `network`)
|
|
14
14
|
|
|
15
15
|
If you want the full developer docs, start here:
|
|
16
|
-
- [Thirdfy Agent CLI
|
|
17
|
-
- [
|
|
16
|
+
- [Thirdfy Agent CLI hub](https://docs.thirdfy.com/documentation/agent-cli) (guided chapters)
|
|
17
|
+
- [Thirdfy Agent CLI — full reference](https://docs.thirdfy.com/documentation/api/thirdfy-agent-cli)
|
|
18
|
+
- [API Reference home](https://docs.thirdfy.com/documentation/api/api-reference)
|
|
18
19
|
- [LLM docs map (llms.txt)](https://docs.thirdfy.com/llms.txt)
|
|
19
20
|
- [LLM full API snapshot (llms-full.txt)](https://docs.thirdfy.com/llms-full.txt)
|
|
20
21
|
- [Provider docs index (repo)](./docs/providers/index.md)
|
|
@@ -41,7 +42,7 @@ npx @thirdfy/agent-cli --help
|
|
|
41
42
|
|
|
42
43
|
## Release notes
|
|
43
44
|
|
|
44
|
-
Version history and highlights: [CHANGELOG.md](./CHANGELOG.md). **v0.1.38** is a **documentation and metadata patch**: it republishes to npm so the package homepage matches the updated `README.md` on `main`, and aligns root `package-lock.json` version fields with `package.json`. **v0.1.37** adds **email OTP** `thirdfy-agent login email` (send code, then complete with `--code --accept-terms`), **`help onboarding`**, **`doctor auth`**, **`wallet list`**, and **masked `whoami`** output so agents and operators get first-run guidance without dumping secrets.
|
|
45
|
+
Version history and highlights: [CHANGELOG.md](./CHANGELOG.md). **v0.1.40** fixes email/bootstrap credential persistence when the API returns nested registration payloads, so owner-session onboarding can save the hidden execution identity without exposing a raw agent API key. **v0.1.39** makes `agent_wallet` the fresh solo default, adds framework guidance for Hermes/OpenClaw/Claude Managed Agents, clarifies Gator/ERC-7710 delegation flows, and keeps BYOW/self plus hybrid/thirdfy mode switching explicit. **v0.1.38** is a **documentation and metadata patch**: it republishes to npm so the package homepage matches the updated `README.md` on `main`, and aligns root `package-lock.json` version fields with `package.json`. **v0.1.37** adds **email OTP** `thirdfy-agent login email` (send code, then complete with `--code --accept-terms`), **`help onboarding`**, **`doctor auth`**, **`wallet list`**, and **masked `whoami`** output so agents and operators get first-run guidance without dumping secrets. Earlier releases added provider discovery parity, portfolio analytics, managed-wallet swap normalization, and **self/build-tx** swap human-decimal `amountIn` parity when using `amountInHuman` + `tokenInDecimals`.
|
|
45
46
|
|
|
46
47
|
**Maintainers — npm:** follow [docs/releasing.md](./docs/releasing.md). From a clone with gitignored `.env`, use `npm run publish:npm:local -- --dry-run` then `npm run publish:npm:local`. Or run `npm run release:npm` after exporting tokens in the shell. Never commit npm tokens — use [.env.example](./.env.example) as a template only.
|
|
47
48
|
|
|
@@ -103,7 +104,7 @@ thirdfy-agent logout --json
|
|
|
103
104
|
thirdfy-agent logout --all --json
|
|
104
105
|
```
|
|
105
106
|
|
|
106
|
-
`login email` is a two-step headless-safe flow. Without `--code`, the CLI asks the Thirdfy API onboarding broker to send a Privy email OTP and exits with the exact next command. With `--code --accept-terms`, it stores a short-lived owner session in `~/.thirdfy/config.json`, records linked EVM/Solana wallets when Privy returns them,
|
|
107
|
+
`login email` is a two-step headless-safe flow. Without `--code`, the CLI asks the Thirdfy API onboarding broker to send a Privy email OTP and exits with the exact next command. With `--code --accept-terms`, it stores a short-lived owner session in `~/.thirdfy/config.json`, records linked EVM/Solana wallets plus the owner `userDid` when Privy returns them, stores a new agent API key when first-run bootstrap issues one, and defaults new solo profiles to `agent_wallet`. Use `--not-interactive` / `--ni` in automation so missing input fails fast instead of prompting — place **`--ni` after the `login email` subcommand** (for example `thirdfy-agent login email you@example.com --code … --accept-terms --ni`); a leading `thirdfy-agent --ni login email …` is parsed incorrectly because `--ni` would consume the `login` token as its value.
|
|
107
108
|
|
|
108
109
|
`logout --all` is provider-agnostic: it attempts owner-session revocation when a session token exists, then always clears local credentials.
|
|
109
110
|
|
|
@@ -149,15 +150,17 @@ thirdfy-agent bootstrap complete --agent-key "0xYOUR_AGENT_KEY" --challenge-id "
|
|
|
149
150
|
|
|
150
151
|
`thirdfy-agent` supports one command surface with four run modes:
|
|
151
152
|
|
|
152
|
-
- `--run-mode
|
|
153
|
-
- `--run-mode self` -> self-custody rail (`build-tx` flow
|
|
153
|
+
- `--run-mode agent_wallet` -> managed solo wallet execution rail and fresh email-login default
|
|
154
|
+
- `--run-mode self` -> self-custody rail (`build-tx` flow)
|
|
154
155
|
- `--run-mode hybrid` -> self-custody + governance mirror metadata (default for `profile=builder`)
|
|
155
|
-
- `--run-mode
|
|
156
|
+
- `--run-mode thirdfy` -> Thirdfy-governed execute-intent rail (default for `profile=network`)
|
|
156
157
|
|
|
157
158
|
| Goal | Recommended mode | Why |
|
|
158
159
|
| --- | --- | --- |
|
|
160
|
+
| Fast solo managed-wallet trading | `agent_wallet` | Uses `/execution-wallet` + `/execute`; owner `userDid` comes from login config. |
|
|
159
161
|
| BYOW signing and local custody | `self` | Keeps signing and broadcast on your wallet path. |
|
|
160
|
-
|
|
|
162
|
+
| External wallet delegated execution | `hybrid` or `thirdfy` after `delegation create/activate` | Uses Gator/ERC-7710 authority from the user wallet to the Thirdfy/session executor. |
|
|
163
|
+
| Publisher fan-out / subscriber execution | `thirdfy` | Uses delegated governance rail and active `delegation_bindings`. |
|
|
161
164
|
| Self execution plus governance mirror checks | `hybrid` | Preserves local execution while emitting managed-lane mirrors. |
|
|
162
165
|
| Managed wallet primary + governance mirror checks | `hybrid --hybrid-wallet-mode agent_wallet` | Uses managed wallet as primary hybrid path while still running Thirdfy mirror checks. |
|
|
163
166
|
|
|
@@ -166,7 +169,7 @@ Auth expectations by mode:
|
|
|
166
169
|
- `self`: requires execution identity (`agentApiKey`) today; keyless self lane is blocked with deterministic `SELF_OPEN_DISABLED`.
|
|
167
170
|
- `hybrid`: requires execution identity and governed mirror semantics (`--hybrid-wallet-mode self|agent_wallet`, default `self`).
|
|
168
171
|
- `thirdfy`: requires execution identity and delegated governance path.
|
|
169
|
-
- `agent_wallet`: requires execution identity and
|
|
172
|
+
- `agent_wallet`: requires execution identity and owner `userDid`, but does not require subscriber delegation when the owner identity matches the agent wallet/creator.
|
|
170
173
|
- custody mode defaults are profile-aware:
|
|
171
174
|
- `managed` default for `thirdfy`
|
|
172
175
|
- `external` default for `self` and `hybrid`
|
|
@@ -177,7 +180,7 @@ Selection precedence:
|
|
|
177
180
|
1. `--run-mode`
|
|
178
181
|
2. `THIRDFY_RUN_MODE`
|
|
179
182
|
3. saved profile config (`~/.thirdfy/config.json`)
|
|
180
|
-
4. profile default
|
|
183
|
+
4. profile default (`personal=agent_wallet`, `builder=hybrid`, `network=thirdfy`)
|
|
181
184
|
|
|
182
185
|
Profile commands:
|
|
183
186
|
|
|
@@ -189,6 +192,16 @@ thirdfy-agent whoami --json
|
|
|
189
192
|
|
|
190
193
|
`profile use --profile <name>` resets run mode to that profile default unless `--run-mode` is explicitly provided.
|
|
191
194
|
|
|
195
|
+
## Framework Guidance
|
|
196
|
+
|
|
197
|
+
Use the same execution identities across frameworks, but choose the rail by who signs and who the agent acts for:
|
|
198
|
+
|
|
199
|
+
- **Hermes agents:** provision the Thirdfy profile and env before the Hermes dashboard/runtime starts, run `thirdfy-agent doctor auth` and `doctor self` during startup checks, prefer `agent_wallet` for the agent’s own managed wallet, and keep `thirdfy` for publisher/fanout or delegated external-wallet execution.
|
|
200
|
+
- **OpenClaw agents:** store Thirdfy credentials as runtime secrets, call `thirdfy-agent actions --provider <id>` before planning, use `run --run-mode agent_wallet` for own-wallet actions, use `self` only when OWS/local signing is configured, and switch to `hybrid`/`thirdfy` only after delegation readiness is confirmed.
|
|
201
|
+
- **Claude Managed Agents:** prefer MCP `agentRun` / `walletExecute` for solo managed execution, `buildTx` / `walletSign` for BYOW local signing, and `executeIntent` only for subscriber fanout or explicit validation-only mirror previews. The CLI remains useful for smoke checks and local operator recovery.
|
|
202
|
+
|
|
203
|
+
For all frameworks, treat `agentApiKey` as a hidden execution credential in secret storage, keep owner-session tokens short lived, and verify mode requirements with `thirdfy-agent help onboarding --json`.
|
|
204
|
+
|
|
192
205
|
### One-command self execution (BYOW)
|
|
193
206
|
|
|
194
207
|
Use `--broadcast` with `run --run-mode self` to keep one deterministic command for build/sign/send:
|
|
@@ -400,7 +413,7 @@ Managed-self execution performs strict wallet preflight and returns deterministi
|
|
|
400
413
|
## More docs
|
|
401
414
|
|
|
402
415
|
- OpenClaw CLI onboarding flow:
|
|
403
|
-
[docs.thirdfy.com/api/thirdfy-agent-cli#create-an-openclaw-agent-with-the-cli](https://docs.thirdfy.com/api/thirdfy-agent-cli#create-an-openclaw-agent-with-the-cli)
|
|
416
|
+
[docs.thirdfy.com/documentation/api/thirdfy-agent-cli#create-an-openclaw-agent-with-the-cli](https://docs.thirdfy.com/documentation/api/thirdfy-agent-cli#create-an-openclaw-agent-with-the-cli)
|
|
404
417
|
- Creator platform (agent registration and keys):
|
|
405
418
|
[thirdfy.com/creator](https://thirdfy.com/creator)
|
|
406
419
|
|
|
@@ -416,7 +429,7 @@ npm run e2e:managed:delegation:parity
|
|
|
416
429
|
|
|
417
430
|
## Release
|
|
418
431
|
|
|
419
|
-
Version history and user-facing notes: [CHANGELOG.md](./CHANGELOG.md) (for example [v0.1.38](https://github.com/thirdfy/agent-cli/releases/tag/v0.1.38) — npm README + lockfile metadata alignment; [v0.1.37](https://github.com/thirdfy/agent-cli/releases/tag/v0.1.37) — email OTP login, onboarding help, auth doctor, wallet list, masked `whoami`). **Before every version bump or npm publish:** update this **README** (release notes paragraph, quick start, and command groups) so the npm package page matches what ships; keep `CHANGELOG.md`, [command reference](./docs/command-reference.md), and MCP summaries aligned when discovery or onboarding surfaces change.
|
|
432
|
+
Version history and user-facing notes: [CHANGELOG.md](./CHANGELOG.md) (for example [v0.1.39](https://github.com/thirdfy/agent-cli/releases/tag/v0.1.39) — agent-wallet solo default, framework guidance, and delegation UX; [v0.1.38](https://github.com/thirdfy/agent-cli/releases/tag/v0.1.38) — npm README + lockfile metadata alignment; [v0.1.37](https://github.com/thirdfy/agent-cli/releases/tag/v0.1.37) — email OTP login, onboarding help, auth doctor, wallet list, masked `whoami`). **Before every version bump or npm publish:** update this **README** (release notes paragraph, quick start, and command groups) so the npm package page matches what ships; keep `CHANGELOG.md`, [command reference](./docs/command-reference.md), and MCP summaries aligned when discovery or onboarding surfaces change.
|
|
420
433
|
|
|
421
434
|
```bash
|
|
422
435
|
npm version patch
|
package/bin/thirdfy-agent.mjs
CHANGED
|
@@ -43,7 +43,7 @@ const CUSTODY_MODES = ['managed', 'external'];
|
|
|
43
43
|
const EFFECT_CHECK_MODES = ['strict', 'relaxed', 'off'];
|
|
44
44
|
let negotiatedCapabilitiesCache = null;
|
|
45
45
|
const PROFILE_DEFAULTS = {
|
|
46
|
-
personal: '
|
|
46
|
+
personal: 'agent_wallet',
|
|
47
47
|
builder: 'hybrid',
|
|
48
48
|
network: 'thirdfy',
|
|
49
49
|
};
|
|
@@ -113,6 +113,43 @@ function buildTopLevelOnboardingHint(payload) {
|
|
|
113
113
|
return null;
|
|
114
114
|
}
|
|
115
115
|
|
|
116
|
+
function extractAgentApiKey(value) {
|
|
117
|
+
if (!value || typeof value !== 'object') return '';
|
|
118
|
+
const candidates = [
|
|
119
|
+
value.agentApiKey,
|
|
120
|
+
value.apiKey,
|
|
121
|
+
value.data?.agentApiKey,
|
|
122
|
+
value.data?.apiKey,
|
|
123
|
+
value.bootstrap?.agentApiKey,
|
|
124
|
+
value.bootstrap?.apiKey,
|
|
125
|
+
value.registration?.agentApiKey,
|
|
126
|
+
value.registration?.apiKey,
|
|
127
|
+
value.registration?.registration?.agentApiKey,
|
|
128
|
+
value.registration?.registration?.apiKey,
|
|
129
|
+
value.data?.registration?.agentApiKey,
|
|
130
|
+
value.data?.registration?.apiKey,
|
|
131
|
+
value.data?.registration?.registration?.agentApiKey,
|
|
132
|
+
value.data?.registration?.registration?.apiKey,
|
|
133
|
+
];
|
|
134
|
+
return String(candidates.find((candidate) => String(candidate || '').trim()) || '').trim();
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
function extractAgentKey(value) {
|
|
138
|
+
if (!value || typeof value !== 'object') return '';
|
|
139
|
+
const candidates = [
|
|
140
|
+
value.agentKey,
|
|
141
|
+
value.data?.agentKey,
|
|
142
|
+
value.bootstrap?.agentKey,
|
|
143
|
+
value.registration?.agentKey,
|
|
144
|
+
value.registration?.registration?.agentKey,
|
|
145
|
+
value.data?.registration?.agentKey,
|
|
146
|
+
value.data?.registration?.registration?.agentKey,
|
|
147
|
+
value.owner?.creatorWallet,
|
|
148
|
+
value.data?.owner?.creatorWallet,
|
|
149
|
+
];
|
|
150
|
+
return String(candidates.find((candidate) => String(candidate || '').trim()) || '').trim();
|
|
151
|
+
}
|
|
152
|
+
|
|
116
153
|
async function main() {
|
|
117
154
|
const profileState = resolveProfileState(globalFlags);
|
|
118
155
|
context.profile = profileState.profile;
|
|
@@ -224,6 +261,7 @@ async function main() {
|
|
|
224
261
|
await commandTrackAction(context, subFlags, capabilities);
|
|
225
262
|
return;
|
|
226
263
|
case 'delegation create':
|
|
264
|
+
case 'delegation grant':
|
|
227
265
|
await commandDelegationCreate(context, subFlags, capabilities);
|
|
228
266
|
return;
|
|
229
267
|
case 'delegation init-custodial':
|
|
@@ -1432,7 +1470,17 @@ async function commandDelegationCreate(ctx, flags, capabilities) {
|
|
|
1432
1470
|
success,
|
|
1433
1471
|
code: success ? 'DELEGATION_CREATED' : 'DELEGATION_CREATE_FAILED',
|
|
1434
1472
|
message: response?.message || (success ? 'Delegation payload created' : 'Delegation create failed'),
|
|
1435
|
-
data:
|
|
1473
|
+
data: {
|
|
1474
|
+
...response,
|
|
1475
|
+
delegationModel: 'gator_erc7710',
|
|
1476
|
+
nextSteps: success
|
|
1477
|
+
? [
|
|
1478
|
+
'Sign data.delegation with the external wallet or OWS account that owns --owner-address.',
|
|
1479
|
+
'Run `thirdfy-agent delegation activate --agent-key <key> --wallet-address <owner> --session-account-address <delegate> --delegation-manager <manager> --delegation <json> --signature <hex>`.',
|
|
1480
|
+
'Verify with `thirdfy-agent delegation inspect --agent-key <key> --verify`, then use `delegation redeem` or run with --run-mode hybrid|thirdfy for delegated execution.',
|
|
1481
|
+
]
|
|
1482
|
+
: [],
|
|
1483
|
+
},
|
|
1436
1484
|
meta: {
|
|
1437
1485
|
apiBase: ctx.apiBase,
|
|
1438
1486
|
capabilitiesVersion: capabilities?.contractVersion || null,
|
|
@@ -1533,7 +1581,16 @@ async function commandDelegationActivate(ctx, flags, capabilities) {
|
|
|
1533
1581
|
success,
|
|
1534
1582
|
code: success ? 'DELEGATION_ACTIVATED' : 'DELEGATION_ACTIVATE_FAILED',
|
|
1535
1583
|
message: response?.message || (success ? 'Delegation activated' : 'Delegation activate failed'),
|
|
1536
|
-
data:
|
|
1584
|
+
data: {
|
|
1585
|
+
...response,
|
|
1586
|
+
delegationModel: 'gator_erc7710',
|
|
1587
|
+
nextSteps: success
|
|
1588
|
+
? [
|
|
1589
|
+
'Run `thirdfy-agent delegation inspect --agent-key <key> --verify` to confirm period allowance and revocation state.',
|
|
1590
|
+
'Use `thirdfy-agent delegation redeem --run-mode hybrid|thirdfy ...` when Thirdfy/session executor should act under this external-wallet delegation.',
|
|
1591
|
+
]
|
|
1592
|
+
: [],
|
|
1593
|
+
},
|
|
1537
1594
|
meta: {
|
|
1538
1595
|
apiBase: ctx.apiBase,
|
|
1539
1596
|
capabilitiesVersion: capabilities?.contractVersion || null,
|
|
@@ -1854,6 +1911,8 @@ async function commandDeveloperBootstrap(ctx, flags, capabilities) {
|
|
|
1854
1911
|
{ ...registerFlags, __suppressOutput: true },
|
|
1855
1912
|
capabilities
|
|
1856
1913
|
);
|
|
1914
|
+
const agentApiKey = extractAgentApiKey(registerResponse || {});
|
|
1915
|
+
const responseAgentKey = extractAgentKey(registerResponse || {}) || agentKey;
|
|
1857
1916
|
|
|
1858
1917
|
const config = loadProfileConfig();
|
|
1859
1918
|
const next = {
|
|
@@ -1870,6 +1929,13 @@ async function commandDeveloperBootstrap(ctx, flags, capabilities) {
|
|
|
1870
1929
|
bootstrapCompletedAt: new Date().toISOString(),
|
|
1871
1930
|
},
|
|
1872
1931
|
};
|
|
1932
|
+
if (agentApiKey || responseAgentKey) {
|
|
1933
|
+
next.agent = {
|
|
1934
|
+
...(config.agent && typeof config.agent === 'object' ? config.agent : {}),
|
|
1935
|
+
...(agentApiKey ? { apiKey: agentApiKey } : {}),
|
|
1936
|
+
...(responseAgentKey ? { agentKey: responseAgentKey } : {}),
|
|
1937
|
+
};
|
|
1938
|
+
}
|
|
1873
1939
|
persistProfileConfig(next);
|
|
1874
1940
|
if (!flags.__suppressOutput) {
|
|
1875
1941
|
printEnvelope({
|
|
@@ -2134,6 +2200,19 @@ async function commandBootstrapComplete(ctx, flags, capabilities) {
|
|
|
2134
2200
|
capabilities
|
|
2135
2201
|
);
|
|
2136
2202
|
}
|
|
2203
|
+
const registrationAgentApiKey = extractAgentApiKey(registration || {});
|
|
2204
|
+
const registrationAgentKey = extractAgentKey(registration || {}) || agentKey;
|
|
2205
|
+
if (registrationAgentApiKey || registrationAgentKey) {
|
|
2206
|
+
const latest = loadProfileConfig();
|
|
2207
|
+
persistProfileConfig({
|
|
2208
|
+
...latest,
|
|
2209
|
+
agent: {
|
|
2210
|
+
...(latest.agent && typeof latest.agent === 'object' ? latest.agent : {}),
|
|
2211
|
+
...(registrationAgentApiKey ? { apiKey: registrationAgentApiKey } : {}),
|
|
2212
|
+
...(registrationAgentKey ? { agentKey: registrationAgentKey } : {}),
|
|
2213
|
+
},
|
|
2214
|
+
});
|
|
2215
|
+
}
|
|
2137
2216
|
|
|
2138
2217
|
printEnvelope({
|
|
2139
2218
|
success: true,
|
|
@@ -2434,8 +2513,8 @@ async function commandProfileShow(ctx, _flags) {
|
|
|
2434
2513
|
message: 'Profile configuration loaded',
|
|
2435
2514
|
data: {
|
|
2436
2515
|
profile: ctx.profile || config.profile || 'personal',
|
|
2437
|
-
runMode: ctx.runMode || config.runMode || '
|
|
2438
|
-
custodyMode: ctx.custodyMode || config.custodyMode || defaultCustodyModeForRunMode(ctx.runMode || config.runMode || '
|
|
2516
|
+
runMode: ctx.runMode || config.runMode || 'agent_wallet',
|
|
2517
|
+
custodyMode: ctx.custodyMode || config.custodyMode || defaultCustodyModeForRunMode(ctx.runMode || config.runMode || 'agent_wallet'),
|
|
2439
2518
|
profileDefaults: PROFILE_DEFAULTS,
|
|
2440
2519
|
configPath: getProfileConfigPath(),
|
|
2441
2520
|
config,
|
|
@@ -2455,7 +2534,7 @@ function maskSecret(value) {
|
|
|
2455
2534
|
function buildOnboardingHelp(ctx) {
|
|
2456
2535
|
return {
|
|
2457
2536
|
title: 'Thirdfy Agent CLI onboarding',
|
|
2458
|
-
summary: 'Use email OTP, wallet-sign, or existing credentials to create owner auth, wallets, and agent execution credentials.',
|
|
2537
|
+
summary: 'Use email OTP, wallet-sign, or existing credentials to create owner auth, wallets, and hidden agent execution credentials. Fresh solo profiles default to agent_wallet.',
|
|
2459
2538
|
paths: [
|
|
2460
2539
|
{
|
|
2461
2540
|
id: 'email_otp',
|
|
@@ -2466,6 +2545,7 @@ function buildOnboardingHelp(ctx) {
|
|
|
2466
2545
|
'thirdfy-agent login email user@example.com --code <otp> --accept-terms --key-name "My Agent"',
|
|
2467
2546
|
'thirdfy-agent whoami',
|
|
2468
2547
|
'thirdfy-agent managed wallet init --chain-id 8453',
|
|
2548
|
+
'thirdfy-agent preflight --run-mode agent_wallet --action <action> --params <json> --json',
|
|
2469
2549
|
],
|
|
2470
2550
|
},
|
|
2471
2551
|
{
|
|
@@ -2499,6 +2579,21 @@ function buildOnboardingHelp(ctx) {
|
|
|
2499
2579
|
'thirdfy-agent login email user@example.com --code 123456 --accept-terms --ni (place --ni after the subcommand so it is not parsed as a flag value)',
|
|
2500
2580
|
rule: 'Commands must fail fast instead of waiting for prompts when required input is missing.',
|
|
2501
2581
|
},
|
|
2582
|
+
executionModes: {
|
|
2583
|
+
agent_wallet: 'Solo managed wallet execution through /execution-wallet + /execute; no subscriber binding when owner userDid matches.',
|
|
2584
|
+
self: 'BYOW/OWS local signing through build-tx, wallet sign, and wallet submit.',
|
|
2585
|
+
hybrid: 'Primary self or agent_wallet execution with optional Thirdfy mirror validation.',
|
|
2586
|
+
thirdfy: 'Publisher/fanout execute-intent; requires active subscriber/delegation bindings for writes.',
|
|
2587
|
+
externalDelegation: 'Use delegation create/grant -> sign -> activate -> inspect/balance -> redeem for Gator/ERC-7710 external-wallet authority.',
|
|
2588
|
+
},
|
|
2589
|
+
frameworkGuidance: {
|
|
2590
|
+
hermes:
|
|
2591
|
+
'Hermes agents should receive a pre-provisioned Thirdfy profile/env, run doctor auth/self at startup, use agent_wallet for managed solo writes, and keep strategy logic separate from CLI secrets.',
|
|
2592
|
+
openclaw:
|
|
2593
|
+
'OpenClaw agents should keep Thirdfy credentials in runtime secrets, use actions discovery before planning, prefer run --run-mode agent_wallet for own-wallet execution, and use thirdfy only for publisher/fanout.',
|
|
2594
|
+
claudeManagedAgents:
|
|
2595
|
+
'Claude Managed Agents should use MCP agentRun/walletExecute for solo managed execution, walletSign/buildTx for BYOW, and executeIntent only for fanout or mirror previews.',
|
|
2596
|
+
},
|
|
2502
2597
|
apiBase: ctx.apiBase,
|
|
2503
2598
|
};
|
|
2504
2599
|
}
|
|
@@ -2518,14 +2613,15 @@ async function commandWhoami(ctx, _flags) {
|
|
|
2518
2613
|
const auth = config.auth && typeof config.auth === 'object' ? config.auth : {};
|
|
2519
2614
|
const agent = config.agent && typeof config.agent === 'object' ? config.agent : {};
|
|
2520
2615
|
const wallets = config.wallets && typeof config.wallets === 'object' ? config.wallets : {};
|
|
2616
|
+
const runMode = ctx.runMode || config.runMode || 'agent_wallet';
|
|
2521
2617
|
printEnvelope({
|
|
2522
2618
|
success: true,
|
|
2523
2619
|
code: 'WHOAMI_OK',
|
|
2524
2620
|
message: 'Thirdfy CLI identity loaded',
|
|
2525
2621
|
data: {
|
|
2526
2622
|
profile: ctx.profile || config.profile || 'personal',
|
|
2527
|
-
runMode
|
|
2528
|
-
custodyMode: ctx.custodyMode || config.custodyMode || defaultCustodyModeForRunMode(
|
|
2623
|
+
runMode,
|
|
2624
|
+
custodyMode: ctx.custodyMode || config.custodyMode || defaultCustodyModeForRunMode(runMode),
|
|
2529
2625
|
auth: {
|
|
2530
2626
|
source: auth.source || null,
|
|
2531
2627
|
hasAuthToken: Boolean(auth.authToken),
|
|
@@ -2539,6 +2635,7 @@ async function commandWhoami(ctx, _flags) {
|
|
|
2539
2635
|
agentKey: agent.agentKey || null,
|
|
2540
2636
|
},
|
|
2541
2637
|
wallets: {
|
|
2638
|
+
userDid: wallets.userDid || config.identity?.userDid || null,
|
|
2542
2639
|
primaryEvmWallet: wallets.primaryEvmWallet || null,
|
|
2543
2640
|
evm: Array.isArray(wallets.evm) ? wallets.evm : [],
|
|
2544
2641
|
solana: Array.isArray(wallets.solana) ? wallets.solana : [],
|
|
@@ -2571,6 +2668,8 @@ async function commandDoctorAuth(ctx, _flags) {
|
|
|
2571
2668
|
const config = loadProfileConfig();
|
|
2572
2669
|
const auth = config.auth && typeof config.auth === 'object' ? config.auth : {};
|
|
2573
2670
|
const agent = config.agent && typeof config.agent === 'object' ? config.agent : {};
|
|
2671
|
+
const runMode = normalizeRunMode(ctx.runMode || config.runMode || 'agent_wallet');
|
|
2672
|
+
const userDid = resolveEffectiveUserDid({});
|
|
2574
2673
|
const ownerSessionTokenFromEnv = String(process.env.THIRDFY_OWNER_SESSION_TOKEN || '').trim();
|
|
2575
2674
|
const authTokenFromEnv = String(process.env.THIRDFY_AUTH_TOKEN || '').trim();
|
|
2576
2675
|
const agentApiKeyFromEnv = String(process.env.THIRDFY_AGENT_API_KEY || '').trim();
|
|
@@ -2598,6 +2697,14 @@ async function commandDoctorAuth(ctx, _flags) {
|
|
|
2598
2697
|
? 'agent API key from THIRDFY_AGENT_API_KEY'
|
|
2599
2698
|
: 'not stored yet (optional until execution); complete onboarding or bootstrap to add one',
|
|
2600
2699
|
},
|
|
2700
|
+
{
|
|
2701
|
+
name: 'user-did',
|
|
2702
|
+
optional: runMode !== 'agent_wallet',
|
|
2703
|
+
ok: Boolean(userDid),
|
|
2704
|
+
detail: userDid
|
|
2705
|
+
? `user DID available (${userDid})`
|
|
2706
|
+
: 'missing user DID; run `thirdfy-agent login email <email>` or set THIRDFY_USER_DID',
|
|
2707
|
+
},
|
|
2601
2708
|
{
|
|
2602
2709
|
name: 'wallet-profile',
|
|
2603
2710
|
ok: Boolean(config.wallets?.primaryEvmWallet || (Array.isArray(config.wallets?.evm) && config.wallets.evm.length)),
|
|
@@ -2660,7 +2767,7 @@ async function commandLogin(ctx, flags) {
|
|
|
2660
2767
|
const current = loadProfileConfig();
|
|
2661
2768
|
const authToken = String(flags.authToken || process.env.THIRDFY_AUTH_TOKEN || '').trim();
|
|
2662
2769
|
let ownerSessionToken = String(flags.ownerSessionToken || process.env.THIRDFY_OWNER_SESSION_TOKEN || '').trim();
|
|
2663
|
-
const loginRunMode = normalizeRunMode(flags.runMode || current.runMode || process.env.THIRDFY_RUN_MODE || '
|
|
2770
|
+
const loginRunMode = normalizeRunMode(flags.runMode || current.runMode || process.env.THIRDFY_RUN_MODE || 'agent_wallet');
|
|
2664
2771
|
const custodyMode = normalizeCustodyMode(
|
|
2665
2772
|
flags.custodyMode || current.custodyMode || process.env.THIRDFY_CUSTODY_MODE,
|
|
2666
2773
|
loginRunMode
|
|
@@ -2842,10 +2949,10 @@ async function commandLoginEmail(ctx, flags) {
|
|
|
2842
2949
|
acceptTerms: true,
|
|
2843
2950
|
keyName,
|
|
2844
2951
|
agentKey: String(flags.agentKey || '').trim() || undefined,
|
|
2845
|
-
runMode: normalizeRunMode(flags.runMode || current.runMode || process.env.THIRDFY_RUN_MODE || '
|
|
2952
|
+
runMode: normalizeRunMode(flags.runMode || current.runMode || process.env.THIRDFY_RUN_MODE || 'agent_wallet'),
|
|
2846
2953
|
custodyMode: normalizeCustodyMode(
|
|
2847
2954
|
flags.custodyMode || current.custodyMode || process.env.THIRDFY_CUSTODY_MODE,
|
|
2848
|
-
normalizeRunMode(flags.runMode || current.runMode || process.env.THIRDFY_RUN_MODE || '
|
|
2955
|
+
normalizeRunMode(flags.runMode || current.runMode || process.env.THIRDFY_RUN_MODE || 'agent_wallet')
|
|
2849
2956
|
),
|
|
2850
2957
|
bootstrapAgent: !parseBooleanFlag(flags.noBootstrapAgent, false),
|
|
2851
2958
|
rotateAgentKey: parseBooleanFlag(flags.rotateAgentKey, false),
|
|
@@ -2853,15 +2960,17 @@ async function commandLoginEmail(ctx, flags) {
|
|
|
2853
2960
|
const data = response?.data || response || {};
|
|
2854
2961
|
const ownerSessionToken = String(data.ownerSessionToken || '').trim();
|
|
2855
2962
|
const bootstrap = data.bootstrap && typeof data.bootstrap === 'object' ? data.bootstrap : {};
|
|
2856
|
-
const agentApiKey =
|
|
2857
|
-
const agentKey =
|
|
2963
|
+
const agentApiKey = extractAgentApiKey(data);
|
|
2964
|
+
const agentKey = extractAgentKey(data);
|
|
2858
2965
|
const wallets = data.wallets && typeof data.wallets === 'object' ? data.wallets : {};
|
|
2966
|
+
const userDid = String(data.owner?.creatorDid || wallets.userDid || '').trim();
|
|
2859
2967
|
const primaryEvmWallet = String(wallets.primaryEvmWallet || data.owner?.creatorWallet || '').trim();
|
|
2860
2968
|
const persistedWallets = {
|
|
2861
2969
|
...wallets,
|
|
2970
|
+
...(userDid ? { userDid } : {}),
|
|
2862
2971
|
...(primaryEvmWallet ? { primaryEvmWallet } : {}),
|
|
2863
2972
|
};
|
|
2864
|
-
const runMode = normalizeRunMode(flags.runMode || current.runMode || process.env.THIRDFY_RUN_MODE || '
|
|
2973
|
+
const runMode = normalizeRunMode(flags.runMode || current.runMode || process.env.THIRDFY_RUN_MODE || 'agent_wallet');
|
|
2865
2974
|
const custodyMode = normalizeCustodyMode(flags.custodyMode || current.custodyMode || process.env.THIRDFY_CUSTODY_MODE, runMode);
|
|
2866
2975
|
if (!ownerSessionToken) {
|
|
2867
2976
|
throw createCliError('LOGIN_EMAIL_OWNER_SESSION_MISSING', 'Email onboarding completed without an owner session token.');
|
|
@@ -3590,6 +3699,15 @@ function buildBuildTxPayload(flags, resolvedAction) {
|
|
|
3590
3699
|
return payload;
|
|
3591
3700
|
}
|
|
3592
3701
|
|
|
3702
|
+
function resolveEffectiveUserDid(flags) {
|
|
3703
|
+
const explicit = String(flags.userDid || '').trim();
|
|
3704
|
+
if (explicit) return explicit;
|
|
3705
|
+
const envUserDid = String(process.env.THIRDFY_USER_DID || '').trim();
|
|
3706
|
+
if (envUserDid) return envUserDid;
|
|
3707
|
+
const config = loadProfileConfig();
|
|
3708
|
+
return String(config?.wallets?.userDid || config?.identity?.userDid || '').trim();
|
|
3709
|
+
}
|
|
3710
|
+
|
|
3593
3711
|
function buildManagedExecutePayload(flags, options) {
|
|
3594
3712
|
const runMode = String(options.runMode || flags.runMode || 'agent_wallet').trim().toLowerCase();
|
|
3595
3713
|
const params = getPreparedParams(flags);
|
|
@@ -3620,7 +3738,7 @@ function buildManagedExecutePayload(flags, options) {
|
|
|
3620
3738
|
const payload = {
|
|
3621
3739
|
agentApiKey: resolveAgentApiKey(flags, runMode),
|
|
3622
3740
|
action: String(options.resolvedAction || requireFlag(flags, 'action', 'Missing --action')).trim(),
|
|
3623
|
-
userDid:
|
|
3741
|
+
userDid: resolveEffectiveUserDid(flags),
|
|
3624
3742
|
params: managedParams,
|
|
3625
3743
|
chainId: Number(flags.chainId || 8453),
|
|
3626
3744
|
executionStrategy: {
|
|
@@ -3631,7 +3749,7 @@ function buildManagedExecutePayload(flags, options) {
|
|
|
3631
3749
|
if (!payload.userDid) {
|
|
3632
3750
|
throw createCliError(
|
|
3633
3751
|
'MISSING_USER_DID',
|
|
3634
|
-
'Managed wallet execution requires --user-did
|
|
3752
|
+
'Managed wallet execution requires --user-did, THIRDFY_USER_DID, or a saved userDid from `thirdfy-agent login email`.'
|
|
3635
3753
|
);
|
|
3636
3754
|
}
|
|
3637
3755
|
if (flags.estimatedAmountUsd) payload.estimatedAmountUsd = Number(flags.estimatedAmountUsd);
|
|
@@ -3655,13 +3773,13 @@ async function resolveManagedExecutionPreflight(ctx, flags, resolved, options =
|
|
|
3655
3773
|
const chainId = Number(flags.chainId || 8453);
|
|
3656
3774
|
const runMode = String(options.runMode || 'agent_wallet').trim().toLowerCase();
|
|
3657
3775
|
const agentApiKey = resolveAgentApiKey(flags, runMode);
|
|
3658
|
-
const userDid =
|
|
3776
|
+
const userDid = resolveEffectiveUserDid(flags);
|
|
3659
3777
|
if (!userDid) {
|
|
3660
3778
|
return {
|
|
3661
3779
|
success: false,
|
|
3662
3780
|
blockedReason: 'MISSING_USER_DID',
|
|
3663
3781
|
blockedStage: 'routing',
|
|
3664
|
-
error: 'Managed wallet execution requires --user-did
|
|
3782
|
+
error: 'Managed wallet execution requires --user-did, THIRDFY_USER_DID, or a saved userDid from `thirdfy-agent login email`.',
|
|
3665
3783
|
signerMethod: 'managed_wallet_server',
|
|
3666
3784
|
};
|
|
3667
3785
|
}
|
|
@@ -3742,8 +3860,16 @@ function buildIntentPayload(flags, options) {
|
|
|
3742
3860
|
if (flags.catalog) payload.catalog = String(flags.catalog).trim();
|
|
3743
3861
|
if (flags.chainId) payload.chainId = Number(flags.chainId);
|
|
3744
3862
|
if (flags.estimatedAmountUsd) payload.estimatedAmountUsd = Number(flags.estimatedAmountUsd);
|
|
3745
|
-
|
|
3863
|
+
const userDid = resolveEffectiveUserDid(flags);
|
|
3864
|
+
if (flags.userDid || (options.validationOnly && (runMode === 'agent_wallet' || (runMode === 'hybrid' && options.mirrorOnly)))) {
|
|
3865
|
+
if (userDid) payload.userDid = userDid;
|
|
3866
|
+
}
|
|
3746
3867
|
if (options.validationOnly) payload.executionLane = 'validation_only';
|
|
3868
|
+
if (flags.executionScope) {
|
|
3869
|
+
payload.executionScope = String(flags.executionScope).trim();
|
|
3870
|
+
} else if (options.validationOnly && (runMode === 'agent_wallet' || (runMode === 'hybrid' && options.mirrorOnly))) {
|
|
3871
|
+
payload.executionScope = 'solo_owner_mirror';
|
|
3872
|
+
}
|
|
3747
3873
|
if (options.runMode) {
|
|
3748
3874
|
payload.executionStrategy = {
|
|
3749
3875
|
runMode: String(options.runMode),
|