@songsid/agend 2.2.0-beta.6 → 2.2.0-beta.8
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/agent-cli-instructions.md +6 -2
- package/dist/agent-cli.js +9 -5
- package/dist/agent-cli.js.map +1 -1
- package/dist/agent-whats-new.js +9 -3
- package/dist/agent-whats-new.js.map +1 -1
- package/dist/backend/kiro.d.ts +9 -0
- package/dist/backend/kiro.js +23 -0
- package/dist/backend/kiro.js.map +1 -1
- package/dist/backend/muse.d.ts +26 -0
- package/dist/backend/muse.js +131 -0
- package/dist/backend/muse.js.map +1 -1
- package/dist/channel/adapters/discord.js +12 -1
- package/dist/channel/adapters/discord.js.map +1 -1
- package/dist/channel/adapters/telegram.js +6 -0
- package/dist/channel/adapters/telegram.js.map +1 -1
- package/dist/channel/mcp-tools.js +3 -3
- package/dist/channel/mcp-tools.js.map +1 -1
- package/dist/channel/types.d.ts +3 -0
- package/dist/config-validator.js +3 -0
- package/dist/config-validator.js.map +1 -1
- package/dist/cross-instance-envelope.js +2 -0
- package/dist/cross-instance-envelope.js.map +1 -1
- package/dist/daemon.d.ts +8 -0
- package/dist/daemon.js +126 -54
- package/dist/daemon.js.map +1 -1
- package/dist/delivery-outbox.d.ts +27 -0
- package/dist/delivery-outbox.js +175 -6
- package/dist/delivery-outbox.js.map +1 -1
- package/dist/fleet-manager.d.ts +40 -1
- package/dist/fleet-manager.js +248 -40
- package/dist/fleet-manager.js.map +1 -1
- package/dist/general-knowledge/skills/cross-instance-messaging/SKILL.md +16 -1
- package/dist/general-knowledge/skills/worker-collaboration/SKILL.md +4 -0
- package/dist/human-delivery-receipts.d.ts +83 -0
- package/dist/human-delivery-receipts.js +194 -0
- package/dist/human-delivery-receipts.js.map +1 -0
- package/dist/instance-config-impact.js +1 -0
- package/dist/instance-config-impact.js.map +1 -1
- package/dist/instructions.d.ts +1 -1
- package/dist/instructions.js +1 -1
- package/dist/instructions.js.map +1 -1
- package/dist/locale.js +8 -0
- package/dist/locale.js.map +1 -1
- package/dist/log-service.d.ts +99 -0
- package/dist/log-service.js +235 -0
- package/dist/log-service.js.map +1 -0
- package/dist/log-steps.d.ts +143 -0
- package/dist/log-steps.js +646 -0
- package/dist/log-steps.js.map +1 -0
- package/dist/outbound-handlers.d.ts +3 -0
- package/dist/outbound-handlers.js +20 -4
- package/dist/outbound-handlers.js.map +1 -1
- package/dist/outbound-schemas.d.ts +15 -13
- package/dist/outbound-schemas.js +5 -0
- package/dist/outbound-schemas.js.map +1 -1
- package/dist/public-link-progress.d.ts +1 -1
- package/dist/public-link-progress.js +8 -2
- package/dist/public-link-progress.js.map +1 -1
- package/dist/public-preview.d.ts +2 -1
- package/dist/public-preview.js +8 -5
- package/dist/public-preview.js.map +1 -1
- package/dist/public-web-gateway.js +1 -1
- package/dist/public-web-gateway.js.map +1 -1
- package/dist/public-web-link.d.ts +4 -1
- package/dist/public-web-link.js +42 -13
- package/dist/public-web-link.js.map +1 -1
- package/dist/reply-buttons-controller.d.ts +3 -0
- package/dist/reply-buttons-controller.js +3 -2
- package/dist/reply-buttons-controller.js.map +1 -1
- package/dist/settings-api.js +4 -0
- package/dist/settings-api.js.map +1 -1
- package/dist/settings-change.js +2 -0
- package/dist/settings-change.js.map +1 -1
- package/dist/settings-effect.js +2 -0
- package/dist/settings-effect.js.map +1 -1
- package/dist/tool-progress.js +3 -2
- package/dist/tool-progress.js.map +1 -1
- package/dist/topic-commands.d.ts +3 -2
- package/dist/topic-commands.js +14 -4
- package/dist/topic-commands.js.map +1 -1
- package/dist/transcript-jsonl.d.ts +80 -3
- package/dist/transcript-jsonl.js +147 -10
- package/dist/transcript-jsonl.js.map +1 -1
- package/dist/transcript-monitor.d.ts +11 -0
- package/dist/transcript-monitor.js +61 -13
- package/dist/transcript-monitor.js.map +1 -1
- package/dist/transcript-sources.d.ts +24 -0
- package/dist/transcript-sources.js +205 -55
- package/dist/transcript-sources.js.map +1 -1
- package/dist/tunnel/cloudflared.d.ts +7 -15
- package/dist/tunnel/cloudflared.js +36 -1
- package/dist/tunnel/cloudflared.js.map +1 -1
- package/dist/tunnel/types.d.ts +5 -1
- package/dist/tunnel/types.js.map +1 -1
- package/dist/types.d.ts +6 -0
- package/dist/ui/chat-strings.js +8 -2
- package/dist/ui/chat-thread.js +24 -4
- package/dist/ui/chat-time.js +80 -0
- package/dist/ui/panel-log.js +388 -0
- package/dist/ui/panel-settings.js +6 -1
- package/dist/ui/preview-panel.js +7 -1
- package/dist/ui/preview.js +5 -1
- package/dist/ui/settings-strings.js +2 -0
- package/dist/ui/shared/app-i18n.js +2 -2
- package/dist/ui/shared/app-route.js +6 -1
- package/dist/ui/shared/app-shell.js +3 -3
- package/dist/ui/shared/app-stream.js +2 -1
- package/dist/ui/shared/app.css +44 -6
- package/dist/ui/shared/app.js +5 -0
- package/dist/ui/shared/fonts/README.md +1 -1
- package/dist/ui/shared/instance-nav.js +9 -6
- package/dist/ui/shared/instance-switch.js +4 -4
- package/dist/ui/shared/tokens.css +1 -1
- package/dist/web-api.d.ts +5 -0
- package/dist/web-api.js +49 -1
- package/dist/web-api.js.map +1 -1
- package/dist/web-auth.js +2 -1
- package/dist/web-auth.js.map +1 -1
- package/dist/web-commands.d.ts +2 -2
- package/dist/web-commands.js +3 -2
- package/dist/web-commands.js.map +1 -1
- package/dist/web-reply-files.d.ts +45 -0
- package/dist/web-reply-files.js +223 -0
- package/dist/web-reply-files.js.map +1 -0
- package/dist/web-settings-patch.d.ts +3 -1
- package/dist/web-settings-patch.js +4 -1
- package/dist/web-settings-patch.js.map +1 -1
- package/dist/web-shell-routes.d.ts +4 -0
- package/dist/web-shell-routes.js +9 -0
- package/dist/web-shell-routes.js.map +1 -1
- package/dist/web-upload.d.ts +17 -4
- package/dist/web-upload.js +64 -13
- package/dist/web-upload.js.map +1 -1
- package/package.json +1 -1
package/dist/ui/shared/app.css
CHANGED
|
@@ -208,7 +208,7 @@ html:has(.shell), html:has(.shell) body { overflow: hidden; }
|
|
|
208
208
|
:root { --text-scale: 1; }
|
|
209
209
|
:root[data-text-size="s"] { --text-scale: 0.9; }
|
|
210
210
|
:root[data-text-size="l"] { --text-scale: 1.15; }
|
|
211
|
-
.thread, .p-details .panel-body, .p-fleet .panel-body {
|
|
211
|
+
.thread, .p-details .panel-body, .p-fleet .panel-body, .p-log .panel-body {
|
|
212
212
|
--fs-xs: calc(12px * var(--text-scale)); --lh-xs: calc(16px * var(--text-scale));
|
|
213
213
|
--fs-sm: calc(13px * var(--text-scale)); --lh-sm: calc(18px * var(--text-scale));
|
|
214
214
|
--fs-md: calc(15px * var(--text-scale)); --lh-md: calc(24px * var(--text-scale));
|
|
@@ -217,10 +217,40 @@ html:has(.shell), html:has(.shell) body { overflow: hidden; }
|
|
|
217
217
|
}
|
|
218
218
|
/* #1523 N2: Details. */
|
|
219
219
|
.p-details .d-tags { display: inline-flex; flex-wrap: wrap; gap: var(--s-1); }
|
|
220
|
+
/* #1218: an instance's Log — one column of steps, newest at the bottom; the filter bar above it. Classes only (#1300). */
|
|
221
|
+
.p-log .log-bar { display: flex; align-items: center; gap: var(--s-1); padding: var(--s-2) var(--s-4); border-bottom: 1px solid var(--border); flex: none; }
|
|
222
|
+
.p-log .log-bar .chip-btn.on { color: var(--text); background: var(--surface-2); font-weight: var(--fw-semibold); }
|
|
223
|
+
.p-log .log-live { display: inline-flex; align-items: center; gap: 6px; font-size: var(--fs-xs); color: var(--text-3); }
|
|
224
|
+
.p-log .log-list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: var(--s-1); }
|
|
225
|
+
.p-log .log-item { padding: var(--s-2) var(--s-3); border-radius: var(--r-md); background: var(--surface); min-width: 0; overflow-wrap: anywhere; }
|
|
226
|
+
.p-log .log-item.turn-start { margin-top: var(--s-4); }
|
|
227
|
+
.p-log .log-item.k-user { background: var(--user-bubble); }
|
|
228
|
+
.p-log .log-item.k-turn_end, .p-log .log-item.k-state, .p-log .log-item.k-event { background: transparent; color: var(--text-3); padding-block: 2px; font-size: var(--fs-sm); }
|
|
229
|
+
.p-log .log-item.k-error, .p-log .log-item.s-failed { border-left: 3px solid var(--danger); }
|
|
230
|
+
.p-log .log-head { display: flex; align-items: baseline; gap: var(--s-2); min-width: 0; }
|
|
231
|
+
.p-log .log-icon { flex: none; }
|
|
232
|
+
.p-log .log-label { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
233
|
+
.p-log .log-cat { font-weight: var(--fw-semibold); }
|
|
234
|
+
.p-log .log-subject { font-size: var(--fs-sm); }
|
|
235
|
+
.p-log .log-time { flex: none; font-family: var(--font-mono); font-size: var(--fs-xs); color: var(--text-3); white-space: nowrap; }
|
|
236
|
+
.p-log .log-status { flex: none; font-size: var(--fs-xs); color: var(--text-3); white-space: nowrap; }
|
|
237
|
+
.p-log .log-status.done { color: var(--ok); }
|
|
238
|
+
.p-log .log-status.failed { color: var(--danger); }
|
|
239
|
+
.p-log .log-status.interrupted { color: var(--warn, var(--text-3)); }
|
|
240
|
+
.p-log .log-status .spinner { display: inline-block; width: 8px; height: 8px; margin-right: 4px; border-radius: 50%; background: var(--accent); animation: pulse 1.2s ease-in-out infinite; }
|
|
241
|
+
.p-log .log-repeat { flex: none; font-size: var(--fs-xs); padding: 0 6px; border-radius: 8px; background: var(--surface-2); color: var(--text-2); }
|
|
242
|
+
.p-log .log-text { white-space: pre-wrap; margin-top: var(--s-1); font-size: var(--fs-sm); }
|
|
243
|
+
.p-log .log-text.mono { font-family: var(--font-mono); font-size: var(--fs-xs); }
|
|
244
|
+
.p-log .log-text.folded { max-height: 10.5em; overflow: hidden; mask-image: linear-gradient(to bottom, #000 70%, transparent); -webkit-mask-image: linear-gradient(to bottom, #000 70%, transparent); }
|
|
245
|
+
.p-log .link-btn { border: 0; background: none; padding: 2px 0; color: var(--accent); cursor: pointer; font-size: var(--fs-xs); }
|
|
246
|
+
.p-log .log-detail { margin-top: var(--s-1); display: flex; flex-direction: column; gap: var(--s-2); }
|
|
247
|
+
.p-log .log-sec-h { font-size: var(--fs-xs); color: var(--text-3); text-transform: uppercase; letter-spacing: 0.04em; }
|
|
248
|
+
.p-log .log-older, .p-log .log-start { text-align: center; margin: var(--s-2) 0; }
|
|
249
|
+
.p-log .log-off { padding: var(--s-6) var(--s-4); }
|
|
220
250
|
.p-details .d-edit { display: flex; align-items: center; justify-content: space-between; gap: var(--s-2); flex-wrap: wrap; margin-top: var(--s-2); }
|
|
221
251
|
@media (max-width: 899px) {
|
|
222
252
|
.panel-head:has(.inst-switch) { flex-wrap: wrap; }
|
|
223
|
-
.inst-switch.seg { order: 3; flex-basis: 100%; display: grid; grid-template-columns: repeat(
|
|
253
|
+
.inst-switch.seg { order: 3; flex-basis: 100%; display: grid; grid-template-columns: repeat(4, 1fr); padding: 0 0 var(--s-1); }
|
|
224
254
|
.inst-switch .seg-item { justify-content: center; border-radius: var(--r-sm); }
|
|
225
255
|
}
|
|
226
256
|
.panel-body { flex: 1; min-height: 0; overflow-y: auto; }
|
|
@@ -336,7 +366,10 @@ html:has(.shell), html:has(.shell) body { overflow: hidden; }
|
|
|
336
366
|
.msg .time { font-family: var(--font-mono); white-space: nowrap; flex: none; }
|
|
337
367
|
.msg.user { align-self: flex-end; max-width: min(80%, 620px); }
|
|
338
368
|
.msg.user .meta { justify-content: flex-end; }
|
|
339
|
-
|
|
369
|
+
/* #1773: the bubble hugs its text. The message is as wide as its widest child — usually the meta row (sender, a time
|
|
370
|
+
such as 「昨天 下午03:58」, ticks) — and a block body would stretch to it; fit-content keeps a short message short, on
|
|
371
|
+
the right under its meta, and a long one still wraps at the message's max-width. */
|
|
372
|
+
.msg.user .body { background: var(--user-bubble); border-radius: var(--r-xl); padding: 10px var(--s-4); width: fit-content; max-width: 100%; margin-left: auto; }
|
|
340
373
|
.msg.agent { align-self: stretch; }
|
|
341
374
|
/* #1672: a status line (another instance's message, a daemon notice): muted plain text, no bubble; a long one folds.
|
|
342
375
|
Its class is `status-line`, never `status`: that is the header's inline-flex badge class, and on a message it turned
|
|
@@ -652,11 +685,16 @@ body.side-resizing, body.side-resizing * { cursor: col-resize !important; user-s
|
|
|
652
685
|
.v-filter-clear { width: 28px; height: 28px; }
|
|
653
686
|
.v-filter .note { padding: var(--s-1) var(--s-1) 0; font-variant-numeric: tabular-nums; }
|
|
654
687
|
/* alpha.2 (N1): status and CLI chips under the filter box — each a group of checkboxes under its header (#1700). */
|
|
655
|
-
|
|
656
|
-
|
|
688
|
+
/* #1776: the "N / M" count is on the headers' row, at its right end — not a row of its own. The headers (and an open
|
|
689
|
+
group's list, the reset) wrap in their own box beside it, so a filter's summary or a narrow sidebar moves a header
|
|
690
|
+
down, never the count; the count is as tall as a header, so the row does not grow. */
|
|
691
|
+
.nav-facets { display: flex; align-items: flex-start; gap: var(--s-2); padding: var(--s-2) var(--s-1) 0; }
|
|
692
|
+
.nav-facet-groups { display: flex; flex-wrap: wrap; align-items: flex-start; gap: var(--s-1) var(--s-2); flex: 1 1 auto; min-width: 0; }
|
|
693
|
+
.nav-facets .nav-shown { flex: none; margin: 0 0 0 auto; padding: 2px 0; white-space: nowrap; }
|
|
694
|
+
.nav-facet { font-size: var(--fs-xs); color: var(--text-2); min-width: 0; max-width: 100%; }
|
|
657
695
|
/* #1700: a facet group's header — the whole row toggles its list; folded with a filter set, a short summary. */
|
|
658
696
|
.nav-facet-head { display: inline-flex; align-items: center; gap: var(--s-1); max-width: 100%; padding: 2px var(--s-2); border: 0; border-radius: var(--r-sm);
|
|
659
|
-
background: transparent; color: inherit; font: inherit; cursor: pointer; text-align: left; }
|
|
697
|
+
background: transparent; color: inherit; font: inherit; cursor: pointer; text-align: left; white-space: nowrap; }
|
|
660
698
|
.nav-facet-head:hover { background: var(--hover); }
|
|
661
699
|
.nav-facet-head:focus-visible { outline: 2px solid var(--accent); }
|
|
662
700
|
.nav-facet-head .caret { color: var(--text-3); flex: none; }
|
package/dist/ui/shared/app.js
CHANGED
|
@@ -51,6 +51,8 @@ if (mode === "full") {
|
|
|
51
51
|
stream.on("hydration", (hydration) => appStore.set({ hydration }));
|
|
52
52
|
appStore.set({ retryHydration: () => stream.catchUp() });
|
|
53
53
|
stream.on("needs", (d) => appStore.set({ needs: Array.isArray(d && d.items) ? d.items : [] }));
|
|
54
|
+
// #1218: a panel's way to hear one stream event (the Log's steps_tick), unsubscribed by its lease.
|
|
55
|
+
appStore.set({ streamOn: (name, fn) => stream.on(name, fn) });
|
|
54
56
|
appStore.set({ connection: stream.connection() });
|
|
55
57
|
|
|
56
58
|
// The chat's store must hear every message from the first frame, whatever panel is open: its module boots once,
|
|
@@ -67,6 +69,7 @@ if (mode === "full") {
|
|
|
67
69
|
const loadSettings = retryable((a) => import(retryUrl("/ui/js/panel-settings.js", a)));
|
|
68
70
|
const loadNeeds = retryable((a) => import(retryUrl("/ui/js/panel-needs.js", a)));
|
|
69
71
|
const loadDetails = retryable((a) => import(retryUrl("/ui/js/panel-details.js", a)));
|
|
72
|
+
const loadLog = retryable((a) => import(retryUrl("/ui/js/panel-log.js", a)));
|
|
70
73
|
// The stream opens once the chat listens, so the frames sent on connect (status, open prompts, ticks) reach it too.
|
|
71
74
|
// If the chat cannot load (a session that just ended), the stream still opens for the sidebar.
|
|
72
75
|
const chatBoot = loadChat();
|
|
@@ -80,6 +83,8 @@ if (mode === "full") {
|
|
|
80
83
|
panels.set("needs", { load: () => loadNeeds().then((m) => m.NeedsPanel) });
|
|
81
84
|
// #1523 N2: one instance's Details (/ui/fleet/agent/<name>).
|
|
82
85
|
panels.set("details", { load: () => loadDetails().then((m) => m.DetailsPanel) });
|
|
86
|
+
// #1218: one instance's Log (/ui/log/<name>).
|
|
87
|
+
panels.set("log", { load: () => loadLog().then((m) => m.LogPanel) });
|
|
83
88
|
// #1386 §6.3: desktop notifications for new "Needs you" items, in any panel, while this signed-in page is open.
|
|
84
89
|
startNeedsNotifier({ open: (instance) => navigate(chatPath(instance)) });
|
|
85
90
|
// #1665: this device's first upload of its own choices (once), then another device's changes at each status frame.
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
- The optical-size axis is fixed at its text size (`opsz=14`).
|
|
9
9
|
- The font is subset to Latin, with the same range as `unicode-range` in `tokens.css`.
|
|
10
10
|
- It is saved as WOFF2. About 44 KB, against 352 KB for the full variable font.
|
|
11
|
-
- Chinese and other scripts fall back to the system fonts named in `--font-ui` (PingFang TC → Noto Sans TC → Microsoft JhengHei → the system stack).
|
|
11
|
+
- Chinese and other scripts fall back to the system fonts named in `--font-ui` (PingFang TC → PingFang SC → Noto Sans TC → Noto Sans SC → Noto Sans CJK TC → Noto Sans CJK SC → Microsoft JhengHei → Microsoft YaHei → the system stack).
|
|
12
12
|
|
|
13
13
|
Rebuilt with fontTools 4.59 (and `brotli` for WOFF2):
|
|
14
14
|
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
import { html, useLayoutEffect, useRef, useState } from "./app-html.js";
|
|
11
11
|
import { t } from "./app-i18n.js";
|
|
12
12
|
import { appStore, createStore, useStore } from "./app-store.js";
|
|
13
|
-
import { chatPath, detailsPath, viewPath } from "./app-route.js";
|
|
13
|
+
import { chatPath, detailsPath, logPath, viewPath } from "./app-route.js";
|
|
14
14
|
import { Icon } from "./ui-icons.js";
|
|
15
15
|
import { Skeleton } from "./ui-states.js";
|
|
16
16
|
import { viewStore } from "./view-roster-store.js";
|
|
@@ -353,8 +353,9 @@ export function InstanceNav({ route, items, loaded = true, exec = {}, awaiting =
|
|
|
353
353
|
// Where a row goes: the view the page is in (#1523 §3.1). View → View; Details, or a Fleet tab (Q4 = A), → Details;
|
|
354
354
|
// everywhere else → Chat. The anonymous reader only ever has View.
|
|
355
355
|
const rowPath = viewOnly || (route && route.panel === "view") ? viewPath
|
|
356
|
+
: route && route.panel === "log" ? logPath // #1218: Log → Log
|
|
356
357
|
: route && (route.panel === "details" || route.panel === "fleet") ? detailsPath : chatPath;
|
|
357
|
-
const active = route && (route.panel === "chat" || route.panel === "view" || route.panel === "details") ? route.instance : null;
|
|
358
|
+
const active = route && (route.panel === "chat" || route.panel === "view" || route.panel === "log" || route.panel === "details") ? route.instance : null;
|
|
358
359
|
useLayoutEffect(() => { keepActiveInView(list.current); }, [active, items.length, loaded]);
|
|
359
360
|
const own = (map, name) => Object.prototype.hasOwnProperty.call(map, name);
|
|
360
361
|
const byName = new Map(items.map((it) => [nameOf(it), it]));
|
|
@@ -478,11 +479,13 @@ export function InstanceNav({ route, items, loaded = true, exec = {}, awaiting =
|
|
|
478
479
|
onClick=${() => { setNavFilter({ q: "" }); filterRef.current && filterRef.current.focus(); }}><${Icon} name="close" size=${14} /></button>` : null}
|
|
479
480
|
</label>
|
|
480
481
|
<div class="nav-facets">
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
482
|
+
<div class="nav-facet-groups">
|
|
483
|
+
${viewOnly ? null : chips("status", STATUS_FACETS, tn("filterStatus"), statusText)}
|
|
484
|
+
${backends.length > 1 || f.cli.length ? chips("cli", backends, tn("filterCli"), backendLabel) : null}
|
|
485
|
+
${f.status.length || f.cli.length ? html`<button type="button" class="chip-btn nav-facet-reset" onClick=${() => setNavFilter({ status: [], cli: [] })}>${tn("filterFacetsReset")}</button>` : null}
|
|
486
|
+
</div>
|
|
487
|
+
<p class="note nav-shown" aria-live="polite">${shown.shown} / ${shown.total}${shown.active ? ` ${tn("filterShown")}` : ""}</p>
|
|
484
488
|
</div>
|
|
485
|
-
<p class="note" aria-live="polite">${shown.shown} / ${shown.total}${shown.active ? ` ${tn("filterShown")}` : ""}</p>
|
|
486
489
|
</div>
|
|
487
490
|
</div>`;
|
|
488
491
|
}
|
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
// #1523 N2 (§3.2): the instance view switch — Chat | View | Details of ONE instance. Each segment is a link to that
|
|
2
2
|
// view's URL for the same instance, so switching keeps the instance (and the sidebar, which is never remounted).
|
|
3
|
-
// Desktop: links in the header; a phone: the same
|
|
3
|
+
// Desktop: links in the header; a phone: the same four as top tabs under it (app.css). The anonymous View reader
|
|
4
4
|
// gets none (Chat and Details need a session): the caller does not render it there.
|
|
5
5
|
import { html } from "./app-html.js";
|
|
6
6
|
import { t } from "./app-i18n.js";
|
|
7
|
-
import { chatPath, detailsPath, viewPath } from "./app-route.js";
|
|
7
|
+
import { chatPath, detailsPath, logPath, viewPath } from "./app-route.js";
|
|
8
8
|
|
|
9
|
-
/** current: "chat" | "view" | "details". */
|
|
9
|
+
/** current: "chat" | "view" | "log" | "details". #1218: Log sits between View and Details. */
|
|
10
10
|
export function InstanceSwitch({ name, current }) {
|
|
11
11
|
const seg = (key, href, label) => html`<a key=${key} class=${`seg-item${current === key ? " active" : ""}`} href=${href}
|
|
12
12
|
aria-current=${current === key ? "page" : undefined}>${label}</a>`;
|
|
13
13
|
return html`<nav class="inst-switch seg" aria-label=${t("app.instanceViews", name)}>
|
|
14
|
-
${seg("chat", chatPath(name), t("app.chat"))}${seg("view", viewPath(name), t("app.view"))}${seg("details", detailsPath(name), t("app.details"))}
|
|
14
|
+
${seg("chat", chatPath(name), t("app.chat"))}${seg("view", viewPath(name), t("app.view"))}${seg("log", logPath(name), t("app.log"))}${seg("details", detailsPath(name), t("app.details"))}
|
|
15
15
|
</nav>`;
|
|
16
16
|
}
|
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
|
|
23
23
|
:root {
|
|
24
24
|
/* ── Type ── */
|
|
25
|
-
--font-ui: Inter, "PingFang TC", "Noto Sans TC", "Microsoft JhengHei", system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
|
|
25
|
+
--font-ui: Inter, "PingFang TC", "PingFang SC", "Noto Sans TC", "Noto Sans SC", "Noto Sans CJK TC", "Noto Sans CJK SC", "Microsoft JhengHei", "Microsoft YaHei", system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
|
|
26
26
|
--font-mono: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, "Liberation Mono", monospace;
|
|
27
27
|
--fs-xs: 12px; --lh-xs: 16px;
|
|
28
28
|
--fs-sm: 13px; --lh-sm: 18px;
|
package/dist/web-api.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { type HumanOrigin } from "./human-delivery-receipts.js";
|
|
1
2
|
import type { CachedImage } from "./web-images.js";
|
|
2
3
|
import type { SettingsExecution } from "./settings-transaction.js";
|
|
3
4
|
import type { PaneContextSource } from "./pane-context-cache.js";
|
|
@@ -106,6 +107,7 @@ export interface WebApiContext {
|
|
|
106
107
|
sendClassicWebEcho?(instance: string, text: string): Promise<number>;
|
|
107
108
|
/** false: definitely not delivered (the instance's IPC is gone, or it was restarted meanwhile). */
|
|
108
109
|
deliverToInstance(instanceName: string, payload: Record<string, unknown>): Promise<boolean | void>;
|
|
110
|
+
deliverHumanToInstance?(instanceName: string, payload: Record<string, unknown>, origin: HumanOrigin, originalBody: string): Promise<boolean | void>;
|
|
109
111
|
getUiStatus(): unknown;
|
|
110
112
|
/** Current owned pane reader shared with status; absent means context is unavailable. */
|
|
111
113
|
getPaneContextSource?(name: string): PaneContextSource | null;
|
|
@@ -146,6 +148,8 @@ export interface WebApiContext {
|
|
|
146
148
|
needsYouItems?(): unknown[];
|
|
147
149
|
/** #1389: the org chart's structure (fleet.yaml's teams, General, descriptions, thread links); absent: an empty chart. */
|
|
148
150
|
orgChart?(): unknown;
|
|
151
|
+
/** #1218: an instance's Log steps, read on demand (null: no such instance; `{ enabled: false }`: the Log is off). */
|
|
152
|
+
logSteps?(name: string, q: import("./log-service.js").LogQuery): Promise<unknown>;
|
|
149
153
|
/** #1468: the prompt-cache expiry analysis for a window ("24h" | "7d" | "30d"); absent: not offered. */
|
|
150
154
|
cacheReport?(window: string): Promise<unknown>;
|
|
151
155
|
/** #1269: an instance's chat command from the web chat (web-commands.ts); absent: not offered. */
|
|
@@ -251,6 +255,7 @@ export interface AppShellContext {
|
|
|
251
255
|
readonly fleetConfig?: {
|
|
252
256
|
web?: {
|
|
253
257
|
usage_panel?: boolean;
|
|
258
|
+
log_tab?: boolean;
|
|
254
259
|
};
|
|
255
260
|
} | null;
|
|
256
261
|
/** #1665: the host's theme and language go into the page itself, so a device paints them before any script reads. */
|
package/dist/web-api.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { webHumanOrigin } from "./human-delivery-receipts.js";
|
|
1
2
|
import { withSpawnHoldReply } from "./spawn-hold-reply.js";
|
|
2
3
|
import { binaryProbe } from "./binary-probe.js";
|
|
3
4
|
import { settingsRequestExecution, settingsWrite, isSettingsReplay } from "./settings-request-capability.js";
|
|
@@ -32,6 +33,7 @@ import { commitTeamCreate, commitTeamUpdate } from "./outbound-handlers.js";
|
|
|
32
33
|
import { parseMentionQuery } from "./web-mentions.js";
|
|
33
34
|
import { authorizeExplicitInstanceRemoval } from "./instance-removal.js";
|
|
34
35
|
import { measureSyncWork } from "./sync-work-attribution.js";
|
|
36
|
+
import { resolveTz } from "./tz-utils.js";
|
|
35
37
|
// ── Strict public-facing schemas ────────────────────────────────────────────
|
|
36
38
|
// web-api endpoints must reject unknown fields so the dashboard cannot inject
|
|
37
39
|
// internal-only flags that would reach handleCreate/scheduler/config writers.
|
|
@@ -345,6 +347,45 @@ export function handleWebRequest(req, res, url, ctx) {
|
|
|
345
347
|
json(res, 200, { messages: ctx.webChatHistory?.list(instance, limit) ?? [], boot: ctx.webChatHistory?.boot ?? null, lastId: ctx.webChatHistory?.lastId ?? 0 });
|
|
346
348
|
return true;
|
|
347
349
|
}
|
|
350
|
+
// ── #1218: the Log tab ─────────────────────────────────
|
|
351
|
+
// An instance's steps, read on demand from its own transcript (log-service.ts). Read-only, and offered on the public
|
|
352
|
+
// link on purpose (the user's decision): the same masking applies to every viewer. Passive (web-auth.ts).
|
|
353
|
+
if (method === "GET" && path === "/ui/steps") {
|
|
354
|
+
const p = url.searchParams;
|
|
355
|
+
const instance = p.get("instance") ?? "";
|
|
356
|
+
const num = (k) => {
|
|
357
|
+
const v = p.get(k);
|
|
358
|
+
if (v === null)
|
|
359
|
+
return undefined;
|
|
360
|
+
const n = Number(v);
|
|
361
|
+
return /^\d{1,15}$/.test(v) && Number.isSafeInteger(n) ? n : null;
|
|
362
|
+
};
|
|
363
|
+
const after = num("after"), before = num("before"), ev = num("ev"), limit = num("limit");
|
|
364
|
+
const key = p.get("key");
|
|
365
|
+
if (!instance || instance.length > 128 || after === null || before === null || ev === null || limit === null
|
|
366
|
+
|| (limit !== undefined && (limit < 1 || limit > 200)) || (key !== null && !/^\d{1,20}-\d{1,20}$/.test(key))) {
|
|
367
|
+
json(res, 400, { error: "instance (1-128 chars); after, before, ev: offsets; limit 1-200; key: <dev>-<ino>" });
|
|
368
|
+
return true;
|
|
369
|
+
}
|
|
370
|
+
if (!ctx.logSteps) {
|
|
371
|
+
json(res, 404, { error: "not available" });
|
|
372
|
+
return true;
|
|
373
|
+
}
|
|
374
|
+
ctx.logSteps(instance, {
|
|
375
|
+
...(after !== undefined ? { after } : {}), ...(before !== undefined ? { before } : {}), ...(ev !== undefined ? { ev } : {}),
|
|
376
|
+
...(limit !== undefined ? { limit } : {}), ...(key !== null ? { key } : {}),
|
|
377
|
+
skip: p.get("skip") === "1", watch: p.get("watch") === "1",
|
|
378
|
+
}).then(answer => {
|
|
379
|
+
if (answer === null)
|
|
380
|
+
json(res, 404, { error: `Instance not found: ${instance}` });
|
|
381
|
+
else
|
|
382
|
+
json(res, 200, answer);
|
|
383
|
+
}).catch(err => {
|
|
384
|
+
ctx.logger.error({ err: err.message, instance }, "Log steps read failed");
|
|
385
|
+
json(res, 500, { error: "could not read the steps" });
|
|
386
|
+
});
|
|
387
|
+
return true;
|
|
388
|
+
}
|
|
348
389
|
// ── Send message ───────────────────────────────────────
|
|
349
390
|
if (method === "POST" && path === "/ui/send") {
|
|
350
391
|
handleSendMessage(req, res, ctx);
|
|
@@ -1403,7 +1444,10 @@ function handleSendMessage(req, res, ctx) {
|
|
|
1403
1444
|
}) : undefined;
|
|
1404
1445
|
let delivered;
|
|
1405
1446
|
try {
|
|
1406
|
-
|
|
1447
|
+
const deliver = (name, payload) => ctx.deliverHumanToInstance
|
|
1448
|
+
? ctx.deliverHumanToInstance(name, payload, webHumanOrigin(), typed)
|
|
1449
|
+
: ctx.deliverToInstance(name, payload);
|
|
1450
|
+
delivered = await deliver(instance, {
|
|
1407
1451
|
type: "fleet_inbound",
|
|
1408
1452
|
content: delivery.text,
|
|
1409
1453
|
targetSession: instance,
|
|
@@ -1607,6 +1651,10 @@ function shellBodyTag(req, ctx, mode) {
|
|
|
1607
1651
|
+ `${p?.detail ? ` data-preview-detail="${attr(p.detail)}"` : ""}`
|
|
1608
1652
|
// #1523 N3: whether the fleet serves /api/ai-usage (web.usage_panel), for every page's ◔.
|
|
1609
1653
|
+ ` data-usage-panel="${ctx.fleetConfig?.web?.usage_panel === false ? "0" : "1"}"`
|
|
1654
|
+
// #1218: whether the Log tab is on (web.log_tab; off by default): the tab says so and offers to turn it on.
|
|
1655
|
+
+ ` data-log-tab="${ctx.fleetConfig?.web?.log_tab === true ? "1" : "0"}"`
|
|
1656
|
+
// #1770: the fleet's zone (chat-logs' and fleet.log's), in which the chat reads its message times and days.
|
|
1657
|
+
+ ` data-fleet-tz="${attr(resolveTz())}"`
|
|
1610
1658
|
// #1589: the public link shows attachments as downloads only (no inline text or media): its scope is unchanged.
|
|
1611
1659
|
+ `${gatewayRequestContext(req) ? ' data-public-link="1"' : ""}>`;
|
|
1612
1660
|
}
|