@lazyingart/agintiflow 0.20.170 → 0.20.172

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lazyingart/agintiflow",
3
- "version": "0.20.170",
3
+ "version": "0.20.172",
4
4
  "type": "module",
5
5
  "description": "AgInTiFlow is a project-aware agent workspace for hybrid wet-dry R&D, hardware-aware intelligence, software automation, and industrial workflows.",
6
6
  "license": "Apache-2.0",
package/public/app.js CHANGED
@@ -1795,6 +1795,15 @@ function renderLogs(run) {
1795
1795
  parts.push(renderCommandOutputLog(entry));
1796
1796
  continue;
1797
1797
  }
1798
+ if (entry.message === "plan.created" && entry.data?.plan) {
1799
+ parts.push(`
1800
+ <article class="log-plan">
1801
+ <div class="log-plan-title">${escapeHtml(`[${entry.at}] plan`)}</div>
1802
+ <div class="markdown-body">${renderMarkdown(entry.data.plan)}</div>
1803
+ </article>
1804
+ `);
1805
+ continue;
1806
+ }
1798
1807
  parts.push(`<div class="log-line">${escapeHtml(`[${entry.at}] ${entry.kind}: ${entry.message}`)}</div>`);
1799
1808
  if (entry.message === "tool.blocked" && entry.data?.permissionAdvice) {
1800
1809
  parts.push(renderPermissionApproval(entry));
@@ -2044,6 +2053,18 @@ function renderChat(chatEntries) {
2044
2053
 
2045
2054
  chatThreadEl.innerHTML = lastChatEntries
2046
2055
  .map((entry) => {
2056
+ if (entry.role === "event") {
2057
+ const label = entry.eventLabel || entry.eventType || "event";
2058
+ const content = entry.markdown
2059
+ ? `<div class="markdown-body">${renderMarkdown(entry.content)}</div>`
2060
+ : escapeHtml(entry.content || "").replace(/\n/g, "<br>");
2061
+ return `
2062
+ <article class="chat-item event" data-event-type="${escapeHtml(entry.eventType || "")}">
2063
+ <div class="chat-meta">${escapeHtml(label)}${entry.at ? ` · ${new Date(entry.at).toLocaleString()}` : ""}</div>
2064
+ <div class="chat-content">${content}</div>
2065
+ </article>
2066
+ `;
2067
+ }
2047
2068
  const role = entry.role === "assistant" ? "assistant" : "user";
2048
2069
  const label = role === "assistant" ? t("assistantLabel") : t("youLabel");
2049
2070
  const content =
@@ -2830,7 +2851,7 @@ async function refreshChat() {
2830
2851
  pendingInboxItems = data.inbox || [];
2831
2852
  pendingAfterFinishItems = loadAfterFinishQueue(currentSessionId);
2832
2853
  renderPendingMessages();
2833
- renderChat(data.chat || []);
2854
+ renderChat(data.timeline || data.chat || []);
2834
2855
  }
2835
2856
 
2836
2857
  async function savePreferences() {
package/public/styles.css CHANGED
@@ -1155,6 +1155,23 @@ button.danger {
1155
1155
  color: #fecaca;
1156
1156
  }
1157
1157
 
1158
+ .log-plan {
1159
+ margin: 0 0 12px;
1160
+ padding: 12px;
1161
+ border: 1px solid rgba(94, 234, 212, 0.22);
1162
+ border-radius: 14px;
1163
+ background: rgba(15, 23, 42, 0.54);
1164
+ }
1165
+
1166
+ .log-plan-title {
1167
+ margin-bottom: 8px;
1168
+ color: #99f6e4;
1169
+ font-size: 0.82rem;
1170
+ font-weight: 800;
1171
+ letter-spacing: 0.04em;
1172
+ text-transform: uppercase;
1173
+ }
1174
+
1158
1175
  .log-json,
1159
1176
  .log-command pre {
1160
1177
  margin: 8px 0 12px;
@@ -1271,6 +1288,25 @@ button.danger {
1271
1288
  border-bottom-left-radius: 6px;
1272
1289
  }
1273
1290
 
1291
+ .chat-item.event {
1292
+ justify-self: stretch;
1293
+ width: auto;
1294
+ max-width: 100%;
1295
+ border-color: rgba(20, 184, 166, 0.28);
1296
+ border-style: dashed;
1297
+ background:
1298
+ linear-gradient(90deg, rgba(20, 184, 166, 0.12), rgba(245, 158, 11, 0.08)),
1299
+ rgba(255, 255, 255, 0.48);
1300
+ box-shadow: none;
1301
+ }
1302
+
1303
+ .chat-item.event[data-event-type="plan.created"] {
1304
+ border-style: solid;
1305
+ background:
1306
+ radial-gradient(circle at 0% 0%, rgba(20, 184, 166, 0.16), transparent 28%),
1307
+ linear-gradient(180deg, rgba(240, 253, 250, 0.88), rgba(255, 251, 235, 0.7));
1308
+ }
1309
+
1274
1310
  .chat-meta {
1275
1311
  font-size: 0.8rem;
1276
1312
  color: var(--muted);
@@ -890,10 +890,13 @@ try {
890
890
  if (!latest.stdout.includes(" user>") || !latest.stdout.includes("aginti>") || latest.stdout.includes("user> ")) {
891
891
  throw new Error("resume history should use prompt-style user>/aginti> labels");
892
892
  }
893
- if (!latest.stdout.includes("resume note=showing chat transcript only")) {
894
- throw new Error("bare aginti resume did not clarify that tool/run events are separate from chat history");
893
+ if (!latest.stdout.includes(" plan ")) {
894
+ throw new Error("bare aginti resume did not replay saved plan/run context");
895
895
  }
896
- if (latest.stdout.includes("showing=") || latest.stdout.includes("…")) {
896
+ if (latest.stdout.includes("resume note=showing chat transcript only")) {
897
+ throw new Error("bare aginti resume regressed to chat-only history");
898
+ }
899
+ if (latest.stdout.includes("showing=")) {
897
900
  throw new Error("resume history should render full saved messages instead of compact previews");
898
901
  }
899
902
 
@@ -303,6 +303,15 @@ try {
303
303
  const chat = await fetchJson(`/api/sessions/${encodeURIComponent(runStart.sessionId)}/chat`);
304
304
  if (!Array.isArray(chat.chat) || chat.chat.length < 2) throw new Error("chat history was not persisted");
305
305
  if (!Array.isArray(chat.inbox)) throw new Error("chat endpoint did not include shared inbox state");
306
+ if (!Array.isArray(chat.events) || !chat.events.some((entry) => entry.type === "plan.created")) {
307
+ throw new Error("chat endpoint did not include saved plan events");
308
+ }
309
+ if (
310
+ !Array.isArray(chat.timeline) ||
311
+ !chat.timeline.some((entry) => entry.role === "event" && entry.eventType === "plan.created" && String(entry.content || "").trim())
312
+ ) {
313
+ throw new Error("chat endpoint did not return a resume-ready timeline with the saved plan");
314
+ }
306
315
 
307
316
  const queued = await fetchJson(`/api/sessions/${encodeURIComponent(runStart.sessionId)}/inbox`, {
308
317
  method: "POST",
@@ -2207,6 +2207,127 @@ function printHistoryEntry(entry) {
2207
2207
  printHistoryBlock(role, entry.content, { time, bg });
2208
2208
  }
2209
2209
 
2210
+ function timestampMs(value = "") {
2211
+ const parsed = Date.parse(String(value || ""));
2212
+ return Number.isFinite(parsed) ? parsed : 0;
2213
+ }
2214
+
2215
+ function isReplayableResumeEvent(event = {}) {
2216
+ const type = String(event.type || "");
2217
+ const data = event.data || {};
2218
+ if (type === "plan.created") return Boolean(data.plan);
2219
+ if (
2220
+ [
2221
+ "budget.initialized",
2222
+ "conversation.continued",
2223
+ "conversation.queued_input_applied",
2224
+ "file.changed",
2225
+ "session.failed",
2226
+ "session.finished",
2227
+ "session.stopped",
2228
+ "tool.blocked",
2229
+ "tool.failed",
2230
+ "tool.skipped",
2231
+ "tool.started",
2232
+ ].includes(type)
2233
+ ) {
2234
+ return true;
2235
+ }
2236
+ if (type === "tool.completed") {
2237
+ const toolName = String(data.toolName || "");
2238
+ return ["run_command", "write_file", "apply_patch", "create_artifact"].includes(toolName) || data.ok === false || data.blocked || data.error;
2239
+ }
2240
+ return false;
2241
+ }
2242
+
2243
+ function resumeTimelineItems(chat = [], events = [], { limit = 0 } = {}) {
2244
+ const shownChat = limit > 0 ? chat.slice(-limit) : chat;
2245
+ const firstChatTime = limit > 0 && shownChat.length > 0 ? timestampMs(shownChat[0].at) : 0;
2246
+ const chatItems = shownChat.map((entry, index) => ({
2247
+ kind: "chat",
2248
+ entry,
2249
+ order: index * 2,
2250
+ at: timestampMs(entry.at),
2251
+ }));
2252
+ const eventItems = events
2253
+ .map((event, index) => ({ event, index }))
2254
+ .filter(({ event }) => isReplayableResumeEvent(event))
2255
+ .filter(({ event }) => !firstChatTime || timestampMs(event.timestamp) >= firstChatTime)
2256
+ .map(({ event, index }) => ({
2257
+ kind: "event",
2258
+ event,
2259
+ order: index * 2 + 1,
2260
+ at: timestampMs(event.timestamp),
2261
+ }));
2262
+ return [...chatItems, ...eventItems].sort((left, right) => (left.at || 0) - (right.at || 0) || left.order - right.order);
2263
+ }
2264
+
2265
+ function printResumeEvent(event = {}) {
2266
+ const type = String(event.type || "");
2267
+ const data = event.data || {};
2268
+ if (type === "plan.created") {
2269
+ printWrapped(`${label("plan", ansi.systemBg)} `, data.plan || "");
2270
+ return true;
2271
+ }
2272
+ if (type === "tool.started") {
2273
+ outputLine(`${label("tool", ansi.statusBg)} ${toolStatusDetails(data)}`);
2274
+ return true;
2275
+ }
2276
+ if (type === "tool.completed" || type === "tool.failed" || type === "tool.skipped") {
2277
+ const failed = type === "tool.failed" || data.ok === false || data.blocked || data.error;
2278
+ if (data.toolName === "run_command") {
2279
+ printCommandOutputLog({
2280
+ command: data.args?.command || data.command || "",
2281
+ stdout: data.stdout || "",
2282
+ stderr: data.stderr || "",
2283
+ diagnosticHint: data.diagnosticHint || "",
2284
+ commandPolicy: data.commandPolicy,
2285
+ blocked: Boolean(data.blocked),
2286
+ error: data.error || data.reason || "",
2287
+ permissionAdvice: data.permissionAdvice || null,
2288
+ });
2289
+ }
2290
+ outputLine(`${label(failed ? "fail" : "done", failed ? ansi.red : ansi.systemBg)} ${toolStatusDetails(data)}`);
2291
+ return true;
2292
+ }
2293
+ if (type === "tool.blocked") {
2294
+ outputLine(`${label("perm", ansi.red)} ${compactLine(data.reason || data.permissionAdvice?.summary || data.toolName || "Permission blocked.", 104)}`);
2295
+ if (data.permissionAdvice?.suggestedCommand) outputLine(`${color(" | ", ansi.red)} rerun: ${compactLine(data.permissionAdvice.suggestedCommand, 120)}`);
2296
+ if (data.permissionAdvice?.trustedHostCommand) outputLine(`${color(" | ", ansi.red)} host: ${compactLine(data.permissionAdvice.trustedHostCommand, 120)}`);
2297
+ return true;
2298
+ }
2299
+ if (type === "file.changed") {
2300
+ if (data.diff) printWorkspaceChange(data);
2301
+ else outputLine(`${label("write", ansi.systemBg)} ${compactLine(formatWorkspaceChange(data).summary, 92)}`);
2302
+ return true;
2303
+ }
2304
+ if (type === "budget.initialized") {
2305
+ outputLine(`${label("state", ansi.systemBg)} budget=${data.currentMaxSteps || data.initialMaxSteps || data.maxSteps || "unknown"} steps`);
2306
+ return true;
2307
+ }
2308
+ if (type === "conversation.continued") {
2309
+ outputLine(`${label("state", ansi.systemBg)} continued=${compactLine(data.prompt || "", 76)}`);
2310
+ return true;
2311
+ }
2312
+ if (type === "conversation.queued_input_applied") {
2313
+ outputLine(`${label("state", ansi.systemBg)} queued_input_applied=${data.priority === "asap" ? "asap" : "normal"}`);
2314
+ return true;
2315
+ }
2316
+ if (type === "session.finished") {
2317
+ outputLine(`${label("state", ansi.systemBg)} status=finished${data.result ? ` result=${compactLine(data.result, 86)}` : ""}`);
2318
+ return true;
2319
+ }
2320
+ if (type === "session.failed") {
2321
+ outputLine(`${label("state", ansi.red)} status=failed${data.error ? ` error=${compactLine(data.error, 86)}` : ""}`);
2322
+ return true;
2323
+ }
2324
+ if (type === "session.stopped") {
2325
+ outputLine(`${label("state", ansi.systemBg)} status=stopped${data.reason ? ` reason=${compactLine(data.reason, 86)}` : ""}`);
2326
+ return true;
2327
+ }
2328
+ return false;
2329
+ }
2330
+
2210
2331
  async function printResumeHistory(state, { limit = 0 } = {}) {
2211
2332
  if (!state.sessionId) return;
2212
2333
  const paths = projectPaths(process.cwd());
@@ -2215,23 +2336,24 @@ async function printResumeHistory(state, { limit = 0 } = {}) {
2215
2336
  const events = await store.loadEvents().catch(() => []);
2216
2337
  const chat = Array.isArray(saved?.chat) ? saved.chat.filter((entry) => entry?.content) : [];
2217
2338
  promptHistory.seedFromChat(chat, state.sessionId);
2218
- const metadata = `chat=${chat.length}${events.length > 0 ? ` events=${events.length}` : ""}`;
2219
- if (chat.length === 0) {
2339
+ const timeline = resumeTimelineItems(chat, events, { limit });
2340
+ const replayedEvents = timeline.filter((item) => item.kind === "event").length;
2341
+ const metadata = `chat=${chat.length}${events.length > 0 ? ` events=${events.length}` : ""}${replayedEvents > 0 ? ` replay=${replayedEvents}` : ""}`;
2342
+ if (timeline.length === 0) {
2220
2343
  printSystemLine(`resume history session=${state.sessionId} ${metadata}`);
2221
- if (events.length > 0) {
2222
- printSystemLine("resume note=showing chat transcript only; model/tool/run events are saved in events.jsonl and artifacts/");
2223
- }
2224
2344
  return;
2225
2345
  }
2226
2346
 
2227
- const shown = limit > 0 ? chat.slice(-limit) : chat;
2228
2347
  printSystemLine(
2229
- `resume history session=${state.sessionId} ${metadata}${limit > 0 ? ` showing=${shown.length}/${chat.length}` : ""}`
2348
+ `resume history session=${state.sessionId} ${metadata}${limit > 0 ? ` showing=${timeline.length}/${chat.length + events.length}` : ""}`
2230
2349
  );
2231
- if (events.length > chat.length) {
2232
- printSystemLine("resume note=showing chat transcript only; model/tool/run events are saved in events.jsonl and artifacts/");
2350
+ if (events.length > replayedEvents) {
2351
+ printSystemLine("resume note=showing chat plus key run events; full raw events remain in events.jsonl and artifacts/.");
2352
+ }
2353
+ for (const item of timeline) {
2354
+ if (item.kind === "chat") printHistoryEntry(item.entry);
2355
+ else printResumeEvent(item.event);
2233
2356
  }
2234
- for (const entry of shown) printHistoryEntry(entry);
2235
2357
  }
2236
2358
 
2237
2359
  function toolStatusDetails(data = {}) {
package/src/web-db.js CHANGED
@@ -245,6 +245,18 @@ export class WebDatabase {
245
245
  }
246
246
  this.savePreferences(preferences);
247
247
  }
248
+ if (
249
+ preferences.veniceMode !== true &&
250
+ preferences.routingMode !== "manual" &&
251
+ preferences.provider !== "venice" &&
252
+ (preferences.routeProvider === "venice" || preferences.mainProvider === "venice")
253
+ ) {
254
+ preferences.routeProvider = "deepseek";
255
+ preferences.routeModel = "deepseek-v4-flash";
256
+ preferences.mainProvider = "deepseek";
257
+ preferences.mainModel = "deepseek-v4-pro";
258
+ this.savePreferences(preferences);
259
+ }
248
260
  return preferences;
249
261
  } catch {
250
262
  return defaultPreferences(this.baseDir);
package/web.js CHANGED
@@ -102,6 +102,131 @@ function mapEventLogs(events) {
102
102
  }));
103
103
  }
104
104
 
105
+ function compactText(value = "", limit = 120) {
106
+ const text = String(value || "").replace(/\s+/g, " ").trim();
107
+ return text.length <= limit ? text : `${text.slice(0, Math.max(limit - 1, 1)).trim()}...`;
108
+ }
109
+
110
+ function toolTimelineSummary(data = {}) {
111
+ const tool = data.toolName || "unknown";
112
+ const args = data.args || {};
113
+ if (tool === "run_command" && args.command) return `${tool}: ${compactText(args.command, 92)}`;
114
+ if ((tool === "write_file" || tool === "apply_patch" || tool === "read_file" || tool === "open_workspace_file") && args.path) {
115
+ return `${tool}: ${compactText(args.path, 92)}`;
116
+ }
117
+ if ((tool === "open_url" || tool === "web_research" || tool === "web_search") && (args.url || args.query || args.q)) {
118
+ return `${tool}: ${compactText(args.url || args.query || args.q, 92)}`;
119
+ }
120
+ return tool;
121
+ }
122
+
123
+ function timelineEntryForEvent(event = {}) {
124
+ const type = String(event.type || "");
125
+ const data = event.data || {};
126
+ const at = event.timestamp || "";
127
+ if (type === "plan.created" && data.plan) {
128
+ return {
129
+ role: "event",
130
+ eventType: type,
131
+ eventLabel: "plan",
132
+ content: data.plan,
133
+ markdown: true,
134
+ at,
135
+ };
136
+ }
137
+ if (type === "tool.started") {
138
+ return {
139
+ role: "event",
140
+ eventType: type,
141
+ eventLabel: "tool",
142
+ content: toolTimelineSummary(data),
143
+ at,
144
+ };
145
+ }
146
+ if (type === "tool.completed" || type === "tool.failed" || type === "tool.skipped") {
147
+ const failed = type === "tool.failed" || data.ok === false || data.blocked || data.error;
148
+ const stdout = data.toolName === "run_command" && data.stdout ? `\nstdout: ${compactText(data.stdout, 180)}` : "";
149
+ const stderr = data.toolName === "run_command" && data.stderr ? `\nstderr: ${compactText(data.stderr, 180)}` : "";
150
+ return {
151
+ role: "event",
152
+ eventType: type,
153
+ eventLabel: failed ? "tool failed" : "tool done",
154
+ content: `${toolTimelineSummary(data)}${stdout}${stderr}`,
155
+ at,
156
+ };
157
+ }
158
+ if (type === "tool.blocked") {
159
+ return {
160
+ role: "event",
161
+ eventType: type,
162
+ eventLabel: "permission",
163
+ content: data.permissionAdvice?.summary || data.reason || data.toolName || "Permission blocked.",
164
+ at,
165
+ };
166
+ }
167
+ if (type === "file.changed") {
168
+ return {
169
+ role: "event",
170
+ eventType: type,
171
+ eventLabel: data.toolName === "apply_patch" ? "patch" : "write",
172
+ content: [data.toolName || data.action || "file.changed", data.path || "", data.created ? "created" : "updated"]
173
+ .filter(Boolean)
174
+ .join(" "),
175
+ at,
176
+ };
177
+ }
178
+ if (type === "budget.initialized") {
179
+ return {
180
+ role: "event",
181
+ eventType: type,
182
+ eventLabel: "budget",
183
+ content: `${data.currentMaxSteps || data.initialMaxSteps || data.maxSteps || "unknown"} steps`,
184
+ at,
185
+ };
186
+ }
187
+ if (type === "conversation.continued") {
188
+ return {
189
+ role: "event",
190
+ eventType: type,
191
+ eventLabel: "continued",
192
+ content: data.prompt || "",
193
+ at,
194
+ };
195
+ }
196
+ if (type === "conversation.queued_input_applied") {
197
+ return {
198
+ role: "event",
199
+ eventType: type,
200
+ eventLabel: "queued input",
201
+ content: data.priority === "asap" ? "ASAP queued input applied" : "Queued input applied",
202
+ at,
203
+ };
204
+ }
205
+ if (type === "session.finished" || type === "session.failed" || type === "session.stopped") {
206
+ return {
207
+ role: "event",
208
+ eventType: type,
209
+ eventLabel: type.replace("session.", ""),
210
+ content: data.result || data.error || data.reason || type,
211
+ at,
212
+ };
213
+ }
214
+ return null;
215
+ }
216
+
217
+ function sessionTimelineFromChatAndEvents(chat = [], events = []) {
218
+ const chatItems = (Array.isArray(chat) ? chat : [])
219
+ .filter((entry) => entry?.content)
220
+ .map((entry, index) => ({ ...entry, order: index * 2, sortAt: Date.parse(entry.at || "") || 0 }));
221
+ const eventItems = (Array.isArray(events) ? events : [])
222
+ .map(timelineEntryForEvent)
223
+ .filter(Boolean)
224
+ .map((entry, index) => ({ ...entry, order: index * 2 + 1, sortAt: Date.parse(entry.at || "") || 0 }));
225
+ return [...chatItems, ...eventItems]
226
+ .sort((left, right) => (left.sortAt || 0) - (right.sortAt || 0) || left.order - right.order)
227
+ .map(({ sortAt: _sortAt, order: _order, ...entry }) => entry);
228
+ }
229
+
105
230
  async function loadStoredRun(sessionId) {
106
231
  const meta = db.getSession(sessionId);
107
232
  if (!meta) return null;
@@ -606,11 +731,16 @@ function deriveChatFromState(state, meta) {
606
731
 
607
732
  async function loadChat(sessionId) {
608
733
  const store = sessionStore(sessionId);
609
- const [state, meta] = await Promise.all([store.loadState(), Promise.resolve(db.getSession(sessionId))]);
734
+ const [state, meta, events] = await Promise.all([
735
+ store.loadState(),
736
+ Promise.resolve(db.getSession(sessionId)),
737
+ store.loadEvents().catch(() => []),
738
+ ]);
610
739
 
611
740
  if (!state && !meta) return null;
612
741
 
613
742
  const inbox = await store.loadInbox().catch(() => []);
743
+ const chat = deriveChatFromState(state, meta);
614
744
  return {
615
745
  sessionId,
616
746
  goal: state?.goal || meta?.goal || "",
@@ -619,7 +749,9 @@ async function loadChat(sessionId) {
619
749
  model: state?.model || meta?.model || "",
620
750
  status: meta?.status || "",
621
751
  inbox,
622
- chat: deriveChatFromState(state, meta),
752
+ chat,
753
+ events: events.slice(-120),
754
+ timeline: sessionTimelineFromChatAndEvents(chat, events).slice(-220),
623
755
  };
624
756
  }
625
757