@my-life-buddies/cli 0.13.0 → 0.14.1

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.
@@ -0,0 +1,184 @@
1
+ (() => {
2
+ "use strict";
3
+ window.createPreviewDiagnostics = ({ node, formatTime, formatDuration, toast, showError }) => {
4
+ const $ = id => document.getElementById(id);
5
+ const models = $("model-records"), logs = $("protocol-log");
6
+ const modelConversation = $("models-conversation"), logConversation = $("logs-conversation");
7
+ const kind = $("models-kind"), level = $("logs-level");
8
+ const modelSearch = $("models-search"), logSearch = $("logs-search");
9
+ let snapshot = {}, conversation = "", visibleLogs = [];
10
+ const cached = new Map(), expanded = new Map();
11
+ const object = value => value && typeof value === "object" && !Array.isArray(value) ? value : {};
12
+ const json = value => typeof value === "string" ? value : JSON.stringify(value, null, 2);
13
+ const number = value => typeof value === "number" && Number.isFinite(value) && value >= 0;
14
+ const metricHelp = {
15
+ "模型响应耗时": "本次模型请求发出至响应结束。",
16
+ "模型首字耗时": "本次模型请求发出至首段输出。",
17
+ "结束原因": "模型停止原因;toolUse 表示请求调用工具。",
18
+ "输入 Token": "本次调用的输入 Token 数。",
19
+ "输出 Token": "本次调用的输出 Token 数。",
20
+ "缓存读取": "本次调用读取缓存的 Token 数。",
21
+ "缓存写入": "本次调用写入缓存的 Token 数。",
22
+ "总 Token": "本次调用上报的 Token 总数。",
23
+ };
24
+ function section(label, value, key) {
25
+ const details = node("details", { class: "diagnostic-section", "data-detail": key });
26
+ details.append(node("summary", {}, label));
27
+ details.append(node("pre", {}, json(value)));
28
+ return details;
29
+ }
30
+ function metrics(pairs) {
31
+ const list = node("dl", { class: "diagnostic-metrics" });
32
+ for (const [label, value] of pairs) {
33
+ const item = node("div");
34
+ const detail = node("dd", {}, value);
35
+ if (metricHelp[label]) {
36
+ detail.append(node("p", { class: "metric-help" }, metricHelp[label]));
37
+ }
38
+ item.append(node("dt", {}, label), detail);
39
+ list.append(item);
40
+ }
41
+ return list;
42
+ }
43
+ async function copy(text) {
44
+ try { await navigator.clipboard.writeText(text); toast("日志已复制"); }
45
+ catch { showError(new Error("浏览器未允许复制,请展开原始日志后手动选择")); }
46
+ }
47
+ function describe(record) {
48
+ const data = object(record.data), payload = object(data.payload);
49
+ if (record.kind === "request") {
50
+ const messages = payload.messages || payload.input;
51
+ const count = Array.isArray(messages) ? `${messages.length} 条上下文` : "请求上下文";
52
+ return `${typeof payload.model === "string" ? payload.model : "模型未上报"} · ${count}${Array.isArray(payload.tools) ? ` · ${payload.tools.length} 个工具` : ""}`;
53
+ }
54
+ const content = Array.isArray(data.content) ? data.content : [];
55
+ const texts = content.filter(item => item?.type === "text").map(item => item.text).filter(text => typeof text === "string");
56
+ const tools = content.filter(item => item?.type === "toolCall").map(item => item.name).filter(name => typeof name === "string");
57
+ return typeof data.error === "string" ? data.error : tools.length ? `请求工具:${tools.join("、")}` : texts.join(" ") || record.title;
58
+ }
59
+ function row(record, model) {
60
+ const data = object(record.data);
61
+ const details = node("details", { class: "diagnostic-entry", "data-record": record.id, "data-detail": "entry" });
62
+ details.dataset.level = data.error || data.stopReason === "error" ? "error" : record.level;
63
+ const summary = node("summary");
64
+ summary.append(node("span", { class: "diagnostic-badge", "data-kind": record.kind }, model ? record.kind === "request" ? "请求" : "响应" : record.level.toUpperCase()));
65
+ const heading = node("span", { class: "diagnostic-heading" });
66
+ heading.append(node("strong", {}, model ? record.kind === "request" ? "模型请求" : "模型响应" : record.title));
67
+ if (model) heading.append(node("span", { class: "diagnostic-preview" }, describe(record)));
68
+ summary.append(heading, node("time", { dateTime: record.timestamp, title: record.timestamp }, formatTime(record.timestamp)));
69
+ details.append(summary);
70
+ const body = node("div", { class: "diagnostic-body" });
71
+ const meta = node("div", { class: "diagnostic-meta" });
72
+ meta.append(node("span", {}, `${record.convoKey || "未标注会话"} · ${record.source}`));
73
+ const button = node("button", { type: "button", class: "text-button" }, "复制原始日志");
74
+ button.addEventListener("click", () => void copy(record.raw));
75
+ meta.append(button); body.append(meta);
76
+ if (record.truncated || (model && !record.data)) {
77
+ body.append(node("p", { class: "diagnostic-warning" }, record.truncated
78
+ ? "日志已截断,无法还原完整数据。请查看原始日志。"
79
+ : "尚未收到完整数据,或日志格式无法解析。已保留原始日志。"));
80
+ }
81
+ if (model && record.data) {
82
+ if (record.kind === "request") {
83
+ const payload = object(data.payload);
84
+ const messages = payload.messages ?? payload.input;
85
+ const system = payload.system ?? (Array.isArray(messages) ? messages.filter(item => ["system", "developer"].includes(item?.role)) : undefined);
86
+ if (system != null && (!Array.isArray(system) || system.length)) body.append(section("系统提示词", system, "system"));
87
+ if (messages != null) body.append(section(`请求上下文${Array.isArray(messages) ? ` · ${messages.length} 条` : ""}`, messages, "messages"));
88
+ if (payload.tools != null) body.append(section(`可用工具${Array.isArray(payload.tools) ? ` · ${payload.tools.length} 个` : ""}`, payload.tools, "tools"));
89
+ const { messages: _messages, input: _input, system: _system, tools: _tools, ...parameters } = payload;
90
+ body.append(section("模型与请求参数", parameters, "parameters"));
91
+ body.append(section("完整请求体", data.payload ?? data, "payload"));
92
+ } else {
93
+ const usage = object(data.usage);
94
+ body.append(metrics([
95
+ ["模型响应耗时", number(data.ms) ? formatDuration(data.ms) : "未上报"],
96
+ ["模型首字耗时", number(data.firstTokenMs) ? formatDuration(data.firstTokenMs) : "未上报"],
97
+ ["结束原因", typeof data.stopReason === "string" ? data.stopReason : "未上报"],
98
+ ]));
99
+ const tokenFields = [["输入 Token", "input"], ["输出 Token", "output"], ["缓存读取", "cacheRead"], ["缓存写入", "cacheWrite"], ["总 Token", "totalTokens"]];
100
+ body.append(metrics(tokenFields.map(([label, key]) => [label, number(usage[key]) ? String(usage[key]) : "未上报"])));
101
+ if (typeof data.error === "string" && data.error) body.append(node("p", { class: "diagnostic-warning" }, data.error));
102
+ const content = Array.isArray(data.content) ? data.content : [];
103
+ content.forEach((block, index) => {
104
+ if (!block || typeof block !== "object") return;
105
+ const label = block.type === "text" ? "响应正文" : block.type === "thinking" ? "思考内容(日志已提供)" : block.type === "toolCall" ? `工具调用请求 · ${block.name || "未命名"}` : `响应片段 · ${block.type || index + 1}`;
106
+ body.append(section(label, block.type === "text" ? block.text : block, `content-${index}`));
107
+ });
108
+ if (data.content != null && !content.length) body.append(section("响应内容", data.content, "content"));
109
+ if (data.usage != null) body.append(section("完整用量", data.usage, "usage"));
110
+ }
111
+ }
112
+ body.append(section("原始日志", record.raw, "raw"));
113
+ details.append(body);
114
+ return details;
115
+ }
116
+ function reconcile(container, records, model, emptyText) {
117
+ const prefix = model ? "model:" : "log:";
118
+ const wanted = new Set();
119
+ records.forEach((record, index) => {
120
+ const key = prefix + record.id;
121
+ wanted.add(key);
122
+ const signature = JSON.stringify(record);
123
+ let entry = cached.get(key);
124
+ if (!entry || entry.signature !== signature) {
125
+ if (entry) expanded.set(key, [...entry.node.querySelectorAll("details"), entry.node].filter(item => item.open).map(item => item.dataset.detail));
126
+ const element = row(record, model);
127
+ for (const item of [...element.querySelectorAll("details"), element]) item.open = (expanded.get(key) || []).includes(item.dataset.detail);
128
+ entry?.node.remove();
129
+ entry = { node: element, signature };
130
+ cached.set(key, entry);
131
+ }
132
+ if (container.children[index] !== entry.node) container.insertBefore(entry.node, container.children[index] || null);
133
+ });
134
+ for (const child of [...container.children]) if (!wanted.has(prefix + child.dataset.record)) child.remove();
135
+ if (!records.length) {
136
+ const empty = node("div", { class: "diagnostic-empty" });
137
+ empty.append(node("strong", {}, emptyText[0]), node("p", {}, emptyText[1]));
138
+ container.append(empty);
139
+ }
140
+ }
141
+ function updateConversations(records) {
142
+ const keys = [...new Set(records.map(record => record.convoKey).filter(Boolean))].sort();
143
+ if (conversation && !keys.includes(conversation)) keys.push(conversation);
144
+ for (const select of [modelConversation, logConversation]) {
145
+ if (document.activeElement === select) continue;
146
+ const signature = JSON.stringify(keys);
147
+ if (select.dataset.options !== signature) {
148
+ select.replaceChildren(node("option", { value: "" }, "全部会话"), ...keys.map(key => node("option", { value: key }, key)));
149
+ select.dataset.options = signature;
150
+ }
151
+ select.value = conversation;
152
+ }
153
+ }
154
+ function render() {
155
+ const runtime = snapshot.diagnostics?.records || [];
156
+ updateConversations(runtime);
157
+ const matches = (record, search) => (!conversation || record.convoKey === conversation) && (!search || record.raw.toLocaleLowerCase().includes(search.toLocaleLowerCase()));
158
+ const modelRecords = runtime.filter(record => record.kind === "request" || record.kind === "response");
159
+ const filteredModels = modelRecords.filter(record => (!kind.value || record.kind === kind.value) && matches(record, modelSearch.value.trim()));
160
+ reconcile(models, filteredModels, true, modelRecords.length
161
+ ? ["没有匹配的模型记录", "试试其他会话、类型或关键词。"]
162
+ : ["等待模型调用", "启动 DEV 并发送消息后,Runtime 提供的模型请求与响应会出现在这里。旧版本或未输出这些日志的 Runtime 可先查看运行日志。"]);
163
+ $("models-count").textContent = `${filteredModels.length} 条记录${snapshot.diagnostics?.dropped ? ` · 已淘汰 ${snapshot.diagnostics.dropped} 条较早日志` : ""}`;
164
+ const events = snapshot.events || [];
165
+ const deltas = events.filter(event => event.type === "run.output.delta").length;
166
+ const other = events.filter(event => event.type !== "run.output.delta" && (!snapshot.diagnostics || !/^agent\.(stdout|stderr)$/.test(event.type))).map(event => ({
167
+ id: event.eventId, timestamp: event.timestamp, source: event.channel, level: /failed|error|stderr/.test(event.type) ? "error" : "info", title: event.summary || event.type,
168
+ kind: "log", raw: `${event.timestamp} ${event.channel} ${event.type}${event.runId ? ` ${event.runId}` : ""}\n${event.summary || ""}`,
169
+ }));
170
+ const allLogs = [...runtime, ...other].sort((a, b) => Date.parse(b.timestamp) - Date.parse(a.timestamp));
171
+ visibleLogs = allLogs.filter(record => (!level.value || record.level === level.value || level.value === "error" && (record.data?.error || record.data?.stopReason === "error")) && matches(record, logSearch.value.trim()));
172
+ reconcile(logs, visibleLogs, false, allLogs.length ? ["没有匹配的日志", "调整会话、级别或搜索条件后再试。"] : ["等待运行日志", "启动 DEV 后,这里会保留进程输出与连接事件。"]);
173
+ $("logs-count").textContent = `${visibleLogs.length} 条可见日志${deltas ? ` · 已折叠 ${deltas} 条文字分片事件` : ""}${snapshot.diagnostics?.dropped ? ` · 已淘汰 ${snapshot.diagnostics.dropped} 条较早日志` : ""}`;
174
+ const retained = new Set([...runtime.map(record => record.id), ...other.map(record => record.id)]);
175
+ for (const key of cached.keys()) if (!retained.has(key.slice(key.indexOf(":") + 1))) { cached.delete(key); expanded.delete(key); }
176
+ }
177
+ for (const select of [modelConversation, logConversation]) {
178
+ select.addEventListener("change", () => { conversation = select.value; render(); });
179
+ select.addEventListener("blur", render);
180
+ }
181
+ for (const input of [kind, level, modelSearch, logSearch]) input.addEventListener(input.type === "search" ? "input" : "change", render);
182
+ return { update(next) { snapshot = next; render(); }, copyLogs() { return copy(visibleLogs.map(record => record.raw).join("\n\n")); } };
183
+ };
184
+ })();
@@ -13,6 +13,7 @@
13
13
  <link rel="stylesheet" href="/assets/app.css">
