@lambdacurry/arbor 0.21.9 → 0.21.11

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 +140 -30
  2. package/package.json +1 -1
package/dist/arbor.js CHANGED
@@ -17207,6 +17207,17 @@ function date4(params) {
17207
17207
 
17208
17208
  // ../../node_modules/.pnpm/zod@4.4.3/node_modules/zod/v4/classic/external.js
17209
17209
  config(en_default());
17210
+ // ../actions/src/external-read-defaults.ts
17211
+ var EXTERNAL_DEFAULT_PAGE_SIZE = 50;
17212
+ var EXTERNAL_DEFAULT_CONTRIB_LIMIT = 50;
17213
+ var EXTERNAL_DEFAULT_THREAD_VIEW = "compact";
17214
+ var EXTERNAL_READ_DEFAULTS_BY_OPERATION = {};
17215
+ function recordExternalReadDefaults(operation, defaults) {
17216
+ if (!operation || !defaults)
17217
+ return;
17218
+ EXTERNAL_READ_DEFAULTS_BY_OPERATION[operation] = defaults;
17219
+ }
17220
+
17210
17221
  // ../actions/src/output-schemas.ts
17211
17222
  var id = exports_external.string();
17212
17223
  var timestamp = exports_external.string();
@@ -18365,7 +18376,9 @@ var MCP_OUTPUT_SCHEMAS = {
18365
18376
  exitCode: exports_external.number().optional(),
18366
18377
  processId: exports_external.string().optional(),
18367
18378
  status: exports_external.string().optional(),
18368
- cwd: exports_external.string().optional()
18379
+ cwd: exports_external.string().optional(),
18380
+ execution: exports_external.enum(["completed", "running"]).optional(),
18381
+ nextAction: exports_external.literal("computer_process_read").optional()
18369
18382
  }),
