@songsid/agend 2.3.2-beta.4 → 2.3.2-beta.6
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/THIRD_PARTY_NOTICES.md +40 -0
- package/dist/antigravity-usage-scan.d.ts +58 -0
- package/dist/antigravity-usage-scan.js +257 -0
- package/dist/antigravity-usage-scan.js.map +1 -0
- package/dist/antigravity-usage-worker.d.ts +1 -0
- package/dist/antigravity-usage-worker.js +10 -0
- package/dist/antigravity-usage-worker.js.map +1 -0
- package/dist/backend/kiro-identity.d.ts +45 -0
- package/dist/backend/kiro-identity.js +74 -0
- package/dist/backend/kiro-identity.js.map +1 -1
- package/dist/backend/kiro.d.ts +15 -0
- package/dist/backend/kiro.js +42 -5
- package/dist/backend/kiro.js.map +1 -1
- package/dist/backend/types.d.ts +6 -0
- package/dist/backend/types.js.map +1 -1
- package/dist/backend-signin-guide.d.ts +1 -0
- package/dist/backend-signin-guide.js +9 -0
- package/dist/backend-signin-guide.js.map +1 -0
- package/dist/backend-signin-pending.d.ts +10 -0
- package/dist/backend-signin-pending.js +44 -0
- package/dist/backend-signin-pending.js.map +1 -0
- package/dist/browser-backend-signins.d.ts +56 -0
- package/dist/browser-backend-signins.js +189 -0
- package/dist/browser-backend-signins.js.map +1 -0
- package/dist/cache-ledger.d.ts +38 -2
- package/dist/cache-ledger.js +133 -8
- package/dist/cache-ledger.js.map +1 -1
- package/dist/cache-prices.d.ts +2 -1
- package/dist/cache-prices.js +5 -2
- package/dist/cache-prices.js.map +1 -1
- package/dist/cache-service.d.ts +68 -0
- package/dist/cache-service.js +355 -13
- package/dist/cache-service.js.map +1 -1
- package/dist/channel/adapters/discord.d.ts +11 -0
- package/dist/channel/adapters/discord.js +50 -8
- package/dist/channel/adapters/discord.js.map +1 -1
- package/dist/channel/mcp-tools.js +3 -1
- package/dist/channel/mcp-tools.js.map +1 -1
- package/dist/cli.js +21 -2
- package/dist/cli.js.map +1 -1
- package/dist/config-validator.js +25 -3
- package/dist/config-validator.js.map +1 -1
- package/dist/config.d.ts +19 -0
- package/dist/config.js +25 -0
- package/dist/config.js.map +1 -1
- package/dist/cpu-quota.d.ts +23 -0
- package/dist/cpu-quota.js +86 -0
- package/dist/cpu-quota.js.map +1 -0
- package/dist/daemon.js +6 -3
- package/dist/daemon.js.map +1 -1
- package/dist/fleet-manager.d.ts +56 -5
- package/dist/fleet-manager.js +387 -144
- package/dist/fleet-manager.js.map +1 -1
- package/dist/general-knowledge/skills/credential-profiles/SKILL.md +5 -2
- package/dist/general-knowledge/skills/kiro-engine-migration/SKILL.md +14 -1
- package/dist/general-knowledge/skills/web-dashboard/SKILL.canvas-worker-off.md +20 -4
- package/dist/general-knowledge/skills/web-dashboard/SKILL.md +20 -4
- package/dist/instance-lifecycle.d.ts +9 -0
- package/dist/instance-lifecycle.js +27 -1
- package/dist/instance-lifecycle.js.map +1 -1
- package/dist/kiro-db-reader.d.ts +5 -0
- package/dist/kiro-db-reader.js +18 -0
- package/dist/kiro-db-reader.js.map +1 -1
- package/dist/kiro-engine-status.d.ts +2 -0
- package/dist/kiro-engine-status.js +9 -0
- package/dist/kiro-engine-status.js.map +1 -1
- package/dist/kiro-migrate.d.ts +49 -0
- package/dist/kiro-migrate.js +105 -0
- package/dist/kiro-migrate.js.map +1 -0
- package/dist/kiro-transcript-lane.d.ts +2 -0
- package/dist/kiro-transcript-lane.js.map +1 -1
- package/dist/kiro-transcript-worker.js +1 -0
- package/dist/kiro-transcript-worker.js.map +1 -1
- package/dist/kiro-usage-run.d.ts +7 -0
- package/dist/kiro-usage-run.js +39 -0
- package/dist/kiro-usage-run.js.map +1 -0
- package/dist/kiro-usage-scan.d.ts +49 -0
- package/dist/kiro-usage-scan.js +223 -0
- package/dist/kiro-usage-scan.js.map +1 -0
- package/dist/kiro-usage-worker.d.ts +1 -0
- package/dist/kiro-usage-worker.js +10 -0
- package/dist/kiro-usage-worker.js.map +1 -0
- package/dist/kiro-usage.d.ts +58 -0
- package/dist/kiro-usage.js +310 -0
- package/dist/kiro-usage.js.map +1 -0
- package/dist/locale.js +78 -2
- package/dist/locale.js.map +1 -1
- package/dist/log-service.js +1 -1
- package/dist/log-service.js.map +1 -1
- package/dist/login-url.d.ts +4 -0
- package/dist/login-url.js +22 -0
- package/dist/login-url.js.map +1 -0
- package/dist/needs-you-hub.d.ts +2 -1
- package/dist/needs-you-hub.js +1 -1
- package/dist/needs-you-hub.js.map +1 -1
- package/dist/needs-you.d.ts +8 -2
- package/dist/needs-you.js +6 -0
- package/dist/needs-you.js.map +1 -1
- package/dist/notice-delivery.d.ts +7 -6
- package/dist/notice-delivery.js +16 -18
- package/dist/notice-delivery.js.map +1 -1
- package/dist/office-share-page.d.ts +13 -0
- package/dist/office-share-page.js +82 -0
- package/dist/office-share-page.js.map +1 -0
- package/dist/office-share-service.d.ts +89 -0
- package/dist/office-share-service.js +172 -0
- package/dist/office-share-service.js.map +1 -0
- package/dist/office-share.d.ts +228 -0
- package/dist/office-share.js +489 -0
- package/dist/office-share.js.map +1 -0
- package/dist/opencode-usage-scan.d.ts +28 -0
- package/dist/opencode-usage-scan.js +92 -0
- package/dist/opencode-usage-scan.js.map +1 -0
- package/dist/opencode-usage-worker.d.ts +1 -0
- package/dist/opencode-usage-worker.js +10 -0
- package/dist/opencode-usage-worker.js.map +1 -0
- package/dist/outbound-handlers.js +22 -2
- package/dist/outbound-handlers.js.map +1 -1
- package/dist/outbound-schemas.d.ts +4 -0
- package/dist/outbound-schemas.js +4 -0
- package/dist/outbound-schemas.js.map +1 -1
- package/dist/public-web-gateway.js +6 -0
- package/dist/public-web-gateway.js.map +1 -1
- package/dist/quickstart-api.js +13 -2
- package/dist/quickstart-api.js.map +1 -1
- package/dist/quickstart.js +2 -3
- package/dist/quickstart.js.map +1 -1
- package/dist/settings-api.js +1 -1
- package/dist/settings-api.js.map +1 -1
- package/dist/setup-form.d.ts +1 -1
- package/dist/setup-form.js +7 -1
- package/dist/setup-form.js.map +1 -1
- package/dist/setup-host.d.ts +9 -0
- package/dist/setup-host.js +125 -7
- package/dist/setup-host.js.map +1 -1
- package/dist/setup-signin-assets.d.ts +2 -0
- package/dist/setup-signin-assets.js +6 -0
- package/dist/setup-signin-assets.js.map +1 -0
- package/dist/setup-wizard.js +2 -2
- package/dist/setup-wizard.js.map +1 -1
- package/dist/structured-signin-transport.d.ts +8 -0
- package/dist/structured-signin-transport.js +79 -0
- package/dist/structured-signin-transport.js.map +1 -0
- package/dist/structured-signin.d.ts +131 -0
- package/dist/structured-signin.js +388 -0
- package/dist/structured-signin.js.map +1 -0
- package/dist/tool-permissions.js +2 -0
- package/dist/tool-permissions.js.map +1 -1
- package/dist/topic-commands.js +4 -1
- package/dist/topic-commands.js.map +1 -1
- package/dist/transcript-sources.d.ts +26 -2
- package/dist/transcript-sources.js +87 -40
- package/dist/transcript-sources.js.map +1 -1
- package/dist/types.d.ts +15 -5
- package/dist/ui/backend-signin.js +63 -10
- package/dist/ui/fleet-office-pack.js +7 -2
- package/dist/ui/fleet-office-skin.js +5 -4
- package/dist/ui/fleet-office-zones.js +17 -6
- package/dist/ui/fleet-office.js +20 -10
- package/dist/ui/fleet-usage.js +19 -15
- package/dist/ui/office-share-dialog.js +145 -0
- package/dist/ui/office-share-page.js +116 -0
- package/dist/ui/panel-log.js +2 -2
- package/dist/ui/panel-needs.js +4 -4
- package/dist/ui/panel-settings.js +12 -8
- package/dist/ui/settings-registry.js +2 -2
- package/dist/ui/settings-strings.js +6 -6
- package/dist/ui/shared/app-i18n.js +2 -2
- package/dist/ui/shared/app.css +29 -0
- package/dist/ui/structured-signin.js +115 -0
- package/dist/usage-report.d.ts +20 -0
- package/dist/usage-report.js +54 -17
- package/dist/usage-report.js.map +1 -1
- package/dist/usage-rollup.d.ts +9 -1
- package/dist/usage-rollup.js +20 -4
- package/dist/usage-rollup.js.map +1 -1
- package/dist/web-api.d.ts +15 -0
- package/dist/web-api.js +190 -0
- package/dist/web-api.js.map +1 -1
- package/dist/web-auth.js +1 -1
- package/dist/web-auth.js.map +1 -1
- package/dist/web-backend-login.d.ts +1 -7
- package/dist/web-backend-login.js +2 -24
- package/dist/web-backend-login.js.map +1 -1
- package/dist/web-office-skins.d.ts +2 -1
- package/dist/web-office-skins.js +2 -1
- package/dist/web-office-skins.js.map +1 -1
- package/dist/web-settings-patch.js +1 -1
- package/dist/web-settings-patch.js.map +1 -1
- package/dist/working-recovery.d.ts +2 -2
- package/dist/working-recovery.js +43 -8
- package/dist/working-recovery.js.map +1 -1
- package/dist/working-state-journal.d.ts +2 -0
- package/dist/working-state-journal.js +16 -1
- package/dist/working-state-journal.js.map +1 -1
- package/package.json +2 -1
- package/templates/windows-shortcuts.ps1 +7 -5
package/dist/ui/panel-log.js
CHANGED
|
@@ -62,7 +62,7 @@ register("log", {
|
|
|
62
62
|
ev_backend_unreachable: "CLI unreachable", ev_startup_backend_unreachable: "CLI unreachable at start", ev_backend_launch_warning: "Launch warning",
|
|
63
63
|
ev_clear_action: "Conversation cleared", ev_normal_exit_action: "Exited", ev_reply_drop_detected: "A reply went missing",
|
|
64
64
|
ev_reply_drop_recovered: "Missing reply recovered", ev_reply_drop_unrecovered: "Missing reply not recovered",
|
|
65
|
-
ev_supervision_ended: "Supervision ended", ev_tmux_server_crash: "tmux crashed", ev_malformed_tool_call: "Malformed tool call", ev_health_check_error: "Health check error", ev_working_state: "Working-state journal",
|
|
65
|
+
ev_supervision_ended: "Supervision ended", ev_tmux_server_crash: "tmux crashed", ev_malformed_tool_call: "Malformed tool call", ev_health_check_error: "Health check error", ev_context_lost: "Started fresh: context lost", ev_working_state: "Working-state journal",
|
|
66
66
|
},
|
|
67
67
|
"zh-TW": {
|
|
68
68
|
offTitle: "紀錄未開啟",
|
|
@@ -100,7 +100,7 @@ register("log", {
|
|
|
100
100
|
ev_backend_unreachable: "連不到 CLI", ev_startup_backend_unreachable: "啟動時連不到 CLI", ev_backend_launch_warning: "啟動警告",
|
|
101
101
|
ev_clear_action: "對話已清除", ev_normal_exit_action: "已結束", ev_reply_drop_detected: "有回覆遺失",
|
|
102
102
|
ev_reply_drop_recovered: "遺失的回覆已補回", ev_reply_drop_unrecovered: "遺失的回覆沒有補回",
|
|
103
|
-
ev_supervision_ended: "監督結束", ev_tmux_server_crash: "tmux 當掉", ev_malformed_tool_call: "格式錯誤的工具呼叫", ev_health_check_error: "健康檢查錯誤", ev_working_state: "工作狀態紀錄",
|
|
103
|
+
ev_supervision_ended: "監督結束", ev_tmux_server_crash: "tmux 當掉", ev_malformed_tool_call: "格式錯誤的工具呼叫", ev_health_check_error: "健康檢查錯誤", ev_context_lost: "改用全新對話:context 遺失", ev_working_state: "工作狀態紀錄",
|
|
104
104
|
},
|
|
105
105
|
});
|
|
106
106
|
const tl = (key, ...a) => t(`log.${key}`, ...a);
|
package/dist/ui/panel-needs.js
CHANGED
|
@@ -25,7 +25,7 @@ import { openSignIn, signInOffered } from "./backend-signin.js";
|
|
|
25
25
|
register("needs", {
|
|
26
26
|
en: {
|
|
27
27
|
title: "Needs you", empty: "Nothing needs you right now", emptyHint: "When an agent waits for you, it shows here — and in your chat app's General.", emptyHintWeb: "When an agent waits for you, it shows here.",
|
|
28
|
-
open: "Open chat", openCard: "Open the card", noAssignee: "Task board (nobody assigned)", signIn: "Sign in",
|
|
28
|
+
open: "Open chat", openCard: "Open the card", noAssignee: "Task board (nobody assigned)", fleetSignIns: "Fleet sign-ins", signIn: "Sign in",
|
|
29
29
|
gap: "not wrapped up: {0}", gapUnchecked: "{0} checklist item(s) unchecked", gapNoResult: "no result", gapJoin: ", ", acknowledge: "Acknowledge", acknowledging: "Acknowledging…", answering: "Answering…",
|
|
30
30
|
ageNow: "just now", ageMin: "{0} min", ageHour: "{0} h", ageDay: "{0} d",
|
|
31
31
|
notifyTitle: "Notify me on this device", notifyOn: "Notifications are on for this browser while a tab is open.",
|
|
@@ -38,7 +38,7 @@ register("needs", {
|
|
|
38
38
|
},
|
|
39
39
|
"zh-TW": {
|
|
40
40
|
title: "等你處理", empty: "目前沒有需要你處理的事", emptyHint: "agent 在等你時,會出現在這裡——也會出現在聊天 app 的 General。", emptyHintWeb: "agent 在等你時,會出現在這裡。",
|
|
41
|
-
open: "開啟聊天", openCard: "打開卡片", noAssignee: "任務看板(沒有負責人)", signIn: "登入",
|
|
41
|
+
open: "開啟聊天", openCard: "打開卡片", noAssignee: "任務看板(沒有負責人)", fleetSignIns: "Fleet 登入", signIn: "登入",
|
|
42
42
|
gap: "還沒補齊:{0}", gapUnchecked: "還有 {0} 項沒勾", gapNoResult: "沒有產出總結", gapJoin: "、", acknowledge: "確認", acknowledging: "確認中…", answering: "回覆中…",
|
|
43
43
|
ageNow: "剛剛", ageMin: "{0} 分鐘", ageHour: "{0} 小時", ageDay: "{0} 天",
|
|
44
44
|
notifyTitle: "在這台裝置通知我", notifyOn: "這個瀏覽器的通知已開啟(需要開著分頁)。",
|
|
@@ -143,7 +143,7 @@ export function NeedsPanel({ navKey }) {
|
|
|
143
143
|
<div data-trust="panel-content" class="panel-body"><div class="col">
|
|
144
144
|
${!items.length ? html`<${Empty} icon="check" title=${tn("empty")} hint=${tn(chatApp ? "emptyHint" : "emptyHintWeb")} />`
|
|
145
145
|
: groups.map((g) => html`<section key=${g.instance} class="n-group" aria-label=${g.instance}>
|
|
146
|
-
<h2 class="n-group-head">${g.instance ? html`<a href=${chatPath(g.instance)} title=${g.instance}>${nameFor(g.instance)}</a>` : tn("noAssignee")}</h2>
|
|
146
|
+
<h2 class="n-group-head">${g.instance ? html`<a href=${chatPath(g.instance)} title=${g.instance}>${nameFor(g.instance)}</a>` : tn(g.items.some(i => i.type === "backend") ? "fleetSignIns" : "noAssignee")}</h2>
|
|
147
147
|
${g.items.map((item) => html`<${NeedsItem} key=${item.id} item=${item} nowMs=${nowMs} owner=${owner} />`)}
|
|
148
148
|
</section>`)}
|
|
149
149
|
<${NotifyToggle} />
|
|
@@ -200,7 +200,7 @@ function NeedsItem({ item, nowMs, owner }) {
|
|
|
200
200
|
disabled=${promptBusy} onClick=${() => answer(a.id)}>${(p ? p.busy : fallback === "busy") ? tn("answering") : a.label}</button>`) : null}
|
|
201
201
|
${item.type === "delivery" ? html`<button type="button" class="btn btn-sm" disabled=${!!ack} onClick=${acknowledge}>
|
|
202
202
|
<${Icon} name="check" size=${14} />${ack === "busy" ? tn("acknowledging") : tn("acknowledge")}</button>` : null}
|
|
203
|
-
${item.reason === "auth_paused" && item.signIn && signInOffered() ? html`<button type="button" class="btn btn-sm btn-primary" onClick=${() => openSignIn(item.signIn)}>${tn("signIn")}</button>` : null}
|
|
203
|
+
${(item.reason === "auth_paused" || item.reason === "backend_signin") && item.signIn && signInOffered() ? html`<button type="button" class="btn btn-sm btn-primary" onClick=${() => openSignIn(item.signIn)}>${tn("signIn")}</button>` : null}
|
|
204
204
|
${item.type === "task" && item.card ? html`<a class="btn btn-sm" href=${`/ui/fleet?card=${encodeURIComponent(item.card.code)}`}>${tn("openCard")}</a>` : null}
|
|
205
205
|
${item.instance ? html`<a class="btn btn-sm btn-ghost" href=${chatPath(item.instance)}>${tn("open")}</a>` : null}
|
|
206
206
|
</div>
|
|
@@ -46,6 +46,7 @@ import { startTour } from "./chat-tour.js";
|
|
|
46
46
|
import "./chat-strings.js";
|
|
47
47
|
import { AppearanceSection } from "./settings-appearance.js";
|
|
48
48
|
import { GuestsCard } from "./settings-guests.js";
|
|
49
|
+
import { OfficeSharesCard } from "./office-share-dialog.js";
|
|
49
50
|
import { syncStage, useDraft } from "./settings-draft.js";
|
|
50
51
|
import { WebAuthorityCard } from "./settings-web-authority.js";
|
|
51
52
|
import { SignInList } from "./backend-signin.js";
|
|
@@ -952,10 +953,11 @@ function Classic({ ctx, search, openDialog }) {
|
|
|
952
953
|
})}</div>`;
|
|
953
954
|
}
|
|
954
955
|
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
const
|
|
958
|
-
const
|
|
956
|
+
// Sign-in notices and daily tips post in the topic or not at all: the DM choice was withdrawn (hmsg-d1809f2a,
|
|
957
|
+
// hmsg-3b8ac2af), and an old "dm" reads as on.
|
|
958
|
+
const noticeMode = (v) => v !== "false" && v !== false;
|
|
959
|
+
const noticeChoices = () => [{ value: "true", label: tn("noticeTopic") }, { value: "false", label: tn("noticeOff") }];
|
|
960
|
+
const noticeLabel = (v) => tn(v ? "noticeTopic" : "noticeOff");
|
|
959
961
|
|
|
960
962
|
/** General's form, from the saved defaults (`d`) and ClassicBot's (`c`). */
|
|
961
963
|
function generalForm(d, c, fleet = {}, schema = {}) {
|
|
@@ -971,7 +973,7 @@ function generalForm(d, c, fleet = {}, schema = {}) {
|
|
|
971
973
|
// #2347 §10 review: unset startup concurrency is automatic (deriveSpawnConcurrency: 2–10 by free memory and cores),
|
|
972
974
|
// not 10 — an empty field, and a save that leaves it unset (the defaults merge replaces `startup` whole).
|
|
973
975
|
concurrency: startup.concurrency == null ? "" : String(startup.concurrency), stagger: String(startup.stagger_delay_ms ?? 500),
|
|
974
|
-
tips: String(
|
|
976
|
+
tips: String(noticeMode(d.tips)),
|
|
975
977
|
replyTriggers: Boolean(d.reply_triggers ?? schema.reply_triggers_default),
|
|
976
978
|
classicReplyTriggers: Boolean(c.reply_triggers ?? d.reply_triggers ?? schema.reply_triggers_default), classicReplyTriggersInherit: !hasOwn(c, "reply_triggers"),
|
|
977
979
|
classicBackend: c.backend || d.backend || "claude-code",
|
|
@@ -1029,7 +1031,7 @@ function General({ ctx, part = "all" }) {
|
|
|
1029
1031
|
}, {
|
|
1030
1032
|
backend: d.backend || "claude-code", model: d.model || undefined, locale: d.locale || null, auto_pause_after: d.auto_pause_after ?? 0,
|
|
1031
1033
|
agent_mode: d.agent_mode || "mcp", tool_set: TOOL_SETS.includes(d.tool_set) ? d.tool_set : null, tool_progress: d.tool_progress ?? "off",
|
|
1032
|
-
tips:
|
|
1034
|
+
tips: d.tips ?? true, cross_instance_visibility: visibilityDefault(d), reply_triggers: Boolean(d.reply_triggers ?? schema.reply_triggers_default), reply_completion_guard: d.reply_completion_guard ?? true, log_level: d.log_level || "info",
|
|
1033
1035
|
dashboard: Object.fromEntries(DASHBOARD_FEATURES.filter((k) => k !== "office").map((k) => [k, dashboardDefault(d, k)])),
|
|
1034
1036
|
office_access: dashboardDefault(d, "office"),
|
|
1035
1037
|
hang_detector: { enabled: hang.enabled !== false, timeout_minutes: hang.timeout_minutes ?? HANG_TIMEOUT_DEFAULT_MINUTES },
|
|
@@ -1190,7 +1192,7 @@ function WebSection({ ctx, staged, part = "all" }) {
|
|
|
1190
1192
|
// serves their reads there).
|
|
1191
1193
|
const overPublicLink = globalThis.document?.body?.dataset?.publicLink === "1";
|
|
1192
1194
|
const original = useMemo(() => ({
|
|
1193
|
-
notify:
|
|
1195
|
+
notify: noticeMode(web.notify_login),
|
|
1194
1196
|
echo: web.echo_to_channel ?? true,
|
|
1195
1197
|
log: web.log_tab === true, // #1218: off by default
|
|
1196
1198
|
usagePub: web.usage_report_on_public_link === true, // #2122: off by default
|
|
@@ -1353,7 +1355,9 @@ function WebSection({ ctx, staged, part = "all" }) {
|
|
|
1353
1355
|
${named || f.autoOpen ? html`<label class=${fc("check", "autoOpen")}><input id="publicAutoOpen" type="checkbox" checked=${f.autoOpen} onChange=${(e) => set("autoOpen")(e.target.checked)} /><span>${tn("publicAutoOpen")}</span><${Impact} schema=${schema} field="web.public_link.auto_open" /></label>
|
|
1354
1356
|
<p class="note">${tn("publicAutoOpenHint")}</p><${Feedback} error=${errors.autoOpen} />` : null}
|
|
1355
1357
|
</section>`) : null}
|
|
1356
|
-
${has("access") && !overPublicLink ? html`<${PublicTunnelToken}
|
|
1358
|
+
${has("access") && !overPublicLink ? html`<${PublicTunnelToken} />` : null}
|
|
1359
|
+
${has("access") ? html`<${OfficeSharesCard} />` : null}
|
|
1360
|
+
${has("access") && !overPublicLink ? html`<${GuestsCard} ctx=${ctx} staged=${staged} /><${WebAuthorityCard} ctx=${ctx} staged=${staged} />` : null}
|
|
1357
1361
|
${has("chat") ? lock(html`<section class="card"><h3>${tn("webChat")}</h3>
|
|
1358
1362
|
<label class=${fc("check", "echo")}><input id="webEchoToChannel" type="checkbox" checked=${f.echo} onChange=${(e) => set("echo")(e.target.checked)} /><span>${tn("webEcho")}</span><${Impact} schema=${schema} field="web.echo_to_channel" /></label>
|
|
1359
1363
|
<p class="note">${tn("webEchoHint")}</p>
|
|
@@ -33,8 +33,8 @@ const ROWS = [
|
|
|
33
33
|
// Fleet · Agents
|
|
34
34
|
["agents.new", "agents", "#newAgentBtn", "settings.newAgent", null, ["new agent", "create", "add agent", "instance"], ["新增 agent", "建立", "新 agent"]],
|
|
35
35
|
["agents.provider-keys", "agents", ".s-provider-keys", "settings.providerKeys", "web.provider_secrets", ["api key", "provider", "openai", "anthropic", "token", "secret"], ["API 金鑰", "金鑰", "供應商", "token"]],
|
|
36
|
-
["defaults.tips", "agent-defaults", "#g-tips", "settings.dailyTips", "defaults.tips", ["daily tips", "
|
|
37
|
-
["web.notify-login", "access", "#webNotifyLogin", "settings.webNotifyLogin", "web.notify_login", ["sign in", "login", "
|
|
36
|
+
["defaults.tips", "agent-defaults", "#g-tips", "settings.dailyTips", "defaults.tips", ["daily tips", "notices"], ["每日提示"]],
|
|
37
|
+
["web.notify-login", "access", "#webNotifyLogin", "settings.webNotifyLogin", "web.notify_login", ["sign in", "login", "notices"], ["登入通知"]],
|
|
38
38
|
// Fleet · Agent defaults
|
|
39
39
|
["defaults.locale", "agent-defaults", "#g-locale", "settings.language", "defaults.locale", ["language", "bot language", "reply language", "locale"], ["語言", "回覆語言", "bot 語言"]],
|
|
40
40
|
["defaults.backend", "agent-defaults", "#g-be", "settings.defaultBackend", "defaults.backend", ["backend", "cli", "claude", "codex", "kiro"], ["後端", "CLI"]],
|
|
@@ -4,10 +4,10 @@ import { register } from "/assets/app-i18n.js";
|
|
|
4
4
|
|
|
5
5
|
register("settings", {
|
|
6
6
|
en: {
|
|
7
|
-
noticeTopic: "Topic",
|
|
7
|
+
noticeTopic: "Topic", noticeOff: "Off",
|
|
8
8
|
webNotifyLogin: "Web sign-in notices", dailyTips: "Daily tips",
|
|
9
|
-
webNotifyLoginHint: "
|
|
10
|
-
dailyTipsHint: "
|
|
9
|
+
webNotifyLoginHint: "Posted in the topic. Off suppresses operator notices only; guest and public-link security notices still post. Default: Topic.",
|
|
10
|
+
dailyTipsHint: "Posted in the General topic. Default: Topic. /tips still posts where invoked.",
|
|
11
11
|
title: "Settings",
|
|
12
12
|
myAgents: "My Agents",
|
|
13
13
|
bots: "Bots",
|
|
@@ -679,10 +679,10 @@ register("settings", {
|
|
|
679
679
|
se_externalEmojiHint: "使用其他伺服器的 emoji,bot 在要加反應的頻道需有「使用外部表情符號」權限。",
|
|
680
680
|
se_unavailable: "無法使用",
|
|
681
681
|
se_hint: "留空即使用預設值。預覽由 AgEnD 依 bot 實際 react 的方式解析。進度前綴不是反應,而是進度訊息開頭的 emoji。",
|
|
682
|
-
noticeTopic: "頻道",
|
|
682
|
+
noticeTopic: "頻道", noticeOff: "關閉",
|
|
683
683
|
webNotifyLogin: "網頁登入通知", dailyTips: "每日提示",
|
|
684
|
-
webNotifyLoginHint: "
|
|
685
|
-
dailyTipsHint: "
|
|
684
|
+
webNotifyLoginHint: "發在頻道。關閉只停用管理者登入通知;訪客與公開連結安全通知仍會發出。預設:頻道。",
|
|
685
|
+
dailyTipsHint: "發在 General 頻道。預設:頻道。/tips 仍送至呼叫的聊天室。",
|
|
686
686
|
title: "設定",
|
|
687
687
|
myAgents: "我的 Agent",
|
|
688
688
|
bots: "機器人",
|
|
@@ -105,7 +105,7 @@ register("app", {
|
|
|
105
105
|
confirmTitle: "Please confirm", confirmOk: "Continue", cancel: "Cancel",
|
|
106
106
|
sidePanelLeave: "Leave {0} without saving? Your changes there will be lost.", sidePanelDiscard: "Discard changes",
|
|
107
107
|
// The server's own wording (src/locale.ts needs.reason.*), so the web says what Discord says.
|
|
108
|
-
needs_reason_hang: "Not responding", needs_reason_exited: "Exited", needs_reason_assist: "Waiting at its terminal", needs_reason_permission: "Permission needed", needs_reason_dangerous_command: "Dangerous command waiting", needs_reason_login: "Sign-in needed", needs_reason_dialog: "Waiting at a dialog", needs_reason_terminal_input: "May be waiting for input", needs_reason_auth_paused: "Paused: sign-in needed", needs_reason_crashed: "Crashed", needs_reason_delivery_uncertain: "Could not confirm delivery", needs_reason_delivery_failed: "Delivery failed", needs_reason_task_review: "Waiting for your confirmation", needs_reason_task_blocked: "Blocked: asks you",
|
|
108
|
+
needs_reason_hang: "Not responding", needs_reason_exited: "Exited", needs_reason_assist: "Waiting at its terminal", needs_reason_permission: "Permission needed", needs_reason_dangerous_command: "Dangerous command waiting", needs_reason_login: "Sign-in needed", needs_reason_dialog: "Waiting at a dialog", needs_reason_terminal_input: "May be waiting for input", needs_reason_auth_paused: "Paused: sign-in needed", needs_reason_backend_signin: "Backend sign-in needed", needs_reason_crashed: "Crashed", needs_reason_delivery_uncertain: "Could not confirm delivery", needs_reason_delivery_failed: "Delivery failed", needs_reason_task_review: "Waiting for your confirmation", needs_reason_task_blocked: "Blocked: asks you",
|
|
109
109
|
},
|
|
110
110
|
"zh-TW": {
|
|
111
111
|
brand: "AgEnD", fleet: "Fleet", fleetOverview: "Fleet 概覽", view: "檢視", settings: "設定", instances: "Instances", newInstance: "新增 instance", newAgent: "新增 Agent",
|
|
@@ -145,6 +145,6 @@ register("app", {
|
|
|
145
145
|
search: "搜尋", searchTitle: "搜尋聊天、agent 之間的訊息與任務({0})",
|
|
146
146
|
confirmTitle: "請確認", confirmOk: "繼續", cancel: "取消",
|
|
147
147
|
sidePanelLeave: "不儲存就離開「{0}」?你在那裡的修改會遺失。", sidePanelDiscard: "捨棄修改",
|
|
148
|
-
needs_reason_hang: "沒有回應", needs_reason_exited: "已結束", needs_reason_assist: "在終端機上等待", needs_reason_permission: "需要權限", needs_reason_dangerous_command: "危險指令待確認", needs_reason_login: "需要登入", needs_reason_dialog: "停在對話框", needs_reason_terminal_input: "可能在等輸入", needs_reason_auth_paused: "已暫停:需要登入", needs_reason_crashed: "已崩潰", needs_reason_delivery_uncertain: "無法確認是否送達", needs_reason_delivery_failed: "傳送失敗", needs_reason_task_review: "等你確認", needs_reason_task_blocked: "卡住,在問你",
|
|
148
|
+
needs_reason_hang: "沒有回應", needs_reason_exited: "已結束", needs_reason_assist: "在終端機上等待", needs_reason_permission: "需要權限", needs_reason_dangerous_command: "危險指令待確認", needs_reason_login: "需要登入", needs_reason_dialog: "停在對話框", needs_reason_terminal_input: "可能在等輸入", needs_reason_auth_paused: "已暫停:需要登入", needs_reason_backend_signin: "後端需要登入", needs_reason_crashed: "已崩潰", needs_reason_delivery_uncertain: "無法確認是否送達", needs_reason_delivery_failed: "傳送失敗", needs_reason_task_review: "等你確認", needs_reason_task_blocked: "卡住,在問你",
|
|
149
149
|
},
|
|
150
150
|
});
|
package/dist/ui/shared/app.css
CHANGED
|
@@ -1936,6 +1936,7 @@ html.oo-immersive .p-fleet > .panel-body { z-index: 50; }
|
|
|
1936
1936
|
.us-bar { display: block; width: 100%; height: 6px; margin: 4px 0; }
|
|
1937
1937
|
.us-bar-bg { fill: var(--surface-2); } .us-bar-fg { fill: var(--us-0); }
|
|
1938
1938
|
.us-meta { display: block; font-size: var(--fs-xs); color: var(--text-3); overflow-wrap: anywhere; }
|
|
1939
|
+
.us-est { display: block; font-size: var(--fs-xs); font-weight: var(--fw-regular); color: var(--text-3); }
|
|
1939
1940
|
.us-flags { display: flex; flex-wrap: wrap; gap: 4px; margin-top: 4px; }
|
|
1940
1941
|
.us-trend, .us-heat { display: flex; flex-direction: column; gap: var(--s-2); min-width: 0; }
|
|
1941
1942
|
.us-trend h3, .us-heat h3, .us-rest h3 { font-size: var(--fs-sm); font-weight: var(--fw-semibold); }
|
|
@@ -2503,3 +2504,31 @@ html.oo-immersive .p-fleet > .panel-body { z-index: 50; }
|
|
|
2503
2504
|
.msg.user .msg-reactions { justify-content: flex-end; }
|
|
2504
2505
|
.msg-reactions .reaction { display: inline-flex; align-items: center; justify-content: center; min-width: 28px; height: 24px; padding: 0 6px;
|
|
2505
2506
|
border: 1px solid var(--border); border-radius: var(--r-full); background: var(--surface-2); font-family: var(--font-emoji); font-size: 15px; line-height: 1; }
|
|
2507
|
+
|
|
2508
|
+
/* #2636: an Office share link's page (office-share-page.js) — the Office alone, filling the window under a one-line head. */
|
|
2509
|
+
.share-page { display: flex; flex-direction: column; min-height: 100dvh; background: var(--bg); color: var(--text); }
|
|
2510
|
+
.share-head { display: flex; flex-wrap: wrap; align-items: baseline; gap: var(--s-1) var(--s-3); padding: var(--s-3) var(--s-4); border-bottom: 1px solid var(--border); }
|
|
2511
|
+
.share-head .share-note { margin-left: auto; }
|
|
2512
|
+
.share-stage { flex: 1 1 auto; display: flex; flex-direction: column; min-height: 0; padding: var(--s-2); }
|
|
2513
|
+
.share-loading, .share-page > .note { padding: var(--s-4); }
|
|
2514
|
+
.share-ended { align-items: center; justify-content: center; padding: var(--s-6); text-align: center; }
|
|
2515
|
+
@media (max-width: 640px) { .share-head .share-note { margin-left: 0; flex-basis: 100%; } }
|
|
2516
|
+
|
|
2517
|
+
/* #2636: Office share links — the Share… dialog and the open links (office-share-dialog.js; Settings → Access & security). */
|
|
2518
|
+
.os { display: flex; flex-direction: column; gap: var(--s-3); }
|
|
2519
|
+
.os > p { margin: 0; }
|
|
2520
|
+
.os-form { display: grid; grid-template-columns: 1fr 1fr; gap: var(--s-3); align-items: end; }
|
|
2521
|
+
.os-form .check, .os-form .note { grid-column: 1 / -1; margin: 0; }
|
|
2522
|
+
.os-made { display: flex; flex-wrap: wrap; align-items: center; gap: var(--s-2); }
|
|
2523
|
+
.os-url { flex: 1 1 260px; min-width: 0; min-height: 40px; padding: var(--s-2) var(--s-3); border: 1px solid var(--border-strong); border-radius: var(--r-sm);
|
|
2524
|
+
background: var(--bg); color: var(--text); font-size: var(--fs-sm); outline: none; }
|
|
2525
|
+
.os-url:focus { border-color: var(--focus); box-shadow: 0 0 0 3px var(--accent-dim); }
|
|
2526
|
+
.os-made .note { flex-basis: 100%; margin: 0; }
|
|
2527
|
+
.os-head { margin: var(--s-2) 0 0; font-size: var(--fs-sm); font-weight: var(--fw-medium); color: var(--text-2); }
|
|
2528
|
+
.os-rows { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: var(--s-2); }
|
|
2529
|
+
.os-row { display: flex; align-items: center; gap: var(--s-3); padding: var(--s-2) var(--s-3); border: 1px solid var(--border); border-radius: var(--r-sm); }
|
|
2530
|
+
.os-what { flex: 1; min-width: 0; overflow-wrap: anywhere; font-size: var(--fs-sm); }
|
|
2531
|
+
.os-all { margin-top: var(--s-2); }
|
|
2532
|
+
.os-none { margin: 0; }
|
|
2533
|
+
.os-form .os-err { grid-column: 1 / -1; margin: 0; padding: var(--s-2) var(--s-3); border: 1px solid var(--danger); border-radius: var(--r-sm); }
|
|
2534
|
+
@media (max-width: 480px) { .os-form { grid-template-columns: 1fr; } }
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
/* Shared browser controls. No terminal relay, provider output or secrets in diagnostics. */
|
|
2
|
+
const NAMES = { "kiro-cli": "Kiro", "claude-code": "Claude Code", codex: "Codex", antigravity: "Antigravity (console fallback)" };
|
|
3
|
+
const CHOICES = { builder_id: "Builder ID", google: "Google", github: "GitHub", organization: "Your organization", subscription: "Claude subscription", console: "Anthropic Console" };
|
|
4
|
+
const ERRORS = {
|
|
5
|
+
unsupported_prompt: "This CLI screen is not supported. Cancel and use the console fallback.",
|
|
6
|
+
cleanup_failed: "The login process could not be confirmed closed. Close it on the host and restart setup before retrying.",
|
|
7
|
+
signin_cleanup_failed: "The login process could not be confirmed closed. Close it on the host and restart setup before retrying.",
|
|
8
|
+
signin_action_stale: "That prompt changed or was already answered. Refresh its status; do not submit it again.",
|
|
9
|
+
signin_console_only: "This backend uses the explicit console fallback.",
|
|
10
|
+
backend_signin_local_only: "Backend sign-in is available only on this computer. Use the local setup page, or sign in later.",
|
|
11
|
+
signin_session_expired: "This setup session has ended. Reopen setup on the host.",
|
|
12
|
+
expired: "This login attempt expired. Start a new attempt.",
|
|
13
|
+
auth_unconfirmed: "The CLI exited, but credentials could not be verified. Retry or sign in later.",
|
|
14
|
+
provider_rejected: "The provider rejected this sign-in. Retry or sign in later.",
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
export function mountStructuredSignIns(root, { endpoint, fetcher = fetch, changed = () => {} } = {}) {
|
|
18
|
+
const doc = root.ownerDocument;
|
|
19
|
+
let view = null, busy = false, disposed = false, timer = null, generation = 0, message = "", rendered = "";
|
|
20
|
+
const el = (tag, text, parent = root) => { const node = doc.createElement(tag); if (text) node.textContent = text; parent.append(node); return node; };
|
|
21
|
+
const errorText = code => ERRORS[code] || "Could not complete sign-in. Check status, retry, or sign in later.";
|
|
22
|
+
const button = (parent, label, action) => { const b = el("button", label, parent); b.type = "button"; b.disabled = busy; b.onclick = action; return b; };
|
|
23
|
+
const request = async data => {
|
|
24
|
+
const response = await fetcher(endpoint, { method: data ? "POST" : "GET", cache: "no-store", headers: { "Content-Type": "application/json" }, ...(data ? { body: JSON.stringify(data) } : {}) });
|
|
25
|
+
const body = await response.json().catch(() => null);
|
|
26
|
+
if (!response.ok) throw new Error(body?.error || "signin_failed");
|
|
27
|
+
return body;
|
|
28
|
+
};
|
|
29
|
+
const accept = next => { view = next; root.dataset.ready = String(view.ready); changed(view); };
|
|
30
|
+
async function perform(data) {
|
|
31
|
+
if (busy || disposed) return;
|
|
32
|
+
busy = true; generation++; message = ""; render();
|
|
33
|
+
try { const next = await request(data); if (!disposed) accept(next); }
|
|
34
|
+
catch (error) { if (!disposed) message = errorText(error?.message); }
|
|
35
|
+
finally { busy = false; if (!disposed) render(); }
|
|
36
|
+
}
|
|
37
|
+
function render() {
|
|
38
|
+
const signature = JSON.stringify([view, busy, message]);
|
|
39
|
+
if (disposed || rendered === signature) return;
|
|
40
|
+
rendered = signature; root.replaceChildren();
|
|
41
|
+
el("h2", "Sign in your AI backends");
|
|
42
|
+
el("p", "Use the controls here; no terminal typing is needed for Kiro, Claude Code or Codex. You can sign in later from the dashboard.");
|
|
43
|
+
if (message) { const alert = el("p", message); alert.setAttribute("role", "alert"); }
|
|
44
|
+
if (!view) { button(root, "Check sign-ins", () => perform({ operation: "refresh" })); return; }
|
|
45
|
+
for (const backend of view.selected) {
|
|
46
|
+
const card = el("section"); card.dataset.backend = backend;
|
|
47
|
+
el("h3", NAMES[backend] || backend, card);
|
|
48
|
+
const state = view.states[backend], pending = view.pending.includes(backend), skipped = view.skipped.includes(backend);
|
|
49
|
+
el("p", skipped ? "Sign in later · remains in Needs you" : state === "valid" ? "Signed in ✓" : state === "not_installed" ? "Not installed on this host" : state === "console_only" ? "Use the explicit console fallback, or sign in later." : "Sign-in needed", card);
|
|
50
|
+
const login = view.login?.backend === backend ? view.login : null;
|
|
51
|
+
const active = login && login.phase !== "done" && !(login.phase === "failed" && login.reason);
|
|
52
|
+
if (active) {
|
|
53
|
+
el("p", login.phase === "waiting" || login.phase === "starting" ? "Waiting for the CLI…" : "Login: " + login.phase.replaceAll("_", " "), card);
|
|
54
|
+
if (login.phase === "method" && login.canAct) for (const choice of login.choices || []) {
|
|
55
|
+
if (CHOICES[choice]) button(card, CHOICES[choice], () => perform({ operation: "input", id: login.id, revision: login.revision, action: { kind: "method", choice } }));
|
|
56
|
+
}
|
|
57
|
+
if (login.url) {
|
|
58
|
+
// The host already allowlists these; still never turn a non-HTTPS URL into a link.
|
|
59
|
+
let url; try { url = new URL(login.url); } catch {}
|
|
60
|
+
if (url?.protocol === "https:" && !url.username && !url.password) {
|
|
61
|
+
const link = el("a", "Open provider sign-in", card); link.href = url.href; link.target = "_blank"; link.rel = "noopener noreferrer";
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
if (login.code) { el("p", "Device code:", card); el("code", login.code, card); }
|
|
65
|
+
if (login.canAct && ["code", "start_url", "region"].includes(login.phase)) {
|
|
66
|
+
const label = el("label", { code: "Paste the returned code", start_url: "Organization Start URL", region: "AWS region" }[login.phase], card);
|
|
67
|
+
const input = el("input", null, label); input.type = login.phase === "code" ? "password" : "text"; input.autocomplete = "off"; input.spellcheck = false;
|
|
68
|
+
input.setAttribute("aria-label", label.textContent); input.disabled = busy;
|
|
69
|
+
button(card, "Submit " + login.phase.replaceAll("_", " "), () => { const value = input.value; input.value = ""; void perform({ operation: "input", id: login.id, revision: login.revision, action: { kind: login.phase, value } }); });
|
|
70
|
+
}
|
|
71
|
+
if (["method", "code", "start_url", "region"].includes(login.phase) && !login.canAct) el("p", "Answer sent. Waiting for the next screen; it will not be sent twice.", card);
|
|
72
|
+
button(card, "Cancel sign-in", () => perform({ operation: "cancel" }));
|
|
73
|
+
} else {
|
|
74
|
+
if (login?.reason && login.reason !== "cancelled") el("p", errorText(login.reason), card);
|
|
75
|
+
const usable = !["antigravity"].includes(backend) && state !== "not_installed";
|
|
76
|
+
if (usable) button(card, state === "valid" ? "Sign in again" : "Sign in", () => {
|
|
77
|
+
const relogin = state === "valid";
|
|
78
|
+
if (relogin && !globalThis.confirm("Sign in again? Kiro will sign out its existing account first.")) return;
|
|
79
|
+
void perform({ operation: "start", backend, relogin });
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
if (pending && !skipped) button(card, "Sign in later", () => perform({ operation: "skip", backend }));
|
|
83
|
+
}
|
|
84
|
+
button(root, "Check sign-ins", () => perform({ operation: "refresh" }));
|
|
85
|
+
if (view.ready) el("p", "You can continue setup.");
|
|
86
|
+
}
|
|
87
|
+
async function poll() {
|
|
88
|
+
if (disposed) return;
|
|
89
|
+
const before = generation;
|
|
90
|
+
if (!busy) try { const next = await request(); if (!disposed && before === generation) { accept(next); render(); } }
|
|
91
|
+
catch (error) { if (!disposed && before === generation) { message = errorText(error?.message); render(); } }
|
|
92
|
+
if (!disposed) timer = setTimeout(poll, 1000);
|
|
93
|
+
}
|
|
94
|
+
render(); void poll();
|
|
95
|
+
return {
|
|
96
|
+
addBackends(backends) {
|
|
97
|
+
if (view && Array.isArray(backends) && backends.every(b => NAMES[b]) && backends.some(b => !view.selected.includes(b)))
|
|
98
|
+
return perform({ operation: "select", backends: [...new Set([...view.selected, ...backends])] });
|
|
99
|
+
},
|
|
100
|
+
addBackend(backend) { if (view && NAMES[backend] && !view.selected.includes(backend)) return perform({ operation: "select", backends: [...view.selected, backend] }); },
|
|
101
|
+
dispose() { disposed = true; generation++; clearTimeout(timer); root.replaceChildren(); },
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
const setupRoot = typeof document === "undefined" ? null : document.getElementById("setupSignins");
|
|
106
|
+
if (setupRoot) {
|
|
107
|
+
const cards = mountStructuredSignIns(setupRoot, { endpoint: "setup/signins" });
|
|
108
|
+
const backend = document.getElementById("backend");
|
|
109
|
+
const select = () => cards.addBackend(backend?.value);
|
|
110
|
+
backend?.addEventListener("change", select);
|
|
111
|
+
const observer = new MutationObserver(select); if (backend) observer.observe(backend, { childList: true });
|
|
112
|
+
// Initial GET may finish after the environment list, so also observe our readiness result.
|
|
113
|
+
const ready = new MutationObserver(select); ready.observe(setupRoot, { attributes: true, attributeFilter: ["data-ready"] });
|
|
114
|
+
addEventListener("pagehide", () => { observer.disconnect(); ready.disconnect(); cards.dispose(); }, { once: true });
|
|
115
|
+
}
|
package/dist/usage-report.d.ts
CHANGED
|
@@ -7,6 +7,9 @@
|
|
|
7
7
|
* Honest by construction: an amount is "API-equivalent at list price (checked <date>) — not money spent"; an unpriced
|
|
8
8
|
* model's tokens are shown and its amount is not; Codex's cache writes are "not reported" (every CLI seen records 0),
|
|
9
9
|
* never priced as uncached; an instance whose backend AgEnD cannot measure is listed with its reason, never as $0.
|
|
10
|
+
* Kiro records no token counts: its rows are ESTIMATED from text size (kiro-usage.ts, design §10.2) and say so — on the
|
|
11
|
+
* row (`estimated`), on any total that includes one (`includes_estimates`, with the estimated part of the amount), in
|
|
12
|
+
* the API, on the page and in the CSV. An estimate never mixes silently into a measured number.
|
|
10
13
|
*/
|
|
11
14
|
import type { Ledger } from "./cache-ledger.js";
|
|
12
15
|
import { type UsageDays } from "./usage-rollup.js";
|
|
@@ -27,6 +30,12 @@ export declare const usageBasis: (checked?: string) => string;
|
|
|
27
30
|
* latest whole days and says from which date (`dailyFrom`); the rows and totals always cover the whole range.
|
|
28
31
|
*/
|
|
29
32
|
export declare const USAGE_DAILY_MAX = 20000;
|
|
33
|
+
/** The backends whose usage is estimated, not measured (#2122 §10.2: Kiro records no token counts). */
|
|
34
|
+
export declare const ESTIMATED_BACKENDS: ReadonlySet<string>;
|
|
35
|
+
/** The backends whose transcripts report reasoning tokens (#2122 §10.3); elsewhere a 0 means not reported. */
|
|
36
|
+
export declare const REPORTS_REASONING: ReadonlySet<string>;
|
|
37
|
+
/** The backends whose transcripts carry a cache-write field that stays 0: "not reported" until a real write is seen. */
|
|
38
|
+
export declare const WRITES_UNREPORTED: ReadonlySet<string>;
|
|
30
39
|
export interface UsageInstanceInput {
|
|
31
40
|
name: string;
|
|
32
41
|
/** Its backend now: what it is collected with from here on (each slice of its history carries its own). */
|
|
@@ -63,7 +72,10 @@ export interface UsageInput {
|
|
|
63
72
|
export interface UsageTotals {
|
|
64
73
|
/** Uncached input. */
|
|
65
74
|
input: number;
|
|
75
|
+
/** Output without its reasoning part — the units of tokscale's daily rows (design §10.3). */
|
|
66
76
|
output: number;
|
|
77
|
+
/** The part of the output a CLI reports as reasoning (Codex); 0 where none is reported (Claude). */
|
|
78
|
+
reasoning: number;
|
|
67
79
|
cacheRead: number;
|
|
68
80
|
cacheWrite5m: number;
|
|
69
81
|
cacheWrite1h: number;
|
|
@@ -73,6 +85,12 @@ export interface UsageTotals {
|
|
|
73
85
|
}
|
|
74
86
|
export interface UsageRow extends UsageTotals {
|
|
75
87
|
key: string;
|
|
88
|
+
/** Whether any of it comes from a CLI that reports reasoning (REPORTS_REASONING): false → its 0 is "not reported". */
|
|
89
|
+
reasoningReported: boolean;
|
|
90
|
+
/** Some of it is estimated (ESTIMATED_BACKENDS); flagged "estimated" (a row) or "includes_estimates" (the totals). */
|
|
91
|
+
estimated: boolean;
|
|
92
|
+
/** The estimated part of `amount` (null: nothing estimated is priced). */
|
|
93
|
+
amountEstimated: number | null;
|
|
76
94
|
total: number;
|
|
77
95
|
/** Delivered messages; null where a message has no such key (by model, by profile). */
|
|
78
96
|
messages: number | null;
|
|
@@ -92,6 +110,8 @@ export interface UsageRow extends UsageTotals {
|
|
|
92
110
|
}
|
|
93
111
|
export interface UsageDaily extends UsageTotals {
|
|
94
112
|
date: string;
|
|
113
|
+
reasoningReported: boolean;
|
|
114
|
+
estimated: boolean;
|
|
95
115
|
zone: string;
|
|
96
116
|
instance: string;
|
|
97
117
|
backend: string;
|
package/dist/usage-report.js
CHANGED
|
@@ -10,6 +10,12 @@ export const usageBasis = (checked = PRICES_CHECKED) => `API-equivalent at list
|
|
|
10
10
|
*/
|
|
11
11
|
export const USAGE_DAILY_MAX = 20_000;
|
|
12
12
|
const M = 1_000_000;
|
|
13
|
+
/** The backends whose usage is estimated, not measured (#2122 §10.2: Kiro records no token counts). */
|
|
14
|
+
export const ESTIMATED_BACKENDS = new Set(["kiro-cli"]);
|
|
15
|
+
/** The backends whose transcripts report reasoning tokens (#2122 §10.3); elsewhere a 0 means not reported. */
|
|
16
|
+
export const REPORTS_REASONING = new Set(["codex", "grok", "muse", "opencode", "antigravity"]);
|
|
17
|
+
/** The backends whose transcripts carry a cache-write field that stays 0: "not reported" until a real write is seen. */
|
|
18
|
+
export const WRITES_UNREPORTED = new Set(["codex", "muse"]);
|
|
13
19
|
const DATE = /^\d{4}-\d{2}-\d{2}$/;
|
|
14
20
|
/** A range as asked (query string or tool argument); anything else is the last 7 days. */
|
|
15
21
|
export function usageRange(kind, from, to) {
|
|
@@ -31,19 +37,23 @@ export function rangeDates(range, now, zone) {
|
|
|
31
37
|
case "custom": return { from: range.from, to: range.to };
|
|
32
38
|
}
|
|
33
39
|
}
|
|
34
|
-
const zero = () => ({ input: 0, output: 0, cacheRead: 0, cacheWrite5m: 0, cacheWrite1h: 0, cacheWrite: 0, requests: 0 });
|
|
35
|
-
|
|
40
|
+
const zero = () => ({ input: 0, output: 0, reasoning: 0, cacheRead: 0, cacheWrite5m: 0, cacheWrite1h: 0, cacheWrite: 0, requests: 0 });
|
|
41
|
+
/** One slice into a row: `reasoning` is the part of the slice's output reported as reasoning (shown beside the rest). */
|
|
42
|
+
const addTotals = (into, [n, uncached, read, w5, w1h, w, out], reasoning) => {
|
|
43
|
+
const r = Math.min(Math.max(0, reasoning), out);
|
|
36
44
|
into.requests += n;
|
|
37
45
|
into.input += uncached;
|
|
38
46
|
into.cacheRead += read;
|
|
39
47
|
into.cacheWrite5m += w5;
|
|
40
48
|
into.cacheWrite1h += w1h;
|
|
41
49
|
into.cacheWrite += w;
|
|
42
|
-
into.output += out;
|
|
50
|
+
into.output += out - r;
|
|
51
|
+
into.reasoning += r;
|
|
43
52
|
};
|
|
44
53
|
const splitKey = (mk) => { const bar = mk.lastIndexOf("|"); return { model: mk.slice(0, bar), long: mk.slice(bar + 1) === "1" }; };
|
|
45
54
|
/**
|
|
46
|
-
* The list-price amount of one slice, or null when its model is not in the table.
|
|
55
|
+
* The list-price amount of one slice, or null when its model is not in the table. Reasoning is a part of the slice's
|
|
56
|
+
* output, so the whole output is priced at the output rate once. Codex writes not reported are 0 in the
|
|
47
57
|
* ledger (it marks writesSeen on the first one above 0), so they add nothing and are never priced as uncached input.
|
|
48
58
|
*/
|
|
49
59
|
function amountOf(model, long, [, uncached, read, w5, w1h, w, out]) {
|
|
@@ -52,6 +62,7 @@ function amountOf(model, long, [, uncached, read, w5, w1h, w, out]) {
|
|
|
52
62
|
return null;
|
|
53
63
|
return (uncached * r.input + read * r.read + w5 * r.write5m + w1h * r.write1h + w * r.write + out * r.output) / M;
|
|
54
64
|
}
|
|
65
|
+
const estimatedSlice = (s) => ESTIMATED_BACKENDS.has(s.backend);
|
|
55
66
|
const unknownSource = (s) => s.backend === "unknown" || s.profile === "unknown";
|
|
56
67
|
export function buildUsageReport(input) {
|
|
57
68
|
const { from, to } = rangeDates(input.range, input.now, input.zone);
|
|
@@ -70,14 +81,14 @@ export function buildUsageReport(input) {
|
|
|
70
81
|
if (!i.ledger || !i.days)
|
|
71
82
|
continue;
|
|
72
83
|
coverage.push({ name: i.name, intervals: i.days.coverage.map(([a, b]) => [a, b]) });
|
|
73
|
-
// Codex: a write is reported only once a real one has been seen (every CLI so far records 0).
|
|
84
|
+
// Codex and Muse: a write is reported only once a real one has been seen (every CLI so far records 0).
|
|
74
85
|
const writesSeen = i.ledger.writesSeen === true;
|
|
75
86
|
const base = { instance: i.name, team: i.team };
|
|
76
87
|
for (const row of i.days.rows) {
|
|
77
88
|
if (!inRange(row.date))
|
|
78
89
|
continue;
|
|
79
90
|
for (const [mk, tot] of Object.entries(row.m))
|
|
80
|
-
slices.push({ ...base, backend: row.be, writesReported: row.be
|
|
91
|
+
slices.push({ ...base, backend: row.be, writesReported: !WRITES_UNREPORTED.has(row.be) || writesSeen, date: row.date, zone: row.zone, profile: row.pf, ...splitKey(mk), tot, reasoning: row.r?.[mk] ?? 0, cost: row.c?.[mk] ?? 0 });
|
|
81
92
|
}
|
|
82
93
|
for (const [hk, hour] of Object.entries(i.ledger.hours)) {
|
|
83
94
|
const h = Number(hk);
|
|
@@ -88,7 +99,7 @@ export function buildUsageReport(input) {
|
|
|
88
99
|
continue;
|
|
89
100
|
for (const src of hourSources(hour)) {
|
|
90
101
|
for (const [mk, tot] of Object.entries(src.m))
|
|
91
|
-
slices.push({ ...base, backend: src.backend, writesReported: src.backend
|
|
102
|
+
slices.push({ ...base, backend: src.backend, writesReported: !WRITES_UNREPORTED.has(src.backend) || writesSeen, date, zone: input.zone, profile: src.profile, ...splitKey(mk), tot, reasoning: src.r[mk] ?? 0, cost: src.c[mk] ?? 0 });
|
|
92
103
|
}
|
|
93
104
|
}
|
|
94
105
|
}
|
|
@@ -102,7 +113,7 @@ export function buildUsageReport(input) {
|
|
|
102
113
|
}
|
|
103
114
|
};
|
|
104
115
|
const rows = new Map();
|
|
105
|
-
const newRow = (key) => ({ key, ...zero(), total: 0, messages: null, amount: null, priced: "none", flags: [], unpriced: [], instances: [], backends: [], models: [], profiles: [], _amount: 0, _pricedAny: false, _unpricedAny: false });
|
|
116
|
+
const newRow = (key) => ({ key, ...zero(), reasoningReported: false, estimated: false, amountEstimated: null, _estAmount: 0, _estPriced: false, total: 0, messages: null, amount: null, priced: "none", flags: [], unpriced: [], instances: [], backends: [], models: [], profiles: [], _amount: 0, _pricedAny: false, _unpricedAny: false });
|
|
106
117
|
const uniq = (list, v) => { if (!list.includes(v))
|
|
107
118
|
list.push(v); };
|
|
108
119
|
// Daily rows: one per day, zone, agent, backend, model, tier and profile (an hour's slices summed into their day).
|
|
@@ -110,12 +121,18 @@ export function buildUsageReport(input) {
|
|
|
110
121
|
for (const s of slices) {
|
|
111
122
|
const key = keyOf(s);
|
|
112
123
|
const row = rows.get(key) ?? (rows.set(key, newRow(key)), rows.get(key));
|
|
113
|
-
addTotals(row, s.tot);
|
|
124
|
+
addTotals(row, s.tot, s.reasoning);
|
|
125
|
+
if (REPORTS_REASONING.has(s.backend))
|
|
126
|
+
row.reasoningReported = true;
|
|
114
127
|
uniq(row.instances, s.instance);
|
|
115
128
|
uniq(row.backends, s.backend);
|
|
116
129
|
uniq(row.models, s.model);
|
|
117
130
|
uniq(row.profiles, s.profile);
|
|
118
|
-
|
|
131
|
+
// List price first; a model the table lacks takes the cost its CLI reported (Grok), said so (design §10.2).
|
|
132
|
+
const listed = amountOf(s.model, s.long, s.tot), reported = listed === null && s.cost > 0;
|
|
133
|
+
const amount = listed ?? (reported ? s.cost : null);
|
|
134
|
+
if (reported)
|
|
135
|
+
uniq(row.flags, "cost_reported");
|
|
119
136
|
if (amount === null) {
|
|
120
137
|
row._unpricedAny = true;
|
|
121
138
|
uniq(row.unpriced, s.model);
|
|
@@ -124,6 +141,14 @@ export function buildUsageReport(input) {
|
|
|
124
141
|
row._pricedAny = true;
|
|
125
142
|
row._amount += amount;
|
|
126
143
|
}
|
|
144
|
+
if (estimatedSlice(s)) {
|
|
145
|
+
row.estimated = true;
|
|
146
|
+
uniq(row.flags, "estimated");
|
|
147
|
+
if (amount !== null) {
|
|
148
|
+
row._estPriced = true;
|
|
149
|
+
row._estAmount += amount;
|
|
150
|
+
}
|
|
151
|
+
}
|
|
127
152
|
if (!s.writesReported)
|
|
128
153
|
uniq(row.flags, "cache_write_not_reported");
|
|
129
154
|
if (unknownSource(s))
|
|
@@ -132,11 +157,13 @@ export function buildUsageReport(input) {
|
|
|
132
157
|
const dk = [s.date, s.zone, s.instance, s.backend, s.model, tier, s.profile].join("\u0000");
|
|
133
158
|
let d = dailyBy.get(dk);
|
|
134
159
|
if (!d) {
|
|
135
|
-
d = { date: s.date, zone: s.zone, instance: s.instance, backend: s.backend, model: s.model, tier, profile: s.profile, ...zero(), amount: amount === null ? null : 0,
|
|
136
|
-
flags: [...(s.writesReported ? [] : ["cache_write_not_reported"]), ...(amount === null ? ["not_priced"] : []), ...(unknownSource(s) ? ["source_unknown"] : [])] };
|
|
160
|
+
d = { date: s.date, zone: s.zone, instance: s.instance, backend: s.backend, model: s.model, tier, profile: s.profile, ...zero(), reasoningReported: REPORTS_REASONING.has(s.backend), estimated: estimatedSlice(s), amount: amount === null ? null : 0,
|
|
161
|
+
flags: [...(estimatedSlice(s) ? ["estimated"] : []), ...(s.writesReported ? [] : ["cache_write_not_reported"]), ...(amount === null ? ["not_priced"] : []), ...(unknownSource(s) ? ["source_unknown"] : [])] };
|
|
137
162
|
dailyBy.set(dk, d);
|
|
138
163
|
}
|
|
139
|
-
addTotals(d, s.tot);
|
|
164
|
+
addTotals(d, s.tot, s.reasoning);
|
|
165
|
+
if (reported && !d.flags.includes("cost_reported"))
|
|
166
|
+
d.flags.push("cost_reported");
|
|
140
167
|
if (amount !== null && d.amount !== null)
|
|
141
168
|
d.amount += amount;
|
|
142
169
|
}
|
|
@@ -188,8 +215,9 @@ export function buildUsageReport(input) {
|
|
|
188
215
|
row.messages ??= 0;
|
|
189
216
|
}
|
|
190
217
|
const finish = (row) => {
|
|
191
|
-
const { _amount, _pricedAny, _unpricedAny, ...rest } = row;
|
|
192
|
-
rest.
|
|
218
|
+
const { _amount, _pricedAny, _unpricedAny, _estAmount, _estPriced, ...rest } = row;
|
|
219
|
+
rest.amountEstimated = _estPriced ? _estAmount : null;
|
|
220
|
+
rest.total = rest.input + rest.output + rest.reasoning + rest.cacheRead + rest.cacheWrite5m + rest.cacheWrite1h + rest.cacheWrite;
|
|
193
221
|
rest.amount = _pricedAny ? _amount : null;
|
|
194
222
|
rest.priced = _pricedAny ? (_unpricedAny ? "partial" : "all") : "none";
|
|
195
223
|
if (_unpricedAny)
|
|
@@ -200,15 +228,24 @@ export function buildUsageReport(input) {
|
|
|
200
228
|
.sort((a, b) => (b.amount ?? -1) - (a.amount ?? -1) || b.total - a.total || a.key.localeCompare(b.key));
|
|
201
229
|
const totals = finish(newRow("all"));
|
|
202
230
|
for (const r of out) {
|
|
203
|
-
for (const k of ["input", "output", "cacheRead", "cacheWrite5m", "cacheWrite1h", "cacheWrite", "requests"])
|
|
231
|
+
for (const k of ["input", "output", "reasoning", "cacheRead", "cacheWrite5m", "cacheWrite1h", "cacheWrite", "requests"])
|
|
204
232
|
totals[k] += r[k];
|
|
205
233
|
totals.total += r.total;
|
|
206
234
|
if (r.messages !== null)
|
|
207
235
|
totals.messages = (totals.messages ?? 0) + r.messages;
|
|
208
236
|
if (r.amount !== null)
|
|
209
237
|
totals.amount = (totals.amount ?? 0) + r.amount;
|
|
238
|
+
if (r.reasoningReported)
|
|
239
|
+
totals.reasoningReported = true;
|
|
240
|
+
if (r.estimated) {
|
|
241
|
+
totals.estimated = true;
|
|
242
|
+
uniq(totals.flags, "includes_estimates");
|
|
243
|
+
}
|
|
244
|
+
if (r.amountEstimated !== null)
|
|
245
|
+
totals.amountEstimated = (totals.amountEstimated ?? 0) + r.amountEstimated;
|
|
210
246
|
for (const f of r.flags)
|
|
211
|
-
|
|
247
|
+
if (f !== "estimated")
|
|
248
|
+
uniq(totals.flags, f);
|
|
212
249
|
for (const m of r.unpriced)
|
|
213
250
|
uniq(totals.unpriced, m);
|
|
214
251
|
}
|