14
14
  <script src="/assets/iphone-16-max.js" defer></script>
15
15
  <script src="/assets/widgets.js" defer></script>
16
+ <script src="/assets/diagnostics.js" defer></script>
16
17
  <script src="/assets/app.js" defer></script>
17
18
  <link rel="stylesheet" href="/assets/widgets.css">
18
19
  </head>
@@ -30,16 +31,13 @@
30
31
  <p class="sidebar-label">开发调试</p>
31
32
  <nav class="debug-tabs" role="tablist" aria-label="本地回合调试视图">
32
33
  <button id="tab-overview" role="tab" aria-selected="true" aria-controls="panel-overview" tabindex="0">
33
- <svg aria-hidden="true" viewBox="0 0 24 24"><rect x="4" y="4" width="7" height="7" rx="1.5"/><rect x="13" y="4" width="7" height="7" rx="1.5"/><rect x="4" y="13" width="7" height="7" rx="1.5"/><rect x="13" y="13" width="7" height="7" rx="1.5"/></svg><span>概览</span>
34
+ <svg aria-hidden="true" viewBox="0 0 24 24"><rect x="4" y="4" width="7" height="7" rx="1.5"/><rect x="13" y="4" width="7" height="7" rx="1.5"/><rect x="4" y="13" width="7" height="7" rx="1.5"/><rect x="13" y="13" width="7" height="7" rx="1.5"/></svg><span>运行概况</span>
34
35
  </button>
