@orboto/mcp 0.192.0 → 0.194.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.
- package/dist/event-bridge.js +1 -47
- package/dist/event-bridge.test.js +0 -19
- package/dist/http-transport-binding.test.js +1 -4
- package/dist/http-transport.js +1 -213
- package/dist/http-transport.test.js +0 -56
- package/dist/index.js +1 -48
- package/dist/input-schema.js +2 -76
- package/dist/input-schema.test.js +1 -15
- package/dist/instructions-budget.test.js +0 -2
- package/dist/manifest-size.test.js +3 -72
- package/dist/oauth-bootstrap.js +0 -47
- package/dist/oauth-bootstrap.test.js +4 -16
- package/dist/orboto-client.js +0 -30
- package/dist/orboto-client.test.js +0 -12
- package/dist/prompts.js +0 -39
- package/dist/protect-text-usage.test.js +1 -9
- package/dist/required-rules-wrapper.test.js +0 -5
- package/dist/required-rules.js +0 -2
- package/dist/resources.js +0 -62
- package/dist/resources.test.js +0 -16
- package/dist/response-budget.js +2 -150
- package/dist/response-budget.test.js +1 -32
- package/dist/server.js +4 -180
- package/dist/session-nudge.js +0 -9
- package/dist/session-nudge.test.js +2 -10
- package/dist/session-nudge.transport.test.js +0 -23
- package/dist/tool-annotations.test.js +0 -44
- package/dist/tool-docs.js +1 -12
- package/dist/tool-docs.test.js +2 -8
- package/dist/tool-parameters.test.js +0 -19
- package/dist/tools/absence-writes.js +1 -17
- package/dist/tools/admin-translations.js +0 -11
- package/dist/tools/admin-writes.js +0 -28
- package/dist/tools/admin-writes.test.js +0 -1
- package/dist/tools/agent-coordination.js +2 -24
- package/dist/tools/agent-coordination.test.js +0 -2
- package/dist/tools/agent-instructions.js +0 -10
- package/dist/tools/agent-instructions.test.js +0 -2
- package/dist/tools/agent-messages.js +0 -1
- package/dist/tools/ai-status.js +0 -14
- package/dist/tools/ai-usage.js +0 -10
- package/dist/tools/analytics.js +0 -2
- package/dist/tools/api-call.js +0 -12
- package/dist/tools/api-call.test.js +1 -2
- package/dist/tools/api-search.js +1 -6
- package/dist/tools/approvals.js +0 -6
- package/dist/tools/attach.js +0 -25
- package/dist/tools/backup.js +0 -13
- package/dist/tools/backup.test.js +1 -2
- package/dist/tools/bulk-create.js +1 -25
- package/dist/tools/bulk-create.test.js +0 -4
- package/dist/tools/bulk-writes.js +69 -61
- package/dist/tools/bulk-writes.test.js +34 -4
- package/dist/tools/check-similar.js +0 -17
- package/dist/tools/checklist-writes.js +82 -27
- package/dist/tools/checklist-writes.test.js +69 -1
- package/dist/tools/claim.js +1 -71
- package/dist/tools/claim.test.js +0 -2
- package/dist/tools/critical-path.js +0 -5
- package/dist/tools/critical-path.test.js +0 -3
- package/dist/tools/cross-project-links.ee.js +1 -24
- package/dist/tools/doc-attachments.js +0 -28
- package/dist/tools/doc-attachments.test.js +1 -4
- package/dist/tools/doc-comments.js +1 -37
- package/dist/tools/doc-comments.test.js +1 -2
- package/dist/tools/doc-edits.js +1 -31
- package/dist/tools/doc-edits.test.js +0 -9
- package/dist/tools/doc-export.js +0 -24
- package/dist/tools/doc-export.test.js +0 -12
- package/dist/tools/doc-revisions.js +0 -23
- package/dist/tools/docs-ai.js +0 -34
- package/dist/tools/docs-ai.test.js +0 -11
- package/dist/tools/docs.js +1 -75
- package/dist/tools/docs.test.js +1 -3
- package/dist/tools/embedding-status.js +0 -13
- package/dist/tools/embedding-status.test.js +0 -1
- package/dist/tools/feedback.js +1 -15
- package/dist/tools/feedback.test.js +0 -1
- package/dist/tools/get-checklists.js +6 -14
- package/dist/tools/get-project-primer.js +1 -25
- package/dist/tools/get-project.js +0 -17
- package/dist/tools/get-ticket.js +1 -61
- package/dist/tools/get-timer.js +0 -11
- package/dist/tools/git-health-reasons.js +0 -1
- package/dist/tools/identity.js +0 -13
- package/dist/tools/list-projects.js +0 -20
- package/dist/tools/list-projects.test.js +0 -1
- package/dist/tools/list-tickets.js +10 -9
- package/dist/tools/milestones-crud.test.js +0 -4
- package/dist/tools/milestones.js +0 -43
- package/dist/tools/my-tickets.js +0 -7
- package/dist/tools/phase-b-tools.test.js +14 -21
- package/dist/tools/primer-facts.js +1 -49
- package/dist/tools/primer-facts.test.js +0 -20
- package/dist/tools/project-listings.js +0 -25
- package/dist/tools/query.js +1 -12
- package/dist/tools/query.test.js +0 -8
- package/dist/tools/raci.js +0 -11
- package/dist/tools/requirements-spec.js +1 -12
- package/dist/tools/response-expand.js +0 -19
- package/dist/tools/review-policy.js +0 -24
- package/dist/tools/review-policy.test.js +0 -2
- package/dist/tools/search.js +1 -13
- package/dist/tools/session-start.js +1 -136
- package/dist/tools/session-start.test.js +1 -44
- package/dist/tools/set-parent.js +1 -19
- package/dist/tools/shared.js +0 -41
- package/dist/tools/shared.test.js +0 -4
- package/dist/tools/similar-projection.js +0 -15
- package/dist/tools/ticket-attachments.js +1 -25
- package/dist/tools/ticket-schedules.js +0 -9
- package/dist/tools/ticket-schedules.test.js +0 -1
- package/dist/tools/ticket-writes.js +1 -110
- package/dist/tools/ticket-writes.test.js +1 -15
- package/dist/tools/time-writes.js +26 -46
- package/dist/tools/time-writes.test.js +18 -0
- package/dist/tools/update-project.js +1 -27
- package/dist/tools/update-project.test.js +1 -1
- package/dist/tools/wiki.js +0 -8
- package/dist/tools/work-next.js +0 -5
- package/dist/tools/work-sessions-start.test.js +0 -2
- package/dist/tools/work-sessions.js +0 -48
- package/dist/tools/work-sessions.test.js +0 -5
- package/dist/toolset.js +2 -81
- package/dist/toolset.test.js +0 -20
- package/dist/version-guard.test.js +0 -4
- package/dist/version.js +0 -12
- package/dist/with-metrics-output-schema.test.js +1 -14
- package/dist/with-metrics.js +2 -74
- package/dist/with-metrics.test.js +7 -29
- package/package.json +1 -1
package/dist/tools/doc-export.js
CHANGED
|
@@ -1,27 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* ORB-915 - doc-export MCP tools (epic ORB-911 Phase 4).
|
|
3
|
-
*
|
|
4
|
-
* - orboto_export_doc_md - GET /docs/:id/export/md (text/markdown)
|
|
5
|
-
* - orboto_export_doc_pdf - POST /docs/:id/export/pdf (application/pdf)
|
|
6
|
-
*
|
|
7
|
-
* The Markdown export differs from `orboto_get_doc.content` because
|
|
8
|
-
* the server-side renderer strips backlinks / smart-link inflation
|
|
9
|
-
* and emits the raw saved Markdown - useful when the agent wants to
|
|
10
|
-
* pipe the body through another tool without the get-doc envelope.
|
|
11
|
-
*
|
|
12
|
-
* The PDF export returns binary bytes. MCP supports binary resource
|
|
13
|
-
* attachments via the `{ type: 'resource', resource: { uri, blob:
|
|
14
|
-
* <base64>, mimeType } }` content shape; we return the PDF that way.
|
|
15
|
-
*
|
|
16
|
-
* Both endpoints rely on `OrbotoClient.getText` / `postBinary` which
|
|
17
|
-
* landed alongside this phase - JSON-only `get` / `post` would have
|
|
18
|
-
* blown up on the non-JSON response bodies.
|
|
19
3
|
*/
|
|
20
4
|
import { z } from 'zod';
|
|
21
5
|
import { resolveDocId } from './docs.js';
|
|
22
|
-
// ---------------------------------------------------------------------------
|
|
23
|
-
// orboto_export_doc_md
|
|
24
|
-
// ---------------------------------------------------------------------------
|
|
25
6
|
export const exportDocMdToolConfig = {
|
|
26
7
|
title: 'Export a doc page as Markdown',
|
|
27
8
|
description: 'Return the doc body as plain Markdown (text/markdown). Differs from orboto_get_doc.content in that the server-side export endpoint emits the canonical saved body - agents pulling docs to feed into another tool should prefer this over get_doc, which wraps the body in get-doc envelope text + backlinks.',
|
|
@@ -44,9 +25,6 @@ export function makeExportDocMdHandler(client) {
|
|
|
44
25
|
};
|
|
45
26
|
};
|
|
46
27
|
}
|
|
47
|
-
// ---------------------------------------------------------------------------
|
|
48
|
-
// orboto_export_doc_pdf
|
|
49
|
-
// ---------------------------------------------------------------------------
|
|
50
28
|
export const exportDocPdfToolConfig = {
|
|
51
29
|
title: 'Export a doc page as PDF',
|
|
52
30
|
description: 'Render the doc page to PDF via the workspace\'s PdfService (Puppeteer-backed) and return the bytes as a base64 MCP resource attachment. Requires the PDF engine to be configured - deployments without Chromium / Puppeteer return 503 (errors.pdf.engine_unavailable) which surfaces as an OrbotoApiError.',
|
|
@@ -59,8 +37,6 @@ export function makeExportDocPdfHandler(client) {
|
|
|
59
37
|
return async ({ docId }) => {
|
|
60
38
|
docId = await resolveDocId(client, docId);
|
|
61
39
|
const { bytes, contentType } = await client.postBinary(`/docs/${docId}/export/pdf`);
|
|
62
|
-
// MCP's `resource` content type takes a base64 blob - Buffer →
|
|
63
|
-
// base64 is the standard Node path.
|
|
64
40
|
const base64 = Buffer.from(bytes).toString('base64');
|
|
65
41
|
return {
|
|
66
42
|
content: [
|
|
@@ -1,15 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* ORB-915 - doc-export tool tests.
|
|
3
|
-
*
|
|
4
|
-
* Markdown export: stub fetch returning a string + content-type
|
|
5
|
-
* text/markdown. Verify the tool returns the Markdown verbatim.
|
|
6
|
-
*
|
|
7
|
-
* PDF export: stub fetch returning a Uint8Array body. Verify the
|
|
8
|
-
* tool wraps it as an MCP resource attachment with base64 blob.
|
|
9
|
-
*
|
|
10
|
-
* 503 from a deployment with no PDF engine: verify the
|
|
11
|
-
* OrbotoApiError bubbles up so the model can tell the user what's
|
|
12
|
-
* wrong.
|
|
13
3
|
*/
|
|
14
4
|
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
|
|
15
5
|
import { OrbotoApiError, OrbotoClient } from '../orboto-client.js';
|
|
@@ -81,7 +71,6 @@ describe('orboto_export_doc_md', () => {
|
|
|
81
71
|
});
|
|
82
72
|
describe('orboto_export_doc_pdf', () => {
|
|
83
73
|
it('POSTs /docs/:id/export/pdf and wraps the bytes as a base64 MCP resource', async () => {
|
|
84
|
-
// Pretend the renderer returned a minimal 8-byte PDF header.
|
|
85
74
|
const pdfBytes = new Uint8Array([0x25, 0x50, 0x44, 0x46, 0x2d, 0x31, 0x2e, 0x37]);
|
|
86
75
|
const calls = stubBinary(pdfBytes);
|
|
87
76
|
const res = await makeExportDocPdfHandler(client)({ docId: DOC_ID });
|
|
@@ -93,7 +82,6 @@ describe('orboto_export_doc_pdf', () => {
|
|
|
93
82
|
expect(first.type).toBe('resource');
|
|
94
83
|
expect(first.resource.uri).toBe(`orboto://doc/${DOC_ID}/export.pdf`);
|
|
95
84
|
expect(first.resource.mimeType).toBe('application/pdf');
|
|
96
|
-
// The blob is the base64 of the bytes - decode it back and compare.
|
|
97
85
|
const decoded = Buffer.from(first.resource.blob, 'base64');
|
|
98
86
|
expect(Array.from(decoded)).toEqual(Array.from(pdfBytes));
|
|
99
87
|
expect(res.structuredContent).toMatchObject({
|
|
@@ -1,25 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* ORB-916 - doc revision history MCP tools (epic ORB-911 Phase 5).
|
|
3
|
-
*
|
|
4
|
-
* - orboto_list_doc_revisions - GET /docs/:id/revisions (cursor-paged)
|
|
5
|
-
* - orboto_get_doc_revision - GET /docs/:id/revisions/:rid
|
|
6
|
-
* - orboto_restore_doc_revision - POST /docs/:id/revisions/:rid/restore
|
|
7
|
-
*
|
|
8
|
-
* Revisions are auto-captured by PATCH /docs/:id every time the title
|
|
9
|
-
* or body changes, so by the time an agent wants to roll back there's
|
|
10
|
-
* usually a long history to walk. The list endpoint is cursor-paged
|
|
11
|
-
* (newest-first by editedAt DESC + id tiebreak) - the tool surfaces
|
|
12
|
-
* `nextCursor` in structuredContent so the caller can page through if
|
|
13
|
-
* the default 25-row page isn't enough.
|
|
14
|
-
*
|
|
15
|
-
* Restore writes a new revision capturing the current body before
|
|
16
|
-
* rolling back, so the restore itself is also undoable.
|
|
17
3
|
*/
|
|
18
4
|
import { z } from 'zod';
|
|
19
5
|
import { resolveDocId } from './docs.js';
|
|
20
|
-
// ---------------------------------------------------------------------------
|
|
21
|
-
// orboto_list_doc_revisions
|
|
22
|
-
// ---------------------------------------------------------------------------
|
|
23
6
|
export const listDocRevisionsToolConfig = {
|
|
24
7
|
title: 'List the revision history of a doc page',
|
|
25
8
|
description: 'Return saved revisions for a doc page, newest-first. Each row carries the revision id, title at snapshot time, editor user id, and editedAt timestamp; the body content is NOT in this list (use orboto_get_doc_revision to fetch one). Cursor-paged - pass `cursor` to walk older pages.',
|
|
@@ -69,9 +52,6 @@ export function makeListDocRevisionsHandler(client) {
|
|
|
69
52
|
};
|
|
70
53
|
};
|
|
71
54
|
}
|
|
72
|
-
// ---------------------------------------------------------------------------
|
|
73
|
-
// orboto_get_doc_revision
|
|
74
|
-
// ---------------------------------------------------------------------------
|
|
75
55
|
export const getDocRevisionToolConfig = {
|
|
76
56
|
title: 'Get a single doc revision',
|
|
77
57
|
description: 'Return the full content + title of one saved revision. Use this to diff against the current doc before deciding whether to restore.',
|
|
@@ -101,9 +81,6 @@ export function makeGetDocRevisionHandler(client) {
|
|
|
101
81
|
};
|
|
102
82
|
};
|
|
103
83
|
}
|
|
104
|
-
// ---------------------------------------------------------------------------
|
|
105
|
-
// orboto_restore_doc_revision
|
|
106
|
-
// ---------------------------------------------------------------------------
|
|
107
84
|
export const restoreDocRevisionToolConfig = {
|
|
108
85
|
title: 'Restore a doc page to a saved revision',
|
|
109
86
|
description: 'Roll back the doc to the saved revision. The API snapshots the CURRENT body to a fresh revision row first, so the restore itself is also undoable. Returns the post-restore doc state.',
|
package/dist/tools/docs-ai.js
CHANGED
|
@@ -1,34 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* ORB-799 - docs-AI surface (ask + ingest).
|
|
3
|
-
*
|
|
4
|
-
* Three tools that mirror the wrapper's `ask-docs` / `ingest-url` /
|
|
5
|
-
* `ingest-file` subcommands. Together they're the foundation for any
|
|
6
|
-
* agent that needs to "remember this URL" / "add this PDF to our
|
|
7
|
-
* wiki" workflows.
|
|
8
|
-
*
|
|
9
|
-
* - orboto_ask_docs - RAG Q&A across wiki docs, returns answer +
|
|
10
|
-
* citations. Requires both `configured` and
|
|
11
|
-
* `embeddingsConfigured` on `/ai/status`
|
|
12
|
-
* (RAG retrieval needs an embedding-capable
|
|
13
|
-
* provider).
|
|
14
|
-
* - orboto_ingest_url - fetch + extract + create doc from a public
|
|
15
|
-
* URL via the Readability-fallback pipeline.
|
|
16
|
-
* - orboto_ingest_file - upload + extract + create doc from a local
|
|
17
|
-
* file (PDF / DOCX / Markdown / plain text).
|
|
18
|
-
* Uses the multipart-upload route, so the MCP
|
|
19
|
-
* tool receives the bytes as a base64 string
|
|
20
|
-
* to avoid the model needing local FS access.
|
|
21
|
-
*
|
|
22
|
-
* AI-gated note: the `ai_status` tool exists for pre-flight. We do NOT
|
|
23
|
-
* pre-check inside the handlers - the API returns the same gating
|
|
24
|
-
* error regardless and a pre-check would double the latency of every
|
|
25
|
-
* call. Models are expected to call `orboto_ai_status` once per
|
|
26
|
-
* session if they're unsure of workspace shape.
|
|
27
3
|
*/
|
|
28
4
|
import { z } from 'zod';
|
|
29
|
-
// ---------------------------------------------------------------------------
|
|
30
|
-
// orboto_ask_docs
|
|
31
|
-
// ---------------------------------------------------------------------------
|
|
32
5
|
export const askDocsToolConfig = {
|
|
33
6
|
title: 'Ask a question against the wiki (RAG)',
|
|
34
7
|
description: 'Run a natural-language question against the wiki via the workspace\'s RAG pipeline. Returns an `answer` (Markdown) + `citations` array `[{index, title, link, spaceName}]`. The model is the workspace\'s configured AI provider; retrieval requires the embedding provider too - call `orboto_ai_status` to verify both before relying on this. Restrict to a single doc space with `spaceId` when scoping a query (e.g. "only the runbooks space"). Latency is dominated by retrieval + LLM round-trip; expect 2-10s.',
|
|
@@ -67,9 +40,6 @@ export function makeAskDocsHandler(client) {
|
|
|
67
40
|
};
|
|
68
41
|
};
|
|
69
42
|
}
|
|
70
|
-
// ---------------------------------------------------------------------------
|
|
71
|
-
// orboto_ingest_url
|
|
72
|
-
// ---------------------------------------------------------------------------
|
|
73
43
|
export const ingestUrlToolConfig = {
|
|
74
44
|
title: 'Ingest a public URL into a wiki space',
|
|
75
45
|
description: 'Fetch a public URL, run Readability extraction, store the result as a Markdown doc in `spaceId`. Optionally nest under `parentDocId` to keep the tree tidy. Sets `readabilityFallback: true` when the page didn\'t look like a recognisable article (the body falls back to full-page text - a flag the caller should surface to the operator for review).',
|
|
@@ -108,9 +78,6 @@ export function makeIngestUrlHandler(client) {
|
|
|
108
78
|
};
|
|
109
79
|
};
|
|
110
80
|
}
|
|
111
|
-
// ---------------------------------------------------------------------------
|
|
112
|
-
// orboto_ingest_file
|
|
113
|
-
// ---------------------------------------------------------------------------
|
|
114
81
|
function mimetypeFor(filename) {
|
|
115
82
|
const lower = filename.toLowerCase();
|
|
116
83
|
if (lower.endsWith('.pdf'))
|
|
@@ -136,7 +103,6 @@ export function makeIngestFileHandler(client) {
|
|
|
136
103
|
return async ({ spaceId, filename, contentBase64, parentDocId }) => {
|
|
137
104
|
let arrayBuffer;
|
|
138
105
|
try {
|
|
139
|
-
// See attach.ts for the Buffer → ArrayBuffer copy rationale.
|
|
140
106
|
const buf = Buffer.from(contentBase64, 'base64');
|
|
141
107
|
arrayBuffer = new ArrayBuffer(buf.byteLength);
|
|
142
108
|
new Uint8Array(arrayBuffer).set(buf);
|
|
@@ -1,13 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* ORB-799 - docs-AI tools tests.
|
|
3
|
-
*
|
|
4
|
-
* `orboto_ask_docs` happy + AI-not-configured-style 400.
|
|
5
|
-
* `orboto_ingest_url` happy + readability-fallback flag.
|
|
6
|
-
* `orboto_ingest_file` happy + empty-bytes refusal.
|
|
7
|
-
*
|
|
8
|
-
* The multipart upload's wire format is verified by inspecting the
|
|
9
|
-
* FormData body the fetch mock receives - we read the form-fields
|
|
10
|
-
* back to assert filename + parent.
|
|
11
3
|
*/
|
|
12
4
|
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
|
|
13
5
|
import { OrbotoApiError, OrbotoClient } from '../orboto-client.js';
|
|
@@ -124,9 +116,6 @@ describe('orboto_ingest_url', () => {
|
|
|
124
116
|
});
|
|
125
117
|
describe('orboto_ingest_file', () => {
|
|
126
118
|
it('POSTs multipart with filename + decoded content', async () => {
|
|
127
|
-
// Capture the multipart body via the fetch mock - FormData is
|
|
128
|
-
// opaque to JSON.parse, so we don't decode it; we only assert the
|
|
129
|
-
// route, method, and the JSON response shape.
|
|
130
119
|
const calls = [];
|
|
131
120
|
vi.spyOn(globalThis, 'fetch').mockImplementation(async (url, init) => {
|
|
132
121
|
calls.push({
|
package/dist/tools/docs.js
CHANGED
|
@@ -1,30 +1,10 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* ORB-244 Phase B - doc tools.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
* same reason the milestone tools do - cheap neighbours on the same
|
|
6
|
-
* API root. Docs carry a human-readable key (`ORB-D12` / `DOC-5`,
|
|
7
|
-
* ORB-1004) and spaces carry one too (`ORB-S1` / `SPACE-5`, ORB-1015);
|
|
8
|
-
* both resolve by key or UUID.
|
|
9
|
-
*
|
|
10
|
-
* ORB-912 (epic ORB-911) added the write-path neighbours so an
|
|
11
|
-
* MCP-aware client can do the full space lifecycle without falling
|
|
12
|
-
* back to REST: create / update / delete spaces, list docs in a
|
|
13
|
-
* space, and (in follow-up phases) write / move / attach / export /
|
|
14
|
-
* roll back individual doc pages.
|
|
15
|
-
*
|
|
16
|
-
* Schema alignment: field names match `@orboto/shared-schema`
|
|
17
|
-
* exactly - `content` not `body` on docs, `excerpt` not `snippet` on
|
|
18
|
-
* hits, backlinks carry `{type, id, label, sourceDocId,
|
|
19
|
-
* sourceDocTitle, sourceSpaceId}`. Getting those wrong 500s the tool
|
|
20
|
-
* because the API's Zod response validator rejects off-shape rows
|
|
21
|
-
* before they leave the server.
|
|
4
|
+
* @see ORB-1004, ORB-1015, ORB-912, ORB-911
|
|
22
5
|
*/
|
|
23
6
|
import { z } from 'zod';
|
|
24
7
|
import { resolveProjectByKey } from './shared.js';
|
|
25
|
-
// ---------------------------------------------------------------------------
|
|
26
|
-
// orboto_list_doc_spaces
|
|
27
|
-
// ---------------------------------------------------------------------------
|
|
28
8
|
export const listDocSpacesToolConfig = {
|
|
29
9
|
title: 'List doc spaces',
|
|
30
10
|
description: 'List wiki spaces (global and project-scoped) the caller can read. Each space contains a tree of docs accessed via orboto_get_doc.',
|
|
@@ -37,20 +17,12 @@ export function makeListDocSpacesHandler(client) {
|
|
|
37
17
|
const text = spaces.length === 0
|
|
38
18
|
? 'No doc spaces visible to this user.'
|
|
39
19
|
: spaces.map((s) => {
|
|
40
|
-
// `type` is the authoritative scope indicator; `projectId` is
|
|
41
|
-
// populated when type === 'project'. We use the type flag so
|
|
42
|
-
// the text rendering doesn't depend on an extra API join.
|
|
43
20
|
const scope = s.type === 'project' ? 'project-scoped' : 'workspace-wide';
|
|
44
|
-
// ORB-1161 - show key AND uuid. Doc tools accept the key directly,
|
|
45
|
-
// but surface the uuid too for any tool that still needs it.
|
|
46
21
|
return `- ${s.name} (${scope}) - key: ${s.key ?? '(none)'} id: ${s.id}`;
|
|
47
22
|
}).join('\n');
|
|
48
23
|
return {
|
|
49
24
|
content: [{ type: 'text', text }],
|
|
50
25
|
structuredContent: {
|
|
51
|
-
// ORB-1700 - metadata only; description/slug/visibility belong to
|
|
52
|
-
// the follow-up space read. id stays: every doc tool accepts it,
|
|
53
|
-
// and pre-key spaces have no key to address by.
|
|
54
26
|
spaces: spaces.map((s) => ({
|
|
55
27
|
id: s.id,
|
|
56
28
|
key: s.key ?? null,
|
|
@@ -61,13 +33,7 @@ export function makeListDocSpacesHandler(client) {
|
|
|
61
33
|
};
|
|
62
34
|
};
|
|
63
35
|
}
|
|
64
|
-
// ---------------------------------------------------------------------------
|
|
65
|
-
// orboto_get_doc
|
|
66
|
-
// ---------------------------------------------------------------------------
|
|
67
36
|
const UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
68
|
-
// ORB-1084 - every docId-taking tool accepts the human-readable doc
|
|
69
|
-
// key (ORB-D12 / DOC-5) as well; writes were UUID-only while no tool
|
|
70
|
-
// exposed the full UUID, so agents could read but never write.
|
|
71
37
|
export async function resolveDocId(client, docIdOrKey) {
|
|
72
38
|
if (UUID_RE.test(docIdOrKey))
|
|
73
39
|
return docIdOrKey;
|
|
@@ -84,8 +50,6 @@ export const getDocToolConfig = {
|
|
|
84
50
|
};
|
|
85
51
|
export function makeGetDocHandler(client) {
|
|
86
52
|
return async ({ docId }) => {
|
|
87
|
-
// ORB-1004 - accept a doc key too; resolve via the by-key route, then
|
|
88
|
-
// use the resolved UUID for the backlinks fetch.
|
|
89
53
|
const doc = UUID_RE.test(docId)
|
|
90
54
|
? await client.get(`/docs/${docId}`)
|
|
91
55
|
: await client.get(`/docs/by-key/${encodeURIComponent(docId)}`);
|
|
@@ -99,10 +63,6 @@ export function makeGetDocHandler(client) {
|
|
|
99
63
|
if (backlinks.length > 0) {
|
|
100
64
|
lines.push('', `## Backlinks (${backlinks.length})`);
|
|
101
65
|
for (const b of backlinks) {
|
|
102
|
-
// sourceDocTitle is the doc that links *to* this one; the
|
|
103
|
-
// `type` + `id` pair identifies the target - same row, target
|
|
104
|
-
// side. For most backlinks the target is this doc, so we
|
|
105
|
-
// surface the source.
|
|
106
66
|
lines.push(`- ${b.sourceDocTitle}`);
|
|
107
67
|
}
|
|
108
68
|
}
|
|
@@ -131,9 +91,6 @@ export function makeGetDocHandler(client) {
|
|
|
131
91
|
};
|
|
132
92
|
};
|
|
133
93
|
}
|
|
134
|
-
// ---------------------------------------------------------------------------
|
|
135
|
-
// orboto_create_doc_space (ORB-912)
|
|
136
|
-
// ---------------------------------------------------------------------------
|
|
137
94
|
export const createDocSpaceToolConfig = {
|
|
138
95
|
title: 'Create a doc space',
|
|
139
96
|
description: 'Create a new wiki space. IMPORTANT: every project ALREADY has an auto-generated general space (slug `<key>-general`, system-generated, holds the AI primer + manual notes) - do NOT create a second space just to hold a project\'s docs. Call orboto_list_doc_spaces first and reuse the existing project space; only create a new one for a genuinely separate collection. `type=global` creates a workspace-wide space (super-admin only); `type=project` creates a space scoped to a project - pass `projectKey` (e.g. "ORB"); a raw `projectId` UUID is still accepted for back-compat. The slug is derived from `name` when omitted. Returns the new space row.',
|
|
@@ -152,8 +109,6 @@ export function makeCreateDocSpaceHandler(client) {
|
|
|
152
109
|
return async (input) => {
|
|
153
110
|
const body = { name: input.name, type: input.type };
|
|
154
111
|
if (input.type === 'project') {
|
|
155
|
-
// Prefer the key (agent-facing surfaces speak keys); fall back to a raw
|
|
156
|
-
// UUID for back-compat.
|
|
157
112
|
let projectId = input.projectId;
|
|
158
113
|
if (!projectId && input.projectKey) {
|
|
159
114
|
projectId = (await resolveProjectByKey(client, input.projectKey)).id;
|
|
@@ -187,9 +142,6 @@ export function makeCreateDocSpaceHandler(client) {
|
|
|
187
142
|
};
|
|
188
143
|
};
|
|
189
144
|
}
|
|
190
|
-
// ---------------------------------------------------------------------------
|
|
191
|
-
// orboto_update_doc_space (ORB-912)
|
|
192
|
-
// ---------------------------------------------------------------------------
|
|
193
145
|
export const updateDocSpaceToolConfig = {
|
|
194
146
|
title: 'Update a doc space',
|
|
195
147
|
description: 'Patch a space\'s name / description / icon / slug, and (ORB-1080) its visibility settings: `visibility` (workspace = every internal member, restricted = explicit allowlist via memberIds), `guestsVisible` (external users see the space only when true). Visibility fields require space-management rights (global spaces: super-admin; project spaces: project:edit) - plain membership gets a 403 for those. `isPublic` is deprecated (replaced by guestsVisible). `type` and `projectId` are immutable; auto-generated primer spaces refuse name/slug edits.',
|
|
@@ -219,7 +171,6 @@ export function makeUpdateDocSpaceHandler(client) {
|
|
|
219
171
|
body.isPublic = input.isPublic;
|
|
220
172
|
if (input.slug !== undefined)
|
|
221
173
|
body.slug = input.slug;
|
|
222
|
-
// ORB-1080 - visibility settings (manage-gated server-side).
|
|
223
174
|
if (input.visibility !== undefined)
|
|
224
175
|
body.visibility = input.visibility;
|
|
225
176
|
if (input.guestsVisible !== undefined)
|
|
@@ -245,9 +196,6 @@ export function makeUpdateDocSpaceHandler(client) {
|
|
|
245
196
|
};
|
|
246
197
|
};
|
|
247
198
|
}
|
|
248
|
-
// ---------------------------------------------------------------------------
|
|
249
|
-
// orboto_delete_doc_space (ORB-912)
|
|
250
|
-
// ---------------------------------------------------------------------------
|
|
251
199
|
export const deleteDocSpaceToolConfig = {
|
|
252
200
|
title: 'Delete a doc space',
|
|
253
201
|
description: 'DESTRUCTIVE - cascades through every doc in the space (the pages themselves are gone, not just hidden). System-generated project primer spaces refuse deletion (they cascade only when the owning project is deleted). Returns success silently; 404 surfaces as an OrbotoApiError.',
|
|
@@ -265,9 +213,6 @@ export function makeDeleteDocSpaceHandler(client) {
|
|
|
265
213
|
};
|
|
266
214
|
};
|
|
267
215
|
}
|
|
268
|
-
// ---------------------------------------------------------------------------
|
|
269
|
-
// orboto_list_docs_in_space (ORB-912)
|
|
270
|
-
// ---------------------------------------------------------------------------
|
|
271
216
|
export const listDocsInSpaceToolConfig = {
|
|
272
217
|
title: 'List docs inside a space',
|
|
273
218
|
description: 'Return the flat list of doc pages in a space - each carries `parentDocId` so the caller can reconstruct the tree client-side. Use this when an agent needs to find a doc by title rather than asking for a UUID first. Pair with orboto_get_doc to read individual page bodies.',
|
|
@@ -285,9 +230,6 @@ export function makeListDocsInSpaceHandler(client) {
|
|
|
285
230
|
structuredContent: { docs: [] },
|
|
286
231
|
};
|
|
287
232
|
}
|
|
288
|
-
// Index by parent so we can render a simple indented tree. The
|
|
289
|
-
// tree-walk runs in JS - the API hands us the flat list because
|
|
290
|
-
// sort-order is per-parent and traversal is the caller's concern.
|
|
291
233
|
const byParent = new Map();
|
|
292
234
|
for (const r of rows) {
|
|
293
235
|
const key = r.parentDocId ?? null;
|
|
@@ -304,7 +246,6 @@ export function makeListDocsInSpaceHandler(client) {
|
|
|
304
246
|
for (const c of children) {
|
|
305
247
|
const indent = ' '.repeat(depth);
|
|
306
248
|
const iconPart = c.icon ? `${c.icon} ` : '';
|
|
307
|
-
// ORB-1004 - show the typeable key (falls back to UUID).
|
|
308
249
|
lines.push(`${indent}- ${iconPart}${c.title} (${c.docKey ?? c.id})`);
|
|
309
250
|
walk(c.id, depth + 1);
|
|
310
251
|
}
|
|
@@ -328,9 +269,6 @@ export function makeListDocsInSpaceHandler(client) {
|
|
|
328
269
|
};
|
|
329
270
|
};
|
|
330
271
|
}
|
|
331
|
-
// ---------------------------------------------------------------------------
|
|
332
|
-
// orboto_create_doc (ORB-913)
|
|
333
|
-
// ---------------------------------------------------------------------------
|
|
334
272
|
export const createDocToolConfig = {
|
|
335
273
|
title: 'Create a doc page',
|
|
336
274
|
description: 'Create a new wiki page in `spaceId` with the supplied Markdown body. Unlike orboto_ingest_url / orboto_ingest_file (which derive content from an external source), this is the plain "I have the Markdown, make me a page" path. Title is required; content is optional (creates an empty page). Returns the new doc row including its UUID and its human-readable doc key (e.g. ORB-D12).',
|
|
@@ -370,9 +308,6 @@ export function makeCreateDocHandler(client) {
|
|
|
370
308
|
};
|
|
371
309
|
};
|
|
372
310
|
}
|
|
373
|
-
// ---------------------------------------------------------------------------
|
|
374
|
-
// orboto_update_doc (ORB-913)
|
|
375
|
-
// ---------------------------------------------------------------------------
|
|
376
311
|
export const updateDocToolConfig = {
|
|
377
312
|
title: 'Update a doc page',
|
|
378
313
|
description: 'Patch a doc\'s title / content / visibility / parent / icon. Content-only updates do not require sending the title. Every body or title change snapshots the previous version into the revision history automatically - agents can roll back via orboto_restore_doc_revision (when that tool ships in Phase 5). Re-embeds the doc when content or title change.',
|
|
@@ -417,9 +352,6 @@ export function makeUpdateDocHandler(client) {
|
|
|
417
352
|
};
|
|
418
353
|
};
|
|
419
354
|
}
|
|
420
|
-
// ---------------------------------------------------------------------------
|
|
421
|
-
// orboto_delete_doc (ORB-913)
|
|
422
|
-
// ---------------------------------------------------------------------------
|
|
423
355
|
export const deleteDocToolConfig = {
|
|
424
356
|
title: 'Delete a doc page',
|
|
425
357
|
description: 'DESTRUCTIVE - removes the page from the tree. Revisions are NOT auto-cascaded; the API keeps them so an admin could in principle resurrect from history, but the page itself is gone from listings. System-generated primer docs refuse deletion (the regenerator re-creates them). Returns success silently; 404 is also silent (idempotent).',
|
|
@@ -438,9 +370,6 @@ export function makeDeleteDocHandler(client) {
|
|
|
438
370
|
};
|
|
439
371
|
};
|
|
440
372
|
}
|
|
441
|
-
// ---------------------------------------------------------------------------
|
|
442
|
-
// orboto_move_doc (ORB-913)
|
|
443
|
-
// ---------------------------------------------------------------------------
|
|
444
373
|
export const moveDocToolConfig = {
|
|
445
374
|
title: 'Move a doc page (reparent / reorder / cross-space)',
|
|
446
375
|
description: 'Reparent a doc, change its sort order, or move it into a different space. All three target fields are optional but at least one must be supplied. Distinct from orboto_update_doc\'s parentDocId because the API endpoint emits a different WS event for the tree-rebuild path connected clients care about.',
|
|
@@ -477,9 +406,6 @@ export function makeMoveDocHandler(client) {
|
|
|
477
406
|
};
|
|
478
407
|
};
|
|
479
408
|
}
|
|
480
|
-
// ---------------------------------------------------------------------------
|
|
481
|
-
// orboto_duplicate_doc_space (ORB-918)
|
|
482
|
-
// ---------------------------------------------------------------------------
|
|
483
409
|
export const duplicateDocSpaceToolConfig = {
|
|
484
410
|
title: 'Duplicate a doc space (clone the space + every doc inside it)',
|
|
485
411
|
description: 'Fork a doc space along with its entire doc tree. The new space\'s name becomes `<source> (copy)`, parent-child relationships in the tree are preserved via UUID remap. Useful when an agent wants to iterate on a runbook space without mutating the original.',
|
package/dist/tools/docs.test.js
CHANGED
|
@@ -148,7 +148,7 @@ describe('orboto_list_docs_in_space', () => {
|
|
|
148
148
|
sortOrder: 0,
|
|
149
149
|
};
|
|
150
150
|
it('renders an indented tree text + flat structured list', async () => {
|
|
151
|
-
stubJSON([{ json: [CHILD, ROOT]
|
|
151
|
+
stubJSON([{ json: [CHILD, ROOT] }]);
|
|
152
152
|
const res = await makeListDocsInSpaceHandler(client)({ spaceId: PROJECT_SCOPED_SPACE.id });
|
|
153
153
|
const text = res.content[0].text;
|
|
154
154
|
expect(text.indexOf('Architecture')).toBeLessThan(text.indexOf('Queue worker'));
|
|
@@ -229,8 +229,6 @@ describe('orboto_create_doc', () => {
|
|
|
229
229
|
});
|
|
230
230
|
});
|
|
231
231
|
describe('orboto_update_doc', () => {
|
|
232
|
-
// ORB-1084 - write tools accept the human-readable doc key: the key
|
|
233
|
-
// resolves via /docs/by-key, then the PATCH targets the UUID.
|
|
234
232
|
it('resolves a doc key to the UUID before writing', async () => {
|
|
235
233
|
const calls = stubJSON([
|
|
236
234
|
{ json: DOC }, // by-key resolve
|
|
@@ -1,15 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* ORB-1309 - `orboto_embedding_status`.
|
|
3
|
-
*
|
|
4
|
-
* Operator diagnostic for the embedding pipeline: provider/model/dims, coverage
|
|
5
|
-
* (embedded vs total, and how many are PENDING - never-embedded / stale - per
|
|
6
|
-
* tickets / comments / docs and overall), the circuit-breaker state, and when
|
|
7
|
-
* the last embedding was written. Wraps GET /admin/ai/embedding-status.
|
|
8
|
-
*
|
|
9
|
-
* `orboto_ai_status` only reports whether embeddings are configured; this is the
|
|
10
|
-
* deeper surface for "why is semantic search / duplicate detection / ask-docs
|
|
11
|
-
* stale" - a tripped breaker, a stuck queue, or a provider that stopped
|
|
12
|
-
* responding. Admin:ai:read gated (403 for non-admin callers).
|
|
13
3
|
*/
|
|
14
4
|
import { z } from 'zod';
|
|
15
5
|
export const embeddingStatusToolConfig = {
|
|
@@ -52,12 +42,9 @@ export function makeEmbeddingStatusHandler(client) {
|
|
|
52
42
|
lines.push(`Coverage: ${o.embedded} / ${o.embeddable} embeddable embedded · ${o.pending} pending${o.noContent > 0 ? ` · ${o.noContent} with no embeddable content (excluded)` : ''}`);
|
|
53
43
|
lines.push(` tickets ${s.coverage.ticket.embedded}/${s.coverage.ticket.embeddable} · comments ${s.coverage.comment.embedded}/${s.coverage.comment.embeddable} · docs ${s.coverage.doc.embedded}/${s.coverage.doc.embeddable} (embeddable)`);
|
|
54
44
|
lines.push(`Circuit breaker: ${s.breaker.tripped ? `TRIPPED - ${s.breaker.lastTrippedReason ?? 'unknown reason'} (${s.breaker.consecutiveFailures} consecutive failures)` : 'ok'}`);
|
|
55
|
-
// ORB-1715 - a billing gate is the one cause the customer can fix
|
|
56
|
-
// themselves; name it instead of letting it read as a provider fault.
|
|
57
45
|
if (s.billingGate?.gated) {
|
|
58
46
|
lines.push(`BILLING GATE: managed AI is paused for billing${s.billingGate.reason ? ` (${s.billingGate.reason})` : ''} since ${s.billingGate.blockedAt ?? 'unknown'}. The workspace admin resolves this in their orboto account; queued items embed automatically after the unblock.`);
|
|
59
47
|
}
|
|
60
|
-
// ORB-1719 - spend visibility so an agent can warn BEFORE the gate.
|
|
61
48
|
if (s.spend?.allowanceGrantMicrocents != null && s.spend.allowanceConsumedMicrocents != null) {
|
|
62
49
|
const eur = (mc) => (mc / 1_000_000).toFixed(2);
|
|
63
50
|
lines.push(`Included AI this month: ${eur(s.spend.allowanceConsumedMicrocents)} of ${eur(s.spend.allowanceGrantMicrocents)} EUR used${s.spend.allowanceRemainingMicrocents != null ? ` · ${eur(s.spend.allowanceRemainingMicrocents)} EUR left` : ''} · overage ${s.spend.overageMode ?? 'unknown'}`);
|
|
@@ -57,7 +57,6 @@ describe('tools/embedding-status', () => {
|
|
|
57
57
|
const text = result.content[0].text;
|
|
58
58
|
expect(text).toContain('BILLING GATE');
|
|
59
59
|
expect(text).toContain('allowance_exhausted');
|
|
60
|
-
// Contract guarantee: never vendor prose on any surface.
|
|
61
60
|
expect(text).not.toContain('Key is blocked');
|
|
62
61
|
});
|
|
63
62
|
it('surfaces the spend state so agents can warn before the gate (ORB-1719)', async () => {
|
package/dist/tools/feedback.js
CHANGED
|
@@ -2,24 +2,12 @@
|
|
|
2
2
|
* ORB-1910 - `orboto_report_feedback`: file a bug report / feedback / feature
|
|
3
3
|
* request with the operator from inside the workspace (epic ORB-1907).
|
|
4
4
|
*
|
|
5
|
-
*
|
|
6
|
-
* instance validates the closed schema, normalises the text and forwards
|
|
7
|
-
* the report to the control plane with its own credential - this tool never
|
|
8
|
-
* talks to the control plane and never learns the reporter's identity. It
|
|
9
|
-
* checks `GET /feedback/availability` first so a self-hosted instance gets a
|
|
10
|
-
* plain refusal naming the public issue tracker instead of a 503.
|
|
11
|
-
*
|
|
12
|
-
* The result carries the report id ONLY. The body is never echoed back into
|
|
13
|
-
* the model's context: a report may quote hostile text (a bug reproduction
|
|
14
|
-
* with an injected instruction), and re-reading it would be the one way this
|
|
15
|
-
* tool could turn data into an instruction.
|
|
5
|
+
* @see ORB-1908
|
|
16
6
|
*/
|
|
17
7
|
import { z } from 'zod';
|
|
18
8
|
const PUBLIC_TRACKER = 'https://github.com/orboto/orboto-cli/issues';
|
|
19
9
|
export const reportFeedbackToolConfig = {
|
|
20
10
|
title: 'Send feedback or a bug report to the operator',
|
|
21
|
-
// ORB-1805 - kept short: every client pays for this text at connect;
|
|
22
|
-
// orboto_help serves the full contract (caps, attachment policy, privacy).
|
|
23
11
|
description: 'File a bug report, feedback or feature request with the orboto operator from inside this workspace. Relayed without your identity; up to 3 png/jpeg/txt attachments (2 MB each, base64). Refused on self-hosted instances without an operator link. Answers with the report id only.',
|
|
24
12
|
inputSchema: z.object({
|
|
25
13
|
kind: z.enum(['bug', 'feedback', 'feature']),
|
|
@@ -37,8 +25,6 @@ export const reportFeedbackToolConfig = {
|
|
|
37
25
|
contentBase64: z.string().min(1),
|
|
38
26
|
})).max(3).optional(),
|
|
39
27
|
}).shape,
|
|
40
|
-
// A write that creates one report on the operator side; nothing is
|
|
41
|
-
// removed or overwritten, and sending twice files two reports.
|
|
42
28
|
annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: false },
|
|
43
29
|
};
|
|
44
30
|
export function makeReportFeedbackHandler(client) {
|
|
@@ -56,7 +56,6 @@ describe('orboto_report_feedback', () => {
|
|
|
56
56
|
attachments: [{ filename: 'a.txt', mimetype: 'text/plain', contentBase64: 'aGk=' }],
|
|
57
57
|
});
|
|
58
58
|
expect(result.structuredContent).toEqual({ reportId: 'rep-9', kind: 'bug', attachments: 1 });
|
|
59
|
-
// The body never comes back into the model's context.
|
|
60
59
|
expect(JSON.stringify(result)).not.toContain('Ignore previous instructions');
|
|
61
60
|
});
|
|
62
61
|
it('refuses on an instance without an operator link and names the public tracker - no POST', async () => {
|
|
@@ -1,19 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* ORB-244 Phase B - `orboto_get_checklists`.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
* this without paying for the full ticket payload:
|
|
6
|
-
*
|
|
7
|
-
* 1. Models asking "what's still unchecked on ACME-42?" - don't
|
|
8
|
-
* need description + comments + git activity for that.
|
|
9
|
-
* 2. Phase-C write tools (`orboto_check` / `orboto_uncheck`) - the
|
|
10
|
-
* user usually wants to confirm the item exists before
|
|
11
|
-
* toggling it, and this tool is the cheap round-trip.
|
|
12
|
-
*
|
|
13
|
-
* ORB-234 detail exposed here: when a checklist item links to
|
|
14
|
-
* another ticket, `effectiveCompleted` mirrors that ticket's status
|
|
15
|
-
* category. We surface the linked ticket so the model can explain
|
|
16
|
-
* "item is done because [ORB-99] shipped" rather than just "done".
|
|
4
|
+
* @see ORB-234, ORB-99
|
|
17
5
|
*/
|
|
18
6
|
import { z } from 'zod';
|
|
19
7
|
import { resolveTicketByKey } from './shared.js';
|
|
@@ -42,7 +30,8 @@ export function makeGetChecklistsHandler(client) {
|
|
|
42
30
|
const linkSuffix = i.linkedTicketKey
|
|
43
31
|
? ` ↪ [${i.linkedTicketKey}] ${i.linkedTicketTitle ?? ''} (${i.linkedTicketStatusCategory ?? 'unknown'})`
|
|
44
32
|
: '';
|
|
45
|
-
|
|
33
|
+
const metaSuffix = `${i.assigneeName ? ` @${i.assigneeName}` : ''}${i.dueDate ? ` (due ${i.dueDate})` : ''}`;
|
|
34
|
+
lines.push(`- [${i.effectiveCompleted ? 'x' : ' '}] ${i.content}${linkSuffix}${metaSuffix}`);
|
|
46
35
|
}
|
|
47
36
|
lines.push('');
|
|
48
37
|
}
|
|
@@ -59,6 +48,9 @@ export function makeGetChecklistsHandler(client) {
|
|
|
59
48
|
id: i.id,
|
|
60
49
|
content: i.content,
|
|
61
50
|
done: i.effectiveCompleted,
|
|
51
|
+
assignedTo: i.assignedTo ?? null,
|
|
52
|
+
assigneeName: i.assigneeName ?? null,
|
|
53
|
+
dueDate: i.dueDate ?? null,
|
|
62
54
|
linkedTicket: i.linkedTicketKey ? {
|
|
63
55
|
key: i.linkedTicketKey,
|
|
64
56
|
title: i.linkedTicketTitle,
|
|
@@ -1,27 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* ORB-408 (Phase 3 of ORB-406) - `orboto_get_project_primer`.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
* markdown blob, token-budget aware. AI agents call this as their
|
|
6
|
-
* first read per session.
|
|
7
|
-
*
|
|
8
|
-
* What's actually in the primer (ORB-563):
|
|
9
|
-
* - active milestones, ticket counts, recently-closed milestones
|
|
10
|
-
* - top docs from the project space
|
|
11
|
-
* - structured project facts (the `primer_facts` table - workspace +
|
|
12
|
-
* project rows)
|
|
13
|
-
* - recent activity, when the operator enabled it
|
|
14
|
-
* - repo briefings (e.g. CLAUDE.md / AGENTS.md) ONLY when the
|
|
15
|
-
* operator configured `repoFiles` AND the API host has those files
|
|
16
|
-
* on its local filesystem. Most Coolify / SaaS deployments do
|
|
17
|
-
* neither - agents must NOT assume CLAUDE.md / AGENTS.md content
|
|
18
|
-
* is in the primer. If the team relies on CLAUDE.md / AGENTS.md
|
|
19
|
-
* content cross-deployment, those conventions belong in
|
|
20
|
-
* `primer_facts` instead.
|
|
21
|
-
*
|
|
22
|
-
* Sections that didn't make the budget come back in
|
|
23
|
-
* `truncatedSections` so the agent can decide whether to bump the
|
|
24
|
-
* budget for a follow-up call.
|
|
4
|
+
* @see ORB-563
|
|
25
5
|
*/
|
|
26
6
|
import { z } from 'zod';
|
|
27
7
|
import { resolveProjectByKey } from './shared.js';
|
|
@@ -42,10 +22,6 @@ export function makeGetProjectPrimerHandler(client) {
|
|
|
42
22
|
if (maxTokens !== undefined)
|
|
43
23
|
qs.set('max_tokens', String(maxTokens));
|
|
44
24
|
const res = await client.get(`/projects/${project.id}/ai-primer?${qs.toString()}`);
|
|
45
|
-
// ORB-1104 - the trimmed-sections note is now appended in-band by
|
|
46
|
-
// assembleWithBudget, so res.markdown already carries it (and so do
|
|
47
|
-
// baked snapshots + the raw markdown route). No manual append here,
|
|
48
|
-
// otherwise it would double up.
|
|
49
25
|
return {
|
|
50
26
|
content: [
|
|
51
27
|
{
|