@lambdacurry/arbor 0.21.58 → 0.21.60
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 +97 -3
- package/package.json +1 -1
package/dist/arbor.js
CHANGED
|
@@ -16,7 +16,7 @@ var __export = (target, all) => {
|
|
|
16
16
|
// package.json
|
|
17
17
|
var package_default = {
|
|
18
18
|
name: "@lambdacurry/arbor",
|
|
19
|
-
version: "0.21.
|
|
19
|
+
version: "0.21.60",
|
|
20
20
|
description: "The Arbor CLI — a shared workspace for people and agents. The human + headless-agent write path over Arbor's guarded operation surface.",
|
|
21
21
|
keywords: [
|
|
22
22
|
"agents",
|
|
@@ -17144,6 +17144,16 @@ var watches = sqliteTable("watches", {
|
|
|
17144
17144
|
watchUq: uniqueIndex("watches_watcher_target_uniq").on(t.watcherProfileId, t.targetType, t.targetId),
|
|
17145
17145
|
watchTargetIdx: index("watches_target_idx").on(t.targetType, t.targetId)
|
|
17146
17146
|
}));
|
|
17147
|
+
var threadAssignments = sqliteTable("thread_assignments", {
|
|
17148
|
+
id: text("id").primaryKey(),
|
|
17149
|
+
threadId: text("thread_id").notNull().references(() => threads.id),
|
|
17150
|
+
profileId: text("profile_id").notNull().references(() => profiles.id),
|
|
17151
|
+
assignedByProfileId: text("assigned_by_profile_id").notNull().references(() => profiles.id),
|
|
17152
|
+
createdAt: ts("created_at").notNull()
|
|
17153
|
+
}, (t) => ({
|
|
17154
|
+
assigneeUq: uniqueIndex("thread_assignments_thread_profile_uniq").on(t.threadId, t.profileId),
|
|
17155
|
+
profileIdx: index("thread_assignments_profile_idx").on(t.profileId, t.createdAt)
|
|
17156
|
+
}));
|
|
17147
17157
|
// ../core/src/ops/secret.ts
|
|
17148
17158
|
var SECRET_PERMISSIONS = [
|
|
17149
17159
|
"create",
|
|
@@ -17206,6 +17216,17 @@ var EMOJI_RE = new RegExp("^\\p{RGI_Emoji}$", "v");
|
|
|
17206
17216
|
var BASE_MS = Date.parse("2026-07-20T15:00:00.000Z");
|
|
17207
17217
|
// ../core/src/ops/agent-pairing.ts
|
|
17208
17218
|
var PAIRING_TTL_MS = 15 * 60 * 1000;
|
|
17219
|
+
// ../core/src/queries/thread-assignment.ts
|
|
17220
|
+
var assigneeColumns = {
|
|
17221
|
+
threadId: threadAssignments.threadId,
|
|
17222
|
+
assignedAt: threadAssignments.createdAt,
|
|
17223
|
+
assignedByProfileId: threadAssignments.assignedByProfileId,
|
|
17224
|
+
id: profiles.id,
|
|
17225
|
+
kind: profiles.kind,
|
|
17226
|
+
displayName: profiles.displayName,
|
|
17227
|
+
color: profiles.color,
|
|
17228
|
+
emoji: profiles.emoji
|
|
17229
|
+
};
|
|
17209
17230
|
// ../core/src/ops/computer-recovery.ts
|
|
17210
17231
|
var COMPUTER_RECOVERY_GRANT_MAX_TTL_MS = 24 * 60 * 60 * 1000;
|
|
17211
17232
|
// ../core/src/ops/preview.ts
|
|
@@ -17791,6 +17812,10 @@ var threadAuthor = exports_external.looseObject({
|
|
|
17791
17812
|
color: nullableString,
|
|
17792
17813
|
emoji: nullableString
|
|
17793
17814
|
});
|
|
17815
|
+
var threadAssignee = threadAuthor.extend({
|
|
17816
|
+
assignedAt: exports_external.string(),
|
|
17817
|
+
assignedByProfileId: id
|
|
17818
|
+
});
|
|
17794
17819
|
var threadContribution = exports_external.looseObject({
|
|
17795
17820
|
id,
|
|
17796
17821
|
computerSessionId: nullableString,
|
|
@@ -18161,6 +18186,7 @@ var MCP_OUTPUT_SCHEMAS = {
|
|
|
18161
18186
|
openRequests: exports_external.array(threadOpenRequest).optional(),
|
|
18162
18187
|
scheduledRequests: exports_external.array(threadScheduledRequest).optional(),
|
|
18163
18188
|
participants: exports_external.array(threadAuthor).optional(),
|
|
18189
|
+
assignees: exports_external.array(threadAssignee).optional(),
|
|
18164
18190
|
artifacts: exports_external.array(artifact4).optional(),
|
|
18165
18191
|
initiatives: exports_external.array(initiative3).optional(),
|
|
18166
18192
|
distillate: exports_external.array(jsonObject).optional(),
|
|
@@ -18168,6 +18194,33 @@ var MCP_OUTPUT_SCHEMAS = {
|
|
|
18168
18194
|
cursor: exports_external.string().optional(),
|
|
18169
18195
|
unchanged: exports_external.boolean().optional()
|
|
18170
18196
|
}),
|
|
18197
|
+
thread_assign: exports_external.looseObject({
|
|
18198
|
+
threadId: id,
|
|
18199
|
+
profileId: id,
|
|
18200
|
+
assigned: exports_external.boolean(),
|
|
18201
|
+
created: exports_external.boolean().optional(),
|
|
18202
|
+
removed: exports_external.boolean().optional(),
|
|
18203
|
+
assignees: exports_external.array(threadAssignee)
|
|
18204
|
+
}),
|
|
18205
|
+
thread_list: exports_external.looseObject({
|
|
18206
|
+
items: exports_external.array(exports_external.looseObject({
|
|
18207
|
+
id,
|
|
18208
|
+
title: exports_external.string(),
|
|
18209
|
+
objective: exports_external.string(),
|
|
18210
|
+
status: exports_external.string(),
|
|
18211
|
+
spaceId: id,
|
|
18212
|
+
spaceTitle: exports_external.string(),
|
|
18213
|
+
topicId: id,
|
|
18214
|
+
topicTitle: exports_external.string(),
|
|
18215
|
+
createdAt: exports_external.string(),
|
|
18216
|
+
lastActivityAt: exports_external.string(),
|
|
18217
|
+
assignees: exports_external.array(threadAssignee),
|
|
18218
|
+
url: exports_external.string().optional()
|
|
18219
|
+
})),
|
|
18220
|
+
nextCursor: nullableString,
|
|
18221
|
+
total: exports_external.number().int().min(0),
|
|
18222
|
+
status: exports_external.string().optional()
|
|
18223
|
+
}),
|
|
18171
18224
|
tree: exports_external.looseObject({
|
|
18172
18225
|
spaces: exports_external.array(treeSpace),
|
|
18173
18226
|
pageUnit: exports_external.enum(["spaces", "topics", "threads"]).optional(),
|
|
@@ -19052,6 +19105,8 @@ var MCP_TOOL_ANNOTATIONS = {
|
|
|
19052
19105
|
topic_update: additive,
|
|
19053
19106
|
create_thread: additive,
|
|
19054
19107
|
thread_update: additive,
|
|
19108
|
+
thread_assign: idempotent,
|
|
19109
|
+
thread_list: readOnly,
|
|
19055
19110
|
set_organization_computer: destructiveIdempotent,
|
|
19056
19111
|
set_space_computer: destructiveIdempotent,
|
|
19057
19112
|
set_topic_computer: destructiveIdempotent,
|
|
@@ -19167,7 +19222,7 @@ Arbor's MCP is the collaboration and durable-knowledge surface. Configure, autho
|
|
|
19167
19222
|
|
|
19168
19223
|
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. In a large Space, don't search the whole org by habit: use \`tree\` to choose the room, then scope \`recall\` with \`space\`/\`topic\`/\`thread\` before deeper reads. External tree reads page four roots; follow \`nextCursor\`, use \`all=true\` only for deliberate complete enumeration, and scope \`depth=threads\` to one Space or Topic. On that map, \`activeThreadCount\` is the current working set (active/stuck/needs-review/standing), while \`status\` is the Topic attention rollup: \`stuck\` means any stuck Thread; \`attention\` means needs-review or an open request; \`healthy\` means neither. 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\`.
|
|
19169
19224
|
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.
|
|
19170
|
-
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).
|
|
19225
|
+
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). Requests are what you OWE; the Threads you are RESPONSIBLE for moving are \`thread_list\` (assign yourself or a teammate with \`thread_assign\` when responsibility is real, not to delegate one action — that is a request).
|
|
19171
19226
|
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").
|
|
19172
19227
|
5. EDIT, don't repost. \`edit\` your own contribution to sharpen it; the record stays audited. A genuinely different point is a NEW contribution.
|
|
19173
19228
|
6. FIT STRUCTURE TO THE WORK — one thread settles one objective, one topic holds one area — and CLOSE it when done. Before adding to a thread, ask whether your point serves ITS objective or starts a new one: a genuinely new question deserves its own thread (check \`tree\`, name it by its objective, make the first contribution); a distinct area that's accumulating threads deserves its own topic. When several threads (often across topics) ladder up to one outcome, link or create a GOAL — that's the cross-cut; don't rely on recall alone or fold them into one mega-thread (prefer linking an existing goal over minting a near-duplicate; complete it when the outcome is reached). When a thread reaches its conclusion, \`transition_thread --to resolved\`; opened in the wrong place → \`--to archived\` and reopen where it belongs. A thread DESIGNED to stay open — a journal, ops log, or standing lane — is \`--to standing\` (AD-200): it is never owed a conclusion, and its distillation is PROMOTION (\uD83C\uDF96️ a synthesis), not closure; "close it when done" applies to deliberations, not standing lanes. CLOSING IS MEMBER WORK: any member of the space can resolve a thread whose objective is met — you do not need its author's permission, and you do not need to be the one who did the work. Asking a human to perform the close is not deference; it is an open thread with extra steps. If the objective is met, close it and say why. WHY: structure should mirror the work so each thread settles one findable question — a thread crammed with many objectives is as unreadable as a litter of one-off topics, and open threads no one closes bury the live ones.
|
|
@@ -19778,6 +19833,45 @@ var ACTION_DEFINITIONS = [
|
|
|
19778
19833
|
toolset: "loop",
|
|
19779
19834
|
run: forward("thread.setConfig")
|
|
19780
19835
|
},
|
|
19836
|
+
{
|
|
19837
|
+
name: "thread_assign",
|
|
19838
|
+
title: "Assign or unassign a thread",
|
|
19839
|
+
description: "Make a Space member (yourself when profileId is omitted) responsible for moving a Thread, or remove that responsibility; assignment is immediate, idempotent, grants no permissions, and creates no Inbox item — a specific owed action is still a `request`. Assignees stay attached through every lifecycle move, so transfer responsibility by assigning the new owner and unassigning the old.",
|
|
19840
|
+
inputSchema: {
|
|
19841
|
+
verb: exports_external.enum(["assign", "unassign"]).describe("add or remove responsibility"),
|
|
19842
|
+
threadId: exports_external.string().describe("the thread, thr_…"),
|
|
19843
|
+
profileId: exports_external.string().optional().describe("the member to (un)assign, prof_…; omit for yourself")
|
|
19844
|
+
},
|
|
19845
|
+
surfaces: ["mcp", "cli"],
|
|
19846
|
+
toolset: "loop",
|
|
19847
|
+
run: dispatch({ assign: "thread.assign", unassign: "thread.unassign" })
|
|
19848
|
+
},
|
|
19849
|
+
{
|
|
19850
|
+
name: "thread_list",
|
|
19851
|
+
title: "List threads by responsibility",
|
|
19852
|
+
description: "List the Threads you are responsible for (default), those assigned to another profile, or the unassigned ones inside one Space or Topic — current work only (active, needs-review, stuck) unless status widens it. Read it to decide what to move forward; `inbox` remains the list of specific actions you owe.",
|
|
19853
|
+
inputSchema: {
|
|
19854
|
+
assignee: exports_external.string().optional().describe("`me` (default) or a profile id, prof_…"),
|
|
19855
|
+
unassigned: exports_external.boolean().optional().describe("threads with no assignee — requires spaceId or topicId"),
|
|
19856
|
+
spaceId: exports_external.string().optional().describe("narrow to one space, spc_…"),
|
|
19857
|
+
topicId: exports_external.string().optional().describe("narrow to one topic, top_…"),
|
|
19858
|
+
status: exports_external.enum([
|
|
19859
|
+
"current",
|
|
19860
|
+
"all",
|
|
19861
|
+
"active",
|
|
19862
|
+
"needs-review",
|
|
19863
|
+
"stuck",
|
|
19864
|
+
"standing",
|
|
19865
|
+
"resolved",
|
|
19866
|
+
"archived"
|
|
19867
|
+
]).optional().describe("default current = active + needs-review + stuck; all includes standing/resolved/archived"),
|
|
19868
|
+
limit: exports_external.number().int().min(1).max(100).optional().describe("page size (default 20, max 100)"),
|
|
19869
|
+
cursor: exports_external.string().optional().describe("keyset cursor from a prior page's nextCursor")
|
|
19870
|
+
},
|
|
19871
|
+
surfaces: ["mcp", "cli"],
|
|
19872
|
+
toolset: "loop",
|
|
19873
|
+
run: forward("thread.list")
|
|
19874
|
+
},
|
|
19781
19875
|
{
|
|
19782
19876
|
name: "set_organization_computer",
|
|
19783
19877
|
title: "Replace organization computer defaults",
|
|
@@ -20392,7 +20486,7 @@ var ACTION_DEFINITIONS = [
|
|
|
20392
20486
|
{
|
|
20393
20487
|
name: "request",
|
|
20394
20488
|
title: "Request a contribution or review",
|
|
20395
|
-
description: "Request a contribution or review on a Thread: omit aboutContributionId for a contribution answered via respond, or pass it for a review answered only by stamp; target people or leave it open. Choose completion/deadline and optionally opensAt/opensIn
|
|
20489
|
+
description: "Request a contribution or review on a Thread: omit aboutContributionId for a contribution answered via respond, or pass it for a review answered only by stamp; explicitly target yourself for a normal Inbox obligation, target other people, or leave it open. Choose completion/deadline and optionally opensAt/opensIn to keep it dormant until scheduled delivery; fulfill a self-target through respond or use cancel_request to retire it.",
|
|
20396
20490
|
inputSchema: {
|
|
20397
20491
|
threadId: exports_external.string().describe("the thread the request is in, thr_…"),
|
|
20398
20492
|
requestedAction: exports_external.string().optional().describe("a one-line 'what you're requesting'"),
|
package/package.json
CHANGED