35
- <button id="tab-timeline" role="tab" aria-selected="false" aria-controls="panel-timeline" tabindex="-1">
36
- <svg aria-hidden="true" viewBox="0 0 24 24"><circle cx="12" cy="12" r="8"/><path d="M12 7v5l3.5 2"/></svg><span>执行时间线</span>
37
- </button>
38
- <button id="tab-context" role="tab" aria-selected="false" aria-controls="panel-context" tabindex="-1">
39
- <svg aria-hidden="true" viewBox="0 0 24 24"><path d="M4 5h13v10H9l-4 4v-4H4Z"/><path d="M8 9h5M8 12h3M18 8h2v10h-7"/></svg><span>对话记录</span>
36
+ <button id="tab-models" role="tab" aria-selected="false" aria-controls="panel-models" tabindex="-1">
37
+ <svg aria-hidden="true" viewBox="0 0 24 24"><path d="M4 5h16v14H4ZM8 9h8M8 13h5"/></svg><span>模型调用</span>
40
38
  </button>
41
39
  <button id="tab-protocol" role="tab" aria-selected="false" aria-controls="panel-protocol" tabindex="-1">
42
- <svg aria-hidden="true" viewBox="0 0 24 24"><rect x="3.5" y="5" width="17" height="14" rx="2"/><path d="m7.5 9 3 3-3 3M13 15h3.5"/></svg><span>协议日志</span>
40
+ <svg aria-hidden="true" viewBox="0 0 24 24"><rect x="3.5" y="5" width="17" height="14" rx="2"/><path d="m7.5 9 3 3-3 3M13 15h3.5"/></svg><span>运行日志</span>
43
41
  </button>
