@lambdacurry/arbor 0.13.5 → 0.13.6

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 +465 -145
  2. package/package.json +1 -1
package/dist/arbor.js CHANGED
@@ -16844,6 +16844,13 @@ var MCP_OUTPUT_SCHEMAS = {
16844
16844
  all: exports_external.array(initiative3).optional(),
16845
16845
  ok: exports_external.boolean().optional()
16846
16846
  }),
16847
+ goal_get: exports_external.looseObject({ initiative: initiative3 }),
16848
+ goal_list: exports_external.looseObject({
16849
+ initiatives: exports_external.array(initiative3).optional(),
16850
+ linked: exports_external.array(initiative3).optional(),
16851
+ all: exports_external.array(initiative3).optional()
16852
+ }),
16853
+ goal_delete: exports_external.looseObject({ ok: exports_external.boolean() }),
16847
16854
  membership: exports_external.looseObject({
16848
16855
  spaceId: id.optional(),
16849
16856
  profileId: id.optional(),
@@ -16859,20 +16866,32 @@ var MCP_OUTPUT_SCHEMAS = {
16859
16866
  nextCursor: exports_external.string().optional(),
16860
16867
  ok: exports_external.boolean().optional()
16861
16868
  }),
16862
- notification: exports_external.looseObject({
16869
+ space_leave: exports_external.looseObject({ spaceId: id, left: exports_external.boolean() }),
16870
+ space_list_open: paged("spaces", space3),
16871
+ space_invitation_list: paged("invitations", jsonObject),
16872
+ notification_list: exports_external.looseObject({
16863
16873
  items: exports_external.array(jsonObject).optional(),
16864
16874
  total: exports_external.number().optional(),
16865
16875
  unseenCount: exports_external.number().optional(),
16866
16876
  lastSeenAt: timestamp.nullable().optional(),
16867
- notificationId: id.optional(),
16868
- read: exports_external.boolean().optional(),
16869
- markedCount: exports_external.number().optional(),
16870
16877
  nextCursor: exports_external.string().optional()
16871
16878
  }),
16879
+ notification_read: exports_external.looseObject({
16880
+ notificationId: id.optional(),
16881
+ read: exports_external.boolean().optional()
16882
+ }),
16883
+ notification_read_all: exports_external.looseObject({
16884
+ markedCount: exports_external.number().optional()
16885
+ }),
16872
16886
  watch: exports_external.looseObject({
16873
16887
  targetType: exports_external.string().optional(),
16874
16888
  targetId: id.optional(),
16875
- watching: exports_external.boolean().optional(),
16889
+ watching: exports_external.boolean().optional()
16890
+ }),
16891
+ watch_get: exports_external.looseObject({
16892
+ watching: exports_external.boolean()
16893
+ }),
16894
+ watch_list: exports_external.looseObject({
16876
16895
  watches: exports_external.array(jsonObject).optional()
16877
16896
  }),
16878
16897
  curation: exports_external.looseObject({
@@ -16900,23 +16919,59 @@ var MCP_OUTPUT_SCHEMAS = {
16900
16919
  versions: exports_external.array(jsonObject).optional(),
16901
16920
  appBundles: exports_external.array(jsonObject).optional()
16902
16921
  }),
16903
- apps: exports_external.looseObject({
16904
- result: exports_external.array(app2).optional(),
16905
- spaceId: id.optional(),
16906
- title: exports_external.string().optional(),
16907
- slug: exports_external.string().optional(),
16908
- access: exports_external.string().optional(),
16909
- stateMode: exports_external.string().optional(),
16910
- app: app2.optional(),
16911
- deployment: deployment.optional(),
16912
- deployments: exports_external.array(jsonObject).optional(),
16913
- bundle: jsonObject.optional(),
16914
- reset: jsonObject.optional(),
16915
- activated: exports_external.boolean().optional(),
16922
+ artifact_get: exports_external.looseObject({
16923
+ id,
16924
+ title: exports_external.string(),
16925
+ kind: exports_external.string(),
16926
+ status: exports_external.string(),
16927
+ content: jsonObject,
16928
+ version: exports_external.number().optional(),
16929
+ versions: exports_external.array(jsonObject).optional(),
16930
+ appBundles: exports_external.array(jsonObject),
16931
+ watching: exports_external.boolean(),
16932
+ url: exports_external.string()
16933
+ }),
16934
+ artifact_transition: exports_external.looseObject({
16935
+ artifactId: id,
16936
+ status: exports_external.string(),
16937
+ retrievalTier: exports_external.string()
16938
+ }),
16939
+ artifact_delete: exports_external.looseObject({
16940
+ artifactId: id,
16941
+ status: exports_external.literal("deleted"),
16942
+ retrievalTier: exports_external.string()
16943
+ }),
16944
+ app_list: exports_external.object({ result: exports_external.array(app2) }),
16945
+ app_get: exports_external.looseObject({
16946
+ app: app2,
16947
+ deployments: exports_external.array(jsonObject),
16948
+ resets: exports_external.array(jsonObject),
16949
+ events: exports_external.array(jsonObject),
16950
+ url: exports_external.string()
16951
+ }),
16952
+ app_deployment_get: exports_external.looseObject({ deployment, bundle: jsonObject }),
16953
+ app_create: app2,
16954
+ app_deploy: exports_external.looseObject({
16955
+ deployment,
16956
+ activated: exports_external.boolean(),
16957
+ url: exports_external.string(),
16958
+ replayed: exports_external.boolean().optional()
16959
+ }),
16960
+ app_activate: exports_external.looseObject({
16961
+ app: app2,
16962
+ deployment,
16916
16963
  replayed: exports_external.boolean().optional(),
16917
- url: exports_external.string().optional(),
16918
- id: id.optional()
16964
+ url: exports_external.string()
16965
+ }),
16966
+ app_rollback: exports_external.looseObject({
16967
+ app: app2,
16968
+ deployment,
16969
+ replayed: exports_external.boolean().optional(),
16970
+ url: exports_external.string()
16919
16971
  }),
16972
+ app_set_access: app2,
16973
+ app_reset_state: exports_external.looseObject({ reset: jsonObject, replayed: exports_external.boolean() }),
16974
+ app_archive: app2,
16920
16975
  charter: exports_external.looseObject({
16921
16976
  ok: exports_external.boolean().optional(),
16922
16977
  space: space3.optional(),
@@ -16990,9 +17045,19 @@ var MCP_OUTPUT_SCHEMAS = {
16990
17045
  head: exports_external.string().optional(),
16991
17046
  refs: exports_external.array(exports_external.string()).optional()
16992
17047
  }),
16993
- computer_sync: exports_external.looseObject({
17048
+ computer_fetch: exports_external.looseObject({
16994
17049
  ok: exports_external.literal(true),
16995
- operation: exports_external.enum(["fetch", "pull", "push"]),
17050
+ operation: exports_external.literal("fetch"),
17051
+ repository: exports_external.string(),
17052
+ dir: exports_external.string(),
17053
+ branch: exports_external.string().optional(),
17054
+ head: exports_external.string().optional(),
17055
+ fetchHead: exports_external.string().optional(),
17056
+ refs: exports_external.array(exports_external.string()).optional()
17057
+ }),
17058
+ computer_pull: exports_external.looseObject({
17059
+ ok: exports_external.literal(true),
17060
+ operation: exports_external.literal("pull"),
16996
17061
  repository: exports_external.string(),
16997
17062
  dir: exports_external.string(),
16998
17063
  branch: exports_external.string().optional(),
@@ -17002,6 +17067,17 @@ var MCP_OUTPUT_SCHEMAS = {
17002
17067
  fetchHead: exports_external.string().optional(),
17003
17068
  refs: exports_external.array(exports_external.string()).optional()
17004
17069
  }),
17070
+ computer_push: exports_external.looseObject({
17071
+ ok: exports_external.literal(true),
17072
+ operation: exports_external.literal("push"),
17073
+ repository: exports_external.string(),
17074
+ dir: exports_external.string(),
17075
+ branch: exports_external.string().optional(),
17076
+ head: exports_external.string().optional(),
17077
+ before: exports_external.string().optional(),
17078
+ after: exports_external.string().optional(),
17079
+ refs: exports_external.array(exports_external.string()).optional()
17080
+ }),
17005
17081
  computer_checkpoint: checkpoint,
17006
17082
  computer_publish_preview: exports_external.looseObject({
17007
17083
  previewId: id,
@@ -17031,6 +17107,141 @@ function outputSchemaFor(actionName) {
17031
17107
  return MCP_OUTPUT_SCHEMAS[actionName];
17032
17108
  }
17033
17109
 
17110
+ // ../actions/src/tool-annotations.ts
17111
+ var readOnly = {
17112
+ readOnlyHint: true,
17113
+ destructiveHint: false,
17114
+ idempotentHint: true,
17115
+ openWorldHint: false
17116
+ };
17117
+ var additive = {
17118
+ readOnlyHint: false,
17119
+ destructiveHint: false,
17120
+ idempotentHint: false,
17121
+ openWorldHint: false
17122
+ };
17123
+ var destructive = {
17124
+ readOnlyHint: false,
17125
+ destructiveHint: true,
17126
+ idempotentHint: false,
17127
+ openWorldHint: false
17128
+ };
17129
+ var idempotent = {
17130
+ readOnlyHint: false,
17131
+ destructiveHint: false,
17132
+ idempotentHint: true,
17133
+ openWorldHint: false
17134
+ };
17135
+ var destructiveIdempotent = {
17136
+ readOnlyHint: false,
17137
+ destructiveHint: true,
17138
+ idempotentHint: true,
17139
+ openWorldHint: false
17140
+ };
17141
+ var openWorld = {
17142
+ readOnlyHint: false,
17143
+ destructiveHint: false,
17144
+ idempotentHint: false,
17145
+ openWorldHint: true
17146
+ };
17147
+ var destructiveOpenWorld = {
17148
+ readOnlyHint: false,
17149
+ destructiveHint: true,
17150
+ idempotentHint: false,
17151
+ openWorldHint: true
17152
+ };
17153
+ var idempotentOpenWorld = {
17154
+ readOnlyHint: false,
17155
+ destructiveHint: false,
17156
+ idempotentHint: true,
17157
+ openWorldHint: true
17158
+ };
17159
+ var MCP_TOOL_ANNOTATIONS = {
17160
+ recall: readOnly,
17161
+ contribute: additive,
17162
+ edit: additive,
17163
+ stamp: additive,
17164
+ react: idempotent,
17165
+ create_topic: additive,
17166
+ topic_update: additive,
17167
+ create_thread: additive,
17168
+ set_organization_computer: additive,
17169
+ set_space_computer: additive,
17170
+ set_topic_computer: additive,
17171
+ set_thread_computer: additive,
17172
+ attach_computer: additive,
17173
+ get_computer: readOnly,
17174
+ publish_topic_computer: additive,
17175
+ checkpoint_computer: additive,
17176
+ transition_thread: additive,
17177
+ request: additive,
17178
+ respond: additive,
17179
+ mark_as_response: idempotent,
17180
+ cancel_request: destructive,
17181
+ inbox: readOnly,
17182
+ dismiss: destructive,
17183
+ whoami: readOnly,
17184
+ introduce: additive,
17185
+ thread_get: readOnly,
17186
+ tree: readOnly,
17187
+ members: readOnly,
17188
+ space_create: additive,
17189
+ space_update: additive,
17190
+ space_get: readOnly,
17191
+ topic_get: readOnly,
17192
+ goal_get: readOnly,
17193
+ goal_list: readOnly,
17194
+ goal: additive,
17195
+ goal_delete: destructive,
17196
+ membership: additive,
17197
+ space_leave: destructive,
17198
+ space_list_open: readOnly,
17199
+ space_invitation_list: readOnly,
17200
+ notification_list: readOnly,
17201
+ notification_read: idempotent,
17202
+ notification_read_all: idempotent,
17203
+ watch: idempotent,
17204
+ watch_get: readOnly,
17205
+ watch_list: readOnly,
17206
+ curation: additive,
17207
+ artifact: additive,
17208
+ artifact_get: readOnly,
17209
+ artifact_transition: additive,
17210
+ artifact_delete: destructive,
17211
+ app_get: readOnly,
17212
+ app_list: readOnly,
17213
+ app_deployment_get: readOnly,
17214
+ app_create: additive,
17215
+ app_deploy: idempotent,
17216
+ app_activate: additive,
17217
+ app_rollback: additive,
17218
+ app_set_access: additive,
17219
+ app_reset_state: destructiveIdempotent,
17220
+ app_archive: additive,
17221
+ charter: destructive,
17222
+ computer_open: additive,
17223
+ computer_exec: destructiveOpenWorld,
17224
+ computer_status: readOnly,
17225
+ computer_verify: openWorld,
17226
+ computer_read: readOnly,
17227
+ computer_export: idempotent,
17228
+ computer_write: destructive,
17229
+ computer_clone: idempotentOpenWorld,
17230
+ computer_fetch: idempotentOpenWorld,
17231
+ computer_pull: destructiveOpenWorld,
17232
+ computer_push: destructiveOpenWorld,
17233
+ computer_checkpoint: additive,
17234
+ computer_publish_preview: idempotent,
17235
+ computer_publish_app_bundle: idempotent,
17236
+ computer_stop: additive
17237
+ };
17238
+ function mcpAnnotationsFor(name) {
17239
+ const annotations = MCP_TOOL_ANNOTATIONS[name];
17240
+ if (!annotations)
17241
+ throw new Error(`${name}: MCP action is missing behavior annotations`);
17242
+ return annotations;
17243
+ }
17244
+
17034
17245
  // ../actions/src/index.ts
17035
17246
  var CONTRIBUTION_ADD_LINK_RELS = [
17036
17247
  "cites",
@@ -17050,7 +17261,7 @@ var ORIENTATION = `Arbor is your team's deliberation room and shared memory —
17050
17261
  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.
17051
17262
  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.
17052
17263
 
17053
- 8. DURABLE OUTPUT IS AN ARTIFACT, NOT A LONG CONTRIBUTION (AD-201). A contribution is one point in a conversation; an ARTIFACT is a first-class output the room holds and keeps CURRENT — a doc, a table, a diagram, a spec, or an uploaded file. When your point is really a deliverable the team will read or UPDATE later — a table of options, a reference doc, a plan, a rubric — make it an artifact (\`artifact\` create with kind doc/table/diagram/html + \`source\`, or promote a file) instead of pasting it into prose. Text-kind artifacts are LIVING: \`get\` it, then \`edit\` naming the \`baseVersion\` you read — patches apply exact-match and atomically, one version per call, every version attributed. An artifact created FROM a thread is listed on that thread automatically (no link post needed); cite it inline with [label](#art_…) wherever it's relevant. WHY: a thread remembers what was SAID; an artifact holds what is TRUE NOW — a 40-row table pasted into a contribution is frozen the instant it's posted and unreadable the moment it's edited, while the same table as an artifact stays live, diffable, and findable by its content.
17264
+ 8. DURABLE OUTPUT IS AN ARTIFACT, NOT A LONG CONTRIBUTION (AD-201). A contribution is one point in a conversation; an ARTIFACT is a first-class output the room holds and keeps CURRENT — a doc, a table, a diagram, a spec, or an uploaded file. When your point is really a deliverable the team will read or UPDATE later — a table of options, a reference doc, a plan, a rubric — make it an artifact (\`artifact\` verb=create with kind doc/table/diagram/html + \`source\`, or promote a file) instead of pasting it into prose. Text-kind artifacts are LIVING: \`artifact_get\` it, then \`artifact\` verb=edit naming the \`baseVersion\` you read — patches apply exact-match and atomically, one version per call, every version attributed. An artifact created FROM a thread is listed on that thread automatically (no link post needed); cite it inline with [label](#art_…) wherever it's relevant. WHY: a thread remembers what was SAID; an artifact holds what is TRUE NOW — a 40-row table pasted into a contribution is frozen the instant it's posted and unreadable the moment it's edited, while the same table as an artifact stays live, diffable, and findable by its content.
17054
17265
 
17055
17266
  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.`;
17056
17267
  var PROSE_LISTS = exports_external.registry();
@@ -17461,10 +17672,52 @@ var ACTION_DEFINITIONS = [
17461
17672
  githubToken: exports_external.string().min(1).describe("short-lived GitHub token supplied by the caller"),
17462
17673
  githubUsername: exports_external.string().optional().describe("GitHub username; defaults to x-access-token")
17463
17674
  },
17464
- surfaces: ["computer-mcp", "computer-cli"],
17675
+ surfaces: ["computer-cli"],
17465
17676
  toolset: "loop",
17466
17677
  run: forward("computer_runtime.sync")
17467
17678
  },
17679
+ {
17680
+ name: "computer_fetch",
17681
+ title: "Fetch an allowed repository",
17682
+ description: "Fetch remote refs for a cloned recipe repository without changing its checked-out branch or any remote ref. The collaborator's GitHub token is spent only on this call and is neither stored nor returned.",
17683
+ inputSchema: {
17684
+ computerSessionId: exports_external.string().describe("the computerSessionId returned by computer_open, cms_…"),
17685
+ repository: exports_external.string().describe("allowed owner/repository slug already cloned"),
17686
+ githubToken: exports_external.string().min(1).describe("short-lived GitHub token supplied by the caller"),
17687
+ githubUsername: exports_external.string().optional().describe("GitHub username; defaults to x-access-token")
17688
+ },
17689
+ surfaces: ["computer-mcp"],
17690
+ toolset: "loop",
17691
+ run: (ex, input) => ex.call("computer_runtime.sync", { ...input, operation: "fetch" })
17692
+ },
17693
+ {
17694
+ name: "computer_pull",
17695
+ title: "Fast-forward an allowed repository",
17696
+ description: "Fetch and fast-forward the checked-out branch of a cloned recipe repository; divergent history is rejected. The collaborator's GitHub token is spent only on this call and is neither stored nor returned.",
17697
+ inputSchema: {
17698
+ computerSessionId: exports_external.string().describe("the computerSessionId returned by computer_open, cms_…"),
17699
+ repository: exports_external.string().describe("allowed owner/repository slug already cloned"),
17700
+ githubToken: exports_external.string().min(1).describe("short-lived GitHub token supplied by the caller"),
17701
+ githubUsername: exports_external.string().optional().describe("GitHub username; defaults to x-access-token")
17702
+ },
17703
+ surfaces: ["computer-mcp"],
17704
+ toolset: "loop",
17705
+ run: (ex, input) => ex.call("computer_runtime.sync", { ...input, operation: "pull" })
17706
+ },
17707
+ {
17708
+ name: "computer_push",
17709
+ title: "Push an allowed repository",
17710
+ description: "Push the checked-out branch to its fixed allowlisted GitHub repository without force-pushing or deleting refs. The collaborator's GitHub token is spent only on this call and is neither stored nor returned.",
17711
+ inputSchema: {
17712
+ computerSessionId: exports_external.string().describe("the computerSessionId returned by computer_open, cms_…"),
17713
+ repository: exports_external.string().describe("allowed owner/repository slug already cloned"),
17714
+ githubToken: exports_external.string().min(1).describe("short-lived GitHub token supplied by the caller"),
17715
+ githubUsername: exports_external.string().optional().describe("GitHub username; defaults to x-access-token")
17716
+ },
17717
+ surfaces: ["computer-mcp"],
17718
+ toolset: "loop",
17719
+ run: (ex, input) => ex.call("computer_runtime.sync", { ...input, operation: "push" })
17720
+ },
17468
17721
  {
17469
17722
  name: "computer_checkpoint",
17470
17723
  title: "Checkpoint completed work",
@@ -17493,7 +17746,7 @@ var ACTION_DEFINITIONS = [
17493
17746
  {
17494
17747
  name: "computer_publish_app_bundle",
17495
17748
  title: "Publish an immutable AppBundle",
17496
- description: "After a Bun container build is green, freeze one Worker entry module plus optional static assets from this Thread computer into a content-addressed AppBundle sourced from an ArtifactVersion. Use the returned bundleId with apps verb=deploy (CLI: app_deploy); uploads complete and verify before Arbor publishes the bundle manifest, and retries require the same idempotencyKey.",
17749
+ description: "After a Bun container build is green, freeze one Worker entry module plus optional static assets from this Thread computer into a content-addressed AppBundle sourced from an ArtifactVersion. Use the returned bundleId with app_deploy; uploads complete and verify before Arbor publishes the bundle manifest, and retries require the same idempotencyKey.",
17497
17750
  inputSchema: {
17498
17751
  computerSessionId: exports_external.string().describe("the computerSessionId returned by computer_open, cms_…"),
17499
17752
  artifactVersionId: exports_external.string().describe("the source ArtifactVersion, artv_…"),
@@ -17815,7 +18068,7 @@ var ACTION_DEFINITIONS = [
17815
18068
  inputSchema: {
17816
18069
  notificationId: exports_external.string().describe("the notification id, ntf_…")
17817
18070
  },
17818
- surfaces: ["cli"],
18071
+ surfaces: ["mcp", "cli"],
17819
18072
  toolset: "loop",
17820
18073
  run: forward("notification.markRead")
17821
18074
  },
@@ -17824,7 +18077,7 @@ var ACTION_DEFINITIONS = [
17824
18077
  title: "Mark all notifications read",
17825
18078
  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.",
17826
18079
  inputSchema: {},
17827
- surfaces: ["cli"],
18080
+ surfaces: ["mcp", "cli"],
17828
18081
  toolset: "loop",
17829
18082
  run: forward("notification.markAllRead")
17830
18083
  },
@@ -18235,63 +18488,84 @@ var ACTION_DEFINITIONS = [
18235
18488
  toolset: "admin",
18236
18489
  run: forward("admin.getThread")
18237
18490
  },
18491
+ {
18492
+ name: "goal_get",
18493
+ title: "Read one goal",
18494
+ description: "Read one goal fully, including its purpose, lifecycle, attached Spaces, and related Threads visible to you. It cannot create, update, link, archive, or delete a goal.",
18495
+ inputSchema: { id: exports_external.string().describe("the goal, ini_…") },
18496
+ surfaces: ["mcp"],
18497
+ toolset: "goals",
18498
+ run: forward("initiative.get")
18499
+ },
18500
+ {
18501
+ name: "goal_list",
18502
+ title: "List goals",
18503
+ description: "List the goals attached to one Space, or the linked and available goals for one Thread. It cannot create, update, link, archive, or delete a goal.",
18504
+ inputSchema: {
18505
+ spaceId: exports_external.string().optional().describe("a Space whose attached goals to list, spc_…"),
18506
+ threadId: exports_external.string().optional().describe("a Thread whose linked and available goals to list, thr_…")
18507
+ },
18508
+ surfaces: ["mcp"],
18509
+ toolset: "goals",
18510
+ run: (ex, input) => {
18511
+ if (typeof input.spaceId === "string")
18512
+ return ex.call("initiative.listForSpace", { spaceId: input.spaceId });
18513
+ if (typeof input.threadId === "string")
18514
+ return ex.call("initiative.forThread", { threadId: input.threadId });
18515
+ return Promise.reject(new Error("goal_list requires a spaceId (a Space's goals) or a threadId (a Thread's goals)"));
18516
+ }
18517
+ },
18238
18518
  {
18239
18519
  name: "goal",
18240
- title: "Work with goals",
18241
- description: "GOALS (initiatives) — the wide 'why' threads serve toward (AD-146/180). verb=get (id — one goal read fully, ANY status incl. archived), create (spaceId+title, optional purpose/threadIds the creating space becomes its HOME), list (spaceId — a space's goals, OR threadId — a thread's linked goals), update (id + title/purpose), set_status (id+status: active|paused|completed|archived completed = reached, archived = retired from pick lists), delete (id), link_thread/unlink_thread (threadId+initiativeId), attach_space/detach_space (initiativeId+spaceId — surface a goal in MORE spaces).",
18520
+ title: "Manage goals",
18521
+ description: "Create, update, change lifecycle, or change the Space/Thread links of a goal (initiative). Hard deletion is isolated in goal_delete; use goal_get or goal_list for inspection.",
18242
18522
  inputSchema: {
18243
18523
  verb: exports_external.enum([
18244
18524
  "create",
18245
- "list",
18246
18525
  "update",
18247
18526
  "set_status",
18248
- "delete",
18249
18527
  "link_thread",
18250
18528
  "unlink_thread",
18251
18529
  "attach_space",
18252
18530
  "detach_space"
18253
18531
  ]).describe("which goal move to make"),
18254
- spaceId: exports_external.string().optional().describe("create: HOME space · list: a space's goals · attach/detach_space: the target space, spc_…"),
18532
+ spaceId: exports_external.string().optional().describe("create: HOME space · attach/detach_space: the target space, spc_…"),
18255
18533
  title: exports_external.string().optional().describe("create/update: the goal's name — an outcome, not a task"),
18256
18534
  purpose: exports_external.string().optional().describe("create/update: one or two lines on what reaching it means"),
18257
18535
  threadIds: exports_external.array(exports_external.string()).optional().describe("create: threads to link at birth"),
18258
- id: exports_external.string().optional().describe("get/update/set_status/delete: the goal, ini_…"),
18536
+ id: exports_external.string().optional().describe("update/set_status: the goal, ini_…"),
18259
18537
  status: exports_external.enum(["active", "paused", "completed", "archived"]).optional().describe("set_status: the new lifecycle state"),
18260
- threadId: exports_external.string().optional().describe("list/link_thread/unlink_thread: the thread, thr_…"),
18538
+ threadId: exports_external.string().optional().describe("link_thread/unlink_thread: the thread, thr_…"),
18261
18539
  initiativeId: exports_external.string().optional().describe("link/unlink/attach/detach: the goal, ini_…")
18262
18540
  },
18263
18541
  surfaces: ["mcp"],
18264
18542
  toolset: "goals",
18265
- run: (ex, input) => {
18266
- const { verb, spaceId, threadId } = input;
18267
- if (verb === "list") {
18268
- if (typeof spaceId === "string")
18269
- return ex.call("initiative.listForSpace", { spaceId });
18270
- if (typeof threadId === "string")
18271
- return ex.call("initiative.forThread", { threadId });
18272
- return Promise.reject(new Error("goal list requires a spaceId (a space's goals) or a threadId (a thread's goals)"));
18273
- }
18274
- return dispatch({
18275
- get: "initiative.get",
18276
- create: "initiative.create",
18277
- list: "initiative.forThread",
18278
- update: "initiative.update",
18279
- set_status: "initiative.setStatus",
18280
- delete: "initiative.delete",
18281
- link_thread: "initiative.linkThread",
18282
- unlink_thread: "initiative.unlinkThread",
18283
- attach_space: "initiative.attachSpace",
18284
- detach_space: "initiative.detachSpace"
18285
- })(ex, input);
18286
- }
18543
+ run: dispatch({
18544
+ create: "initiative.create",
18545
+ update: "initiative.update",
18546
+ set_status: "initiative.setStatus",
18547
+ link_thread: "initiative.linkThread",
18548
+ unlink_thread: "initiative.unlinkThread",
18549
+ attach_space: "initiative.attachSpace",
18550
+ detach_space: "initiative.detachSpace"
18551
+ })
18552
+ },
18553
+ {
18554
+ name: "goal_delete",
18555
+ title: "Permanently delete a goal",
18556
+ description: "Permanently delete a mistaken or duplicate goal and all of its Space/Thread links. This is irreversible; archive a goal through goal instead when its history should remain.",
18557
+ inputSchema: { id: exports_external.string().describe("the goal to permanently delete, ini_…") },
18558
+ surfaces: ["mcp"],
18559
+ toolset: "goals",
18560
+ run: forward("initiative.delete")
18287
18561
  },
18288
18562
  {
18289
18563
  name: "membership",
18290
18564
  title: "Space membership moves",
18291
- description: "Your space membership, in one tool. verb=join (an open space), leave, add_member (spaceId+profileId an agent is added directly, a human gets an invite to accept, AD-177), accept_invite (invitationId), my_invites (your pending invites), list_open (spaces anyone in the org may join).",
18565
+ description: "Join an open Space, add a member, or accept one invitation. Leaving is isolated in space_leave; read available Spaces with space_list_open and pending invitations with space_invitation_list.",
18292
18566
  inputSchema: {
18293
- verb: exports_external.enum(["join", "leave", "add_member", "accept_invite", "my_invites", "list_open"]).describe("which membership move to make"),
18294
- spaceId: exports_external.string().optional().describe("join/leave/add_member: the space, spc_…"),
18567
+ verb: exports_external.enum(["join", "add_member", "accept_invite"]).describe("which membership move to make"),
18568
+ spaceId: exports_external.string().optional().describe("join/add_member: the Space, spc_…"),
18295
18569
  profileId: exports_external.string().optional().describe("add_member: who to add, prof_…"),
18296
18570
  invitationId: exports_external.string().optional().describe("accept_invite: the invitation, sinv_…")
18297
18571
  },
@@ -18299,48 +18573,83 @@ var ACTION_DEFINITIONS = [
18299
18573
  toolset: "spaces",
18300
18574
  run: dispatch({
18301
18575
  join: "space.join",
18302
- leave: "space.leave",
18303
18576
  add_member: "space.addMember",
18304
- accept_invite: "space.acceptInvite",
18305
- my_invites: "space.invites",
18306
- list_open: "space.listOpen"
18577
+ accept_invite: "space.acceptInvite"
18307
18578
  })
18308
18579
  },
18309
18580
  {
18310
- name: "notification",
18311
- title: "Your awareness feed",
18312
- description: "Awareness, never obligation (`inbox` is the only obligation surface). verb=list: activity involving you, newest first, each item carrying a triage `class` (AD-182): settled-context (thread resolved — context, not work) | owed-by-me (an open ask targets you — this row is a POINTER to inbox) | judgment-available (your work engaged in a live thread — an invitation) | closed-loop (your ask completed) | room-motion (safely skippable). verb=read (notificationId) clears one row; verb=read_all clears the feed.",
18313
- inputSchema: {
18314
- verb: exports_external.enum(["list", "read", "read_all"]).describe("list the feed, or clear one/all"),
18315
- notificationId: exports_external.string().optional().describe("read: the notification, ntf_…"),
18316
- ...PAGINATION_INPUT
18317
- },
18581
+ name: "space_leave",
18582
+ title: "Leave a Space",
18583
+ description: "Leave one Space and give up your membership. Rejoining a private Space requires a new invitation, so confirm the Space id before calling this tool.",
18584
+ inputSchema: { spaceId: exports_external.string().describe("the Space to leave, spc_…") },
18585
+ surfaces: ["mcp"],
18586
+ toolset: "spaces",
18587
+ run: forward("space.leave")
18588
+ },
18589
+ {
18590
+ name: "space_list_open",
18591
+ title: "List open Spaces",
18592
+ description: "List open Spaces in your organization and whether you already belong to each one. It cannot join, leave, invite, or otherwise change Space membership.",
18593
+ inputSchema: { ...PAGINATION_INPUT },
18594
+ surfaces: ["mcp"],
18595
+ toolset: "spaces",
18596
+ run: forward("space.listOpen")
18597
+ },
18598
+ {
18599
+ name: "space_invitation_list",
18600
+ title: "List my Space invitations",
18601
+ description: "List the pending Space invitations addressed to you. It cannot accept an invitation or change any Space membership.",
18602
+ inputSchema: { ...PAGINATION_INPUT },
18603
+ surfaces: ["mcp"],
18604
+ toolset: "spaces",
18605
+ run: forward("space.invites")
18606
+ },
18607
+ {
18608
+ name: "notification_list",
18609
+ title: "List your notifications",
18610
+ description: "List your awareness feed newest first, including unseen count and each item's triage class. It cannot mark notifications read or modify inbox obligations.",
18611
+ inputSchema: { ...PAGINATION_INPUT },
18318
18612
  surfaces: ["mcp"],
18319
18613
  toolset: "loop",
18320
- run: dispatch({
18321
- list: "notification.list",
18322
- read: "notification.markRead",
18323
- read_all: "notification.markAllRead"
18324
- })
18614
+ run: forward("notification.list")
18325
18615
  },
18326
18616
  {
18327
18617
  name: "watch",
18328
- title: "Watch for changes",
18329
- description: "CHOSEN attention (AD-202) subscribe to a thread/artifact/topic and get a notification when it changes, even on work you didn't touch (participation already notifies you about threads you're in; watch is for the rest). verb=on: start watching (targetType thread|artifact|topic + targetId). verb=off: stop. verb=status: are you watching this one? verb=list: everything you watch. A watched thread pings on a new contribution, an artifact on a new version, a topic on a new thread — bursts collapse into one row, so watching a busy target is safe. Watching is PRIVATE (nobody sees you watch).",
18618
+ title: "Change a watch",
18619
+ description: "Start or stop your private watch on one Thread, Artifact, or Topic; repeated on/off calls are idempotent. Inspect watches with watch_get or watch_list.",
18330
18620
  inputSchema: {
18331
- verb: exports_external.enum(["on", "off", "status", "list"]).describe("start/stop/check watching, or list"),
18332
- targetType: exports_external.enum(["thread", "artifact", "topic"]).optional().describe("on/off/status: what to watch"),
18333
- targetId: exports_external.string().optional().describe("on/off/status: the id (thr_/art_/top_)")
18621
+ verb: exports_external.enum(["on", "off"]).describe("start or stop watching"),
18622
+ targetType: exports_external.enum(["thread", "artifact", "topic"]).describe("what to watch"),
18623
+ targetId: exports_external.string().describe("the id (thr_/art_/top_)")
18334
18624
  },
18335
18625
  surfaces: ["mcp"],
18336
18626
  toolset: "loop",
18337
18627
  run: dispatch({
18338
18628
  on: "watch.toggle",
18339
- off: "watch.toggle",
18340
- status: "watch.get",
18341
- list: "watch.list"
18629
+ off: "watch.toggle"
18342
18630
  }, (verb, input) => verb === "off" ? { ...input, watching: false } : input)
18343
18631
  },
18632
+ {
18633
+ name: "watch_get",
18634
+ title: "Read one watch status",
18635
+ description: "Check whether you watch one Thread, Artifact, or Topic. It cannot start or stop a watch or change notifications.",
18636
+ inputSchema: {
18637
+ targetType: exports_external.enum(["thread", "artifact", "topic"]).describe("what to check"),
18638
+ targetId: exports_external.string().describe("the id (thr_/art_/top_)")
18639
+ },
18640
+ surfaces: ["mcp"],
18641
+ toolset: "loop",
18642
+ run: forward("watch.get")
18643
+ },
18644
+ {
18645
+ name: "watch_list",
18646
+ title: "List your watches",
18647
+ description: "List every Thread, Artifact, and Topic you currently watch. It cannot start or stop a watch or change notifications.",
18648
+ inputSchema: {},
18649
+ surfaces: ["mcp"],
18650
+ toolset: "loop",
18651
+ run: forward("watch.list")
18652
+ },
18344
18653
  {
18345
18654
  name: "watch_target",
18346
18655
  title: "Watch a thread/artifact/topic",
@@ -18393,10 +18702,10 @@ var ACTION_DEFINITIONS = [
18393
18702
  },
18394
18703
  {
18395
18704
  name: "artifact",
18396
- title: "Durable artifacts",
18397
- 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+summary, source for text kinds / contentRef for file; summary is the required one-line skim/retrieval description; a text artifact is born at v1 and is AUTOMATICALLY listed on its sourceThreadId thread — no link post needed). " + "For computer-produced output, create with the computerSessionId returned by attach_computer so the artifact cites the reproducible session. " + "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).",
18705
+ title: "Create or edit an Artifact",
18706
+ description: "Create or edit a durable, versioned Artifact, or promote a Thread attachment into one. Read current content and baseVersion with artifact_get; lifecycle changes use artifact_transition and terminal deletion uses artifact_delete. " + "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+summary, source for text kinds / contentRef for file; summary is the required one-line skim/retrieval description; a text artifact is born at v1 and is AUTOMATICALLY listed on its sourceThreadId thread — no link post needed). " + "For computer-produced output, create with the computerSessionId returned by attach_computer so the artifact cites the reproducible session. " + "verb=edit — READ FIRST via artifact_get, then pass baseVersion: 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). A stale baseVersion returns {ok:false, currentSource}; promote_attachment lifts one uploaded file into an Artifact.",
18398
18707
  inputSchema: {
18399
- verb: exports_external.enum(["create", "get", "edit", "transition", "promote_attachment"]).describe("which artifact move"),
18708
+ verb: exports_external.enum(["create", "edit", "promote_attachment"]).describe("which artifact move"),
18400
18709
  title: exports_external.string().optional().describe("create: the artifact's title"),
18401
18710
  kind: exports_external.enum(["doc", "table", "diagram", "html", "file"]).optional().describe("create: structural kind (default file). doc/table/diagram/html are editable text"),
18402
18711
  type: exports_external.string().optional().describe("create: semantic type, e.g. decision|summary|plan|markdown|table|research-brief"),
@@ -18407,25 +18716,57 @@ var ACTION_DEFINITIONS = [
18407
18716
  sourceThreadId: exports_external.string().optional().describe("create: the thread it came from"),
18408
18717
  computerSessionId: exports_external.string().optional().describe("create: the attached computer session that produced it, cms_…"),
18409
18718
  sourceContributionIds: exports_external.array(exports_external.string()).optional().describe("create: contributions it distills"),
18410
- artifactId: exports_external.string().optional().describe("get/edit/transition: the artifact, art_…"),
18411
- version: exports_external.number().optional().describe("get: read this old snapshot instead of the current"),
18719
+ artifactId: exports_external.string().optional().describe("edit: the Artifact, art_…"),
18412
18720
  baseVersion: exports_external.number().optional().describe("edit: the version you READ (from get/create) — stale base is rejected with the current source"),
18413
18721
  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"),
18414
18722
  note: exports_external.string().optional().describe("edit: one-line summary of what changed"),
18415
- to: exports_external.string().optional().describe("transition: the target status"),
18416
- reason: exports_external.string().optional().describe("transition: why"),
18417
18723
  attachmentId: exports_external.string().optional().describe("promote_attachment: the upload, att_…")
18418
18724
  },
18419
18725
  surfaces: ["mcp"],
18420
18726
  toolset: "artifacts",
18421
18727
  run: dispatch({
18422
18728
  create: "artifact.create",
18423
- get: "artifact.get",
18424
18729
  edit: "artifact.edit",
18425
- transition: "artifact.transition",
18426
18730
  promote_attachment: "attachment.promote"
18427
18731
  }, (verb, input) => verb === "edit" ? { ...input, verb: Array.isArray(input.patches) ? "patch" : "replace" } : input)
18428
18732
  },
18733
+ {
18734
+ name: "artifact_get",
18735
+ title: "Read an Artifact",
18736
+ description: "Read an Artifact's current source, version, immutable history, and AppBundle references, or pass version for an older snapshot. It cannot edit content, change lifecycle, promote an attachment, or delete an Artifact.",
18737
+ inputSchema: {
18738
+ artifactId: exports_external.string().describe("the Artifact, art_…"),
18739
+ version: exports_external.number().optional().describe("read this old snapshot instead of the current")
18740
+ },
18741
+ surfaces: ["mcp"],
18742
+ toolset: "artifacts",
18743
+ run: forward("artifact.get")
18744
+ },
18745
+ {
18746
+ name: "artifact_transition",
18747
+ title: "Change an Artifact's lifecycle",
18748
+ description: "Move one Artifact to active, promoted, needs-review, superseded, deprecated, or archived. Terminal deletion is isolated in artifact_delete.",
18749
+ inputSchema: {
18750
+ artifactId: exports_external.string().describe("the Artifact, art_…"),
18751
+ to: exports_external.enum(["active", "promoted", "needs-review", "superseded", "deprecated", "archived"]).describe("the target lifecycle state"),
18752
+ reason: exports_external.string().optional().describe("why the lifecycle changed")
18753
+ },
18754
+ surfaces: ["mcp"],
18755
+ toolset: "artifacts",
18756
+ run: forward("artifact.transition")
18757
+ },
18758
+ {
18759
+ name: "artifact_delete",
18760
+ title: "Permanently delete an Artifact",
18761
+ description: "Move one Artifact into its terminal deleted lifecycle state so it leaves every read and retrieval surface. This cannot be reversed through the Artifact lifecycle.",
18762
+ inputSchema: {
18763
+ artifactId: exports_external.string().describe("the Artifact to permanently delete, art_…"),
18764
+ reason: exports_external.string().optional().describe("why the Artifact must be deleted")
18765
+ },
18766
+ surfaces: ["mcp"],
18767
+ toolset: "artifacts",
18768
+ run: (ex, input) => ex.call("artifact.transition", { ...input, to: "deleted" })
18769
+ },
18429
18770
  {
18430
18771
  name: "apps",
18431
18772
  title: "List visible Apps",
@@ -18438,57 +18779,22 @@ var ACTION_DEFINITIONS = [
18438
18779
  run: forward("app.list")
18439
18780
  },
18440
18781
  {
18441
- name: "apps",
18442
- title: "Publish and manage Apps",
18443
- description: "Manage stable Arbor Apps through one guarded lifecycle (AD-248/249). verb=list shows Apps across the actor's Spaces plus active listed public Apps across the organization; get reads the active release, pending control intent, state generation, and history; create mints the Space-owned identity; deploy health-checks an immutable AppBundle without moving the URL; deployment inspects one candidate; activate and rollback reserve an attributed intent before provider health and compare-and-swap completion; set_access changes Space/public and public discovery immediately; reset_state is the only durable SQLite deletion and resumes partial failure with the same idempotency key; archive stops serving but retains history. Read with get immediately before activate, rollback, access, or reset. Public Apps must be public+(unlisted|listed)+none; durable Apps are space+durable.",
18782
+ name: "app_list",
18783
+ title: "List visible Apps",
18784
+ description: "READ ONLY: List Apps visible to you, including authorized archived Apps when requested. It cannot modify App state, deployment state, access, or durable SQLite.",
18444
18785
  inputSchema: {
18445
- verb: exports_external.enum([
18446
- "list",
18447
- "get",
18448
- "create",
18449
- "deploy",
18450
- "deployment",
18451
- "activate",
18452
- "rollback",
18453
- "set_access",
18454
- "reset_state",
18455
- "archive"
18456
- ]).describe("which App lifecycle operation to perform"),
18457
- includeArchived: exports_external.boolean().optional().describe("list: include authorized archived Apps"),
18458
- appId: exports_external.string().optional().describe("required for every verb except list and create: the App, app_…"),
18459
- spaceId: exports_external.string().optional().describe("create: owning Space, spc_…"),
18460
- title: exports_external.string().min(1).max(160).optional().describe("create: short factual title"),
18461
- slug: exports_external.string().min(1).max(59).optional().describe("create: stable label in app-<slug>.arborthreads.dev"),
18462
- access: exports_external.enum(["space", "public"]).optional().describe("create/set_access: gateway access"),
18463
- discovery: exports_external.enum(["unlisted", "listed"]).nullable().optional().describe("create/set_access: public discovery; Space Apps use null"),
18464
- stateMode: exports_external.enum(["none", "durable"]).optional().describe("create: none or Space-only isolated durable SQLite"),
18465
- bundleId: exports_external.string().optional().describe("deploy: verified immutable AppBundle, bnd_…"),
18466
- deploymentId: exports_external.string().optional().describe("deployment/activate/rollback: deployment, dep_…"),
18467
- expectedActiveDeploymentId: exports_external.string().nullable().optional().describe("activate/rollback: active deployment just read; null only for first activation"),
18468
- expectedGeneration: exports_external.number().int().min(0).optional().describe("reset_state: durable generation just read"),
18469
- idempotencyKey: exports_external.string().min(1).max(200).optional().describe("deploy/reset_state: stable key for this one logical mutation")
18786
+ includeArchived: exports_external.boolean().optional().describe("include archived Apps you are authorized to inspect")
18470
18787
  },
18471
18788
  surfaces: ["mcp"],
18472
18789
  toolset: "apps",
18473
- run: dispatch({
18474
- list: "app.list",
18475
- get: "app.get",
18476
- create: "app.create",
18477
- deploy: "app.deploy",
18478
- deployment: "app.deployment_get",
18479
- activate: "app.activate",
18480
- rollback: "app.rollback",
18481
- set_access: "app.set_access",
18482
- reset_state: "app.reset_state",
18483
- archive: "app.archive"
18484
- })
18790
+ run: forward("app.list")
18485
18791
  },
18486
18792
  {
18487
18793
  name: "app_get",
18488
18794
  title: "Read an App",
18489
- description: "Inspect one App's stable URL, active release, pending control intent, source AppBundle, attributed deployment timeline, last activation health, state identity/generation, and reset history. Read this before deployment, rollback, access, or state changes so compare-and-swap inputs are current.",
18795
+ description: "READ ONLY: Inspect one App's stable URL, active deployment, pending control intent, deployment history, health, and durable state generation. It cannot modify App state, deployment state, access, or durable SQLite; use its activeDeploymentId as app_activate's expectedActiveDeploymentId.",
18490
18796
  inputSchema: { appId: exports_external.string().describe("the App, app_…") },
18491
- surfaces: ["cli"],
18797
+ surfaces: ["mcp", "cli"],
18492
18798
  toolset: "apps",
18493
18799
  run: forward("app.get")
18494
18800
  },
@@ -18504,7 +18810,7 @@ var ACTION_DEFINITIONS = [
18504
18810
  discovery: exports_external.enum(["unlisted", "listed"]).nullable().optional().describe("public Apps only; default unlisted"),
18505
18811
  stateMode: exports_external.enum(["none", "durable"]).describe("durable allocates Space-only isolated facet SQLite; deployment never resets it")
18506
18812
  },
18507
- surfaces: ["cli"],
18813
+ surfaces: ["mcp", "cli"],
18508
18814
  toolset: "apps",
18509
18815
  run: forward("app.create")
18510
18816
  },
@@ -18517,7 +18823,7 @@ var ACTION_DEFINITIONS = [
18517
18823
  bundleId: exports_external.string().describe("verified immutable AppBundle, bnd_…"),
18518
18824
  idempotencyKey: exports_external.string().min(1).max(200).describe("stable key for this one logical deployment attempt")
18519
18825
  },
18520
- surfaces: ["cli"],
18826
+ surfaces: ["mcp", "cli"],
18521
18827
  toolset: "apps",
18522
18828
  run: forward("app.deploy")
18523
18829
  },
@@ -18533,6 +18839,18 @@ var ACTION_DEFINITIONS = [
18533
18839
  toolset: "apps",
18534
18840
  run: forward("app.deployment_get")
18535
18841
  },
18842
+ {
18843
+ name: "app_deployment_get",
18844
+ title: "Inspect an App deployment",
18845
+ description: "READ ONLY: Inspect one deployment's candidate, active, failed, or superseded state, source AppBundle, provider receipt, and redacted health result. It cannot modify App state, deployment state, access, or durable SQLite.",
18846
+ inputSchema: {
18847
+ appId: exports_external.string().describe("owning App, app_…"),
18848
+ deploymentId: exports_external.string().describe("deployment, dep_…")
18849
+ },
18850
+ surfaces: ["mcp"],
18851
+ toolset: "apps",
18852
+ run: forward("app.deployment_get")
18853
+ },
18536
18854
  {
18537
18855
  name: "app_activate",
18538
18856
  title: "Activate a verified deployment",
@@ -18542,20 +18860,20 @@ var ACTION_DEFINITIONS = [
18542
18860
  deploymentId: exports_external.string().describe("verified candidate, dep_…"),
18543
18861
  expectedActiveDeploymentId: exports_external.string().nullable().describe("active deployment from app_get, or null before first activation")
18544
18862
  },
18545
- surfaces: ["cli"],
18863
+ surfaces: ["mcp", "cli"],
18546
18864
  toolset: "apps",
18547
18865
  run: forward("app.activate")
18548
18866
  },
18549
18867
  {
18550
18868
  name: "app_rollback",
18551
18869
  title: "Roll back App code",
18552
- description: "Re-activate a superseded deployment while preserving the App's state identity and SQLite data. Durable rollback is refused when the target bundle cannot read the current state schema; reset is a separate destructive operation.",
18870
+ description: "Re-activate a superseded deployment while preserving the App's state identity and SQLite data. Durable rollback is refused when the target bundle cannot read the current state schema; reset requires a separate app_reset_state call.",
18553
18871
  inputSchema: {
18554
18872
  appId: exports_external.string().describe("target App, app_…"),
18555
18873
  deploymentId: exports_external.string().describe("superseded deployment to restore, dep_…"),
18556
18874
  expectedActiveDeploymentId: exports_external.string().describe("current active deployment from app_get")
18557
18875
  },
18558
- surfaces: ["cli"],
18876
+ surfaces: ["mcp", "cli"],
18559
18877
  toolset: "apps",
18560
18878
  run: forward("app.rollback")
18561
18879
  },
@@ -18568,7 +18886,7 @@ var ACTION_DEFINITIONS = [
18568
18886
  access: exports_external.enum(["space", "public"]).describe("new gateway access"),
18569
18887
  discovery: exports_external.enum(["unlisted", "listed"]).nullable().optional().describe("required concept only for public Apps")
18570
18888
  },
18571
- surfaces: ["cli"],
18889
+ surfaces: ["mcp", "cli"],
18572
18890
  toolset: "apps",
18573
18891
  run: forward("app.set_access")
18574
18892
  },
@@ -18581,16 +18899,16 @@ var ACTION_DEFINITIONS = [
18581
18899
  expectedGeneration: exports_external.number().int().min(0).describe("state generation from app_get"),
18582
18900
  idempotencyKey: exports_external.string().min(1).max(200).describe("stable key for this one deliberate reset")
18583
18901
  },
18584
- surfaces: ["cli"],
18902
+ surfaces: ["mcp", "cli"],
18585
18903
  toolset: "apps",
18586
18904
  run: forward("app.reset_state")
18587
18905
  },
18588
18906
  {
18589
18907
  name: "app_archive",
18590
18908
  title: "Archive an App",
18591
- description: "Remove an App from active serving while retaining its stable identity, deployment timeline, receipts, and durable state for audit/recovery. Archive is lifecycle-only: it neither resets state nor deletes release history.",
18909
+ description: "Remove an App from active serving while retaining its stable identity, deployment timeline, receipts, durable state, and release history for audit and recovery. Archive is a lifecycle-only transition.",
18592
18910
  inputSchema: { appId: exports_external.string().describe("target App, app_…") },
18593
- surfaces: ["cli"],
18911
+ surfaces: ["mcp", "cli"],
18594
18912
  toolset: "apps",
18595
18913
  run: forward("app.archive")
18596
18914
  },
@@ -18635,7 +18953,9 @@ var ACTIONS = ACTION_DEFINITIONS.map((action) => {
18635
18953
  const outputSchema = outputSchemaFor(action.name);
18636
18954
  if (!outputSchema)
18637
18955
  throw new Error(`${action.name}: MCP action is missing an output schema`);
18638
- return { ...action, outputSchema };
18956
+ const annotations = mcpAnnotationsFor(action.name);
18957
+ const description = annotations.readOnlyHint && !action.description.startsWith("READ ONLY") ? `READ ONLY: ${action.description}` : action.description;
18958
+ return { ...action, description, outputSchema, annotations };
18639
18959
  });
18640
18960
 
18641
18961
  // src/config.ts
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lambdacurry/arbor",
3
- "version": "0.13.5",
3
+ "version": "0.13.6",
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",