orbitmap 0.4.4 → 0.5.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.
Files changed (86) hide show
  1. package/README.md +73 -10
  2. package/dist/adapters/cloud.d.ts +85 -10
  3. package/dist/adapters/cloud.js +118 -17
  4. package/dist/adapters/cloud.js.map +1 -1
  5. package/dist/adapters/factory.js +7 -0
  6. package/dist/adapters/factory.js.map +1 -1
  7. package/dist/adapters/local/adapter.d.ts +47 -8
  8. package/dist/adapters/local/adapter.js +37 -17
  9. package/dist/adapters/local/adapter.js.map +1 -1
  10. package/dist/adapters/local/context.d.ts +13 -1
  11. package/dist/adapters/local/context.js +7 -2
  12. package/dist/adapters/local/context.js.map +1 -1
  13. package/dist/adapters/local/entities/documents.d.ts +18 -4
  14. package/dist/adapters/local/entities/documents.js +46 -7
  15. package/dist/adapters/local/entities/documents.js.map +1 -1
  16. package/dist/adapters/local/entities/planning.d.ts +36 -7
  17. package/dist/adapters/local/entities/planning.js +245 -39
  18. package/dist/adapters/local/entities/planning.js.map +1 -1
  19. package/dist/adapters/local/entities/tasks.d.ts +16 -7
  20. package/dist/adapters/local/entities/tasks.js +55 -42
  21. package/dist/adapters/local/entities/tasks.js.map +1 -1
  22. package/dist/adapters/local/entities/work-items.d.ts +28 -7
  23. package/dist/adapters/local/entities/work-items.js +111 -39
  24. package/dist/adapters/local/entities/work-items.js.map +1 -1
  25. package/dist/adapters/local/lists.d.ts +139 -0
  26. package/dist/adapters/local/lists.js +230 -0
  27. package/dist/adapters/local/lists.js.map +1 -0
  28. package/dist/adapters/local/rules.js +22 -0
  29. package/dist/adapters/local/rules.js.map +1 -1
  30. package/dist/adapters/local/shapes.d.ts +165 -22
  31. package/dist/adapters/local/shapes.js +338 -50
  32. package/dist/adapters/local/shapes.js.map +1 -1
  33. package/dist/adapters/local/types.d.ts +5 -0
  34. package/dist/adapters/local/types.js.map +1 -1
  35. package/dist/adapters/types.d.ts +72 -9
  36. package/dist/agent-instructions.d.ts +39 -10
  37. package/dist/agent-instructions.js +268 -68
  38. package/dist/agent-instructions.js.map +1 -1
  39. package/dist/commands/context.d.ts +62 -37
  40. package/dist/commands/context.js +105 -36
  41. package/dist/commands/context.js.map +1 -1
  42. package/dist/commands/docs.d.ts +4 -0
  43. package/dist/commands/docs.js +12 -5
  44. package/dist/commands/docs.js.map +1 -1
  45. package/dist/commands/ideas.d.ts +3 -0
  46. package/dist/commands/ideas.js +14 -7
  47. package/dist/commands/ideas.js.map +1 -1
  48. package/dist/commands/intent.d.ts +26 -0
  49. package/dist/commands/intent.js +110 -7
  50. package/dist/commands/intent.js.map +1 -1
  51. package/dist/commands/issues.d.ts +4 -0
  52. package/dist/commands/issues.js +15 -7
  53. package/dist/commands/issues.js.map +1 -1
  54. package/dist/commands/mission.d.ts +2 -0
  55. package/dist/commands/mission.js +9 -5
  56. package/dist/commands/mission.js.map +1 -1
  57. package/dist/commands/setup-agent.d.ts +34 -0
  58. package/dist/commands/setup-agent.js +95 -4
  59. package/dist/commands/setup-agent.js.map +1 -1
  60. package/dist/commands/start.d.ts +13 -0
  61. package/dist/commands/start.js +125 -13
  62. package/dist/commands/start.js.map +1 -1
  63. package/dist/commands/tasks.d.ts +23 -5
  64. package/dist/commands/tasks.js +46 -23
  65. package/dist/commands/tasks.js.map +1 -1
  66. package/dist/commands/vibes.d.ts +3 -0
  67. package/dist/commands/vibes.js +11 -5
  68. package/dist/commands/vibes.js.map +1 -1
  69. package/dist/errors.d.ts +23 -2
  70. package/dist/errors.js +31 -3
  71. package/dist/errors.js.map +1 -1
  72. package/dist/index.js +108 -36
  73. package/dist/index.js.map +1 -1
  74. package/dist/list-meta.d.ts +117 -0
  75. package/dist/list-meta.js +160 -0
  76. package/dist/list-meta.js.map +1 -0
  77. package/dist/list-options.d.ts +33 -0
  78. package/dist/list-options.js +70 -0
  79. package/dist/list-options.js.map +1 -0
  80. package/dist/workspace-resolve.d.ts +47 -1
  81. package/dist/workspace-resolve.js +74 -11
  82. package/dist/workspace-resolve.js.map +1 -1
  83. package/dist/write-target.d.ts +9 -0
  84. package/dist/write-target.js +11 -2
  85. package/dist/write-target.js.map +1 -1
  86. package/package.json +2 -2