18370
18383
  computer_process_read: exports_external.looseObject({
18371
18384
  status: exports_external.string(),
@@ -18505,7 +18518,6 @@ var MCP_OUTPUT_SCHEMAS = {
18505
18518
  function outputSchemaFor(actionName) {
18506
18519
  return MCP_OUTPUT_SCHEMAS[actionName];
18507
18520
  }
18508
-
18509
18521
  // ../actions/src/output-shaping.ts
18510
18522
  function record2(value) {
18511
18523
  return value !== null && typeof value === "object" && !Array.isArray(value) ? value : null;
@@ -18682,6 +18694,8 @@ var OUTPUT_SHAPERS = {
18682
18694
  ["exitCode", result.exitCode],
18683
18695
  ["processId", result.processId],
18684
18696
  ["status", result.status],
18697
+ ["execution", result.execution],
18698
+ ["nextAction", result.nextAction],
18685
18699
  ["cwd", input.cwd === undefined ? result.cwd : undefined]
18686
18700
  ]),
18687
18701
  computer_process_read: (result) => defined([
@@ -18906,7 +18920,9 @@ Arbor's MCP is the collaboration and durable-knowledge surface. Configure, autho
18906
18920
  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.
18907
18921
 
18908
18922
  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.`;
18909
- var COMPUTER_ORIENTATION = `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.
18923
+ var COMPUTER_ORIENTATION = `Long Computer work returns a handle do not hold one tool call for a full test or build. \`computer_exec\` runs a command; if the result is still running, take \`processId\` and poll \`computer_process_read\`. Do not chain \`pnpm test && pnpm build\` in one exec; split steps or pass \`background:true\`. The CLI waits on the same handle unless \`--no-wait\`.
18924
+
18925
+ 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.
18910
18926
 
18911
18927
  ${ARBOR_FEEDBACK_GUIDANCE}
18912
18928
 
@@ -18929,7 +18945,15 @@ function isProseList(schema) {
18929
18945
  return PROSE_LISTS.has(schema);
18930
18946
  }
18931
18947
  function forward(operation) {
18932
- return (ex, input) => ex.call(operation, input);
18948
+ const run = (ex, input) => ex.call(operation, input);
18949
+ run.operation = operation;
18950
+ return run;
18951
+ }
18952
+ function forwardedOperation(run) {
18953
+ if (!("operation" in run))
18954
+ return;
18955
+ const value = run.operation;
18956
+ return typeof value === "string" ? value : undefined;
18933
18957
  }
18934
18958
  function dispatch(map2, reshape) {
18935
18959
  return (ex, input) => {
@@ -18985,8 +19009,9 @@ function runAdminFeedback(ex, input) {
18985
19009
  }
18986
19010
  var NOTIFICATION_TRIAGE_DESCRIPTION = "READ ONLY: List your awareness feed newest first with unseen count and derived triage class: `settled-context` = settled Thread context; `owed-by-me` = an open request pointing to `inbox`; `judgment-available` = live engagement inviting judgment; `closed-loop` = your request completed; `room-motion` = ambient activity. Notifications are FYI, not a second obligation surface: `inbox` alone contains work owed by you; this cannot mark notifications read or modify inbox obligations.";
18987
19011
  var PAGINATION_INPUT = {
18988
- limit: exports_external.number().int().min(1).optional().describe("max items to return enables pagination"),
18989
- cursor: exports_external.string().optional().describe("opaque cursor from a prior response's nextCursor (use with --limit)")
19012
+ limit: exports_external.number().int().min(1).optional().describe(`max items to return. MCP/CLI/API default to ${EXTERNAL_DEFAULT_PAGE_SIZE} and return nextCursor when more remain; pass all=true/--all for the complete list. In-process web omit is still a compatibility full read`),
19013
+ cursor: exports_external.string().optional().describe("opaque cursor from a prior response's nextCursor (use with --limit)"),
19014
+ all: exports_external.boolean().optional().describe("explicit complete retrieval — skip the bounded default page. Prefer limit+cursor unless you need the whole collection")
18990
19015
  };
18991
19016
  var LIVE_PREVIEW_CONFIG_INPUT = {
18992
19017
  livePreview: exports_external.object({
@@ -19612,14 +19637,14 @@ var ACTION_DEFINITIONS = [
19612
19637
  {
19613
19638
  name: "computer_exec",
19614
19639
  title: "Run a command",
19615
- description: "Run one bounded shell operation in a parent Computer or Run with ambient GitHub authorization for connected humans. For long work prefer background:true + computer_process_read; explicit timeout terminates execution, while after a missed isolated response use computer_run_receipt.operations[].operationId with computer_process_read instead of repeating the mutation.",
19640
+ description: "Run one bounded shell operation in a parent Computer or Run with ambient GitHub authorization for connected humans. A long command returns processId if still running — poll computer_process_read (background:true skips the wait; timeout kills the process); after a missed isolated response use computer_run_receipt.operations[].operationId with computer_process_read instead of repeating the mutation.",
19616
19641
  inputSchema: {
19617
19642
  computerSessionId: exports_external.string().optional().describe("the computerSessionId returned by computer_open, cms_…; required unless runId is supplied"),
19618
19643
  command: exports_external.string().min(1).describe("the shell command to run"),
19619
19644
  runId: exports_external.string().optional().describe("route this command according to this Run execution placement, run_… (from computer_run_start)"),
19620
19645
  cwd: exports_external.string().optional().describe("working directory under /workspace, or under the run; omit to use defaultCwd"),
19621
- timeout: exports_external.number().int().min(1000).max(1800000).optional().describe("command deadline in milliseconds, from 1,000 through 1,800,000"),
19622
- background: exports_external.boolean().optional().describe("start a container process and return its pid"),
19646
+ timeout: exports_external.number().int().min(1000).max(1800000).optional().describe("process kill deadline in milliseconds, from 1,000 through 1,800,000 — not the RPC hold"),
19647
+ background: exports_external.boolean().optional().describe("start the process and return immediately without the short RPC wait"),
19623
19648
  activityLabel: exports_external.string().min(1).max(COMPUTER_RUN_ACTIVITY_LABEL_MAX).optional().describe("short human-readable label for run-scoped background work; Arbor records this label instead of the raw command or output"),
19624
19649
  activityKind: exports_external.literal("verification").optional().describe("mark a labeled run-scoped background command as a verification check; its provider-observed terminal result is recorded as verification.finished"),
19625
19650
  strict: exports_external.boolean().optional().describe("fail on the first unhandled command or pipeline failure; defaults to true, set false only for intentional best-effort execution")
@@ -20210,14 +20235,19 @@ var ACTION_DEFINITIONS = [
20210
20235
  {
20211
20236
  name: "thread_get",
20212
20237
  title: "Read a thread",
20213
- description: "READ ONLY: Read one Thread; full view returns transcript, requests, and artifacts, while compact returns bounded decision-ready context with distillate/recent items and supports cursored unchanged checks. Read it before contributing so you can answer open requests correctly; use contribution pagination only for full history.",
20238
+ description: "READ ONLY: Read one Thread as compact current context (distillate, requests, artifacts, recent tail, cursored unchanged checks) not the durable transcript; pass view=full to page history (contribLimit default 50) or all=true for the complete transcript. Read it before contributing so you can answer open requests correctly.",
20214
20239
  inputSchema: {
20215
20240
  threadId: exports_external.string().describe("the thread id, e.g. thr_…"),
20216
- view: exports_external.enum(["full", "compact"]).optional().describe("full (default) = complete transcript; compact = bounded decision-ready context packet for recurring checks"),
20241
+ view: exports_external.enum(["full", "compact"]).optional().describe("MCP/CLI/API default compact = bounded decision-ready context; full = transcript page"),
20217
20242
  cursor: exports_external.string().optional().describe("compact reads only: cursor from the previous compact response; if context is unchanged the response collapses to a tiny unchanged envelope"),
20218
20243
  recentLimit: exports_external.number().int().min(1).max(20).optional().describe("compact reads only: latest contributions to include (default 6, max 20)"),
20219
- contribLimit: exports_external.number().int().positive().optional().describe("full reads only: max contributions to return (AD-151 keyset paging) omit to load the whole thread"),
20220
- contribCursor: exports_external.string().optional().describe("full reads only: continuation cursor from a previous page's contributionPage.nextCursor")
20244
+ contribLimit: exports_external.number().int().positive().optional().describe(`full reads only: max contributions to return (AD-151 keyset paging). MCP/CLI/API default to ${EXTERNAL_DEFAULT_CONTRIB_LIMIT}; pass all=true for the whole thread`),
20245
+ contribCursor: exports_external.string().optional().describe("full reads only: continuation cursor from a previous page's contributionPage.nextCursor"),
20246
+ all: exports_external.boolean().optional().describe("explicit complete transcript on view=full — skip the bounded contribLimit. Prefer contribLimit+contribCursor unless you need every contribution")
20247
+ },
20248
+ externalReadDefaults: {
20249
+ view: EXTERNAL_DEFAULT_THREAD_VIEW,
20250
+ contribLimit: EXTERNAL_DEFAULT_CONTRIB_LIMIT
20221
20251
  },
20222
20252
  surfaces: ["mcp", "cli"],
20223
20253
  toolset: "loop",
@@ -21327,15 +21357,17 @@ var ACTIONS = ACTION_DEFINITIONS.map((action) => {
21327
21357
  if (action.requiresCapability && !action.surfaces.includes("mcp")) {
21328
21358
  throw new Error(`${action.name}: requiresCapability is an MCP catalog gate`);
21329
21359
  }
21360
+ const externalReadDefaults = action.inputSchema.limit === PAGINATION_INPUT.limit ? { ...action.externalReadDefaults, limit: EXTERNAL_DEFAULT_PAGE_SIZE } : action.externalReadDefaults;
21361
+ recordExternalReadDefaults(forwardedOperation(action.run), externalReadDefaults);
21330
21362
  if (!action.surfaces.some((surface) => surface === "mcp" || surface === "computer-mcp")) {
21331
- return action;
21363
+ return externalReadDefaults ? { ...action, externalReadDefaults } : action;
21332
21364
  }
21333
21365
  const outputSchema = outputSchemaFor(action.name);
21334
21366
  if (!outputSchema)
21335
21367
  throw new Error(`${action.name}: MCP action is missing an output schema`);
21336
21368
  const annotations = mcpAnnotationsFor(action.name);
21337
21369
  const description = annotations.readOnlyHint && !action.description.startsWith("READ ONLY") ? `READ ONLY: ${action.description}` : action.description;
21338
- return { ...action, description, outputSchema, annotations };
21370
+ return { ...action, externalReadDefaults, description, outputSchema, annotations };
21339
21371
  });
21340
21372
 
21341
21373
  // src/config.ts
@@ -21633,7 +21665,8 @@ var GLOBAL_BOOLEAN_FLAGS = new Set([
21633
21665
  "json",
21634
21666
  "quiet",
21635
21667
  "no-quiet",
21636
- "help"
21668
+ "help",
21669
+ "no-wait"
21637
21670
  ]);
21638
21671
  var RESERVED_FLAGS = new Set([
21639
21672
  ...GLOBAL_BOOLEAN_FLAGS,
@@ -21725,6 +21758,66 @@ function emitDual(data, human, action, ctx) {
21725
21758
  process.stdout.write(human);
21726
21759
  }
21727
21760
 
21761
+ // src/computer-exec-wait.ts
21762
+ var DEFAULT_PROCESS_DEADLINE_MS = 1800000;
21763
+ var POLL_MS = 400;
21764
+ var processRead = ACTIONS.find((action) => action.name === "computer_process_read");
21765
+ function record3(value) {
21766
+ return value !== null && typeof value === "object" && !Array.isArray(value) ? value : null;
21767
+ }
21768
+ function isRunning(value) {
21769
+ const status = typeof value.status === "string" ? value.status.toLowerCase() : "";
21770
+ if (value.done === false)
21771
+ return true;
21772
+ if (status === "running" || status === "started" || status === "starting")
21773
+ return true;
21774
+ if (value.done === true)
21775
+ return false;
21776
+ if (status === "completed" || status === "exited" || status === "failed" || status === "cancelled" || status === "terminated" || status === "killed" || status === "unknown") {
21777
+ return false;
21778
+ }
21779
+ if (typeof value.exitCode === "number")
21780
+ return false;
21781
+ return typeof value.processId === "string";
21782
+ }
21783
+ function sleep(ms) {
21784
+ return new Promise((resolve) => setTimeout(resolve, ms));
21785
+ }
21786
+ function isComputerExecHandle(value) {
21787
+ const rec = record3(value);
21788
+ return Boolean(rec && typeof rec.processId === "string" && isRunning(rec));
21789
+ }
21790
+ async function waitForComputerExec(executor, input, value, ctx) {
21791
+ if (!processRead || !isComputerExecHandle(value))
21792
+ return value;
21793
+ let latest = value;
21794
+ let cursor = typeof latest.nextCursor === "string" && latest.nextCursor ? latest.nextCursor : undefined;
21795
+ const deadline = Date.now() + (typeof input.timeout === "number" && Number.isFinite(input.timeout) ? input.timeout : DEFAULT_PROCESS_DEADLINE_MS);
21796
+ advise(`Process ${String(latest.processId)} is still running; polling computer_process_read.
21797
+ `, ctx);
21798
+ while (isRunning(latest) && Date.now() < deadline) {
21799
+ await sleep(POLL_MS);
21800
+ const read = record3(await processRead.run(executor, {
21801
+ processId: latest.processId,
21802
+ ...cursor ? { cursor } : {},
21803
+ ...typeof input.runId === "string" ? { runId: input.runId } : {},
21804
+ ...typeof input.computerSessionId === "string" ? { computerSessionId: input.computerSessionId } : {}
21805
+ }));
21806
+ if (!read) {
21807
+ throw new Error("computer_process_read returned an empty result");
21808
+ }
21809
+ latest = read;
21810
+ if (typeof read.nextCursor === "string" && read.nextCursor)
21811
+ cursor = read.nextCursor;
21812
+ const stdout = typeof latest.stdout === "string" ? latest.stdout : "";
21813
+ if (stdout)
21814
+ advise(stdout, ctx);
21815
+ if (latest.recovery)
21816
+ break;
21817
+ }
21818
+ return latest;
21819
+ }
21820
+
21728
21821
  // src/schema.ts
21729
21822
  import { readFileSync as readFileSync2 } from "node:fs";
21730
21823
  function kebab(field) {
@@ -22067,11 +22160,14 @@ Bare arguments fill ${explicitPositionals.map((spec) => `--${spec.flag}`).join("
22067
22160
 
22068
22161
  Alias: \`arbor ${alias2}\` (the tool-name form) resolves to this same command.` : "";
22069
22162
  const heading = `${commandWords(action)}${alias2 ? ` (alias: ${alias2})` : ""}`;
22163
+ const waitLines = action.name === "computer_exec" ? [
22164
+ " --no-wait [<boolean>] [optional] — return a running processId immediately instead of polling computer_process_read"
22165
+ ] : [];
22070
22166
  return `${heading} — ${action.description}
22071
22167
 
22072
- ${usage}
22168
+ ${usage}${waitLines.length > 0 ? " [--no-wait]" : ""}
22073
22169
 
22074
- ${lines.join(`
22170
+ ${[...lines, ...waitLines].join(`
22075
22171
  `)}${primaryNote}${explicitNote}${aliasNote}
22076
22172
  `;
22077
22173
  }
@@ -22080,13 +22176,24 @@ function commandCatalog() {
22080
22176
  command: commandWords(action),
22081
22177
  name: action.name,
22082
22178
  description: action.description,
22083
- flags: flagsForSchema(action.inputSchema).map((f) => ({
22084
- flag: f.flag,
22085
- kind: f.kind,
22086
- required: f.required,
22087
- acceptsFile: f.kind === "string",
22088
- description: f.description
22089
- }))
22179
+ flags: [
22180
+ ...flagsForSchema(action.inputSchema).map((f) => ({
22181
+ flag: f.flag,
22182
+ kind: f.kind,
22183
+ required: f.required,
22184
+ acceptsFile: f.kind === "string",
22185
+ description: f.description
22186
+ })),
22187
+ ...action.name === "computer_exec" ? [
22188
+ {
22189
+ flag: "no-wait",
22190
+ kind: "boolean",
22191
+ required: false,
22192
+ acceptsFile: false,
22193
+ description: "return a running processId immediately instead of polling computer_process_read"
22194
+ }
22195
+ ] : []
22196
+ ]
22090
22197
  }));
22091
22198
  }
22092
22199
  function applyPositionals(action, extras, flags) {
@@ -22165,13 +22272,16 @@ async function runObjectVerb(positionals, flags, ctx) {
22165
22272
  advise(`Agent registered. Save its apiKey now — it is shown only once:
22166
22273
  `, ctx);
22167
22274
  }
22168
- const value = await action.run(httpExecutor, input);
22275
+ let value = await action.run(httpExecutor, input);
22276
+ if (action.name === "computer_exec" && !truthyFlag(flags["no-wait"])) {
22277
+ value = await waitForComputerExec(httpExecutor, input, value, ctx);
22278
+ }
22169
22279
  emitData(shapeActionOutput(action.name, value, input), action.name, ctx);
22170
22280
  }
22171
22281
 
22172
22282
  // src/connect.ts
22173
22283
  var DEFAULT_POLL_INTERVAL_MS = 2000;
22174
- var sleep = (ms) => new Promise((r) => setTimeout(r, ms));
22284
+ var sleep2 = (ms) => new Promise((r) => setTimeout(r, ms));
22175
22285
  function bareConnectUrl(apiUrl, returnedUrl) {
22176
22286
  const fallback = `${apiUrl.replace(/\/$/, "")}/connect`;
22177
22287
  if (!returnedUrl)
@@ -22215,7 +22325,7 @@ async function connect(opts) {
22215
22325
  if (Date.now() > deadline) {
22216
22326
  throw new Error("pairing timed out — ask for a new pairing key and run `arbor connect` again");
22217
22327
  }
22218
- await sleep(pollIntervalMs);
22328
+ await sleep2(pollIntervalMs);
22219
22329
  const tokRes = await fetch(`${apiUrl}/api/agent/connect/token`, {
22220
22330
  method: "POST",
22221
22331
  headers,
@@ -22241,7 +22351,7 @@ async function connect(opts) {
22241
22351
  // src/login.ts
22242
22352
  var CLIENT_ID = "arbor-cli";
22243
22353
  var GRANT_TYPE = "urn:ietf:params:oauth:grant-type:device_code";
22244
- var sleep2 = (ms) => new Promise((r) => setTimeout(r, ms));
22354
+ var sleep3 = (ms) => new Promise((r) => setTimeout(r, ms));
22245
22355
  async function login(opts) {
22246
22356
  const apiUrl = opts.url ?? loadConfig().apiUrl;
22247
22357
  const codeRes = await fetch(`${apiUrl}/api/auth/device/code`, {
@@ -22266,7 +22376,7 @@ async function login(opts) {
22266
22376
  for (;; ) {
22267
22377
  if (Date.now() > deadline)
22268
22378
  throw new Error("login timed out — run `arbor login` again");
22269
- await sleep2(interval);
22379
+ await sleep3(interval);
22270
22380
  const tokRes = await fetch(`${apiUrl}/api/auth/device/token`, {
22271
22381
  method: "POST",
22272
22382
  headers: { "content-type": "application/json" },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lambdacurry/arbor",
3
- "version": "0.21.9",
3
+ "version": "0.21.11",
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",