@lambdacurry/arbor 0.6.6 → 0.7.1
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 +116 -27
- package/package.json +1 -1
package/dist/arbor.js
CHANGED
|
@@ -1345,6 +1345,7 @@ var profiles = sqliteTable("profiles", {
|
|
|
1345
1345
|
color: text("color").$type(),
|
|
1346
1346
|
emoji: text("emoji"),
|
|
1347
1347
|
description: text("description"),
|
|
1348
|
+
notificationPrefs: text("notification_prefs", { mode: "json" }).$type(),
|
|
1348
1349
|
createdAt: ts("created_at").notNull()
|
|
1349
1350
|
}, (t) => ({ orgIdx: index("profiles_org_idx").on(t.orgId) }));
|
|
1350
1351
|
var clients = sqliteTable("clients", {
|
|
@@ -1560,12 +1561,28 @@ var artifacts = sqliteTable("artifacts", {
|
|
|
1560
1561
|
supersedes: text("supersedes", { mode: "json" }).$type(),
|
|
1561
1562
|
supersededBy: text("superseded_by", { mode: "json" }).$type(),
|
|
1562
1563
|
reviewAfter: ts("review_after"),
|
|
1563
|
-
createdAt: ts("created_at").notNull()
|
|
1564
|
+
createdAt: ts("created_at").notNull(),
|
|
1565
|
+
kind: text("kind").$type().notNull().default("file"),
|
|
1566
|
+
source: text("source"),
|
|
1567
|
+
version: integer("version").notNull().default(0),
|
|
1568
|
+
editPolicy: text("edit_policy").$type().notNull().default("members")
|
|
1564
1569
|
}, (t) => ({
|
|
1565
1570
|
statusIdx: index("artifacts_status_idx").on(t.status),
|
|
1566
1571
|
tierIdx: index("artifacts_tier_idx").on(t.retrievalTier),
|
|
1567
1572
|
ownerIdx: index("artifacts_owner_idx").on(t.ownerProfileId)
|
|
1568
1573
|
}));
|
|
1574
|
+
var artifactVersions = sqliteTable("artifact_versions", {
|
|
1575
|
+
id: text("id").primaryKey(),
|
|
1576
|
+
artifactId: text("artifact_id").notNull().references(() => artifacts.id),
|
|
1577
|
+
version: integer("version").notNull(),
|
|
1578
|
+
source: text("source").notNull(),
|
|
1579
|
+
editorProfileId: text("editor_profile_id").notNull().references(() => profiles.id),
|
|
1580
|
+
executionContextId: text("execution_context_id").references(() => executionContexts.id),
|
|
1581
|
+
note: text("note"),
|
|
1582
|
+
createdAt: ts("created_at").notNull()
|
|
1583
|
+
}, (t) => ({
|
|
1584
|
+
artifactVersionUq: uniqueIndex("artifact_versions_artifact_version_uq").on(t.artifactId, t.version)
|
|
1585
|
+
}));
|
|
1569
1586
|
var requests = sqliteTable("requests", {
|
|
1570
1587
|
id: text("id").primaryKey(),
|
|
1571
1588
|
threadId: text("thread_id").notNull().references(() => threads.id),
|
|
@@ -1624,9 +1641,11 @@ var notifications = sqliteTable("notifications", {
|
|
|
1624
1641
|
groupKey: text("group_key"),
|
|
1625
1642
|
actors: text("actors", { mode: "json" }).$type().notNull().default([]),
|
|
1626
1643
|
readAt: ts("read_at"),
|
|
1644
|
+
emailedAt: ts("emailed_at"),
|
|
1627
1645
|
createdAt: ts("created_at").notNull()
|
|
1628
1646
|
}, (t) => ({
|
|
1629
1647
|
recipientIdx: index("notifications_recipient_idx").on(t.recipientProfileId, t.readAt),
|
|
1648
|
+
emailIdx: index("notifications_emailed_idx").on(t.emailedAt),
|
|
1630
1649
|
groupIdx: uniqueIndex("notifications_group_key_uniq").on(t.groupKey)
|
|
1631
1650
|
}));
|
|
1632
1651
|
var notificationCursors = sqliteTable("notification_cursors", {
|
|
@@ -1729,6 +1748,7 @@ var INDEX_ON = new Set([
|
|
|
1729
1748
|
"contribution.added",
|
|
1730
1749
|
"contribution.edited",
|
|
1731
1750
|
"artifact.created",
|
|
1751
|
+
"artifact.edited",
|
|
1732
1752
|
"review.added",
|
|
1733
1753
|
"thread.created",
|
|
1734
1754
|
"topic.created"
|
|
@@ -16012,24 +16032,27 @@ config(en_default());
|
|
|
16012
16032
|
// ../actions/src/index.ts
|
|
16013
16033
|
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:
|
|
16014
16034
|
|
|
16015
|
-
1. RECALL FIRST. 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. WHY: a room where everyone re-derives is just a chat log.
|
|
16016
|
-
2. CONTRIBUTE typed points — ONE point per contribution, 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 —
|
|
16035
|
+
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.
|
|
16036
|
+
2. CONTRIBUTE typed points — ONE point per contribution, 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: one typed point is reviewable on its own, and 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.
|
|
16017
16037
|
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).
|
|
16018
|
-
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️). WHY: promotion is curation, not applause — and it's about an OUTPUT, never a whole thread (a thread RESOLVES; it is never "promoted").
|
|
16038
|
+
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").
|
|
16019
16039
|
5. EDIT, don't repost. \`edit\` your own contribution to sharpen it; the record stays audited. A genuinely different point is a NEW contribution.
|
|
16020
|
-
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 a thread reaches its conclusion, \`transition_thread --to resolved\`; opened in the wrong place → \`--to archived\` and reopen where it belongs. 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.
|
|
16040
|
+
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 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.
|
|
16041
|
+
7. ACT, THEN REPORT — escalate by exception (AD-197). Before routing a decision to a human, do the CHEAP CHECK that would let you act yourself — the registry lookup, the live-state read, the policy check; the blocker is usually fictional. Then: recoverable + conclusive evidence → act and say why. Recoverable but uncertain → state your intent WITH a deadline ("resolving Thursday unless someone objects") and act at the deadline. Irreversible, outward-facing, or a genuine taste/priority call → escalate; that's what humans are for. Reversibility is the test you can always answer in the moment — a wrong resolve costs one click to undo (\`resolved\` → \`active\`), while waiting costs days. WHY: a room where every met condition waits for a human keystroke makes the human the bottleneck and the agents decorative at exactly the moments they're most useful.
|
|
16042
|
+
|
|
16043
|
+
8. DURABLE OUTPUT IS AN ARTIFACT, NOT A LONG CONTRIBUTION (AD-201). A contribution is one point in a conversation; an ARTIFACT is a first-class output the room holds and keeps CURRENT — a doc, a table, a diagram, a spec, or an uploaded file. When your point is really a deliverable the team will read or UPDATE later — a table of options, a reference doc, a plan, a rubric — make it an artifact (\`artifact\` create with kind doc/table/diagram/html + \`source\`, or promote a file) instead of pasting it into prose. Text-kind artifacts are LIVING: \`get\` it, then \`edit\` naming the \`baseVersion\` you read — patches apply exact-match and atomically, one version per call, every version attributed. An artifact created FROM a thread is listed on that thread automatically (no link post needed); cite it inline with [label](#art_…) wherever it's relevant. WHY: a thread remembers what was SAID; an artifact holds what is TRUE NOW — a 40-row table pasted into a contribution is frozen the instant it's posted and unreadable the moment it's edited, while the same table as an artifact stays live, diffable, and findable by its content.
|
|
16021
16044
|
|
|
16022
16045
|
Run \`arbor help\` (or read the MCP tool list) for the exact command/flags — those stay generated from the live action surface, so they're always current.`;
|
|
16023
16046
|
function forward(operation) {
|
|
16024
16047
|
return (ex, input) => ex.call(operation, input);
|
|
16025
16048
|
}
|
|
16026
|
-
function dispatch(map2) {
|
|
16049
|
+
function dispatch(map2, reshape) {
|
|
16027
16050
|
return (ex, input) => {
|
|
16028
16051
|
const { verb, ...rest } = input;
|
|
16029
16052
|
const op = verb ? map2[verb] : undefined;
|
|
16030
16053
|
if (!op)
|
|
16031
16054
|
return Promise.reject(new Error(`unknown verb "${verb ?? ""}" — expected one of: ${Object.keys(map2).join(" | ")}`));
|
|
16032
|
-
return ex.call(op, rest);
|
|
16055
|
+
return ex.call(op, reshape && verb ? reshape(verb, rest) : rest);
|
|
16033
16056
|
};
|
|
16034
16057
|
}
|
|
16035
16058
|
var PAGINATION_INPUT = {
|
|
@@ -16056,7 +16079,7 @@ var ACTIONS = [
|
|
|
16056
16079
|
{
|
|
16057
16080
|
name: "contribute",
|
|
16058
16081
|
title: "Contribute to a thread",
|
|
16059
|
-
description: "Add your own typed contribution (proposal/critique/question/evidence/…) — ONE point per contribution, on the record. 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.",
|
|
16082
|
+
description: "Add your own typed contribution (proposal/critique/question/evidence/…) — ONE point per contribution, on the record. 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. To thread your contribution UNDER a specific one (a reply), pass links: [{rel: 'inReplyTo', targetId}] — that deliberate edge is what nests it.",
|
|
16060
16083
|
inputSchema: {
|
|
16061
16084
|
threadId: exports_external.string().describe("the thread id, e.g. thr_…"),
|
|
16062
16085
|
type: exports_external.enum(CONTRIBUTION_TYPES).describe("the contribution type (AD-066 taxonomy)"),
|
|
@@ -16068,7 +16091,7 @@ var ACTIONS = [
|
|
|
16068
16091
|
id: exports_external.string(),
|
|
16069
16092
|
label: exports_external.string()
|
|
16070
16093
|
})).optional().describe("inline @-mentions in the body; person mentions notify"),
|
|
16071
|
-
links: exports_external.array(exports_external.object({ rel: exports_external.enum(CONTRIBUTION_LINK_RELS), targetId: exports_external.string() })).optional().describe("internal typed links: { rel, targetId } (same-space). External refs go IN THE BODY as URLs.")
|
|
16094
|
+
links: exports_external.array(exports_external.object({ rel: exports_external.enum(CONTRIBUTION_LINK_RELS), targetId: exports_external.string() })).optional().describe("internal typed links: { rel, targetId } (same-space) — a DELIBERATE edge: inReplyTo/contests nests your contribution under the target (AD-196); body refs never do. External refs go IN THE BODY as URLs.")
|
|
16072
16095
|
},
|
|
16073
16096
|
surfaces: ["mcp", "cli"],
|
|
16074
16097
|
toolset: "loop",
|
|
@@ -16077,7 +16100,7 @@ var ACTIONS = [
|
|
|
16077
16100
|
{
|
|
16078
16101
|
name: "edit",
|
|
16079
16102
|
title: "Edit your contribution",
|
|
16080
|
-
description: "Edit the BODY of your own contribution (AD-132 — editable-but-audited: the displayed text changes, an 'edited' marker appears, and the event log keeps the previous body). Author-only; the type and links are fixed — a different point is a NEW contribution, not an edit.",
|
|
16103
|
+
description: "Edit the BODY of your own contribution (AD-132 — editable-but-audited: the displayed text changes, an 'edited' marker appears, and the event log keeps the previous body). Author-only; the type and DELIBERATE links (reply edges, fulfills) are fixed — body-borne prose refs follow the edited body: a [label](#con_…) you edit in gains its citation edge, one you edit out drops it (AD-196). A different point is a NEW contribution, not an edit.",
|
|
16081
16104
|
inputSchema: {
|
|
16082
16105
|
contributionId: exports_external.string().describe("your contribution's id, con_…"),
|
|
16083
16106
|
body: exports_external.string().min(1).describe("the replacement markdown body"),
|
|
@@ -16173,10 +16196,10 @@ var ACTIONS = [
|
|
|
16173
16196
|
{
|
|
16174
16197
|
name: "transition_thread",
|
|
16175
16198
|
title: "Move a thread's lifecycle",
|
|
16176
|
-
description: "Move a thread along its lifecycle (AD-068/159): `resolved` = the deliberation reached its conclusion; `archived` = retired / wrong place (both recoverable to `active`); `stuck`/`needs-review` flag an open thread. Threads RESOLVE — standout outputs get promoted via `curation`; there is no promote-thread. Illegal jumps are rejected",
|
|
16199
|
+
description: "Move a thread along its lifecycle (AD-068/159/200): `resolved` = the deliberation reached its conclusion; `archived` = retired / wrong place (both recoverable to `active`); `stuck`/`needs-review` flag an open thread; `standing` = DESIGNED to stay open (a journal, ops log, or standing lane — never owed a conclusion; distill with promotions instead of closing). Threads RESOLVE — standout outputs get promoted via `curation`; there is no promote-thread. Illegal jumps are rejected",
|
|
16177
16200
|
inputSchema: {
|
|
16178
16201
|
threadId: exports_external.string().describe("the thread to move, thr_…"),
|
|
16179
|
-
to: exports_external.string().describe("target status: active|needs-review|stuck|resolved|archived"),
|
|
16202
|
+
to: exports_external.string().describe("target status: active|needs-review|stuck|standing|resolved|archived"),
|
|
16180
16203
|
reason: exports_external.string().optional().describe("optional one-line why, recorded on the event")
|
|
16181
16204
|
},
|
|
16182
16205
|
surfaces: ["mcp", "cli"],
|
|
@@ -16186,11 +16209,14 @@ var ACTIONS = [
|
|
|
16186
16209
|
{
|
|
16187
16210
|
name: "create_artifact",
|
|
16188
16211
|
title: "Create an artifact",
|
|
16189
|
-
description: "Promote durable output — a decision, summary, plan, or research brief — into a first-class artifact you own. Reach for this when a thread has produced something worth retaining and citing beyond the conversation
|
|
16212
|
+
description: "Promote durable output — a decision, summary, plan, or research brief — into a first-class artifact you own. Text kinds (doc/table/diagram/html) are living documents: pass `source` (born at v1, editable in place via edit_artifact); kind=file passes a contentRef instead. The artifact is AUTOMATICALLY listed on its source thread (no link post needed). Reach for this when a thread has produced something worth retaining and citing beyond the conversation — especially a table or doc the team will UPDATE, which beats pasting it into a prose contribution.",
|
|
16190
16213
|
inputSchema: {
|
|
16191
|
-
type: exports_external.string().describe("
|
|
16214
|
+
type: exports_external.string().describe("semantic type, e.g. decision|summary|plan|markdown|table|research-brief"),
|
|
16215
|
+
kind: exports_external.enum(["doc", "table", "diagram", "html", "file"]).optional().describe("structural kind (default file). doc/table/diagram/html are editable, versioned text"),
|
|
16192
16216
|
title: exports_external.string().min(1),
|
|
16193
|
-
|
|
16217
|
+
source: exports_external.string().optional().describe("text kinds: the initial content (v1)"),
|
|
16218
|
+
contentRef: exports_external.string().optional().describe("kind=file: where the content lives, e.g. inline://… or a URL"),
|
|
16219
|
+
editPolicy: exports_external.enum(["owner", "members", "anyone"]).optional().describe("who may edit (default members)"),
|
|
16194
16220
|
summary: exports_external.string().optional(),
|
|
16195
16221
|
sourceThreadId: exports_external.string().optional(),
|
|
16196
16222
|
sourceContributionIds: exports_external.array(exports_external.string()).optional()
|
|
@@ -16199,6 +16225,36 @@ var ACTIONS = [
|
|
|
16199
16225
|
toolset: "artifacts",
|
|
16200
16226
|
run: forward("artifact.create")
|
|
16201
16227
|
},
|
|
16228
|
+
{
|
|
16229
|
+
name: "get_artifact",
|
|
16230
|
+
title: "Read an artifact",
|
|
16231
|
+
description: "Read an artifact: current source + version + history (who edited, via what). READ BEFORE YOU EDIT — the version you read is the baseVersion your edit must name. Pass --version to read an old snapshot.",
|
|
16232
|
+
inputSchema: {
|
|
16233
|
+
artifactId: exports_external.string().describe("the artifact, art_…"),
|
|
16234
|
+
version: exports_external.number().optional().describe("read this old snapshot instead of the current")
|
|
16235
|
+
},
|
|
16236
|
+
surfaces: ["cli"],
|
|
16237
|
+
toolset: "artifacts",
|
|
16238
|
+
run: forward("artifact.get")
|
|
16239
|
+
},
|
|
16240
|
+
{
|
|
16241
|
+
name: "edit_artifact",
|
|
16242
|
+
title: "Edit a text artifact",
|
|
16243
|
+
description: `Edit a text-backed artifact (doc/table/diagram/html) in place. Read it first (get_artifact) and pass baseVersion = the version you read. Either --source (full replace) or --patches '[{"old":…,"new":…}]' (exact-match hunks, applied in order, ATOMIC — all land or none; one call mints ONE version, so batch related row-edits into one call). A stale baseVersion returns ok:false with the current source — reconcile and retry; a failed patch names the culprit index.`,
|
|
16244
|
+
inputSchema: {
|
|
16245
|
+
artifactId: exports_external.string().describe("the artifact, art_…"),
|
|
16246
|
+
baseVersion: exports_external.number().describe("the version you READ (get_artifact) — stale is rejected"),
|
|
16247
|
+
source: exports_external.string().optional().describe("full replace: the complete new content"),
|
|
16248
|
+
patches: exports_external.array(exports_external.object({ old: exports_external.string(), new: exports_external.string() })).optional().describe("exact-match hunks applied in order; each old must match exactly once"),
|
|
16249
|
+
note: exports_external.string().optional().describe("one-line summary of what changed")
|
|
16250
|
+
},
|
|
16251
|
+
surfaces: ["cli"],
|
|
16252
|
+
toolset: "artifacts",
|
|
16253
|
+
run: (ex, input) => ex.call("artifact.edit", {
|
|
16254
|
+
...input,
|
|
16255
|
+
verb: Array.isArray(input.patches) ? "patch" : "replace"
|
|
16256
|
+
})
|
|
16257
|
+
},
|
|
16202
16258
|
{
|
|
16203
16259
|
name: "transition_artifact",
|
|
16204
16260
|
title: "Transition an artifact's lifecycle",
|
|
@@ -16227,7 +16283,7 @@ var ACTIONS = [
|
|
|
16227
16283
|
{
|
|
16228
16284
|
name: "promote_contribution",
|
|
16229
16285
|
title: "Promote a contribution to a \uD83C\uDF96️ standout",
|
|
16230
|
-
description: "Mark a contribution a \uD83C\uDF96️ standout — recognition that boosts it in recall and flags it as one of the thread's outputs (AD-140: promotion is a MARK on the contribution, not a separate artifact). Reach for this when a contribution is worth recognizing. MANY contributions in a thread can be standouts (there's no single 'answer'); idempotent — re-promoting is a quiet no-op.",
|
|
16286
|
+
description: "Mark a contribution a \uD83C\uDF96️ standout — recognition that boosts it in recall and flags it as one of the thread's outputs (AD-140: promotion is a MARK on the contribution, not a separate artifact). Reach for this when a contribution is worth recognizing — and routinely in a STANDING thread, where promoting the periodic distilling synthesis is the lane's compression mechanism (AD-200). MANY contributions in a thread can be standouts (there's no single 'answer'); idempotent — re-promoting is a quiet no-op.",
|
|
16231
16287
|
inputSchema: {
|
|
16232
16288
|
contributionId: exports_external.string().describe("the contribution to promote, con_… (its thread + author are derived)")
|
|
16233
16289
|
},
|
|
@@ -16414,7 +16470,7 @@ var ACTIONS = [
|
|
|
16414
16470
|
{
|
|
16415
16471
|
name: "thread_get",
|
|
16416
16472
|
title: "Read a thread",
|
|
16417
|
-
description: "Read the full state of the thread you're working in — objective, every contribution + stamp, and its
|
|
16473
|
+
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.",
|
|
16418
16474
|
inputSchema: {
|
|
16419
16475
|
threadId: exports_external.string().describe("the thread id, e.g. thr_…"),
|
|
16420
16476
|
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"),
|
|
@@ -16427,7 +16483,7 @@ var ACTIONS = [
|
|
|
16427
16483
|
{
|
|
16428
16484
|
name: "tree",
|
|
16429
16485
|
title: "Navigate the workspace tree",
|
|
16430
|
-
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
|
|
16486
|
+
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',
|
|
16431
16487
|
inputSchema: {
|
|
16432
16488
|
space: exports_external.string().optional().describe("scope to one space id (spc_…)"),
|
|
16433
16489
|
topic: exports_external.string().optional().describe("scope to one topic id (top_…)"),
|
|
@@ -16823,7 +16879,7 @@ var ACTIONS = [
|
|
|
16823
16879
|
{
|
|
16824
16880
|
name: "curation",
|
|
16825
16881
|
title: "Curate the record",
|
|
16826
|
-
description: "Curation moves on a contribution (all take contributionId). verb=promote (mark it a \uD83C\uDF96️ standout — sparingly: promotion is curation, not applause, AD-140), demote (remove the mark), delete (soft-remove), restore (bring a removed one back).",
|
|
16882
|
+
description: "Curation moves on a contribution (all take contributionId). verb=promote (mark it a \uD83C\uDF96️ standout — sparingly in deliberations: promotion is curation, not applause, AD-140. In a STANDING thread the calculus flips: promoting a periodic distilling synthesis is the lane's compression mechanism, AD-200 — do it, including on your own synthesis), demote (remove the mark), delete (soft-remove), restore (bring a removed one back).",
|
|
16827
16883
|
inputSchema: {
|
|
16828
16884
|
verb: exports_external.enum(["promote", "demote", "delete", "restore"]).describe("which curation move"),
|
|
16829
16885
|
contributionId: exports_external.string().describe("the contribution, con_…")
|
|
@@ -16840,15 +16896,23 @@ var ACTIONS = [
|
|
|
16840
16896
|
{
|
|
16841
16897
|
name: "artifact",
|
|
16842
16898
|
title: "Durable artifacts",
|
|
16843
|
-
description: "Durable,
|
|
16899
|
+
description: "Durable, versioned artifacts — the team's living documents (AD-201). Kinds: doc (markdown), table (CSV), diagram (Mermaid), html — all versioned text you edit in place — plus file (a contentRef: URL/inline/blob). " + "verb=create (kind+type+title, source for text kinds / contentRef for file; a text artifact is born at v1 and is AUTOMATICALLY listed on its sourceThreadId thread — no link post needed). " + "verb=get (artifactId — the current source + version + history; pass version to read an old snapshot). " + "verb=edit — READ FIRST via get, then pass baseVersion (the version you read): either source (full replace) or patches [{old,new},…] (exact-match hunks, applied in order, ATOMIC — all land or none, and one call mints ONE version, so batch related row-edits into one call). A stale baseVersion returns {ok:false, currentSource} — reconcile and retry against the returned source; a failed patch names the culprit index. " + "verb=transition (artifactId + to: active|promoted|needs-review|superseded|deprecated|archived|deleted), promote_attachment (attachmentId — lift an uploaded file into an artifact).",
|
|
16844
16900
|
inputSchema: {
|
|
16845
|
-
verb: exports_external.enum(["create", "transition", "promote_attachment"]).describe("which artifact move"),
|
|
16901
|
+
verb: exports_external.enum(["create", "get", "edit", "transition", "promote_attachment"]).describe("which artifact move"),
|
|
16846
16902
|
title: exports_external.string().optional().describe("create: the artifact's title"),
|
|
16847
|
-
|
|
16903
|
+
kind: exports_external.enum(["doc", "table", "diagram", "html", "file"]).optional().describe("create: structural kind (default file). doc/table/diagram/html are editable text"),
|
|
16904
|
+
type: exports_external.string().optional().describe("create: semantic type, e.g. decision|summary|plan|markdown|table|research-brief"),
|
|
16905
|
+
source: exports_external.string().optional().describe("create (text kinds): the initial content · edit verb=replace: the full new content"),
|
|
16906
|
+
contentRef: exports_external.string().optional().describe("create (kind=file): where the content lives, e.g. inline://… or a URL"),
|
|
16907
|
+
editPolicy: exports_external.enum(["owner", "members", "anyone"]).optional().describe("create: who may edit (default members)"),
|
|
16848
16908
|
summary: exports_external.string().optional().describe("create: a short summary"),
|
|
16849
16909
|
sourceThreadId: exports_external.string().optional().describe("create: the thread it came from"),
|
|
16850
16910
|
sourceContributionIds: exports_external.array(exports_external.string()).optional().describe("create: contributions it distills"),
|
|
16851
|
-
artifactId: exports_external.string().optional().describe("transition: the artifact, art_…"),
|
|
16911
|
+
artifactId: exports_external.string().optional().describe("get/edit/transition: the artifact, art_…"),
|
|
16912
|
+
version: exports_external.number().optional().describe("get: read this old snapshot instead of the current"),
|
|
16913
|
+
baseVersion: exports_external.number().optional().describe("edit: the version you READ (from get/create) — stale base is rejected with the current source"),
|
|
16914
|
+
patches: exports_external.array(exports_external.object({ old: exports_external.string(), new: exports_external.string() })).optional().describe("edit: exact-match hunks applied in order, atomically; each old must match exactly once"),
|
|
16915
|
+
note: exports_external.string().optional().describe("edit: one-line summary of what changed"),
|
|
16852
16916
|
to: exports_external.string().optional().describe("transition: the target status"),
|
|
16853
16917
|
reason: exports_external.string().optional().describe("transition: why"),
|
|
16854
16918
|
attachmentId: exports_external.string().optional().describe("promote_attachment: the upload, att_…")
|
|
@@ -16857,9 +16921,11 @@ var ACTIONS = [
|
|
|
16857
16921
|
toolset: "artifacts",
|
|
16858
16922
|
run: dispatch({
|
|
16859
16923
|
create: "artifact.create",
|
|
16924
|
+
get: "artifact.get",
|
|
16925
|
+
edit: "artifact.edit",
|
|
16860
16926
|
transition: "artifact.transition",
|
|
16861
16927
|
promote_attachment: "attachment.promote"
|
|
16862
|
-
})
|
|
16928
|
+
}, (verb, input) => verb === "edit" ? { ...input, verb: Array.isArray(input.patches) ? "patch" : "replace" } : input)
|
|
16863
16929
|
},
|
|
16864
16930
|
{
|
|
16865
16931
|
name: "charter",
|
|
@@ -16984,6 +17050,25 @@ class UsageError extends Error {
|
|
|
16984
17050
|
}
|
|
16985
17051
|
}
|
|
16986
17052
|
|
|
17053
|
+
// src/source.ts
|
|
17054
|
+
var SNIFF = [
|
|
17055
|
+
["CLAUDECODE", "Claude Code"],
|
|
17056
|
+
["CLAUDE_CODE_ENTRYPOINT", "Claude Code"],
|
|
17057
|
+
["OPENCLAW_HOME", "openclaw"],
|
|
17058
|
+
["CODEX_SANDBOX", "Codex"],
|
|
17059
|
+
["CURSOR_TRACE_ID", "Cursor"]
|
|
17060
|
+
];
|
|
17061
|
+
function detectSource(env = process.env) {
|
|
17062
|
+
const explicit = env.ARBOR_SOURCE?.trim();
|
|
17063
|
+
if (explicit)
|
|
17064
|
+
return explicit;
|
|
17065
|
+
for (const [envVar, tool] of SNIFF) {
|
|
17066
|
+
if (env[envVar]?.trim())
|
|
17067
|
+
return tool;
|
|
17068
|
+
}
|
|
17069
|
+
return;
|
|
17070
|
+
}
|
|
17071
|
+
|
|
16987
17072
|
// src/client.ts
|
|
16988
17073
|
class NotLoggedInError extends Error {
|
|
16989
17074
|
code = "UNAUTHORIZED";
|
|
@@ -16998,6 +17083,7 @@ function authHeader() {
|
|
|
16998
17083
|
throw new NotLoggedInError;
|
|
16999
17084
|
return { authorization: `Bearer ${cfg.token}`, apiUrl: cfg.apiUrl };
|
|
17000
17085
|
}
|
|
17086
|
+
var source = detectSource();
|
|
17001
17087
|
var httpExecutor = {
|
|
17002
17088
|
async call(name, input) {
|
|
17003
17089
|
const { authorization, apiUrl } = authHeader();
|
|
@@ -17008,7 +17094,11 @@ var httpExecutor = {
|
|
|
17008
17094
|
try {
|
|
17009
17095
|
res = await fetch(`${apiUrl}/api`, {
|
|
17010
17096
|
method: "POST",
|
|
17011
|
-
headers: {
|
|
17097
|
+
headers: {
|
|
17098
|
+
"content-type": "application/json",
|
|
17099
|
+
authorization,
|
|
17100
|
+
...source ? { "x-arbor-source": source } : {}
|
|
17101
|
+
},
|
|
17012
17102
|
body: JSON.stringify({ object: object2, verb, input })
|
|
17013
17103
|
});
|
|
17014
17104
|
} catch (cause) {
|
|
@@ -17030,7 +17120,6 @@ var GLOBAL_BOOLEAN_FLAGS = new Set([
|
|
|
17030
17120
|
"json",
|
|
17031
17121
|
"quiet",
|
|
17032
17122
|
"no-quiet",
|
|
17033
|
-
"version",
|
|
17034
17123
|
"help"
|
|
17035
17124
|
]);
|
|
17036
17125
|
var RESERVED_FLAGS = new Set([
|
|
@@ -17634,7 +17723,7 @@ async function main() {
|
|
|
17634
17723
|
const ctx = resolveOutput(flags);
|
|
17635
17724
|
const [cmd] = positionals;
|
|
17636
17725
|
try {
|
|
17637
|
-
if (
|
|
17726
|
+
if (flags.version !== undefined && positionals.length === 0 || cmd === "version") {
|
|
17638
17727
|
renderVersion(ctx);
|
|
17639
17728
|
return;
|
|
17640
17729
|
}
|
package/package.json
CHANGED