mio-agent-runtime 0.12.0 → 0.13.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/README.md +13 -3
- package/bin/mio.js +63 -0
- package/package.json +2 -2
- package/server/mio-intelligence-mcp/index.js +18 -0
- package/server/observer-store.js +104 -2
package/README.md
CHANGED
|
@@ -65,6 +65,7 @@ mio observer <view> Observer research pipeline views (research pipeline
|
|
|
65
65
|
status|world-model|trends|research|insights|essays|dag (--base-dir DIR)
|
|
66
66
|
mio observer collect Fetch from the configured sources (--sources a,b/--keywords k1,k2/--limit N)
|
|
67
67
|
mio observer ferment Run the fermentation engine (--session morning|afternoon|night)
|
|
68
|
+
mio observer pipeline Run the research DAG (previews by default; --run executes, --mode neutral|analytical|creative)
|
|
68
69
|
mio observer ingest --trace-id T --event-type E Record a trace event (--payload JSON/--outcome)
|
|
69
70
|
mio observer subscribe --event-types a,b Subscribe to events (--yes to apply; previews by default)
|
|
70
71
|
mio observer digest New events since the last digest (advances the cursor)
|
|
@@ -99,13 +100,13 @@ mio --version Print the version and exit (-V)
|
|
|
99
100
|
|
|
100
101
|
## MCP 工具
|
|
101
102
|
|
|
102
|
-
MCP 服务端在 5 大域共暴露
|
|
103
|
+
MCP 服务端在 5 大域共暴露 50 个工具:
|
|
103
104
|
|
|
104
105
|
### 记忆(11)
|
|
105
106
|
`mio.memory.query` · `mio.memory.record` · `mio.memory.archive` · `mio.memory.merge` · `mio.memory.migrate` · `mio.memory.analyze` · `mio.memory.forget` · `mio.experience.list` · `mio.experience.confirm` · `mio.experience.reuse` · `mio.policy.check`
|
|
106
107
|
|
|
107
|
-
### 观察管线(
|
|
108
|
-
`mio.observer.world_model` · `mio.observer.trends` · `mio.observer.research` · `mio.observer.insights` · `mio.observer.status` · `mio.observer.collect` · `mio.observer.ferment` · `mio.observer.essays` · `mio.observer.dag` · `mio.observer.ingest` · `mio.observer.subscribe` · `mio.observer.digest`
|
|
108
|
+
### 观察管线(15)
|
|
109
|
+
`mio.observer.world_model` · `mio.observer.trends` · `mio.observer.research` · `mio.observer.insights` · `mio.observer.status` · `mio.observer.collect` · `mio.observer.ferment` · `mio.observer.pipeline` · `mio.observer.essays` · `mio.observer.dag` · `mio.observer.ingest` · `mio.observer.subscribe` · `mio.observer.digest`
|
|
109
110
|
`mio.trace.query` — 按类型/结果/agent/项目/时间窗口查询 trace 日志(任务结果、工具错误);CLI 上也可通过 `mio traces` 调用
|
|
110
111
|
`mio.digest.generate` — 把近期数据聚合成可执行的 digest(agent 成功率、项目活跃度、错误热点、复用证据、建议);CLI 上也可通过 `mio digest` 调用
|
|
111
112
|
|
|
@@ -490,6 +491,15 @@ mio observer trends --base-dir /path/to/.local/observer
|
|
|
490
491
|
```
|
|
491
492
|
|
|
492
493
|
两者都依赖可选包 `@akemi-mio/observer`(未安装时提示 `not installed`,不是空结果)。`collect` 会**联网**抓取,`ferment` 需要 LLM;它们**不做预览**,因为预览意味着把数据抓两遍。单个源失败不会中断整次运行——该源会以 `errors: ...` 出现在输出里,其余源照常统计。
|
|
494
|
+
- **`mio observer pipeline` 是研究 DAG 的入口**(`collect → trend → tension → research → multi-brain → compose → world model → publish → self-evolve`)。此前 `runPipeline` / `tickPipeline` / `forcePipeline` 在 mio-agent-runtime 里**没有任何调用方**,所以 trends / research / insights 默认永远是空的,只有手动实例化服务才能跑通。它同样依赖 `@akemi-mio/observer`,会**联网并调用多次 LLM**,因此和 `subscribe` 一样**默认只预览**(读今天的 DAG 状态与解析后的 LLM 端点,不构造服务、不建目录),`--run` 才真正执行:
|
|
495
|
+
|
|
496
|
+
```bash
|
|
497
|
+
mio observer pipeline # 预览(离线、秒回)
|
|
498
|
+
mio observer pipeline --run # 执行完整 DAG
|
|
499
|
+
mio observer pipeline --mode creative --run
|
|
500
|
+
```
|
|
501
|
+
|
|
502
|
+
- **观察者的 LLM 现在与 creativity / insight 共用同一份配置**。此前 `ObserverLlmService` 把 Ollama 地址与模型写死(`localhost:11434` / `qwen2.5:7b`,只认 `OBSERVER_*` 环境变量),完全无视 `mio config llm` —— 于是 `mio config llm` 配好的 deepseek 只对 server 侧生效,观察管道仍然空转。现在 `observer-store.js` 通过共享的 `server/llm-client.js` 解析配置并注入:用户配置过(`config.json` 的 `llm` 或 `LLM_*` 环境变量)就用它,否则保持 `@akemi-mio/observer` 自己的 `LLM_*` / `OBSERVER_*` / 本地 Ollama 回退,不会把既有本地 Ollama 用户静默改道到托管默认值。端点按 URL 形态自动选择传输:含 `/chat/completions` 走 OpenAI 兼容协议,否则按 Ollama 处理。
|
|
493
503
|
- **`mio observer ingest` 记录任意 trace 事件**(`tool_call` / `error` / `retry` / `task_outcome`):
|
|
494
504
|
|
|
495
505
|
```bash
|
package/bin/mio.js
CHANGED
|
@@ -1530,6 +1530,9 @@ function observerUsage(write = console.log) {
|
|
|
1530
1530
|
[--limit N] (needs @akemi-mio/observer; hits the network)
|
|
1531
1531
|
mio observer ferment Run the fermentation engine over recent observations
|
|
1532
1532
|
(--session morning|afternoon|night; needs @akemi-mio/observer)
|
|
1533
|
+
mio observer pipeline Run the full research DAG (collect → trend → research →
|
|
1534
|
+
insights → world model). Previews by default; --run
|
|
1535
|
+
executes it (network + LLM). --mode neutral|analytical|creative
|
|
1533
1536
|
|
|
1534
1537
|
Options:
|
|
1535
1538
|
--base-dir DIR Observer data directory (default: <cwd>/.local/observer)
|
|
@@ -1540,6 +1543,8 @@ Options:
|
|
|
1540
1543
|
--sources a,b Sources to collect from (default: all configured)
|
|
1541
1544
|
--keywords a,b Keyword filter, OR matched (collect)
|
|
1542
1545
|
--session label Fermentation session (default: afternoon)
|
|
1546
|
+
--mode name Writing mode for the pipeline (default: analytical)
|
|
1547
|
+
--run Actually execute the pipeline (default: preview)
|
|
1543
1548
|
--json Machine-readable output (same shape as mio.observer.*)`)
|
|
1544
1549
|
}
|
|
1545
1550
|
|
|
@@ -1854,6 +1859,62 @@ async function observerFermentCommand(args, useJson) {
|
|
|
1854
1859
|
printObserverFerment(result, session)
|
|
1855
1860
|
}
|
|
1856
1861
|
|
|
1862
|
+
// The pipeline entry point D1 was missing: runPipeline/tickPipeline/forcePipeline
|
|
1863
|
+
// had no caller in mio-agent-runtime, so the observer's trend/research/insight
|
|
1864
|
+
// stages never ran on their own. It is heavy (network + several LLM calls), so
|
|
1865
|
+
// it follows the same rule as `mio observer subscribe` and `mio task
|
|
1866
|
+
// record-outcome`: preview by default, execute only with --run.
|
|
1867
|
+
const PIPELINE_MODES = ['neutral', 'analytical', 'creative']
|
|
1868
|
+
|
|
1869
|
+
function printObserverPipeline(result) {
|
|
1870
|
+
if (result.dryRun) {
|
|
1871
|
+
console.log(`Observer pipeline preview (mode=${result.mode}, base-dir=${result.baseDir})`)
|
|
1872
|
+
const state = result.todayState || 'no task yet'
|
|
1873
|
+
const done = result.todayCompleted ? ' (already completed)' : ''
|
|
1874
|
+
console.log(` today ${result.taskId}: ${state}${done}`)
|
|
1875
|
+
if (result.llm) {
|
|
1876
|
+
const key = result.llm.hasApiKey ? 'key set' : 'no key'
|
|
1877
|
+
console.log(` LLM: ${result.llm.provider} ${result.llm.model} (${key})`)
|
|
1878
|
+
console.log(` ${result.llm.apiUrl}`)
|
|
1879
|
+
} else {
|
|
1880
|
+
console.log(' LLM: unknown (observer package did not expose ObserverLlmService)')
|
|
1881
|
+
}
|
|
1882
|
+
console.log('\nPreview only. Re-run with --run to execute the full DAG (network + LLM calls).')
|
|
1883
|
+
return
|
|
1884
|
+
}
|
|
1885
|
+
if (!result.completed) {
|
|
1886
|
+
console.log(`Observer pipeline did not complete: ${result.reason || 'unknown reason'}`)
|
|
1887
|
+
process.exitCode = 1
|
|
1888
|
+
return
|
|
1889
|
+
}
|
|
1890
|
+
console.log(`Observer pipeline completed (${result.type}, ${result.durationMs || 0}ms, ${result.llmCalls || 0} LLM call(s))`)
|
|
1891
|
+
if (result.topic) console.log(` topic: ${result.topic}`)
|
|
1892
|
+
console.log(` sections: ${result.sections}`)
|
|
1893
|
+
if (result.insightId) console.log(` insight: ${result.insightId}`)
|
|
1894
|
+
}
|
|
1895
|
+
|
|
1896
|
+
async function observerPipelineCommand(args, useJson) {
|
|
1897
|
+
const flags = args.slice(2)
|
|
1898
|
+
const mode = optionValue(flags, '--mode') || 'analytical'
|
|
1899
|
+
if (!PIPELINE_MODES.includes(mode)) {
|
|
1900
|
+
console.error(`--mode must be one of: ${PIPELINE_MODES.join(', ')}`)
|
|
1901
|
+
process.exitCode = 1
|
|
1902
|
+
return
|
|
1903
|
+
}
|
|
1904
|
+
const base = { baseDir: optionValue(flags, '--base-dir') }
|
|
1905
|
+
const run = flagPresent(flags, '--run')
|
|
1906
|
+
let result
|
|
1907
|
+
try {
|
|
1908
|
+
result = await cliObserverStore().pipeline({ ...base, mode, run })
|
|
1909
|
+
} catch (error) {
|
|
1910
|
+
console.error(error.message || error)
|
|
1911
|
+
process.exitCode = 1
|
|
1912
|
+
return
|
|
1913
|
+
}
|
|
1914
|
+
if (useJson) return jsonOrText(result, true)
|
|
1915
|
+
printObserverPipeline(result)
|
|
1916
|
+
}
|
|
1917
|
+
|
|
1857
1918
|
function observerCommand(args, useJson) {
|
|
1858
1919
|
const sub = args[1]
|
|
1859
1920
|
if (!sub || sub === 'help' || sub === '--help' || sub === '-h') {
|
|
@@ -1868,6 +1929,7 @@ function observerCommand(args, useJson) {
|
|
|
1868
1929
|
// returned rather than called -- main() is async and awaits them.
|
|
1869
1930
|
if (sub === 'collect') return observerCollectCommand(args, useJson)
|
|
1870
1931
|
if (sub === 'ferment') return observerFermentCommand(args, useJson)
|
|
1932
|
+
if (sub === 'pipeline') return observerPipelineCommand(args, useJson)
|
|
1871
1933
|
|
|
1872
1934
|
if (!['status', 'world-model', 'trends', 'research', 'insights', 'essays', 'dag'].includes(sub)) {
|
|
1873
1935
|
console.error(`Unknown observer subcommand: ${sub}`)
|
|
@@ -3131,6 +3193,7 @@ Usage:
|
|
|
3131
3193
|
mio observer digest New events since the last digest (advances cursor)
|
|
3132
3194
|
mio observer collect Fetch from the configured sources (--sources/--keywords/--limit)
|
|
3133
3195
|
mio observer ferment Run the fermentation engine (--session morning|afternoon|night)
|
|
3196
|
+
mio observer pipeline Run the research DAG (previews; --run to execute, --mode neutral|analytical|creative)
|
|
3134
3197
|
mio phase0 report Show the Phase 0 validation report (--project X, --format markdown)
|
|
3135
3198
|
mio host capabilities Show what each host supports and whether it is installed
|
|
3136
3199
|
mio task route "<task>" Which verified experiences apply to this task (--project/--scope/--limit)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "mio-agent-runtime",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.13.0",
|
|
4
4
|
"description": "Mio Agent Runtime installer and MCP intelligence layer",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "commonjs",
|
|
@@ -33,7 +33,7 @@
|
|
|
33
33
|
"@akemi-mio/evolution-strategy": "^0.1.0",
|
|
34
34
|
"@akemi-mio/evolution-safety": "^0.1.0",
|
|
35
35
|
"@akemi-mio/evolution-scheduler": "^0.1.0",
|
|
36
|
-
"@akemi-mio/observer": "^0.1.
|
|
36
|
+
"@akemi-mio/observer": "^0.1.1",
|
|
37
37
|
"@akemi-mio/insight": "^0.1.0"
|
|
38
38
|
},
|
|
39
39
|
"main": "index.js",
|
|
@@ -860,6 +860,18 @@ const TOOLS = [
|
|
|
860
860
|
},
|
|
861
861
|
},
|
|
862
862
|
},
|
|
863
|
+
{
|
|
864
|
+
name: 'mio.observer.pipeline',
|
|
865
|
+
description: 'Run the Observer research DAG (collect -> trend -> tension -> research -> multi-brain -> compose -> world model -> publish). Heavy: network plus several LLM calls. Previews by default; pass run:true to execute.',
|
|
866
|
+
inputSchema: {
|
|
867
|
+
type: 'object',
|
|
868
|
+
properties: {
|
|
869
|
+
mode: { type: 'string', description: 'Writing mode: neutral | analytical | creative (default: analytical)' },
|
|
870
|
+
run: { type: 'boolean', description: 'Execute the pipeline. Default false returns a dry-run plan without network or LLM calls.' },
|
|
871
|
+
baseDir: { type: 'string', description: 'Observer data directory' },
|
|
872
|
+
},
|
|
873
|
+
},
|
|
874
|
+
},
|
|
863
875
|
{
|
|
864
876
|
name: 'mio.observer.essays',
|
|
865
877
|
description: 'List published or draft essays from the Observer.',
|
|
@@ -941,6 +953,10 @@ function observerFerment(args = {}) {
|
|
|
941
953
|
return observerStore.ferment(args)
|
|
942
954
|
}
|
|
943
955
|
|
|
956
|
+
function observerPipeline(args = {}) {
|
|
957
|
+
return observerStore.pipeline(args)
|
|
958
|
+
}
|
|
959
|
+
|
|
944
960
|
function observerEssays(args = {}) {
|
|
945
961
|
return observerStore.essays(args)
|
|
946
962
|
}
|
|
@@ -1045,6 +1061,8 @@ async function callTool(name, args = {}) {
|
|
|
1045
1061
|
return observerCollect(args)
|
|
1046
1062
|
case 'mio.observer.ferment':
|
|
1047
1063
|
return observerFerment(args)
|
|
1064
|
+
case 'mio.observer.pipeline':
|
|
1065
|
+
return observerPipeline(args)
|
|
1048
1066
|
case 'mio.observer.essays':
|
|
1049
1067
|
return observerEssays(args)
|
|
1050
1068
|
case 'mio.observer.dag':
|
package/server/observer-store.js
CHANGED
|
@@ -14,6 +14,19 @@
|
|
|
14
14
|
const fs = require('fs')
|
|
15
15
|
const path = require('path')
|
|
16
16
|
|
|
17
|
+
// The observer pipeline is one of the LLM users, so it must read the same
|
|
18
|
+
// resolved model configuration as creativity / insight. `isLlmConfigured()`
|
|
19
|
+
// gates the override: when the user never configured an LLM we pass nothing and
|
|
20
|
+
// let @akemi-mio/observer keep its own LLM_* / OBSERVER_* / Ollama fallback, so
|
|
21
|
+
// an existing local-Ollama setup is not silently redirected to the hosted
|
|
22
|
+
// default. This is the D2 fix -- the observer used to hardcode Ollama and
|
|
23
|
+
// ignore `mio config llm` entirely.
|
|
24
|
+
const { llmConfig, isLlmConfigured } = require('./llm-client.js')
|
|
25
|
+
|
|
26
|
+
function observerLlmConfig() {
|
|
27
|
+
return isLlmConfigured() ? llmConfig() : undefined
|
|
28
|
+
}
|
|
29
|
+
|
|
17
30
|
// Observer research pipeline (optional -- installed via @akemi-mio/observer).
|
|
18
31
|
// Loaded lazily at module level so the CLI can report availability without the
|
|
19
32
|
// MCP server having to pass its own handles down.
|
|
@@ -31,10 +44,12 @@ function loadObserver() {
|
|
|
31
44
|
|
|
32
45
|
let ObserverStore = null
|
|
33
46
|
let ObserverService = null
|
|
47
|
+
let ObserverLlmService = null
|
|
34
48
|
try {
|
|
35
49
|
const obs = loadObserver()
|
|
36
50
|
ObserverStore = obs.ObserverStore
|
|
37
51
|
ObserverService = obs.ObserverService
|
|
52
|
+
ObserverLlmService = obs.ObserverLlmService
|
|
38
53
|
} catch {}
|
|
39
54
|
|
|
40
55
|
// Subdirectories that make up the pipeline. `observerStatus` counts files in
|
|
@@ -62,6 +77,17 @@ function readJsonSafe(filePath) {
|
|
|
62
77
|
}
|
|
63
78
|
}
|
|
64
79
|
|
|
80
|
+
// Today's DAG task id, matching DagStateMachine's `dag_YYYYMMDD` (LOCAL date).
|
|
81
|
+
// Duplicated here rather than importing DagStateMachine because its constructor
|
|
82
|
+
// mkdir's the dag directory, and the dry run below must not touch disk.
|
|
83
|
+
function todayTaskId() {
|
|
84
|
+
const d = new Date()
|
|
85
|
+
const y = d.getFullYear()
|
|
86
|
+
const m = String(d.getMonth() + 1).padStart(2, '0')
|
|
87
|
+
const day = String(d.getDate()).padStart(2, '0')
|
|
88
|
+
return `dag_${y}${m}${day}`
|
|
89
|
+
}
|
|
90
|
+
|
|
65
91
|
function readJsonlSafe(filePath) {
|
|
66
92
|
if (!fs.existsSync(filePath)) return []
|
|
67
93
|
try {
|
|
@@ -184,7 +210,7 @@ function createObserverStore(options = {}) {
|
|
|
184
210
|
// object made path.resolve() throw before any collector ran, so collect()
|
|
185
211
|
// was dead on arrival for every caller. (collectorConfig was never read by
|
|
186
212
|
// the package; it was invented here.)
|
|
187
|
-
const service = new ObserverService(baseDirOf(args))
|
|
213
|
+
const service = new ObserverService(baseDirOf(args), observerLlmConfig())
|
|
188
214
|
const sources = args.sources || ['bilibili', 'hackernews', 'github', 'douyin', 'rss']
|
|
189
215
|
const allObs = []
|
|
190
216
|
for (const src of sources) {
|
|
@@ -210,7 +236,7 @@ function createObserverStore(options = {}) {
|
|
|
210
236
|
|
|
211
237
|
async function ferment(args = {}) {
|
|
212
238
|
if (!ObserverService) throw new Error('@akemi-mio/observer not installed')
|
|
213
|
-
const service = new ObserverService(baseDirOf(args))
|
|
239
|
+
const service = new ObserverService(baseDirOf(args), observerLlmConfig())
|
|
214
240
|
const session = args.session || 'afternoon'
|
|
215
241
|
try {
|
|
216
242
|
const fermentation = service.getFermentation()
|
|
@@ -221,6 +247,81 @@ function createObserverStore(options = {}) {
|
|
|
221
247
|
}
|
|
222
248
|
}
|
|
223
249
|
|
|
250
|
+
// Drives the full research DAG (collect → trend → tension → research →
|
|
251
|
+
// multi-brain → compose → world model → publish → self-evolve). This is the
|
|
252
|
+
// entry point D1 found missing: runPipeline/tickPipeline/forcePipeline had no
|
|
253
|
+
// caller anywhere in mio-agent-runtime, so trends/research/insights stayed
|
|
254
|
+
// empty forever unless someone hand-instantiated the service.
|
|
255
|
+
//
|
|
256
|
+
// It is heavy (network + several LLM calls) and writes files, so like every
|
|
257
|
+
// other side-effecting command here it previews by default: without
|
|
258
|
+
// `run: true` it reads today's DAG state and the resolved LLM endpoint and
|
|
259
|
+
// returns a plan without constructing the service, which would mkdir the dag
|
|
260
|
+
// directory. That keeps the README doc gate and the MCP live gate (both call
|
|
261
|
+
// with empty arguments) offline and instant.
|
|
262
|
+
async function pipeline(args = {}) {
|
|
263
|
+
if (!ObserverService) throw new Error('@akemi-mio/observer not installed')
|
|
264
|
+
const baseDir = baseDirOf(args)
|
|
265
|
+
const mode = args.mode || 'analytical'
|
|
266
|
+
const llmConfig = observerLlmConfig()
|
|
267
|
+
|
|
268
|
+
if (!args.run) {
|
|
269
|
+
const taskId = todayTaskId()
|
|
270
|
+
const dag = readJsonSafe(path.join(baseDir, 'dag', `${taskId}.json`))
|
|
271
|
+
// Guard the diagnostic: an older published @akemi-mio/observer lacks
|
|
272
|
+
// ObserverLlmService.getInfo(), and a preview must never throw because of
|
|
273
|
+
// a package-version skew (the mio-cli side ships ahead of the package).
|
|
274
|
+
let llm = null
|
|
275
|
+
if (ObserverLlmService) {
|
|
276
|
+
try {
|
|
277
|
+
const svc = new ObserverLlmService(llmConfig)
|
|
278
|
+
if (svc && typeof svc.getInfo === 'function') llm = svc.getInfo()
|
|
279
|
+
} catch (_) {
|
|
280
|
+
llm = null
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
return {
|
|
284
|
+
dryRun: true,
|
|
285
|
+
baseDir,
|
|
286
|
+
mode,
|
|
287
|
+
observerAvailable: true,
|
|
288
|
+
taskId,
|
|
289
|
+
todayState: dag ? dag.state : null,
|
|
290
|
+
todayCompleted: Boolean(dag && dag.state === 'COMPLETED'),
|
|
291
|
+
llm,
|
|
292
|
+
hint: 'Re-run with --run (CLI) or {"run": true} (MCP) to execute the full DAG.',
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
const service = new ObserverService(baseDir, llmConfig)
|
|
297
|
+
const envelope = await service.forcePipeline(mode)
|
|
298
|
+
if (!envelope) {
|
|
299
|
+
return {
|
|
300
|
+
dryRun: false,
|
|
301
|
+
baseDir,
|
|
302
|
+
mode,
|
|
303
|
+
ran: true,
|
|
304
|
+
completed: false,
|
|
305
|
+
reason: 'forcePipeline returned null (today already completed, or a run is in progress)',
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
const payload = envelope.payload || {}
|
|
309
|
+
return {
|
|
310
|
+
dryRun: false,
|
|
311
|
+
baseDir,
|
|
312
|
+
mode,
|
|
313
|
+
ran: true,
|
|
314
|
+
completed: envelope.dagState && envelope.dagState.state === 'COMPLETED',
|
|
315
|
+
type: envelope.type,
|
|
316
|
+
taskId: envelope.dagState ? envelope.dagState.taskId : null,
|
|
317
|
+
topic: payload.topic || null,
|
|
318
|
+
sections: Array.isArray(payload.sections) ? payload.sections.length : 0,
|
|
319
|
+
insightId: payload.id || null,
|
|
320
|
+
durationMs: envelope.metadata ? envelope.metadata.taskDurationMs : null,
|
|
321
|
+
llmCalls: envelope.metadata ? envelope.metadata.llmCalls : null,
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
|
|
224
325
|
return {
|
|
225
326
|
baseDir: defaultBaseDir,
|
|
226
327
|
status,
|
|
@@ -232,6 +333,7 @@ function createObserverStore(options = {}) {
|
|
|
232
333
|
dag,
|
|
233
334
|
collect,
|
|
234
335
|
ferment,
|
|
336
|
+
pipeline,
|
|
235
337
|
}
|
|
236
338
|
}
|
|
237
339
|
|