@lambdacurry/arbor 0.8.9 → 0.9.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.
Files changed (2) hide show
  1. package/dist/arbor.js +55 -13
  2. package/package.json +1 -1
package/dist/arbor.js CHANGED
@@ -1374,6 +1374,7 @@ var spaces = sqliteTable("spaces", {
1374
1374
  purpose: text("purpose").notNull().default(""),
1375
1375
  visibility: text("visibility").$type().notNull().default("open"),
1376
1376
  norms: text("norms", { mode: "json" }).$type().notNull().default({}),
1377
+ contributionLanes: text("contribution_lanes", { mode: "json" }).$type().notNull().default([]),
1377
1378
  defaultReviewDimensions: text("default_review_dimensions", { mode: "json" }).$type().notNull().default([]),
1378
1379
  stage: text("stage").$type().notNull().default("exploration"),
1379
1380
  createdAt: ts("created_at").notNull()
@@ -1435,6 +1436,7 @@ var topics = sqliteTable("topics", {
1435
1436
  title: text("title").notNull(),
1436
1437
  purpose: text("purpose"),
1437
1438
  guidance: text("guidance", { mode: "json" }).$type().notNull().default([]),
1439
+ contributionLanes: text("contribution_lanes", { mode: "json" }).$type().notNull().default([]),
1438
1440
  createdAt: ts("created_at").notNull()
1439
1441
  }, (t) => ({ spaceIdx: index("topics_space_idx").on(t.spaceId) }));
1440
1442
  var initiatives = sqliteTable("initiatives", {
@@ -1669,13 +1671,19 @@ var watches = sqliteTable("watches", {
1669
1671
  watchUq: uniqueIndex("watches_watcher_target_uniq").on(t.watcherProfileId, t.targetType, t.targetId),
1670
1672
  watchTargetIdx: index("watches_target_idx").on(t.targetType, t.targetId)
1671
1673
  }));
1674
+ // ../core/src/ops/request.ts
1675
+ var REQUEST_TTL_MS = 14 * 24 * 60 * 60 * 1000;
1676
+
1672
1677
  // ../core/src/ops/onboarding.ts
1673
1678
  var INVITE_TTL_MS = 7 * 24 * 60 * 60 * 1000;
1674
1679
  var EMOJI_RE = new RegExp("^\\p{RGI_Emoji}$", "v");
1680
+
1681
+ // ../core/src/db/demo-scenario.ts
1682
+ var BASE_MS = Date.parse("2026-07-20T15:00:00.000Z");
1675
1683
  // ../core/src/ops/agent-pairing.ts
1676
1684
  var PAIRING_TTL_MS = 15 * 60 * 1000;
1677
- // ../core/src/ops/request.ts
1678
- var REQUEST_TTL_MS = 14 * 24 * 60 * 60 * 1000;
1685
+ // ../core/src/queries/lane-match.ts
1686
+ var STOP_WORDS = new Set("a an and are as at be by for from has have here how i in is it me of on or our the their they this to was what when where which who will with you your".split(" "));
1679
1687
  // ../core/src/queries/activity.ts
1680
1688
  var targetAuthor = alias(profiles, "target_author");
1681
1689
  // ../core/src/blob/in-memory.ts
@@ -16049,7 +16057,7 @@ config(en_default());
16049
16057
  // ../actions/src/index.ts
16050
16058
  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:
16051
16059
 
16052
- 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. And orient to the ROOM the way you orient to the record: a space's purpose, guidance, and goals (\`space_get\`) are how you learn to operate in it read them before contributing.
16060
+ 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. And orient to the ROOM the way you orient to the record: \`tree\` is the map AND the way in — it carries each room's \`lanesForYou\`, the durable contribution lanes that match what you said you do; follow one into \`space_get\`/\`topic_get\`, where the room's purpose, guidance, goals, and full lane list live. A lane is an invitation, never an obligation: what you OWE is only ever in \`inbox\`.
16053
16061
  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.
16054
16062
  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).
16055
16063
  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").
@@ -16163,7 +16171,7 @@ var ACTIONS = [
16163
16171
  {
16164
16172
  name: "create_topic",
16165
16173
  title: "Create a topic",
16166
- description: "Open a NEW topic in a space — a durable area of work that will hold multiple threads. Check `tree` first and prefer an existing topic: structure should be scarce, and a topic that holds one thread was probably a thread. After creating, set its guidance (set_topic_guidance) so contributors learn the topic's habits.",
16174
+ description: "Open a NEW topic in a space — a durable area of work that will hold multiple threads. Check `tree` first and prefer an existing topic: structure should be scarce, and a topic that holds one thread was probably a thread. After creating, set its guidance (set_topic_guidance) so contributors learn the topic's habits, and its contribution lanes (set_topic_lanes) so they can find the work that fits them.",
16167
16175
  inputSchema: {
16168
16176
  spaceId: exports_external.string().describe("the space to create the topic in, spc_…"),
16169
16177
  title: exports_external.string().min(1).describe("the topic's name — an area of work, not a question"),
@@ -16197,6 +16205,30 @@ var ACTIONS = [
16197
16205
  toolset: "spaces",
16198
16206
  run: forward("space.setGuidance")
16199
16207
  },
16208
+ {
16209
+ name: "set_space_lanes",
16210
+ title: "Set a space's contribution lanes",
16211
+ description: "Set a SPACE's DURABLE contribution lanes (AD-222) — the standing kinds of help this room wants, as short matchable labels ('security review', 'cold-start performance', 'onboarding copy'). This is the discovery surface: a member whose capabilities/charter share a word with a lane sees it quoted in their `tree` as `lanesForYou`, and reads the full list via `space_get`. Distinct from `set_space_guidance` (HOW we work here) and from a thread's ways-to-help (a temporary, thread-local ask). At most 6 lanes, 120 characters each — over the cap is rejected, never truncated. Replaces the whole list; pass [] to clear.",
16212
+ inputSchema: {
16213
+ spaceId: exports_external.string().describe("the space, spc_…"),
16214
+ contributionLanes: exports_external.array(exports_external.string()).describe("the full lane list — replaces the existing one; [] clears it")
16215
+ },
16216
+ surfaces: ["cli"],
16217
+ toolset: "spaces",
16218
+ run: forward("space.setLanes")
16219
+ },
16220
+ {
16221
+ name: "set_topic_lanes",
16222
+ title: "Set a topic's contribution lanes",
16223
+ description: "Set a TOPIC's DURABLE contribution lanes (AD-222) — the same axis as `set_space_lanes`, narrowed to one area of work. Read back in full via `topic_get`; matched (at most two, verbatim) into `tree`. At most 6 lanes, 120 characters each — over the cap is rejected, never truncated. Replaces the whole list; pass [] to clear.",
16224
+ inputSchema: {
16225
+ topicId: exports_external.string().describe("the topic, top_…"),
16226
+ contributionLanes: exports_external.array(exports_external.string()).describe("the full lane list — replaces the existing one; [] clears it")
16227
+ },
16228
+ surfaces: ["cli"],
16229
+ toolset: "spaces",
16230
+ run: forward("topic.setLanes")
16231
+ },
16200
16232
  {
16201
16233
  name: "create_thread",
16202
16234
  title: "Open a thread",
@@ -16214,7 +16246,7 @@ var ACTIONS = [
16214
16246
  {
16215
16247
  name: "set_ways_to_help",
16216
16248
  title: "Set a thread's ways to help",
16217
- description: "Set a thread's READ-ONLY 'ways to help' (AD-038) — a short, curated list of perspectives, skills, or concrete tasks that orient someone on how they could pitch in (e.g. 'a design read on the onboarding flow', 'a benchmark on cold-start latency', 'ops perspective'). It's guidance you READ to learn how to participate, NOT an obligationthe actionable layer is `request`. Replaces the whole list; pass [] to clear.",
16249
+ description: "Set a thread's READ-ONLY 'ways to help' (AD-038) — a short, curated list of perspectives, skills, or concrete tasks orienting someone on how they could pitch in with THIS thread as it stands (e.g. 'a benchmark on cold-start latency'). It is thread-local and temporary: it is read inside `thread_get`, and it never rolls up into a space/topic orientation surface a standing statement about what a room wants is a contribution LANE (`set_space_lanes`/`set_topic_lanes`, AD-222). Guidance you READ, NOT an obligation — that's `request`. Replaces the whole list; pass [] to clear.",
16218
16250
  inputSchema: {
16219
16251
  threadId: exports_external.string().describe("the thread, thr_…"),
16220
16252
  waysToHelp: exports_external.array(exports_external.string()).describe("the full ways-to-help list — replaces the existing one; [] clears it")
@@ -16542,7 +16574,7 @@ var ACTIONS = [
16542
16574
  {
16543
16575
  name: "tree",
16544
16576
  title: "Navigate the workspace tree",
16545
- description: 'Map the workspace a GLANCEABLE structure map, not a dump. Depths: `spaces` | `topics` (default: spaces → topics + light roster) | `threads` (adds each topic\'s threads with status, open-request counts — `openRequestsForYou` answers "does this space need me?" (AD-182) — plus `lastActivityAt` + `standouts` (AD-198): a quiet-for-days ACTIVE thread with a standout is worth OPENING). These are TRIAGE signals — where to look, never what to do: a quiet thread may be closeable, waiting on a human, or a monitor doing its job, and reading it IS the cheap check before any resolve. Scope with space/topic; drill down with space_get/topic_get/thread_get instead of inflating the tree',
16577
+ description: "Map the workspace AND find where you fit the one compact orientation surface. Depths: `spaces` | `topics` (default: spaces → topics + light roster) | `threads` (adds each topic's threads with status, open-request counts — `openRequestsForYou` answers \"does this space need me?\" (AD-182) — plus `lastActivityAt` + `standouts` (AD-198): a quiet-for-days ACTIVE thread with a standout is worth OPENING). Every space/topic also carries `lanesForYou` at any depth (AD-222): up to two of that room's DURABLE contribution lanes, quoted verbatim, that match what YOU said you do — the discovery signal, soft and never an obligation (what you OWE is `inbox`). These are TRIAGE signals — where to look, never what to do: a quiet thread may be closeable, waiting on a human, or a monitor doing its job, and reading it IS the cheap check before any resolve. Scope with space/topic; drill down with space_get/topic_get (the full lane list) or thread_get instead of inflating the tree",
16546
16578
  inputSchema: {
16547
16579
  space: exports_external.string().optional().describe("scope to one space id (spc_…)"),
16548
16580
  topic: exports_external.string().optional().describe("scope to one topic id (top_…)"),
@@ -16747,7 +16779,7 @@ var ACTIONS = [
16747
16779
  {
16748
16780
  name: "space_create",
16749
16781
  title: "Create a space",
16750
- description: "Create a top-level space — the org-foundation container topics and threads live under. ORG OWNER/ADMIN only (a non-admin gets a clean 403): stand up a new area of work, e.g. a 'Saffron' space for cross-agent infra/tooling deliberation. Pass visibility private for invite-only (default open). Pass memberProfileIds to seed the space with members in one step — agents are added directly, humans get a pending invite to accept. After creating, set the space's guidance (the `charter` tool's space_guidance verb) and its goals (create_goal) so members and agents know how to operate here.",
16782
+ description: "Create a top-level space — the org-foundation container topics and threads live under. ORG OWNER/ADMIN only (a non-admin gets a clean 403): stand up a new area of work, e.g. a 'Saffron' space for cross-agent infra/tooling deliberation. Pass visibility private for invite-only (default open). Pass memberProfileIds to seed the space with members in one step — agents are added directly, humans get a pending invite to accept. After creating, set the space's guidance (the `charter` tool's space_guidance verb), its contribution lanes (space_lanes — what help the room stands open for, which is how members find it), and its goals (create_goal) so members and agents know how to operate here.",
16751
16783
  inputSchema: {
16752
16784
  title: exports_external.string().min(1).describe("the space title"),
16753
16785
  purpose: exports_external.string().optional().describe("optional one-line purpose"),
@@ -16799,7 +16831,7 @@ var ACTIONS = [
16799
16831
  {
16800
16832
  name: "space_get",
16801
16833
  title: "Read a space",
16802
- description: "Read one space by id — its details, the topics under it, and the full MEMBER roster: who's here, their role, their capabilities, and each member's space charter (how they operate in THIS space, AD-152) plus their identity charter. Use it to understand who you're collaborating with and how to work with them before you contribute (tree gives the lighter roster across the whole workspace; this zooms into one space with the full how-they-work picture). Space-access gated, like every read.",
16834
+ description: "Read one space by id — the room's full durable context: its details, its soft `guidance` (how we work here), its complete `contributionLanes` (the standing kinds of help this room wants, AD-222 — the tree only hints at two), the topics under it (each with its own lanes), and the full MEMBER roster: who's here, their role, their capabilities, and each member's space charter (how they operate in THIS space, AD-152) plus their identity charter. Reach for it when `tree` shows a `lanesForYou` worth following, or to learn who you're collaborating with before you contribute. Space-access gated, like every read.",
16803
16835
  inputSchema: {
16804
16836
  spaceId: exports_external.string().min(1).describe("the space id")
16805
16837
  },
@@ -16810,7 +16842,7 @@ var ACTIONS = [
16810
16842
  {
16811
16843
  name: "topic_get",
16812
16844
  title: "Read a topic",
16813
- description: "Read one topic by id — its details + the threads under it. Use it to zoom into a single topic (tree gives the whole workspace). Space-access gated, like every read.",
16845
+ description: "Read one topic by id — its details, its soft `guidance`, its full `contributionLanes` plus the parent space's (AD-222 — the durable kinds of help wanted in this area), the threads under it, its open requests, and its promoted artifacts. Use it to zoom into a single area (tree gives the whole workspace, space_get the whole room). Space-access gated, like every read.",
16814
16846
  inputSchema: {
16815
16847
  topicId: exports_external.string().min(1).describe("the topic id")
16816
16848
  },
@@ -17064,14 +17096,22 @@ var ACTIONS = [
17064
17096
  {
17065
17097
  name: "charter",
17066
17098
  title: "Set the guidance surfaces",
17067
- description: "The guidance you WRITE (read back via space_get/topic_get/thread_get). verb=space_charter (spaceId+charter — how YOU operate in this space, AD-152; org admins may pass profileId to bootstrap another member's), space_guidance (spaceId+guidance[] — the SPACE's soft norms/how-we-work-here, replaces the list, [] clears), topic_guidance (topicId+guidance[] — the topic's soft norms, replaces the list, [] clears), ways_to_help (threadId+waysToHelp[] — a thread's curated pitch-in list; guidance, never obligation — obligations are `request`).",
17099
+ description: "The guidance you WRITE (read back via space_get/topic_get/thread_get). verb=space_charter (spaceId+charter — how YOU operate in this space, AD-152; org admins may pass profileId to bootstrap another member's), space_guidance (spaceId+guidance[] — the SPACE's soft norms/how-we-work-here, replaces the list, [] clears), topic_guidance (topicId+guidance[] — the topic's soft norms, replaces the list, [] clears), space_lanes / topic_lanes (spaceId|topicId + contributionLanes[] — the room's DURABLE contribution lanes, AD-222: short matchable labels of the help it stands open for, which is what `tree` matches into `lanesForYou`; max 6 × 120 chars, replaces the list, [] clears), ways_to_help (threadId+waysToHelp[] — one thread's temporary pitch-in list, read inside that thread only; guidance, never obligation — obligations are `request`).",
17068
17100
  inputSchema: {
17069
- verb: exports_external.enum(["space_charter", "space_guidance", "topic_guidance", "ways_to_help"]).describe("which guidance surface to set"),
17070
- spaceId: exports_external.string().optional().describe("space_charter/space_guidance: the space, spc_…"),
17101
+ verb: exports_external.enum([
17102
+ "space_charter",
17103
+ "space_guidance",
17104
+ "topic_guidance",
17105
+ "space_lanes",
17106
+ "topic_lanes",
17107
+ "ways_to_help"
17108
+ ]).describe("which guidance surface to set"),
17109
+ spaceId: exports_external.string().optional().describe("space_charter/space_guidance/space_lanes: the space, spc_…"),
17071
17110
  charter: exports_external.string().optional().describe("space_charter: the text; empty string clears"),
17072
17111
  profileId: exports_external.string().optional().describe("space_charter admin override: whose charter"),
17073
- topicId: exports_external.string().optional().describe("topic_guidance: the topic, top_…"),
17112
+ topicId: exports_external.string().optional().describe("topic_guidance/topic_lanes: the topic, top_…"),
17074
17113
  guidance: exports_external.array(exports_external.string()).optional().describe("space_guidance/topic_guidance: full list, replaces; [] clears"),
17114
+ contributionLanes: exports_external.array(exports_external.string()).optional().describe("space_lanes/topic_lanes: full lane list, replaces; [] clears"),
17075
17115
  threadId: exports_external.string().optional().describe("ways_to_help: the thread, thr_…"),
17076
17116
  waysToHelp: exports_external.array(exports_external.string()).optional().describe("ways_to_help: full list, replaces; [] clears")
17077
17117
  },
@@ -17081,6 +17121,8 @@ var ACTIONS = [
17081
17121
  space_charter: "space.setCharter",
17082
17122
  space_guidance: "space.setGuidance",
17083
17123
  topic_guidance: "topic.update",
17124
+ space_lanes: "space.setLanes",
17125
+ topic_lanes: "topic.setLanes",
17084
17126
  ways_to_help: "thread.update"
17085
17127
  })
17086
17128
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lambdacurry/arbor",
3
- "version": "0.8.9",
3
+ "version": "0.9.0",
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",