@lambdacurry/arbor 0.14.4 → 0.14.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 +158 -8
  2. package/package.json +1 -1
package/dist/arbor.js CHANGED
@@ -38,6 +38,7 @@ var ERROR_REASONS = new Set([
38
38
  "authorization.denied",
39
39
  "resource.not_found",
40
40
  "conflict.state",
41
+ "conflict.computer_recipe",
41
42
  "limit.exceeded",
42
43
  "provider.timeout",
43
44
  "provider.failure",
@@ -1581,12 +1582,55 @@ var githubWebhookDeliveries = sqliteTable("github_webhook_deliveries", {
1581
1582
  var threadComputers = sqliteTable("thread_computers", {
1582
1583
  id: text("id").primaryKey(),
1583
1584
  threadId: text("thread_id").notNull().references(() => threads.id),
1585
+ generation: integer("generation").notNull().default(1),
1584
1586
  provider: text("provider").$type().notNull(),
1587
+ backend: text("backend").$type().notNull().default("worker-shell"),
1588
+ containerProfile: text("container_profile"),
1589
+ repositories: text("repositories", { mode: "json" }).$type().notNull().default([]),
1590
+ setupScriptConfigured: integer("setup_script_configured", { mode: "boolean" }).notNull().default(false),
1591
+ configKey: text("config_key").notNull().default("legacy"),
1592
+ configRevision: text("config_revision").notNull().default("legacy"),
1593
+ predecessorComputerId: text("predecessor_computer_id"),
1594
+ successorComputerId: text("successor_computer_id"),
1595
+ restoredFromSnapshotId: text("restored_from_snapshot_id"),
1585
1596
  currentSnapshotId: text("current_snapshot_id"),
1586
1597
  createdByProfileId: text("created_by_profile_id").notNull().references(() => profiles.id),
1587
1598
  createdExecutionContextId: text("created_execution_context_id").notNull().references(() => executionContexts.id),
1588
1599
  createdAt: ts("created_at").notNull()
1589
- }, (t) => ({ threadIdx: uniqueIndex("thread_computers_thread_idx").on(t.threadId) }));
1600
+ }, (t) => ({
1601
+ threadIdx: index("thread_computers_thread_idx").on(t.threadId, t.generation),
1602
+ generationIdx: uniqueIndex("thread_computers_generation_idx").on(t.threadId, t.generation)
1603
+ }));
1604
+ var threadComputerHeads = sqliteTable("thread_computer_heads", {
1605
+ threadId: text("thread_id").primaryKey().references(() => threads.id),
1606
+ activeComputerId: text("active_computer_id").notNull().unique().references(() => threadComputers.id),
1607
+ updatedByProfileId: text("updated_by_profile_id").notNull().references(() => profiles.id),
1608
+ updatedExecutionContextId: text("updated_execution_context_id").notNull().references(() => executionContexts.id),
1609
+ updatedAt: ts("updated_at").notNull()
1610
+ });
1611
+ var computerReprovisions = sqliteTable("computer_reprovisions", {
1612
+ id: text("id").primaryKey(),
1613
+ threadId: text("thread_id").notNull().references(() => threads.id),
1614
+ actorProfileId: text("actor_profile_id").notNull().references(() => profiles.id),
1615
+ executionContextId: text("execution_context_id").notNull().references(() => executionContexts.id),
1616
+ idempotencyKey: text("idempotency_key").notNull(),
1617
+ targetConfigKey: text("target_config_key").notNull(),
1618
+ reason: text("reason").notNull(),
1619
+ status: text("status").$type().notNull().default("pending"),
1620
+ oldComputerId: text("old_computer_id").notNull().references(() => threadComputers.id),
1621
+ newComputerId: text("new_computer_id").references(() => threadComputers.id),
1622
+ sourceSessionId: text("source_session_id"),
1623
+ targetSessionId: text("target_session_id"),
1624
+ checkpointOutcome: text("checkpoint_outcome").$type().notNull().default("not_needed"),
1625
+ restorationOutcome: text("restoration_outcome").$type().notNull().default("not_needed"),
1626
+ restoredSnapshotId: text("restored_snapshot_id"),
1627
+ failureCode: text("failure_code"),
1628
+ createdAt: ts("created_at").notNull(),
1629
+ completedAt: ts("completed_at")
1630
+ }, (t) => ({
1631
+ idempotencyIdx: uniqueIndex("computer_reprovisions_idempotency_idx").on(t.threadId, t.actorProfileId, t.idempotencyKey),
1632
+ threadIdx: index("computer_reprovisions_thread_idx").on(t.threadId, t.createdAt)
1633
+ }));
1590
1634
  var computerSessions = sqliteTable("computer_sessions", {
1591
1635
  id: text("id").primaryKey(),
1592
1636
  computerId: text("computer_id").notNull().references(() => threadComputers.id),
@@ -1864,6 +1908,7 @@ var appBundles = sqliteTable("app_bundles", {
1864
1908
  }).$type(),
1865
1909
  runtimeEntrypoint: text("runtime_entrypoint").$type().notNull(),
1866
1910
  capabilityRequirements: text("capability_requirements", { mode: "json" }).$type().notNull().default([]),
1911
+ runtimeConfigRequirements: text("runtime_config_requirements", { mode: "json" }).$type().notNull().default([]),
1867
1912
  stateSchemaVersion: integer("state_schema_version").notNull().default(0),
1868
1913
  stateCompatibleFrom: integer("state_compatible_from").notNull().default(0),
1869
1914
  stateCompatibleThrough: integer("state_compatible_through").notNull().default(0),
@@ -1930,6 +1975,22 @@ var secretBindings = sqliteTable("secret_bindings", {
1930
1975
  appNameUq: uniqueIndex("secret_bindings_app_name_uq").on(t.appId, t.bindingName),
1931
1976
  secretIdx: index("secret_bindings_secret_idx").on(t.secretId, t.status)
1932
1977
  }));
1978
+ var appRuntimeVars = sqliteTable("app_runtime_vars", {
1979
+ id: text("id").primaryKey(),
1980
+ appId: text("app_id").notNull().references(() => apps.id),
1981
+ name: text("name").notNull(),
1982
+ value: text("value").notNull(),
1983
+ revision: integer("revision").notNull().default(1),
1984
+ createdByProfileId: text("created_by_profile_id").notNull().references(() => profiles.id),
1985
+ createdExecutionContextId: text("created_execution_context_id").notNull().references(() => executionContexts.id),
1986
+ createdAt: ts("created_at").notNull(),
1987
+ updatedByProfileId: text("updated_by_profile_id").notNull().references(() => profiles.id),
1988
+ updatedExecutionContextId: text("updated_execution_context_id").notNull().references(() => executionContexts.id),
1989
+ updatedAt: ts("updated_at").notNull()
1990
+ }, (t) => ({
1991
+ appNameUq: uniqueIndex("app_runtime_vars_app_name_uq").on(t.appId, t.name),
1992
+ appIdx: index("app_runtime_vars_app_idx").on(t.appId)
1993
+ }));
1933
1994
  var appDeployments = sqliteTable("app_deployments", {
1934
1995
  id: text("id").primaryKey(),
1935
1996
  appId: text("app_id").notNull().references(() => apps.id),
@@ -16576,6 +16637,15 @@ var computerConfig = exports_external.looseObject({
16576
16637
  setupScript: nullableString,
16577
16638
  repositories: exports_external.array(exports_external.string())
16578
16639
  });
16640
+ var redactedComputerRecipe = exports_external.looseObject({
16641
+ provider: exports_external.string(),
16642
+ backend: exports_external.enum(["worker-shell", "container"]),
16643
+ containerProfile: nullableString,
16644
+ repositories: exports_external.array(exports_external.string()),
16645
+ setupScriptConfigured: exports_external.boolean(),
16646
+ configRevision: exports_external.string(),
16647
+ layers: exports_external.array(exports_external.looseObject({ scope: exports_external.string() }))
16648
+ });
16579
16649
  var computerConfigLayer = exports_external.looseObject({
16580
16650
  id,
16581
16651
  scope: exports_external.string(),
@@ -16903,11 +16973,20 @@ var MCP_OUTPUT_SCHEMAS = {
16903
16973
  }),
16904
16974
  get_computer: exports_external.looseObject({
16905
16975
  threadId: id,
16906
- config: computerConfig,
16907
- topicBase: snapshot.nullable(),
16976
+ config: redactedComputerRecipe,
16977
+ compatibility: exports_external.looseObject({
16978
+ state: exports_external.enum(["ready", "stale_config", "backend_mismatch", "replacement_required"]),
16979
+ reason: exports_external.string(),
16980
+ effectiveRecipe: redactedComputerRecipe,
16981
+ materializedRecipe: redactedComputerRecipe.nullable()
16982
+ }),
16983
+ topicBase: jsonObject.nullable(),
16908
16984
  computer: jsonObject.nullable(),
16985
+ activeGenerationId: id.nullable(),
16986
+ generations: exports_external.array(jsonObject),
16909
16987
  sessions: exports_external.array(jsonObject),
16910
- lineage: exports_external.array(snapshot)
16988
+ lineage: exports_external.array(jsonObject),
16989
+ reprovisions: exports_external.array(jsonObject)
16911
16990
  }),
16912
16991
  publish_topic_computer: exports_external.looseObject({
16913
16992
  published: exports_external.boolean(),
@@ -17141,6 +17220,7 @@ var MCP_OUTPUT_SCHEMAS = {
17141
17220
  app_fetch: appInvocationResponse,
17142
17221
  app_request: appInvocationResponse,
17143
17222
  app_create: app2,
17223
+ app_config_apply: exports_external.object({ appId: id, dryRun: exports_external.boolean(), changes: exports_external.array(jsonObject) }),
17144
17224
  app_deploy: exports_external.looseObject({
17145
17225
  deployment,
17146
17226
  activated: exports_external.boolean(),
@@ -17181,6 +17261,19 @@ var MCP_OUTPUT_SCHEMAS = {
17181
17261
  materialized: checkpoint.nullable(),
17182
17262
  runtime: jsonObject
17183
17263
  }),
17264
+ computer_reprovision: exports_external.looseObject({
17265
+ reprovisionId: id,
17266
+ threadId: id,
17267
+ status: exports_external.string(),
17268
+ oldComputerId: id,
17269
+ newComputerId: id.nullable(),
17270
+ computerSessionId: id.nullable(),
17271
+ checkpointOutcome: exports_external.string(),
17272
+ restorationOutcome: exports_external.string(),
17273
+ restoredSnapshotId: id.nullable(),
17274
+ failureCode: exports_external.string().nullable(),
17275
+ reason: exports_external.string()
17276
+ }),
17184
17277
  computer_exec: exports_external.looseObject({
17185
17278
  stdout: exports_external.string().optional(),
17186
17279
  stderr: exports_external.string().optional(),
@@ -17412,6 +17505,7 @@ var MCP_TOOL_ANNOTATIONS = {
17412
17505
  app_fetch: readOnly,
17413
17506
  app_request: destructive,
17414
17507
  app_create: additive,
17508
+ app_config_apply: idempotent,
17415
17509
  app_deploy: idempotent,
17416
17510
  app_activate: additive,
17417
17511
  app_rollback: additive,
@@ -17421,6 +17515,7 @@ var MCP_TOOL_ANNOTATIONS = {
17421
17515
  app_archive: additive,
17422
17516
  charter: destructive,
17423
17517
  computer_open: additive,
17518
+ computer_reprovision: destructiveIdempotent,
17424
17519
  computer_exec: destructiveOpenWorld,
17425
17520
  computer_status: readOnly,
17426
17521
  computer_verify: openWorld,
@@ -17779,6 +17874,19 @@ var ACTION_DEFINITIONS = [
17779
17874
  toolset: "loop",
17780
17875
  run: forward("computer_runtime.open")
17781
17876
  },
17877
+ {
17878
+ name: "computer_reprovision",
17879
+ title: "Replace a Thread computer",
17880
+ description: "Explicitly replace an incompatible active Computer generation from the Thread's effective recipe. The old generation remains authoritative unless the successor restores supported state and passes health. Same-provider worker-shell→container migration is supported; container→worker-shell and cross-provider migration fail explicitly. Reuse one idempotencyKey only for the same replacement request.",
17881
+ inputSchema: {
17882
+ threadId: exports_external.string().describe("the Thread whose active Computer generation is replaced, thr_…"),
17883
+ idempotencyKey: exports_external.string().min(1).max(200).describe("stable key for this ONE replacement request; reuse it unchanged for retries"),
17884
+ reason: exports_external.string().min(1).max(500).describe("concise auditable reason for replacing the active generation")
17885
+ },
17886
+ surfaces: ["computer-mcp", "computer-cli"],
17887
+ toolset: "loop",
17888
+ run: forward("computer_runtime.reprovision")
17889
+ },
17782
17890
  {
17783
17891
  name: "computer_exec",
17784
17892
  title: "Run a command",
@@ -17968,6 +18076,7 @@ var ACTION_DEFINITIONS = [
17968
18076
  stateSchemaVersion: exports_external.number().int().min(0).optional().describe("durable schema this release writes; default 0"),
17969
18077
  stateCompatibleFrom: exports_external.number().int().min(0).optional().describe("oldest durable schema this code can open"),
17970
18078
  stateCompatibleThrough: exports_external.number().int().min(0).optional().describe("newest durable schema this code can open"),
18079
+ runtimeConfigRequirements: exports_external.array(exports_external.object({ name: exports_external.string().min(1).max(64), kind: exports_external.enum(["var", "secret"]) })).optional().describe("required runtime config keys and kinds; values are never part of the AppBundle"),
17971
18080
  idempotencyKey: exports_external.string().min(1).max(200).describe("stable key for this one logical bundle publication")
17972
18081
  },
17973
18082
  surfaces: ["computer-mcp", "computer-cli"],
@@ -17988,7 +18097,7 @@ var ACTION_DEFINITIONS = [
17988
18097
  {
17989
18098
  name: "get_computer",
17990
18099
  title: "Read a Thread computer",
17991
- description: "Read a Thread's effective recipe, stable computer identity, current Topic/Thread snapshot refs, recent attachment receipts, and bounded lineage. This is durable Arbor protocol state, not live process/presence status; use Currybox for ephemeral runtime observations.",
18100
+ description: "Read a Thread's redacted effective recipe, active materialized generation, compatibility state, historical generation lineage, current Topic/Thread snapshot refs, recent attachment receipts, and replacement outcomes. Recipe metadata includes provider/backend/profile/repositories/config revision and whether setup is configured, never setup content or credentials. This is durable Arbor protocol state, not live process/presence status.",
17992
18101
  inputSchema: {
17993
18102
  threadId: exports_external.string().describe("the Thread, thr_…"),
17994
18103
  lineageLimit: exports_external.number().int().min(1).max(100).optional().describe("recent receipts/lineage rows (default 20)")
@@ -19198,6 +19307,22 @@ var ACTION_DEFINITIONS = [
19198
19307
  toolset: "apps",
19199
19308
  run: forward("app.create")
19200
19309
  },
19310
+ {
19311
+ name: "app_config_apply",
19312
+ title: "Apply App runtime config",
19313
+ description: "Declaratively apply the complete typed runtime config for one App. var stores an ordinary runtime value; secret references an existing Arbor Secret without accepting or returning secret material. Dry-run performs the same validation and returns create/update/delete/no-op actions without mutating.",
19314
+ inputSchema: {
19315
+ appId: exports_external.string().describe("target App, app_…"),
19316
+ config: exports_external.record(exports_external.string(), exports_external.discriminatedUnion("kind", [
19317
+ exports_external.object({ kind: exports_external.literal("var"), value: exports_external.string().max(16384) }),
19318
+ exports_external.object({ kind: exports_external.literal("secret"), secretId: exports_external.string() })
19319
+ ])).describe("complete desired runtime config object keyed by environment variable name"),
19320
+ dryRun: exports_external.boolean().optional().describe("validate and plan without mutating")
19321
+ },
19322
+ surfaces: ["mcp", "cli"],
19323
+ toolset: "apps",
19324
+ run: forward("app.config_apply")
19325
+ },
19201
19326
  {
19202
19327
  name: "app_deploy",
19203
19328
  title: "Prepare an App deployment",
@@ -19506,6 +19631,11 @@ function parseErrorDetails(value) {
19506
19631
  };
19507
19632
  }
19508
19633
  }
19634
+ if (typeof raw.compatibilityState === "string") {
19635
+ details.compatibilityState = raw.compatibilityState;
19636
+ }
19637
+ if (typeof raw.suggestedAction === "string")
19638
+ details.suggestedAction = raw.suggestedAction;
19509
19639
  return details;
19510
19640
  }
19511
19641
  function detailsForError(err) {
@@ -19748,6 +19878,9 @@ function kindOf(node) {
19748
19878
  return "array";
19749
19879
  case "boolean":
19750
19880
  return "boolean";
19881
+ case "object":
19882
+ case "record":
19883
+ return "json";
19751
19884
  default:
19752
19885
  return "string";
19753
19886
  }
@@ -19756,7 +19889,7 @@ function acceptedFlags(inputSchema) {
19756
19889
  const out = new Set;
19757
19890
  for (const spec of flagsForSchema(inputSchema)) {
19758
19891
  out.add(spec.flag);
19759
- if (spec.kind === "string")
19892
+ if (spec.kind === "string" || spec.kind === "json")
19760
19893
  out.add(`${spec.flag}-file`);
19761
19894
  }
19762
19895
  return out;
@@ -19792,7 +19925,7 @@ function buildInput(inputSchema, flags, command) {
19792
19925
  const primary = primaryPositionalField(inputSchema);
19793
19926
  const input = {};
19794
19927
  for (const spec of flagsForSchema(inputSchema)) {
19795
- if (spec.kind === "string") {
19928
+ if (spec.kind === "string" || spec.kind === "json") {
19796
19929
  const fileSrc = lastValue(flags[`${spec.flag}-file`]);
19797
19930
  if (fileSrc !== undefined) {
19798
19931
  if (flags[spec.flag] !== undefined) {
@@ -19800,7 +19933,16 @@ function buildInput(inputSchema, flags, command) {
19800
19933
  }
19801
19934
  if (fileSrc === true)
19802
19935
  throw new UsageError(`--${spec.flag}-file expects a path (or - for stdin)`);
19803
- input[spec.field] = readTextSource(String(fileSrc));
19936
+ const sourced = readTextSource(String(fileSrc));
19937
+ if (spec.kind === "json") {
19938
+ try {
19939
+ input[spec.field] = JSON.parse(sourced);
19940
+ } catch {
19941
+ throw new UsageError(`--${spec.flag}-file must contain valid JSON`);
19942
+ }
19943
+ } else {
19944
+ input[spec.field] = sourced;
19945
+ }
19804
19946
  continue;
19805
19947
  }
19806
19948
  }
@@ -19864,6 +20006,14 @@ function buildInput(inputSchema, flags, command) {
19864
20006
  input[spec.field] = out;
19865
20007
  break;
19866
20008
  }
20009
+ case "json": {
20010
+ try {
20011
+ input[spec.field] = JSON.parse(String(lastValue(raw)));
20012
+ } catch {
20013
+ throw new UsageError(`--${spec.flag} must be valid JSON`);
20014
+ }
20015
+ break;
20016
+ }
19867
20017
  case "boolean": {
19868
20018
  const v = lastValue(raw);
19869
20019
  input[spec.field] = v === true ? true : v !== "false";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lambdacurry/arbor",
3
- "version": "0.14.4",
3
+ "version": "0.14.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",