44
42
  </nav>
45
43
  </div>
@@ -85,6 +83,18 @@
85
83
 
86
84
  <main class="workspace" id="debug-content">
87
85
  <section class="debugger" aria-label="本地回合调试器">
86
+ <div class="debug-panels">
87
+ <section id="panel-models" class="debug-panel" role="tabpanel" aria-labelledby="tab-models" hidden>
88
+ <div class="section-heading"><div><h3>模型调用</h3></div></div>
89
+ <div class="diagnostic-filters">
90
+ <label>会话<select id="models-conversation" aria-label="模型会话"><option value="">全部会话</option></select></label>
91
+ <label>类型<select id="models-kind"><option value="">请求与响应</option><option value="request">模型请求</option><option value="response">模型响应</option></select></label>
92
+ <label class="diagnostic-search">搜索<input id="models-search" type="search" placeholder="搜索模型、工具或内容" autocomplete="off"></label>
93
+ </div>
94
+ <p class="diagnostic-note" id="models-count"></p>
95
+ <div id="model-records" class="diagnostic-records"></div>
96
+ </section>
97
+ <section id="panel-overview" class="debug-panel" role="tabpanel" aria-labelledby="tab-overview">
88
98
  <div class="run-strip">
89
99
  <div class="run-heading">
90
100
  <div class="run-identity">