package/README.md CHANGED
@@ -155,7 +155,7 @@ OrbitMap organises work on several levels. The CLI mirrors them 1:1:
155
155
 
156
156
  | Command | Description |
157
157
  |---------|-------------|
158
- | `orbitmap tasks` | List your assigned tasks (`--status`, `--priority`, `--orbit`) |
158
+ | `orbitmap tasks` | List tasks across every area you reach and every assignee (`--area`, `--status`, `--priority`, `--orbit`, `--intent`, `--mission`, `--agent`, `--unassigned`, `--limit`, `--all`) |
159
159
  | `orbitmap task <id-or-number>` | Show full task details |
160
160
  | `orbitmap start [id-or-number]` | Start working on a task (auto-picks next TODO if no ID) |
161
161
  | `orbitmap status <id-or-number> <status>` | Update task status (`--force` to move a parent to `done` with unfinished subtasks) |
@@ -169,6 +169,59 @@ OrbitMap organises work on several levels. The CLI mirrors them 1:1:
169
169
 
170
170
  **Valid statuses:** `backlog`, `todo`, `in_progress`, `in_review`, `review_changes`, `done`, `blocked`
171
171
 
172
+ ### What a list shows, and what it hides
173
+
174
+ Every list — `tasks`, `issues`, `ideas`, `vibes`, `docs`, `intents`, `missions` — spans
175
+ **every area your key reaches and every assignee**. It is not scoped to your own work, and it
176
+ is not scoped to the area your directory maps to: that area is the context for *writes* and
177
+ for single-object reads, and the server deliberately ignores it when listing.
178
+
179
+ Narrowing is explicit, and always visible in the output:
180
+
181
+ ```bash
182
+ orbitmap tasks # everything you can reach
183
+ orbitmap tasks --area demo # one area
184
+ orbitmap tasks --area demo,web # two areas (comma-separated, no spaces)
185
+ orbitmap tasks --area all # cancel an ORBITMAP_AREA narrowing for this call
186
+ orbitmap tasks --agent me # only your own tasks — the pre-9.0.0 default
187
+ orbitmap tasks --agent jakub-agent # a colleague's
188
+ orbitmap tasks --unassigned # the free backlog
189
+ orbitmap tasks --intent IN-nahetc # everything under one intent, in one call
190
+ orbitmap tasks --mission MS-abc123
191
+ orbitmap tasks --status all # every status, on every list
192
+ ```
193
+
194
+ Lists are paged: **30 rows by default, 100 at most** per page. The page is not the answer —
195
+ `--all` is:
196
+
197
+ ```bash
198
+ orbitmap tasks --limit 100 # one bigger page
199
+ orbitmap tasks --all # follow every page and print all 143 rows
200
+ ```
201
+
202
+ Under every result the CLI prints what produced it, whether the list was empty, truncated or
203
+ complete:
204
+
205
+ ```
206
+ 30 of 143 shown across 7 areas — run with --all for every row.
207
+ Scope: all 7 reachable areas, status todo,in_progress (46 hidden, add --status all), assignee any.
208
+ ```
209
+
210
+ An empty list carries the same line, so "nothing exists" and "you did not ask for it" are
211
+ never the same sentence:
212
+
213
+ ```
214
+ No tasks. Scope: areas demo (1 of 7), status todo,in_progress (46 hidden, add --status all), assignee unassigned.
215
+ ```
216
+
217
+ `--json` carries the whole `meta` block: `total`, `next_cursor`, `per_area`
218
+ (`{slug, shown, total}` per area), `applied_filters`, `scope` and `workspace`, plus each row's
219
+ own `area`, `agent_id` and `assigned_user_id`.
220
+
221
+ `--area` names *one* area everywhere else (`create`, `log`, `status`, `doc-import`, …) — that
222
+ is the area a write lands in, so a comma-separated `--area` is refused there rather than
223
+ silently taking the first.
224
+
172
225
  ### Referring to objects: `<id-or-number>`
173
226
 
174
227
  Every command that takes an object reference accepts all three forms interchangeably:
@@ -234,7 +287,7 @@ when there is none — so a rejected transition is re-run with `--log`, not retr
234
287
 
235
288
  | Command | Description |
236
289
  |---------|-------------|
