@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.
- package/dist/arbor.js +29 -13
- 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) => ({
|
|
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(
|
|
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(
|
|
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:
|
|
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
|
|
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\` (
|
|
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