@aiaiai-pt/martha-cli 0.2.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/CHANGELOG.md +22 -0
- package/README.md +127 -0
- package/dist/index.js +16825 -0
- package/package.json +56 -0
- package/skills/martha-cli/SKILL.md +907 -0
|
@@ -0,0 +1,907 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: martha-cli
|
|
3
|
+
description: Complete reference for the Martha CLI. Use this whenever you need to manage Martha definitions (functions, workflows, agents), execute workflows, manage agent tasks, configure tracker integrations (Linear/GitHub/GitLab), upload documents for RAG, handle approvals, manage credentials in Vault, or interact with Martha's AI platform from the command line.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Martha CLI Reference
|
|
7
|
+
|
|
8
|
+
Martha is an AI workflow orchestration platform. Agents, workflows, and tasks run on a Temporal-backed engine. External agent harnesses (ork, Claude Code, CrewAI, Pydantic AI) interact through this CLI. This skill is the operational reference — assume you have shell access and never need `--help`.
|
|
9
|
+
|
|
10
|
+
## Conceptual model
|
|
11
|
+
|
|
12
|
+
Martha has a few distinct primitives that get confused in everyday talk. This is the precise meaning each one has in the platform:
|
|
13
|
+
|
|
14
|
+
| Concept | What it is | When to use |
|
|
15
|
+
|---|---|---|
|
|
16
|
+
| **Tenant** | Opaque data isolation boundary (`tenant_id` string). All queries filter by this. Set from the JWT, never from request body. | Every entity is tenant-scoped. You don't pass it explicitly to the CLI — it comes from your token. |
|
|
17
|
+
| **Client** | A chat-API consumer (web app, SMS sender, voice line). Has `keycloak_client_id`, `system_prompts`, allowlists. **Not a tenant.** | Use when you're configuring how a frontend or messaging channel talks to Martha. |
|
|
18
|
+
| **Agent** | An `AgentDefinition` row: prompt + LLM config + loop config + tool grants. Cloud or external. | Cloud agents are run by Martha (Temporal). External agents are remote harnesses that authenticate and execute Martha tasks. |
|
|
19
|
+
| **Team** | A named group of agents with a routing strategy (`round_robin`, `manual`, `external`). | Use to spread work across many similar agents (e.g. 5 ork instances doing code review). |
|
|
20
|
+
| **Task** | A unit of work with goal, priority, lifecycle (`open`/`claimed`/`running`/`completed`/`failed`/`cancelled`/`stale`/`poisoned`). Optionally linked to a tracker issue. | Use to queue async work for agents. Humans or agents can create them. |
|
|
21
|
+
| **Function** | An HTTP endpoint or platform Python callable that an agent can invoke as a tool. Stored as `FunctionDefinition`. | Define once, grant to many agents. |
|
|
22
|
+
| **Workflow** | A graph of nodes (LLM, function, choice, parallel, agent_loop, etc.) executed by Temporal. | Multi-step pipelines that an agent or human triggers. |
|
|
23
|
+
| **Connection** | A stored credential for an integration (tracker, OpenAPI service). Auth values live in Vault, not in DB. | One per (tenant, integration_name, name). Used by tracker adapters and HTTP function execution. |
|
|
24
|
+
| **Tracker** | Built-in adapter for an external issue tracker (`linear`, `github`, `gitlab`). Bidirectional sync with Martha tasks. | Configure once per tenant, then tasks can carry `external_ref` + `tracker_type`. |
|
|
25
|
+
| **Trigger** | Event-driven workflow/function dispatch. Listens for `event_type`, optionally filters, then fires a target. | Wire `task.completed` → notify, or `webhook.received` → sync inbound. |
|
|
26
|
+
| **Webhook** | Inbound endpoint with per-webhook HMAC secret. Emits `webhook.received` events that triggers consume. | Receive callbacks from external services (trackers, payment processors, etc.). |
|
|
27
|
+
| **Approval** | Human-in-the-loop pause point inside a workflow. | Use the `approval_gate` workflow node when you need a human OK before continuing. |
|
|
28
|
+
|
|
29
|
+
Everything below operates on these primitives. When in doubt, run `martha status` to confirm tenant + auth before issuing commands.
|
|
30
|
+
|
|
31
|
+
---
|
|
32
|
+
|
|
33
|
+
## Global Options (every command)
|
|
34
|
+
|
|
35
|
+
| Flag | Purpose |
|
|
36
|
+
|---|---|
|
|
37
|
+
| `--json` | Machine-readable JSON output. **Always set this when piping to `jq` or parsing.** Without it, output is human-formatted and may include color codes. |
|
|
38
|
+
| `--profile <name>` | Use a named profile from `~/.martha/profiles/`. Default profile is `default`. |
|
|
39
|
+
| `--api-url <url>` | Override `MARTHA_API_URL`. Useful for hitting staging/prod from the same shell. |
|
|
40
|
+
| `--verbose` | DEBUG-level logging on stderr. Shows HTTP requests, retry attempts, token expiry decisions. |
|
|
41
|
+
| `--quiet` | Suppress informational output. Errors still print. |
|
|
42
|
+
| `--yes` | Skip confirmation prompts. Required in non-TTY contexts (CI, agents). **The CLI does NOT silently cancel on no-TTY** — it errors with `use --yes`. |
|
|
43
|
+
|
|
44
|
+
**Exit codes:** `0` success · `1` generic error · `2` auth failure · `3` not found · `4` validation · `5` conflict (409)
|
|
45
|
+
|
|
46
|
+
---
|
|
47
|
+
|
|
48
|
+
## Authentication
|
|
49
|
+
|
|
50
|
+
The CLI resolves credentials in this priority order. The first non-empty wins:
|
|
51
|
+
|
|
52
|
+
1. `MARTHA_TOKEN` — raw JWT, bypasses all login flows. Highest priority.
|
|
53
|
+
2. `MARTHA_CLIENT_ID` + `MARTHA_CLIENT_SECRET` — OAuth2 client credentials, auto-refreshes.
|
|
54
|
+
3. Profile-stored token from prior `martha auth login` (cached at `~/.martha/profiles/<name>.json`).
|
|
55
|
+
4. Browser-based OIDC (interactive only, won't fire in non-TTY).
|
|
56
|
+
|
|
57
|
+
```bash
|
|
58
|
+
# Headless login (humans, scripts)
|
|
59
|
+
martha auth login --username admin --password admin123
|
|
60
|
+
|
|
61
|
+
# Service account (agents, CI)
|
|
62
|
+
export MARTHA_CLIENT_ID="martha-agent-<uuid>"
|
|
63
|
+
export MARTHA_CLIENT_SECRET="..."
|
|
64
|
+
martha auth login --service-account
|
|
65
|
+
|
|
66
|
+
# Browser flow (local dev)
|
|
67
|
+
martha auth login
|
|
68
|
+
|
|
69
|
+
# Inspect current state
|
|
70
|
+
martha auth status # profile, user, tenant, expiry, roles
|
|
71
|
+
martha auth token # raw JWT to stdout (for piping into curl)
|
|
72
|
+
martha auth logout
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
**Token lifecycle:** Service-account tokens auto-refresh ~30s before expiry. Human tokens expire after ~5min and require re-login (or use `MARTHA_TOKEN` for long-running scripts).
|
|
76
|
+
|
|
77
|
+
**Troubleshooting:**
|
|
78
|
+
- `2 auth failure` on first call → token missing or expired. Run `martha auth status` to confirm, then re-login.
|
|
79
|
+
- `403 forbidden` → authenticated but lacking the required role. Most admin endpoints need `admin` realm role; executor endpoints accept any authenticated user.
|
|
80
|
+
- `401 unauthorized` despite valid token → check `MARTHA_API_URL` matches the Keycloak realm the token was issued for.
|
|
81
|
+
|
|
82
|
+
---
|
|
83
|
+
|
|
84
|
+
## Definitions: functions, workflows, agents
|
|
85
|
+
|
|
86
|
+
Definitions are the durable building blocks. They're versioned (every update creates a `DefinitionVersion` row), tenant-scoped, and rollback-able.
|
|
87
|
+
|
|
88
|
+
### Declarative Apply (preferred for IaC-style workflows)
|
|
89
|
+
|
|
90
|
+
```bash
|
|
91
|
+
martha definitions apply -f definitions.yaml [--dry-run] [--yes]
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
Reads YAML or JSON. Multi-document files (separated by `---`) are processed in order. The CLI **never deletes** — to remove a definition, use the per-kind `delete` command.
|
|
95
|
+
|
|
96
|
+
```yaml
|
|
97
|
+
kind: Function
|
|
98
|
+
name: get-weather
|
|
99
|
+
description: Fetches current weather by city name
|
|
100
|
+
definition:
|
|
101
|
+
endpoint: https://api.weather.com/current
|
|
102
|
+
http_method: GET
|
|
103
|
+
parameters:
|
|
104
|
+
city:
|
|
105
|
+
type: string
|
|
106
|
+
required: true
|
|
107
|
+
location: query
|
|
108
|
+
auth:
|
|
109
|
+
scheme: bearer
|
|
110
|
+
credential_source: weather_api
|
|
111
|
+
---
|
|
112
|
+
kind: Workflow
|
|
113
|
+
name: morning-briefing
|
|
114
|
+
description: Get weather + headlines, summarize
|
|
115
|
+
definition:
|
|
116
|
+
nodes:
|
|
117
|
+
- id: weather
|
|
118
|
+
type: function
|
|
119
|
+
config: { function_name: get-weather, inputs: { city: "{{user.city}}" } }
|
|
120
|
+
- id: summarize
|
|
121
|
+
type: llm
|
|
122
|
+
config: { prompt: "Summarize for a morning briefing: {{steps.weather.output}}" }
|
|
123
|
+
edges:
|
|
124
|
+
- { source: weather, target: summarize }
|
|
125
|
+
---
|
|
126
|
+
kind: Agent
|
|
127
|
+
name: briefing-agent
|
|
128
|
+
description: Sends a daily morning briefing
|
|
129
|
+
agent_type: cloud
|
|
130
|
+
auth_method: service_account # Slice 3B: provisions Keycloak SA on create
|
|
131
|
+
system_prompt: "You write friendly morning briefings."
|
|
132
|
+
llm_config: { provider: anthropic, model: claude-sonnet-4-5-20250929 }
|
|
133
|
+
loop_config: { enabled: true, max_iterations: 5 }
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
`--dry-run` prints what would change without writing. `--yes` skips the confirmation when applying to a non-empty tenant.
|
|
137
|
+
|
|
138
|
+
### Export (back up or migrate)
|
|
139
|
+
|
|
140
|
+
```bash
|
|
141
|
+
martha definitions export [--format yaml|json] [--output FILE] [--inactive]
|
|
142
|
+
martha definitions export --functions-only
|
|
143
|
+
martha definitions export --workflows-only
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
### Stats
|
|
147
|
+
|
|
148
|
+
```bash
|
|
149
|
+
martha definitions stats # Counts by kind and source (manual/openapi/plugin)
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
### Functions CRUD
|
|
153
|
+
|
|
154
|
+
```bash
|
|
155
|
+
martha functions list [--source manual|openapi|platform|plugin] [--tag TAG] [--inactive] [--limit 50]
|
|
156
|
+
martha functions get <name> # Full definition + auth + extra_headers
|
|
157
|
+
martha functions create -f definition.yaml
|
|
158
|
+
martha functions update <name> -f definition.yaml # Bumps version
|
|
159
|
+
martha functions delete <name> [--hard] [--yes] # Soft delete by default; --hard purges
|
|
160
|
+
martha functions versions <name> # Version history with timestamps
|
|
161
|
+
martha functions rollback <name> <version> # Restore prior version (creates new version)
|
|
162
|
+
martha functions export [--format yaml|json] [--output FILE]
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
Function `auth` field is the most error-prone part — see the [Function authentication patterns](#function-authentication-patterns) section below.
|
|
166
|
+
|
|
167
|
+
### Workflows CRUD + Execution
|
|
168
|
+
|
|
169
|
+
```bash
|
|
170
|
+
martha workflows list [--inactive] [--limit 50]
|
|
171
|
+
martha workflows get <name> # Full graph + variables + format_version
|
|
172
|
+
martha workflows create -f definition.yaml
|
|
173
|
+
martha workflows update <name> -f definition.yaml
|
|
174
|
+
martha workflows delete <name> [--hard] [--yes]
|
|
175
|
+
martha workflows versions <name>
|
|
176
|
+
martha workflows rollback <name> <version>
|
|
177
|
+
martha workflows export [--format yaml|json] [--output FILE]
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
**Execution:**
|
|
181
|
+
|
|
182
|
+
```bash
|
|
183
|
+
# Fire and forget
|
|
184
|
+
martha workflows execute <name> --inputs '{"key": "value"}'
|
|
185
|
+
|
|
186
|
+
# Wait for completion (blocks up to --timeout seconds, default 300)
|
|
187
|
+
martha workflows execute <name> --inputs '{"key": "value"}' --wait
|
|
188
|
+
|
|
189
|
+
# Wait + stream node-by-node progress to stdout
|
|
190
|
+
martha workflows execute <name> --inputs '{"key": "value"}' --wait --follow
|
|
191
|
+
|
|
192
|
+
# Inspect a running execution
|
|
193
|
+
martha workflows execution <execution-id> # Snapshot
|
|
194
|
+
martha workflows execution <execution-id> --follow # Live stream
|
|
195
|
+
|
|
196
|
+
# List recent executions
|
|
197
|
+
martha workflows executions [--status running|completed|failed] [--workflow <name>] [--limit 20]
|
|
198
|
+
|
|
199
|
+
# Cancel a running execution (signals Temporal workflow)
|
|
200
|
+
martha workflows cancel <execution-id>
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
**Inspection (don't memorize, query the system):**
|
|
204
|
+
|
|
205
|
+
```bash
|
|
206
|
+
martha workflows inputs <name> # Inputs the workflow expects, with types
|
|
207
|
+
martha workflows nodes <name> # All nodes + edges, useful for debugging
|
|
208
|
+
martha workflows node-types # Available node types you can use
|
|
209
|
+
martha workflows node-type <type> # Schema for a specific node type (e.g. agent_loop)
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
**Project a workflow as portable self-guidance (Phase 2 / Slice 2B):**
|
|
213
|
+
|
|
214
|
+
When an external harness (ork, Claude Code, CrewAI, Pydantic AI) wants to *use* a Martha workflow as a recipe — without Martha being in the loop at execution time — project it to a portable format:
|
|
215
|
+
|
|
216
|
+
```bash
|
|
217
|
+
# JSON plan: topologically ordered nodes + execute_on hints + graph structure
|
|
218
|
+
martha workflows project <name> # default --format=plan
|
|
219
|
+
martha workflows project <name> --format=plan -o plan.json
|
|
220
|
+
|
|
221
|
+
# Markdown run-book: numbered steps with prompts inlined
|
|
222
|
+
martha workflows project <name> --format=skill # writes to stdout
|
|
223
|
+
martha workflows project <name> --format=skill -o skill.md
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
**Plan format** is for programmatic agents. Each node carries `id`, `type`, `execute_on` (hint only: `martha` = a server-side tool like a function; `local` = the agent runs it), `depends_on`, and — when known — `input_schema` / `output_schema`. Structural `start`/`end` nodes are filtered out; `edges` is preserved. No CLI commands are emitted — the projection is guidance, not a script.
|
|
227
|
+
|
|
228
|
+
**Skill format** is Markdown, suitable for embedding in an LLM prompt or dropping into a Claude Code / ork skill directory. Numbered steps with LLM prompts inlined for `local` steps and Martha function names surfaced for `martha` steps.
|
|
229
|
+
|
|
230
|
+
**How to execute a projected workflow:** the agent chooses. Common options:
|
|
231
|
+
- **Run it yourself end-to-end**: use your own LLM for `local` steps; for `martha` steps, either call the underlying function as a tool (`martha functions call <name> --args …`) or implement an equivalent in your runtime.
|
|
232
|
+
- **Delegate the whole thing**: skip the projection entirely and `martha workflows execute <name> --inputs '…'` — Martha runs it cloud-side and you just read the outcome.
|
|
233
|
+
- **Mix and match**: follow the skill for structure but call Martha functions where useful.
|
|
234
|
+
|
|
235
|
+
Default `execute_on` mapping: `llm` → `local`; `function` / `wait` / `transform` / `agent_loop` / `choice` → `martha`. Override per node by setting `execute_on` in the node config.
|
|
236
|
+
|
|
237
|
+
> **Naming note:** the spec called this command `workflows export <name>`, but `workflows export` was already taken by the bulk-YAML export alias. The Phase 2 command is `project` instead.
|
|
238
|
+
|
|
239
|
+
### Agents CRUD + Provisioning + Access
|
|
240
|
+
|
|
241
|
+
```bash
|
|
242
|
+
martha agents list [--inactive] [--limit 50]
|
|
243
|
+
martha agents get <name> # Includes auth_method, status, llm_config, granted functions/workflows
|
|
244
|
+
martha agents create --name <n> --model <m> --prompt "system prompt" \
|
|
245
|
+
[--description "..."] [--temperature 0.7] [--max-tokens 4096] \
|
|
246
|
+
[--type cloud|external] [--auth service-account|api-key] \
|
|
247
|
+
[--tags code-review,python] [--local-tools filesystem,git]
|
|
248
|
+
martha agents update <name> [--model <m>] [--prompt "..."] [--description "..."]
|
|
249
|
+
martha agents delete <name> [--hard] [--yes]
|
|
250
|
+
martha agents versions <name>
|
|
251
|
+
martha agents rollback <name> <version>
|
|
252
|
+
```
|
|
253
|
+
|
|
254
|
+
**Auth provisioning (Slice 3B — unified):**
|
|
255
|
+
|
|
256
|
+
```bash
|
|
257
|
+
# Provision auth for the first time, switch methods, or rotate credentials
|
|
258
|
+
martha agents provision-auth <agent> --method service-account
|
|
259
|
+
martha agents provision-auth <agent> --method api-key
|
|
260
|
+
|
|
261
|
+
# Backward-compat alias for provision-auth --method=api-key
|
|
262
|
+
martha agents generate-key <agent>
|
|
263
|
+
```
|
|
264
|
+
|
|
265
|
+
`provision-auth` shows credentials **once** — copy them immediately. Output is `Cache-Control: no-store` and never logs the secret.
|
|
266
|
+
|
|
267
|
+
| Method | Returns | Use when |
|
|
268
|
+
|---|---|---|
|
|
269
|
+
| `service-account` | `client_id` + `client_secret` (export to `MARTHA_CLIENT_ID`/`MARTHA_CLIENT_SECRET`) | Long-running agents that need token refresh. Recommended. |
|
|
270
|
+
| `api-key` | Single `martha_ak_...` token (export to `MARTHA_TOKEN`) | One-shot scripts, CI jobs, debugging. |
|
|
271
|
+
|
|
272
|
+
**Function grants:**
|
|
273
|
+
|
|
274
|
+
```bash
|
|
275
|
+
martha agents add-function <agent> <function> [--set key=value] # config_overrides
|
|
276
|
+
martha agents remove-function <agent> <function>
|
|
277
|
+
martha agents functions <agent> # List granted functions
|
|
278
|
+
```
|
|
279
|
+
|
|
280
|
+
---
|
|
281
|
+
|
|
282
|
+
## Tasks: queue + executor lifecycle
|
|
283
|
+
|
|
284
|
+
Tasks are the unit of async work. Two flows operate on the same `tasks` table from different angles:
|
|
285
|
+
|
|
286
|
+
- **Admin flow:** humans (or agents acting as humans) create, monitor, cancel tasks
|
|
287
|
+
- **Executor flow:** external agents poll, claim, heartbeat, complete
|
|
288
|
+
|
|
289
|
+
### Admin commands
|
|
290
|
+
|
|
291
|
+
```bash
|
|
292
|
+
martha tasks list [--status open|claimed|running|completed|failed|cancelled|stale|poisoned] \
|
|
293
|
+
[--agent <id>] [--priority low|medium|high|urgent] [--search QUERY] [--limit 50]
|
|
294
|
+
martha tasks stats # Counts per status
|
|
295
|
+
|
|
296
|
+
martha tasks create --agent <agent-id> --goal "instruction text" \
|
|
297
|
+
[--title "label"] [--priority medium] [--context '{"key":"val"}'] \
|
|
298
|
+
[--team <team-id>] [--assigned-agent <agent-id>] [--sticky] \
|
|
299
|
+
[--lease-timeout 600] [--max-retries 3]
|
|
300
|
+
|
|
301
|
+
martha tasks view <id> # Full task: outcome, error, executor, lease, tracker info
|
|
302
|
+
martha tasks cancel <id> # Transitions to cancelled; signals Temporal workflow if running
|
|
303
|
+
```
|
|
304
|
+
|
|
305
|
+
**Team routing:**
|
|
306
|
+
- Without `--team`/`--assigned-agent` → task lives in tenant queue, any agent can claim
|
|
307
|
+
- `--team <id>` → routes via team's `routing_strategy` (round_robin assigns to next active member at create time)
|
|
308
|
+
- `--assigned-agent <id>` → pinned to that agent; only it can claim
|
|
309
|
+
- `--sticky` → assignment survives the agent going offline (default: re-route on offline)
|
|
310
|
+
|
|
311
|
+
**Tracker linkage (Phase 4):**
|
|
312
|
+
Tasks created from a tracker webhook automatically get `external_ref` (e.g. `ENG-423`) and `tracker_type` (`linear`/`github`/`gitlab`). On status changes, the auto-provisioned trigger pushes back to the tracker. To create a tracker-linked task manually, use the API directly — there's no `--tracker-ref` flag yet.
|
|
313
|
+
|
|
314
|
+
### Executor commands (the agent side)
|
|
315
|
+
|
|
316
|
+
```bash
|
|
317
|
+
# 1a. Discover available work — ordered by priority then age (polling)
|
|
318
|
+
martha tasks poll [--agent <id>] [--limit 10]
|
|
319
|
+
|
|
320
|
+
# 1b. Long-poll via SSE — connection stays open, prints one event per assignment
|
|
321
|
+
# (Phase 2 / Slice 2A). Survives reconnect via Last-Event-ID; sends
|
|
322
|
+
# ": keepalive" comment heartbeats every ~5s so intermediaries don't drop.
|
|
323
|
+
martha tasks watch --team <name-or-id> [--json] [--max-reconnects N]
|
|
324
|
+
|
|
325
|
+
# 2. Atomic claim — sets executor_id from your auth context, starts lease
|
|
326
|
+
martha tasks claim <id> [--lease-timeout 300]
|
|
327
|
+
|
|
328
|
+
# 3. Heartbeat — renew lease, optionally save checkpoint
|
|
329
|
+
martha tasks heartbeat <id>
|
|
330
|
+
martha tasks heartbeat <id> --checkpoint '{"step": 3, "progress": 0.75}'
|
|
331
|
+
|
|
332
|
+
# 4. Report completion
|
|
333
|
+
martha tasks complete <id> --outcome '{"result": "done", "files_changed": ["src/foo.py"]}'
|
|
334
|
+
```
|
|
335
|
+
|
|
336
|
+
**Claim lifecycle:** `open` → `claimed` (after `claim`) → `running` (when work starts; auto-set on first heartbeat after claim) → `completed`
|
|
337
|
+
|
|
338
|
+
**Lease semantics:**
|
|
339
|
+
- Default lease: 300s (5min). Override with `--lease-timeout` on claim.
|
|
340
|
+
- Heartbeat **before `lease_expires_at`** or the reaper marks task `stale` and re-queues.
|
|
341
|
+
- Recommended cadence: heartbeat every 30–60s.
|
|
342
|
+
- Heartbeats from a non-executor return 403. Heartbeats after lease expiry return 409.
|
|
343
|
+
|
|
344
|
+
**Checkpoint resume:**
|
|
345
|
+
If `martha tasks claim <id>` returns a task with `retry_count > 0` and a `last_checkpoint`, a previous executor failed mid-work. Resume from the checkpoint instead of starting over. Inspect:
|
|
346
|
+
```bash
|
|
347
|
+
martha tasks view <id> --json | jq '.last_checkpoint, .retry_count'
|
|
348
|
+
```
|
|
349
|
+
|
|
350
|
+
**Error handling cheat sheet:**
|
|
351
|
+
|
|
352
|
+
| Code | Meaning | Action |
|
|
353
|
+
|---|---|---|
|
|
354
|
+
| `409 Conflict` on claim | Task already claimed | Skip and try the next polled task |
|
|
355
|
+
| `409 Conflict` on heartbeat | Lease expired | Stop work, do NOT complete (another agent will pick it up) |
|
|
356
|
+
| `403 Forbidden` on heartbeat | You're not the executor | Stop work |
|
|
357
|
+
| `404 Not Found` | Task was hard-deleted | Stop work |
|
|
358
|
+
| `423 Locked` | Concurrent claim attempt in flight | Retry once after 100ms |
|
|
359
|
+
|
|
360
|
+
### Standard executor loop
|
|
361
|
+
|
|
362
|
+
Two flavours — pick polling for simplicity or `watch` for low-latency assignment:
|
|
363
|
+
|
|
364
|
+
**Polling (works on any team / unassigned pool):**
|
|
365
|
+
|
|
366
|
+
```bash
|
|
367
|
+
#!/usr/bin/env bash
|
|
368
|
+
set -euo pipefail
|
|
369
|
+
export MARTHA_TOKEN="${MARTHA_TOKEN:?need token}"
|
|
370
|
+
|
|
371
|
+
while true; do
|
|
372
|
+
TASK_JSON=$(martha tasks poll --json --limit 1 | jq -c '.[0] // empty')
|
|
373
|
+
if [ -z "$TASK_JSON" ]; then
|
|
374
|
+
sleep 10
|
|
375
|
+
continue
|
|
376
|
+
fi
|
|
377
|
+
|
|
378
|
+
TASK_ID=$(echo "$TASK_JSON" | jq -r '.id')
|
|
379
|
+
GOAL=$(echo "$TASK_JSON" | jq -r '.goal')
|
|
380
|
+
|
|
381
|
+
# Atomic claim — bail if someone else got it
|
|
382
|
+
if ! martha tasks claim "$TASK_ID" --json >/dev/null 2>&1; then
|
|
383
|
+
continue
|
|
384
|
+
fi
|
|
385
|
+
|
|
386
|
+
# Background heartbeat every 30s for the duration
|
|
387
|
+
( while sleep 30; do martha tasks heartbeat "$TASK_ID" --json >/dev/null || break; done ) &
|
|
388
|
+
HB_PID=$!
|
|
389
|
+
|
|
390
|
+
# Do the work — replace with your agent's actual logic
|
|
391
|
+
RESULT=$(echo "Working on: $GOAL" | your-agent-here)
|
|
392
|
+
|
|
393
|
+
kill $HB_PID 2>/dev/null || true
|
|
394
|
+
martha tasks complete "$TASK_ID" --json --outcome "$(jq -n --arg r "$RESULT" '{result: $r}')"
|
|
395
|
+
done
|
|
396
|
+
```
|
|
397
|
+
|
|
398
|
+
**SSE watch (Phase 2 / Slice 2A — for low-latency, real-time tracker-driven work):**
|
|
399
|
+
|
|
400
|
+
```bash
|
|
401
|
+
#!/usr/bin/env bash
|
|
402
|
+
set -euo pipefail
|
|
403
|
+
export MARTHA_TOKEN="${MARTHA_TOKEN:?need token}"
|
|
404
|
+
|
|
405
|
+
# `watch` writes one JSON line per assignment to stdout; reconnects automatically
|
|
406
|
+
# with Last-Event-ID so no work is missed across transient network drops.
|
|
407
|
+
martha tasks watch --team code-review --json | while IFS= read -r LINE; do
|
|
408
|
+
TYPE=$(echo "$LINE" | jq -r '.type // empty')
|
|
409
|
+
[ "$TYPE" = "task.assigned" ] || continue
|
|
410
|
+
TASK_ID=$(echo "$LINE" | jq -r '.data.id')
|
|
411
|
+
|
|
412
|
+
if ! martha tasks claim "$TASK_ID" --json >/dev/null 2>&1; then
|
|
413
|
+
# Lost the race to another team member — keep watching
|
|
414
|
+
continue
|
|
415
|
+
fi
|
|
416
|
+
|
|
417
|
+
( while sleep 30; do martha tasks heartbeat "$TASK_ID" --json >/dev/null || break; done ) &
|
|
418
|
+
HB_PID=$!
|
|
419
|
+
|
|
420
|
+
RESULT=$(your-agent-here "$TASK_ID")
|
|
421
|
+
|
|
422
|
+
kill $HB_PID 2>/dev/null || true
|
|
423
|
+
martha tasks complete "$TASK_ID" --json --outcome "$(jq -n --arg r "$RESULT" '{result: $r}')"
|
|
424
|
+
done
|
|
425
|
+
```
|
|
426
|
+
|
|
427
|
+
**When to choose which:**
|
|
428
|
+
- **Polling**: simplest; tolerates seconds of latency; works without a team scope.
|
|
429
|
+
- **Watch**: best for trackers (Linear/GitHub webhooks → instant assignment) and high-throughput teams. Requires `--team`. Server enforces team membership for agent service accounts.
|
|
430
|
+
|
|
431
|
+
---
|
|
432
|
+
|
|
433
|
+
## Teams
|
|
434
|
+
|
|
435
|
+
Teams group agents and define how incoming work is distributed.
|
|
436
|
+
|
|
437
|
+
```bash
|
|
438
|
+
martha teams create --name "code-review" [--description "..."] [--routing round_robin|manual|external]
|
|
439
|
+
martha teams list
|
|
440
|
+
martha teams view <name-or-id> # Members, current task counts, routing config
|
|
441
|
+
martha teams update <name-or-id> [--name "..."] [--routing manual]
|
|
442
|
+
martha teams delete <name-or-id> [--yes]
|
|
443
|
+
martha teams add-member <team> <agent-name> [--role member|lead]
|
|
444
|
+
martha teams remove-member <team> <agent-id>
|
|
445
|
+
```
|
|
446
|
+
|
|
447
|
+
**Routing strategies:**
|
|
448
|
+
|
|
449
|
+
| Strategy | Behavior | Best for |
|
|
450
|
+
|---|---|---|
|
|
451
|
+
| `manual` | Admin sets `assigned_agent_id` explicitly | Heterogeneous teams, deliberate routing |
|
|
452
|
+
| `round_robin` | Auto-assigns to next active member at task creation | Homogeneous worker pools (5x ork instances) |
|
|
453
|
+
| `external` | Tasks land in queue; external orchestrator (e.g. Linear, GitHub Actions) decides who claims | Tracker-driven workflows |
|
|
454
|
+
|
|
455
|
+
**Setting up an external agent on a team:**
|
|
456
|
+
|
|
457
|
+
```bash
|
|
458
|
+
# 1. Create the agent definition
|
|
459
|
+
martha agents create --name ork-worker-1 --type external --description "ork instance #1" \
|
|
460
|
+
--tags code-review,refactoring
|
|
461
|
+
|
|
462
|
+
# 2. Provision auth — service account is recommended for long-running agents
|
|
463
|
+
martha agents provision-auth ork-worker-1 --method service-account
|
|
464
|
+
# Copy client_id + client_secret immediately
|
|
465
|
+
|
|
466
|
+
# 3. Create the team if it doesn't exist
|
|
467
|
+
martha teams create --name code-review --routing round_robin
|
|
468
|
+
|
|
469
|
+
# 4. Add the agent to the team
|
|
470
|
+
martha teams add-member code-review ork-worker-1
|
|
471
|
+
|
|
472
|
+
# 5. On the agent host
|
|
473
|
+
export MARTHA_CLIENT_ID="<client_id>"
|
|
474
|
+
export MARTHA_CLIENT_SECRET="<client_secret>"
|
|
475
|
+
martha auth login --service-account
|
|
476
|
+
martha tasks poll --json
|
|
477
|
+
```
|
|
478
|
+
|
|
479
|
+
---
|
|
480
|
+
|
|
481
|
+
## Connections + Vault credentials
|
|
482
|
+
|
|
483
|
+
A **Connection** is a stored credential record for an integration. Auth values live in HashiCorp Vault keyed by `(scope, scope_id, service_name)`. The DB only has metadata. Tracker connections include adapter-specific config (team_id, repository, project_id) stored as a flat dict in Vault — `resolve_connection_config()` is the single merge point for tracker adapter calls.
|
|
484
|
+
|
|
485
|
+
The CLI doesn't yet have a top-level `connections` subcommand — manage them via the admin UI at `/settings` (Trackers tab) or via the API:
|
|
486
|
+
|
|
487
|
+
```bash
|
|
488
|
+
# List connections (uses your token's tenant_id scope)
|
|
489
|
+
curl -s -H "Authorization: Bearer $(martha auth token)" \
|
|
490
|
+
"$MARTHA_API_URL/api/admin/connections" | jq
|
|
491
|
+
|
|
492
|
+
# List available tracker adapters + their config_schema
|
|
493
|
+
curl -s -H "Authorization: Bearer $(martha auth token)" \
|
|
494
|
+
"$MARTHA_API_URL/api/admin/trackers" | jq
|
|
495
|
+
|
|
496
|
+
# Create a Linear connection (full config dict in JSON, stored as one Vault entry)
|
|
497
|
+
curl -s -X POST -H "Authorization: Bearer $(martha auth token)" \
|
|
498
|
+
-H "Content-Type: application/json" \
|
|
499
|
+
-d '{
|
|
500
|
+
"integration_name": "linear",
|
|
501
|
+
"name": "production",
|
|
502
|
+
"auth_type": "api_key",
|
|
503
|
+
"credential_value": "{\"api_key\":\"lin_api_xxx\",\"team_id\":\"uuid-here\"}",
|
|
504
|
+
"is_default": true
|
|
505
|
+
}' \
|
|
506
|
+
"$MARTHA_API_URL/api/admin/connections"
|
|
507
|
+
|
|
508
|
+
# Test a connection (calls adapter.test_connection() with merged config)
|
|
509
|
+
curl -s -X POST -H "Authorization: Bearer $(martha auth token)" \
|
|
510
|
+
"$MARTHA_API_URL/api/admin/connections/<connection-id>/test" | jq
|
|
511
|
+
|
|
512
|
+
# Delete (also removes from Vault and deprovisions tracker triggers if applicable)
|
|
513
|
+
curl -s -X DELETE -H "Authorization: Bearer $(martha auth token)" \
|
|
514
|
+
"$MARTHA_API_URL/api/admin/connections/<connection-id>"
|
|
515
|
+
```
|
|
516
|
+
|
|
517
|
+
**Tracker connection auto-provisioning:** When you create a connection where `integration_name` matches a tracker (`linear`/`github`/`gitlab`), the backend automatically provisions:
|
|
518
|
+
1. A `webhook_definition` (returns one-time `webhook_url` + `webhook_secret` in the create response)
|
|
519
|
+
2. Three trigger definitions (managed by `tracker:{type}`):
|
|
520
|
+
- `sync-task-status-{type}` — fires on `task.*` events with `external_ref` set
|
|
521
|
+
- `sync-task-activity-{type}` — fires on `task.activity.created`
|
|
522
|
+
- `inbound-tracker-{type}` — fires on `webhook.received` with matching `webhook_name`
|
|
523
|
+
|
|
524
|
+
After creating the connection, paste the `webhook_url` + `webhook_secret` into your tracker's webhook configuration page (Linear/GitHub/GitLab settings).
|
|
525
|
+
|
|
526
|
+
---
|
|
527
|
+
|
|
528
|
+
## Triggers + Events + Webhooks
|
|
529
|
+
|
|
530
|
+
Triggers wire events to workflow or platform-function dispatches.
|
|
531
|
+
|
|
532
|
+
```bash
|
|
533
|
+
# Triggers
|
|
534
|
+
martha triggers list [--active] [--limit 50]
|
|
535
|
+
martha triggers create -f trigger.yaml
|
|
536
|
+
martha triggers get <name>
|
|
537
|
+
martha triggers update <name> -f trigger.yaml
|
|
538
|
+
martha triggers delete <name> [--yes]
|
|
539
|
+
martha triggers test <name> --event '{"type": "task.completed", "data": {...}}' # Dry-run
|
|
540
|
+
|
|
541
|
+
# Events (browse what's flowing)
|
|
542
|
+
martha events list [--type task.*] [--since 1h] [--limit 50]
|
|
543
|
+
martha events types # Available event types with sample payloads
|
|
544
|
+
martha events emit --type custom.event --data '{"foo": "bar"}' # Manual emit (admin only)
|
|
545
|
+
|
|
546
|
+
# Webhooks
|
|
547
|
+
martha webhooks list
|
|
548
|
+
martha webhooks create --name <n> [--description "..."] # Returns one-time secret
|
|
549
|
+
martha webhooks rotate-secret <name> # New secret, invalidates old
|
|
550
|
+
martha webhooks delete <name> [--yes]
|
|
551
|
+
```
|
|
552
|
+
|
|
553
|
+
**Trigger YAML:**
|
|
554
|
+
|
|
555
|
+
```yaml
|
|
556
|
+
name: notify-on-task-completion
|
|
557
|
+
event_type: task.completed
|
|
558
|
+
event_filter:
|
|
559
|
+
data:
|
|
560
|
+
priority: ["urgent", "high"]
|
|
561
|
+
target_type: workflow # or 'platform_function'
|
|
562
|
+
target_name: send-completion-email
|
|
563
|
+
input_mapping:
|
|
564
|
+
task_id: "{{event.data.task_id}}"
|
|
565
|
+
recipient: "carlos@nomadriver.co"
|
|
566
|
+
is_active: true
|
|
567
|
+
max_concurrent: 5 # Concurrent dispatches per tenant
|
|
568
|
+
dedup_key: "{{event.data.task_id}}"
|
|
569
|
+
dedup_window_seconds: 300
|
|
570
|
+
```
|
|
571
|
+
|
|
572
|
+
**Filter operators:** Each filter value is a list. Within a list, ANY match counts. Across keys, ALL must match. Operators: literal equality, `[{"exists": true}]`, `[{"prefix": "foo"}]`, `[{"in": [...]}]`, `[{"regex": "..."}]`.
|
|
573
|
+
|
|
574
|
+
---
|
|
575
|
+
|
|
576
|
+
## Documents (RAG)
|
|
577
|
+
|
|
578
|
+
Document collections are tenant-scoped containers that ingest files (PDF, DOCX, MD, etc.) and serve them for retrieval.
|
|
579
|
+
|
|
580
|
+
```bash
|
|
581
|
+
# Collections
|
|
582
|
+
martha documents collections [--inactive] [--limit 50]
|
|
583
|
+
martha documents collection <id> # Stats, ingestion status, total size
|
|
584
|
+
martha documents create-collection --name "My Docs" [--description "..."]
|
|
585
|
+
|
|
586
|
+
# Document lifecycle
|
|
587
|
+
martha documents upload <collection-id> <file> [--wait] [--follow] # --follow streams ingestion progress
|
|
588
|
+
martha documents list <collection-id> [--inactive] [--limit 50]
|
|
589
|
+
martha documents get <doc-id> # Full metadata + chunk count
|
|
590
|
+
martha documents status <doc-id> [--follow] # Ingestion progress: validate → parse → embed → finalize
|
|
591
|
+
martha documents download <doc-id> [--expires 3600] # Presigned URL (default 1h)
|
|
592
|
+
martha documents delete <doc-id> [--yes]
|
|
593
|
+
martha documents reingest <doc-id> [--wait] [--follow] # Re-run ingestion (after pipeline upgrade)
|
|
594
|
+
martha documents page-images <doc-id> [--expires 3600] # Presigned URLs for VLM-described pages
|
|
595
|
+
|
|
596
|
+
# Search & query
|
|
597
|
+
martha documents search <collection-id> "search query" [--max-results 10] # Hybrid keyword+vector
|
|
598
|
+
martha documents query <collection-id> "question" [--max-chunks 10] [--model claude-sonnet-4-5] # RAG with answer
|
|
599
|
+
```
|
|
600
|
+
|
|
601
|
+
**Ingestion stages (visible in `status --follow`):**
|
|
602
|
+
1. `validate` — MIME type, size limits, content scan
|
|
603
|
+
2. `parse_and_chunk` — Docling extraction + HybridChunker + page classification
|
|
604
|
+
3. `enrich` — embed + VLM describe (visual pages) + ColPali index (parallel, all non-fatal)
|
|
605
|
+
4. `finalize` — write to `document_chunks`, mark ready
|
|
606
|
+
|
|
607
|
+
A failed enrich step does NOT fail the document — it falls back to keyword-only search.
|
|
608
|
+
|
|
609
|
+
---
|
|
610
|
+
|
|
611
|
+
## Approvals (human-in-the-loop)
|
|
612
|
+
|
|
613
|
+
Approvals are pause-points inside workflows. The `approval_gate` workflow node creates an `ApprovalCase`; downstream nodes wait until a human resolves it.
|
|
614
|
+
|
|
615
|
+
```bash
|
|
616
|
+
martha approvals list [--status pending|approved|rejected|expired] [--assigned-to USER] [--limit 50]
|
|
617
|
+
martha approvals stats
|
|
618
|
+
martha approvals get <id> # Context summary, workflow context
|
|
619
|
+
martha approvals approve <id> [--comment "looks good"]
|
|
620
|
+
martha approvals reject <id> [--comment "needs more data"]
|
|
621
|
+
```
|
|
622
|
+
|
|
623
|
+
The workflow execution resumes within seconds of approval/rejection (Temporal signal).
|
|
624
|
+
|
|
625
|
+
---
|
|
626
|
+
|
|
627
|
+
## Citations (RAG provenance)
|
|
628
|
+
|
|
629
|
+
Citations link an LLM response to specific document chunks it consulted. They survive their source documents (no FK).
|
|
630
|
+
|
|
631
|
+
```bash
|
|
632
|
+
# Browse citations for an execution / message / approval
|
|
633
|
+
martha citations list --execution <id>
|
|
634
|
+
martha citations list --message <id>
|
|
635
|
+
martha citations get <citation-id> # Quoted text, page range, verification status
|
|
636
|
+
martha citations stats # Total / verified / failed / pending
|
|
637
|
+
martha citations verify <citation-id> # Re-runs exact/normalized/fuzzy matching
|
|
638
|
+
```
|
|
639
|
+
|
|
640
|
+
Available verification states: `verified`, `failed`, `pending`, `unverified`, `out_of_provenance`.
|
|
641
|
+
|
|
642
|
+
---
|
|
643
|
+
|
|
644
|
+
## Clients (chat API consumers)
|
|
645
|
+
|
|
646
|
+
Clients are the consumer-side identity for the chat API (web frontend, SMS sender, voice line). Each Client has allowlists for which functions, workflows, and agents the client's sessions can use.
|
|
647
|
+
|
|
648
|
+
```bash
|
|
649
|
+
martha clients list [--limit 50]
|
|
650
|
+
martha clients get <name-or-id>
|
|
651
|
+
martha clients create --name <n> [--system-prompts '{"default": "..."}'] [--keycloak-client-id <id>]
|
|
652
|
+
martha clients update <name-or-id> [--name <n>] [--system-prompts '...']
|
|
653
|
+
martha clients delete <name-or-id> [--force] [--yes] # --force drops sessions
|
|
654
|
+
|
|
655
|
+
# Access grants — what definitions this client's sessions can use
|
|
656
|
+
martha clients grant <client> function|workflow|agent <def-name> [--config key=value]
|
|
657
|
+
martha clients revoke <client> function|workflow|agent <def-name>
|
|
658
|
+
martha clients access <name-or-id> # All grants for this client
|
|
659
|
+
```
|
|
660
|
+
|
|
661
|
+
---
|
|
662
|
+
|
|
663
|
+
## Sessions + Chat
|
|
664
|
+
|
|
665
|
+
```bash
|
|
666
|
+
martha sessions list [--search QUERY] [--limit 20] [--type api|sms|whatsapp]
|
|
667
|
+
martha sessions get <session-id> # Messages + tool calls
|
|
668
|
+
martha sessions delete <session-id> [--yes]
|
|
669
|
+
|
|
670
|
+
# Interactive chat (REPL)
|
|
671
|
+
martha chat [--session <id>] [--client <name>] [--message "text"] [--show-tools]
|
|
672
|
+
|
|
673
|
+
# Single-message mode
|
|
674
|
+
martha chat --client web --message "What's the weather?" # Creates new session, prints response
|
|
675
|
+
```
|
|
676
|
+
|
|
677
|
+
`--show-tools` prints each tool call + result inline so you can debug agent reasoning.
|
|
678
|
+
|
|
679
|
+
---
|
|
680
|
+
|
|
681
|
+
## Integrations (OpenAPI specs + plugins)
|
|
682
|
+
|
|
683
|
+
Integrations are external API surfaces. Three types: `core` (`@platform_function`), `plugin` (Martha-published), `connected` (OpenAPI spec import), `custom` (manual function definitions).
|
|
684
|
+
|
|
685
|
+
```bash
|
|
686
|
+
martha integrations list # All four sections
|
|
687
|
+
martha integrations specs [--inactive] # OpenAPI specs only
|
|
688
|
+
martha integrations sync <spec-id> [--force] # Re-pull and reconcile (--force skips hash check)
|
|
689
|
+
martha integrations import-openapi --source <url> --name <name> \
|
|
690
|
+
[--prefix <p>] [--auth-scheme bearer] [--auth-value '${API_KEY}'] \
|
|
691
|
+
[--include-tags tag1,tag2] [--exclude-tags admin] [--dry-run]
|
|
692
|
+
martha integrations plugins # Installed plugins
|
|
693
|
+
martha integrations plugin <name> # Plugin manifest + resources
|
|
694
|
+
|
|
695
|
+
# Plugin proxy — call plugin endpoints directly (debugging)
|
|
696
|
+
martha integrations proxy <plugin> GET|POST|PUT|DELETE <path> [--data '{}'] [--query 'k=v']
|
|
697
|
+
```
|
|
698
|
+
|
|
699
|
+
**OpenAPI import flow:**
|
|
700
|
+
1. `--dry-run` first to see what functions would be created
|
|
701
|
+
2. Resolve any naming conflicts (use `--prefix`)
|
|
702
|
+
3. Re-run without `--dry-run` to apply
|
|
703
|
+
4. Use `martha agents add-function` to grant new functions to agents
|
|
704
|
+
5. Use `martha integrations sync <spec-id>` to refresh after the upstream API changes
|
|
705
|
+
|
|
706
|
+
### Function authentication patterns
|
|
707
|
+
|
|
708
|
+
Functions imported from OpenAPI inherit auth from the spec's `securitySchemes`. To bridge to Vault credentials, set `credential_source` on the function's `auth` block:
|
|
709
|
+
|
|
710
|
+
```yaml
|
|
711
|
+
kind: Function
|
|
712
|
+
name: example-create-order
|
|
713
|
+
definition:
|
|
714
|
+
endpoint: https://example.com/orders
|
|
715
|
+
http_method: POST
|
|
716
|
+
auth:
|
|
717
|
+
scheme: bearer
|
|
718
|
+
header: Authorization
|
|
719
|
+
credential_source: example_prod # Vault key for the credential
|
|
720
|
+
# value: null — left null; resolved at runtime from Vault
|
|
721
|
+
```
|
|
722
|
+
|
|
723
|
+
`FunctionHandler._resolve_credentials()` walks 4 tiers in priority order:
|
|
724
|
+
1. **Session** — `vault.get_credential("sessions", session_id, credential_source)`
|
|
725
|
+
2. **Client** — `vault.get_credential("clients", str(client_id), credential_source)`
|
|
726
|
+
3. **Tenant by credential_source** — direct Vault lookup
|
|
727
|
+
4. **Tenant by Connection** — looks up `Connection.credential_source` for `integration_name`, then Vault
|
|
728
|
+
|
|
729
|
+
If all 4 miss, falls back to baked-in `auth.value` (legacy). Use `martha agents provision-auth` for agent-scoped credentials, or the connections API for integration-scoped ones.
|
|
730
|
+
|
|
731
|
+
---
|
|
732
|
+
|
|
733
|
+
## Operational commands
|
|
734
|
+
|
|
735
|
+
```bash
|
|
736
|
+
martha status # Connection + auth + Vault + Temporal health summary
|
|
737
|
+
martha models [--provider anthropic|openai|google] # Available LLM models with cost
|
|
738
|
+
martha messaging health # Infobip/Twilio status
|
|
739
|
+
martha messaging send-sms --to <number> --from <sender> --content "text"
|
|
740
|
+
martha config show # Resolved config (token-redacted)
|
|
741
|
+
martha config set <key> <value> # Update profile config
|
|
742
|
+
martha config profiles # List + active marker
|
|
743
|
+
martha config use <profile> # Switch active profile
|
|
744
|
+
```
|
|
745
|
+
|
|
746
|
+
---
|
|
747
|
+
|
|
748
|
+
## Patterns (cookbook)
|
|
749
|
+
|
|
750
|
+
### Create + grant + execute a workflow end-to-end
|
|
751
|
+
|
|
752
|
+
```bash
|
|
753
|
+
# 1. Define functions + workflow declaratively
|
|
754
|
+
cat > pipeline.yaml <<'EOF'
|
|
755
|
+
kind: Function
|
|
756
|
+
name: fetch-orders
|
|
757
|
+
definition:
|
|
758
|
+
endpoint: https://api.example.com/orders
|
|
759
|
+
http_method: GET
|
|
760
|
+
auth:
|
|
761
|
+
scheme: bearer
|
|
762
|
+
credential_source: example_prod
|
|
763
|
+
---
|
|
764
|
+
kind: Workflow
|
|
765
|
+
name: daily-orders-summary
|
|
766
|
+
definition:
|
|
767
|
+
nodes:
|
|
768
|
+
- id: fetch
|
|
769
|
+
type: function
|
|
770
|
+
config: { function_name: fetch-orders }
|
|
771
|
+
- id: summarize
|
|
772
|
+
type: llm
|
|
773
|
+
config:
|
|
774
|
+
prompt: "Summarize: {{steps.fetch.output}}"
|
|
775
|
+
model: claude-sonnet-4-5
|
|
776
|
+
edges: [{source: fetch, target: summarize}]
|
|
777
|
+
EOF
|
|
778
|
+
|
|
779
|
+
martha definitions apply -f pipeline.yaml --yes
|
|
780
|
+
|
|
781
|
+
# 2. Set up the credential (one-time per tenant)
|
|
782
|
+
# Use the admin UI at /settings, or POST to /api/admin/connections
|
|
783
|
+
|
|
784
|
+
# 3. Execute
|
|
785
|
+
martha workflows execute daily-orders-summary --inputs '{}' --wait --follow
|
|
786
|
+
```
|
|
787
|
+
|
|
788
|
+
### External agent on a team — full setup
|
|
789
|
+
|
|
790
|
+
```bash
|
|
791
|
+
# Admin side
|
|
792
|
+
martha agents create --name ork-1 --type external --tags refactoring
|
|
793
|
+
martha agents provision-auth ork-1 --method service-account --json
|
|
794
|
+
# Copy client_id + client_secret from output
|
|
795
|
+
|
|
796
|
+
martha teams create --name refactoring-team --routing round_robin
|
|
797
|
+
martha teams add-member refactoring-team ork-1
|
|
798
|
+
|
|
799
|
+
# Agent side
|
|
800
|
+
export MARTHA_CLIENT_ID=<from above>
|
|
801
|
+
export MARTHA_CLIENT_SECRET=<from above>
|
|
802
|
+
export MARTHA_API_URL=https://martha.nomadriver.co
|
|
803
|
+
martha auth login --service-account
|
|
804
|
+
martha tasks poll --json # Should return open team-scoped tasks
|
|
805
|
+
```
|
|
806
|
+
|
|
807
|
+
### Tracker integration — Linear
|
|
808
|
+
|
|
809
|
+
```bash
|
|
810
|
+
# 1. Create a Linear connection via admin UI: /settings > Trackers > Linear > Connect
|
|
811
|
+
# Provide: API Key (lin_api_...), Team ID (UUID from team URL)
|
|
812
|
+
# On success, copy the webhook_url + webhook_secret
|
|
813
|
+
|
|
814
|
+
# 2. Configure Linear webhook
|
|
815
|
+
# Linear settings > API > Webhooks > New webhook
|
|
816
|
+
# URL: <webhook_url>, Secret: <webhook_secret>
|
|
817
|
+
# Subscribe to: Issues, Comments
|
|
818
|
+
|
|
819
|
+
# 3. Verify the auto-created triggers
|
|
820
|
+
martha triggers list --active | grep linear
|
|
821
|
+
# Expect: sync-task-status-linear, sync-task-activity-linear, inbound-tracker-linear
|
|
822
|
+
|
|
823
|
+
# 4. Test by creating a Linear issue — Martha should create a corresponding task
|
|
824
|
+
martha tasks list --json | jq '.[] | select(.tracker_type == "linear")'
|
|
825
|
+
```
|
|
826
|
+
|
|
827
|
+
### Reingest documents after VLM upgrade
|
|
828
|
+
|
|
829
|
+
```bash
|
|
830
|
+
COLL=<collection-id>
|
|
831
|
+
martha documents list $COLL --json | jq -r '.[] | .id' | while read DOC; do
|
|
832
|
+
martha documents reingest $DOC --wait
|
|
833
|
+
done
|
|
834
|
+
```
|
|
835
|
+
|
|
836
|
+
### Debug a failed task
|
|
837
|
+
|
|
838
|
+
```bash
|
|
839
|
+
TASK=<id>
|
|
840
|
+
martha tasks view $TASK --json | jq '{status, error_message, retry_count, last_checkpoint, executor_id}'
|
|
841
|
+
|
|
842
|
+
# Look at the workflow execution if any
|
|
843
|
+
EXEC=$(martha tasks view $TASK --json | jq -r '.workflow_execution_id')
|
|
844
|
+
[ -n "$EXEC" ] && martha workflows execution $EXEC
|
|
845
|
+
|
|
846
|
+
# Check audit log for the executor
|
|
847
|
+
SVC=$(martha tasks view $TASK --json | jq -r '.executor_id')
|
|
848
|
+
martha events list --since 24h --json | jq ".[] | select(.data.executor_id == \"$SVC\")"
|
|
849
|
+
```
|
|
850
|
+
|
|
851
|
+
### Bulk grant many functions to an agent
|
|
852
|
+
|
|
853
|
+
```bash
|
|
854
|
+
AGENT=my-agent
|
|
855
|
+
martha integrations specs --json | jq -r '.[] | select(.name == "linear") | .id' | while read SPEC; do
|
|
856
|
+
martha functions list --source openapi --json | jq -r ".[] | select(.spec_id == \"$SPEC\") | .name" | while read FN; do
|
|
857
|
+
martha agents add-function $AGENT $FN
|
|
858
|
+
done
|
|
859
|
+
done
|
|
860
|
+
```
|
|
861
|
+
|
|
862
|
+
---
|
|
863
|
+
|
|
864
|
+
## Troubleshooting
|
|
865
|
+
|
|
866
|
+
### "auth failure" (exit 2)
|
|
867
|
+
- `martha auth status` — check the token isn't expired or for a different tenant
|
|
868
|
+
- For service accounts: confirm `MARTHA_CLIENT_SECRET` matches the value from the most recent `provision-auth`
|
|
869
|
+
- For human users: re-login (`martha auth login --username ... --password ...`)
|
|
870
|
+
|
|
871
|
+
### "not found" (exit 3) when the entity exists in the UI
|
|
872
|
+
- Tenant mismatch — your token belongs to a different tenant than where the entity lives
|
|
873
|
+
- Run `martha auth status` to see your `tenant_id`, then check the entity's tenant in the admin UI
|
|
874
|
+
|
|
875
|
+
### Workflow execution stuck in "running"
|
|
876
|
+
- `martha workflows execution <id> --follow` — see which node is blocking
|
|
877
|
+
- `martha approvals list --status pending` — common cause is a waiting `approval_gate`
|
|
878
|
+
- For agent_loop nodes: check the agent's tool grants and credential resolution chain
|
|
879
|
+
|
|
880
|
+
### Function returns 401 from upstream API
|
|
881
|
+
- Check the function's `auth.credential_source` matches a Vault entry: `martha auth token` then `curl /api/admin/connections | jq` to find the connection
|
|
882
|
+
- Verify the connection's `status` is `active` (rotate if expired)
|
|
883
|
+
- For OAuth2 connections: a 401 may trigger automatic token refresh; check `events list --type oauth.refresh.*`
|
|
884
|
+
|
|
885
|
+
### Task heartbeat returns 409 unexpectedly
|
|
886
|
+
- Lease expired — your agent didn't heartbeat in time. Default lease is 300s; reduce work granularity or increase lease.
|
|
887
|
+
- Another agent claimed it after a stale event. Inspect: `martha tasks view <id> --json | jq '{executor_id, lease_expires_at, retry_count}'`
|
|
888
|
+
|
|
889
|
+
### Tracker webhook not creating tasks
|
|
890
|
+
- Check the inbound trigger is active: `martha triggers list | grep inbound-tracker`
|
|
891
|
+
- Check recent webhook events: `martha events list --type webhook.received --since 1h`
|
|
892
|
+
- Look for HMAC signature failures in API logs (per-webhook secret mismatch)
|
|
893
|
+
- Verify the connection still has `status: active`
|
|
894
|
+
|
|
895
|
+
### "DuplicateColumn" alembic error on deploy
|
|
896
|
+
- Migration trying to add a column that already exists. Check if a previous migration in the chain added it via a different path (this happened with `s4t5u6v7w8x9` and `u6v7w8x9y0z1`). Make the duplicate migration idempotent with `IF NOT EXISTS`.
|
|
897
|
+
|
|
898
|
+
---
|
|
899
|
+
|
|
900
|
+
## Conventions
|
|
901
|
+
|
|
902
|
+
- **Always pass `--json`** when scripting. Human output may include color codes and is not parse-stable.
|
|
903
|
+
- **Always pass `--yes`** in non-TTY contexts. The CLI errors instead of silently cancelling — this prevents CI from completing without doing anything.
|
|
904
|
+
- **Tenant comes from the token**, never from a flag. To switch tenants, switch profiles.
|
|
905
|
+
- **Time fields are ISO 8601 with timezone** in JSON output. Local-format dates appear only in human output.
|
|
906
|
+
- **Pagination:** all `list` commands accept `--limit` (default 50, max 200) and most accept `--offset`. There's no cursor pagination yet.
|
|
907
|
+
- **Soft delete is default.** `delete` without `--hard` marks `is_active=false` and keeps history. `--hard` purges. Tombstoned entities don't appear in `list` unless you pass `--inactive`.
|