@lambdacurry/arbor 0.6.5 → 0.7.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 (2) hide show
  1. package/dist/arbor.js +177 -90
  2. package/package.json +1 -1
package/dist/arbor.js CHANGED
@@ -1345,6 +1345,7 @@ var profiles = sqliteTable("profiles", {
1345
1345
  color: text("color").$type(),
1346
1346
  emoji: text("emoji"),
1347
1347
  description: text("description"),
1348
+ notificationPrefs: text("notification_prefs", { mode: "json" }).$type(),
1348
1349
  createdAt: ts("created_at").notNull()
1349
1350
  }, (t) => ({ orgIdx: index("profiles_org_idx").on(t.orgId) }));
1350
1351
  var clients = sqliteTable("clients", {
@@ -1560,12 +1561,28 @@ var artifacts = sqliteTable("artifacts", {
1560
1561
  supersedes: text("supersedes", { mode: "json" }).$type(),
1561
1562
  supersededBy: text("superseded_by", { mode: "json" }).$type(),
1562
1563
  reviewAfter: ts("review_after"),
1563
- createdAt: ts("created_at").notNull()
1564
+ createdAt: ts("created_at").notNull(),
1565
+ kind: text("kind").$type().notNull().default("file"),
1566
+ source: text("source"),
1567
+ version: integer("version").notNull().default(0),
1568
+ editPolicy: text("edit_policy").$type().notNull().default("members")
1564
1569
  }, (t) => ({
1565
1570
  statusIdx: index("artifacts_status_idx").on(t.status),
1566
1571
  tierIdx: index("artifacts_tier_idx").on(t.retrievalTier),
1567
1572
  ownerIdx: index("artifacts_owner_idx").on(t.ownerProfileId)
1568
1573
  }));
1574
+ var artifactVersions = sqliteTable("artifact_versions", {
1575
+ id: text("id").primaryKey(),
1576
+ artifactId: text("artifact_id").notNull().references(() => artifacts.id),
1577
+ version: integer("version").notNull(),
1578
+ source: text("source").notNull(),
1579
+ editorProfileId: text("editor_profile_id").notNull().references(() => profiles.id),
1580
+ executionContextId: text("execution_context_id").references(() => executionContexts.id),
1581
+ note: text("note"),
1582
+ createdAt: ts("created_at").notNull()
1583
+ }, (t) => ({
1584
+ artifactVersionUq: uniqueIndex("artifact_versions_artifact_version_uq").on(t.artifactId, t.version)
1585
+ }));
1569
1586
  var requests = sqliteTable("requests", {
1570
1587
  id: text("id").primaryKey(),
1571
1588
  threadId: text("thread_id").notNull().references(() => threads.id),
@@ -1624,9 +1641,11 @@ var notifications = sqliteTable("notifications", {
1624
1641
  groupKey: text("group_key"),
1625
1642
  actors: text("actors", { mode: "json" }).$type().notNull().default([]),
1626
1643
  readAt: ts("read_at"),
1644
+ emailedAt: ts("emailed_at"),
1627
1645
  createdAt: ts("created_at").notNull()
1628
1646
  }, (t) => ({
1629
1647
  recipientIdx: index("notifications_recipient_idx").on(t.recipientProfileId, t.readAt),
1648
+ emailIdx: index("notifications_emailed_idx").on(t.emailedAt),
1630
1649
  groupIdx: uniqueIndex("notifications_group_key_uniq").on(t.groupKey)
1631
1650
  }));
1632
1651
  var notificationCursors = sqliteTable("notification_cursors", {
@@ -1729,6 +1748,7 @@ var INDEX_ON = new Set([
1729
1748
  "contribution.added",
1730
1749
  "contribution.edited",
1731
1750
  "artifact.created",
1751
+ "artifact.edited",
1732
1752
  "review.added",
1733
1753
  "thread.created",
1734
1754
  "topic.created"
@@ -16012,24 +16032,25 @@ config(en_default());
16012
16032
  // ../actions/src/index.ts
16013
16033
  var ORIENTATION = `Arbor is your team's deliberation room and shared memory — people and agents settle typed work here, and Arbor remembers what's decided. To work well:
16014
16034
 
16015
- 1. RECALL FIRST. Run \`recall\` before re-deriving or restating anything — it may already be settled; cite prior work ([label](#con_…)) and build on it. Phrase the query in PROBLEM-LANGUAGE (a natural-language question — "how do agents handle X"), not extracted keywords; it ranks better. Empty recall is itself worth noting. WHY: a room where everyone re-derives is just a chat log.
16016
- 2. CONTRIBUTE typed points — ONE point per contribution, with the type that names your move (proposal / critique / question / evidence / risk / correction / assertion / decision). Markdown welcome; put references IN your prose (a URL or [label](#con_…) becomes a navigable reference — there's no separate links field). WHY: one typed point is reviewable on its own.
16035
+ 1. RECALL FIRST — but Arbor is MEMORY, NOT TRUTH. Run \`recall\` before re-deriving or restating anything — it may already be settled; cite prior work ([label](#con_…)) and build on it. Phrase the query in PROBLEM-LANGUAGE (a natural-language question — "how do agents handle X"), not extracted keywords; it ranks better. Empty recall is itself worth noting. A contribution records what was true when it was WRITTEN, so before you assert the CURRENT state of anything outside Arbor — a PR, a build, a deploy, a config — check the live source; another contribution is not evidence of the present, and neither is a local copy of something whose home is elsewhere. WHY: a room where everyone re-derives is just a chat log — but a room that mistakes its own memory for the world confidently reports blockers that no longer exist.
16036
+ 2. CONTRIBUTE typed points — ONE point per contribution, with the type that names your move (proposal / critique / question / evidence / risk / correction / assertion / decision). Markdown welcome; put references IN your prose (a URL or [label](#con_…) becomes a navigable reference). Prose refs are CITATIONS they never move your contribution in the thread, so cite freely; to REPLY under a specific contribution, pass links: [{rel: 'inReplyTo', targetId}] (AD-196). WHY: one typed point is reviewable on its own, and a synthesis citing five points should read as the most top-level thing in the thread, not as a reply to the first one it mentions.
16017
16037
  3. ANSWER through requests. When \`inbox\` or a thread shows an open request you can meet, answer THROUGH it — \`respond\` to it, or \`stamp\` the contribution a review request is about — so it completes and the requester is notified. WHY: a plain reply that merely happens to answer leaves their request hanging (the most common failure).
16018
- 4. REVIEW honestly; promote the standout, sparingly. \`stamp\` to vouch or push back with a one-line why (you can't stamp your own work — request a review via \`request\`). PROMOTE a contribution/artifact only when it's the standout the org should find FIRST (the \uD83C\uDF96️). WHY: promotion is curation, not applause — and it's about an OUTPUT, never a whole thread (a thread RESOLVES; it is never "promoted").
16038
+ 4. REVIEW honestly; promote the standout, sparingly. \`stamp\` to vouch or push back with a one-line why (you can't stamp your own work — request a review via \`request\`). PROMOTE a contribution/artifact only when it's the standout the org should find FIRST (the \uD83C\uDF96️). One deliberate exception (AD-200): in a STANDING thread, periodically promoting a distilling synthesis IS the job — it's how an open-forever lane compresses for newcomers and recall, not applause inflation; promoting your own synthesis there is fine (only stamps bar self-review). WHY: promotion is curation, not applause — and it's about an OUTPUT, never a whole thread (a thread RESOLVES; it is never "promoted").
16019
16039
  5. EDIT, don't repost. \`edit\` your own contribution to sharpen it; the record stays audited. A genuinely different point is a NEW contribution.
16020
- 6. FIT STRUCTURE TO THE WORK — one thread settles one objective, one topic holds one area — and CLOSE it when done. Before adding to a thread, ask whether your point serves ITS objective or starts a new one: a genuinely new question deserves its own thread (check \`tree\`, name it by its objective, make the first contribution); a distinct area that's accumulating threads deserves its own topic. When a thread reaches its conclusion, \`transition_thread --to resolved\`; opened in the wrong place → \`--to archived\` and reopen where it belongs. WHY: structure should mirror the work so each thread settles one findable question — a thread crammed with many objectives is as unreadable as a litter of one-off topics, and open threads no one closes bury the live ones.
16040
+ 6. FIT STRUCTURE TO THE WORK — one thread settles one objective, one topic holds one area — and CLOSE it when done. Before adding to a thread, ask whether your point serves ITS objective or starts a new one: a genuinely new question deserves its own thread (check \`tree\`, name it by its objective, make the first contribution); a distinct area that's accumulating threads deserves its own topic. When a thread reaches its conclusion, \`transition_thread --to resolved\`; opened in the wrong place → \`--to archived\` and reopen where it belongs. A thread DESIGNED to stay open — a journal, ops log, or standing lane — is \`--to standing\` (AD-200): it is never owed a conclusion, and its distillation is PROMOTION (\uD83C\uDF96️ a synthesis), not closure; "close it when done" applies to deliberations, not standing lanes. CLOSING IS MEMBER WORK: any member of the space can resolve a thread whose objective is met — you do not need its author's permission, and you do not need to be the one who did the work. Asking a human to perform the close is not deference; it is an open thread with extra steps. If the objective is met, close it and say why. WHY: structure should mirror the work so each thread settles one findable question — a thread crammed with many objectives is as unreadable as a litter of one-off topics, and open threads no one closes bury the live ones.
16041
+ 7. ACT, THEN REPORT — escalate by exception (AD-197). Before routing a decision to a human, do the CHEAP CHECK that would let you act yourself — the registry lookup, the live-state read, the policy check; the blocker is usually fictional. Then: recoverable + conclusive evidence → act and say why. Recoverable but uncertain → state your intent WITH a deadline ("resolving Thursday unless someone objects") and act at the deadline. Irreversible, outward-facing, or a genuine taste/priority call → escalate; that's what humans are for. Reversibility is the test you can always answer in the moment — a wrong resolve costs one click to undo (\`resolved\` → \`active\`), while waiting costs days. WHY: a room where every met condition waits for a human keystroke makes the human the bottleneck and the agents decorative at exactly the moments they're most useful.
16021
16042
 
16022
16043
  Run \`arbor help\` (or read the MCP tool list) for the exact command/flags — those stay generated from the live action surface, so they're always current.`;
16023
16044
  function forward(operation) {
16024
16045
  return (ex, input) => ex.call(operation, input);
16025
16046
  }
16026
- function dispatch(map2) {
16047
+ function dispatch(map2, reshape) {
16027
16048
  return (ex, input) => {
16028
16049
  const { verb, ...rest } = input;
16029
16050
  const op = verb ? map2[verb] : undefined;
16030
16051
  if (!op)
16031
16052
  return Promise.reject(new Error(`unknown verb "${verb ?? ""}" — expected one of: ${Object.keys(map2).join(" | ")}`));
16032
- return ex.call(op, rest);
16053
+ return ex.call(op, reshape && verb ? reshape(verb, rest) : rest);
16033
16054
  };
16034
16055
  }
16035
16056
  var PAGINATION_INPUT = {
@@ -16050,13 +16071,13 @@ var ACTIONS = [
16050
16071
  includeHistorical: exports_external.boolean().optional().describe("include superseded/archived knowledge (default false)")
16051
16072
  },
16052
16073
  surfaces: ["mcp", "cli"],
16053
- tier: "core",
16074
+ toolset: "loop",
16054
16075
  run: forward("knowledge.recall")
16055
16076
  },
16056
16077
  {
16057
16078
  name: "contribute",
16058
16079
  title: "Contribute to a thread",
16059
- description: "Add your own typed contribution (proposal/critique/question/evidence/…) — ONE point per contribution, on the record. RECALL FIRST (recalling beats re-deriving), and put references IN THE BODY: URLs and inline [label](#con_…) refs become typed reference edges + smart pills automatically.",
16080
+ description: "Add your own typed contribution (proposal/critique/question/evidence/…) — ONE point per contribution, on the record. RECALL FIRST (recalling beats re-deriving), and put references IN THE BODY: URLs and inline [label](#con_…) refs become typed reference edges + smart pills automatically. Body refs are CITATIONS (AD-196) — they never change where your contribution sits in the thread, so cite freely. To thread your contribution UNDER a specific one (a reply), pass links: [{rel: 'inReplyTo', targetId}] — that deliberate edge is what nests it.",
16060
16081
  inputSchema: {
16061
16082
  threadId: exports_external.string().describe("the thread id, e.g. thr_…"),
16062
16083
  type: exports_external.enum(CONTRIBUTION_TYPES).describe("the contribution type (AD-066 taxonomy)"),
@@ -16068,23 +16089,23 @@ var ACTIONS = [
16068
16089
  id: exports_external.string(),
16069
16090
  label: exports_external.string()
16070
16091
  })).optional().describe("inline @-mentions in the body; person mentions notify"),
16071
- links: exports_external.array(exports_external.object({ rel: exports_external.enum(CONTRIBUTION_LINK_RELS), targetId: exports_external.string() })).optional().describe("internal typed links: { rel, targetId } (same-space). External refs go IN THE BODY as URLs.")
16092
+ links: exports_external.array(exports_external.object({ rel: exports_external.enum(CONTRIBUTION_LINK_RELS), targetId: exports_external.string() })).optional().describe("internal typed links: { rel, targetId } (same-space) — a DELIBERATE edge: inReplyTo/contests nests your contribution under the target (AD-196); body refs never do. External refs go IN THE BODY as URLs.")
16072
16093
  },
16073
16094
  surfaces: ["mcp", "cli"],
16074
- tier: "core",
16095
+ toolset: "loop",
16075
16096
  run: forward("contribution.add")
16076
16097
  },
16077
16098
  {
16078
16099
  name: "edit",
16079
16100
  title: "Edit your contribution",
16080
- description: "Edit the BODY of your own contribution (AD-132 — editable-but-audited: the displayed text changes, an 'edited' marker appears, and the event log keeps the previous body). Author-only; the type and links are fixed — a different point is a NEW contribution, not an edit.",
16101
+ description: "Edit the BODY of your own contribution (AD-132 — editable-but-audited: the displayed text changes, an 'edited' marker appears, and the event log keeps the previous body). Author-only; the type and DELIBERATE links (reply edges, fulfills) are fixed — body-borne prose refs follow the edited body: a [label](#con_…) you edit in gains its citation edge, one you edit out drops it (AD-196). A different point is a NEW contribution, not an edit.",
16081
16102
  inputSchema: {
16082
16103
  contributionId: exports_external.string().describe("your contribution's id, con_…"),
16083
16104
  body: exports_external.string().min(1).describe("the replacement markdown body"),
16084
16105
  summary: exports_external.string().max(300).optional().describe("optional ≤300-char gist — omit to leave unchanged, empty string to clear")
16085
16106
  },
16086
16107
  surfaces: ["mcp", "cli"],
16087
- tier: "core",
16108
+ toolset: "loop",
16088
16109
  run: forward("contribution.update")
16089
16110
  },
16090
16111
  {
@@ -16103,7 +16124,7 @@ var ACTIONS = [
16103
16124
  })).optional().describe("inline @-mentions in the note; person mentions notify")
16104
16125
  },
16105
16126
  surfaces: ["mcp", "cli"],
16106
- tier: "core",
16127
+ toolset: "loop",
16107
16128
  run: forward("review.add")
16108
16129
  },
16109
16130
  {
@@ -16116,7 +16137,7 @@ var ACTIONS = [
16116
16137
  emoji: exports_external.string().min(1)
16117
16138
  },
16118
16139
  surfaces: ["mcp", "cli"],
16119
- tier: "extended",
16140
+ toolset: "loop",
16120
16141
  run: forward("reaction.toggle")
16121
16142
  },
16122
16143
  {
@@ -16129,7 +16150,7 @@ var ACTIONS = [
16129
16150
  purpose: exports_external.string().optional().describe("one line on what belongs here")
16130
16151
  },
16131
16152
  surfaces: ["mcp", "cli"],
16132
- tier: "extended",
16153
+ toolset: "structure",
16133
16154
  run: forward("topic.create")
16134
16155
  },
16135
16156
  {
@@ -16141,7 +16162,7 @@ var ACTIONS = [
16141
16162
  guidance: exports_external.array(exports_external.string()).describe("the full guidance list — replaces the existing one; [] clears it")
16142
16163
  },
16143
16164
  surfaces: ["cli"],
16144
- tier: "extended",
16165
+ toolset: "spaces",
16145
16166
  run: forward("topic.update")
16146
16167
  },
16147
16168
  {
@@ -16155,7 +16176,7 @@ var ACTIONS = [
16155
16176
  objective: exports_external.string().min(1).describe("what settled/done looks like for this thread")
16156
16177
  },
16157
16178
  surfaces: ["mcp", "cli"],
16158
- tier: "core",
16179
+ toolset: "loop",
16159
16180
  run: forward("thread.create")
16160
16181
  },
16161
16182
  {
@@ -16167,38 +16188,71 @@ var ACTIONS = [
16167
16188
  waysToHelp: exports_external.array(exports_external.string()).describe("the full ways-to-help list — replaces the existing one; [] clears it")
16168
16189
  },
16169
16190
  surfaces: ["cli"],
16170
- tier: "extended",
16191
+ toolset: "spaces",
16171
16192
  run: forward("thread.update")
16172
16193
  },
16173
16194
  {
16174
16195
  name: "transition_thread",
16175
16196
  title: "Move a thread's lifecycle",
16176
- description: "Move a thread along its lifecycle (AD-068/159): `resolved` = the deliberation reached its conclusion; `archived` = retired / wrong place (both recoverable to `active`); `stuck`/`needs-review` flag an open thread. Threads RESOLVE — standout outputs get promoted via `curation`; there is no promote-thread. Illegal jumps are rejected",
16197
+ description: "Move a thread along its lifecycle (AD-068/159/200): `resolved` = the deliberation reached its conclusion; `archived` = retired / wrong place (both recoverable to `active`); `stuck`/`needs-review` flag an open thread; `standing` = DESIGNED to stay open (a journal, ops log, or standing lane — never owed a conclusion; distill with promotions instead of closing). Threads RESOLVE — standout outputs get promoted via `curation`; there is no promote-thread. Illegal jumps are rejected",
16177
16198
  inputSchema: {
16178
16199
  threadId: exports_external.string().describe("the thread to move, thr_…"),
16179
- to: exports_external.string().describe("target status: active|needs-review|stuck|resolved|archived"),
16200
+ to: exports_external.string().describe("target status: active|needs-review|stuck|standing|resolved|archived"),
16180
16201
  reason: exports_external.string().optional().describe("optional one-line why, recorded on the event")
16181
16202
  },
16182
16203
  surfaces: ["mcp", "cli"],
16183
- tier: "core",
16204
+ toolset: "loop",
16184
16205
  run: forward("thread.transition")
16185
16206
  },
16186
16207
  {
16187
16208
  name: "create_artifact",
16188
16209
  title: "Create an artifact",
16189
- description: "Promote durable output — a decision, summary, plan, or research brief — into a first-class artifact you own. Reach for this when a thread has produced something worth retaining and citing beyond the conversation; its retrieval tier follows its status.",
16210
+ description: "Promote durable output — a decision, summary, plan, or research brief — into a first-class artifact you own. Text kinds (doc/table/diagram/html) are living documents: pass `source` (born at v1, editable in place via edit_artifact); kind=file passes a contentRef instead. Reach for this when a thread has produced something worth retaining and citing beyond the conversation.",
16190
16211
  inputSchema: {
16191
- type: exports_external.string().describe("artifact type, e.g. decision|summary|plan|markdown|concept|research-brief"),
16212
+ type: exports_external.string().describe("semantic type, e.g. decision|summary|plan|markdown|table|research-brief"),
16213
+ kind: exports_external.enum(["doc", "table", "diagram", "html", "file"]).optional().describe("structural kind (default file). doc/table/diagram/html are editable, versioned text"),
16192
16214
  title: exports_external.string().min(1),
16193
- contentRef: exports_external.string().min(1).describe("where the content lives, e.g. inline://… or a URL"),
16215
+ source: exports_external.string().optional().describe("text kinds: the initial content (v1)"),
16216
+ contentRef: exports_external.string().optional().describe("kind=file: where the content lives, e.g. inline://… or a URL"),
16217
+ editPolicy: exports_external.enum(["owner", "members", "anyone"]).optional().describe("who may edit (default members)"),
16194
16218
  summary: exports_external.string().optional(),
16195
16219
  sourceThreadId: exports_external.string().optional(),
16196
16220
  sourceContributionIds: exports_external.array(exports_external.string()).optional()
16197
16221
  },
16198
16222
  surfaces: ["cli"],
16199
- tier: "extended",
16223
+ toolset: "artifacts",
16200
16224
  run: forward("artifact.create")
16201
16225
  },
16226
+ {
16227
+ name: "get_artifact",
16228
+ title: "Read an artifact",
16229
+ description: "Read an artifact: current source + version + history (who edited, via what). READ BEFORE YOU EDIT — the version you read is the baseVersion your edit must name. Pass --version to read an old snapshot.",
16230
+ inputSchema: {
16231
+ artifactId: exports_external.string().describe("the artifact, art_…"),
16232
+ version: exports_external.number().optional().describe("read this old snapshot instead of the current")
16233
+ },
16234
+ surfaces: ["cli"],
16235
+ toolset: "artifacts",
16236
+ run: forward("artifact.get")
16237
+ },
16238
+ {
16239
+ name: "edit_artifact",
16240
+ title: "Edit a text artifact",
16241
+ description: `Edit a text-backed artifact (doc/table/diagram/html) in place. Read it first (get_artifact) and pass baseVersion = the version you read. Either --source (full replace) or --patches '[{"old":…,"new":…}]' (exact-match hunks, applied in order, ATOMIC — all land or none; one call mints ONE version, so batch related row-edits into one call). A stale baseVersion returns ok:false with the current source — reconcile and retry; a failed patch names the culprit index.`,
16242
+ inputSchema: {
16243
+ artifactId: exports_external.string().describe("the artifact, art_…"),
16244
+ baseVersion: exports_external.number().describe("the version you READ (get_artifact) — stale is rejected"),
16245
+ source: exports_external.string().optional().describe("full replace: the complete new content"),
16246
+ patches: exports_external.array(exports_external.object({ old: exports_external.string(), new: exports_external.string() })).optional().describe("exact-match hunks applied in order; each old must match exactly once"),
16247
+ note: exports_external.string().optional().describe("one-line summary of what changed")
16248
+ },
16249
+ surfaces: ["cli"],
16250
+ toolset: "artifacts",
16251
+ run: (ex, input) => ex.call("artifact.edit", {
16252
+ ...input,
16253
+ verb: Array.isArray(input.patches) ? "patch" : "replace"
16254
+ })
16255
+ },
16202
16256
  {
16203
16257
  name: "transition_artifact",
16204
16258
  title: "Transition an artifact's lifecycle",
@@ -16209,7 +16263,7 @@ var ACTIONS = [
16209
16263
  reason: exports_external.string().optional()
16210
16264
  },
16211
16265
  surfaces: ["cli"],
16212
- tier: "extended",
16266
+ toolset: "artifacts",
16213
16267
  run: forward("artifact.transition")
16214
16268
  },
16215
16269
  {
@@ -16221,18 +16275,18 @@ var ACTIONS = [
16221
16275
  sourceContributionId: exports_external.string().describe("the contribution it's produced by, con_… (must share the attachment's thread)")
16222
16276
  },
16223
16277
  surfaces: ["cli"],
16224
- tier: "extended",
16278
+ toolset: "artifacts",
16225
16279
  run: forward("attachment.promote")
16226
16280
  },
16227
16281
  {
16228
16282
  name: "promote_contribution",
16229
16283
  title: "Promote a contribution to a \uD83C\uDF96️ standout",
16230
- description: "Mark a contribution a \uD83C\uDF96️ standout — recognition that boosts it in recall and flags it as one of the thread's outputs (AD-140: promotion is a MARK on the contribution, not a separate artifact). Reach for this when a contribution is worth recognizing. MANY contributions in a thread can be standouts (there's no single 'answer'); idempotent — re-promoting is a quiet no-op.",
16284
+ description: "Mark a contribution a \uD83C\uDF96️ standout — recognition that boosts it in recall and flags it as one of the thread's outputs (AD-140: promotion is a MARK on the contribution, not a separate artifact). Reach for this when a contribution is worth recognizing — and routinely in a STANDING thread, where promoting the periodic distilling synthesis is the lane's compression mechanism (AD-200). MANY contributions in a thread can be standouts (there's no single 'answer'); idempotent — re-promoting is a quiet no-op.",
16231
16285
  inputSchema: {
16232
16286
  contributionId: exports_external.string().describe("the contribution to promote, con_… (its thread + author are derived)")
16233
16287
  },
16234
16288
  surfaces: ["cli"],
16235
- tier: "extended",
16289
+ toolset: "curation",
16236
16290
  run: forward("contribution.promote")
16237
16291
  },
16238
16292
  {
@@ -16243,7 +16297,7 @@ var ACTIONS = [
16243
16297
  contributionId: exports_external.string().describe("the standout contribution to demote, con_…")
16244
16298
  },
16245
16299
  surfaces: ["cli"],
16246
- tier: "extended",
16300
+ toolset: "curation",
16247
16301
  run: forward("contribution.demote")
16248
16302
  },
16249
16303
  {
@@ -16254,7 +16308,7 @@ var ACTIONS = [
16254
16308
  contributionId: exports_external.string().describe("the contribution to remove, con_…")
16255
16309
  },
16256
16310
  surfaces: ["cli"],
16257
- tier: "extended",
16311
+ toolset: "curation",
16258
16312
  run: forward("contribution.delete")
16259
16313
  },
16260
16314
  {
@@ -16265,7 +16319,7 @@ var ACTIONS = [
16265
16319
  contributionId: exports_external.string().describe("the removed contribution to restore, con_…")
16266
16320
  },
16267
16321
  surfaces: ["cli"],
16268
- tier: "extended",
16322
+ toolset: "curation",
16269
16323
  run: forward("contribution.restore")
16270
16324
  },
16271
16325
  {
@@ -16283,7 +16337,7 @@ var ACTIONS = [
16283
16337
  expiresAt: exports_external.string().optional().describe("ISO-8601 deadline (default: 14 days from creation, AD-167)")
16284
16338
  },
16285
16339
  surfaces: ["mcp", "cli"],
16286
- tier: "core",
16340
+ toolset: "loop",
16287
16341
  run: forward("request.create")
16288
16342
  },
16289
16343
  {
@@ -16297,7 +16351,7 @@ var ACTIONS = [
16297
16351
  confidence: exports_external.number().int().min(0).max(100).optional()
16298
16352
  },
16299
16353
  surfaces: ["mcp", "cli"],
16300
- tier: "core",
16354
+ toolset: "loop",
16301
16355
  run: forward("request.respond")
16302
16356
  },
16303
16357
  {
@@ -16309,7 +16363,7 @@ var ACTIONS = [
16309
16363
  contributionId: exports_external.string().describe("the existing contribution that responds to it, con_…")
16310
16364
  },
16311
16365
  surfaces: ["mcp", "cli"],
16312
- tier: "core",
16366
+ toolset: "loop",
16313
16367
  run: forward("request.bind")
16314
16368
  },
16315
16369
  {
@@ -16320,7 +16374,7 @@ var ACTIONS = [
16320
16374
  requestId: exports_external.string().describe("the open request to cancel, req_…")
16321
16375
  },
16322
16376
  surfaces: ["mcp", "cli"],
16323
- tier: "core",
16377
+ toolset: "loop",
16324
16378
  run: forward("request.cancel")
16325
16379
  },
16326
16380
  {
@@ -16332,7 +16386,7 @@ var ACTIONS = [
16332
16386
  ...PAGINATION_INPUT
16333
16387
  },
16334
16388
  surfaces: ["mcp", "cli"],
16335
- tier: "core",
16389
+ toolset: "loop",
16336
16390
  run: forward("inbox.list")
16337
16391
  },
16338
16392
  {
@@ -16343,7 +16397,7 @@ var ACTIONS = [
16343
16397
  inboxItemId: exports_external.string().describe("the inbox item to dismiss, inb_…")
16344
16398
  },
16345
16399
  surfaces: ["mcp", "cli"],
16346
- tier: "core",
16400
+ toolset: "loop",
16347
16401
  run: forward("inbox.dismiss")
16348
16402
  },
16349
16403
  {
@@ -16352,7 +16406,7 @@ var ACTIONS = [
16352
16406
  description: "List the awareness feed of activity involving you — stamps on your work, replies in your threads, @-mentions, and your completed requests — newest first, with an unseen count. Each item carries a triage `class` (AD-182) derived from thread state: `settled-context` (thread resolved — durable context, never live work), `owed-by-me` (an open ask targets you — go to `inbox`, this row is just a pointer), `judgment-available` (your work was engaged in a live thread — an invitation, not an obligation), `closed-loop` (your ask completed), `room-motion` (ambient activity, safely skippable). This is FYI (no response owed); `inbox` remains the only obligation surface.",
16353
16407
  inputSchema: { ...PAGINATION_INPUT },
16354
16408
  surfaces: ["cli"],
16355
- tier: "extended",
16409
+ toolset: "loop",
16356
16410
  run: forward("notification.list")
16357
16411
  },
16358
16412
  {
@@ -16363,7 +16417,7 @@ var ACTIONS = [
16363
16417
  notificationId: exports_external.string().describe("the notification id, ntf_…")
16364
16418
  },
16365
16419
  surfaces: ["cli"],
16366
- tier: "extended",
16420
+ toolset: "loop",
16367
16421
  run: forward("notification.markRead")
16368
16422
  },
16369
16423
  {
@@ -16372,7 +16426,7 @@ var ACTIONS = [
16372
16426
  description: "Mark every unread notification in your feed read in one call; returns how many were cleared. Reach for this to zero out your awareness feed after a catch-up.",
16373
16427
  inputSchema: {},
16374
16428
  surfaces: ["cli"],
16375
- tier: "extended",
16429
+ toolset: "loop",
16376
16430
  run: forward("notification.markAllRead")
16377
16431
  },
16378
16432
  {
@@ -16381,7 +16435,7 @@ var ACTIONS = [
16381
16435
  description: "Return your own Arbor identity (profile + source client) as resolved from your credential. Reach for this to confirm which profile and org you're acting as before you write.",
16382
16436
  inputSchema: {},
16383
16437
  surfaces: ["mcp", "cli"],
16384
- tier: "core",
16438
+ toolset: "loop",
16385
16439
  run: forward("me.get")
16386
16440
  },
16387
16441
  {
@@ -16395,7 +16449,7 @@ var ACTIONS = [
16395
16449
  capabilities: exports_external.array(exports_external.string()).optional().describe("free-form skill tags (≤20, each ≤60 chars) for what you can do — e.g. ['market-research','data-viz']; how teammates route work to you. An empty array clears them.")
16396
16450
  },
16397
16451
  surfaces: ["mcp", "cli"],
16398
- tier: "extended",
16452
+ toolset: "spaces",
16399
16453
  run: forward("me.update")
16400
16454
  },
16401
16455
  {
@@ -16408,7 +16462,7 @@ var ACTIONS = [
16408
16462
  profileId: exports_external.string().optional().describe("admin override — set THIS member's charter instead of your own (org owner/admin only)")
16409
16463
  },
16410
16464
  surfaces: ["cli"],
16411
- tier: "extended",
16465
+ toolset: "spaces",
16412
16466
  run: forward("space.setCharter")
16413
16467
  },
16414
16468
  {
@@ -16421,20 +16475,20 @@ var ACTIONS = [
16421
16475
  contribCursor: exports_external.string().optional().describe("continuation cursor from a previous page's contributionPage.nextCursor")
16422
16476
  },
16423
16477
  surfaces: ["mcp", "cli"],
16424
- tier: "core",
16478
+ toolset: "loop",
16425
16479
  run: forward("thread.get")
16426
16480
  },
16427
16481
  {
16428
16482
  name: "tree",
16429
16483
  title: "Navigate the workspace tree",
16430
- description: 'Map the workspace — a GLANCEABLE structure map, not a dump. Depths: `spaces` | `topics` (default: spaces → topics + light roster) | `threads` (adds each topic\'s threads with status + open-request counts; `openRequestsForYou` answers "does this space need me?" — AD-182). Scope with space/topic; drill down with space_get/topic_get/thread_get instead of inflating the tree',
16484
+ description: 'Map the workspace — a GLANCEABLE structure map, not a dump. Depths: `spaces` | `topics` (default: spaces → topics + light roster) | `threads` (adds each topic\'s threads with status, open-request counts `openRequestsForYou` answers "does this space need me?" (AD-182) plus `lastActivityAt` + `standouts` (AD-198): a quiet-for-days ACTIVE thread with a standout is worth OPENING). These are TRIAGE signals — where to look, never what to do: a quiet thread may be closeable, waiting on a human, or a monitor doing its job, and reading it IS the cheap check before any resolve. Scope with space/topic; drill down with space_get/topic_get/thread_get instead of inflating the tree',
16431
16485
  inputSchema: {
16432
16486
  space: exports_external.string().optional().describe("scope to one space id (spc_…)"),
16433
16487
  topic: exports_external.string().optional().describe("scope to one topic id (top_…)"),
16434
16488
  depth: exports_external.enum(["spaces", "topics", "threads"]).optional().describe("how deep to expand: spaces (just spaces) | topics (default: + topics + light roster) | threads (+ each topic's threads). For one topic's threads or one thread's contributions, prefer topic_get / thread_get.")
16435
16489
  },
16436
16490
  surfaces: ["mcp", "cli"],
16437
- tier: "core",
16491
+ toolset: "loop",
16438
16492
  run: forward("navigation.tree")
16439
16493
  },
16440
16494
  {
@@ -16443,7 +16497,7 @@ var ACTIONS = [
16443
16497
  description: "List everyone in your org — humans and agents — with their profile id, identity, role, and self-declared capabilities. Reach for this to find WHO to route work to: the profile id to @-mention in a contribution, or to target a `request` at a specific person (e.g. request a review from Meg). Read-only.",
16444
16498
  inputSchema: {},
16445
16499
  surfaces: ["mcp", "cli"],
16446
- tier: "core",
16500
+ toolset: "loop",
16447
16501
  run: forward("org.members")
16448
16502
  },
16449
16503
  {
@@ -16454,7 +16508,7 @@ var ACTIONS = [
16454
16508
  spaceId: exports_external.string().describe("the space id to join, e.g. spc_…")
16455
16509
  },
16456
16510
  surfaces: ["cli"],
16457
- tier: "extended",
16511
+ toolset: "spaces",
16458
16512
  run: forward("space.join")
16459
16513
  },
16460
16514
  {
@@ -16465,7 +16519,7 @@ var ACTIONS = [
16465
16519
  spaceId: exports_external.string().describe("the space id to leave, e.g. spc_…")
16466
16520
  },
16467
16521
  surfaces: ["cli"],
16468
- tier: "extended",
16522
+ toolset: "spaces",
16469
16523
  run: forward("space.leave")
16470
16524
  },
16471
16525
  {
@@ -16477,7 +16531,7 @@ var ACTIONS = [
16477
16531
  profileId: exports_external.string().describe("the profile id to add, e.g. prof_… (agent → granted, human → invited)")
16478
16532
  },
16479
16533
  surfaces: ["cli"],
16480
- tier: "extended",
16534
+ toolset: "spaces",
16481
16535
  run: forward("space.addMember")
16482
16536
  },
16483
16537
  {
@@ -16488,7 +16542,7 @@ var ACTIONS = [
16488
16542
  invitationId: exports_external.string().describe("the space invitation id, e.g. sinv_…")
16489
16543
  },
16490
16544
  surfaces: ["cli"],
16491
- tier: "extended",
16545
+ toolset: "spaces",
16492
16546
  run: forward("space.acceptInvite")
16493
16547
  },
16494
16548
  {
@@ -16497,7 +16551,7 @@ var ACTIONS = [
16497
16551
  description: "Discover the OPEN spaces in your org you could join. Each is flagged with whether you're already a member; private spaces are never listed.",
16498
16552
  inputSchema: { ...PAGINATION_INPUT },
16499
16553
  surfaces: ["cli"],
16500
- tier: "extended",
16554
+ toolset: "spaces",
16501
16555
  run: forward("space.listOpen")
16502
16556
  },
16503
16557
  {
@@ -16506,7 +16560,7 @@ var ACTIONS = [
16506
16560
  description: "List your pending space invitations to accept. Reach for this to find an invite, then `accept_space_invite` it.",
16507
16561
  inputSchema: { ...PAGINATION_INPUT },
16508
16562
  surfaces: ["cli"],
16509
- tier: "extended",
16563
+ toolset: "spaces",
16510
16564
  run: forward("space.invites")
16511
16565
  },
16512
16566
  {
@@ -16520,7 +16574,7 @@ var ACTIONS = [
16520
16574
  threadIds: exports_external.array(exports_external.string()).optional().describe("optional threads IN THIS SPACE to link at creation (others are ignored)")
16521
16575
  },
16522
16576
  surfaces: ["cli"],
16523
- tier: "extended",
16577
+ toolset: "goals",
16524
16578
  run: forward("initiative.create")
16525
16579
  },
16526
16580
  {
@@ -16532,7 +16586,7 @@ var ACTIONS = [
16532
16586
  spaceId: exports_external.string().describe("the space to surface it in, spc_… (you must be a member)")
16533
16587
  },
16534
16588
  surfaces: ["cli"],
16535
- tier: "extended",
16589
+ toolset: "goals",
16536
16590
  run: forward("initiative.attachSpace")
16537
16591
  },
16538
16592
  {
@@ -16544,7 +16598,7 @@ var ACTIONS = [
16544
16598
  spaceId: exports_external.string().describe("the space to stop surfacing it in, spc_…")
16545
16599
  },
16546
16600
  surfaces: ["cli"],
16547
- tier: "extended",
16601
+ toolset: "goals",
16548
16602
  run: forward("initiative.detachSpace")
16549
16603
  },
16550
16604
  {
@@ -16556,7 +16610,7 @@ var ACTIONS = [
16556
16610
  initiativeId: exports_external.string().describe("the goal it serves, ini_…")
16557
16611
  },
16558
16612
  surfaces: ["cli"],
16559
- tier: "extended",
16613
+ toolset: "goals",
16560
16614
  run: forward("initiative.linkThread")
16561
16615
  },
16562
16616
  {
@@ -16568,7 +16622,7 @@ var ACTIONS = [
16568
16622
  initiativeId: exports_external.string().describe("the goal to unlink it from, ini_…")
16569
16623
  },
16570
16624
  surfaces: ["cli"],
16571
- tier: "extended",
16625
+ toolset: "goals",
16572
16626
  run: forward("initiative.unlinkThread")
16573
16627
  },
16574
16628
  {
@@ -16579,7 +16633,7 @@ var ACTIONS = [
16579
16633
  id: exports_external.string().describe("the goal, ini_…")
16580
16634
  },
16581
16635
  surfaces: ["cli"],
16582
- tier: "extended",
16636
+ toolset: "goals",
16583
16637
  run: forward("initiative.get")
16584
16638
  },
16585
16639
  {
@@ -16590,7 +16644,7 @@ var ACTIONS = [
16590
16644
  threadId: exports_external.string().describe("the thread to read goals for, thr_…")
16591
16645
  },
16592
16646
  surfaces: ["cli"],
16593
- tier: "extended",
16647
+ toolset: "goals",
16594
16648
  run: forward("initiative.forThread")
16595
16649
  },
16596
16650
  {
@@ -16603,7 +16657,7 @@ var ACTIONS = [
16603
16657
  purpose: exports_external.string().optional().describe("a new one-or-two-line purpose")
16604
16658
  },
16605
16659
  surfaces: ["cli"],
16606
- tier: "extended",
16660
+ toolset: "goals",
16607
16661
  run: forward("initiative.update")
16608
16662
  },
16609
16663
  {
@@ -16615,7 +16669,7 @@ var ACTIONS = [
16615
16669
  status: exports_external.enum(["active", "paused", "completed", "archived"]).describe("the new lifecycle state")
16616
16670
  },
16617
16671
  surfaces: ["cli"],
16618
- tier: "extended",
16672
+ toolset: "goals",
16619
16673
  run: forward("initiative.setStatus")
16620
16674
  },
16621
16675
  {
@@ -16626,7 +16680,7 @@ var ACTIONS = [
16626
16680
  id: exports_external.string().describe("the goal to delete, ini_…")
16627
16681
  },
16628
16682
  surfaces: ["cli"],
16629
- tier: "extended",
16683
+ toolset: "goals",
16630
16684
  run: forward("initiative.delete")
16631
16685
  },
16632
16686
  {
@@ -16640,7 +16694,7 @@ var ACTIONS = [
16640
16694
  memberProfileIds: exports_external.array(exports_external.string()).optional().describe("org profile ids to seed as members — agents added directly, humans get a pending invite")
16641
16695
  },
16642
16696
  surfaces: ["mcp", "cli"],
16643
- tier: "extended",
16697
+ toolset: "structure",
16644
16698
  run: forward("space.create")
16645
16699
  },
16646
16700
  {
@@ -16651,7 +16705,7 @@ var ACTIONS = [
16651
16705
  displayName: exports_external.string().min(1).describe("the agent's display name")
16652
16706
  },
16653
16707
  surfaces: ["cli"],
16654
- tier: "extended",
16708
+ toolset: "admin",
16655
16709
  run: forward("agent.register")
16656
16710
  },
16657
16711
  {
@@ -16664,7 +16718,7 @@ var ACTIONS = [
16664
16718
  orgRole: exports_external.enum(["member", "admin"]).optional().describe("org role (default member)")
16665
16719
  },
16666
16720
  surfaces: ["cli"],
16667
- tier: "extended",
16721
+ toolset: "admin",
16668
16722
  run: forward("human.invite")
16669
16723
  },
16670
16724
  {
@@ -16675,7 +16729,7 @@ var ACTIONS = [
16675
16729
  spaceId: exports_external.string().min(1).describe("the space id")
16676
16730
  },
16677
16731
  surfaces: ["mcp", "cli"],
16678
- tier: "core",
16732
+ toolset: "loop",
16679
16733
  run: forward("space.get")
16680
16734
  },
16681
16735
  {
@@ -16686,7 +16740,7 @@ var ACTIONS = [
16686
16740
  topicId: exports_external.string().min(1).describe("the topic id")
16687
16741
  },
16688
16742
  surfaces: ["mcp", "cli"],
16689
- tier: "core",
16743
+ toolset: "loop",
16690
16744
  run: forward("topic.get")
16691
16745
  },
16692
16746
  {
@@ -16695,7 +16749,7 @@ var ACTIONS = [
16695
16749
  description: "Platform-operator only: list EVERY org on the instance with member + space counts. The entry point for cross-org oversight — start here, then dive into one with `admin org`.",
16696
16750
  inputSchema: { ...PAGINATION_INPUT },
16697
16751
  surfaces: ["cli"],
16698
- tier: "extended",
16752
+ toolset: "admin",
16699
16753
  run: forward("admin.listOrgs")
16700
16754
  },
16701
16755
  {
@@ -16706,7 +16760,7 @@ var ACTIONS = [
16706
16760
  orgId: exports_external.string().min(1).describe("the org id, e.g. org_…")
16707
16761
  },
16708
16762
  surfaces: ["cli"],
16709
- tier: "extended",
16763
+ toolset: "admin",
16710
16764
  run: forward("admin.getOrg")
16711
16765
  },
16712
16766
  {
@@ -16717,7 +16771,7 @@ var ACTIONS = [
16717
16771
  spaceId: exports_external.string().min(1).describe("the space id, e.g. spc_…")
16718
16772
  },
16719
16773
  surfaces: ["cli"],
16720
- tier: "extended",
16774
+ toolset: "admin",
16721
16775
  run: forward("admin.getSpace")
16722
16776
  },
16723
16777
  {
@@ -16728,7 +16782,7 @@ var ACTIONS = [
16728
16782
  topicId: exports_external.string().min(1).describe("the topic id, e.g. top_…")
16729
16783
  },
16730
16784
  surfaces: ["cli"],
16731
- tier: "extended",
16785
+ toolset: "admin",
16732
16786
  run: forward("admin.getTopic")
16733
16787
  },
16734
16788
  {
@@ -16739,7 +16793,7 @@ var ACTIONS = [
16739
16793
  threadId: exports_external.string().min(1).describe("the thread id, e.g. thr_…")
16740
16794
  },
16741
16795
  surfaces: ["cli"],
16742
- tier: "extended",
16796
+ toolset: "admin",
16743
16797
  run: forward("admin.getThread")
16744
16798
  },
16745
16799
  {
@@ -16768,7 +16822,7 @@ var ACTIONS = [
16768
16822
  initiativeId: exports_external.string().optional().describe("link/unlink/attach/detach: the goal, ini_…")
16769
16823
  },
16770
16824
  surfaces: ["mcp"],
16771
- tier: "extended",
16825
+ toolset: "goals",
16772
16826
  run: dispatch({
16773
16827
  get: "initiative.get",
16774
16828
  create: "initiative.create",
@@ -16793,7 +16847,7 @@ var ACTIONS = [
16793
16847
  invitationId: exports_external.string().optional().describe("accept_invite: the invitation, sinv_…")
16794
16848
  },
16795
16849
  surfaces: ["mcp"],
16796
- tier: "extended",
16850
+ toolset: "spaces",
16797
16851
  run: dispatch({
16798
16852
  join: "space.join",
16799
16853
  leave: "space.leave",
@@ -16813,7 +16867,7 @@ var ACTIONS = [
16813
16867
  ...PAGINATION_INPUT
16814
16868
  },
16815
16869
  surfaces: ["mcp"],
16816
- tier: "core",
16870
+ toolset: "loop",
16817
16871
  run: dispatch({
16818
16872
  list: "notification.list",
16819
16873
  read: "notification.markRead",
@@ -16823,13 +16877,13 @@ var ACTIONS = [
16823
16877
  {
16824
16878
  name: "curation",
16825
16879
  title: "Curate the record",
16826
- description: "Curation moves on a contribution (all take contributionId). verb=promote (mark it a \uD83C\uDF96️ standout — sparingly: promotion is curation, not applause, AD-140), demote (remove the mark), delete (soft-remove), restore (bring a removed one back).",
16880
+ description: "Curation moves on a contribution (all take contributionId). verb=promote (mark it a \uD83C\uDF96️ standout — sparingly in deliberations: promotion is curation, not applause, AD-140. In a STANDING thread the calculus flips: promoting a periodic distilling synthesis is the lane's compression mechanism, AD-200 — do it, including on your own synthesis), demote (remove the mark), delete (soft-remove), restore (bring a removed one back).",
16827
16881
  inputSchema: {
16828
16882
  verb: exports_external.enum(["promote", "demote", "delete", "restore"]).describe("which curation move"),
16829
16883
  contributionId: exports_external.string().describe("the contribution, con_…")
16830
16884
  },
16831
16885
  surfaces: ["mcp"],
16832
- tier: "extended",
16886
+ toolset: "curation",
16833
16887
  run: dispatch({
16834
16888
  promote: "contribution.promote",
16835
16889
  demote: "contribution.demote",
@@ -16840,26 +16894,36 @@ var ACTIONS = [
16840
16894
  {
16841
16895
  name: "artifact",
16842
16896
  title: "Durable artifacts",
16843
- description: "Durable, lifecycle-managed artifacts. verb=create (title+contentRef, optional summary/sourceThreadId/sourceContributionIds), transition (artifactId + to: active|promoted|needs-review|superseded|deprecated|archived|deleted — the state machine guards jumps and retrieval tier follows), promote_attachment (attachmentId — lift an uploaded file into an artifact).",
16897
+ description: "Durable, versioned artifacts — the team's living documents (AD-201). Kinds: doc (markdown), table (CSV), diagram (Mermaid), html — all versioned text you edit in place — plus file (a contentRef: URL/inline/blob). " + "verb=create (kind+type+title, source for text kinds / contentRef for file; a text artifact is born at v1). " + "verb=get (artifactId — the current source + version + history; pass version to read an old snapshot). " + "verb=edit — READ FIRST via get, then pass baseVersion (the version you read): either source (full replace) or patches [{old,new},…] (exact-match hunks, applied in order, ATOMIC — all land or none, and one call mints ONE version, so batch related row-edits into one call). A stale baseVersion returns {ok:false, currentSource} — reconcile and retry against the returned source; a failed patch names the culprit index. " + "verb=transition (artifactId + to: active|promoted|needs-review|superseded|deprecated|archived|deleted), promote_attachment (attachmentId — lift an uploaded file into an artifact).",
16844
16898
  inputSchema: {
16845
- verb: exports_external.enum(["create", "transition", "promote_attachment"]).describe("which artifact move"),
16899
+ verb: exports_external.enum(["create", "get", "edit", "transition", "promote_attachment"]).describe("which artifact move"),
16846
16900
  title: exports_external.string().optional().describe("create: the artifact's title"),
16847
- contentRef: exports_external.string().optional().describe("create: where the content lives, e.g. inline://… or a URL"),
16901
+ kind: exports_external.enum(["doc", "table", "diagram", "html", "file"]).optional().describe("create: structural kind (default file). doc/table/diagram/html are editable text"),
16902
+ type: exports_external.string().optional().describe("create: semantic type, e.g. decision|summary|plan|markdown|table|research-brief"),
16903
+ source: exports_external.string().optional().describe("create (text kinds): the initial content · edit verb=replace: the full new content"),
16904
+ contentRef: exports_external.string().optional().describe("create (kind=file): where the content lives, e.g. inline://… or a URL"),
16905
+ editPolicy: exports_external.enum(["owner", "members", "anyone"]).optional().describe("create: who may edit (default members)"),
16848
16906
  summary: exports_external.string().optional().describe("create: a short summary"),
16849
16907
  sourceThreadId: exports_external.string().optional().describe("create: the thread it came from"),
16850
16908
  sourceContributionIds: exports_external.array(exports_external.string()).optional().describe("create: contributions it distills"),
16851
- artifactId: exports_external.string().optional().describe("transition: the artifact, art_…"),
16909
+ artifactId: exports_external.string().optional().describe("get/edit/transition: the artifact, art_…"),
16910
+ version: exports_external.number().optional().describe("get: read this old snapshot instead of the current"),
16911
+ baseVersion: exports_external.number().optional().describe("edit: the version you READ (from get/create) — stale base is rejected with the current source"),
16912
+ patches: exports_external.array(exports_external.object({ old: exports_external.string(), new: exports_external.string() })).optional().describe("edit: exact-match hunks applied in order, atomically; each old must match exactly once"),
16913
+ note: exports_external.string().optional().describe("edit: one-line summary of what changed"),
16852
16914
  to: exports_external.string().optional().describe("transition: the target status"),
16853
16915
  reason: exports_external.string().optional().describe("transition: why"),
16854
16916
  attachmentId: exports_external.string().optional().describe("promote_attachment: the upload, att_…")
16855
16917
  },
16856
16918
  surfaces: ["mcp"],
16857
- tier: "extended",
16919
+ toolset: "artifacts",
16858
16920
  run: dispatch({
16859
16921
  create: "artifact.create",
16922
+ get: "artifact.get",
16923
+ edit: "artifact.edit",
16860
16924
  transition: "artifact.transition",
16861
16925
  promote_attachment: "attachment.promote"
16862
- })
16926
+ }, (verb, input) => verb === "edit" ? { ...input, verb: Array.isArray(input.patches) ? "patch" : "replace" } : input)
16863
16927
  },
16864
16928
  {
16865
16929
  name: "charter",
@@ -16876,7 +16940,7 @@ var ACTIONS = [
16876
16940
  waysToHelp: exports_external.array(exports_external.string()).optional().describe("ways_to_help: full list, replaces; [] clears")
16877
16941
  },
16878
16942
  surfaces: ["mcp"],
16879
- tier: "extended",
16943
+ toolset: "spaces",
16880
16944
  run: dispatch({
16881
16945
  space_charter: "space.setCharter",
16882
16946
  topic_guidance: "topic.update",
@@ -16984,6 +17048,25 @@ class UsageError extends Error {
16984
17048
  }
16985
17049
  }
16986
17050
 
17051
+ // src/source.ts
17052
+ var SNIFF = [
17053
+ ["CLAUDECODE", "Claude Code"],
17054
+ ["CLAUDE_CODE_ENTRYPOINT", "Claude Code"],
17055
+ ["OPENCLAW_HOME", "openclaw"],
17056
+ ["CODEX_SANDBOX", "Codex"],
17057
+ ["CURSOR_TRACE_ID", "Cursor"]
17058
+ ];
17059
+ function detectSource(env = process.env) {
17060
+ const explicit = env.ARBOR_SOURCE?.trim();
17061
+ if (explicit)
17062
+ return explicit;
17063
+ for (const [envVar, tool] of SNIFF) {
17064
+ if (env[envVar]?.trim())
17065
+ return tool;
17066
+ }
17067
+ return;
17068
+ }
17069
+
16987
17070
  // src/client.ts
16988
17071
  class NotLoggedInError extends Error {
16989
17072
  code = "UNAUTHORIZED";
@@ -16998,6 +17081,7 @@ function authHeader() {
16998
17081
  throw new NotLoggedInError;
16999
17082
  return { authorization: `Bearer ${cfg.token}`, apiUrl: cfg.apiUrl };
17000
17083
  }
17084
+ var source = detectSource();
17001
17085
  var httpExecutor = {
17002
17086
  async call(name, input) {
17003
17087
  const { authorization, apiUrl } = authHeader();
@@ -17008,7 +17092,11 @@ var httpExecutor = {
17008
17092
  try {
17009
17093
  res = await fetch(`${apiUrl}/api`, {
17010
17094
  method: "POST",
17011
- headers: { "content-type": "application/json", authorization },
17095
+ headers: {
17096
+ "content-type": "application/json",
17097
+ authorization,
17098
+ ...source ? { "x-arbor-source": source } : {}
17099
+ },
17012
17100
  body: JSON.stringify({ object: object2, verb, input })
17013
17101
  });
17014
17102
  } catch (cause) {
@@ -17030,7 +17118,6 @@ var GLOBAL_BOOLEAN_FLAGS = new Set([
17030
17118
  "json",
17031
17119
  "quiet",
17032
17120
  "no-quiet",
17033
- "version",
17034
17121
  "help"
17035
17122
  ]);
17036
17123
  var RESERVED_FLAGS = new Set([
@@ -17634,7 +17721,7 @@ async function main() {
17634
17721
  const ctx = resolveOutput(flags);
17635
17722
  const [cmd] = positionals;
17636
17723
  try {
17637
- if (truthyFlag(flags.version) || cmd === "version") {
17724
+ if (flags.version !== undefined && positionals.length === 0 || cmd === "version") {
17638
17725
  renderVersion(ctx);
17639
17726
  return;
17640
17727
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lambdacurry/arbor",
3
- "version": "0.6.5",
3
+ "version": "0.7.0",
4
4
  "description": "The Arbor CLI — a shared workspace for people and agents. The human + headless-agent write path over Arbor's guarded operation surface.",
5
5
  "keywords": [
6
6
  "agents",