@lambdacurry/arbor 0.21.23 → 0.21.25

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 +36 -6
  2. package/package.json +1 -1
package/dist/arbor.js CHANGED
@@ -2011,6 +2011,11 @@ var threadComputers = sqliteTable("thread_computers", {
2011
2011
  successorComputerId: text("successor_computer_id"),
2012
2012
  restoredFromSnapshotId: text("restored_from_snapshot_id"),
2013
2013
  currentSnapshotId: text("current_snapshot_id"),
2014
+ workspaceMutationStarted: integer("workspace_mutation_started").notNull().default(0),
2015
+ workspaceMutationAcknowledged: integer("workspace_mutation_acknowledged").notNull().default(0),
2016
+ workspaceDurableMutationVersion: integer("workspace_durable_mutation_version"),
2017
+ workspaceRecoveryLeaseId: text("workspace_recovery_lease_id"),
2018
+ workspaceRecoveryLeaseExpiresAt: ts("workspace_recovery_lease_expires_at"),
2014
2019
  createdByProfileId: text("created_by_profile_id").notNull().references(() => profiles.id),
2015
2020
  createdExecutionContextId: text("created_execution_context_id").notNull().references(() => executionContexts.id),
2016
2021
  createdAt: ts("created_at").notNull()
@@ -2057,10 +2062,13 @@ var computerSessions = sqliteTable("computer_sessions", {
2057
2062
  executionContextId: text("execution_context_id").notNull().references(() => executionContexts.id),
2058
2063
  configKey: text("config_key").notNull(),
2059
2064
  startingSnapshotId: text("starting_snapshot_id"),
2065
+ openIdempotencyKey: text("open_idempotency_key"),
2066
+ openIdempotencyFingerprint: text("open_idempotency_fingerprint"),
2060
2067
  attachedAt: ts("attached_at").notNull()
2061
2068
  }, (t) => ({
2062
2069
  computerIdx: index("computer_sessions_computer_idx").on(t.computerId, t.attachedAt),
2063
- threadIdx: index("computer_sessions_thread_idx").on(t.threadId, t.attachedAt)
2070
+ threadIdx: index("computer_sessions_thread_idx").on(t.threadId, t.attachedAt),
2071
+ openIdempotencyIdx: uniqueIndex("computer_sessions_open_idempotency_idx").on(t.profileId, t.threadId, t.openIdempotencyKey)
2064
2072
  }));
2065
2073
  var computerRuns = sqliteTable("computer_runs", {
2066
2074
  id: text("id").primaryKey(),
@@ -2426,6 +2434,7 @@ var appBundles = sqliteTable("app_bundles", {
2426
2434
  capabilityRequirements: text("capability_requirements", { mode: "json" }).$type().notNull().default([]),
2427
2435
  runtimeConfigRequirements: text("runtime_config_requirements", { mode: "json" }).$type().notNull().default([]),
2428
2436
  protectedIngress: text("protected_ingress", { mode: "json" }).$type().notNull().default([]),
2437
+ roomEntrypoint: text("room_entrypoint", { mode: "json" }).$type(),
2429
2438
  stateSchemaVersion: integer("state_schema_version").notNull().default(0),
2430
2439
  stateCompatibleFrom: integer("state_compatible_from").notNull().default(0),
2431
2440
  stateCompatibleThrough: integer("state_compatible_through").notNull().default(0),
@@ -2485,6 +2494,7 @@ var apps = sqliteTable("apps", {
2485
2494
  runtimeHealth: text("runtime_health").$type().notNull().default("unknown"),
2486
2495
  runtimeHealthUpdatedAt: ts("runtime_health_updated_at"),
2487
2496
  stateMode: text("state_mode").$type().notNull(),
2497
+ roomMode: text("room_mode").$type().notNull().default("none"),
2488
2498
  stateIdentity: text("state_identity").notNull(),
2489
2499
  stateGeneration: integer("state_generation").notNull().default(0),
2490
2500
  pendingStateOperationId: text("pending_state_reset_id"),
@@ -2841,6 +2851,11 @@ var RUN_PROVIDER_LIFECYCLE_PHASE_SET = new Set(RUN_PROVIDER_LIFECYCLE_PHASES);
2841
2851
 
2842
2852
  // ../core/src/ops/computer.ts
2843
2853
  var MAX_COMPUTER_REPOSITORIES = 10;
2854
+ var COMPUTER_WORKSPACE_RECOVERY_CHECKPOINT_DEADLINE_MS = 10 * 60 * 1000;
2855
+ var COMPUTER_WORKSPACE_RECOVERY_ATTACH_DEADLINE_MS = 31 * 60 * 1000;
2856
+ var COMPUTER_WORKSPACE_RECOVERY_HEALTH_DEADLINE_MS = 2 * 60 * 1000;
2857
+ var COMPUTER_WORKSPACE_RECOVERY_HEADROOM_MS = 2 * 60 * 1000;
2858
+ var COMPUTER_WORKSPACE_RECOVERY_LEASE_MS = COMPUTER_WORKSPACE_RECOVERY_CHECKPOINT_DEADLINE_MS + COMPUTER_WORKSPACE_RECOVERY_ATTACH_DEADLINE_MS + COMPUTER_WORKSPACE_RECOVERY_HEALTH_DEADLINE_MS + COMPUTER_WORKSPACE_RECOVERY_HEADROOM_MS;
2844
2859
 
2845
2860
  // ../core/src/ops/request.ts
2846
2861
  var REQUEST_TTL_MS = 14 * 24 * 60 * 60 * 1000;
@@ -18333,6 +18348,11 @@ var MCP_OUTPUT_SCHEMAS = {
18333
18348
  defaultCwd: exports_external.string(),
18334
18349
  capabilities: computerOpenCapabilities,
18335
18350
  agentBrief: computerOpenAgentBrief,
18351
+ startupReceipt: exports_external.object({
18352
+ computerSessionId: id,
18353
+ replayed: exports_external.boolean(),
18354
+ mutationRetrySafety: exports_external.literal("safe")
18355
+ }),
18336
18356
  reconciliation: exports_external.union([
18337
18357
  exports_external.object({
18338
18358
  action: exports_external.literal("automatic_reprovision"),
@@ -18712,6 +18732,11 @@ var OUTPUT_SHAPERS = {
18712
18732
  "restorationOutcome",
18713
18733
  "addedRepositories"
18714
18734
  ]);
18735
+ const startupReceipt = selected(result.startupReceipt, [
18736
+ "computerSessionId",
18737
+ "replayed",
18738
+ "mutationRetrySafety"
18739
+ ]);
18715
18740
  return defined([
18716
18741
  ["computerSessionId", result.computerSessionId],
18717
18742
  [
@@ -18743,6 +18768,7 @@ var OUTPUT_SHAPERS = {
18743
18768
  ["activeRuns", activeRuns]
18744
18769
  ]) : undefined
18745
18770
  ],
18771
+ ["startupReceipt", startupReceipt],
18746
18772
  ["reconciliation", reconciliation]
18747
18773
  ]);
18748
18774
  },
@@ -19681,6 +19707,7 @@ var ACTION_DEFINITIONS = [
19681
19707
  description: "Open a Thread project environment and return its session/workspace, verified root repository instruction pointers, Agent Brief, capabilities, Runs, and reconciliation receipt. Forward profile-revision or additive authorized repository drift auto-reconciles through a successor; removal, setup, backend/provider/profile changes, or ambiguous drift refuse with computer_reprovision guidance.",
19682
19708
  inputSchema: {
19683
19709
  threadId: exports_external.string().describe("the Arbor Thread whose stable computer to open, thr_…"),
19710
+ idempotencyKey: exports_external.string().min(1).max(200).describe("stable key for this ONE logical open; retry the same call unchanged—even after a lost response—to reuse the same ComputerSession; never mint a new key for an unresolved attempt"),
19684
19711
  vcpus: exports_external.number().int().min(1).max(4).optional().describe("optional container vCPU request"),
19685
19712
  label: exports_external.string().optional().describe("short runtime label for operator diagnostics")
19686
19713
  },
@@ -20003,6 +20030,7 @@ var ACTION_DEFINITIONS = [
20003
20030
  });
20004
20031
  }
20005
20032
  }).optional().describe("externally routable protected App paths; v1 supports one MCP route using Arbor OAuth and injects no caller principal into App code"),
20033
+ roomEntrypoint: exports_external.object({ className: exports_external.literal("Room"), protocol: exports_external.literal("websocket") }).optional().describe("declare that this fetch bundle also exports class Room for ephemeral App rooms (AD-295); requires no runtime config and changes the bundle's content digest"),
20006
20034
  idempotencyKey: exports_external.string().min(1).max(200).describe("stable key for this one logical bundle publication")
20007
20035
  },
20008
20036
  surfaces: ["computer-mcp", "computer-cli"],
@@ -21294,7 +21322,8 @@ var ACTION_DEFINITIONS = [
21294
21322
  slug: exports_external.string().min(1).max(59).describe("stable DNS label used by app-<slug>.arborthreads.dev"),
21295
21323
  access: exports_external.enum(["space", "public"]).describe("space requires membership; public is anonymous at the gateway"),
21296
21324
  discovery: exports_external.enum(["unlisted", "listed"]).nullable().optional().describe("public Apps only; default unlisted"),
21297
- stateMode: exports_external.enum(["none", "durable"]).describe("durable allocates Space-only isolated facet SQLite; deployment never resets it")
21325
+ stateMode: exports_external.enum(["none", "durable"]).describe("durable allocates Space-only isolated facet SQLite; deployment never resets it"),
21326
+ roomMode: exports_external.enum(["none", "ephemeral"]).optional().describe("ephemeral opts a public stateMode:none App into bounded TTL-scoped anonymous rooms (AD-295); default none keeps every room surface off")
21298
21327
  },
21299
21328
  surfaces: ["computer-mcp", "cli"],
21300
21329
  toolset: "apps",
@@ -21410,12 +21439,13 @@ var ACTION_DEFINITIONS = [
21410
21439
  },
21411
21440
  {
21412
21441
  name: "app_update",
21413
- title: "Edit an App's title or description",
21414
- description: "Change the human-facing metadata a reader sees before opening an App. The description is the one line that answers 'what is this for'; keep it factual and current, because the directory and the App page both lead with it. Slug is deliberately not editable — the URL is the App's stable identity. Access, lifecycle, release, and state each have their own operation.",
21442
+ title: "Edit an App's metadata or room mode",
21443
+ description: "Change the human-facing metadata a reader sees before opening an App, or toggle its ephemeral-room capability. The description is the one line that answers 'what is this for'; keep it factual and current, because the directory and the App page both lead with it. Slug is deliberately not editable — the URL is the App's stable identity. Access, lifecycle, release, and state each have their own operation.",
21415
21444
  inputSchema: {
21416
21445
  appId: exports_external.string().describe("target App, app_…"),
21417
21446
  title: exports_external.string().min(1).max(160).optional().describe("short factual App title"),
21418
- description: exports_external.string().min(1).max(200).optional().describe("one line of what this App is for")
21447
+ description: exports_external.string().min(1).max(200).optional().describe("one line of what this App is for"),
21448
+ roomMode: exports_external.enum(["none", "ephemeral"]).optional().describe("ephemeral requires a public stateMode:none App; rooms serve once the active release declares a Room entrypoint, and none turns the surface off")
21419
21449
  },
21420
21450
  surfaces: ["cli"],
21421
21451
  toolset: "apps",
@@ -22635,7 +22665,7 @@ async function renderMe(ctx, action) {
22635
22665
  ` : "") + spaceLines;
22636
22666
  emitDual(me, human, action, ctx);
22637
22667
  }
22638
- var CLI_NOTE = `On this CLI, before your first write: commands are NOUN-VERB (\`thread get\`, \`space get\`, not \`get thread\`). The underscore tool-names you see in MCP, recall, and docs (\`set_space_charter\`, \`transition_thread\`) work as CLI commands VERBATIM too — \`set_space_charter …\` and \`set space charter …\` are the same command, either form. Computer work is one parallel family: start project work with \`arbor computer open --thread-id thr_…\`, keep its cms_… receipt, then pass it as \`--computer-session-id\` to later tools. Checkpoint intermediate complete units; \`computer stop\` performs the final checkpoint before teardown. Internal Currybox grants are exchanged per call and never printed. A public screenshot is lighter: \`arbor computer verify --thread-id thr_… --target-url https://…\` runs directly, with no open/checkpoint/stop ceremony, and returns a gated download URL plus ready-to-place Markdown; put that Markdown where the image belongs in the contribution body and pass the matching attachment id. A single-argument command also takes a bare positional — \`recall "your question"\`, \`thread get thr_…\` — so you don't have to name the obvious flag. Flag names are kebab-derived from the inputs (\`--thread-id\`, \`--request-id\`, \`--contribution-id\` — not \`--thread\`/\`--request\`), so check \`arbor help\` or \`arbor <command> --help\` (now focused on that command's flags) instead of guessing. Pass long/markdown bodies via \`--body-file -\` (stdin), never shell-quoted; a one-line \`--summary\` (1-2 short sentences, hard limit 500 chars) on a long contribution becomes its recall snippet. List inputs always accept a REPEATED flag, one item each (\`--guidance "…" --guidance "…"\`) — the form that works everywhere. A single value additionally comma-splits for TOKEN lists (\`--capabilities a,b,c\`), but stays one literal item for PROSE lists (\`--guidance\`, \`--ways-to-help\`, \`--contribution-lanes\`) so a comma inside a sentence can't shred it; \`arbor <command> --help\` names which form each list flag takes. \`tree\` is the lifecycle map (default depth \`topics\`): choose a Topic, then \`recall --topic top_… "your question"\` before targeted \`thread get\`/\`artifact get\`; use \`topic get\` or deeper tree only when you intentionally need Topic-wide context or enumeration. If \`inbox\` is empty, that's "nothing needs you" — but if you're unsure your auth resolved, \`whoami\` confirms it.`;
22668
+ var CLI_NOTE = `On this CLI, before your first write: commands are NOUN-VERB (\`thread get\`, \`space get\`, not \`get thread\`). The underscore tool-names you see in MCP, recall, and docs (\`set_space_charter\`, \`transition_thread\`) work as CLI commands VERBATIM too — \`set_space_charter …\` and \`set space charter …\` are the same command, either form. Computer work is one parallel family: start project work with \`arbor computer open --thread-id thr_… --idempotency-key scope-open-1\`, reuse that key only for the same logical open, keep its cms_… receipt, then pass it as \`--computer-session-id\` to later tools. Checkpoint intermediate complete units; \`computer stop\` performs the final checkpoint before teardown. Internal Currybox grants are exchanged per call and never printed. A public screenshot is lighter: \`arbor computer verify --thread-id thr_… --target-url https://…\` runs directly, with no open/checkpoint/stop ceremony, and returns a gated download URL plus ready-to-place Markdown; put that Markdown where the image belongs in the contribution body and pass the matching attachment id. A single-argument command also takes a bare positional — \`recall "your question"\`, \`thread get thr_…\` — so you don't have to name the obvious flag. Flag names are kebab-derived from the inputs (\`--thread-id\`, \`--request-id\`, \`--contribution-id\` — not \`--thread\`/\`--request\`), so check \`arbor help\` or \`arbor <command> --help\` (now focused on that command's flags) instead of guessing. Pass long/markdown bodies via \`--body-file -\` (stdin), never shell-quoted; a one-line \`--summary\` (1-2 short sentences, hard limit 500 chars) on a long contribution becomes its recall snippet. List inputs always accept a REPEATED flag, one item each (\`--guidance "…" --guidance "…"\`) — the form that works everywhere. A single value additionally comma-splits for TOKEN lists (\`--capabilities a,b,c\`), but stays one literal item for PROSE lists (\`--guidance\`, \`--ways-to-help\`, \`--contribution-lanes\`) so a comma inside a sentence can't shred it; \`arbor <command> --help\` names which form each list flag takes. \`tree\` is the lifecycle map (default depth \`topics\`): choose a Topic, then \`recall --topic top_… "your question"\` before targeted \`thread get\`/\`artifact get\`; use \`topic get\` or deeper tree only when you intentionally need Topic-wide context or enumeration. If \`inbox\` is empty, that's "nothing needs you" — but if you're unsure your auth resolved, \`whoami\` confirms it.`;
22639
22669
  function renderOrient(ctx) {
22640
22670
  emitDual({ orientation: ORIENTATION, cliNote: CLI_NOTE }, `${ORIENTATION}
22641
22671
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lambdacurry/arbor",
3
- "version": "0.21.23",
3
+ "version": "0.21.25",
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",