@quantod/qq 0.3.7 → 1.0.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.d.ts +23 -7
- package/dist/commands.d.ts.map +1 -1
- package/dist/commands.js +134 -30
- package/dist/commands.js.map +1 -1
- package/dist/db.d.ts.map +1 -1
- package/dist/db.js +11 -17
- package/dist/db.js.map +1 -1
- package/dist/http.d.ts +7 -0
- package/dist/http.d.ts.map +1 -0
- package/dist/http.js +113 -0
- package/dist/http.js.map +1 -0
- package/dist/index.js +52 -29
- package/dist/index.js.map +1 -1
- package/dist/mcp.d.ts +2 -0
- package/dist/mcp.d.ts.map +1 -0
- package/dist/mcp.js +232 -0
- package/dist/mcp.js.map +1 -0
- package/dist/resources/chrome.md +33 -0
- package/dist/resources/guide.md +491 -0
- package/dist/resources/pipeline_design.md +192 -0
- package/dist/sdk-templates/javascript.d.ts +2 -0
- package/dist/sdk-templates/javascript.d.ts.map +1 -0
- package/dist/sdk-templates/javascript.js +24 -0
- package/dist/sdk-templates/javascript.js.map +1 -0
- package/dist/sdk-templates/python.d.ts +2 -0
- package/dist/sdk-templates/python.d.ts.map +1 -0
- package/dist/sdk-templates/python.js +31 -0
- package/dist/sdk-templates/python.js.map +1 -0
- package/package.json +10 -3
- package/src/commands.ts +151 -32
- package/src/db.ts +12 -18
- package/src/http.ts +133 -0
- package/src/index.ts +51 -32
- package/src/mcp.ts +226 -0
- package/src/resources/chrome.md +33 -0
- package/src/resources/guide.md +491 -0
- package/src/resources/pipeline_design.md +192 -0
- package/src/sdk-templates/javascript.ts +20 -0
- package/src/sdk-templates/python.ts +27 -0
|
@@ -0,0 +1,491 @@
|
|
|
1
|
+
# QQ — Complete Reference
|
|
2
|
+
|
|
3
|
+
QQ is a persistent, crash-safe pipeline for multi-stage agentic workflows. It is a **state machine with concurrency guarantees**: stages are states, items move through them, and `claim → release` is an atomic transition only one agent can hold at a time. State survives session restarts and crashes.
|
|
4
|
+
|
|
5
|
+
Use QQ when work fans out: web crawling, bulk API enrichment, research pipelines (find → filter → enrich), batch transforms, any task where multiple items move through multiple stages, possibly with concurrent agents working in parallel.
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## Mental Model
|
|
10
|
+
|
|
11
|
+
A **pipeline** holds **items**. Each item lives in exactly one **stage** at a time. Items are pushed once and never deleted — they move between stages, accumulating payload as they go.
|
|
12
|
+
|
|
13
|
+
Stages are just labels. The pipeline has no built-in notion of "first" or "last" — you define the topology in your PIPELINES.md.
|
|
14
|
+
|
|
15
|
+
Items have:
|
|
16
|
+
- `id` — the deduplication key. Pushing an item with an existing id fails.
|
|
17
|
+
- `stage` — the current state
|
|
18
|
+
- `payload` — item data; append-only string by default; YAML by convention.
|
|
19
|
+
- `seq` — the claim token, returned by `claim`, required to `release`
|
|
20
|
+
- `priority` — float, higher = claimed first (default 0.0)
|
|
21
|
+
|
|
22
|
+
---
|
|
23
|
+
|
|
24
|
+
## The Core Loop
|
|
25
|
+
|
|
26
|
+
```
|
|
27
|
+
make_pipeline → create a pipeline, get its name, once per setup
|
|
28
|
+
push (N times) → load items into a stage
|
|
29
|
+
claim → atomically lock one item (returns seq + payload)
|
|
30
|
+
... do work ...
|
|
31
|
+
release → unlock item, move to next stage, append payload
|
|
32
|
+
status → monitor progress
|
|
33
|
+
unstick → recover stuck/lost items
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
**Orchestrator** creates the pipeline, pushes items, spawns subagents, monitors with `status`, calls `batch_read` to collect results, calls `unstick` after.
|
|
37
|
+
|
|
38
|
+
**Subagent** claims one item, does its work, releases to the next stage, repeats until "no items to claim" — then stops and reports back.
|
|
39
|
+
|
|
40
|
+
---
|
|
41
|
+
|
|
42
|
+
## Orchestrator / Subagent Pattern
|
|
43
|
+
|
|
44
|
+
Orchestrator example:
|
|
45
|
+
```
|
|
46
|
+
make_pipeline → "0529_a3f9c2" (store this as Q)
|
|
47
|
+
push Q/pending "url-1" payload: "url: https://a.com"
|
|
48
|
+
push Q/pending "url-2" payload: "url: https://b.com"
|
|
49
|
+
push Q/pending "url-3" payload: "url: https://c.com"
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
Spawn subagents with instructions like:
|
|
53
|
+
> Claim an item from pipeline `0529_a3f9c2` stage `pending`. Open the URL. Extract name, price, availability. Release to `done` appending the fields. If the page fails, release to `failed` appending `error: <reason>`. Repeat until "No items to claim". Report back.
|
|
54
|
+
|
|
55
|
+
Monitor with `status` — done when `pending: [0, 0]` and nothing claimed.
|
|
56
|
+
|
|
57
|
+
---
|
|
58
|
+
|
|
59
|
+
## MCP Tools — Full Reference
|
|
60
|
+
|
|
61
|
+
### make_pipeline
|
|
62
|
+
|
|
63
|
+
Creates a pipeline. Returns its name.
|
|
64
|
+
|
|
65
|
+
Parameters:
|
|
66
|
+
- `name` (string, optional) — custom name. Auto-generated (`MMDD_xxxxxx`) if omitted. Fails if name already taken.
|
|
67
|
+
- `description` (string, optional) — plain-text description of the pipeline's purpose.
|
|
68
|
+
|
|
69
|
+
Returns:
|
|
70
|
+
```yaml
|
|
71
|
+
pipeline: 0529_a3f9c2
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
---
|
|
75
|
+
|
|
76
|
+
### push
|
|
77
|
+
|
|
78
|
+
Pushes an item into a pipeline stage. Fails if an item with the same `id` already exists in the pipeline (deduplication).
|
|
79
|
+
|
|
80
|
+
Parameters:
|
|
81
|
+
- `pipeline` (string) — pipeline name
|
|
82
|
+
- `stage` (string, optional) — destination stage. Supports sub-paths: `enriched/shortlisted`. Defaults to empty string if omitted.
|
|
83
|
+
- `id` (string, optional) — stable unique identifier. Omit to use auto-generated seq number.
|
|
84
|
+
- `payload` (string, optional) — payload string
|
|
85
|
+
- `payloadFormat` (`yaml` | `json` | `text`, optional, default `yaml`) — `yaml`: validate as YAML. `json`: validate as JSON and minify to one line (appended payloads form valid JSONL). `text`: no validation.
|
|
86
|
+
- `priority` (number, optional) — float, default 0.0. Higher = claimed first. Only set when meaningful.
|
|
87
|
+
|
|
88
|
+
Returns:
|
|
89
|
+
```yaml
|
|
90
|
+
id: url-1
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
**ID strategy**: use the item's natural stable identity from its origin system — URL, database row id, API object id. Omit id only when items have no upstream identity.
|
|
94
|
+
|
|
95
|
+
---
|
|
96
|
+
|
|
97
|
+
### claim
|
|
98
|
+
|
|
99
|
+
Atomically claims the highest-priority lowest-seq unclaimed item from a stage. Returns the item with its full payload.
|
|
100
|
+
|
|
101
|
+
Parameters:
|
|
102
|
+
- `pipeline` (string)
|
|
103
|
+
- `stage` (string) — supports glob wildcards: `enriched/*`, `*`. Omit to claim from any stage.
|
|
104
|
+
- `id` (string, optional) — claim a specific item by id. Fails if not found or already claimed.
|
|
105
|
+
- `random` (boolean, optional) — claim a random unclaimed item instead of priority+seq order
|
|
106
|
+
|
|
107
|
+
Returns:
|
|
108
|
+
```yaml
|
|
109
|
+
id: url-1
|
|
110
|
+
stage: pending
|
|
111
|
+
seq: 3
|
|
112
|
+
priority: 0
|
|
113
|
+
claimed: true
|
|
114
|
+
created: 1748476800000
|
|
115
|
+
last_modified: 1748476800000
|
|
116
|
+
payload: |
|
|
117
|
+
url: https://example.com/product/42
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
Returns `error: no items to claim` when nothing is available. **Stop and report back when this happens** — do not retry.
|
|
121
|
+
|
|
122
|
+
Errors (MCP YAML / SDK JSON):
|
|
123
|
+
- `error: no items to claim` — stage is empty or all items are already claimed. Stop and report back.
|
|
124
|
+
- `error: item <id> not found` — only when claiming by specific `id`
|
|
125
|
+
- `error: item <id> already claimed` — only when claiming by specific `id`
|
|
126
|
+
|
|
127
|
+
**Save the `seq` value** — it is your claim token, required to call `release`.
|
|
128
|
+
|
|
129
|
+
---
|
|
130
|
+
|
|
131
|
+
### release
|
|
132
|
+
|
|
133
|
+
Releases a claimed item. Optionally moves it to a new stage and appends/replaces payload.
|
|
134
|
+
|
|
135
|
+
Parameters:
|
|
136
|
+
- `pipeline` (string)
|
|
137
|
+
- `seq` (number) — the claim token from `claim`
|
|
138
|
+
- `target` (string, optional) — destination stage. **Always specify when moving the item.** Omit to release back to the same stage.
|
|
139
|
+
- `payload` (string, optional) — payload string to append (or replace)
|
|
140
|
+
- `payloadFormat` (`yaml` | `json` | `text`, optional, default `yaml`) — same as `push`
|
|
141
|
+
- `replace` (boolean, optional) — replace payload entirely instead of appending. Use when treating payload as a structured object.
|
|
142
|
+
- `priority` (number, optional) — update the item's priority
|
|
143
|
+
|
|
144
|
+
Returns:
|
|
145
|
+
```yaml
|
|
146
|
+
ok: true
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
Fails if the claim expired (item was unstuck while you worked) or item is not claimed.
|
|
150
|
+
|
|
151
|
+
**Payload is append-only history by default.** Each stage appends its output — resulting duplicate YAML fields are intentional. Use `replace: true` only when treating payload as a structured object you own.
|
|
152
|
+
|
|
153
|
+
---
|
|
154
|
+
|
|
155
|
+
### status
|
|
156
|
+
|
|
157
|
+
Returns the pipeline description and item counts per stage.
|
|
158
|
+
|
|
159
|
+
Parameters:
|
|
160
|
+
- `pipeline` (string) — pipeline name
|
|
161
|
+
|
|
162
|
+
Returns:
|
|
163
|
+
```yaml
|
|
164
|
+
description: Scrape product pages and extract pricing
|
|
165
|
+
stats:
|
|
166
|
+
'*': [23, 3]
|
|
167
|
+
done: [7, 0]
|
|
168
|
+
failed: [6, 0]
|
|
169
|
+
pending: [10, 3]
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
Each value is `[total, claimed]` — total item count and how many are currently claimed (being processed). `*` is the pipeline-level aggregate across all stages. For nested stages (`enriched/shortlisted`), intermediate paths are included as aggregates of their children. `description` is `null` if none was set.
|
|
173
|
+
|
|
174
|
+
---
|
|
175
|
+
|
|
176
|
+
### batch_read
|
|
177
|
+
|
|
178
|
+
Reads items without claiming them. For inspection, data collection, and monitoring.
|
|
179
|
+
|
|
180
|
+
Parameters:
|
|
181
|
+
- `pipeline` (string)
|
|
182
|
+
- `stage` (string, optional) — supports globs. Defaults to `*`
|
|
183
|
+
- `includePayload` (boolean, optional) — include payload in results
|
|
184
|
+
- `claimed` (boolean, optional) — filter by claimed status. Default: unclaimed
|
|
185
|
+
- `ids` (string[], optional)
|
|
186
|
+
- `createdAfter`, `createdBefore`, `modifiedAfter` (number, epoch ms)
|
|
187
|
+
- `limit`, `offset` (number)
|
|
188
|
+
|
|
189
|
+
Returns a YAML list of items. `payload` is omitted unless `includePayload: true`. Returns `[]` when nothing matches.
|
|
190
|
+
|
|
191
|
+
```yaml
|
|
192
|
+
- id: url-1
|
|
193
|
+
stage: done
|
|
194
|
+
claimed: false
|
|
195
|
+
seq: 5
|
|
196
|
+
priority: 0
|
|
197
|
+
created: 1748476800000
|
|
198
|
+
last_modified: 1748476801000
|
|
199
|
+
- id: url-2
|
|
200
|
+
stage: done
|
|
201
|
+
claimed: false
|
|
202
|
+
seq: 7
|
|
203
|
+
priority: 0
|
|
204
|
+
created: 1748476800001
|
|
205
|
+
last_modified: 1748476802000
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
---
|
|
209
|
+
|
|
210
|
+
### unstick
|
|
211
|
+
|
|
212
|
+
Releases all stuck claimed items back to their stage. Use when agents have crashed and left items locked.
|
|
213
|
+
|
|
214
|
+
Parameters:
|
|
215
|
+
- `pipeline` (string)
|
|
216
|
+
- `stage` (string, optional) — limit to a specific stage. Defaults to `*`
|
|
217
|
+
- `ids`, `createdAfter`, `createdBefore`, `modifiedAfter` — optional filters
|
|
218
|
+
|
|
219
|
+
Returns:
|
|
220
|
+
```yaml
|
|
221
|
+
unstuck: 3
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
---
|
|
225
|
+
|
|
226
|
+
### delete_pipeline
|
|
227
|
+
|
|
228
|
+
Deletes a pipeline and all its items. Irreversible. Only call if explicitly asked.
|
|
229
|
+
|
|
230
|
+
Parameters:
|
|
231
|
+
- `pipeline` (string)
|
|
232
|
+
|
|
233
|
+
Returns:
|
|
234
|
+
```yaml
|
|
235
|
+
ok: true
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
---
|
|
239
|
+
|
|
240
|
+
### get_sdk
|
|
241
|
+
|
|
242
|
+
Returns a self-contained SDK snippet (JavaScript or Python) that scripts can use to interact with QQ's HTTP bridge from anywhere — including the Cowork VM or a browser's HTTP-origin tab.
|
|
243
|
+
|
|
244
|
+
Parameters:
|
|
245
|
+
- `language` ("javascript" | "python")
|
|
246
|
+
|
|
247
|
+
Returns a code snippet to inline at the top of your script. The snippet uses only stdlib — no installs. It has the host IP, port, and token baked in for this MCP process's lifetime.
|
|
248
|
+
|
|
249
|
+
**Call once per task.** Re-call only if a script throws `BridgeError: Connection failed` — that means this MCP server restarted and credentials changed.
|
|
250
|
+
|
|
251
|
+
**JavaScript signatures:**
|
|
252
|
+
```
|
|
253
|
+
QQ.push(pipeline, stage, id?, payload?, opts?) → Promise<{ id: string } | { error: string }>
|
|
254
|
+
QQ.claim(pipeline, stage, opts?) → Promise<Item | { error: string }>
|
|
255
|
+
QQ.release(pipeline, seq, opts?) → Promise<{ ok: true } | { error: string }>
|
|
256
|
+
QQ.batch_read(pipeline, stage, opts?) → Promise<Item[] | { error: string }>
|
|
257
|
+
QQ.status(pipeline) → Promise<{ description: string|null, stats: Record<string, [number,number]> } | { error: string }>
|
|
258
|
+
```
|
|
259
|
+
|
|
260
|
+
- `push` opts: `priority` (number), `payloadFormat` (`"yaml"` | `"json"` | `"text"`)
|
|
261
|
+
- `claim` opts: `id` (string — claim a specific item), `random` (boolean)
|
|
262
|
+
- `release` opts: `target` (string), `payload` (string), `replace` (boolean), `priority` (number), `payloadFormat`
|
|
263
|
+
- `batch_read` opts: `includePayload` (boolean), `claimed` (boolean), `ids` (string, comma-separated), `createdAfter`, `createdBefore`, `modifiedAfter` (epoch ms), `limit`, `offset` (number)
|
|
264
|
+
|
|
265
|
+
All functions return `{ error: string }` on failure — never throw (except `BridgeError` for network connection failures). Check `result.error` before using the result.
|
|
266
|
+
|
|
267
|
+
`claim` returns `{ error: "no items to claim" }` when nothing is available.
|
|
268
|
+
|
|
269
|
+
Item shape (returned by `claim` and `batch_read`):
|
|
270
|
+
```
|
|
271
|
+
{ id: string, stage: string, seq: number, priority: number, claimed: boolean,
|
|
272
|
+
created: number, last_modified: number, payload?: string | null }
|
|
273
|
+
```
|
|
274
|
+
|
|
275
|
+
`claim` always includes `payload`. `batch_read` omits `payload` unless `includePayload: true`.
|
|
276
|
+
|
|
277
|
+
**Python signatures:**
|
|
278
|
+
```
|
|
279
|
+
qq_push(pipeline, stage, id=None, payload=None, **opts) → { 'id': str } | { 'error': str }
|
|
280
|
+
qq_claim(pipeline, stage, **opts) → dict | { 'error': str }
|
|
281
|
+
qq_release(pipeline, seq, **opts) → { 'ok': True } | { 'error': str }
|
|
282
|
+
qq_batch_read(pipeline, stage, **opts) → list[dict] | { 'error': str }
|
|
283
|
+
qq_status(pipeline) → { 'description': str|None, 'stats': dict } | { 'error': str }
|
|
284
|
+
```
|
|
285
|
+
|
|
286
|
+
All Python functions return `{ 'error': str }` on failure — never raise (except `BridgeError` for connection failures).
|
|
287
|
+
|
|
288
|
+
---
|
|
289
|
+
|
|
290
|
+
### load_file
|
|
291
|
+
|
|
292
|
+
Loads multiple items from a file on the host filesystem into a pipeline. Each record in the file becomes one pipeline item.
|
|
293
|
+
|
|
294
|
+
**Supported formats:** JSONL (`.jsonl`, `.ndjson`), JSON array (`.json`), YAML array (`.yaml`, `.yml`), CSV (`.csv`). Format is detected from the file extension.
|
|
295
|
+
|
|
296
|
+
**`path`** supports `$VAR` environment variable expansion — e.g. `$HOME/Downloads/data.jsonl`.
|
|
297
|
+
|
|
298
|
+
Parameters:
|
|
299
|
+
- `path` (string) — path to the file. Supports `$VAR` expansion.
|
|
300
|
+
- `pipeline` (string) — pipeline to push records into
|
|
301
|
+
- `stage` (string, optional) — default stage for records that do not specify their own. Defaults to empty string.
|
|
302
|
+
- `deleteAfter` (boolean, optional) — delete the file after loading. Default: false.
|
|
303
|
+
|
|
304
|
+
**Record format** (all fields optional):
|
|
305
|
+
```yaml
|
|
306
|
+
id: item-1
|
|
307
|
+
stage: pending
|
|
308
|
+
payload: "any string"
|
|
309
|
+
priority: 0.0
|
|
310
|
+
```
|
|
311
|
+
- `id` — omit for auto-generated id
|
|
312
|
+
- `stage` — overrides the `stage` parameter for this record
|
|
313
|
+
- `payload` — treated as an opaque string; non-string values are YAML-serialized
|
|
314
|
+
- `priority` — float, default 0.0
|
|
315
|
+
|
|
316
|
+
**Returns** one line per record:
|
|
317
|
+
```
|
|
318
|
+
item-1: ok
|
|
319
|
+
item-2: duplicate
|
|
320
|
+
item-3: error: <message>
|
|
321
|
+
```
|
|
322
|
+
|
|
323
|
+
File-level parse errors (bad JSON, malformed CSV, etc.) abort the entire operation. Per-record push failures are reported individually without aborting remaining records.
|
|
324
|
+
|
|
325
|
+
---
|
|
326
|
+
|
|
327
|
+
## SDK Usage
|
|
328
|
+
|
|
329
|
+
Use the SDK when you need to run code that processes pipeline items programmatically — loops, transforms, bulk operations. It is faster and more efficient than calling MCP tools one at a time for batch work.
|
|
330
|
+
|
|
331
|
+
### When to use SDK vs MCP tools
|
|
332
|
+
|
|
333
|
+
- **MCP tools**: orchestration decisions, one-off operations, monitoring, anything requiring Claude's judgment
|
|
334
|
+
- **SDK (via script)**: processing many items in a loop, transforming data, bulk pushes, anything that is pure computation
|
|
335
|
+
|
|
336
|
+
### Typical workflow
|
|
337
|
+
|
|
338
|
+
```javascript
|
|
339
|
+
// 1. Call get_sdk once to get the snippet
|
|
340
|
+
// 2. Write a script, inline the snippet at the top
|
|
341
|
+
// 3. Run with: node script.js or python script.py
|
|
342
|
+
|
|
343
|
+
// --- JavaScript example ---
|
|
344
|
+
// [paste get_sdk output here]
|
|
345
|
+
|
|
346
|
+
const pipeline = '0529_a3f9c2';
|
|
347
|
+
|
|
348
|
+
// Bulk push — payload is a YAML string
|
|
349
|
+
for (const url of urls) {
|
|
350
|
+
await QQ.push(pipeline, 'pending', url, `url: ${url}`);
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
// Claim loop — check result.error, not null
|
|
354
|
+
while (true) {
|
|
355
|
+
const item = await QQ.claim(pipeline, 'pending');
|
|
356
|
+
if (item.error) break; // "no items to claim" or other error
|
|
357
|
+
const result = await processItem(item.payload);
|
|
358
|
+
await QQ.release(pipeline, item.seq, { target: 'done', payload: result });
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
// Collect results with payloads
|
|
362
|
+
const done = await QQ.batch_read(pipeline, 'done', { includePayload: true });
|
|
363
|
+
for (const item of done) {
|
|
364
|
+
console.log(item.id, item.payload);
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
// Monitor progress
|
|
368
|
+
const s = await QQ.status(pipeline);
|
|
369
|
+
console.log(`total=${s.stats['*'][0]} pending=${s.stats.pending?.[0] ?? 0}`);
|
|
370
|
+
```
|
|
371
|
+
|
|
372
|
+
### Error routing
|
|
373
|
+
|
|
374
|
+
Release to `failed` on error, `done` on success — never leave items claimed when your work throws:
|
|
375
|
+
|
|
376
|
+
```javascript
|
|
377
|
+
while (true) {
|
|
378
|
+
const item = await QQ.claim(pipeline, 'pending');
|
|
379
|
+
if (item.error) break;
|
|
380
|
+
try {
|
|
381
|
+
const result = await processItem(item.payload);
|
|
382
|
+
await QQ.release(pipeline, item.seq, { target: 'done', payload: `result: ${result}` });
|
|
383
|
+
} catch (e) {
|
|
384
|
+
await QQ.release(pipeline, item.seq, { target: 'failed', payload: `error: ${e.message}` });
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
```
|
|
388
|
+
|
|
389
|
+
### Structured JSON payloads
|
|
390
|
+
|
|
391
|
+
Use `payloadFormat: "json"` when payload is a JSON object. Appended payloads form valid JSONL; `replace: true` treats it as a single mutable object:
|
|
392
|
+
|
|
393
|
+
```javascript
|
|
394
|
+
// Push with JSON payload
|
|
395
|
+
await QQ.push(pipeline, 'pending', 'item-1', JSON.stringify({ url: 'https://a.com', score: 0 }),
|
|
396
|
+
{ payloadFormat: 'json' });
|
|
397
|
+
|
|
398
|
+
// Update a structured payload: parse → modify → replace
|
|
399
|
+
const item = await QQ.claim(pipeline, 'pending');
|
|
400
|
+
if (!item.error) {
|
|
401
|
+
const data = JSON.parse(item.payload);
|
|
402
|
+
data.score = await scoreItem(data.url);
|
|
403
|
+
await QQ.release(pipeline, item.seq, {
|
|
404
|
+
target: 'done',
|
|
405
|
+
payload: JSON.stringify(data),
|
|
406
|
+
payloadFormat: 'json',
|
|
407
|
+
replace: true,
|
|
408
|
+
});
|
|
409
|
+
}
|
|
410
|
+
```
|
|
411
|
+
|
|
412
|
+
### Python example
|
|
413
|
+
|
|
414
|
+
```python
|
|
415
|
+
# [paste get_sdk output here]
|
|
416
|
+
|
|
417
|
+
pipeline = "0529_a3f9c2"
|
|
418
|
+
|
|
419
|
+
# Bulk push
|
|
420
|
+
for url in urls:
|
|
421
|
+
qq_push(pipeline, "pending", url, f"url: {url}")
|
|
422
|
+
|
|
423
|
+
# Claim loop
|
|
424
|
+
while True:
|
|
425
|
+
item = qq_claim(pipeline, "pending")
|
|
426
|
+
if "error" in item: # "no items to claim" or other error
|
|
427
|
+
break
|
|
428
|
+
result = process_item(item["payload"])
|
|
429
|
+
qq_release(pipeline, item["seq"], target="done", payload=result)
|
|
430
|
+
|
|
431
|
+
items = qq_batch_read(pipeline, "done", includePayload=True)
|
|
432
|
+
for item in items:
|
|
433
|
+
print(item["id"], item["payload"])
|
|
434
|
+
|
|
435
|
+
# Monitor
|
|
436
|
+
s = qq_status(pipeline)
|
|
437
|
+
print(f"total={s['stats']['*'][0]}")
|
|
438
|
+
```
|
|
439
|
+
|
|
440
|
+
### BridgeError reconnect
|
|
441
|
+
|
|
442
|
+
If a script throws `BridgeError: Connection failed`:
|
|
443
|
+
1. Call `get_sdk` again to get fresh credentials
|
|
444
|
+
2. Update the snippet in your script
|
|
445
|
+
3. Re-run the script
|
|
446
|
+
|
|
447
|
+
This happens when the QQ MCP server restarted (e.g. Claude Desktop was restarted).
|
|
448
|
+
|
|
449
|
+
---
|
|
450
|
+
|
|
451
|
+
## YAML Payload Conventions
|
|
452
|
+
|
|
453
|
+
Payload is a YAML string appended to existing content by default. The result is a YAML document where fields may appear multiple times — this is intentional. Each stage's output appears in sequence, creating a trace of the item's journey.
|
|
454
|
+
|
|
455
|
+
```yaml
|
|
456
|
+
# After push:
|
|
457
|
+
url: https://example.com/product/42
|
|
458
|
+
|
|
459
|
+
# After first subagent appends:
|
|
460
|
+
url: https://example.com/product/42
|
|
461
|
+
name: Widget Pro
|
|
462
|
+
price: 29.99
|
|
463
|
+
available: true
|
|
464
|
+
|
|
465
|
+
# After second subagent appends (review):
|
|
466
|
+
url: https://example.com/product/42
|
|
467
|
+
name: Widget Pro
|
|
468
|
+
price: 29.99
|
|
469
|
+
available: true
|
|
470
|
+
review_reason: price seems anomalous
|
|
471
|
+
```
|
|
472
|
+
|
|
473
|
+
Use `replace: true` only when you own the payload as a structured object (e.g., accumulating messages in a chat pipeline). To update a structured payload: claim, parse with yaml.load, modify, release with replace.
|
|
474
|
+
|
|
475
|
+
---
|
|
476
|
+
|
|
477
|
+
## Globs, Filters, Priority
|
|
478
|
+
|
|
479
|
+
**Stage globs**: `*` matches any single segment, `enriched/*` matches sub-stages. Use in `claim` and `batch_read` to work across multiple stages.
|
|
480
|
+
|
|
481
|
+
**Priority**: float, default 0.0. Higher value = claimed first within the same stage. Use only when meaningful (e.g., score, confidence). Most pipelines don't need priority.
|
|
482
|
+
|
|
483
|
+
**Filters in batch_read / unstick**: `claimed`, `ids`, `createdAfter`, `createdBefore`, `modifiedAfter`, `limit`, `offset`.
|
|
484
|
+
|
|
485
|
+
---
|
|
486
|
+
|
|
487
|
+
## Pipeline Design
|
|
488
|
+
|
|
489
|
+
Before creating a new pipeline, read the `pipeline_design` resource. It walks through the design process, the PIPELINES.md template, a filled example, and a pre-implementation checklist.
|
|
490
|
+
|
|
491
|
+
Document every pipeline in `PIPELINES.md` before pushing a single item.
|
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
# Pipeline Design Guide
|
|
2
|
+
|
|
3
|
+
## The Iron Law
|
|
4
|
+
|
|
5
|
+
```
|
|
6
|
+
NO PIPELINE IMPLEMENTATION WITHOUT A PIPELINES.md ENTRY FIRST
|
|
7
|
+
```
|
|
8
|
+
|
|
9
|
+
Design the pipeline on paper before pushing a single item. Undocumented pipelines drift — stages accumulate, payload grows inconsistently, and future agents guess wrong about what each stage means.
|
|
10
|
+
|
|
11
|
+
**Violating the letter of this process is violating the spirit of it.**
|
|
12
|
+
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
## When to Use This Guide
|
|
16
|
+
|
|
17
|
+
Follow this guide whenever you are:
|
|
18
|
+
- Creating a new pipeline
|
|
19
|
+
- Adding stages to an existing pipeline
|
|
20
|
+
- Changing what payload a stage produces
|
|
21
|
+
|
|
22
|
+
**When working with an existing pipeline:** read `PIPELINES.md` first. It should contain everything you need. If it's missing or incomplete, update it before proceeding.
|
|
23
|
+
|
|
24
|
+
---
|
|
25
|
+
|
|
26
|
+
## Clarifying with the User
|
|
27
|
+
|
|
28
|
+
Pipeline design usually happens with a human partner. Before filling in the template, ask the user to clarify what you don't know. **One question per message.** Prefer multiple-choice when the options are known.
|
|
29
|
+
|
|
30
|
+
The five Phase 1 questions are your agenda — work through them in dialogue, not in isolation.
|
|
31
|
+
|
|
32
|
+
**Good questions to ask:**
|
|
33
|
+
- "What are the sources of items — where do they come from, and what identifies each one uniquely?"
|
|
34
|
+
- "What should happen when an item fails — retry, discard, or route to a review stage?"
|
|
35
|
+
- "Is there a natural stable id for each item (URL, database id), or should the pipeline assign one?"
|
|
36
|
+
- "Are any stages optional, or does every item pass through all of them?"
|
|
37
|
+
- "Who runs the orchestrator and when — on demand, scheduled, or triggered by an event?"
|
|
38
|
+
|
|
39
|
+
**When to skip asking:**
|
|
40
|
+
- The user has already provided enough detail to answer all five Phase 1 questions
|
|
41
|
+
- You are working from a spec or design doc that covers the pipeline completely
|
|
42
|
+
- The pipeline is a trivial two-stage transform with no ambiguous transitions
|
|
43
|
+
|
|
44
|
+
If you skip asking, state why — don't silently proceed.
|
|
45
|
+
|
|
46
|
+
---
|
|
47
|
+
|
|
48
|
+
## Phase 1 — Design
|
|
49
|
+
|
|
50
|
+
Answer all five questions before writing anything.
|
|
51
|
+
|
|
52
|
+
### 1. What does this pipeline do?
|
|
53
|
+
|
|
54
|
+
One sentence. If you need more than one sentence, the scope is unclear — break it down or narrow it.
|
|
55
|
+
|
|
56
|
+
### 2. What are the stages?
|
|
57
|
+
|
|
58
|
+
List every stage by name. For each: what does it mean for an item to be in this stage?
|
|
59
|
+
|
|
60
|
+
Stages are not steps in a script — they are **states** an item can be in. Name them as nouns or past participles, not verbs.
|
|
61
|
+
|
|
62
|
+
| Good stage names | Bad stage names |
|
|
63
|
+
|-----------------|-----------------|
|
|
64
|
+
| `pending`, `fetched`, `done` | `fetch`, `process`, `handle` |
|
|
65
|
+
| `review/approved`, `review/rejected` | `reviewing`, `to_review` |
|
|
66
|
+
|
|
67
|
+
Sub-stages (`review/approved`, `enriched/shortlisted`) are valid — use them when a stage has distinct sub-states that agents need to distinguish.
|
|
68
|
+
|
|
69
|
+
### 3. What are the valid transitions?
|
|
70
|
+
|
|
71
|
+
For each transition: which stage does it come from, which stage does it go to, and what did the agent do to earn the move?
|
|
72
|
+
|
|
73
|
+
**Terminal stages** are stages with no outbound transitions — `done`, `failed`, `discarded`. Be explicit about which stages are terminal.
|
|
74
|
+
|
|
75
|
+
**Loops** (releasing back to the same stage) are valid: use them for retry logic or chat-style accumulation. Document them explicitly.
|
|
76
|
+
|
|
77
|
+
### 4. What is the item ID — and why?
|
|
78
|
+
|
|
79
|
+
The `id` is the deduplication primitive. An item can only be pushed once per pipeline per id. Choose it carefully.
|
|
80
|
+
|
|
81
|
+
| Situation | Use |
|
|
82
|
+
|-----------|-----|
|
|
83
|
+
| Items come from an external system | The external system's stable identifier (URL, database row id, API object id) |
|
|
84
|
+
| Items have no upstream identifier | Omit `id` — the pipeline assigns a sequence number |
|
|
85
|
+
| Items must be globally unique across runs | A hash or composite key (e.g., `sha256(url + date)`) |
|
|
86
|
+
|
|
87
|
+
Never use a generated UUID as the id unless items genuinely have no stable identity.
|
|
88
|
+
|
|
89
|
+
### 5. What does the payload accumulate?
|
|
90
|
+
|
|
91
|
+
Payload is append-only history by default. Each stage appends its output.
|
|
92
|
+
|
|
93
|
+
For each field that will appear in the payload:
|
|
94
|
+
- Which stage introduces it?
|
|
95
|
+
- What type/shape is it?
|
|
96
|
+
- Is it ever replaced (use `replace: true`) rather than appended?
|
|
97
|
+
|
|
98
|
+
---
|
|
99
|
+
|
|
100
|
+
## Red Flags — Stop and Re-Design
|
|
101
|
+
|
|
102
|
+
| Thought | Problem |
|
|
103
|
+
|---------|---------|
|
|
104
|
+
| "I'll figure out the stages as I go" | Undefined stages produce inconsistent item states |
|
|
105
|
+
| "Payload structure is flexible" | Future agents can't reliably parse a payload with no schema |
|
|
106
|
+
| "I'll use auto-generated IDs for now" | If items have a natural identity, you'll get duplicates across runs |
|
|
107
|
+
| "I'll document after it works" | You won't, and future agents will guess wrong |
|
|
108
|
+
| "The pipeline is simple, it doesn't need docs" | Undocumented simple pipelines become undocumented complex ones |
|
|
109
|
+
| "I'll add the review stage later if we need it" | Retrofit stages invalidate existing items' stage semantics |
|
|
110
|
+
| "Error handling is just releasing to `failed`" | What happens to items in `failed`? Document it. |
|
|
111
|
+
|
|
112
|
+
---
|
|
113
|
+
|
|
114
|
+
## Phase 2 — Document
|
|
115
|
+
|
|
116
|
+
Write the PIPELINES.md entry using the template below. Every field is required.
|
|
117
|
+
|
|
118
|
+
PIPELINES.md lives in the project root. One file documents all pipelines in the project.
|
|
119
|
+
|
|
120
|
+
### PIPELINES.md Template
|
|
121
|
+
|
|
122
|
+
```markdown
|
|
123
|
+
## [Pipeline Name]
|
|
124
|
+
|
|
125
|
+
**Description:** One sentence.
|
|
126
|
+
|
|
127
|
+
**Purpose:** 2-3 sentences. Why does this pipeline exist? What triggers it?
|
|
128
|
+
|
|
129
|
+
**Pipeline ID:** `[name or auto-generated pattern]`
|
|
130
|
+
|
|
131
|
+
---
|
|
132
|
+
|
|
133
|
+
### ID Strategy
|
|
134
|
+
[What value is used as the item id and why.]
|
|
135
|
+
|
|
136
|
+
---
|
|
137
|
+
|
|
138
|
+
### Payload Schema
|
|
139
|
+
|
|
140
|
+
| Field | Type | Added by stage | Description |
|
|
141
|
+
|-------|------|----------------|-------------|
|
|
142
|
+
|
|
143
|
+
---
|
|
144
|
+
|
|
145
|
+
### Stages
|
|
146
|
+
|
|
147
|
+
| Stage | Meaning | Terminal? |
|
|
148
|
+
|-------|---------|-----------|
|
|
149
|
+
|
|
150
|
+
---
|
|
151
|
+
|
|
152
|
+
### Transitions
|
|
153
|
+
|
|
154
|
+
| From | To | Agent action | Payload appended | Notes |
|
|
155
|
+
|------|----|-------------|-----------------|-------|
|
|
156
|
+
|
|
157
|
+
---
|
|
158
|
+
|
|
159
|
+
### State Diagram
|
|
160
|
+
|
|
161
|
+
```mermaid
|
|
162
|
+
stateDiagram-v2
|
|
163
|
+
[*] --> pending
|
|
164
|
+
pending --> done : processed
|
|
165
|
+
done --> [*]
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
---
|
|
169
|
+
|
|
170
|
+
### Process Notes
|
|
171
|
+
|
|
172
|
+
**Orchestrator responsibilities:**
|
|
173
|
+
**Subagent responsibilities:**
|
|
174
|
+
**Retry logic:**
|
|
175
|
+
**Error handling:**
|
|
176
|
+
**Known edge cases:**
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
---
|
|
180
|
+
|
|
181
|
+
## Phase 3 — Verify
|
|
182
|
+
|
|
183
|
+
- [ ] Every stage has a defined meaning in the Stages table
|
|
184
|
+
- [ ] Every valid transition appears in both the Transitions table and the Mermaid diagram
|
|
185
|
+
- [ ] Terminal stages are identified
|
|
186
|
+
- [ ] ID strategy is explicit and justified
|
|
187
|
+
- [ ] Payload schema shows which stage introduces each field
|
|
188
|
+
- [ ] Retry logic is documented
|
|
189
|
+
- [ ] Orchestrator and subagent responsibilities are separated and complete
|
|
190
|
+
- [ ] Process notes cover what happens to `failed` items
|
|
191
|
+
|
|
192
|
+
If any item is unchecked: complete it before implementing.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"javascript.d.ts","sourceRoot":"","sources":["../../src/sdk-templates/javascript.ts"],"names":[],"mappings":"AAAA,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAmBrF"}
|