@@ -93,10 +103,10 @@
93
103
  <h2 id="run-title">尚无本地回合</h2>
94
104
  <span class="status-pill" id="run-status" data-status="idle">等待消息</span>
95
105
  </div>
96
- <div class="run-reference" id="run-reference" hidden>
106
+ <details class="run-reference" id="run-reference" hidden><summary>本地发送回执 ID</summary>
97
107
  <span id="run-id"></span>
98
108
  <button type="button" class="text-button" id="copy-run-id" disabled>复制 ID</button>
99
- </div>
109
+ </details>
100
110
  </div>
101
111
  <div class="run-controls">
102
112
  <label class="run-picker-label" for="run-picker">切换回合</label>
@@ -112,29 +122,25 @@
112
122
  </div>
113
123
 
114
124
  <dl class="run-metrics" aria-label="本地回合指标">
115
- <div><dt>接收</dt><dd id="metric-accept">—</dd></div>
116
- <div><dt>首段文字</dt><dd id="metric-first">—</dd></div>
117
- <div><dt>完整耗时</dt><dd id="metric-response">—</dd></div>
118
- <div><dt>消息</dt><dd id="metric-messages">0</dd></div>
125
+ <div><dt>消息被接收</dt><dd><span id="metric-accept">未记录</span><p class="metric-help">Preview 发起发送至平台确认接收。</p></dd></div>
126
+ <div><dt>首字耗时</dt><dd><span id="metric-first">未记录</span><p class="metric-help">Preview 发起发送至收到首段文字。</p></dd></div>
127
+ <div><dt>回复结束</dt><dd><span id="metric-response">未记录</span><p class="metric-help">Preview 发起发送至观察到回复结束。</p></dd></div>
128
+ <div><dt>会话消息</dt><dd><span id="metric-messages">0</span><p class="metric-help">当前会话的消息总数,包含历史消息。</p></dd></div>
119
129
  </dl>
