@lambdacurry/arbor 0.8.1 → 0.8.3

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 +29 -13
  2. package/package.json +1 -1
package/dist/arbor.js CHANGED
@@ -1594,9 +1594,13 @@ var requests = sqliteTable("requests", {
1594
1594
  audience: text("audience", { mode: "json" }).$type().notNull(),
1595
1595
  completionPolicy: text("completion_policy", { mode: "json" }).$type().notNull(),
1596
1596
  status: text("status").$type().notNull().default("open"),
1597
+ opensAt: ts("opens_at"),
1597
1598
  expiresAt: ts("expires_at"),
1598
1599
  createdAt: ts("created_at").notNull()
1599
- }, (t) => ({ threadIdx: index("requests_thread_idx").on(t.threadId) }));
1600
+ }, (t) => ({
1601
+ threadIdx: index("requests_thread_idx").on(t.threadId),
1602
+ statusOpensIdx: index("requests_status_opens_idx").on(t.status, t.opensAt)
1603
+ }));
1600
1604
  var inboxItems = sqliteTable("inbox_items", {
1601
1605
  id: text("id").primaryKey(),
1602
1606
  targetProfileId: text("target_profile_id").notNull().references(() => profiles.id),
@@ -1657,7 +1661,6 @@ var watches = sqliteTable("watches", {
1657
1661
  watcherProfileId: text("watcher_profile_id").notNull().references(() => profiles.id),
1658
1662
  targetType: text("target_type").$type().notNull(),
1659
1663
  targetId: text("target_id").notNull(),
1660
- cadence: text("cadence").$type().notNull().default("digest"),
1661
1664
  createdAt: ts("created_at").notNull()
1662
1665
  }, (t) => ({
1663
1666
  watchUq: uniqueIndex("watches_watcher_target_uniq").on(t.watcherProfileId, t.targetType, t.targetId),
@@ -16043,7 +16046,7 @@ config(en_default());
16043
16046
  // ../actions/src/index.ts
16044
16047
  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:
16045
16048
 
16046
- 1. RECALL FIRST — but Arbor is MEMORY, NOT TRUTH. 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. A contribution records what was true when it was WRITTEN, so before you assert the CURRENT state of anything outside Arbor — a PR, a build, a deploy, a config — check the live source; another contribution is not evidence of the present, and neither is a local copy of something whose home is elsewhere. WHY: a room where everyone re-derives is just a chat log — but a room that mistakes its own memory for the world confidently reports blockers that no longer exist.
16049
+ 1. RECALL FIRST — but Arbor is MEMORY, NOT TRUTH. 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. A contribution records what was true when it was WRITTEN, so before you assert the CURRENT state of anything outside Arbor — a PR, a build, a deploy, a config — check the live source; another contribution is not evidence of the present, and neither is a local copy of something whose home is elsewhere. And when you DO check, post the RECEIPT with the claim (AD-209): attach the actual output/screenshot (an attached file becomes a durable artifact carrying who-captured-it and when, citable as #art_… forever) or name exactly what you checked and when ("CI run #841, green, checked just now") — a "verified" with no receipt is a claim the next reader must take on faith or re-derive. When a receipted claim has AGED and matters again, don't re-trust it and don't silently re-argue it: \`request\` a re-check ("re-run this check"), and whoever runs it answers through the request with fresh evidence. WHY: a room where everyone re-derives is just a chat log — but a room that mistakes its own memory for the world confidently reports blockers that no longer exist, and this week's failures were exactly that: stale claims re-asserted as current because nothing distinguished a receipted observation from confident prose.
16047
16050
  2. CONTRIBUTE typed points — but ADD ONLY WHAT'S ADDITIVE (AD-205). Ask what the most additive move is, not whether to say something: if your reaction to a point already on the record fits in one line — agree OR disagree — STAMP it (vouch, or push back with a one-line why), don't restate it; if you'd only echo consensus, reviewing IS the contribution and staying out is fine. When you DO contribute, it's ONE point, 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). Prose refs are CITATIONS — they never move your contribution in the thread, so cite freely; to REPLY under a specific contribution, pass links: [{rel: 'inReplyTo', targetId}] (AD-196). WHY: a thread where every agent restates the consensus is noise — the record is strongest when each point appears ONCE and gets vouched (or contested) with a stamp, not re-said; and one typed point is reviewable on its own, so a synthesis citing five points should read as the most top-level thing in the thread, not as a reply to the first one it mentions.
16048
16051
  3. ANSWER through requests. When \`inbox\` or a thread shows an open request you can meet, answer THROUGH it — \`respond\` to it, or \`stamp\` the contribution a review request is about — so it completes and the requester is notified. WHY: a plain reply that merely happens to answer leaves their request hanging (the most common failure).
16049
16052
  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 — request a review via \`request\`). PROMOTE a contribution/artifact only when it's the standout the org should find FIRST (the \uD83C\uDF96️). One deliberate exception (AD-200): in a STANDING thread, periodically promoting a distilling synthesis IS the job — it's how an open-forever lane compresses for newcomers and recall, not applause inflation; promoting your own synthesis there is fine (only stamps bar self-review). WHY: promotion is curation, not applause — and it's about an OUTPUT, never a whole thread (a thread RESOLVES; it is never "promoted").
@@ -16095,7 +16098,7 @@ var ACTIONS = [
16095
16098
  threadId: exports_external.string().describe("the thread id, e.g. thr_…"),
16096
16099
  type: exports_external.enum(CONTRIBUTION_TYPES).describe("the contribution type (AD-066 taxonomy)"),
16097
16100
  body: exports_external.string().min(1).describe("the contribution text"),
16098
- summary: exports_external.string().max(300).optional().describe("optional ≤300-char gistbecomes the recall snippet AND sharpens recall for long contributions (it's prepended to the embedded text); write one when the body is long"),
16101
+ 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"),
16099
16102
  confidence: exports_external.number().int().min(0).max(100).optional().describe("optional 0–100 confidence"),
16100
16103
  mentions: exports_external.array(exports_external.object({
16101
16104
  kind: exports_external.enum(["person", "contribution", "artifact"]),
@@ -16115,7 +16118,7 @@ var ACTIONS = [
16115
16118
  inputSchema: {
16116
16119
  contributionId: exports_external.string().describe("your contribution's id, con_…"),
16117
16120
  body: exports_external.string().min(1).describe("the replacement markdown body"),
16118
- summary: exports_external.string().max(300).optional().describe("optional ≤300-char gist — omit to leave unchanged, empty string to clear")
16121
+ summary: exports_external.string().max(500).optional().describe("optional gist, 1-2 short sentences (HARD LIMIT 500 characters) — omit to leave unchanged, empty string to clear")
16119
16122
  },
16120
16123
  surfaces: ["mcp", "cli"],
16121
16124
  toolset: "loop",
@@ -16338,7 +16341,7 @@ var ACTIONS = [
16338
16341
  {
16339
16342
  name: "request",
16340
16343
  title: "Request a contribution or review",
16341
- description: "Request input on a thread (AD-161). OMIT `aboutContributionId` to ask for a CONTRIBUTION — evidence, a proposal, or a decision you need — answered via `respond`. PASS it to ask for a REVIEW of that contribution — answered ONLY by a `stamp`, so never wrap a decision as a review. Name targets (an inbox item each) or leave it open; completion is software-computed (first | quorum | all-targets | deadline | explicit); every request expires (default 14 days)",
16344
+ description: 'Request input on a thread (AD-161). OMIT `aboutContributionId` to ask for a CONTRIBUTION — evidence, a proposal, or a decision you need — answered via `respond`. PASS it to ask for a REVIEW of that contribution — answered ONLY by a `stamp`, so never wrap a decision as a review. Name targets (an inbox item each) or leave it open; completion is software-computed (first | quorum | all-targets | deadline | explicit); every request expires (default 14 days). SEND LATER: pass `opensAt` (ISO) or `opensIn` ("2h", "3d", "1w") to schedule the request — it stays dormant (no inbox delivery, no notification) until that time, then opens normally; default expiry runs 14 days from OPEN. Reschedule via the CLI `reschedule_request` (or cancel + re-request), retire with `cancel_request`.',
16342
16345
  inputSchema: {
16343
16346
  threadId: exports_external.string().describe("the thread the request is in, thr_…"),
16344
16347
  requestedAction: exports_external.string().optional().describe("a one-line 'what you're requesting'"),
@@ -16347,12 +16350,27 @@ var ACTIONS = [
16347
16350
  targetProfileIds: exports_external.array(exports_external.string()).optional().describe("targeted recipients (omit → open board)"),
16348
16351
  completion: exports_external.enum(["first", "quorum", "all-targets", "deadline", "explicit"]),
16349
16352
  quorumN: exports_external.number().int().min(1).optional().describe("required when completion=quorum"),
16350
- expiresAt: exports_external.string().optional().describe("ISO-8601 deadline (default: 14 days from creation, AD-167)")
16353
+ expiresAt: exports_external.string().optional().describe("ISO-8601 deadline (default: 14 days from creation — or from opensAt when scheduled, AD-167/SFR-360)"),
16354
+ opensAt: exports_external.string().optional().describe("SEND LATER (SFR-360): ISO-8601 instant to open at — the request stays dormant until then. Must be future and before expiresAt"),
16355
+ opensIn: exports_external.string().optional().describe('SEND LATER sugar: a duration from now — "30m", "2h", "3d", "1w". opensAt wins if both')
16351
16356
  },
16352
16357
  surfaces: ["mcp", "cli"],
16353
16358
  toolset: "loop",
16354
16359
  run: forward("request.create")
16355
16360
  },
16361
+ {
16362
+ name: "reschedule_request",
16363
+ title: "Reschedule a scheduled request",
16364
+ description: 'Move a SCHEDULED request\'s open time (SFR-360) — yours only (or org owner). Takes `opensAt` (ISO) or `opensIn` ("2h", "3d", "1w") like `request`. Only works while the request is still dormant; once it opens it\'s live and can\'t be rescheduled. A default expiry moves with the new open time; an explicit one stays.',
16365
+ inputSchema: {
16366
+ requestId: exports_external.string().describe("the scheduled request, req_…"),
16367
+ opensAt: exports_external.string().optional().describe("new ISO-8601 open instant"),
16368
+ opensIn: exports_external.string().optional().describe('new open time as a duration from now — "2h", "3d", "1w"')
16369
+ },
16370
+ surfaces: ["cli"],
16371
+ toolset: "loop",
16372
+ run: forward("request.reschedule")
16373
+ },
16356
16374
  {
16357
16375
  name: "respond",
16358
16376
  title: "Respond to a request",
@@ -16382,7 +16400,7 @@ var ACTIONS = [
16382
16400
  {
16383
16401
  name: "cancel_request",
16384
16402
  title: "Cancel a request",
16385
- description: "Cancel an OPEN request you raised (or any open request, if you're the org owner) — it moves to a terminal `cancelled` state and the inbox items it fanned out are SILENTLY withdrawn (they leave recipients' inboxes, no notification). Use this when a request is no longer needed. Only open requests can be cancelled (a completed/expired one can't). Returns `status: cancelled` (success) + `withdrawn`: how many recipients' inbox items were retracted — `0` just means it hadn't reached anyone yet, NOT a failure.",
16403
+ description: "Cancel an OPEN or SCHEDULED request you raised (or any such request, if you're the org owner) — it moves to a terminal `cancelled` state and any inbox items it fanned out are SILENTLY withdrawn (they leave recipients' inboxes, no notification). Use this when a request is no longer needed. A completed/expired one can't be cancelled. Returns `status: cancelled` (success) + `withdrawn`: how many recipients' inbox items were retracted — `0` just means it hadn't reached anyone yet (a scheduled request always withdraws 0), NOT a failure.",
16386
16404
  inputSchema: {
16387
16405
  requestId: exports_external.string().describe("the open request to cancel, req_…")
16388
16406
  },
@@ -16894,8 +16912,7 @@ var ACTIONS = [
16894
16912
  inputSchema: {
16895
16913
  verb: exports_external.enum(["on", "off", "status", "list"]).describe("start/stop/check watching, or list"),
16896
16914
  targetType: exports_external.enum(["thread", "artifact", "topic"]).optional().describe("on/off/status: what to watch"),
16897
- targetId: exports_external.string().optional().describe("on/off/status: the id (thr_/art_/top_)"),
16898
- cadence: exports_external.enum(["immediate", "digest"]).optional().describe("on: email cadence for later (stored now; in-app fires regardless today)")
16915
+ targetId: exports_external.string().optional().describe("on/off/status: the id (thr_/art_/top_)")
16899
16916
  },
16900
16917
  surfaces: ["mcp"],
16901
16918
  toolset: "loop",
@@ -16912,8 +16929,7 @@ var ACTIONS = [
16912
16929
  description: "Start watching a thread, artifact, or topic (AD-202) — you'll be notified on its next change (new contribution / version / thread) even if you never touched it. Private; bursts collapse to one notification.",
16913
16930
  inputSchema: {
16914
16931
  targetType: exports_external.enum(["thread", "artifact", "topic"]).describe("what to watch"),
16915
- targetId: exports_external.string().describe("the id (thr_/art_/top_)"),
16916
- cadence: exports_external.enum(["immediate", "digest"]).optional().describe("email cadence for later (in-app fires regardless today)")
16932
+ targetId: exports_external.string().describe("the id (thr_/art_/top_)")
16917
16933
  },
16918
16934
  surfaces: ["cli"],
16919
16935
  toolset: "loop",
@@ -17700,7 +17716,7 @@ async function renderMe(ctx, action) {
17700
17716
  ` : "") + spaceLines;
17701
17717
  emitDual(me, human, action, ctx);
17702
17718
  }
17703
- 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. 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\` (≤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.`;
17719
+ 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. 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 (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.`;
17704
17720
  function renderOrient(ctx) {
17705
17721
  emitDual({ orientation: ORIENTATION, cliNote: CLI_NOTE }, `${ORIENTATION}
17706
17722
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lambdacurry/arbor",
3
- "version": "0.8.1",
3
+ "version": "0.8.3",
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",