237
- | `orbitmap docs` | List area documents |
290
+ | `orbitmap docs` | List documents across every area you reach, plus workspace-level ones (`--area`, `--type`, `--search`, `--limit`, `--all`) |
238
291
  | `orbitmap doc <id-or-slug>` | Show document (TOC by default, `--full`, `--section`) |
239
292
  | `orbitmap doc-import <title>` | Import a markdown file (`--context` to set when-to-use hint) |
240
293
  | `orbitmap doc-update <id-or-slug>` | Replace a document from a file |
@@ -278,7 +331,7 @@ orbitmap doc-share <doc-uuid> <area-uuid> --agents agent1-uuid,agent2-uuid
278
331
 
279
332
  | Command | Description |
280
333
  |---------|-------------|
281
- | `orbitmap context` | Show agent identity and current area |
334
+ | `orbitmap context` | Session matching material: agent identity, area/workspace, missions and intents with TL;DRs, mission workitems, areas, document metadata |
282
335
  | `orbitmap areas` | List all areas assigned to this agent |
283
336
  | `orbitmap create-area <name>` | Create a new area (`--desc`, `--slug`, `--workspace-id`) |
284
337
  | `orbitmap members <area-id>` | List agents and users in an area (accepts slug) |
@@ -288,16 +341,16 @@ orbitmap doc-share <doc-uuid> <area-uuid> --agents agent1-uuid,agent2-uuid
288
341
 
289
342
  | Command | Description |
290
343
  |---------|-------------|
291
- | `orbitmap issues` | List open issues |
344
+ | `orbitmap issues` | List open issues across every area you reach (`--area`, `--status`, `--severity`, `--intent`, `--limit`, `--all`) |
292
345
  | `orbitmap issue show <id>` | Show issue details (UUID or number) |
293
346
  | `orbitmap issue register` | Register a new issue (`--intent` to link it to an intent) |
294
347
  | `orbitmap issue status <id> <status>` | Update issue status |
295
348
  | `orbitmap issue resolve <id>` | Resolve an issue |
296
- | `orbitmap ideas` | List ideas |
349
+ | `orbitmap ideas` | List ideas across every area you reach (`--area`, `--status`, `--priority`, `--limit`, `--all`) |
297
350
  | `orbitmap idea add` | Add a new idea |
298
351
  | `orbitmap idea show <id>` | Show idea details (UUID or number) |
299
352
  | `orbitmap idea status <id>` | Update idea status |
300
- | `orbitmap vibes` | List vibes |
353
+ | `orbitmap vibes` | List vibes across every area you reach (`--area`, `--type`, `--since`, `--limit`, `--all`) |
301
354
  | `orbitmap vibe log` | Log a new vibe |
302
355
  | `orbitmap vibe show <id>` | Show vibe details |
303
356
  | `orbitmap vibe update <id>` | Update a vibe |
@@ -308,10 +361,10 @@ Workspace-level desired outcomes. Tasks and issues hang off an intent; an intent
308
361
 
309
362
  | Command | Description |
310
363
  |---------|-------------|
311
- | `orbitmap intents` | List intents (shortcut for `intent list`) |
312
- | `orbitmap intent show <id-or-number>` | Show an intent (UUID or `IN-xxxxxx`) |
364
+ | `orbitmap intents` | List intents — workspace-wide (`--status`, `--mission`, `--limit`, `--all`) |
365
+ | `orbitmap intent show <id-or-number>` | Show an intent (UUID or `IN-xxxxxx`) — embeds the context bundle (`--no-bundle` strips it, `--no-logs` skips the work-log tail, `--slim` is the freshness probe) |
313
366
  | `orbitmap intent create <title>` | Create an intent (`--outcome`, `--design`, `--plan`, `--status`, `--orbit`) |
314
- | `orbitmap intent update <id>` | Update title, outcome, design, plan, or status (`--log <type>:<content>`, repeatable) |
367
+ | `orbitmap intent update <id>` | Update title, outcome, design, plan, or status (`--areas <slug>,<slug>` declares its areas; `--log <type>:<content>`, repeatable) |
315
368
  | `orbitmap intent link <id> --type task\|issue --id <uuid>` | Link a task or issue |
316
369
  | `orbitmap intent unlink <id> --type task\|issue --id <uuid>` | Unlink a task or issue |
317
370
  | `orbitmap intent orbit <id> [orbit-id]` | Set the intent's orbit (`--clear` to remove) |
@@ -335,13 +388,23 @@ orbitmap intent update IN-a1b2c3 --status plan \
335
388
 
336
389
  `intent create --orbit` is create-then-attach. If the attach step fails the intent is still created — the CLI reports the failure and does not retry.
337
390
 