120
130
 
121
131
  </div>
122
-
123
- <div class="debug-panels">
124
- <section id="panel-overview" class="debug-panel" role="tabpanel" aria-labelledby="tab-overview">
125
132
  <div class="overview-grid">
126
133
  <article class="run-summary">
127
134
  <div class="section-heading">
128
- <div><h3>这一次发生了什么</h3><p>从消息到回复,按真实事件还原。</p></div>
129
- <span class="source-label">本机观测</span>
135
+ <div><h3>回复观察</h3></div>
130
136
  </div>
131
137
  <div class="empty-state" id="overview-empty">
132
- <span class="empty-glyph" aria-hidden="true">↗</span>
133
138
  <h4>发一条消息,开始记录</h4>
134
- <p>这里会按本地回合展示真实的接收、首段文字、生成过程和完成状态。</p>
139
+ <p>右侧发送消息后,这里显示消息发送与回复的观察结果。</p>
135
140
  </div>
136
141
  <div id="overview-content" hidden>
137
- <div class="waterfall" id="waterfall" aria-label="本地回合耗时分布"></div>
142
+ <ol class="run-progress" id="run-progress" aria-label="回合执行进度"></ol>
143
+ <div class="waterfall" id="waterfall" aria-label="消息时间节点,均从发起发送计时"></div>
138
144
  <p class="run-summary-copy" id="run-summary-copy"></p>
139
145
  </div>
140
146
  </article>
@@ -144,34 +150,26 @@
144
150
  <h3>DEV · 私聊</h3>
145
151
  <dl>
146
152
  <div><dt>Agent</dt><dd id="environment-agent">等待启动</dd></div>
147
- <div><dt>Runtime</dt><dd id="environment-runtime">—</dd></div>
148
- <div><dt>模型</dt><dd id="environment-model">—</dd></div>
153
+ <div><dt>Runtime</dt><dd id="environment-runtime">未记录</dd></div>
154
+ <div><dt>模型</dt><dd id="environment-model">未记录</dd></div>
149
155
  </dl>
150
156
  </article>
151
157
  </div>
152
- </section>
153
-
154
- <section id="panel-timeline" class="debug-panel" role="tabpanel" aria-labelledby="tab-timeline" hidden>
155
- <div class="section-heading">
156
- <div><h3>执行时间线</h3><p>按发生顺序查看消息发送、流式输出和会话状态。</p></div>
157
- <span class="source-label">真实事件</span>
158
- </div>
159
- <ol class="timeline" id="timeline-list"></ol>
160
- </section>
161
-
162
- <section id="panel-context" class="debug-panel" role="tabpanel" aria-labelledby="tab-context" hidden>
163
- <div class="section-heading">
164
- <div><h3>当前对话记录</h3><p>展示平台保存的当前对话消息。</p></div>
165
- <span class="source-label" id="context-source">平台聊天记录</span>
166
- </div>
167
- <div class="context-list" id="context-list"></div>
158
+ <details class="diagnostic-history"><summary>消息与连接事件</summary><ol class="timeline" id="timeline-list"></ol></details>
159
+ <details class="diagnostic-history"><summary>平台对话记录 <span id="context-source">平台聊天记录</span></summary><div class="context-list" id="context-list"></div></details>
168
160
  </section>
169
161
 
170
162
  <section id="panel-protocol" class="debug-panel" role="tabpanel" aria-labelledby="tab-protocol" hidden>
