@synap-core/cli 1.6.1 → 1.8.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/commands/agent-run.d.ts +47 -0
- package/dist/commands/agent-run.js +302 -0
- package/dist/commands/agent-run.js.map +1 -0
- package/dist/commands/artifact.d.ts +26 -0
- package/dist/commands/artifact.js +76 -0
- package/dist/commands/artifact.js.map +1 -0
- package/dist/commands/automation.d.ts +47 -0
- package/dist/commands/automation.js +600 -0
- package/dist/commands/automation.js.map +1 -0
- package/dist/commands/cell.d.ts +55 -0
- package/dist/commands/cell.js +277 -0
- package/dist/commands/cell.js.map +1 -0
- package/dist/commands/connect.js +41 -7
- package/dist/commands/connect.js.map +1 -1
- package/dist/commands/connections.js +0 -16
- package/dist/commands/connections.js.map +1 -1
- package/dist/commands/connectors.d.ts +31 -0
- package/dist/commands/connectors.js +276 -0
- package/dist/commands/connectors.js.map +1 -0
- package/dist/commands/context.d.ts +22 -0
- package/dist/commands/context.js +151 -0
- package/dist/commands/context.js.map +1 -0
- package/dist/commands/data-extra.d.ts +13 -0
- package/dist/commands/data-extra.js +161 -0
- package/dist/commands/data-extra.js.map +1 -0
- package/dist/commands/data.d.ts +13 -9
- package/dist/commands/data.js +220 -109
- package/dist/commands/data.js.map +1 -1
- package/dist/commands/discover.d.ts +18 -0
- package/dist/commands/discover.js +71 -0
- package/dist/commands/discover.js.map +1 -0
- package/dist/commands/doc.d.ts +39 -0
- package/dist/commands/doc.js +130 -0
- package/dist/commands/doc.js.map +1 -0
- package/dist/commands/events.d.ts +26 -0
- package/dist/commands/events.js +68 -0
- package/dist/commands/events.js.map +1 -0
- package/dist/commands/explain.d.ts +21 -0
- package/dist/commands/explain.js +248 -0
- package/dist/commands/explain.js.map +1 -0
- package/dist/commands/graph.d.ts +18 -0
- package/dist/commands/graph.js +73 -0
- package/dist/commands/graph.js.map +1 -0
- package/dist/commands/home.d.ts +12 -0
- package/dist/commands/home.js +54 -0
- package/dist/commands/home.js.map +1 -0
- package/dist/commands/init.d.ts +0 -2
- package/dist/commands/init.js +62 -102
- package/dist/commands/init.js.map +1 -1
- package/dist/commands/keys.d.ts +9 -0
- package/dist/commands/keys.js +79 -0
- package/dist/commands/keys.js.map +1 -0
- package/dist/commands/knowledge.d.ts +32 -18
- package/dist/commands/knowledge.js +303 -53
- package/dist/commands/knowledge.js.map +1 -1
- package/dist/commands/observe.d.ts +9 -0
- package/dist/commands/observe.js +90 -0
- package/dist/commands/observe.js.map +1 -0
- package/dist/commands/open.d.ts +15 -0
- package/dist/commands/open.js +44 -0
- package/dist/commands/open.js.map +1 -0
- package/dist/commands/pods.d.ts +5 -0
- package/dist/commands/pods.js +85 -37
- package/dist/commands/pods.js.map +1 -1
- package/dist/commands/proposals-actions.d.ts +7 -0
- package/dist/commands/proposals-actions.js +40 -0
- package/dist/commands/proposals-actions.js.map +1 -0
- package/dist/commands/providers.d.ts +38 -0
- package/dist/commands/providers.js +170 -0
- package/dist/commands/providers.js.map +1 -0
- package/dist/commands/sessions.d.ts +31 -0
- package/dist/commands/sessions.js +224 -0
- package/dist/commands/sessions.js.map +1 -0
- package/dist/commands/skills-commands.d.ts +26 -0
- package/dist/commands/skills-commands.js +186 -0
- package/dist/commands/skills-commands.js.map +1 -0
- package/dist/commands/skills.d.ts +26 -0
- package/dist/commands/skills.js +195 -0
- package/dist/commands/skills.js.map +1 -0
- package/dist/commands/status.js +68 -30
- package/dist/commands/status.js.map +1 -1
- package/dist/commands/subscribe.d.ts +19 -0
- package/dist/commands/subscribe.js +91 -0
- package/dist/commands/subscribe.js.map +1 -0
- package/dist/commands/view.d.ts +57 -0
- package/dist/commands/view.js +194 -0
- package/dist/commands/view.js.map +1 -0
- package/dist/index.d.ts +3 -4
- package/dist/index.js +823 -206
- package/dist/index.js.map +1 -1
- package/dist/lib/agent-templates.d.ts +25 -0
- package/dist/lib/agent-templates.js +148 -0
- package/dist/lib/agent-templates.js.map +1 -0
- package/dist/lib/agents-config.d.ts +4 -1
- package/dist/lib/agents-config.js +19 -2
- package/dist/lib/agents-config.js.map +1 -1
- package/dist/lib/capture-lane.d.ts +49 -0
- package/dist/lib/capture-lane.js +79 -0
- package/dist/lib/capture-lane.js.map +1 -0
- package/dist/lib/hub-client.d.ts +14 -0
- package/dist/lib/hub-client.js +45 -3
- package/dist/lib/hub-client.js.map +1 -1
- package/dist/lib/pod.d.ts +22 -1
- package/dist/lib/pod.js +32 -2
- package/dist/lib/pod.js.map +1 -1
- package/dist/lib/skills-installer.d.ts +1 -1
- package/dist/lib/skills-installer.js +1 -1
- package/dist/lib/skills-installer.js.map +1 -1
- package/dist/lib/targets.d.ts +17 -1
- package/dist/lib/targets.js +744 -70
- package/dist/lib/targets.js.map +1 -1
- package/node_modules/@synap/hub-rest-client/dist/index.cjs +881 -0
- package/node_modules/@synap/hub-rest-client/dist/index.d.cts +907 -0
- package/node_modules/@synap/hub-rest-client/dist/index.d.ts +907 -0
- package/node_modules/@synap/hub-rest-client/dist/index.js +851 -0
- package/node_modules/@synap/hub-rest-client/package.json +45 -0
- package/node_modules/@synap/hub-rest-client/src/client.ts +1143 -0
- package/node_modules/@synap/hub-rest-client/src/errors.ts +30 -0
- package/node_modules/@synap/hub-rest-client/src/index.ts +111 -0
- package/node_modules/@synap/hub-rest-client/src/setup.ts +77 -0
- package/node_modules/@synap/hub-rest-client/src/types.ts +639 -0
- package/package.json +9 -2
- package/skills/synap/SKILL.md +374 -100
- package/skills/synap-ui/SKILL.md +12 -9
- package/dist/lib/templates.d.ts +0 -11
- package/dist/lib/templates.js +0 -13
- package/dist/lib/templates.js.map +0 -1
- package/dist/templates/agent-os.json +0 -3090
package/skills/synap/SKILL.md
CHANGED
|
@@ -37,17 +37,43 @@ Your job is to turn unstructured input into a **connected** knowledge graph. Iso
|
|
|
37
37
|
|
|
38
38
|
## Mental model
|
|
39
39
|
|
|
40
|
-
Synap is a typed knowledge graph.
|
|
40
|
+
Synap is a typed knowledge graph. **Reading is one verb (`synap ask`) — it routes for you.** Writing is where you must pick the right lane: the destination is decided by the **KIND** of knowledge, not by whichever workspace happens to be active.
|
|
41
|
+
|
|
42
|
+
### Where to write what — the three lanes (decide by KIND)
|
|
43
|
+
|
|
44
|
+
Ask yourself: _who does this knowledge serve?_ **There is no private AI scratchpad** — structuring knowledge into a real lane IS your job. Never write a `note` (that's the human's raw inbox); always `capture` into a lane.
|
|
45
|
+
|
|
46
|
+
| If it… | Lane | Where it goes | Governance |
|
|
47
|
+
| ------------------------------------------------------------------------------------------------------------------------------- | -------------------- | ---------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------- |
|
|
48
|
+
| **is about the CURRENT WORK** — domain know-how for the project/task you're on (incl. a domain-specific gotcha/lesson/decision) | **Work** _(default)_ | a `knowledge` entity in the **active workspace** (`synap capture --type …`) | proposal-gated (it's the user's real data; the workspace IS the domain — Builder ≠ marketing) |
|
|
49
|
+
| **is GLOBAL truth** — a best-practice / runbook / how-to that holds across ALL projects | **Global** | pod-wide procedural `knowledge_keys` (`synap capture --global --type … [--key ns:slug]`) | reviewed for shared truth |
|
|
50
|
+
| **is about the USER** — how they work/talk/decide, their preferences, their life | **User** | pod-wide `user_observation` (`synap observe write` / `record_observation` tool) | inferences are **proposed** (you review); explicit "I always X" auto-saves — never model the user silently |
|
|
51
|
+
|
|
52
|
+
> **Why this matters:** writing to the wrong lane degrades the graph. A gotcha you learned about the **current project** is **Work** (the active workspace — its domain). A best-practice that holds **everywhere** is **Global** (`--global`, pod-wide). A fact about **how the user works** is **User** (pod-wide, inferences proposed). `synap capture` echoes which lane + governance it used; check it.
|
|
53
|
+
|
|
54
|
+
> **Substrate names (tables under the hood):** _semantic_ = `entities` (the `knowledge` profile, workspace-scoped = domain separation), _episodic_ = `knowledge_facts`, _procedural_ = `knowledge_keys` (pod-wide runbooks). `ask` queries across them so you never pick on read.
|
|
55
|
+
|
|
56
|
+
### Data layers — the graph itself
|
|
41
57
|
|
|
42
58
|
| Layer | What it is | When to use |
|
|
43
59
|
| ------------- | ---------------------------------------------- | -------------------------------------------------------- |
|
|
44
60
|
| **Entities** | Typed structured nodes (task, person, …) | Anything worth filtering, sorting, or linking |
|
|
45
61
|
| **Relations** | Typed edges between entities | Making the graph traversable |
|
|
46
62
|
| **Documents** | Long-form markdown attached to an entity | Meeting notes, research writeups, articles |
|
|
47
|
-
| **Memory** | Atomic facts, no structure | Preferences, context, ephemeral notes |
|
|
48
63
|
| **Threads** | Channel conversations, optional entity context | Posting to the user's personal AI channel |
|
|
49
64
|
| **Proposals** | Writes queued for human approval | Governance for some mutations (not an error — see below) |
|
|
50
65
|
|
|
66
|
+
### Key profiles for AI use
|
|
67
|
+
|
|
68
|
+
| Profile slug | Scope | Who writes | Purpose |
|
|
69
|
+
| ------------------ | --------- | -------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
70
|
+
| `note` | pod | **human only** | The human's raw "dump now, structure later" inbox. **The AI never writes a note** — structuring into a lane is its job; use `capture` instead. |
|
|
71
|
+
| `knowledge` | workspace | AI | Validated gotchas/lessons/decisions — the **Work lane** (default `synap capture --type`; ek_type/ek_claim/ek_why). DOMAIN = the workspace (a Builder gotcha ≠ a marketing one). Cross-project runbooks go to `knowledge_keys` via `--global`. |
|
|
72
|
+
| `user_observation` | pod | AI only | Durable user model — habits, communication style, preferences |
|
|
73
|
+
| `decision` | pod | human + AI | Architectural decisions with rationale |
|
|
74
|
+
| `research` | pod | AI | Investigation with sources + conclusion |
|
|
75
|
+
| `question` | pod | human + AI | Open inquiry, closed when a decision answers it |
|
|
76
|
+
|
|
51
77
|
## Quick reference — 90% of tasks in 30 lines
|
|
52
78
|
|
|
53
79
|
```bash
|
|
@@ -56,11 +82,23 @@ synap orient --json # discover userId + works
|
|
|
56
82
|
synap use <workspace-name-or-id> # set active workspace
|
|
57
83
|
synap create entity --profile=task --name="…" --props='{"status":"todo","priority":"high"}' --json
|
|
58
84
|
synap set entity <id> --props='{"status":"done"}' --json # merge-patch (only changed keys)
|
|
59
|
-
synap
|
|
60
|
-
synap
|
|
61
|
-
synap
|
|
85
|
+
synap ask "your question" --json # THE read verb — routes to the right store(s) + shows which answered
|
|
86
|
+
synap capture --type=lesson --claim="…" --json # Work lane (default) — domain knowledge → active workspace
|
|
87
|
+
synap capture --global --type=reference --claim="…" --json # Global lane — pod-wide cross-cutting runbook (knowledge_keys)
|
|
88
|
+
synap observe write "…" --json # User lane — durable user model (inferences proposed)
|
|
62
89
|
```
|
|
63
90
|
|
|
91
|
+
**The canonical verbs:** `ask` (read) · `capture` (structured write — pick a lane:
|
|
92
|
+
Work default / `--global` / `observe` for User) · `orient` (bootstrap). `note` exists
|
|
93
|
+
for the HUMAN's raw "dump now, structure later" inbox — **the AI always `capture`s
|
|
94
|
+
instead.** **Reading is one verb: `ask`** — it classifies your
|
|
95
|
+
question and routes across the three memory substrates (semantic = the typed entity
|
|
96
|
+
graph, procedural = how-to docs, episodic = raw captures), returning one answer
|
|
97
|
+
tagged with which substrate(s) answered (and which, if any, were unavailable). Don't
|
|
98
|
+
pick a store; `ask` picks for you and tells you what it did. (`graph` for an explicit
|
|
99
|
+
traversal and `get`/`show`/`browse` for direct lookups remain; there is no `search`
|
|
100
|
+
or `recall` — `ask` is the door.)
|
|
101
|
+
|
|
64
102
|
```bash
|
|
65
103
|
# REST (when no Bash access)
|
|
66
104
|
POST /api/hub/entities body: { userId, workspaceId?, profileSlug, title, properties }
|
|
@@ -70,26 +108,24 @@ PATCH /api/hub/documents/{id} body: { userId, title?, content? } ← full
|
|
|
70
108
|
POST /api/hub/relations body: { userId, sourceEntityId, targetEntityId, type }
|
|
71
109
|
GET /api/hub/entities?q=…&profileSlug=task&workspaceId=…
|
|
72
110
|
GET /api/hub/entities/{id}/connections?userId=…
|
|
111
|
+
POST /api/hub/knowledge/ask body: { query, workspaceId?, limit? } ← ONE read door, routes across substrates
|
|
73
112
|
POST /api/hub/memory body: { userId, fact }
|
|
74
113
|
GET /api/hub/memory?userId=…&query=…
|
|
75
114
|
```
|
|
76
115
|
|
|
77
|
-
**
|
|
116
|
+
**Profile schemas are runtime-discovered — never hardcoded:**
|
|
78
117
|
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
| `project` | `status` (active/paused/completed/cancelled), `startDate`, `endDate`, `description` |
|
|
84
|
-
| `person` | `email`, `role`, `companyId` (entity_id), `linkedinUrl`, `phone` |
|
|
85
|
-
| `event` | `startDate`, `endDate`, `location`, `attendees` (entity_id[]) |
|
|
86
|
-
| `decision` | `decisionStatus` (draft/accepted/superseded), `decidedAt`, `rationale`, `alternatives`, `projectId` |
|
|
87
|
-
| `research` | `researchStatus` (ongoing/concluded), `questionId` (entity_id), `conclusion`, `researchConfidence` |
|
|
88
|
-
| `question` | `questionStatus` (exploring/answered), `answeredByDecisionId` (entity_id), `projectId` |
|
|
89
|
-
| `deal` | `dealStage` (lead/contacted/qualifying/proposal/won/lost), `estimatedValue` (number) |
|
|
90
|
-
| `document` | `contentType` (markdown/html), `summary`, `projectId` (entity_id) |
|
|
118
|
+
```bash
|
|
119
|
+
synap discover --json # CLI: full profile tree with property schemas + command map
|
|
120
|
+
synap discover --profiles --json # CLI: profiles only
|
|
121
|
+
```
|
|
91
122
|
|
|
92
|
-
|
|
123
|
+
```
|
|
124
|
+
GET /api/hub/discover?userId={userId}&workspaceId={workspaceId}
|
|
125
|
+
→ { profiles: [{ slug, displayName, scope, properties: [{ slug, type, options? }], createCommand }], commands: {...} }
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
Call this once at session start. The response includes every system profile and any custom workspace profiles the user has created, each with its full property schema. Use `createCommand` per profile as a copy-paste template. Do not rely on a static property list — it will drift.
|
|
93
129
|
|
|
94
130
|
**Load more detail on demand** (`GET /api/hub/skills/system?sections=<id>`):
|
|
95
131
|
|
|
@@ -122,15 +158,17 @@ GET /api/hub/users/me
|
|
|
122
158
|
GET /api/hub/workspaces
|
|
123
159
|
→ [{ id, name, role }] ← workspaces[0].id if only one
|
|
124
160
|
|
|
125
|
-
GET /api/hub/
|
|
126
|
-
→ [{ slug, displayName,
|
|
161
|
+
GET /api/hub/discover?userId={userId}&workspaceId={workspaceId}
|
|
162
|
+
→ { profiles: [{ slug, displayName, scope, properties, createCommand }], commands: {...} }
|
|
163
|
+
← replaces /profiles — includes property schemas + custom workspace profiles
|
|
127
164
|
```
|
|
128
165
|
|
|
129
|
-
`
|
|
130
|
-
`
|
|
166
|
+
`scope: "pod"` = visible across all workspaces (note, task, project, person, company, bookmark, event, contact, article, website).
|
|
167
|
+
`scope: "workspace"` = scoped to one workspace (deal, file, capture, custom profiles).
|
|
168
|
+
Each profile includes its full property schema. Use `createCommand` as a template.
|
|
131
169
|
|
|
132
|
-
**2.
|
|
133
|
-
Before answering any question about the user's projects, tasks, contacts, decisions, or anything they might have captured —
|
|
170
|
+
**2. Ask before answering**
|
|
171
|
+
Before answering any question about the user's projects, tasks, contacts, decisions, or anything they might have captured — `ask` Synap first (`synap ask "…"` / `POST /api/hub/knowledge/ask`). It routes across all three memory substrates in one call. Do not answer from your training or context window when Synap may have the authoritative answer.
|
|
134
172
|
|
|
135
173
|
**3. Save proactively — without waiting to be asked**
|
|
136
174
|
When the user shares a decision, task, meeting outcome, contact, or any durable information: save it. Don't ask "should I save this?" for obviously important information. Use:
|
|
@@ -157,8 +195,7 @@ The CLI reads the active pod and workspace from `~/.synap/config.json`. Set cont
|
|
|
157
195
|
|
|
158
196
|
```bash
|
|
159
197
|
synap pods use <profile-name> # switch active pod
|
|
160
|
-
synap use <workspace-id> # switch active workspace
|
|
161
|
-
synap workspace provision-agent --json # provision your agent workspace + auto-sets it active
|
|
198
|
+
synap use <workspace-id> # switch active workspace (captures land here — it IS the domain)
|
|
162
199
|
```
|
|
163
200
|
|
|
164
201
|
**Always orient first:**
|
|
@@ -169,14 +206,14 @@ synap orient --json
|
|
|
169
206
|
# Never hardcode workspace IDs — discover them here.
|
|
170
207
|
```
|
|
171
208
|
|
|
172
|
-
**
|
|
209
|
+
**Ask (the one read verb — routes across all substrates):**
|
|
173
210
|
|
|
174
211
|
```bash
|
|
175
|
-
synap
|
|
176
|
-
synap
|
|
177
|
-
synap
|
|
178
|
-
# Omit --workspace
|
|
179
|
-
#
|
|
212
|
+
synap ask "project ideas" --json
|
|
213
|
+
synap ask "what did Antoine decide about auth" --workspace=<id> --json
|
|
214
|
+
synap ask "how do I deploy the backend" --json # routes to procedural how-to docs
|
|
215
|
+
# Omit --workspace for pod-wide; include it to scope to one workspace.
|
|
216
|
+
# `ask` classifies intent and unions the right substrate(s) — it replaces search/recall.
|
|
180
217
|
```
|
|
181
218
|
|
|
182
219
|
**Read entities:**
|
|
@@ -188,17 +225,19 @@ synap list entities --profile=task --workspace=<id> --json
|
|
|
188
225
|
synap get entity <id> --json
|
|
189
226
|
```
|
|
190
227
|
|
|
191
|
-
**
|
|
228
|
+
**Capturing a decision (the AI structures — it never `note`s):**
|
|
192
229
|
|
|
193
230
|
```bash
|
|
194
|
-
synap
|
|
195
|
-
synap
|
|
231
|
+
synap capture --type decision --claim "Use Typesense for entity search" --json
|
|
232
|
+
# Retrieve later with the one read verb: synap ask "Typesense decision"
|
|
196
233
|
```
|
|
197
234
|
|
|
235
|
+
> `synap note` is the HUMAN's raw "dump now, structure later" inbox. As the AI, always `capture` into a lane — structuring is your job.
|
|
236
|
+
|
|
198
237
|
**Structured knowledge (durable, typed, searchable — preferred for engineering learnings):**
|
|
199
238
|
|
|
200
239
|
```bash
|
|
201
|
-
#
|
|
240
|
+
# Work lane (default): a domain gotcha/lesson/decision → knowledge entity in the ACTIVE workspace
|
|
202
241
|
synap capture --type gotcha --claim "Hono static routes must come before /:id" \
|
|
203
242
|
--why "First-match routing; dynamic routes eat static ones" \
|
|
204
243
|
--tags "repo:synap-backend,layer:routing" --json
|
|
@@ -206,17 +245,20 @@ synap capture --type gotcha --claim "Hono static routes must come before /:id" \
|
|
|
206
245
|
synap capture --type lesson --claim "code-read ≠ runtime-true for library APIs" \
|
|
207
246
|
--evidence "tldraw 2.4.6 binding API changed silently from props.start.boundShapeId"
|
|
208
247
|
|
|
209
|
-
#
|
|
210
|
-
synap
|
|
211
|
-
|
|
248
|
+
# A quick decision-note is a typed knowledge entry (ek_type=decision):
|
|
249
|
+
synap capture --type decision --claim "Use Typesense for entity search" \
|
|
250
|
+
--why "pgvector deferred to V1; Typesense ships now" --json
|
|
212
251
|
|
|
213
|
-
#
|
|
214
|
-
synap
|
|
252
|
+
# Global lane: a runbook/best-practice that holds across ALL projects → pod-wide knowledge_keys
|
|
253
|
+
synap capture --global --type reference --claim "Always fix the canonical path, never a workaround" \
|
|
254
|
+
--key "principle:root-cause" --json
|
|
255
|
+
|
|
256
|
+
# Retrieve any of it later with the one read verb (it spans every lane):
|
|
257
|
+
synap ask "hono routing gotcha" --json
|
|
215
258
|
```
|
|
216
259
|
|
|
217
|
-
`synap capture`
|
|
218
|
-
|
|
219
|
-
Use structured knowledge for anything worth remembering across sessions and projects.
|
|
260
|
+
`synap capture --type` writes a typed **`knowledge`** entity in the **active workspace** (the Work lane); `ek_type` (gotcha|lesson|decision|reference) discriminates the kind — **one store, type tags, not a residual dump**. It's workspace-scoped, so the active workspace supplies the domain — a Builder gotcha ≠ a marketing one (there is no `engineering_knowledge`). Add **`--global`** to write a pod-wide cross-cutting runbook to `knowledge_keys` instead. A formal **decision RECORD** (rationale, alternatives, superseded-by lifecycle) is a different artifact — use smart `synap capture "<free text>"` or `synap create entity --profile=decision`. Retrieve everything with `synap ask`.
|
|
261
|
+
Use `capture` for anything worth remembering across sessions and projects.
|
|
220
262
|
|
|
221
263
|
**Write:**
|
|
222
264
|
|
|
@@ -232,7 +274,7 @@ synap set entity <id> --props='{"status":"done"}' --json
|
|
|
232
274
|
- Always use `--json` when calling from code — clean stdout, no spinners, machine-parseable
|
|
233
275
|
- Run `synap orient` first to discover workspace IDs — never hardcode them
|
|
234
276
|
- Omit `--workspace` to operate pod-wide; include it to scope to a specific workspace
|
|
235
|
-
- `synap
|
|
277
|
+
- `synap ask` is the one read verb — it routes keyword + semantic + procedural automatically; you never choose a search backend.
|
|
236
278
|
|
|
237
279
|
---
|
|
238
280
|
|
|
@@ -418,6 +460,47 @@ Every write returns a `status` field:
|
|
|
418
460
|
2. Give them the link to review — `reviewUrl` opens the proposal in Synap Studio. Show the link as-is.
|
|
419
461
|
3. Move on with the conversation. Don't wait or poll.
|
|
420
462
|
|
|
463
|
+
### The `reasoning` field — required, structured, contextual
|
|
464
|
+
|
|
465
|
+
Every write call (create, update, delete) must include a `reasoning` field. This is what the governance reviewer reads to understand your decision. It is **not optional**.
|
|
466
|
+
|
|
467
|
+
Use this exact structure:
|
|
468
|
+
|
|
469
|
+
```
|
|
470
|
+
Context: [what the user said or what event triggered this write — one sentence]
|
|
471
|
+
Intent: [what this entity or change accomplishes — one sentence]
|
|
472
|
+
Links: [actual entity IDs or slugs this relates to, e.g. "ent_abc, ent_xyz"]
|
|
473
|
+
```
|
|
474
|
+
|
|
475
|
+
For updates, add:
|
|
476
|
+
|
|
477
|
+
```
|
|
478
|
+
Changed: [field] [old value] → [new value]
|
|
479
|
+
```
|
|
480
|
+
|
|
481
|
+
**Example (create):**
|
|
482
|
+
|
|
483
|
+
```
|
|
484
|
+
Context: User asked to track the Acme deal they mentioned in today's call.
|
|
485
|
+
Intent: Creates a deal entity for Acme at lead stage linked to Alice Johnson.
|
|
486
|
+
Links: ent_person_alice_johnson, ent_company_acme
|
|
487
|
+
```
|
|
488
|
+
|
|
489
|
+
**Example (update):**
|
|
490
|
+
|
|
491
|
+
```
|
|
492
|
+
Context: User confirmed the Acme deal moved to proposal stage.
|
|
493
|
+
Intent: Advances the deal through the pipeline so it appears in the proposal view.
|
|
494
|
+
Links: ent_deal_acme, ent_person_alice_johnson
|
|
495
|
+
Changed: dealStage lead → proposal
|
|
496
|
+
```
|
|
497
|
+
|
|
498
|
+
Rules:
|
|
499
|
+
|
|
500
|
+
- One sentence per field. No padding.
|
|
501
|
+
- `Links` must reference real entity IDs or slugs visible in the current context — not descriptions like "the related project".
|
|
502
|
+
- **"Agent requires proposal for all write operations."** is never acceptable as a `reasoning` value. That is an internal governance message, not agent reasoning. Write it and the proposal is meaningless to the reviewer.
|
|
503
|
+
|
|
421
504
|
Example response to the user:
|
|
422
505
|
|
|
423
506
|
> I queued **Delete task "Q2 plan review"** for your review. Destructive actions need your approval. Open it: https://studio.synap.live/proposals/prp_abc
|
|
@@ -566,7 +649,20 @@ POST /api/hub/threads/{threadId}/messages
|
|
|
566
649
|
|
|
567
650
|
## Reading
|
|
568
651
|
|
|
569
|
-
|
|
652
|
+
**Start with `ask` — the one routed read door.** It classifies the question and
|
|
653
|
+
queries the right substrate(s) for you, returning a glass-box answer (which
|
|
654
|
+
substrates answered, which were unavailable, plus the engine's verdict). Reach for
|
|
655
|
+
the low-level doors below only when you deliberately want a single substrate or a
|
|
656
|
+
specific shape (a graph traversal, a typed-entity filter, an entity's neighborhood).
|
|
657
|
+
|
|
658
|
+
```
|
|
659
|
+
# THE read door — routes across semantic / procedural / episodic, tells you which answered
|
|
660
|
+
POST /api/hub/knowledge/ask body: { query, workspaceId?, limit? }
|
|
661
|
+
→ { query, routedTo: [...], primary, answers: [{ substrate, items, status }], degraded, understanding, verdict }
|
|
662
|
+
```
|
|
663
|
+
|
|
664
|
+
Low-level doors (`ask` routes to these — graph-based, not semantic; type filter →
|
|
665
|
+
relations → neighborhood):
|
|
570
666
|
|
|
571
667
|
```
|
|
572
668
|
# Keyword search across everything (entities, documents, views, threads)
|
|
@@ -799,6 +895,52 @@ When the user is interacting with Synap's AI Companion (the in-browser chat pane
|
|
|
799
895
|
- **Only in Companion replies.** These patterns are silently ignored in non-companion channels, documents, and memory. Do not use them there.
|
|
800
896
|
- **Combine with prose.** Don't lead with a chip — embed it naturally: `"Here are your open deals → [[view:xyz|Deals Pipeline]] · [[open:side|view:xyz]]"`
|
|
801
897
|
|
|
898
|
+
## Focus Sessions — Goal-Bound Work Rooms
|
|
899
|
+
|
|
900
|
+
A **focus session** is a named, multi-step work room where you and AI agents collaborate on a specific goal. Use one whenever the work has a clear end state, will take more than one exchange, or involves multiple agents.
|
|
901
|
+
|
|
902
|
+
**When to propose a session** (via the proposal system — always ask first):
|
|
903
|
+
|
|
904
|
+
- Research with 5+ sources → decision memo
|
|
905
|
+
- Lead generation sprint → qualified list + outreach drafts
|
|
906
|
+
- Incident investigation → postmortem doc
|
|
907
|
+
- Data import → structured knowledge base
|
|
908
|
+
- Any task you'd naturally call "a project" rather than "a question"
|
|
909
|
+
|
|
910
|
+
**How the AI proposes a session:**
|
|
911
|
+
|
|
912
|
+
```
|
|
913
|
+
create_proposal with targetType: "focus_session"
|
|
914
|
+
→ user reviews goal + rationale + expected outputs in ProposalReviewBoard
|
|
915
|
+
→ on approval, session is created in focus_sessions table
|
|
916
|
+
→ AI updates progress (0→100) via PATCH /api/hub/focus-sessions/:id { workspaceId, progress: N }
|
|
917
|
+
→ session auto-surfaces in the Active Sessions bento widget on the user's home
|
|
918
|
+
```
|
|
919
|
+
|
|
920
|
+
**Session templates** (pass as `templateId`):
|
|
921
|
+
`research-room` · `lead-sprint` · `decision-memo` · `import-cleanup` · `incident-room` · `campaign-intel`
|
|
922
|
+
|
|
923
|
+
**Hub Protocol REST** (for IS → backend; always include `workspaceId`):
|
|
924
|
+
|
|
925
|
+
- `POST /api/hub/focus-sessions` — create (include `correlationId` for idempotency)
|
|
926
|
+
- `GET /api/hub/focus-sessions/:id?workspaceId=<id>` — read
|
|
927
|
+
- `PATCH /api/hub/focus-sessions/:id` — update `{ workspaceId, progress, status, goal, agentIds }`
|
|
928
|
+
|
|
929
|
+
**CLI** (use when running as Claude Code / OpenClaw agent):
|
|
930
|
+
|
|
931
|
+
```bash
|
|
932
|
+
synap session start --goal "<goal>" [--workspace <id>] # create + start a session
|
|
933
|
+
synap session list [--workspace <id>] [--status active|paused|closed] # list sessions
|
|
934
|
+
synap session get <id> [--workspace <id>] # read a session
|
|
935
|
+
synap session update <id> --workspace <id> --progress 50 # report progress
|
|
936
|
+
synap session update <id> --workspace <id> --status paused # pause
|
|
937
|
+
synap session close <id> --workspace <id> [--recap "what was done"] # close + recap
|
|
938
|
+
```
|
|
939
|
+
|
|
940
|
+
Note: `synap session start` creates a session directly (the agent-facing path). All hub-protocol writes are governance-gated server-side; the in-browser AI companion surfaces session creation through the proposal flow.
|
|
941
|
+
|
|
942
|
+
**Discoverability**: the `active-sessions` bento widget is on the default home dashboard. Sessions group their related proposals under a shared `correlationId` in the Proposal Review Board.
|
|
943
|
+
|
|
802
944
|
## When you need more
|
|
803
945
|
|
|
804
946
|
- Linking conventions, auto-sync table, relation types → **`linking.md`**
|
|
@@ -821,87 +963,225 @@ ViewFrame is the standard way to create custom data visualizations in Synap. Use
|
|
|
821
963
|
|
|
822
964
|
### What ViewFrame Is
|
|
823
965
|
|
|
824
|
-
- A sandboxed iframe that renders
|
|
825
|
-
- Dependencies resolved at runtime via **esm.sh import maps** — no build step
|
|
826
|
-
- The host injects a `SynapWidget` bridge for data access
|
|
966
|
+
- A sandboxed iframe that renders **one ES module** that default-exports a React component (or plain JS)
|
|
967
|
+
- Dependencies resolved at runtime via **esm.sh import maps** built from the `deps` map — no build step required
|
|
968
|
+
- The host injects a `SynapWidget` bridge for data access and shell actions
|
|
827
969
|
- Security: `sandbox="allow-scripts allow-modals allow-popups"`, no `allow-same-origin`, no cookies, no pod token
|
|
828
970
|
|
|
829
|
-
###
|
|
971
|
+
### Authoring contract
|
|
830
972
|
|
|
831
|
-
|
|
973
|
+
A ViewFrame cell is **one self-contained ES module** (inline in `rendererSource`):
|
|
832
974
|
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
975
|
+
- The module **default-exports a React component** (or calls `SynapWidget.onInit()` for plain JS).
|
|
976
|
+
- Bare imports (`"react"`, `"recharts"`, etc.) are resolved via the esm.sh import map generated from `deps`.
|
|
977
|
+
- **No bundler, no `import` of local files** — everything is either inlined or declared in `deps`.
|
|
978
|
+
- External CSS is not supported; inline `<style>` tags or CSS-in-JS only.
|
|
837
979
|
|
|
838
|
-
|
|
980
|
+
### Register a Cell via the Hub Protocol (canonical path)
|
|
981
|
+
|
|
982
|
+
**Use `POST /api/hub/cells/define` — this is the canonical Hub Protocol path for AI-generated cells.**
|
|
983
|
+
|
|
984
|
+
It is idempotent (upserts on typeKey), pod-global by default (no workspaceId needed), and immediately available across all of the user's workspaces without any proposal step.
|
|
839
985
|
|
|
840
986
|
```
|
|
841
|
-
POST /api/hub/
|
|
987
|
+
POST /api/hub/cells/define
|
|
842
988
|
Authorization: Bearer {SYNAP_HUB_API_KEY}
|
|
843
989
|
Content-Type: application/json
|
|
844
990
|
|
|
845
991
|
{
|
|
846
|
-
"userId": "{SYNAP_USER_ID}",
|
|
847
|
-
"workspaceId": "{workspaceId}",
|
|
848
|
-
"typeKey": "deal-stage-funnel",
|
|
849
992
|
"name": "Deal Stage Funnel",
|
|
850
|
-
"
|
|
851
|
-
"
|
|
852
|
-
"
|
|
853
|
-
"defaultSize": { "w": 8, "h": 6 },
|
|
854
|
-
"
|
|
993
|
+
"rendererSource": "<!DOCTYPE html>…</html>",
|
|
994
|
+
"typeKey": "deal-stage-funnel", // optional — derived from name if omitted
|
|
995
|
+
"description": "Funnel chart of deal pipeline stages", // optional
|
|
996
|
+
"defaultSize": { "w": 8, "h": 6 }, // optional
|
|
997
|
+
"deps": { // optional — npm packages pinned for the import map
|
|
998
|
+
"recharts": "2.12.0",
|
|
999
|
+
"@tanstack/react-table": "8"
|
|
1000
|
+
}
|
|
855
1001
|
}
|
|
856
1002
|
```
|
|
857
1003
|
|
|
858
|
-
`
|
|
1004
|
+
**`deps` rules:**
|
|
1005
|
+
|
|
1006
|
+
- Keys are npm package names (`pkg` or `@scope/pkg`). URLs and protocols are rejected.
|
|
1007
|
+
- Values are version strings or `"latest"` — used verbatim in the esm.sh import map URL.
|
|
1008
|
+
- Maximum 30 entries. Omit `deps` (or pass `{}`) for React-only cells (React is always available).
|
|
1009
|
+
|
|
1010
|
+
**`workspaceId` is intentionally omitted** — cells defined without it are pod-global (`workspaceId IS NULL`), visible in every workspace the user owns. Pass `workspaceId` only when you explicitly want a cell scoped to a single workspace.
|
|
1011
|
+
|
|
1012
|
+
Response: `{ "success": true, "typeKey": "generated:deal-stage-funnel" }`
|
|
1013
|
+
|
|
1014
|
+
The typeKey is auto-prefixed `generated:` when not explicitly provided.
|
|
1015
|
+
|
|
1016
|
+
**List cells (all pod-global + optionally workspace-specific):**
|
|
1017
|
+
|
|
1018
|
+
```
|
|
1019
|
+
GET /api/hub/cells — pod-global only
|
|
1020
|
+
GET /api/hub/cells?workspaceId={id} — pod-global + workspace-scoped
|
|
1021
|
+
Authorization: Bearer {SYNAP_HUB_API_KEY}
|
|
1022
|
+
```
|
|
1023
|
+
|
|
1024
|
+
**Delete a cell:**
|
|
1025
|
+
|
|
1026
|
+
```
|
|
1027
|
+
DELETE /api/hub/cells/{typeKey} — pod-global row
|
|
1028
|
+
DELETE /api/hub/cells/{typeKey}?workspaceId={id} — workspace-scoped row
|
|
1029
|
+
Authorization: Bearer {SYNAP_HUB_API_KEY}
|
|
1030
|
+
```
|
|
1031
|
+
|
|
1032
|
+
**Open the cell in the browser (deep link):**
|
|
1033
|
+
|
|
1034
|
+
```
|
|
1035
|
+
synap://open/cell/{typeKey}
|
|
1036
|
+
```
|
|
1037
|
+
|
|
1038
|
+
The browser receives this deep link, looks the typeKey up in the cell registry (which polls `widget_definitions` every 10s), and opens it as a side panel tab with the cell's registered `meta.name` as the tab title.
|
|
1039
|
+
|
|
1040
|
+
**Full AI artifact workflow:**
|
|
1041
|
+
|
|
1042
|
+
```
|
|
1043
|
+
// 1. Generate the HTML/React cell
|
|
1044
|
+
POST /api/hub/cells/define
|
|
1045
|
+
{ "name": "Q2 Revenue Report", "rendererSource": "<!DOCTYPE html>…</html>",
|
|
1046
|
+
"deps": { "recharts": "2.12.0" } }
|
|
1047
|
+
// → { "success": true, "typeKey": "generated:q2-revenue-report" }
|
|
1048
|
+
|
|
1049
|
+
// 2. Open it in the user's browser
|
|
1050
|
+
synap://open/cell/generated:q2-revenue-report
|
|
1051
|
+
```
|
|
1052
|
+
|
|
1053
|
+
The cell appears immediately in the side panel with "Q2 Revenue Report" as the tab title. It persists across sessions and is available from any workspace.
|
|
1054
|
+
|
|
1055
|
+
> **Note:** `POST /api/hub/widget-definitions` (tRPC path) still works but is the internal/admin path. Use `POST /api/hub/cells/define` for all agent-generated cells.
|
|
1056
|
+
|
|
1057
|
+
### CLI commands (when running as Claude Code / OpenClaw agent)
|
|
859
1058
|
|
|
860
|
-
|
|
1059
|
+
```bash
|
|
1060
|
+
# Build a multi-file cell source into a single ES module bundle + emit deps map
|
|
1061
|
+
synap cell build <entry> # e.g. synap cell build ./src/my-chart.tsx
|
|
1062
|
+
# → prints bundled source to stdout and writes deps.json alongside the entry
|
|
1063
|
+
|
|
1064
|
+
# Push a built cell (source + deps) to the pod
|
|
1065
|
+
synap cell define \
|
|
1066
|
+
--name "My Chart" \
|
|
1067
|
+
--source ./dist/my-chart.js \
|
|
1068
|
+
--deps ./deps.json \
|
|
1069
|
+
[--typeKey my-chart] \
|
|
1070
|
+
[--workspace <id>]
|
|
1071
|
+
|
|
1072
|
+
# Document operations (attach prose or reports to entities)
|
|
1073
|
+
synap doc create --title "Q2 Report" --content ./report.md --entity <entityId>
|
|
1074
|
+
synap doc update <docId> --content ./updated-report.md
|
|
1075
|
+
|
|
1076
|
+
# Arrange widgets on an existing bento view
|
|
1077
|
+
synap view arrange <viewId> --blocks '[{"id":"b1","kind":"widget","widgetKind":"generated:my-chart","layout":{"x":0,"y":0,"w":8,"h":6}}]'
|
|
1078
|
+
```
|
|
861
1079
|
|
|
862
1080
|
### The SynapWidget Bridge (inside the iframe)
|
|
863
1081
|
|
|
864
1082
|
`window.SynapWidget` is injected automatically — do NOT import or `<script>` it.
|
|
865
1083
|
|
|
1084
|
+
#### Queries (read-only, always approved)
|
|
1085
|
+
|
|
866
1086
|
```js
|
|
867
1087
|
SynapWidget.onInit(async ({ config, context }) => {
|
|
868
1088
|
// context: { workspaceId, viewId?, entityId?, sdkVersion }
|
|
869
|
-
|
|
870
|
-
|
|
1089
|
+
|
|
1090
|
+
// List entities
|
|
1091
|
+
const deals = await SynapWidget.query("entities.list", {
|
|
1092
|
+
profileSlug: "deal",
|
|
871
1093
|
limit: 200,
|
|
872
1094
|
});
|
|
873
|
-
|
|
1095
|
+
|
|
1096
|
+
// Get a single entity
|
|
1097
|
+
const entity = await SynapWidget.query("entities.get", { id: "uuid" });
|
|
1098
|
+
|
|
1099
|
+
// List views
|
|
1100
|
+
const views = await SynapWidget.query("views.list", {
|
|
1101
|
+
workspaceId: context.workspaceId,
|
|
1102
|
+
});
|
|
1103
|
+
|
|
1104
|
+
// List profiles
|
|
1105
|
+
const profiles = await SynapWidget.query("profiles.list", {});
|
|
1106
|
+
|
|
1107
|
+
render(deals ?? []);
|
|
874
1108
|
SynapWidget.resize(document.body.scrollHeight);
|
|
875
1109
|
});
|
|
876
1110
|
```
|
|
877
1111
|
|
|
878
1112
|
All `query()` calls return a Promise. Entity shape: `{ id, title, profileSlug, properties, createdAt, … }`.
|
|
879
1113
|
|
|
880
|
-
|
|
1114
|
+
#### Mutations (governance-gated — return `{ status: "approved" | "proposed" | "denied" }`)
|
|
1115
|
+
|
|
1116
|
+
```js
|
|
1117
|
+
// Create an entity
|
|
1118
|
+
const result = await SynapWidget.mutate("create_entity", {
|
|
1119
|
+
profileSlug: "task",
|
|
1120
|
+
title: "Follow up",
|
|
1121
|
+
properties: { status: "todo" },
|
|
1122
|
+
});
|
|
1123
|
+
// result.status === "approved" → result.id is the new entity id
|
|
1124
|
+
// result.status === "proposed" → result.proposalId, result.reviewUrl
|
|
1125
|
+
|
|
1126
|
+
// Update an entity
|
|
1127
|
+
await SynapWidget.mutate("update_entity", {
|
|
1128
|
+
id: "uuid",
|
|
1129
|
+
properties: { status: "done" },
|
|
1130
|
+
});
|
|
1131
|
+
|
|
1132
|
+
// Delete an entity (always proposed for agent-generated cells)
|
|
1133
|
+
await SynapWidget.mutate("delete_entity", { id: "uuid" });
|
|
1134
|
+
|
|
1135
|
+
// Create a relation
|
|
1136
|
+
await SynapWidget.mutate("create_relation", {
|
|
1137
|
+
sourceEntityId: "uuid-a",
|
|
1138
|
+
targetEntityId: "uuid-b",
|
|
1139
|
+
type: "related_to",
|
|
1140
|
+
});
|
|
1141
|
+
```
|
|
1142
|
+
|
|
1143
|
+
**Always check `result.status`.** `"proposed"` is not an error — surface `result.reviewUrl` to the user.
|
|
1144
|
+
|
|
1145
|
+
#### Shell actions
|
|
881
1146
|
|
|
882
1147
|
```js
|
|
883
|
-
SynapWidget.navigate({ entityId: "entity-uuid" }); //
|
|
884
|
-
SynapWidget.
|
|
1148
|
+
SynapWidget.navigate({ entityId: "entity-uuid" }); // open entity detail in side panel
|
|
1149
|
+
SynapWidget.openPanel("entity-detail", { entityId: "uuid" }); // explicit panel open
|
|
1150
|
+
SynapWidget.toast("Saved!", "success"); // 'success' | 'error' | 'info'
|
|
1151
|
+
SynapWidget.resize(document.body.scrollHeight); // resize the iframe to content height
|
|
1152
|
+
SynapWidget.updateContext({ viewId: "uuid" }); // update ambient context
|
|
1153
|
+
|
|
1154
|
+
// Subscribe to live entity changes
|
|
1155
|
+
SynapWidget.subscribe("entity:changed", ({ entityId }) => {
|
|
1156
|
+
// re-fetch and re-render when any entity in the pod changes
|
|
1157
|
+
});
|
|
885
1158
|
```
|
|
886
1159
|
|
|
887
1160
|
### Common Dependency Patterns (esm.sh import map)
|
|
888
1161
|
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
1162
|
+
The `deps` map in `/cells/define` drives the import map. Each key becomes a bare specifier in the `<script type="importmap">`, resolved to `https://esm.sh/<pkg>@<version>`.
|
|
1163
|
+
|
|
1164
|
+
```json
|
|
1165
|
+
// deps in the define call:
|
|
1166
|
+
{ "recharts": "2.12.0", "d3": "7" }
|
|
1167
|
+
|
|
1168
|
+
// → generates this importmap inside the frame:
|
|
1169
|
+
{
|
|
1170
|
+
"imports": {
|
|
1171
|
+
"react": "https://esm.sh/react@19",
|
|
1172
|
+
"react-dom/client": "https://esm.sh/react-dom@19/client",
|
|
1173
|
+
"react/jsx-runtime": "https://esm.sh/react@19/jsx-runtime",
|
|
1174
|
+
"recharts": "https://esm.sh/recharts@2.12.0",
|
|
1175
|
+
"d3": "https://esm.sh/d3@7"
|
|
898
1176
|
}
|
|
899
|
-
|
|
1177
|
+
}
|
|
900
1178
|
```
|
|
901
1179
|
|
|
1180
|
+
React 19 core entries are always injected by the host — never put them in `deps`.
|
|
1181
|
+
|
|
902
1182
|
Common library choices:
|
|
903
1183
|
|
|
904
|
-
| Category | Packages
|
|
1184
|
+
| Category | Packages (put in `deps`) |
|
|
905
1185
|
| -------- | -------------------------------------------------------------- |
|
|
906
1186
|
| Data viz | `recharts@2.12.0`, `d3@7`, `chart.js@4`, `observable-plot@0.6` |
|
|
907
1187
|
| Tables | `@tanstack/react-table@8` |
|
|
@@ -926,30 +1206,23 @@ Common library choices:
|
|
|
926
1206
|
background: transparent;
|
|
927
1207
|
}
|
|
928
1208
|
</style>
|
|
929
|
-
|
|
930
|
-
{
|
|
931
|
-
"imports": {
|
|
932
|
-
"react": "https://esm.sh/react@19",
|
|
933
|
-
"react-dom/client": "https://esm.sh/react-dom@19/client",
|
|
934
|
-
"react/jsx-runtime": "https://esm.sh/react@19/jsx-runtime"
|
|
935
|
-
}
|
|
936
|
-
}
|
|
937
|
-
</script>
|
|
1209
|
+
<!-- importmap is injected by the host from deps — do not write one manually -->
|
|
938
1210
|
</head>
|
|
939
1211
|
<body>
|
|
940
1212
|
<div id="root"></div>
|
|
941
1213
|
<script type="module">
|
|
942
1214
|
import { createRoot } from "react-dom/client";
|
|
1215
|
+
import { createElement as h, useState } from "react";
|
|
943
1216
|
|
|
944
1217
|
SynapWidget.onInit(async ({ context }) => {
|
|
945
|
-
const items = await SynapWidget.query("
|
|
1218
|
+
const items = await SynapWidget.query("entities.list", {
|
|
946
1219
|
profileSlug: "deal",
|
|
947
1220
|
limit: 200,
|
|
948
1221
|
}).catch(() => []);
|
|
949
1222
|
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
1223
|
+
createRoot(document.getElementById("root")).render(
|
|
1224
|
+
h("p", null, `Loaded ${(items ?? []).length} deals`)
|
|
1225
|
+
);
|
|
953
1226
|
|
|
954
1227
|
SynapWidget.resize(document.body.scrollHeight);
|
|
955
1228
|
});
|
|
@@ -962,10 +1235,11 @@ Common library choices:
|
|
|
962
1235
|
|
|
963
1236
|
- **Always call `SynapWidget.onInit()`** — the host will not send data until you register this handler.
|
|
964
1237
|
- **Call `SynapWidget.resize()`** after rendering to prevent clipping.
|
|
965
|
-
- **Handle errors** — `query()` can fail; always `.catch()`.
|
|
1238
|
+
- **Handle errors** — `query()` and `mutate()` can fail; always `.catch()`.
|
|
1239
|
+
- **Check `result.status` on mutations** — `"proposed"` is governance, not an error; surface `reviewUrl`.
|
|
966
1240
|
- **Transparent background** — `background: transparent` on `body` inherits the host surface color.
|
|
967
1241
|
- **No external fetch** — the sandbox has no cross-origin access; all data must go through `SynapWidget`.
|
|
968
|
-
- **
|
|
1242
|
+
- **Declare all non-React imports in `deps`** — the host generates the import map from that field.
|
|
969
1243
|
|
|
970
1244
|
---
|
|
971
1245
|
|