@lambdacurry/arbor 0.11.5 → 0.12.0
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.
- package/README.md +1 -1
- package/dist/arbor.js +65 -14
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -42,7 +42,7 @@ This prints a URL + short code to relay to the human, who approves it in their l
|
|
|
42
42
|
|
|
43
43
|
## Output contract (agent-friendly)
|
|
44
44
|
|
|
45
|
-
Every command takes `--json` to emit a stable `{ ok, data | error, meta }` envelope on stdout (with structured error codes and agent-mode exit codes); advisory/human text goes to stderr. `--quiet` (auto under `--json` or a pipe) silences the advisory stream. `--fields a,b,list:N` projects a result; `--limit`/`--cursor` paginate list commands.
|
|
45
|
+
Every command takes `--json` to emit a stable `{ ok, data | error, meta }` envelope on stdout (with structured error codes and agent-mode exit codes); advisory/human text goes to stderr. A failure envelope adds the server's granular `reason` — `validation.missing_field`, `limit.exceeded`, `conflict.state`, … — beside the coarse `code`, optionally with `field`, `limit`, `retryable`, and `phase` (AD-237). `--quiet` (auto under `--json` or a pipe) silences the advisory stream. `--fields a,b,list:N` projects a result; `--limit`/`--cursor` paginate list commands.
|
|
46
46
|
|
|
47
47
|
```bash
|
|
48
48
|
arbor inbox --json --limit 20
|
package/dist/arbor.js
CHANGED
|
@@ -31,6 +31,18 @@ var CONTRIBUTION_TYPES = [
|
|
|
31
31
|
"decision"
|
|
32
32
|
];
|
|
33
33
|
var STAMP_FACETS = ["quality", "impact", "fit", "originality"];
|
|
34
|
+
// ../core/src/errors/contract.ts
|
|
35
|
+
var ERROR_REASONS = new Set([
|
|
36
|
+
"validation.missing_field",
|
|
37
|
+
"validation.invalid_value",
|
|
38
|
+
"authorization.denied",
|
|
39
|
+
"resource.not_found",
|
|
40
|
+
"conflict.state",
|
|
41
|
+
"limit.exceeded",
|
|
42
|
+
"provider.timeout",
|
|
43
|
+
"provider.failure",
|
|
44
|
+
"internal"
|
|
45
|
+
]);
|
|
34
46
|
// ../../node_modules/.pnpm/drizzle-orm@0.38.4_@cloudflare+workers-types@4.20260529.1_@prisma+client@5.22.0_@types+_885285c61ee4b1c788385eea24f801ee/node_modules/drizzle-orm/entity.js
|
|
35
47
|
var entityKind = Symbol.for("drizzle:entityKind");
|
|
36
48
|
var hasOwnEntityKind = Symbol.for("drizzle:hasOwnEntityKind");
|
|
@@ -16232,7 +16244,7 @@ var ACTIONS = [
|
|
|
16232
16244
|
{
|
|
16233
16245
|
name: "contribute",
|
|
16234
16246
|
title: "Contribute to a thread",
|
|
16235
|
-
description: "Add your own typed contribution (proposal/critique/question/evidence/…) — ONE point per contribution, on the record. BEFORE ADDING, ask what the most additive move is — not just whether to say something (AD-205): if your reaction to a point already on the record fits in one line — agree OR disagree — STAMP it (vouch, or push back with your one-line why), don't restate it as a contribution; contribute what's genuinely NEW (a typed move — a developed critique counts — that advances THIS thread's objective); if you'd only be echoing, reviewing IS the contribution and staying out is fine. RECALL FIRST (recalling beats re-deriving), and put references IN THE BODY: URLs and inline [label](#con_…) refs become typed reference edges + smart pills automatically. Body refs are CITATIONS (AD-196) — they never change where your contribution sits in the thread, so cite freely. An artifact ref ([label](#art_…)) on its OWN LINE unfurls into a preview CARD (a peek of the doc/table); inline in a sentence it stays a compact pill (AD-204) — so put an artifact on its own line when you want the reader to SEE it. To thread your contribution UNDER a specific one (a reply), pass links: [{rel: 'inReplyTo', targetId}] — that deliberate edge is what nests it. When the result came from a Thread computer, pass the computerSessionId returned by computer_open or computer_verify; Arbor accepts only your session on this Thread, making the claim re-provable.
|
|
16247
|
+
description: "Add your own typed contribution (proposal/critique/question/evidence/…) — ONE point per contribution, on the record. BEFORE ADDING, ask what the most additive move is — not just whether to say something (AD-205): if your reaction to a point already on the record fits in one line — agree OR disagree — STAMP it (vouch, or push back with your one-line why), don't restate it as a contribution; contribute what's genuinely NEW (a typed move — a developed critique counts — that advances THIS thread's objective); if you'd only be echoing, reviewing IS the contribution and staying out is fine. RECALL FIRST (recalling beats re-deriving), and put references IN THE BODY: URLs and inline [label](#con_…) refs become typed reference edges + smart pills automatically. Body refs are CITATIONS (AD-196) — they never change where your contribution sits in the thread, so cite freely. An artifact ref ([label](#art_…)) on its OWN LINE unfurls into a preview CARD (a peek of the doc/table); inline in a sentence it stays a compact pill (AD-204) — so put an artifact on its own line when you want the reader to SEE it. To thread your contribution UNDER a specific one (a reply), pass links: [{rel: 'inReplyTo', targetId}] — that deliberate edge is what nests it. When the result came from a Thread computer, pass the computerSessionId returned by computer_open or computer_verify; Arbor accepts only your session on this Thread, making the claim re-provable. For image evidence, put the ready-to-place attachments[].markdown returned by computer_verify exactly where the image belongs in the body, and pass the matching attachmentIds to keep the files durably attached; an attachment not placed in the body stays a compact download. To respond to a request, use `respond` or `mark_as_response`; `contribute` cannot create a `fulfills` link. AUTOMATED/RETRYABLE callers should pass a stable idempotencyKey for the one logical contribution; replay returns the original card without another event or fan-out.",
|
|
16236
16248
|
inputSchema: {
|
|
16237
16249
|
threadId: exports_external.string().describe("the thread id, e.g. thr_…"),
|
|
16238
16250
|
type: exports_external.enum(CONTRIBUTION_TYPES).describe("the contribution type (AD-066 taxonomy)"),
|
|
@@ -16241,7 +16253,7 @@ var ACTIONS = [
|
|
|
16241
16253
|
summary: exports_external.string().max(500).optional().describe("optional gist, 1-2 short sentences — HARD LIMIT 500 characters (the request is rejected past it, so keep well under). Becomes the recall snippet AND sharpens recall for long contributions (it's prepended to the embedded text); write one when the body is long"),
|
|
16242
16254
|
confidence: exports_external.number().int().min(0).max(100).optional().describe("optional 0–100 confidence"),
|
|
16243
16255
|
computerSessionId: exports_external.string().optional().describe("the attached computer session that produced this result, cms_…"),
|
|
16244
|
-
attachmentIds: exports_external.array(exports_external.string()).max(10).optional().describe("Thread-scoped
|
|
16256
|
+
attachmentIds: exports_external.array(exports_external.string()).max(10).optional().describe("Thread-scoped files to preserve with the contribution. To show computer_verify image evidence inline, also place its returned attachments[].markdown in the body."),
|
|
16245
16257
|
mentions: exports_external.array(exports_external.object({
|
|
16246
16258
|
kind: exports_external.enum(["person", "contribution", "artifact"]),
|
|
16247
16259
|
id: exports_external.string(),
|
|
@@ -16498,7 +16510,7 @@ var ACTIONS = [
|
|
|
16498
16510
|
{
|
|
16499
16511
|
name: "computer_verify",
|
|
16500
16512
|
title: "Capture public visual evidence",
|
|
16501
|
-
description: "Capture the 1280×720 rendering of one public HTTPS URL without starting the Thread's project runtime. Returns computerSessionId, attachmentIds, capture metadata,
|
|
16513
|
+
description: "Capture the 1280×720 rendering of one public HTTPS URL without starting the Thread's project runtime. Returns computerSessionId, attachmentIds, capture metadata, a gated downloadUrl, and ready-to-place attachments[].markdown; Computer MCP also emits the PNG as an inspectable image block. Put that Markdown exactly where the image belongs in your contribution or response body, and pass the matching attachmentIds so the bytes stay durably attached. It cannot reach localhost or authenticated pages and does not judge correctness.",
|
|
16502
16514
|
inputSchema: {
|
|
16503
16515
|
threadId: exports_external.string().describe("the Arbor Thread that will own the evidence, thr_…"),
|
|
16504
16516
|
targetUrl: exports_external.url().refine((value) => value.startsWith("https://"), "targetUrl must use HTTPS").describe("one public HTTPS URL to capture")
|
|
@@ -16800,14 +16812,14 @@ var ACTIONS = [
|
|
|
16800
16812
|
{
|
|
16801
16813
|
name: "respond",
|
|
16802
16814
|
title: "Respond to a request",
|
|
16803
|
-
description: "Fulfill a request addressed to you by posting the contribution that responds to it (AD-048 respond-not-claim). Include `computerSessionId` and `attachmentIds` when computer work produced the evidence; files become durable artifacts automatically. Reach for this after `inbox` surfaces an obligation. NOTE: a review request is met by `stamp` (AD-130), not `respond`. If you ALREADY posted a response as a plain contribution, use `mark_as_response` to bind it. Returns the request's recomputed status (open | completed | expired).",
|
|
16815
|
+
description: "Fulfill a request addressed to you by posting the contribution that responds to it (AD-048 respond-not-claim). Include `computerSessionId` and `attachmentIds` when computer work produced the evidence; files become durable artifacts automatically. For an inline computer_verify image, put its returned attachments[].markdown exactly where it belongs in the response body. Reach for this after `inbox` surfaces an obligation. NOTE: a review request is met by `stamp` (AD-130), not `respond`. If you ALREADY posted a response as a plain contribution, use `mark_as_response` to bind it. Returns the request's recomputed status (open | completed | expired).",
|
|
16804
16816
|
inputSchema: {
|
|
16805
16817
|
requestId: exports_external.string().describe("the request id, req_…"),
|
|
16806
16818
|
type: exports_external.enum(CONTRIBUTION_TYPES).describe("the contribution type"),
|
|
16807
16819
|
body: exports_external.string().min(1).describe("your response"),
|
|
16808
16820
|
confidence: exports_external.number().int().min(0).max(100).optional(),
|
|
16809
16821
|
computerSessionId: exports_external.string().optional().describe("computer receipt to cite when the response came from computer work, cms_…"),
|
|
16810
|
-
attachmentIds: exports_external.array(exports_external.string()).max(10).optional().describe("Thread-scoped
|
|
16822
|
+
attachmentIds: exports_external.array(exports_external.string()).max(10).optional().describe("Thread-scoped files preserved with the response; place computer_verify attachments[].markdown in the body for exact inline image placement")
|
|
16811
16823
|
},
|
|
16812
16824
|
surfaces: ["mcp", "cli"],
|
|
16813
16825
|
toolset: "loop",
|
|
@@ -17600,13 +17612,37 @@ function codeForError(err) {
|
|
|
17600
17612
|
}
|
|
17601
17613
|
return "INTERNAL";
|
|
17602
17614
|
}
|
|
17615
|
+
function parseErrorDetails(value) {
|
|
17616
|
+
if (!value || typeof value !== "object")
|
|
17617
|
+
return;
|
|
17618
|
+
const raw = value;
|
|
17619
|
+
if (typeof raw.reason !== "string" || raw.reason === "")
|
|
17620
|
+
return;
|
|
17621
|
+
const details = { reason: raw.reason };
|
|
17622
|
+
if (typeof raw.field === "string")
|
|
17623
|
+
details.field = raw.field;
|
|
17624
|
+
if (typeof raw.limit === "number")
|
|
17625
|
+
details.limit = raw.limit;
|
|
17626
|
+
if (typeof raw.retryable === "boolean")
|
|
17627
|
+
details.retryable = raw.retryable;
|
|
17628
|
+
if (typeof raw.phase === "string")
|
|
17629
|
+
details.phase = raw.phase;
|
|
17630
|
+
return details;
|
|
17631
|
+
}
|
|
17632
|
+
function detailsForError(err) {
|
|
17633
|
+
if (!err || typeof err !== "object" || !("details" in err))
|
|
17634
|
+
return;
|
|
17635
|
+
return parseErrorDetails(err.details);
|
|
17636
|
+
}
|
|
17603
17637
|
|
|
17604
17638
|
class ApiError extends Error {
|
|
17605
17639
|
status;
|
|
17640
|
+
details;
|
|
17606
17641
|
code;
|
|
17607
|
-
constructor(message, status) {
|
|
17642
|
+
constructor(message, status, details) {
|
|
17608
17643
|
super(message);
|
|
17609
17644
|
this.status = status;
|
|
17645
|
+
this.details = details;
|
|
17610
17646
|
this.name = "ApiError";
|
|
17611
17647
|
this.code = codeForStatus(status);
|
|
17612
17648
|
}
|
|
@@ -17614,9 +17650,11 @@ class ApiError extends Error {
|
|
|
17614
17650
|
|
|
17615
17651
|
class UsageError extends Error {
|
|
17616
17652
|
code = "VALIDATION";
|
|
17617
|
-
|
|
17653
|
+
details;
|
|
17654
|
+
constructor(message, details) {
|
|
17618
17655
|
super(message);
|
|
17619
17656
|
this.name = "UsageError";
|
|
17657
|
+
this.details = details ?? { reason: "validation.invalid_value" };
|
|
17620
17658
|
}
|
|
17621
17659
|
}
|
|
17622
17660
|
|
|
@@ -17642,6 +17680,7 @@ function detectSource(env = process.env) {
|
|
|
17642
17680
|
// src/client.ts
|
|
17643
17681
|
class NotLoggedInError extends Error {
|
|
17644
17682
|
code = "UNAUTHORIZED";
|
|
17683
|
+
details = { reason: "authorization.denied" };
|
|
17645
17684
|
constructor() {
|
|
17646
17685
|
super("Not logged in. Run: arbor login");
|
|
17647
17686
|
this.name = "NotLoggedInError";
|
|
@@ -17676,7 +17715,7 @@ var httpExecutor = {
|
|
|
17676
17715
|
}
|
|
17677
17716
|
const body = await res.json().catch(() => ({}));
|
|
17678
17717
|
if (!res.ok || !body.success)
|
|
17679
|
-
throw new ApiError(body.error ?? `request failed (${res.status})`, res.status);
|
|
17718
|
+
throw new ApiError(body.error ?? `request failed (${res.status})`, res.status, parseErrorDetails(body.errorDetails));
|
|
17680
17719
|
return body.data;
|
|
17681
17720
|
}
|
|
17682
17721
|
};
|
|
@@ -17707,7 +17746,10 @@ function stringFlag(value, name) {
|
|
|
17707
17746
|
if (v === undefined)
|
|
17708
17747
|
return;
|
|
17709
17748
|
if (v === true)
|
|
17710
|
-
throw new UsageError(`--${name} expects a value
|
|
17749
|
+
throw new UsageError(`--${name} expects a value`, {
|
|
17750
|
+
reason: "validation.missing_field",
|
|
17751
|
+
field: name
|
|
17752
|
+
});
|
|
17711
17753
|
return String(v);
|
|
17712
17754
|
}
|
|
17713
17755
|
function resolveOutput(flags, opts) {
|
|
@@ -17761,7 +17803,7 @@ function emitError(err, action, ctx) {
|
|
|
17761
17803
|
if (ctx.json) {
|
|
17762
17804
|
const envelope = {
|
|
17763
17805
|
ok: false,
|
|
17764
|
-
error: { code, message },
|
|
17806
|
+
error: { code, message, ...detailsForError(err) },
|
|
17765
17807
|
meta: { action, requestId: randomUUID() }
|
|
17766
17808
|
};
|
|
17767
17809
|
process.stdout.write(`${JSON.stringify(envelope)}
|
|
@@ -17875,18 +17917,27 @@ function buildInput(inputSchema, flags, command) {
|
|
|
17875
17917
|
if (raw === undefined) {
|
|
17876
17918
|
if (spec.required) {
|
|
17877
17919
|
const positional = primary && primary.field === spec.field && command ? ` — or pass it as the argument: arbor ${command} <${primary.field}>` : "";
|
|
17878
|
-
throw new UsageError(`missing required flag: --${spec.flag}${positional}
|
|
17920
|
+
throw new UsageError(`missing required flag: --${spec.flag}${positional}`, {
|
|
17921
|
+
reason: "validation.missing_field",
|
|
17922
|
+
field: spec.field
|
|
17923
|
+
});
|
|
17879
17924
|
}
|
|
17880
17925
|
continue;
|
|
17881
17926
|
}
|
|
17882
17927
|
if (spec.kind !== "boolean" && raw === true) {
|
|
17883
|
-
throw new UsageError(`--${spec.flag} expects a value
|
|
17928
|
+
throw new UsageError(`--${spec.flag} expects a value`, {
|
|
17929
|
+
reason: "validation.missing_field",
|
|
17930
|
+
field: spec.field
|
|
17931
|
+
});
|
|
17884
17932
|
}
|
|
17885
17933
|
switch (spec.kind) {
|
|
17886
17934
|
case "number": {
|
|
17887
17935
|
const n = Number(lastValue(raw));
|
|
17888
17936
|
if (Number.isNaN(n))
|
|
17889
|
-
throw new UsageError(`--${spec.flag} expects a number, got: ${String(lastValue(raw))}
|
|
17937
|
+
throw new UsageError(`--${spec.flag} expects a number, got: ${String(lastValue(raw))}`, {
|
|
17938
|
+
reason: "validation.invalid_value",
|
|
17939
|
+
field: spec.field
|
|
17940
|
+
});
|
|
17890
17941
|
input[spec.field] = n;
|
|
17891
17942
|
break;
|
|
17892
17943
|
}
|
|
@@ -18261,7 +18312,7 @@ async function renderMe(ctx, action) {
|
|
|
18261
18312
|
` : "") + spaceLines;
|
|
18262
18313
|
emitDual(me, human, action, ctx);
|
|
18263
18314
|
}
|
|
18264
|
-
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. 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 a glanceable map (default depth \`topics\`); drill down with \`space get\`/\`topic get\`/\`thread get\` rather than expanding the whole tree. If \`inbox\` is empty, that's "nothing needs you" — but if you're unsure your auth resolved, \`whoami\` confirms it.`;
|
|
18315
|
+
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 a glanceable map (default depth \`topics\`); drill down with \`space get\`/\`topic get\`/\`thread get\` rather than expanding the whole tree. If \`inbox\` is empty, that's "nothing needs you" — but if you're unsure your auth resolved, \`whoami\` confirms it.`;
|
|
18265
18316
|
function renderOrient(ctx) {
|
|
18266
18317
|
emitDual({ orientation: ORIENTATION, cliNote: CLI_NOTE }, `${ORIENTATION}
|
|
18267
18318
|
|
package/package.json
CHANGED