@elevasis/sdk 1.46.0 → 1.47.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/cli.cjs +550 -45
- package/dist/index.d.ts +164 -0
- package/dist/test-utils/index.d.ts +164 -0
- package/dist/test-utils/index.js +13 -2
- package/dist/worker/index.d.ts +197 -4
- package/dist/worker/index.js +8 -2
- package/package.json +4 -4
- package/reference/_navigation.md +2 -2
- package/reference/packages/core/src/content/README.md +15 -10
- package/reference/scaffold/recipes/extend-content.md +103 -67
- package/reference/sdk/cli-management.mdx +111 -27
- package/reference/sdk/platform-tools/adapters-platform.mdx +1 -1
- package/reference/sdk/platform-tools/index.mdx +0 -2
- package/reference/sdk/resources/patterns.mdx +14 -6
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
title: Build and Extend Content
|
|
3
|
-
description: Map the content platform primitives available to SDK projects: pipeline and step catalogs, shared review/pipeline/distribution pages, data hooks, the content workflow adapter,
|
|
3
|
+
description: Map the content platform primitives available to SDK projects: pipeline and step catalogs, shared review/pipeline/distribution pages, data hooks, the content workflow adapter, and org-model extension boundaries.
|
|
4
4
|
---
|
|
5
5
|
<!-- @generated by packages/sdk/scripts/copy-reference-docs.mjs -- DO NOT EDIT -->
|
|
6
6
|
<!-- Regenerate: pnpm scaffold:sync -->
|
|
@@ -15,16 +15,16 @@ Good trigger phrases:
|
|
|
15
15
|
- "Add a content pipeline for our podcast."
|
|
16
16
|
- "Change the review screen for clip proposals."
|
|
17
17
|
- "Write a workflow that produces the next step of a content item."
|
|
18
|
-
- "Store our style guide so producers read the same rules."
|
|
19
18
|
- "Track where each piece got published."
|
|
20
19
|
|
|
21
20
|
Content is a layered platform surface, not one component:
|
|
22
21
|
|
|
23
22
|
- **Organization OS:** pipelines, steps, statuses, pillars, and platforms are **catalogs** in the content System's ontology, not hardcoded enums. `content.config.defaultPipelineId` names the pipeline the UI opens by default.
|
|
24
|
-
- **Shared UI:** overview, items list, item review, pipelines, pipeline workspace,
|
|
25
|
-
- **Headless hooks:** item, attempt, distribution,
|
|
26
|
-
- **Workflow adapter:** `content` from `@elevasis/sdk/worker` lets workflows create items, record attempts, and open distributions through platform tools.
|
|
27
|
-
- **
|
|
23
|
+
- **Shared UI:** overview, items list, item review, pipelines, pipeline workspace, and distribution pages live in `@elevasis/ui/features/content`.
|
|
24
|
+
- **Headless hooks:** item, attempt, distribution, and pipeline-summary hooks live under `@elevasis/ui/hooks`.
|
|
25
|
+
- **Workflow adapter:** `content` from `@elevasis/sdk/worker` lets workflows create items, record and patch attempts, manage source assets, and open distributions through platform tools.
|
|
26
|
+
- **Operator surface:** `elevasis-sdk content:*` is the read and review surface for humans and agents outside a session -- list items, inspect one, see what is waiting on a `queued` review gate, read a pipeline's step contract, and clear a gate. See `apps/docs/content/docs/sdk/sdk/cli-management.mdx` for the full command reference.
|
|
27
|
+
- **Producer instructions live in the prompt.** Content has no governing-document tier. Voice, constraints, and style belong in the generating workflow's own prompt, where they behave identically and are versioned with the code that reads them. The removed tier had no producer and no consumer — nothing called `getActive` and nothing wrote back measured performance.
|
|
28
28
|
- **System API Interface:** `content` uses the flat `system.apiInterface` marker. Readiness is derived from scoped resources, ontology bindings, and required catalogs — `content:object/item` plus the pipeline, step, and status catalogs.
|
|
29
29
|
|
|
30
30
|
## The one thing to understand first: a pipeline is data
|
|
@@ -43,29 +43,31 @@ A pipeline's step catalog is named `content:catalog/{pipelineId}-steps` and refe
|
|
|
43
43
|
|
|
44
44
|
## Decision Table
|
|
45
45
|
|
|
46
|
-
| User wants | Start here
|
|
47
|
-
| ------------------------------------------------- |
|
|
48
|
-
| Add a pipeline, step, status, pillar, or platform | `core/config/organization-model.ts` — the content System's `ontology`
|
|
49
|
-
| Produce the next step of an item from a workflow | `content` from `@elevasis/sdk/worker`
|
|
50
|
-
| Change the review screen | `ContentItemReviewPage` slots, or compose `ContentReviewCard` yourself
|
|
51
|
-
| Add content nav or a content route | `CONTENT_ITEMS`, `ContentSidebar`, `ContentSidebarMiddle`
|
|
52
|
-
|
|
|
53
|
-
| Track where a piece was published | `createDistribution` / `updateDistribution`
|
|
54
|
-
|
|
|
46
|
+
| User wants | Start here | Notes |
|
|
47
|
+
| ------------------------------------------------- | --------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------- |
|
|
48
|
+
| Add a pipeline, step, status, pillar, or platform | `core/config/organization-model.ts` — the content System's `ontology` | Catalog edit. Add the step catalog **and** point the pipeline record's `stepCatalog` at it. |
|
|
49
|
+
| Produce the next step of an item from a workflow | `content` from `@elevasis/sdk/worker` | One workflow per step. Claim the step with `stepKey`; return the next step key. |
|
|
50
|
+
| Change the review screen | `ContentItemReviewPage` slots, or compose `ContentReviewCard` yourself | Prefer slots. The card, action bar, alternates panel, and processing strip are all exported individually. |
|
|
51
|
+
| Add content nav or a content route | `CONTENT_ITEMS`, `ContentSidebar`, `ContentSidebarMiddle` | Same manifest/sidebar composition pattern as CRM and lead-gen. |
|
|
52
|
+
| Give producers a style guide or voice constraints | The generating workflow's own prompt | Content has no rules-document tier. Put the instructions where the model reads them. |
|
|
53
|
+
| Track where a piece was published | `createDistribution` / `updateDistribution` | One distribution row per platform per item. |
|
|
54
|
+
| Pick source material for a new item | `createSourceAsset` / `getSourceAsset` / `listSourceAssets` / `updateSourceAsset` | Raw material a producer works from -- an uploaded episode, a transcript, a reference file. |
|
|
55
|
+
| Operate the pipeline from outside a browser | `elevasis-sdk content:*` | Read-only plus one write (`content:review`). Not how a workflow produces content -- see the boundary below. |
|
|
56
|
+
| Add a new persisted content column or table | Platform/API migration work, not just scaffold work | DB, core schemas/types, API service/handlers, hooks, docs, and scaffold contracts move together. |
|
|
55
57
|
|
|
56
58
|
## Published Content Surfaces
|
|
57
59
|
|
|
58
|
-
| Surface | Import from | Use for
|
|
59
|
-
| -------------------------------------------------------------------------------------------------------------------------- | ------------------------------- |
|
|
60
|
-
| `contentManifest`, `CONTENT_ITEMS`, `ContentSidebar`, `
|
|
61
|
-
| `ContentOverviewPage`, `ContentItemsPage`, `ContentItemReviewPage`, `ContentPipelinesPage`, `ContentPipelineWorkspacePage` | `@elevasis/ui/features/content` | Shared pages you can route to or wrap
|
|
62
|
-
| `
|
|
63
|
-
| `ContentReviewCard`, `ReviewActionBar`, `PayloadBody`, `AlternatesPanel`, `ProcessingStateStrip` | `@elevasis/ui/features/content` | Review UI primitives for a custom review screen
|
|
64
|
-
| `useContentConfig`, `resolveContentStepResource` | `@elevasis/ui/features/content` | Read the resolved pipeline/step catalogs from OM
|
|
65
|
-
| `useContentItems`, `useContentItem`, `useContentItemAttempts`, `useUpdateContentItem`, `useReviewContentItem` | `@elevasis/ui/hooks` | Item data access and review mutations
|
|
66
|
-
| `useContentDistributions`, `useContentDistribution`, `useUpdateContentDistribution` | `@elevasis/ui/hooks` | Distribution data access
|
|
67
|
-
| `
|
|
68
|
-
| `content
|
|
60
|
+
| Surface | Import from | Use for |
|
|
61
|
+
| -------------------------------------------------------------------------------------------------------------------------- | ------------------------------- | ------------------------------------------------ |
|
|
62
|
+
| `contentManifest`, `CONTENT_ITEMS`, `ContentSidebar`, `ContentSidebarMiddle`, `MyReviewQueuePanel` | `@elevasis/ui/features/content` | Feature registration and sidebar composition |
|
|
63
|
+
| `ContentOverviewPage`, `ContentItemsPage`, `ContentItemReviewPage`, `ContentPipelinesPage`, `ContentPipelineWorkspacePage` | `@elevasis/ui/features/content` | Shared pages you can route to or wrap |
|
|
64
|
+
| `ContentDistributionsPage`, `ContentDistributionDetailPage` | `@elevasis/ui/features/content` | Distribution surfaces |
|
|
65
|
+
| `ContentReviewCard`, `ReviewActionBar`, `PayloadBody`, `AlternatesPanel`, `ProcessingStateStrip` | `@elevasis/ui/features/content` | Review UI primitives for a custom review screen |
|
|
66
|
+
| `useContentConfig`, `resolveContentStepResource` | `@elevasis/ui/features/content` | Read the resolved pipeline/step catalogs from OM |
|
|
67
|
+
| `useContentItems`, `useContentItem`, `useContentItemAttempts`, `useUpdateContentItem`, `useReviewContentItem` | `@elevasis/ui/hooks` | Item data access and review mutations |
|
|
68
|
+
| `useContentDistributions`, `useContentDistribution`, `useUpdateContentDistribution` | `@elevasis/ui/hooks` | Distribution data access |
|
|
69
|
+
| `useContentPipelineSummary` | `@elevasis/ui/hooks` | Pipeline roll-ups |
|
|
70
|
+
| `content` | `@elevasis/sdk/worker` | Workflow-side content adapter |
|
|
69
71
|
|
|
70
72
|
Read the generated contracts before changing typed boundaries:
|
|
71
73
|
|
|
@@ -104,7 +106,11 @@ Then set `content.config.defaultPipelineId` if this should be the pipeline the U
|
|
|
104
106
|
|
|
105
107
|
## 2. Produce a Step in a Workflow
|
|
106
108
|
|
|
107
|
-
One workflow per step. The workflow records an **attempt** against the item, tagged with the step it satisfies
|
|
109
|
+
One workflow per step. The workflow records an **attempt** against the item, tagged with the step it satisfies.
|
|
110
|
+
|
|
111
|
+
**Attempt lifecycle: write at start, patch at end.** Call `createAttempt` with `status: 'running'` and the `stepKey` _before_ doing the step's work, then call `updateAttempt` on that same row with the terminal status and the final payload once the work finishes. This is **one row per step execution, patched in place** -- never a start row plus a separate finish row. `attempt_number` counts per item, not per step, and there is a live unique constraint on the pair, so two rows for one step execution collide with genuine retries of that step and cannot be told apart from them.
|
|
112
|
+
|
|
113
|
+
A crashed step correctly leaves its row at `status: 'running'` forever. There is deliberately no reaper that flips a stale `running` row to `error` -- a stuck item is information an operator needs to see (`content:get` or `content:queue` surface it as-is), not a signal to paper over. If a nudge is wanted later it is a separate SLA concern, not something this write path should hide.
|
|
108
114
|
|
|
109
115
|
<!-- doc-snippet:skip: illustrative excerpt -- `resourceDescriptors` names the project's own resource-descriptors export -->
|
|
110
116
|
|
|
@@ -141,16 +147,29 @@ export const clipSelectionWorkflow: WorkflowDefinition = {
|
|
|
141
147
|
const input = rawInput as z.infer<typeof inputSchema>
|
|
142
148
|
const item = await content.getItem({ contentItemId: input.contentItemId })
|
|
143
149
|
|
|
150
|
+
// Write-at-start: create the row before doing any work, so a crash
|
|
151
|
+
// mid-step still leaves a `running` row an operator can find.
|
|
144
152
|
const attempt = await content.createAttempt({
|
|
145
153
|
contentItemId: item.id,
|
|
146
154
|
// Tags the attempt with the step it satisfies. Omit it and the
|
|
147
155
|
// attempt is stored with a null step -- the review UI's Step column
|
|
148
156
|
// renders "--" and nothing tells you which step produced the row.
|
|
149
157
|
stepKey: 'clip-selection',
|
|
150
|
-
|
|
158
|
+
status: 'running',
|
|
159
|
+
payload: {},
|
|
151
160
|
sourceExecutionId: context.executionId
|
|
152
161
|
})
|
|
153
162
|
|
|
163
|
+
const clips = await selectClips(item)
|
|
164
|
+
|
|
165
|
+
// Patch-at-end: the same row, moved to its terminal status with the
|
|
166
|
+
// real payload. Never a second createAttempt call for this step.
|
|
167
|
+
await content.updateAttempt({
|
|
168
|
+
attemptId: attempt.id,
|
|
169
|
+
status: 'success',
|
|
170
|
+
payload: { clips }
|
|
171
|
+
})
|
|
172
|
+
|
|
154
173
|
return { attemptId: attempt.id, nextStepKey: 'publish' }
|
|
155
174
|
}
|
|
156
175
|
}
|
|
@@ -159,39 +178,24 @@ export const clipSelectionWorkflow: WorkflowDefinition = {
|
|
|
159
178
|
}
|
|
160
179
|
```
|
|
161
180
|
|
|
162
|
-
`ContentToolMap` is the full surface: `createItem`, `getItem`, `listItems`, `updateItem`, `createAttempt`, `listAttempts`, `createDistribution`, `updateDistribution`. `organizationId` is injected server-side — never pass it from workflow code.
|
|
181
|
+
`ContentToolMap` is the full surface, 13 methods: `createItem`, `getItem`, `listItems`, `updateItem`, `createAttempt`, `listAttempts`, `updateAttempt`, `createSourceAsset`, `getSourceAsset`, `listSourceAssets`, `updateSourceAsset`, `createDistribution`, `updateDistribution`. `organizationId` is injected server-side — never pass it from workflow code.
|
|
163
182
|
|
|
164
|
-
|
|
183
|
+
**`reviewItem` is deliberately not on this list.** Clearing a `queued` review gate is operator work, done through `elevasis-sdk content:review` (see `apps/docs/content/docs/sdk/sdk/cli-management.mdx`), not something a producer workflow can call. A producer approving its own output collapses the gate the review step exists to provide.
|
|
165
184
|
|
|
166
|
-
|
|
185
|
+
### Source Assets
|
|
167
186
|
|
|
168
|
-
|
|
169
|
-
import { artifacts } from '@elevasis/sdk/worker'
|
|
170
|
-
|
|
171
|
-
// `kind` MUST be `content:`-prefixed. The artifacts_manage_content RLS policy
|
|
172
|
-
// grants org-owned writes only for `kind LIKE 'content:%'` -- a non-prefixed
|
|
173
|
-
// kind is rejected server-side with no compile-time warning.
|
|
174
|
-
//
|
|
175
|
-
// `pipelineId` MUST be supplied. getActive filters on
|
|
176
|
-
// (organizationId, pipelineId, kind); an artifact written without one can
|
|
177
|
-
// never be read back by the lookup every consumer uses.
|
|
178
|
-
await artifacts.createArtifact({
|
|
179
|
-
ownerKind: 'organization',
|
|
180
|
-
ownerId: null,
|
|
181
|
-
kind: 'content:style-guide',
|
|
182
|
-
pipelineId: 'podcast-to-clips',
|
|
183
|
-
content: { tone: 'direct', bannedPhrases: ['synergy'] }
|
|
184
|
-
})
|
|
185
|
-
|
|
186
|
-
const { artifact } = await artifacts.getActive({
|
|
187
|
-
pipelineId: 'podcast-to-clips',
|
|
188
|
-
kind: 'content:style-guide'
|
|
189
|
-
})
|
|
190
|
-
```
|
|
187
|
+
`createSourceAsset`, `getSourceAsset`, `listSourceAssets`, and `updateSourceAsset` manage the raw material a producer works from -- an uploaded podcast episode, a transcript, a reference file -- independent of any content item. A producer lists or fetches source assets to pick material, then references the chosen one (`sourceAssetId`) when it calls `createItem`. Source assets have their own `kind`-keyed payload envelope, declared per-kind in the content System's OM catalog, the same pattern `createItem`'s payload uses.
|
|
191
188
|
|
|
192
|
-
|
|
189
|
+
## 3. Give Producers Their Instructions
|
|
193
190
|
|
|
194
|
-
|
|
191
|
+
There is no rules-document tier and no settings page. Voice, banned phrases, structural constraints, and the craft checklist go in the **producing workflow's own prompt**.
|
|
192
|
+
|
|
193
|
+
That is not a downgrade from a stored document — it is where the instructions were always read. The tier that used to sit here stored versioned prose that no step ever loaded and no measurement ever rewrote, so its contents reached the model only by being copied into a prompt anyway. Putting them in the prompt directly means they version with the code that reads them and cannot silently disagree with it.
|
|
194
|
+
|
|
195
|
+
Two consequences worth knowing:
|
|
196
|
+
|
|
197
|
+
- **Per-idea constraints belong with the idea, not in a separate document.** When the substance of a piece and the constraints on that substance come apart, a model will invent the substance to satisfy the constraints. Keep an entry and its rules as one object in whatever the workflow reads.
|
|
198
|
+
- **The `artifacts` platform tool still exists** and is a generic, kind-keyed document store. It is no longer part of content's story, and `artifacts_manage_content` is gone, so a `content:`-prefixed org-owned write is no longer granted by `content.manage`. Reach for it as a general capability, not as content's rules tier.
|
|
195
199
|
|
|
196
200
|
## 4. Customize the Review Screen
|
|
197
201
|
|
|
@@ -215,12 +219,41 @@ When the layout itself has to change, compose the primitives — `ContentReviewC
|
|
|
215
219
|
|
|
216
220
|
## 5. Extend Navigation
|
|
217
221
|
|
|
218
|
-
|
|
222
|
+
The content sidebar has two sections. **`Create` is where your own authoring surfaces go**; the
|
|
223
|
+
pipeline section below it is the shared review and tracking nav. Which one you are adding to decides
|
|
224
|
+
which prop you pass.
|
|
225
|
+
|
|
226
|
+
### Adding an authoring page (the common case)
|
|
227
|
+
|
|
228
|
+
Content creation is tenant-specific by nature — every tenant writes a different kind of thing,
|
|
229
|
+
through a different screen — so `Create` starts empty and you fill it. Pass `createItems`:
|
|
219
230
|
|
|
220
231
|
```tsx
|
|
221
|
-
import { contentManifest,
|
|
232
|
+
import { contentManifest, ContentSidebar } from '@elevasis/ui/features/content'
|
|
222
233
|
import type { SystemModule } from '@elevasis/ui/provider'
|
|
223
234
|
import type { NavItem } from '@elevasis/ui/layout'
|
|
235
|
+
import { IconPencil } from '@tabler/icons-react'
|
|
236
|
+
|
|
237
|
+
const createItems: NavItem[] = [{ label: 'Draft a Post', to: '/content/write', icon: IconPencil, exact: false }]
|
|
238
|
+
|
|
239
|
+
export const customContentManifest: SystemModule = {
|
|
240
|
+
...contentManifest,
|
|
241
|
+
sidebar: () => <ContentSidebar createItems={createItems} />
|
|
242
|
+
}
|
|
243
|
+
```
|
|
244
|
+
|
|
245
|
+
Add as many as you like — a drafting screen, a brief builder, a batch generator. Until you declare
|
|
246
|
+
one, the section renders a "No authoring pages declared yet" hint rather than an empty gap.
|
|
247
|
+
|
|
248
|
+
**`Create` is a fixed platform label and is not vocabulary-driven.** Rename the page, not the
|
|
249
|
+
section.
|
|
250
|
+
|
|
251
|
+
### Adding to the pipeline nav (rare)
|
|
252
|
+
|
|
253
|
+
`items` replaces the pipeline list wholesale, so you must restate the shared entries:
|
|
254
|
+
|
|
255
|
+
```tsx
|
|
256
|
+
import { CONTENT_ITEMS, ContentSidebar, ContentSidebarMiddle } from '@elevasis/ui/features/content'
|
|
224
257
|
import { IconChartBar } from '@tabler/icons-react'
|
|
225
258
|
|
|
226
259
|
const customItems: NavItem[] = [
|
|
@@ -228,17 +261,20 @@ const customItems: NavItem[] = [
|
|
|
228
261
|
{ label: 'Performance', to: '/content/performance', icon: IconChartBar, exact: false }
|
|
229
262
|
]
|
|
230
263
|
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
</ContentSidebar>
|
|
237
|
-
)
|
|
238
|
-
}
|
|
264
|
+
sidebar: () => (
|
|
265
|
+
<ContentSidebar>
|
|
266
|
+
<ContentSidebarMiddle createItems={createItems} items={customItems} />
|
|
267
|
+
</ContentSidebar>
|
|
268
|
+
)
|
|
239
269
|
```
|
|
240
270
|
|
|
241
|
-
|
|
271
|
+
**Prefer `createItems` whenever the page is an authoring surface.** `CONTENT_ITEMS` is a static
|
|
272
|
+
snapshot built from the default vocabulary, so spreading it pins the shared labels at their defaults
|
|
273
|
+
— a tenant that later renames its pipelines to "Campaigns" through `system.config.vocabulary` keeps
|
|
274
|
+
rendering "Pipelines" here, and nothing fails to tell you.
|
|
275
|
+
|
|
276
|
+
Replace `contentManifest` with `customContentManifest` in the local `SYSTEM_MANIFESTS` array and add
|
|
277
|
+
the matching route under `ui/src/routes/content/`.
|
|
242
278
|
|
|
243
279
|
## 6. Mark the System API-Ready
|
|
244
280
|
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
---
|
|
2
2
|
title: CLI Management Commands
|
|
3
|
-
description: elevasis-sdk management commands -- project, note, acquisition, client, agent, session, queue, schedule, om, ui, and
|
|
3
|
+
description: elevasis-sdk management commands -- project, note, acquisition, client, agent, session, queue, schedule, om, ui, skill, and content subcommand families
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
This page covers the domain management commands for `elevasis-sdk`. For core SDK commands (check, deploy, exec, resources, executions, describe, creds, rename), see [CLI Reference](cli.mdx).
|
|
7
7
|
|
|
8
|
-
Every command family on this page -- `project`, `note`, `acquisition`, `client`, `agent`, `session`, `queue`, `schedule`, `om:doctor`, and `
|
|
8
|
+
Every command family on this page -- `project`, `note`, `acquisition`, `client`, `agent`, `session`, `queue`, `schedule`, `om:doctor`, `request`, and `content` -- also accepts `--prod` to target production, overriding `NODE_ENV=development` (live as of `@elevasis/sdk` 1.45.0). See [CLI Reference's Global Flags](cli.mdx#global-flags) for the full description; per-command flag tables below list `--api-url` and other command-specific flags only, not `--prod` or `--json`.
|
|
9
9
|
|
|
10
10
|
---
|
|
11
11
|
|
|
@@ -237,7 +237,7 @@ elevasis-sdk note:create --content <text>
|
|
|
237
237
|
| Flag | Description |
|
|
238
238
|
| ------------------------- | ----------------------------------------------------------------------------------------- |
|
|
239
239
|
| `--content <text>` | Required. The note body text |
|
|
240
|
-
| `--user <email>` | Target user email
|
|
240
|
+
| `--user <email>` | **Required.** Target user email |
|
|
241
241
|
| `--title <text>` | Optional note title |
|
|
242
242
|
| `--priority <priority>` | Priority level: `low`, `normal` (default), `high`, or `urgent` |
|
|
243
243
|
| `--pinned` | Pin the note to the top of the panel |
|
|
@@ -248,8 +248,9 @@ elevasis-sdk note:create --content <text>
|
|
|
248
248
|
**Behavior:**
|
|
249
249
|
|
|
250
250
|
- Posts to `POST /api/external/user-notes`
|
|
251
|
-
-
|
|
252
|
-
-
|
|
251
|
+
- `--user` is **not optional in practice**: the CLI omits `user_email` from the body when the flag is absent, and `ExternalCreateUserNoteBodySchema` requires it, so the call returns 400
|
|
252
|
+
- There is no "API key owner" to default to. An API key is org-wide and its actor is the key itself, not a person -- defaulting would write a note into some human's private space they never asked for. See `.claude/rules/actor-attribution.md`
|
|
253
|
+
- The platform resolves the email to a Supabase user UUID and verifies the resolved user is an active member of the calling organization before writing
|
|
253
254
|
- External agent-created notes are always private; org-shared visibility is a signed-in UI capability, not a broadcast CLI mode
|
|
254
255
|
- The `--source` flag is recorded as the `source` column in `user_notes`; agent runtimes should pass their resource ID here so users can see which workflow created the note
|
|
255
256
|
- Priority `normal` produces no badge in the UI; `high` renders orange, `urgent` renders red, `low` renders dimmed gray
|
|
@@ -257,8 +258,8 @@ elevasis-sdk note:create --content <text>
|
|
|
257
258
|
**Examples:**
|
|
258
259
|
|
|
259
260
|
```bash
|
|
260
|
-
# Create a note for
|
|
261
|
-
elevasis-sdk note:create --content "Deal X has stalled -- follow up needed"
|
|
261
|
+
# Create a note for a named user
|
|
262
|
+
elevasis-sdk note:create --content "Deal X has stalled -- follow up needed" --user ops@acme.com
|
|
262
263
|
|
|
263
264
|
# Create a high-priority pinned note for a specific user
|
|
264
265
|
elevasis-sdk note:create \
|
|
@@ -890,29 +891,112 @@ The same coverage gate runs inside `elevasis-sdk check` when the coverage regist
|
|
|
890
891
|
|
|
891
892
|
---
|
|
892
893
|
|
|
894
|
+
## elevasis-sdk content:\*
|
|
895
|
+
|
|
896
|
+
Read and review access to the content platform: content items, their attempts and distributions, the pipeline step contracts they move through, and the one write this namespace owns -- clearing an open `queued` review gate. Producing content -- creating items, recording attempts, opening distributions -- is workflow-side work through the `content` adapter from `@elevasis/sdk/worker`; see `packages/sdk/docs/scaffold/recipes/extend-content.md`. This namespace is the operator surface for the pipeline that adapter feeds, not a second way to write to it.
|
|
897
|
+
|
|
898
|
+
Matches the DB-backed namespaces documented above (`project:`, `client:`, `note:`, `queue:`, `schedule:`): pretty-printed JSON by default, `--pretty` for human-readable output.
|
|
899
|
+
|
|
900
|
+
### Command Boundary
|
|
901
|
+
|
|
902
|
+
- `content:*` is the operator surface for the **content platform System** -- pipelines, items, attempts, and distributions, as documented in `packages/sdk/docs/scaffold/recipes/extend-content.md`. It reads the state a producer workflow is moving an item through, and it clears the review gates a human has to sign off on.
|
|
903
|
+
- `content:*` is **not** the Organization Model's own `System.content` field. That is a generic, retired bridge input every System carries for compatibility and has nothing to do with the content platform System documented here, despite sharing a name.
|
|
904
|
+
- `content:*` is **not** the acquisition domain's own material -- deals, companies, and CRM records reached through `acquisition:*` and `client:*`. A content item can be _about_ an acquisition client (`clientId` on the item), but the content platform does not read or write acquisition records, and `acquisition:*` does not read or write content items.
|
|
905
|
+
- `content:board` renders the same columns, gates, and waiting counts the Command Center board shows, and it is the **same** placement rule rather than a second copy of it. It was deferred until 2026-08-15 because `packages/sdk` has no `@elevasis/ui` dependency and so could not reach `deriveContentBoard`; the fix was to move that helper and `getContentItemIdentity` into `@repo/core/content`, which `@repo/ui` now re-exports. Duplicating the rule inside the CLI was rejected outright -- two copies of a placement rule diverge, which is exactly what happened to the open-review-gate predicate before it was consolidated into `review-gates.ts`.
|
|
906
|
+
|
|
907
|
+
### Reads
|
|
908
|
+
|
|
909
|
+
```bash
|
|
910
|
+
elevasis-sdk content:list --status queued --pipeline-id short-form-repurpose
|
|
911
|
+
elevasis-sdk content:get <itemId>
|
|
912
|
+
elevasis-sdk content:board <pipelineId> --pretty
|
|
913
|
+
elevasis-sdk content:queue
|
|
914
|
+
elevasis-sdk content:pipeline
|
|
915
|
+
elevasis-sdk content:pipeline <pipelineId>
|
|
916
|
+
elevasis-sdk content:distributions --pipeline-id short-form-repurpose
|
|
917
|
+
```
|
|
918
|
+
|
|
919
|
+
- `content:list` -- list content items. Filters: `--status`, `--pillar`, `--pipeline-id`, `--client-id`, `--reviewed-by`, `--search` (matches title), `--limit`, `--offset`.
|
|
920
|
+
- `content:get <itemId>` -- one item plus its full attempt history and its distributions, in a single response. Each attempt carries a `sourceExecutionId`, so this call is the item's execution lineage in one hop -- feed any id straight into `elevasis-sdk execution` / `pnpm exec elevasis execution`.
|
|
921
|
+
- `content:board <pipelineId>` -- one pipeline rendered as columns: each declared step in `order`, each card at the first step its `processingState` has not recorded as `success`, and a gate with a waiting count on every `live` or `queued` step. Two buckets sit outside the columns: `done` (every declared step succeeded) and `unplaced` (the item's `processingState` names only steps the pipeline no longer declares). Unplaced items are never folded into column 1, so a non-empty bucket is a real signal that the step catalog moved underneath live items. `--limit` defaults to 100, the API's page ceiling for this route, and the command reports a **partial** board rather than truncating silently when a pipeline holds more items than one page.
|
|
922
|
+
- `content:queue` -- items sitting on an open `queued`-gate review. Each row prints the item, the `stepKey` of the gate it is waiting on, and the attempt that opened it. This is the list `content:review` reads from -- see "Why `--step` is required" below.
|
|
923
|
+
- `content:pipeline [id]` -- with no id, lists pipeline templates; with an id, returns that pipeline's step contract (step keys and review modes). Read from the **deployed** Organization Model snapshot, not a local project model -- a model edit that has not been redeployed produces a stale-snapshot 503 that looks like a code bug.
|
|
924
|
+
- `content:distributions` -- list distribution rows (one per platform/format target per item). Filters: `--content-item-id`, `--pipeline-id`, `--platform`, `--status`, `--limit`, `--offset`.
|
|
925
|
+
|
|
926
|
+
**API routes:** `GET /api/external/content/items`, `/api/external/content/items/:itemId`, `/api/external/content/queue`, `/api/external/content/pipelines`, `/api/external/content/pipelines/:id`, `/api/external/content/distributions`.
|
|
927
|
+
|
|
928
|
+
### content:review
|
|
929
|
+
|
|
930
|
+
The one write command in this namespace. Everything else that writes to an item -- creating it, recording an attempt, opening a distribution -- is producer work through the `content` worker adapter, not a CLI command.
|
|
931
|
+
|
|
932
|
+
**Synopsis:**
|
|
933
|
+
|
|
934
|
+
```
|
|
935
|
+
elevasis-sdk content:review <itemId> --step <key> (--approve | --reject) --user <email>
|
|
936
|
+
[--feedback <text>] [--reason <text>]
|
|
937
|
+
[--prod] [--api-url <url>] [--pretty]
|
|
938
|
+
```
|
|
939
|
+
|
|
940
|
+
**Flags:**
|
|
941
|
+
|
|
942
|
+
| Flag | Description |
|
|
943
|
+
| --------------------- | ----------------------------------------------------- |
|
|
944
|
+
| `--step <key>` | Required. The `stepKey` of the `queued` gate to clear |
|
|
945
|
+
| `--approve` | Approve the item at that step |
|
|
946
|
+
| `--reject` | Reject the item at that step. Requires `--reason` |
|
|
947
|
+
| `--user <email>` | Required. The acting reviewer's email |
|
|
948
|
+
| `--feedback <text>` | Optional reviewer feedback, recorded on the review |
|
|
949
|
+
| `--reason <text>` | Rejection reason. Required when `--reject` is used |
|
|
950
|
+
| `--api-url <url>` | Override the API base URL |
|
|
951
|
+
| `--pretty` | Human-readable output instead of raw JSON |
|
|
952
|
+
|
|
953
|
+
`--approve` and `--reject` are mutually exclusive; exactly one is required.
|
|
954
|
+
|
|
955
|
+
**Behavior:**
|
|
956
|
+
|
|
957
|
+
Posts to `POST /api/external/content/items/:itemId/review`.
|
|
958
|
+
|
|
959
|
+
**Why `--step` is required.** Nothing makes a `queued` gate pause the pipeline -- a producer workflow can keep moving an item forward while an earlier step's review is still open, so a single item can have several `queued` gates open at the same time. A live run found exactly that: one item ended up with three open gates at once, and resolving the target server-side (by "most recent step-keyed attempt") cleared a step that was not a gate at all. There is no safe way to infer which gate a caller means, so the caller names it. The operator flow is `content:queue` to see which gates are open and each one's `stepKey`, then `content:review <itemId> --step <that key>` to clear the one intended.
|
|
960
|
+
|
|
961
|
+
**Why `--user` is required.** API keys have no owner -- there is no signed-in user on the API-key path -- and both `content_items.reviewed_by` and `content_item_attempts.created_by` are real foreign keys to `users`. The reviewer's identity has to come from somewhere, so the caller supplies it: `--user` names an email, which must resolve to an active member of the API key's organization.
|
|
962
|
+
|
|
963
|
+
**Examples:**
|
|
964
|
+
|
|
965
|
+
```bash
|
|
966
|
+
# Approve a clip-selection gate
|
|
967
|
+
elevasis-sdk content:review 3f9c1e20-... --step clip-selection --approve --user ops@acme.com --pretty
|
|
968
|
+
|
|
969
|
+
# Reject with a reason
|
|
970
|
+
elevasis-sdk content:review 3f9c1e20-... --step copy-generation --reject --user ops@acme.com --reason "Off-brand tone" --pretty
|
|
971
|
+
```
|
|
972
|
+
|
|
973
|
+
**Implementation:** `packages/sdk/src/cli/commands/content/`
|
|
974
|
+
|
|
975
|
+
---
|
|
976
|
+
|
|
893
977
|
## Appendix: Domain Status
|
|
894
978
|
|
|
895
979
|
Current status of all SDK CLI domains. Domains marked `deferred` have no CLI commands yet.
|
|
896
980
|
|
|
897
|
-
| Domain | CLI surface
|
|
898
|
-
| ----------- |
|
|
899
|
-
| platform | top-level SDK commands
|
|
900
|
-
| project | `project:*`
|
|
901
|
-
| knowledge | `knowledge:*`
|
|
902
|
-
| creds | `creds *` nested Commander group
|
|
903
|
-
| ui | `ui:*`
|
|
904
|
-
| request | `request:submit`, `request:list`, `request:get`
|
|
905
|
-
| error | `error resolve`, `error resolve-execution`
|
|
906
|
-
| acquisition | `acquisition:list:*`, `acquisition:deal:*`
|
|
907
|
-
| client | `client:*`
|
|
908
|
-
| agent | `agent:list`, `agent:get`
|
|
909
|
-
| session | `session:create`, `session:turn`, `session:messages`, `session:list`, `session:get`, `session:end`
|
|
910
|
-
| queue | `queue:list`, `queue:get`, `queue:select`, `queue:expire`, `queue:status`
|
|
911
|
-
| schedule | `schedule:list`, `schedule:get`, `schedule:create`, `schedule:update`
|
|
912
|
-
| skill | `skill:scaffold`, `skill:check-coverage`
|
|
913
|
-
| content |
|
|
914
|
-
| seo | none
|
|
915
|
-
| monitoring | none
|
|
981
|
+
| Domain | CLI surface | API surface | Status |
|
|
982
|
+
| ----------- | ------------------------------------------------------------------------------------------------------------------------------ | -------------------------------------------------------------- | ------------------------------------- |
|
|
983
|
+
| platform | top-level SDK commands | mixed platform APIs | implemented |
|
|
984
|
+
| project | `project:*` | `apps/api/src/projects/` | implemented |
|
|
985
|
+
| knowledge | `knowledge:*` | file/generated knowledge data | implemented |
|
|
986
|
+
| creds | `creds *` nested Commander group | credentials API | implemented |
|
|
987
|
+
| ui | `ui:*` | local project file edits | implemented |
|
|
988
|
+
| request | `request:submit`, `request:list`, `request:get` | requests API | implemented read/write scope |
|
|
989
|
+
| error | `error resolve`, `error resolve-execution` | execution error APIs | partial |
|
|
990
|
+
| acquisition | `acquisition:list:*`, `acquisition:deal:*` | `/api/external/acquisition/lists*`, `/api/external/deals*` | implemented read-only scope |
|
|
991
|
+
| client | `client:*` | `/api/external/clients` | implemented read/write scope |
|
|
992
|
+
| agent | `agent:list`, `agent:get` | `/api/external/agents*` | implemented read-only scope |
|
|
993
|
+
| session | `session:create`, `session:turn`, `session:messages`, `session:list`, `session:get`, `session:end` | `/api/external/sessions*` | implemented multi-turn scope |
|
|
994
|
+
| queue | `queue:list`, `queue:get`, `queue:select`, `queue:expire`, `queue:status` | `/api/external/command-queue*` | implemented |
|
|
995
|
+
| schedule | `schedule:list`, `schedule:get`, `schedule:create`, `schedule:update` | `/api/external/task-scheduler/schedules*` | implemented |
|
|
996
|
+
| skill | `skill:scaffold`, `skill:check-coverage` | local CLI catalog and `.claude/registries/skill-coverage.json` | implemented developer tooling |
|
|
997
|
+
| content | `content:list`, `content:get`, `content:board`, `content:queue`, `content:pipeline`, `content:distributions`, `content:review` | `/api/external/content*` | implemented read + review-write scope |
|
|
998
|
+
| seo | none | not scoped here | deferred |
|
|
999
|
+
| monitoring | none | not scoped here | deferred |
|
|
916
1000
|
|
|
917
1001
|
### Promotion Criteria
|
|
918
1002
|
|
|
@@ -925,4 +1009,4 @@ A domain should meet all four criteria before gaining a `*:list` / `*:get` surfa
|
|
|
925
1009
|
|
|
926
1010
|
---
|
|
927
1011
|
|
|
928
|
-
**Last Updated:** 2026-08-
|
|
1012
|
+
**Last Updated:** 2026-08-14
|
|
@@ -23,4 +23,4 @@ Platform adapters are singletons — import them directly, no credential require
|
|
|
23
23
|
| Execution | `execution` | `trigger`, `triggerAsync` | Execution — trigger other workflows or agents within the same organization. |
|
|
24
24
|
| Email | `email` | `send` | Email — send platform emails (from notifications@elevasis.io) to organization members. |
|
|
25
25
|
| Artifacts | `artifacts` | `listArtifacts`, `createArtifact`, `getActive` | Artifacts — org-scoped governing-document store for rules documents, idea banks, and ICP docs. |
|
|
26
|
-
| Content | `content` | `createItem`, `getItem`, `listItems`, `updateItem`, `createAttempt`, `listAttempts`, `createDistribution`, `updateDistribution` | Content — create and query content_items/content_item_attempts/content_distributions rows for the content pipeline. |
|
|
26
|
+
| Content | `content` | `createItem`, `getItem`, `listItems`, `updateItem`, `createAttempt`, `listAttempts`, `updateAttempt`, `createSourceAsset`, `getSourceAsset`, `listSourceAssets`, `updateSourceAsset`, `createDistribution`, `updateDistribution` | Content — create and query content_items/content_item_attempts/content_distributions rows for the content pipeline. |
|
|
@@ -191,8 +191,6 @@ const qualified = await platform.call({
|
|
|
191
191
|
|
|
192
192
|
**Credential setup:** Create a credential with provider `supabase` -- config fields are `url` and `serviceRoleKey`. Workflows always use the service role key (server-side, no RLS).
|
|
193
193
|
|
|
194
|
-
**`/database init`:** Guided setup that stores your Supabase credential, generates `data/schema.ts`, and creates `docs/database.mdx`.
|
|
195
|
-
|
|
196
194
|
---
|
|
197
195
|
|
|
198
196
|
## Documentation
|
|
@@ -449,7 +449,7 @@ The platform's HITL mechanism works in two parts: your workflow code creates an
|
|
|
449
449
|
|
|
450
450
|
### Creating approval tasks
|
|
451
451
|
|
|
452
|
-
Call `approval.create()` from any workflow step to
|
|
452
|
+
Call `approval.create()` from any workflow step to emit a task to the Command Queue. It does **not** pause or suspend the step -- it inserts a row into `command_queue` and returns `{ id }`, and the workflow keeps running to completion:
|
|
453
453
|
|
|
454
454
|
{/* doc-snippet:skip: illustrative excerpt -- dealId/proposalUrl are shorthand for values from the enclosing step handler's input, not a standalone compilable file */}
|
|
455
455
|
|
|
@@ -458,19 +458,27 @@ import { approval } from '@elevasis/sdk/worker'
|
|
|
458
458
|
|
|
459
459
|
const task = await approval.create({
|
|
460
460
|
actions: [
|
|
461
|
-
{
|
|
461
|
+
{
|
|
462
|
+
id: 'approve',
|
|
463
|
+
label: 'Approve',
|
|
464
|
+
type: 'primary',
|
|
465
|
+
target: { resourceType: 'workflow', resourceId: 'send-proposal' },
|
|
466
|
+
},
|
|
462
467
|
{ id: 'reject', label: 'Reject', type: 'danger' },
|
|
463
468
|
],
|
|
464
469
|
context: { dealId, proposalUrl },
|
|
465
470
|
description: 'Review proposal before sending',
|
|
466
471
|
})
|
|
467
472
|
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
}
|
|
473
|
+
// task is only `{ id: string }` -- the new command_queue row's id.
|
|
474
|
+
// There is no `task.actionId` to branch on here.
|
|
471
475
|
```
|
|
472
476
|
|
|
473
|
-
|
|
477
|
+
**There is no resumption.** This workflow step -- and the whole workflow -- runs to completion and terminates normally right after `approval.create()` returns. When a reviewer later submits an action from the Command Queue, the API starts a **brand-new execution** of the workflow named in that action's `target`, with input `{ ...task.context, actionId, actionPayload }`. The original execution is not revived and cannot be branched on.
|
|
478
|
+
|
|
479
|
+
The consequence for anything you write: state your workflow needs once the reviewer's decision comes back cannot live in a local variable -- the process holding it already exited. Put everything the follow-up execution needs into `context` (it comes back verbatim as part of that execution's input), or persist it somewhere the new execution can look up by id.
|
|
480
|
+
|
|
481
|
+
See [Platform Adapters](../platform-tools/adapters-platform.mdx) for the full `approval.create()` reference.
|
|
474
482
|
|
|
475
483
|
### Built-in Command Center handling
|
|
476
484
|
|