@lambdacurry/arbor 0.8.5 → 0.8.7

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 +15 -8
  2. package/package.json +1 -1
package/dist/arbor.js CHANGED
@@ -1487,6 +1487,8 @@ var contributions = sqliteTable("contributions", {
1487
1487
  type: text("type").$type().notNull(),
1488
1488
  body: text("body").notNull(),
1489
1489
  summary: text("summary"),
1490
+ idempotencyKey: text("idempotency_key"),
1491
+ idempotencyFingerprint: text("idempotency_fingerprint"),
1490
1492
  confidence: integer("confidence"),
1491
1493
  links: text("links", { mode: "json" }).$type(),
1492
1494
  mentions: text("mentions", { mode: "json" }).$type(),
@@ -1501,7 +1503,8 @@ var contributions = sqliteTable("contributions", {
1501
1503
  createdAt: ts("created_at").notNull()
1502
1504
  }, (t) => ({
1503
1505
  threadIdx: index("contributions_thread_idx").on(t.threadId),
1504
- aboutIdx: index("contributions_about_idx").on(t.aboutType, t.aboutId)
1506
+ aboutIdx: index("contributions_about_idx").on(t.aboutType, t.aboutId),
1507
+ authorIdempotencyIdx: uniqueIndex("contributions_author_idempotency_idx").on(t.authorProfileId, t.idempotencyKey)
1505
1508
  }));
1506
1509
  var reviews = sqliteTable("reviews", {
1507
1510
  id: text("id").primaryKey(),
@@ -16093,11 +16096,12 @@ var ACTIONS = [
16093
16096
  {
16094
16097
  name: "contribute",
16095
16098
  title: "Contribute to a thread",
16096
- 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.",
16099
+ 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. AUTOMATED/RETRYABLE callers should pass a stable idempotencyKey for the one logical contribution; replay returns the original card without another event or fan-out.",
16097
16100
  inputSchema: {
16098
16101
  threadId: exports_external.string().describe("the thread id, e.g. thr_…"),
16099
16102
  type: exports_external.enum(CONTRIBUTION_TYPES).describe("the contribution type (AD-066 taxonomy)"),
16100
16103
  body: exports_external.string().min(1).describe("the contribution text"),
16104
+ idempotencyKey: exports_external.string().min(1).max(200).optional().describe("stable key for this ONE logical contribution (≤200 chars), e.g. cron-run/work-item + output slot; safe retries return the original contribution. Reusing it with different content is a conflict."),
16101
16105
  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"),
16102
16106
  confidence: exports_external.number().int().min(0).max(100).optional().describe("optional 0–100 confidence"),
16103
16107
  mentions: exports_external.array(exports_external.object({
@@ -16511,11 +16515,14 @@ var ACTIONS = [
16511
16515
  {
16512
16516
  name: "thread_get",
16513
16517
  title: "Read a thread",
16514
- description: "Read the full state of the thread you're working in objective, every contribution + stamp, its OPEN REQUESTS, and its ARTIFACTS (the durable outputs born from this thread). Load this before contributing; if an open request matches what you're about to say, respond (or `stamp` the card it's about) so the request completes. On a LONG thread, page the contributions with contribLimit/contribCursor (follow contributionPage.nextCursor) to bound how much you load at once.",
16518
+ description: "Read the thread you're working in. Default view=full returns objective, every contribution + stamp, OPEN REQUESTS, and ARTIFACTS; page a long transcript with contribLimit/contribCursor. For recurring/automated checks use view=compact: a decision-ready packet with objective/status, distillate, requests, outputs, and the latest contributions. Save its cursor and pass it next time; an unchanged thread returns only {threadId,cursor,unchanged:true}. Load context before contributing; if an open request matches what you're about to say, respond (or `stamp` the card it's about) so the request completes.",
16515
16519
  inputSchema: {
16516
16520
  threadId: exports_external.string().describe("the thread id, e.g. thr_…"),
16517
- contribLimit: exports_external.number().int().positive().optional().describe("max contributions to return (AD-151 keyset paging) omit to load the whole thread; set on a long thread to bound context"),
16518
- contribCursor: exports_external.string().optional().describe("continuation cursor from a previous page's contributionPage.nextCursor")
16521
+ view: exports_external.enum(["full", "compact"]).optional().describe("full (default) = complete transcript; compact = bounded decision-ready context packet for recurring checks"),
16522
+ cursor: exports_external.string().optional().describe("compact reads only: cursor from the previous compact response; if context is unchanged the response collapses to a tiny unchanged envelope"),
16523
+ recentLimit: exports_external.number().int().min(1).max(20).optional().describe("compact reads only: latest contributions to include (default 6, max 20)"),
16524
+ contribLimit: exports_external.number().int().positive().optional().describe("full reads only: max contributions to return (AD-151 keyset paging) — omit to load the whole thread"),
16525
+ contribCursor: exports_external.string().optional().describe("full reads only: continuation cursor from a previous page's contributionPage.nextCursor")
16519
16526
  },
16520
16527
  surfaces: ["mcp", "cli"],
16521
16528
  toolset: "loop",
@@ -16741,16 +16748,16 @@ var ACTIONS = [
16741
16748
  run: forward("space.create")
16742
16749
  },
16743
16750
  {
16744
- name: "update_space",
16751
+ name: "space_update",
16745
16752
  title: "Edit a space's config",
16746
- description: "Edit a space's PURPOSE, title, or visibility — space config, distinct from its soft `guidance` (the `charter` tool's space_guidance verb). ORG OWNER/ADMIN or the space's own admin only (a non-admin gets a clean 403). Patches ONLY the fields you pass. Reach for this to fix a stub or stale purpose so `space_get` reads true, or to flip a space open ⇄ private.",
16753
+ description: "Edit a space's PURPOSE, title, or visibility — the upsert-config tool for a space, distinct from its soft `guidance` (the `charter` tool's space_guidance verb). ORG OWNER/ADMIN or the space's own admin only (a non-admin gets a clean 403). Patches ONLY the fields you pass. Reach for this to fix a stub or stale purpose so `space_get` reads true, or to flip a space open ⇄ private.",
16747
16754
  inputSchema: {
16748
16755
  spaceId: exports_external.string().describe("the space, spc_…"),
16749
16756
  purpose: exports_external.string().optional().describe("one-line purpose — what this space is about"),
16750
16757
  title: exports_external.string().optional().describe("a new title for the space"),
16751
16758
  visibility: exports_external.enum(["open", "private"]).optional().describe("open or private (invite-only)")
16752
16759
  },
16753
- surfaces: ["cli"],
16760
+ surfaces: ["mcp", "cli"],
16754
16761
  toolset: "spaces",
16755
16762
  run: forward("space.update")
16756
16763
  },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lambdacurry/arbor",
3
- "version": "0.8.5",
3
+ "version": "0.8.7",
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",