171
163
  <div class="section-heading">
172
- <div><h3>协议日志</h3><p>Runtime 输出、错误堆栈与 MLB 通信事件。</p></div>
164
+ <div><h3>运行日志</h3></div>
173
165
  <button class="text-button" id="copy-protocol" type="button">复制可见日志</button>
174
166
  </div>
167
+ <div class="diagnostic-filters">
168
+ <label>会话<select id="logs-conversation" aria-label="日志会话"><option value="">全部会话</option></select></label>
169
+ <label>级别<select id="logs-level"><option value="">全部级别</option><option value="error">ERROR</option><option value="warn">WARN</option><option value="info">INFO</option></select></label>
170
+ <label class="diagnostic-search">搜索<input id="logs-search" type="search" placeholder="搜索日志内容" autocomplete="off"></label>
171
+ </div>
172
+ <p class="diagnostic-note" id="logs-count"></p>
175
173
  <div class="protocol-log" id="protocol-log" role="log" aria-live="polite"></div>
176
174
  </section>
177
175
  </div>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@my-life-buddies/cli",
3
- "version": "0.13.0",
3
+ "version": "0.14.1",
4
4
  "type": "module",
5
5
  "description": "搭搭 Developer Platform 命令行工具",
6
6
  "publishConfig": {
@@ -39,7 +39,7 @@
39
39
  "ws": "8.21.3"
40
40
  },
41
41
  "devDependencies": {
42
- "@buddy/cli-core": "0.13.0",
43
- "@buddy/preview": "0.13.0"
42
+ "@buddy/cli-core": "0.14.1",
43
+ "@buddy/preview": "0.14.1"
44
44
  }
45
45
  }
@@ -8,7 +8,7 @@
8
8
  "name": "buddy-agent-project",
9
9
  "version": "0.0.0",
10
10
  "dependencies": {
11
- "@my-life-buddies/buddy-runtime": "0.12.2",
11
+ "@my-life-buddies/buddy-runtime": "0.16.0",
12
12
  "typebox": "1.3.7"
13
13
  },
14
14
  "engines": {
@@ -981,9 +981,9 @@
981
981
  }
982
982
  },
983
983
  "node_modules/@my-life-buddies/buddy-runtime": {
984
- "version": "0.12.2",
985
- "resolved": "https://registry.npmjs.org/@my-life-buddies/buddy-runtime/-/buddy-runtime-0.12.2.tgz",
986
- "integrity": "sha512-LoPt78FO/h6roKoOjVBREeW5UxsZv1NEE41RkIdhg4ffeHL0rzQvi9yaXel+PNT9W9Ud5JfxDTMF7kpqgs0Y7A==",
984
+ "version": "0.16.0",
985
+ "resolved": "https://registry.npmjs.org/@my-life-buddies/buddy-runtime/-/buddy-runtime-0.16.0.tgz",
986
+ "integrity": "sha512-NkJef6s39Fs/pnxtC5q5ee8dEFMUudPcyO6a4v22xMZmaDIsFBvb/R3D1q1/bVrSCKAvhSaVe0sM9LrhuPn02w==",
987
987
  "dependencies": {
988
988
  "@earendil-works/pi-agent-core": "0.85.1",
989
989
  "@earendil-works/pi-ai": "0.85.1",
@@ -4,13 +4,13 @@
4
4
  "private": true,
5
5
  "type": "module",
6
6
  "scripts": {
7
- "start": "node start.mjs"
7
+ "start": "node --env-file-if-exists=.env start.mjs"
8
8
  },
9
9
  "engines": {
10
10
  "node": ">=24"
11
11
  },
12
12
  "dependencies": {
13
- "@my-life-buddies/buddy-runtime": "0.12.2",
13
+ "@my-life-buddies/buddy-runtime": "0.16.0",
14
14
  "typebox": "1.3.7"
15
15
  }
16
16
  }