391
+ **The context bundle.** `intent show` prints a `Context bundle:` block after the intent's own outcome/design/plan and before the work-log tail: the mission handle with its outcome TL;DR, the documents that surround the intent (listed by their `when_to_use`, with the slug to pass to `orbitmap doc`), sibling intent handles, and the intent's own tasks and issues. It is handle tier — TL;DRs and titles, never bodies. `--no-bundle` strips it when you only want the intent itself.
392
+
393
+ **The freshness probe.** `--slim` replaces the whole body with a ~150-token probe: handle, the outcome/design/plan TL;DRs, declared areas, mission, task counts per status, and the `updated_at` + `last_log_id` pair. Hold that pair from a full fetch; run the probe to see whether the intent moved. Unchanged means your copy is current — do not re-fetch. A work log never touches `updated_at`, so both halves have to be compared. `--slim` wins over `--no-bundle` and `--no-logs`.
394
+
395
+ ```bash
396
+ orbitmap intent show IN-a1b2c3 # intent + context bundle + work-log tail
397
+ orbitmap intent show IN-a1b2c3 --no-bundle # just the intent and its logs
398
+ orbitmap intent show IN-a1b2c3 --slim # freshness probe: TL;DRs + updated_at/last_log_id
399
+ ```
400
+
338
401
  ### Missions
339
402
 
340
403
  Time-boxed workspace focus. Only one mission can be `active` at a time.
341
404
 
342
405
  | Command | Description |
343
406
  |---------|-------------|
344
- | `orbitmap missions` | List missions (shortcut for `mission list`) |
407
+ | `orbitmap missions` | List missions — workspace-wide (`--status`, `--limit`, `--all`) |
345
408
  | `orbitmap mission show <id-or-number>` | Show a mission (UUID or `MS-xxxxxx`) |
346
409
  | `orbitmap mission create <title>` | Create a mission (`--desc`, `--outcome`, `--end-date`) |
347
410
  | `orbitmap mission update <id>` | Update title, description, outcome, or end date (`--log <type>:<content>`, repeatable) |
