@lambdacurry/arbor 0.22.34 → 0.22.36

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 +105 -11
  2. package/package.json +1 -1
package/dist/arbor.js CHANGED
@@ -2,7 +2,7 @@
2
2
  // package.json
3
3
  var package_default = {
4
4
  name: "@lambdacurry/arbor",
5
- version: "0.22.34",
5
+ version: "0.22.36",
6
6
  description: "The Arbor CLI — a shared workspace for people and agents. The human + headless-agent write path over Arbor's guarded operation surface.",
7
7
  keywords: [
8
8
  "agents",
@@ -6284,14 +6284,17 @@ var profiles = sqliteTable("profiles", {
6284
6284
  permissions: text("permissions", { mode: "json" }).$type().notNull().default([]),
6285
6285
  reputation: text("reputation", { mode: "json" }).$type().notNull().default({}),
6286
6286
  status: text("status").$type().notNull().default("active"),
6287
- betterAuthUserId: text("better_auth_user_id").unique(),
6287
+ betterAuthUserId: text("better_auth_user_id"),
6288
6288
  managedByProfileId: text("managed_by_profile_id"),
6289
6289
  color: text("color").$type(),
6290
6290
  emoji: text("emoji"),
6291
6291
  description: text("description"),
6292
6292
  notificationPrefs: text("notification_prefs", { mode: "json" }).$type(),
6293
6293
  createdAt: ts("created_at").notNull()
6294
- }, (t) => ({ orgIdx: index("profiles_org_idx").on(t.orgId) }));
6294
+ }, (t) => ({
6295
+ orgIdx: index("profiles_org_idx").on(t.orgId),
6296
+ baUserOrgUq: uniqueIndex("profiles_better_auth_user_org_unique").on(t.betterAuthUserId, t.orgId)
6297
+ }));
6295
6298
  var clients = sqliteTable("clients", {
6296
6299
  id: text("id").primaryKey(),
6297
6300
  kind: text("kind").$type().notNull(),
@@ -6360,6 +6363,17 @@ var invitations = sqliteTable("invitations", {
6360
6363
  emailIdx: index("invitations_email_idx").on(t.email, t.status),
6361
6364
  tokenIdx: uniqueIndex("invitations_token_idx").on(t.token)
6362
6365
  }));
6366
+ var humanSessionBindings = sqliteTable("human_session_bindings", {
6367
+ sessionId: text("session_id").primaryKey(),
6368
+ activeProfileId: text("active_profile_id").notNull(),
6369
+ updatedAt: ts("updated_at").notNull()
6370
+ }, (t) => ({ profileIdx: index("human_session_bindings_profile_idx").on(t.activeProfileId) }));
6371
+ var oauthClientBindings = sqliteTable("oauth_client_bindings", {
6372
+ userId: text("user_id").notNull(),
6373
+ clientId: text("client_id").notNull(),
6374
+ profileId: text("profile_id").notNull(),
6375
+ createdAt: ts("created_at").notNull()
6376
+ }, (t) => ({ pk: primaryKey({ columns: [t.userId, t.clientId] }) }));
6363
6377
  var agentPairings = sqliteTable("agent_pairings", {
6364
6378
  id: text("id").primaryKey(),
6365
6379
  orgId: text("org_id").notNull().references(() => orgs.id),
@@ -8637,9 +8651,30 @@ var MCP_OUTPUT_SCHEMAS = {
8637
8651
  profile: profile2,
8638
8652
  client: looseObject({ id, kind: string2() }),
8639
8653
  platform: looseObject({ level: string2() }).optional(),
8654
+ memberships: array(jsonObject).optional(),
8655
+ pendingInvites: array(jsonObject).optional(),
8640
8656
  spaceCharters: array(jsonObject).optional(),
8641
8657
  completeness: jsonObject.optional()
8642
8658
  }),
8659
+ org_switch: looseObject({
8660
+ profile: jsonObject,
8661
+ memberships: array(jsonObject)
8662
+ }),
8663
+ invitation_accept: looseObject({
8664
+ profileId: id,
8665
+ orgId: id,
8666
+ orgName: string2().nullable(),
8667
+ orgRole: string2()
8668
+ }),
8669
+ org_create: looseObject({
8670
+ orgId: id,
8671
+ profileId: id,
8672
+ name: string2()
8673
+ }),
8674
+ org_leave: looseObject({
8675
+ leftProfileId: id,
8676
+ locked: boolean2()
8677
+ }),
8643
8678
  introduce: profileMutation,
8644
8679
  thread_get: looseObject({
8645
8680
  threadId: id.optional(),
@@ -9264,10 +9299,10 @@ var MCP_OUTPUT_SCHEMAS = {
9264
9299
  computer_process_read: looseObject({
9265
9300
  processId: string2(),
9266
9301
  status: string2(),
9267
- outputRetention: object({ state: literal("preserved"), expiresAt: timestamp }).optional(),
9302
+ outputRetention: object({ state: _enum(["preserved", "expired"]), expiresAt: timestamp }).optional(),
9268
9303
  timedOut: boolean2().optional(),
9269
9304
  endedAt: timestamp.nullable().optional(),
9270
- durationMs: number2().int().min(0).nullable().optional(),
9305
+ durationMs: number2().int().min(0).nullable().optional().describe("process elapsed runtime, final at terminal; it is not read or RPC latency"),
9271
9306
  stdout: string2().optional(),
9272
9307
  stderr: string2().optional(),
9273
9308
  truncated: boolean2().optional(),
@@ -9807,6 +9842,10 @@ var MCP_TOOL_ANNOTATIONS = {
9807
9842
  inbox: readOnly,
9808
9843
  dismiss: destructive,
9809
9844
  whoami: readOnly,
9845
+ org_switch: idempotent,
9846
+ invitation_accept: additive,
9847
+ org_create: additive,
9848
+ org_leave: destructive,
9810
9849
  introduce: additive,
9811
9850
  thread_get: readOnly,
9812
9851
  tree: readOnly,
@@ -10151,7 +10190,7 @@ Arbor's MCP is the collaboration and durable-knowledge surface. Configure, autho
10151
10190
 
10152
10191
  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.`;
10153
10192
  var ARBOR_SKILL_MARKDOWN = renderArborSkill(ORIENTATION);
10154
- var COMPUTER_ORIENTATION = `Long Computer work returns a processId handle — do not hold one tool call for a full test or build. \`computer_exec\` runs a command; if the result is still running, observe it with \`computer_process_read\`; read a truncated continuation only when you need more output. Finish and suspend preserve unread terminal output internally, with original process IDs/cursors recoverable for 30 days. Do not chain \`pnpm test && pnpm build\` in one exec; split steps or pass \`background:true\`. An explicit waitForTerminalMs uses one server wait and canonical output page; otherwise the CLI polls only while the process is silent unless \`--no-wait\`. It never concatenates continuation pages into one agent context.
10193
+ var COMPUTER_ORIENTATION = `Long Computer work returns a processId handle — do not hold one tool call for a full test or build. \`computer_exec\` runs a command; if the result is still running, observe it with \`computer_process_read\`; read a truncated continuation only when you need more output. Finish and suspend preserve unread terminal output internally, with original process IDs/cursors recoverable for 30 days. Do not chain \`pnpm test && pnpm build\` in one exec; split steps or pass \`background:true\`. An explicit waitForTerminalMs uses one server wait and canonical output page; includeOutput=false on process_read returns status/liveness/recovery without output delivery or cursor advancement. Otherwise the CLI polls only while the process is silent unless \`--no-wait\`. It never concatenates continuation pages into one agent context.
10155
10194
 
10156
10195
  Arbor Computer is the complete software execution platform attached to Arbor's durable collaboration graph. It configures project environments, executes and verifies work, packages and releases Apps, and preserves reviewable evidence; it does not schedule agents or replace Arbor collaboration.
10157
10196
 
@@ -10995,14 +11034,15 @@ var ACTION_DEFINITIONS = [
10995
11034
  {
10996
11035
  name: "computer_process_read",
10997
11036
  title: "Read a background process",
10998
- description: "Read output with nextCursor, including repeated cursors for 30 days after preservation without provider wake. durationMs is process elapsed runtime (final at terminal), not RPC latency; waitForTerminalMs adds one bounded observation/page; expiry may leave work running; transport failure stays unknown/unsafe; only terminal or exact absence proof settles the durable operation.",
11037
+ description: "Read output with nextCursor; includeOutput=false returns status/liveness/recovery without output or cursor delivery; durationMs is process runtime, not RPC latency; wait expiry does not stop work; transport failure stays unknown/unsafe; only terminal or exact absence proof settles the durable operation; preserved output stays readable for 30 days without provider wake.",
10999
11038
  inputSchema: {
11000
11039
  computerSessionId: string2().optional().describe("legacy alternative to runId: a computerSessionId on that Computer, cms_…; required unless runId is supplied"),
11001
11040
  runId: string2().optional().describe("the Run that owns this process, if it was started inside a Run"),
11002
11041
  processId: string2().min(1).describe("the processId returned by background exec, or matching computer_run_receipt.operations[].operationId for recovered foreground Run work, proc_…"),
11003
11042
  cursor: string2().optional().describe("continue after nextCursor from the prior result; omit to read from the beginning"),
11004
11043
  maxBytes: number2().int().min(1024).max(65536).optional().describe("maximum combined output bytes to return; default 4,096, opt up only when needed"),
11005
- waitForTerminalMs: number2().int().min(0).max(45000).optional().describe("use one native terminal-observation window up to this many milliseconds, then return one canonical output page; 0 is immediate, surviving descendants may remain running, and omission preserves the current read behavior")
11044
+ waitForTerminalMs: number2().int().min(0).max(45000).optional().describe("use one native terminal-observation window up to this many milliseconds, then return one canonical output page when includeOutput is true; 0 is immediate, surviving descendants may remain running, and omission preserves the current read behavior"),
11045
+ includeOutput: boolean2().optional().describe("include stdout, stderr, and paging cursors; set false to inspect status, liveness, and recovery without delivering output or marking it delivered")
11006
11046
  },
11007
11047
  surfaces: ["computer-mcp", "computer-cli"],
11008
11048
  toolset: "loop",
@@ -11543,7 +11583,7 @@ var ACTION_DEFINITIONS = [
11543
11583
  {
11544
11584
  name: "whoami",
11545
11585
  title: "Who am I",
11546
- 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.",
11586
+ description: "Return your Arbor identity as resolved from your credential: which profile and org you're acting as, and which other orgs you can switch to on a human session.",
11547
11587
  inputSchema: {},
11548
11588
  surfaces: ["mcp", "cli"],
11549
11589
  toolset: "loop",
@@ -11871,6 +11911,50 @@ var ACTION_DEFINITIONS = [
11871
11911
  toolset: "admin",
11872
11912
  run: forward("service.register")
11873
11913
  },
11914
+ {
11915
+ name: "org_switch",
11916
+ title: "Switch workspace",
11917
+ description: "Act as your Profile in another org you already belong to (orgId from whoami's memberships). Session-only, not a PAT.",
11918
+ inputSchema: {
11919
+ orgId: string2().min(1).describe("the org to switch this session into")
11920
+ },
11921
+ surfaces: ["mcp", "cli"],
11922
+ toolset: "loop",
11923
+ run: forward("org.switch")
11924
+ },
11925
+ {
11926
+ name: "invitation_accept",
11927
+ title: "Accept an org invitation",
11928
+ description: "Join another org from a pending invitation while already signed in (invitationId from whoami's pendingInvites). Session-only, not a PAT.",
11929
+ inputSchema: {
11930
+ invitationId: string2().min(1).describe("the pending invitation id")
11931
+ },
11932
+ surfaces: ["mcp", "cli"],
11933
+ toolset: "loop",
11934
+ run: forward("invitation.accept")
11935
+ },
11936
+ {
11937
+ name: "org_create",
11938
+ title: "Create a workspace",
11939
+ description: "Create a new org you own (a personal workspace) and switch this session into it. Session-only, not a PAT.",
11940
+ inputSchema: {
11941
+ name: string2().min(1).optional().describe("workspace name; defaults to '{you}'s workspace'")
11942
+ },
11943
+ surfaces: ["mcp", "cli"],
11944
+ toolset: "loop",
11945
+ run: forward("org.create")
11946
+ },
11947
+ {
11948
+ name: "org_leave",
11949
+ title: "Leave a workspace",
11950
+ description: "Leave an org you belong to, disabling that Profile — keep at least one workspace, and appoint another owner before leaving a multi-person org. Session-only, not a PAT.",
11951
+ inputSchema: {
11952
+ orgId: string2().min(1).optional().describe("org to leave; defaults to the current org")
11953
+ },
11954
+ surfaces: ["cli"],
11955
+ toolset: "loop",
11956
+ run: forward("org.leave")
11957
+ },
11874
11958
  {
11875
11959
  name: "human_invite",
11876
11960
  title: "Invite a human",
@@ -14480,10 +14564,20 @@ async function renderMe(ctx, action) {
14480
14564
  const setCharters = me.spaceCharters ?? [];
14481
14565
  const charterLines = setCharters.length > 0 ? ` space charters:
14482
14566
  ` + setCharters.map((s) => ` • ${s.spaceTitle} — ${clamp(s.charter)}
14567
+ `).join("") : "";
14568
+ const currentOrg = me.memberships?.find((m) => m.current);
14569
+ const otherOrgs = (me.memberships ?? []).filter((m) => !m.current);
14570
+ const orgLine = currentOrg ? ` org ${currentOrg.orgName} (${me.profile.orgId}) · resolved via ${me.client.kind}
14571
+ ` : ` org ${me.profile.orgId} · resolved via ${me.client.kind}
14572
+ `;
14573
+ const switchLines = otherOrgs.length > 0 ? ` other workspaces:
14574
+ ` + otherOrgs.map((m) => ` • ${m.orgName} — \`org_switch --org-id ${m.orgId}\`
14575
+ `).join("") : "";
14576
+ const inviteLines = (me.pendingInvites ?? []).length > 0 ? ` pending invites:
14577
+ ` + (me.pendingInvites ?? []).map((i) => ` • ${i.orgName} — \`invitation_accept --invitation-id ${i.invitationId}\`
14483
14578
  `).join("") : "";
14484
14579
  const human = `${me.profile.displayName} · ${me.profile.kind} · org-role ${me.profile.orgRole}
14485
- ` + ` org ${me.profile.orgId} · resolved via ${me.client.kind}
14486
- ` + setLines + charterLines + (gaps.length > 0 ? ` to be more legible to your team, set: ${gaps.join("; ")} (\`introduce\`)
14580
+ ` + orgLine + switchLines + inviteLines + setLines + charterLines + (gaps.length > 0 ? ` to be more legible to your team, set: ${gaps.join("; ")} (\`introduce\`)
14487
14581
  ` : "") + spaceLines;
14488
14582
  emitDual(me, human, action, ctx);
14489
14583
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lambdacurry/arbor",
3
- "version": "0.22.34",
3
+ "version": "0.22.36",
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",