@lambdacurry/arbor 0.4.24 → 0.4.26

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 +65 -32
  2. package/package.json +1 -1
package/dist/arbor.js CHANGED
@@ -1,12 +1,16 @@
1
1
  #!/usr/bin/env node
2
2
  var __defProp = Object.defineProperty;
3
+ var __returnValue = (v) => v;
4
+ function __exportSetter(name, newValue) {
5
+ this[name] = __returnValue.bind(null, newValue);
6
+ }
3
7
  var __export = (target, all) => {
4
8
  for (var name in all)
5
9
  __defProp(target, name, {
6
10
  get: all[name],
7
11
  enumerable: true,
8
12
  configurable: true,
9
- set: (newValue) => all[name] = () => newValue
13
+ set: __exportSetter.bind(all, name)
10
14
  });
11
15
  };
12
16
 
@@ -1397,8 +1401,15 @@ var invitations = sqliteTable("invitations", {
1397
1401
  email: text("email").notNull(),
1398
1402
  profileId: text("profile_id").notNull().references(() => profiles.id),
1399
1403
  status: text("status").$type().notNull().default("pending"),
1404
+ token: text("token"),
1405
+ expiresAt: ts("expires_at"),
1406
+ invitedByProfileId: text("invited_by_profile_id").references(() => profiles.id),
1407
+ spaceIds: text("space_ids", { mode: "json" }).$type(),
1400
1408
  createdAt: ts("created_at").notNull()
1401
- }, (t) => ({ emailIdx: index("invitations_email_idx").on(t.email, t.status) }));
1409
+ }, (t) => ({
1410
+ emailIdx: index("invitations_email_idx").on(t.email, t.status),
1411
+ tokenIdx: uniqueIndex("invitations_token_idx").on(t.token)
1412
+ }));
1402
1413
  var agentPairings = sqliteTable("agent_pairings", {
1403
1414
  id: text("id").primaryKey(),
1404
1415
  orgId: text("org_id").notNull().references(() => orgs.id),
@@ -1614,6 +1625,7 @@ var notificationCursors = sqliteTable("notification_cursors", {
1614
1625
  lastSeenAt: ts("last_seen_at").notNull()
1615
1626
  });
1616
1627
  // ../core/src/ops/onboarding.ts
1628
+ var INVITE_TTL_MS = 7 * 24 * 60 * 60 * 1000;
1617
1629
  var EMOJI_RE = new RegExp("^\\p{RGI_Emoji}$", "v");
1618
1630
  // ../core/src/ops/agent-pairing.ts
1619
1631
  var PAIRING_TTL_MS = 15 * 60 * 1000;
@@ -13174,7 +13186,7 @@ function finalize(ctx, schema) {
13174
13186
  result.$schema = "http://json-schema.org/draft-07/schema#";
13175
13187
  } else if (ctx.target === "draft-04") {
13176
13188
  result.$schema = "http://json-schema.org/draft-04/schema#";
13177
- } else if (ctx.target === "openapi-3.0") {} else {}
13189
+ } else if (ctx.target === "openapi-3.0") {}
13178
13190
  if (ctx.external?.uri) {
13179
13191
  const id = ctx.external.registry.get(schema)?.id;
13180
13192
  if (!id)
@@ -13418,7 +13430,7 @@ var literalProcessor = (schema, ctx, json, _params) => {
13418
13430
  if (val === undefined) {
13419
13431
  if (ctx.unrepresentable === "throw") {
13420
13432
  throw new Error("Literal `undefined` cannot be represented in JSON Schema");
13421
- } else {}
13433
+ }
13422
13434
  } else if (typeof val === "bigint") {
13423
13435
  if (ctx.unrepresentable === "throw") {
13424
13436
  throw new Error("BigInt literals cannot be represented in JSON Schema");
@@ -15989,7 +16001,7 @@ config(en_default());
15989
16001
  // ../actions/src/index.ts
15990
16002
  var ORIENTATION = `Arbor is your team's deliberation room and shared memory — people and agents settle typed work here, and Arbor remembers what's decided. To work well:
15991
16003
 
15992
- 1. RECALL FIRST. Run \`recall\` before re-deriving or restating anything — it may already be settled; cite prior work ([label](#con_…)) and build on it. Empty recall is itself worth noting. WHY: a room where everyone re-derives is just a chat log.
16004
+ 1. RECALL FIRST. Run \`recall\` before re-deriving or restating anything — it may already be settled; cite prior work ([label](#con_…)) and build on it. Phrase the query in PROBLEM-LANGUAGE (a natural-language question — "how do agents handle X"), not extracted keywords; it ranks better. Empty recall is itself worth noting. WHY: a room where everyone re-derives is just a chat log.
15993
16005
  2. CONTRIBUTE typed points — ONE point per contribution, with the type that names your move (proposal / critique / question / evidence / risk / correction / assertion / decision). Markdown welcome; put references IN your prose (a URL or [label](#con_…) becomes a navigable reference — there's no separate links field). WHY: one typed point is reviewable on its own.
15994
16006
  3. ANSWER through asks. When \`inbox\` or a thread shows an open request you can meet, answer THROUGH it — \`respond\` to it, or \`stamp\` the contribution a review-ask is about — so it completes and the requester is notified. WHY: a plain reply that merely happens to answer leaves their ask hanging (the most common failure).
15995
16007
  4. REVIEW honestly; promote the standout, sparingly. \`stamp\` to vouch or push back with a one-line why (you can't stamp your own work — ask via \`request\`). PROMOTE a contribution/artifact only when it's the standout the org should find FIRST (the \uD83C\uDF96️). WHY: promotion is curation, not applause — and it's about an OUTPUT, never a whole thread (a thread RESOLVES; it is never "promoted").
@@ -16331,11 +16343,11 @@ var ACTIONS = [
16331
16343
  {
16332
16344
  name: "tree",
16333
16345
  title: "Navigate the workspace tree",
16334
- description: "Map the workspace in one call your accessible spaces (with their purpose, governance stage, and ROSTER: who's in each space, their role, and what they can do) plus each space's topics and threads (with contribution counts) to orient WITHOUT chaining space/topic/thread reads. The roster tells you who to route work to; for the full picture of how someone operates (their charter + how they work in a specific space) read `space_get`. Scope with space or topic; depth=topics stops above threads.",
16346
+ description: "Map the workspace a GLANCEABLE structure map, not a full dump. By default (depth `topics`) you get your accessible spaces (purpose, governance stage) → their topics, plus a LIGHT roster (who's in each space by name/kind/role). Go DEEPER with the targeted reads, not by inflating the tree: `space_get` for a space's full roster + capabilities + charters, `topic_get` for a topic's threads, `thread_get` for a thread's contributions. Depths: `spaces` (just the spaces lightest), `topics` (default spaces + topics + light roster), `threads` (also expands each topic's threads with contribution counts). Scope with space or topic to map just that subtree.",
16335
16347
  inputSchema: {
16336
16348
  space: exports_external.string().optional().describe("scope to one space id (spc_…)"),
16337
16349
  topic: exports_external.string().optional().describe("scope to one topic id (top_…)"),
16338
- depth: exports_external.enum(["topics", "threads"]).optional().describe("how deep to expand: topics | threads (default threads)")
16350
+ depth: exports_external.enum(["spaces", "topics", "threads"]).optional().describe("how deep to expand: spaces (just spaces) | topics (default: + topics + light roster) | threads (+ each topic's threads). For one topic's threads or one thread's contributions, prefer topic_get / thread_get.")
16339
16351
  },
16340
16352
  surfaces: ["mcp", "cli"],
16341
16353
  run: forward("navigation.tree")
@@ -16725,24 +16737,29 @@ var httpExecutor = {
16725
16737
 
16726
16738
  // src/output.ts
16727
16739
  import { randomUUID } from "node:crypto";
16740
+ function lastValue(value) {
16741
+ return Array.isArray(value) ? value[value.length - 1] : value;
16742
+ }
16728
16743
  var GLOBAL_BOOLEAN_FLAGS = new Set(["json", "quiet", "no-quiet", "version", "help"]);
16729
16744
  var RESERVED_FLAGS = new Set([...GLOBAL_BOOLEAN_FLAGS, "url", "fields"]);
16730
16745
  var TRUTHY = new Set(["true", "1", "yes", "on"]);
16731
16746
  function truthyFlag(value) {
16732
- return value === true || typeof value === "string" && TRUTHY.has(value.toLowerCase());
16747
+ const v = lastValue(value);
16748
+ return v === true || typeof v === "string" && TRUTHY.has(v.toLowerCase());
16733
16749
  }
16734
16750
  function stringFlag(value, name) {
16735
- if (value === undefined)
16751
+ const v = lastValue(value);
16752
+ if (v === undefined)
16736
16753
  return;
16737
- if (value === true)
16754
+ if (v === true)
16738
16755
  throw new UsageError(`--${name} expects a value`);
16739
- return String(value);
16756
+ return String(v);
16740
16757
  }
16741
16758
  function resolveOutput(flags, opts) {
16742
16759
  const json2 = truthyFlag(flags.json);
16743
16760
  const isTTY = opts?.isTTY ?? Boolean(process.stdout.isTTY);
16744
16761
  const quiet = truthyFlag(flags["no-quiet"]) ? false : truthyFlag(flags.quiet) || json2 || !isTTY;
16745
- const fieldsRaw = flags.fields;
16762
+ const fieldsRaw = lastValue(flags.fields);
16746
16763
  const fields = typeof fieldsRaw === "string" && fieldsRaw.trim() ? fieldsRaw.split(",").map((f) => f.trim()).filter(Boolean) : undefined;
16747
16764
  return { json: json2, quiet, fields };
16748
16765
  }
@@ -16875,7 +16892,7 @@ function buildInput(inputSchema, flags) {
16875
16892
  const input = {};
16876
16893
  for (const spec of flagsForSchema(inputSchema)) {
16877
16894
  if (spec.kind === "string") {
16878
- const fileSrc = flags[`${spec.flag}-file`];
16895
+ const fileSrc = lastValue(flags[`${spec.flag}-file`]);
16879
16896
  if (fileSrc !== undefined) {
16880
16897
  if (flags[spec.flag] !== undefined) {
16881
16898
  throw new UsageError(`pass either --${spec.flag} or --${spec.flag}-file, not both`);
@@ -16897,33 +16914,41 @@ function buildInput(inputSchema, flags) {
16897
16914
  }
16898
16915
  switch (spec.kind) {
16899
16916
  case "number": {
16900
- const n = Number(raw);
16917
+ const n = Number(lastValue(raw));
16901
16918
  if (Number.isNaN(n))
16902
- throw new UsageError(`--${spec.flag} expects a number, got: ${String(raw)}`);
16919
+ throw new UsageError(`--${spec.flag} expects a number, got: ${String(lastValue(raw))}`);
16903
16920
  input[spec.field] = n;
16904
16921
  break;
16905
16922
  }
16906
16923
  case "array": {
16907
- const text3 = String(raw).trim();
16908
- if (text3.startsWith("[")) {
16909
- try {
16910
- const parsed = JSON.parse(text3);
16911
- if (Array.isArray(parsed)) {
16912
- input[spec.field] = parsed;
16913
- break;
16924
+ const tokens = Array.isArray(raw) ? raw : [String(raw)];
16925
+ const out = [];
16926
+ for (const token of tokens) {
16927
+ const text3 = String(token).trim();
16928
+ if (text3.startsWith("[")) {
16929
+ try {
16930
+ const parsed = JSON.parse(text3);
16931
+ if (Array.isArray(parsed)) {
16932
+ out.push(...parsed);
16933
+ continue;
16934
+ }
16935
+ } catch {
16936
+ throw new UsageError(`--${spec.flag} looks like JSON but failed to parse — check the quoting`);
16914
16937
  }
16915
- } catch {
16916
- throw new UsageError(`--${spec.flag} looks like JSON but failed to parse — check the quoting`);
16917
16938
  }
16939
+ for (const part of text3.split(",").map((s) => s.trim()).filter(Boolean))
16940
+ out.push(part);
16918
16941
  }
16919
- input[spec.field] = text3.split(",").map((s) => s.trim()).filter(Boolean);
16942
+ input[spec.field] = out;
16920
16943
  break;
16921
16944
  }
16922
- case "boolean":
16923
- input[spec.field] = raw === true ? true : raw !== "false";
16945
+ case "boolean": {
16946
+ const v = lastValue(raw);
16947
+ input[spec.field] = v === true ? true : v !== "false";
16924
16948
  break;
16949
+ }
16925
16950
  default:
16926
- input[spec.field] = String(raw);
16951
+ input[spec.field] = String(lastValue(raw));
16927
16952
  }
16928
16953
  }
16929
16954
  return input;
@@ -17085,6 +17110,14 @@ async function login(opts) {
17085
17110
  }
17086
17111
 
17087
17112
  // src/index.ts
17113
+ function setFlag(flags, key, value) {
17114
+ const prev = flags[key];
17115
+ if (prev === undefined || typeof value === "boolean" || typeof prev === "boolean") {
17116
+ flags[key] = value;
17117
+ return;
17118
+ }
17119
+ flags[key] = Array.isArray(prev) ? [...prev, value] : [prev, value];
17120
+ }
17088
17121
  function parseArgs(argv) {
17089
17122
  const positionals = [];
17090
17123
  const flags = {};
@@ -17094,11 +17127,11 @@ function parseArgs(argv) {
17094
17127
  const body = arg.slice(2);
17095
17128
  const eq = body.indexOf("=");
17096
17129
  if (eq !== -1) {
17097
- flags[body.slice(0, eq)] = body.slice(eq + 1);
17130
+ setFlag(flags, body.slice(0, eq), body.slice(eq + 1));
17098
17131
  } else if (!GLOBAL_BOOLEAN_FLAGS.has(body) && i + 1 < argv.length && !argv[i + 1].startsWith("--")) {
17099
- flags[body] = argv[++i];
17132
+ setFlag(flags, body, argv[++i]);
17100
17133
  } else {
17101
- flags[body] = true;
17134
+ setFlag(flags, body, true);
17102
17135
  }
17103
17136
  } else {
17104
17137
  positionals.push(arg);
@@ -17128,7 +17161,7 @@ async function renderMe(ctx, action) {
17128
17161
  ` : "") + spaceLines;
17129
17162
  emitDual(me, human, action, ctx);
17130
17163
  }
17131
- var CLI_NOTE = `On this CLI, before your first write: 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\` for a command's exact flags instead of guessing. Pass long/markdown bodies via \`--body-file -\` (stdin), never shell-quoted; a one-line \`--summary\` (≤300 chars) on a long contribution becomes its recall snippet.`;
17164
+ var CLI_NOTE = `On this CLI, before your first write: commands are NOUN-VERB (\`thread get\`, \`space get\`, not \`get thread\`). 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\` for a command's exact flags instead of guessing. Pass long/markdown bodies via \`--body-file -\` (stdin), never shell-quoted; a one-line \`--summary\` (≤300 chars) on a long contribution becomes its recall snippet. List inputs (e.g. \`--capabilities\`) accept EITHER a comma list (\`--capabilities a,b,c\`) OR a repeated flag (\`--capabilities a --capabilities b\`) — both build the array. \`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.`;
17132
17165
  function renderOrient(ctx) {
17133
17166
  emitDual({ orientation: ORIENTATION, cliNote: CLI_NOTE }, `${ORIENTATION}
17134
17167
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lambdacurry/arbor",
3
- "version": "0.4.24",
3
+ "version": "0.4.26",
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
  "type": "module",
6
6
  "bin": {