@@ -12,13 +12,25 @@ export declare class CloudAdapter implements WorkspaceAdapter {
12
12
  * both or neither may be set.
13
13
  */
14
14
  private readonly workspace?;
15
+ /**
16
+ * The areas the caller EXPLICITLY narrowed a LIST to (`--area a,b`, `ORBITMAP_AREA=a,b`),
17
+ * already resolved by the one area ladder. Sent as the `area_id` query parameter.
18
+ *
19
+ * It is deliberately NOT {@link CloudAdapter.area}. Since API 9.0.0 the `X-Orbitmap-Area`
20
+ * header no longer narrows a list — the CLI sends it on every call made from an
21
+ * area-mapped directory, so honouring it there would leave `orbitmap tasks` answering with
22
+ * one area's work, which is the bug IN-nahetc exists to fix. The header stays the context
23
+ * for writes and single-entity reads; `area_id` is the only narrowing, and only an
24
+ * explicit `--area` fills it.
25
+ */
26
+ private readonly areaFilter;
15
27
  /**
16
28
  * Task reference → uuid, for the lifetime of this adapter. A command that touches the same
17
29
  * task twice (`dep add TS-a TS-b` resolves two, `status` then reads back) pays for one
18
30
  * `/tasks/by-number/{n}` lookup per distinct reference, not per call site.
19
31
  */
20
32
  private readonly taskIdCache;
21
- constructor(apiKey: string, area?: string, apiUrl?: string, workspace?: string);
33
+ constructor(apiKey: string, area?: string, apiUrl?: string, workspace?: string, areaFilter?: string[]);
22
34
  private request;
23
35
  /**
24
36
  * Add the client half of the story to the workspace-context errors the server can raise.
@@ -49,11 +61,39 @@ export declare class CloudAdapter implements WorkspaceAdapter {
49
61
  * No request is made.
50
62
  */
51
63
  getWorkspaceId(): string | undefined;
64
+ /**
65
+ * The `area_id` query value for a LIST: what the caller explicitly asked for, falling back
66
+ * to the areas the ladder resolved from `--area` / `ORBITMAP_AREA`.
67
+ *
68
+ * Comma-joined, exactly as the API's own parser expects
69
+ * (`BaseAgentApiController::splitAreaRefs`). `undefined` — never an empty string — when
70
+ * nothing narrows, so the request carries no `area_id` at all and the answer spans every
71
+ * area the key reaches.
72
+ */
73
+ private listAreaParam;
74
+ /** `limit` / `cursor`, the two paging params every list shares. */
75
+ private pagingParams;
76
+ /**
77
+ * `GET /tasks` — every area the key reaches and every assignee, unless a filter says
78
+ * otherwise (API 9.0.0).
79
+ *
80
+ * `areaId` accepts one ref or several; several are comma-joined, which is the only way to
81
+ * narrow to two areas without two calls. `agentId` (`me`, a slug or a uuid), `unassigned`
82
+ * and `all` are the owner scope — `all` now names the default rather than escaping one.
83
+ * `intentId` / `missionId` take a uuid, `IN-xxxxxx`/`MS-xxxxxx` or the bare code.
84
+ */
52
85
  getTasks(options?: {
53
86
  status?: string;
54
87
  priority?: string;
55
- areaId?: string;
88
+ areaId?: string | string[];
56
89
  orbit?: string;
90
+ intentId?: string;
91
+ missionId?: string;
92
+ agentId?: string;
93
+ unassigned?: boolean;
94
+ all?: boolean;
95
+ limit?: string | number;
96
+ cursor?: string;
57
97
  }): Promise<unknown>;
58
98
  /**
59
99
  * Resolve any task reference — uuid, `TS-uw9yh5` or the bare code — to the uuid the task
@@ -63,7 +103,18 @@ export declare class CloudAdapter implements WorkspaceAdapter {
63
103
  private taskId;
64
104
  getTask(taskId: string): Promise<unknown>;
65
105
  getTaskByNumber(taskNumber: string): Promise<unknown>;
66
- startTask(taskId?: string): Promise<unknown>;
106
+ /**
107
+ * `POST /tasks/{id}/start`, or `POST /tasks/start` to let the server auto-pick.
108
+ *
109
+ * `confirmReassign` is the USER's answer to a 409 `REASSIGN_CONFIRMATION_REQUIRED`
110
+ * travelling back: it reassigns a task that belongs to another agent and starts it in one
111
+ * call. It is never sent on its own initiative — see `askToTakeOver` in
112
+ * `src/commands/start.ts`. Auto-confirming would turn "ask before taking someone else's
113
+ * work" into theatre.
114
+ */
115
+ startTask(taskId?: string, options?: {
116
+ confirmReassign?: boolean;
117
+ }): Promise<unknown>;
67
118
  updateTaskStatus(taskId: string, status?: string, deliveryStatus?: string | null, _force?: boolean): Promise<unknown>;
68
119
  createSubtasks(parentId: string, subtasks: Array<{
69
120
  title: string;
@@ -112,7 +163,10 @@ export declare class CloudAdapter implements WorkspaceAdapter {
112
163
  getDocuments(options?: {
113
164
  type?: string;
114
165
  search?: string;
115
- areaId?: string;
166
+ areaId?: string | string[];
167
+ status?: string;
168
+ limit?: string | number;
169
+ cursor?: string;
116
170
  }): Promise<unknown>;
117
171
  getDocument(idOrSlug: string, mode?: 'inline' | 'file', options?: {
118
172
  full?: boolean;
@@ -221,7 +275,10 @@ export declare class CloudAdapter implements WorkspaceAdapter {
221
275
  listIssues(options?: {
222
276
  status?: string;
223
277
  severity?: string;
224
- limit?: string;
278
+ areaId?: string | string[];
279
+ intentId?: string;
280
+ limit?: string | number;
281
+ cursor?: string;
225
282
  }): Promise<unknown>;
226
283
  /**
227
284
  * `GET /issues/{id_or_number}` — the single issue-read route. It accepts a uuid, a bare
@@ -247,7 +304,9 @@ export declare class CloudAdapter implements WorkspaceAdapter {
247
304
  listVibes(options?: {
248
305
  type?: string;
249
306
  since?: string;
250
- limit?: string;
307
+ areaId?: string | string[];
308
+ limit?: string | number;
309
+ cursor?: string;
251
310
  }): Promise<unknown>;
252
311
  getVibe(id: string): Promise<unknown>;
253
312
  updateVibe(id: string, data: {
@@ -259,7 +318,9 @@ export declare class CloudAdapter implements WorkspaceAdapter {
259
318
  listIdeas(options?: {
260
319
  status?: string;
261
320
  priority?: string;
262
- limit?: string;
321
+ areaId?: string | string[];
322
+ limit?: string | number;
323
+ cursor?: string;
263
324
  }): Promise<unknown>;
264
325
  /**
265
326
  * `GET /ideas/{id_or_number}` — accepts a uuid, a bare code or `ID-xxxxxx`; the server
@@ -313,9 +374,15 @@ export declare class CloudAdapter implements WorkspaceAdapter {
313
374
  }): Promise<unknown>;
314
375
  listIntents(options?: {
315
376
  status?: string;
316
- limit?: string;
377
+ missionId?: string;
378
+ limit?: string | number;
379
+ cursor?: string;
380
+ }): Promise<unknown>;
381
+ getIntent(idOrNumber: string, options?: {
382
+ logs?: boolean;
383
+ bundle?: boolean;
384
+ slim?: boolean;
317
385
  }): Promise<unknown>;
318
- getIntent(idOrNumber: string): Promise<unknown>;
319
386
  createIntent(data: {
320
387
  name: string;
321
388
  outcome?: string;
@@ -330,6 +397,7 @@ export declare class CloudAdapter implements WorkspaceAdapter {
330
397
  design?: string;
331
398
  plan?: string;
332
399
  status?: string;
400
+ area_ids?: string[];
333
401
  logs?: WorkLogEntry[];
334
402
  }): Promise<unknown>;
335
403
  linkIntentItem(idOrNumber: string, data: {
@@ -343,7 +411,8 @@ export declare class CloudAdapter implements WorkspaceAdapter {
343
411
  setIntentOrbit(idOrNumber: string, orbitId: string | null): Promise<unknown>;
344
412
  listMissions(options?: {
345
413
  status?: string;
346
- limit?: string;
414
+ limit?: string | number;
415
+ cursor?: string;
347
416
  }): Promise<unknown>;
348
417
  getMission(idOrNumber: string): Promise<unknown>;
349
418
  createMission(data: {
@@ -391,6 +460,12 @@ export interface CreateClientOptions {
391
460
  * when the caller has it so the ladder runs once per command.
392
461
  */
393
462
  workspace?: string;
463
+ /**
464
+ * The areas the caller EXPLICITLY narrowed to, already produced by the same ladder — the
465
+ * `area_id` list filter. See {@link CloudAdapter.areaFilter} for why this is not
466
+ * {@link CreateClientOptions.area}.
467
+ */
468
+ areas?: string[];
394
469
  /** Global config the caller already loaded, to avoid a second read of the same file. */
395
470
  config?: OrbitMapConfig;
396
471
  }
@@ -1,6 +1,6 @@
1
1
  import { createReadStream, statSync } from 'node:fs';
2
2
  import { basename, extname } from 'node:path';
3
- import { AREA_CONTEXT_REQUIRED, AREA_WORKSPACE_MISMATCH, OrbitMapAPIError, WORKSPACE_CONTEXT_REQUIRED, } from '../errors.js';
3
+ import { AREA_CONTEXT_REQUIRED, OrbitMapAPIError, WORKSPACE_CONTEXT_REQUIRED, } from '../errors.js';
4
4
  import { loadConfig, resolveAuth, DEFAULT_API_URL } from '../config.js';
5
5
  import { areaContextHint, ORBITMAP_DIR, resolveArea, resolveWorkspaceId, } from '../workspace-resolve.js';
6
6
  import { isObjectNumber, resolveTaskRef, stripPrefix } from '../id-resolve.js';
@@ -15,16 +15,29 @@ export class CloudAdapter {
15
15
  * both or neither may be set.
16
16
  */
17
17
  workspace;
18
+ /**
19
+ * The areas the caller EXPLICITLY narrowed a LIST to (`--area a,b`, `ORBITMAP_AREA=a,b`),
20
+ * already resolved by the one area ladder. Sent as the `area_id` query parameter.
21
+ *
22
+ * It is deliberately NOT {@link CloudAdapter.area}. Since API 9.0.0 the `X-Orbitmap-Area`
23
+ * header no longer narrows a list — the CLI sends it on every call made from an
24
+ * area-mapped directory, so honouring it there would leave `orbitmap tasks` answering with
25
+ * one area's work, which is the bug IN-nahetc exists to fix. The header stays the context
26
+ * for writes and single-entity reads; `area_id` is the only narrowing, and only an
27
+ * explicit `--area` fills it.
28
+ */
29
+ areaFilter;
18
30
  /**
19
31
  * Task reference → uuid, for the lifetime of this adapter. A command that touches the same
20
32
  * task twice (`dep add TS-a TS-b` resolves two, `status` then reads back) pays for one
21
33
  * `/tasks/by-number/{n}` lookup per distinct reference, not per call site.
22
34
  */
23
35
  taskIdCache = new Map();
24
- constructor(apiKey, area, apiUrl, workspace) {
36
+ constructor(apiKey, area, apiUrl, workspace, areaFilter) {
25
37
  this.apiKey = apiKey;
26
38
  this.area = area;
27
39
  this.workspace = workspace;
40
+ this.areaFilter = areaFilter ?? [];
28
41
  this.baseUrl = (apiUrl ?? DEFAULT_API_URL).replace(/\/$/, '');
29
42
  }
30
43
  async request(method, path, options) {
@@ -120,8 +133,11 @@ export class CloudAdapter {
120
133
  explainContextError(error) {
121
134
  // Dispatch on the canonical code, not the wire code: the API is renaming `PROJECT_*` to
122
135
  // `AREA_*` and both spellings must land here (see ERROR_CODE_ALIASES in errors.ts).
136
+ // `AREA_WORKSPACE_MISMATCH` (422) used to be handled here too. API 9.0.0 REMOVED it:
137
+ // the workspace ceiling refuses a foreign `X-Orbitmap-Workspace` with 403 ACCESS_DENIED
138
+ // before the area and the workspace are ever compared, so the code is unreachable and
139
+ // the dual-read is gone.
123
140
  if (error.canonicalCode !== WORKSPACE_CONTEXT_REQUIRED &&
124
- error.canonicalCode !== AREA_WORKSPACE_MISMATCH &&
125
141
  error.canonicalCode !== AREA_CONTEXT_REQUIRED) {
126
142
  return error;
127
143
  }
@@ -173,14 +189,55 @@ export class CloudAdapter {
173
189
  getWorkspaceId() {
174
190
  return this.workspace;
175
191
  }
192
+ /**
193
+ * The `area_id` query value for a LIST: what the caller explicitly asked for, falling back
194
+ * to the areas the ladder resolved from `--area` / `ORBITMAP_AREA`.
195
+ *
196
+ * Comma-joined, exactly as the API's own parser expects
197
+ * (`BaseAgentApiController::splitAreaRefs`). `undefined` — never an empty string — when
198
+ * nothing narrows, so the request carries no `area_id` at all and the answer spans every
199
+ * area the key reaches.
200
+ */
201
+ listAreaParam(areaId) {
202
+ const explicit = Array.isArray(areaId) ? areaId : areaId ? [areaId] : [];
203
+ const refs = explicit.length > 0 ? explicit : this.areaFilter;
204
+ const joined = refs
205
+ .flatMap((ref) => ref.split(','))
206
+ .map((ref) => ref.trim())
207
+ .filter((ref) => ref !== '')
208
+ .join(',');
209
+ return joined === '' ? undefined : joined;
210
+ }
211
+ /** `limit` / `cursor`, the two paging params every list shares. */
212
+ pagingParams(options) {
213
+ return {
214
+ limit: options?.limit === undefined ? undefined : String(options.limit),
215
+ cursor: options?.cursor,
216
+ };
217
+ }
176
218
  // ── Tasks ──
219
+ /**
220
+ * `GET /tasks` — every area the key reaches and every assignee, unless a filter says
221
+ * otherwise (API 9.0.0).
222
+ *
223
+ * `areaId` accepts one ref or several; several are comma-joined, which is the only way to
224
+ * narrow to two areas without two calls. `agentId` (`me`, a slug or a uuid), `unassigned`
225
+ * and `all` are the owner scope — `all` now names the default rather than escaping one.
226
+ * `intentId` / `missionId` take a uuid, `IN-xxxxxx`/`MS-xxxxxx` or the bare code.
227
+ */
177
228
  async getTasks(options) {
178
229
  return this.request('GET', '/tasks', {
179
230
  params: {
180
231
  status: options?.status,
181
232
  priority: options?.priority,
182
- area_id: options?.areaId,
233
+ area_id: this.listAreaParam(options?.areaId),
183
234
  orbit_id: options?.orbit,
235
+ intent_id: options?.intentId,
236
+ mission_id: options?.missionId,
237
+ agent_id: options?.agentId,
238
+ unassigned: options?.unassigned ? '1' : undefined,
239
+ all: options?.all ? '1' : undefined,
240
+ ...this.pagingParams(options),
184
241
  },
185
242
  });
186
243
  }
@@ -217,11 +274,21 @@ export class CloudAdapter {
217
274
  async getTaskByNumber(taskNumber) {
218
275
  return this.request('GET', `/tasks/by-number/${taskNumber}`);
219
276
  }
220
- async startTask(taskId) {
277
+ /**
278
+ * `POST /tasks/{id}/start`, or `POST /tasks/start` to let the server auto-pick.
279
+ *
280
+ * `confirmReassign` is the USER's answer to a 409 `REASSIGN_CONFIRMATION_REQUIRED`
281
+ * travelling back: it reassigns a task that belongs to another agent and starts it in one
282
+ * call. It is never sent on its own initiative — see `askToTakeOver` in
283
+ * `src/commands/start.ts`. Auto-confirming would turn "ask before taking someone else's
284
+ * work" into theatre.
285
+ */
286
+ async startTask(taskId, options) {
287
+ const body = options?.confirmReassign ? { confirm_reassign: true } : undefined;
221
288
  if (taskId) {
222
- return this.request('POST', `/tasks/${await this.taskId(taskId)}/start`);
289
+ return this.request('POST', `/tasks/${await this.taskId(taskId)}/start`, { body });
223
290
  }
224
- return this.request('POST', '/tasks/start');
291
+ return this.request('POST', '/tasks/start', { body });
225
292
  }
226
293
  async updateTaskStatus(taskId, status, deliveryStatus,
227
294
  // `force` (spec §6.2) is deliberately ignored in cloud mode: the API already allows
@@ -315,7 +382,9 @@ export class CloudAdapter {
315
382
  params: {
316
383
  type: options?.type,
317
384
  search: options?.search,
318
- area_id: options?.areaId,
385
+ status: options?.status,
386
+ area_id: this.listAreaParam(options?.areaId),
387
+ ...this.pagingParams(options),
319
388
  },
320
389
  });
321
390
  }
@@ -468,7 +537,9 @@ export class CloudAdapter {
468
537
  params: {
469
538
  status: options?.status,
470
539
  severity: options?.severity,
471
- limit: options?.limit,
540
+ area_id: this.listAreaParam(options?.areaId),
541
+ intent_id: options?.intentId,
542
+ ...this.pagingParams(options),
472
543
  },
473
544
  });
474
545
  }
@@ -498,7 +569,8 @@ export class CloudAdapter {
498
569
  params: {
499
570
  type: options?.type,
500
571
  since: options?.since,
501
- limit: options?.limit,
572
+ area_id: this.listAreaParam(options?.areaId),
573
+ ...this.pagingParams(options),
502
574
  },
503
575
  });
504
576
  }
@@ -511,7 +583,12 @@ export class CloudAdapter {
511
583
  // ── Ideas ──
512
584
  async listIdeas(options) {
513
585
  return this.request('GET', '/ideas', {
514
- params: { status: options?.status, priority: options?.priority, limit: options?.limit },
586
+ params: {
587
+ status: options?.status,
588
+ priority: options?.priority,
589
+ area_id: this.listAreaParam(options?.areaId),
590
+ ...this.pagingParams(options),
591
+ },
515
592
  });
516
593
  }
517
594
  /**
@@ -577,11 +654,26 @@ export class CloudAdapter {
577
654
  // ── Intents ──
578
655
  async listIntents(options) {
579
656
  return this.request('GET', '/intents', {
580
- params: { status: options?.status, limit: options?.limit },
657
+ params: {
658
+ status: options?.status,
659
+ mission_id: options?.missionId,
660
+ ...this.pagingParams(options),
661
+ },
581
662
  });
582
663
  }
583
- async getIntent(idOrNumber) {
584
- return this.request('GET', `/intents/${idOrNumber}`);
664
+ async getIntent(idOrNumber, options) {
665
+ return this.request('GET', `/intents/${idOrNumber}`, {
666
+ params: {
667
+ // `?logs=0` (API 10.0.0) skips the embedded work-log tail. The default —
668
+ // no parameter at all — keeps the body the endpoint always returned.
669
+ logs: options?.logs === false ? '0' : undefined,
670
+ // `?bundle=0` (API 10.1.0) strips the context bundle show embeds by default.
671
+ bundle: options?.bundle === false ? '0' : undefined,
672
+ // `?slim=1` (API 10.1.0) answers the freshness probe instead of the full
673
+ // body; server-side it wins over every other parameter.
674
+ slim: options?.slim ? '1' : undefined,
675
+ },
676
+ });
585
677
  }
586
678
  async createIntent(data) {
587
679
  return this.request('POST', '/intents', { body: data });
@@ -606,7 +698,10 @@ export class CloudAdapter {
606
698
  // ── Missions ──
607
699
  async listMissions(options) {
608
700
  return this.request('GET', '/missions', {
609
- params: { status: options?.status, limit: options?.limit },
701
+ params: {
702
+ status: options?.status,
703
+ ...this.pagingParams(options),
704
+ },
610
705
  });
611
706
  }
612
707
  async getMission(idOrNumber) {
@@ -654,8 +749,14 @@ export { CloudAdapter as OrbitMapClient };
654
749
  export async function createClient(explicitArea, options = {}) {
655
750
  const config = options.config ?? (await loadConfig());
656
751
  const auth = await resolveAuth({ config });
657
- const area = explicitArea ?? options.area ?? resolveArea({ config }).area;
752
+ // The ladder is walked once when the caller already did it (`createAdapter` always has),
753
+ // and only re-walked here for the historical `createClient(area)` entry point. `--area a,b`
754
+ // names a LIST filter and no context area, so the raw flag can no longer be used as the
755
+ // header value — it goes through `resolveArea`, which splits the two apart.
756
+ const resolved = options.area !== undefined || options.areas !== undefined
757
+ ? { area: options.area, areas: options.areas ?? [] }
758
+ : resolveArea({ config, ...(explicitArea !== undefined ? { area: explicitArea } : {}) });
658
759
  const workspace = options.workspace ?? resolveWorkspaceId({ config }).workspace;
659
- return new CloudAdapter(auth.apiKey, area, auth.apiUrl, workspace);
760
+ return new CloudAdapter(auth.apiKey, resolved.area, auth.apiUrl, workspace, resolved.areas);
660
761
  }
661
762
  //# sourceMappingURL=cloud.js.map