zigmini 0.9.0-beta.2 → 1.0.0-beta.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.ko.md +35 -5
- package/README.md +35 -5
- package/assets/agent/gateway-rules.md +1 -1
- package/assets/android/zigmini.apk +0 -0
- package/assets/android/zigmini.apk.json +3 -3
- package/assets/build.json +1 -1
- package/docs/manual.ko.md +51 -11
- package/docs/manual.md +52 -12
- package/host/omo-bridge/agent-control.ts +274 -0
- package/host/omo-bridge/bridge.ts +1 -1
- package/host/omo-bridge/continuity-control.ts +1 -1
- package/host/omo-bridge/model-control.ts +15 -3
- package/host/omo-bridge/task-control.ts +26 -16
- package/host/omo-check/patches/omo-5.1.48-compaction-recovery.patch +912 -0
- package/host/omo-check/patches/omo-5.1.48-gateway-port.manifest.json +12 -11
- package/host/omo-check/patches/omo-5.1.51-compaction-long-context.patch +92 -0
- package/host/omo-check/patches/omo-5.1.51-compaction-port.patch +2635 -0
- package/host/omo-check/patches/omo-5.1.51-compaction-recovery.patch +911 -0
- package/host/omo-check/patches/omo-5.1.51-gateway-port.manifest.json +363 -0
- package/host/omo-check/patches/omo-5.1.51-provider-failover.patch +50 -0
- package/host/omo-check/patches/omo-5.1.51-provider-port.patch +441 -0
- package/host/omo-check/patches/omo-5.1.51-rpc-port.patch +799 -0
- package/host/omo-check/patches/omo-5.1.51-runtime-compat.patch +31 -0
- package/package.json +3 -2
- package/src/agent/api.ts +215 -0
- package/src/agent/definition.ts +177 -0
- package/src/agent/fields.ts +42 -0
- package/src/agent/files.ts +192 -0
- package/src/agent/legacy-files.ts +46 -0
- package/src/agent/list.ts +21 -0
- package/src/agent-rules.ts +14 -5
- package/src/approvals.ts +6 -1
- package/src/cli/main.ts +33 -0
- package/src/cli/messages.ts +10 -2
- package/src/cli/rotate-main.ts +2 -1
- package/src/cli/scan.ts +414 -0
- package/src/cli/security-import.ts +155 -0
- package/src/compact.ts +22 -0
- package/src/decisions.ts +6 -4
- package/src/engine/compat.json +3 -3
- package/src/engine/index.ts +1 -0
- package/src/engine/schema-guard.ts +12 -4
- package/src/engine/sdk.ts +8 -0
- package/src/formal-verification.ts +366 -0
- package/src/gc.ts +37 -6
- package/src/hub.ts +525 -85
- package/src/main.ts +95 -9
- package/src/maintenance.ts +1 -1
- package/src/manual.ts +9 -2
- package/src/omo/client.ts +2 -0
- package/src/profile-prompt.ts +73 -0
- package/src/profile.ts +33 -10
- package/src/protocol.ts +11 -2
- package/src/question-notifications.ts +4 -1
- package/src/reply-notifications.ts +7 -1
- package/src/repo-security-setting.ts +33 -0
- package/src/runtime/paths.ts +16 -0
- package/src/security/baseline.ts +170 -0
- package/src/security/dex-strings.ts +242 -0
- package/src/security/repo-scan.ts +369 -0
- package/src/security/revision-probe.ts +144 -0
- package/src/security/terms.ts +97 -0
- package/src/security-allow.ts +1 -0
- package/src/security-baseline.ts +113 -0
- package/src/security-check.ts +91 -16
- package/src/server-text.ts +38 -0
- package/src/store.ts +142 -16
- package/src/transcript.ts +7 -1
- package/src/ui-language.ts +33 -0
- package/src/update/check.ts +7 -3
- package/src/update/handoff-system.ts +24 -16
- package/src/update/handoff.ts +23 -12
- package/src/update/helper.ts +72 -36
- package/src/update/request.ts +46 -30
- package/src/update/state.ts +21 -11
- package/src/update/strategy.ts +12 -5
- package/src/update/target.ts +32 -15
- package/src/usage.ts +43 -20
- package/src/version.ts +39 -16
- package/src/voice/agents.ts +8 -3
- package/src/voice/install.ts +29 -18
- package/src/work-watch.ts +40 -18
- package/web/agent-icon.js +15 -9
- package/web/agents.d.ts +201 -0
- package/web/agents.js +813 -0
- package/web/app-download.js +32 -19
- package/web/app.js +1407 -278
- package/web/autocopy.js +6 -1
- package/web/boot.js +27 -8
- package/web/commands.js +20 -11
- package/web/decisions.js +70 -49
- package/web/devlog.js +25 -19
- package/web/diagram.js +8 -3
- package/web/enroll.js +43 -22
- package/web/gc.js +32 -19
- package/web/i18n/agent-icon.js +24 -0
- package/web/i18n/agents.js +48 -0
- package/web/i18n/app-download.js +26 -0
- package/web/i18n/autocopy.js +5 -0
- package/web/i18n/boot.js +24 -0
- package/web/i18n/commands.js +34 -0
- package/web/i18n/decisions.js +24 -0
- package/web/i18n/devlog.js +60 -0
- package/web/i18n/diagram.js +12 -0
- package/web/i18n/enroll.js +22 -0
- package/web/i18n/gc.js +38 -0
- package/web/i18n/lastsent.js +10 -0
- package/web/i18n/main.d.ts +4 -0
- package/web/i18n/main.js +527 -0
- package/web/i18n/manual.d.ts +4 -0
- package/web/i18n/manual.js +14 -0
- package/web/i18n/memo.js +30 -0
- package/web/i18n/notifications.js +16 -0
- package/web/i18n/profile.js +13 -0
- package/web/i18n/reference.js +26 -0
- package/web/i18n/security-inbox.js +40 -0
- package/web/i18n/slash.js +38 -0
- package/web/i18n/turn-stats.js +5 -0
- package/web/i18n/update.js +42 -0
- package/web/i18n/voice.js +28 -0
- package/web/i18n/wake.js +16 -0
- package/web/i18n/workid.js +10 -0
- package/web/i18n/workstatus.js +20 -0
- package/web/i18n.d.ts +20 -0
- package/web/i18n.js +63 -0
- package/web/index.html +156 -96
- package/web/lastsent.js +9 -4
- package/web/manual.d.ts +1 -1
- package/web/manual.js +11 -5
- package/web/memo.js +18 -13
- package/web/notifications.js +12 -7
- package/web/profile.js +42 -17
- package/web/reference.js +20 -15
- package/web/security-inbox.d.ts +18 -1
- package/web/security-inbox.js +173 -4
- package/web/slash.js +23 -13
- package/web/style.css +132 -3
- package/web/tooltip.js +5 -2
- package/web/turn-stats.js +6 -2
- package/web/update.js +31 -23
- package/web/voice.js +18 -8
- package/web/wake.d.ts +4 -0
- package/web/wake.js +7 -3
- package/web/workid.js +12 -2
- package/web/workstatus.js +36 -32
package/src/work-watch.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { blockingCompaction, COMPACTION_FAILURE_KINDS, compactionOutcome, type OmoEvent } from "./omo/client.ts";
|
|
2
|
+
import { say, type Text } from "./server-text.ts";
|
|
2
3
|
import type { Store, WorkCommand, WorkItem, WorkUpdate } from "./store.ts";
|
|
3
4
|
|
|
4
5
|
/**
|
|
@@ -58,7 +59,7 @@ export interface Verdict {
|
|
|
58
59
|
readonly waitingFor?: "children";
|
|
59
60
|
/** Machine-readable basis, e.g. no_activity, status_request_unanswered, model_error. */
|
|
60
61
|
readonly reason: string;
|
|
61
|
-
/** One short
|
|
62
|
+
/** One short line for the card's name view, in the language it was judged in. */
|
|
62
63
|
readonly note?: string;
|
|
63
64
|
/** When the basis of this verdict began (ms). The board shows it as the card's time. */
|
|
64
65
|
readonly since: number;
|
|
@@ -103,7 +104,28 @@ export interface JudgeInput {
|
|
|
103
104
|
|
|
104
105
|
const TERMINAL = new Set(["completed", "failed", "cancelled"]);
|
|
105
106
|
const UNSETTLED = new Set(["pending", "sending", "unknown"]);
|
|
106
|
-
const ERROR_LABELS = {
|
|
107
|
+
const ERROR_LABELS = {
|
|
108
|
+
model_error: { en: "Model error", ko: "모델 오류" },
|
|
109
|
+
compaction_failed: { en: "Context compaction failed", ko: "컨텍스트 압축 실패" },
|
|
110
|
+
retry_exhausted: { en: "Retry failed", ko: "재시도 실패" },
|
|
111
|
+
} as const satisfies Record<string, Text>;
|
|
112
|
+
const MODEL_RESPONSE_ERROR: Text = { en: "Model response error", ko: "모델 응답 오류" };
|
|
113
|
+
const NOTE_CHILDREN: Text = { en: "Running child tasks: {count}", ko: "하위 작업 {count}개 실행 중" };
|
|
114
|
+
const NOTE_QUIET: Text = { en: "No activity for over {duration}", ko: "{duration} 넘게 활동이 없습니다" };
|
|
115
|
+
const NOTE_QUIET_IN_TOOL: Text = { en: "No activity for over {duration} while a tool ({tool}) was running", ko: "도구 실행({tool}) 중 {duration} 넘게 활동이 없습니다" };
|
|
116
|
+
const NOTE_CANCEL_STOPPED: Text = { en: "The run stopped after the cancel command", ko: "취소 명령 뒤 실행이 멈췄습니다" };
|
|
117
|
+
const NOTE_UNDELIVERED: Text = { en: "The command was not confirmed as received", ko: "명령 수신을 확인하지 못했습니다" };
|
|
118
|
+
const NOTE_DELIVERY_CHECKING: Text = { en: "Confirming that the command was received", ko: "명령 수신을 확인하는 중입니다" };
|
|
119
|
+
const NOTE_REPORT: Text = { en: "Report: {summary}", ko: "보고: {summary}" };
|
|
120
|
+
const NOTE_REQUEST_UNANSWERED: Text = { en: "The status report request got no response", ko: "상태 보고 요청에 응답이 없습니다" };
|
|
121
|
+
const NOTE_REQUESTED: Text = { en: "Stopped without a report, so a status report was requested", ko: "보고 없이 멈춰 상태 보고를 요청했습니다" };
|
|
122
|
+
const NOTE_STOPPED: Text = { en: "Stopped without a report", ko: "보고 없이 멈췄습니다" };
|
|
123
|
+
const SECONDS: Text = { en: "{count} sec", ko: "{count}초" };
|
|
124
|
+
const MINUTES: Text = { en: "{count} min", ko: "{count}분" };
|
|
125
|
+
const ALERT_SUMMARY: Text = {
|
|
126
|
+
en: "Work watcher verdict: failed ({note}). The stored work status was not changed, and no automatic restart or cancel was done.",
|
|
127
|
+
ko: "작업 감시자 판정: 고장 ({note}). 저장된 작업 상태는 바꾸지 않았고 자동 재시작·취소는 하지 않았습니다.",
|
|
128
|
+
};
|
|
107
129
|
|
|
108
130
|
function record(value: unknown): value is Record<string, unknown> {
|
|
109
131
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
@@ -128,7 +150,7 @@ function shorten(text: string, max: number): string {
|
|
|
128
150
|
}
|
|
129
151
|
|
|
130
152
|
function duration(ms: number): string {
|
|
131
|
-
return ms < 60_000 ?
|
|
153
|
+
return ms < 60_000 ? say(SECONDS, { count: Math.round(ms / 1000) }) : say(MINUTES, { count: Math.round(ms / 60_000) });
|
|
132
154
|
}
|
|
133
155
|
|
|
134
156
|
function iso(ms: number): string {
|
|
@@ -159,7 +181,7 @@ export function judge(input: JudgeInput, config: WatchConfig = DEFAULT_WATCH): V
|
|
|
159
181
|
const waitSince = Date.parse(reports.at(-1)?.receivedAt ?? work.createdAt);
|
|
160
182
|
const childWait = (next: number): Verdict => ({
|
|
161
183
|
state: "waiting", waitingFor: "children", reason: "children_running",
|
|
162
|
-
note:
|
|
184
|
+
note: say(NOTE_CHILDREN, { count: children }), since: waitSince, next,
|
|
163
185
|
});
|
|
164
186
|
// Without confirmed runtime evidence (no event, no reconciliation yet) the watcher shows the record and never acts.
|
|
165
187
|
if (live.streaming === null) return { state: storeState(work), reason: "runtime_unknown", since: base, next: Infinity };
|
|
@@ -176,12 +198,12 @@ export function judge(input: JudgeInput, config: WatchConfig = DEFAULT_WATCH): V
|
|
|
176
198
|
const unresponsiveAt = quietFrom + allowed;
|
|
177
199
|
const brokenAt = unresponsiveAt + config.brokenAfterMs;
|
|
178
200
|
const evidence = { signal: "no_activity", last_activity_at: iso(quietFrom), open_tools: tools.map((tool) => tool.name), observed_since: iso(live.observedSince) };
|
|
179
|
-
const
|
|
201
|
+
const quiet = (ms: number): string => tools.length > 0 ? say(NOTE_QUIET_IN_TOOL, { duration: duration(ms), tool: shorten(tools[0]?.name ?? "", 24) }) : say(NOTE_QUIET, { duration: duration(ms) });
|
|
180
202
|
if (now >= brokenAt) {
|
|
181
|
-
return { state: "broken", reason: "no_activity", note:
|
|
203
|
+
return { state: "broken", reason: "no_activity", note: quiet(allowed + config.brokenAfterMs), since: quietFrom, next: Infinity, evidence };
|
|
182
204
|
}
|
|
183
205
|
if (now >= unresponsiveAt) {
|
|
184
|
-
return { state: "unresponsive", reason: "no_activity", note:
|
|
206
|
+
return { state: "unresponsive", reason: "no_activity", note: quiet(allowed), since: quietFrom, next: brokenAt, evidence };
|
|
185
207
|
}
|
|
186
208
|
return { state: "running", reason: "active", since: base, next: unresponsiveAt };
|
|
187
209
|
}
|
|
@@ -191,13 +213,13 @@ export function judge(input: JudgeInput, config: WatchConfig = DEFAULT_WATCH): V
|
|
|
191
213
|
if (children > 0) return childWait(Infinity);
|
|
192
214
|
if (command.kind === "cancel" && command.delivery === "accepted") {
|
|
193
215
|
const at = Math.max(stoppedAt, Date.parse(command.acceptedAt ?? command.createdAt));
|
|
194
|
-
if (now >= at + config.settleMs) return { state: "cancelled", reason: "cancel_stopped", note:
|
|
216
|
+
if (now >= at + config.settleMs) return { state: "cancelled", reason: "cancel_stopped", note: say(NOTE_CANCEL_STOPPED), since: at, next: Infinity };
|
|
195
217
|
return { state: "unknown", reason: "cancel_settling", since: at, next: at + config.settleMs };
|
|
196
218
|
}
|
|
197
219
|
const error = live.lastError;
|
|
198
220
|
if (error && error.at >= commandAt && !(latestReport && Date.parse(latestReport.receivedAt) > error.at)) {
|
|
199
221
|
return {
|
|
200
|
-
state: "broken", reason: error.kind, note: `${ERROR_LABELS[error.kind]}: ${shorten(error.message, 80)}`, since: error.at, next: Infinity,
|
|
222
|
+
state: "broken", reason: error.kind, note: `${say(ERROR_LABELS[error.kind])}: ${shorten(error.message, 80)}`, since: error.at, next: Infinity,
|
|
201
223
|
evidence: { signal: error.kind, at: iso(error.at), message: shorten(error.message, 300) },
|
|
202
224
|
};
|
|
203
225
|
}
|
|
@@ -206,23 +228,23 @@ export function judge(input: JudgeInput, config: WatchConfig = DEFAULT_WATCH): V
|
|
|
206
228
|
const at = Math.max(stoppedAt, commandAt);
|
|
207
229
|
const brokenAt = at + config.reportResponseMs;
|
|
208
230
|
const evidence = { signal: "command_delivery_unknown", command_seq: command.seq, error: command.error, since: iso(at) };
|
|
209
|
-
if (now >= brokenAt) return { state: "broken", reason: "command_undelivered", note:
|
|
210
|
-
return { state: "unknown", reason: "command_delivery_unknown", note:
|
|
231
|
+
if (now >= brokenAt) return { state: "broken", reason: "command_undelivered", note: say(NOTE_UNDELIVERED), since: at, next: Infinity, evidence };
|
|
232
|
+
return { state: "unknown", reason: "command_delivery_unknown", note: say(NOTE_DELIVERY_CHECKING), since: at, next: brokenAt };
|
|
211
233
|
}
|
|
212
234
|
if (command.delivery !== "accepted") return { state: "unknown", reason: `command_${command.delivery}`, since: base, next: Infinity };
|
|
213
235
|
if (latestReport) {
|
|
214
|
-
return { state: "waiting", reason: `reported_${latestReport.kind}`, note:
|
|
236
|
+
return { state: "waiting", reason: `reported_${latestReport.kind}`, note: say(NOTE_REPORT, { summary: shorten(latestReport.summary, 80) }), since: Date.parse(latestReport.receivedAt), next: Infinity };
|
|
215
237
|
}
|
|
216
238
|
if (isStatusRequest(command)) {
|
|
217
239
|
const at = Math.max(stoppedAt, Date.parse(command.acceptedAt ?? command.createdAt));
|
|
218
240
|
const brokenAt = at + config.reportResponseMs;
|
|
219
241
|
if (now >= brokenAt) {
|
|
220
242
|
return {
|
|
221
|
-
state: "broken", reason: "status_request_unanswered", note:
|
|
243
|
+
state: "broken", reason: "status_request_unanswered", note: say(NOTE_REQUEST_UNANSWERED), since: at, next: Infinity,
|
|
222
244
|
evidence: { signal: "status_request_unanswered", command_seq: command.seq, requested_at: command.createdAt, stopped_since: iso(at) },
|
|
223
245
|
};
|
|
224
246
|
}
|
|
225
|
-
return { state: "unknown", reason: "status_requested", note:
|
|
247
|
+
return { state: "unknown", reason: "status_requested", note: say(NOTE_REQUESTED), since: at, next: brokenAt };
|
|
226
248
|
}
|
|
227
249
|
// The latest command gets the full window from its own acceptance, however long the work had been stopped before it.
|
|
228
250
|
const requestFrom = Math.max(stoppedAt, Date.parse(command.acceptedAt ?? command.createdAt));
|
|
@@ -230,7 +252,7 @@ export function judge(input: JudgeInput, config: WatchConfig = DEFAULT_WATCH): V
|
|
|
230
252
|
const interrupted = work.statusReason === "omo_disconnected";
|
|
231
253
|
const requestAt = interrupted ? requestFrom : requestFrom + config.reportRequestMs;
|
|
232
254
|
if (now >= requestAt && !commands.some((candidate) => UNSETTLED.has(candidate.delivery))) {
|
|
233
|
-
return { state: "unknown", reason: "stopped_without_report", note:
|
|
255
|
+
return { state: "unknown", reason: "stopped_without_report", note: say(NOTE_STOPPED), since: requestFrom, next: Infinity, action: "request_status" };
|
|
234
256
|
}
|
|
235
257
|
return { state: "unknown", reason: "stopped", since: requestFrom, next: requestAt };
|
|
236
258
|
}
|
|
@@ -368,7 +390,7 @@ export class WorkWatcher {
|
|
|
368
390
|
// even with errorMessage set; a work that really stops afterwards is judged by the status request (bug 23).
|
|
369
391
|
if (message.stopReason === "aborted") return false;
|
|
370
392
|
if (message.stopReason === "error" || typeof message.errorMessage === "string") {
|
|
371
|
-
live.lastError = { kind: "model_error", message: typeof message.errorMessage === "string" ? message.errorMessage :
|
|
393
|
+
live.lastError = { kind: "model_error", message: typeof message.errorMessage === "string" ? message.errorMessage : say(MODEL_RESPONSE_ERROR), at: now };
|
|
372
394
|
} else {
|
|
373
395
|
live.lastError = null;
|
|
374
396
|
}
|
|
@@ -388,7 +410,7 @@ export class WorkWatcher {
|
|
|
388
410
|
case "auto_retry_end":
|
|
389
411
|
// A user abort cancels a pending retry; that is not a breakdown.
|
|
390
412
|
if (event.success !== false || event.finalError === "Retry cancelled") return false;
|
|
391
|
-
live.lastError = { kind: "retry_exhausted", message: typeof event.finalError === "string" ? event.finalError :
|
|
413
|
+
live.lastError = { kind: "retry_exhausted", message: typeof event.finalError === "string" ? event.finalError : say(ERROR_LABELS.retry_exhausted), at: now };
|
|
392
414
|
return true;
|
|
393
415
|
case "extension_event": {
|
|
394
416
|
if (event.name !== "omo.task.updated" || !record(event.data) || !Array.isArray(event.data.tasks)) return false;
|
|
@@ -510,7 +532,7 @@ export class WorkWatcher {
|
|
|
510
532
|
const recorded = this.store.recordWatchAlert(work.id, {
|
|
511
533
|
sourceKey: `watch:broken:${work.id}:${command.seq}`,
|
|
512
534
|
commandSeq: command.seq,
|
|
513
|
-
summary:
|
|
535
|
+
summary: say(ALERT_SUMMARY, { note: verdict.note ?? verdict.reason }),
|
|
514
536
|
details: { source: "watcher", verdict: "broken", reason: verdict.reason, judged_at: iso(this.clock()), evidence: verdict.evidence ?? null },
|
|
515
537
|
});
|
|
516
538
|
if (recorded) this.hooks.alerted(work);
|
package/web/agent-icon.js
CHANGED
|
@@ -1,3 +1,8 @@
|
|
|
1
|
+
import dict from "./i18n/agent-icon.js";
|
|
2
|
+
import { applyI18n, registerStrings, t } from "./i18n.js";
|
|
3
|
+
|
|
4
|
+
registerStrings(dict);
|
|
5
|
+
|
|
1
6
|
// Improvement 60: the agent's (Zig's) profile icon. Double-clicking the agent avatar opens a small editor: choose a
|
|
2
7
|
// PNG/JPEG/WebP of at most 1 MB, see it center-cropped to a square, save it, or go back to the default icon. Escape closes
|
|
3
8
|
// the editor without a change. The gateway keeps the image (/api/profile/icon); `agentIcon` in the profile is its version.
|
|
@@ -16,8 +21,8 @@ export function agentIconUrl(version) {
|
|
|
16
21
|
|
|
17
22
|
/** Null when [file] can be used, else the Korean message to show. */
|
|
18
23
|
export function iconFileProblem(file) {
|
|
19
|
-
if (!ICON_TYPES.includes(file?.type)) return "
|
|
20
|
-
if (file.size > ICON_MAX_BYTES) return "
|
|
24
|
+
if (!ICON_TYPES.includes(file?.type)) return t("icon.problem.type");
|
|
25
|
+
if (file.size > ICON_MAX_BYTES) return t("icon.problem.size");
|
|
21
26
|
return null;
|
|
22
27
|
}
|
|
23
28
|
|
|
@@ -58,17 +63,18 @@ export function editAgentIcon(host, currentUrl, { onSave, onReset }) {
|
|
|
58
63
|
const editor = document.createElement("div");
|
|
59
64
|
editor.className = "icon-editor";
|
|
60
65
|
editor.setAttribute("role", "dialog");
|
|
61
|
-
editor.setAttribute("aria-label", "
|
|
66
|
+
editor.setAttribute("aria-label", t("icon.editor.label"));
|
|
62
67
|
editor.innerHTML = `<img class="icon-editor-preview" alt="" width="64" height="64" />
|
|
63
|
-
<div class="icon-editor-hint"
|
|
68
|
+
<div class="icon-editor-hint" data-i18n="icon.editor.hint"></div>
|
|
64
69
|
<div class="icon-editor-error" role="alert" hidden></div>
|
|
65
70
|
<input type="file" accept="${ICON_TYPES.join(",")}" hidden />
|
|
66
71
|
<div class="icon-editor-actions">
|
|
67
|
-
<button type="button" data-act="pick"
|
|
68
|
-
<button type="button" data-act="save" disabled
|
|
69
|
-
<button type="button" data-act="reset"
|
|
70
|
-
<button type="button" data-act="cancel"
|
|
72
|
+
<button type="button" data-act="pick" data-i18n="icon.pick"></button>
|
|
73
|
+
<button type="button" data-act="save" data-i18n="icon.save" disabled></button>
|
|
74
|
+
<button type="button" data-act="reset" data-i18n="icon.reset"></button>
|
|
75
|
+
<button type="button" data-act="cancel" data-i18n="icon.cancel"></button>
|
|
71
76
|
</div>`;
|
|
77
|
+
applyI18n(editor);
|
|
72
78
|
const preview = editor.querySelector(".icon-editor-preview");
|
|
73
79
|
const error = editor.querySelector(".icon-editor-error");
|
|
74
80
|
const input = editor.querySelector("input");
|
|
@@ -107,7 +113,7 @@ export function editAgentIcon(host, currentUrl, { onSave, onReset }) {
|
|
|
107
113
|
try {
|
|
108
114
|
chosen = await squarePng(file);
|
|
109
115
|
} catch {
|
|
110
|
-
showError("
|
|
116
|
+
showError(t("icon.readFailed"));
|
|
111
117
|
return;
|
|
112
118
|
}
|
|
113
119
|
showError(null);
|
package/web/agents.d.ts
ADDED
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
export type FieldOrigin = "package" | "defaults" | "agent" | "none";
|
|
2
|
+
export type FieldWidget = "text" | "textarea" | "number" | "toggle" | "model" | "select" | "skills" | "group";
|
|
3
|
+
export type FieldType = "string" | "integer" | "boolean" | "object" | "strings";
|
|
4
|
+
export type EditorKind = "new" | "agent" | "defaults";
|
|
5
|
+
|
|
6
|
+
/** One gateway registry row as the agent API sends it (`registry` in detail and defaults answers; an absent default is undefined). */
|
|
7
|
+
export interface AgentFieldMeta {
|
|
8
|
+
readonly name: string;
|
|
9
|
+
readonly type: FieldType;
|
|
10
|
+
readonly default?: unknown;
|
|
11
|
+
readonly required: boolean;
|
|
12
|
+
readonly merge: "replace" | "keys";
|
|
13
|
+
readonly applies: "gateway" | "extension";
|
|
14
|
+
readonly widget: FieldWidget;
|
|
15
|
+
readonly forbidden: boolean;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/** The editor's labels and hints for one registry field name. */
|
|
19
|
+
export interface FieldPresentation {
|
|
20
|
+
readonly label: string;
|
|
21
|
+
readonly help?: string;
|
|
22
|
+
readonly options?: readonly string[];
|
|
23
|
+
readonly min?: number;
|
|
24
|
+
readonly max?: number;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/** A registry row joined with its presentation: what editorRows() and widgetValue() work from. */
|
|
28
|
+
export interface AgentFieldRow extends AgentFieldMeta {
|
|
29
|
+
readonly label: string;
|
|
30
|
+
readonly help: string;
|
|
31
|
+
readonly options?: readonly string[];
|
|
32
|
+
readonly min?: number;
|
|
33
|
+
readonly max?: number;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/** GET /api/agents rows and `agent.updated` payloads. */
|
|
37
|
+
export interface AgentSummary {
|
|
38
|
+
readonly id: string;
|
|
39
|
+
readonly name: string;
|
|
40
|
+
readonly description: string;
|
|
41
|
+
readonly icon: string | null;
|
|
42
|
+
readonly convId: string;
|
|
43
|
+
readonly definitionSha: string;
|
|
44
|
+
readonly stale: boolean;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/** GET /api/agents/:id: the summary with the definition text, the merged result and the field registry. */
|
|
48
|
+
export interface AgentDetail extends AgentSummary {
|
|
49
|
+
readonly source: string;
|
|
50
|
+
readonly fields: Readonly<Record<string, unknown>>;
|
|
51
|
+
readonly body: string;
|
|
52
|
+
readonly defaultsBody: string;
|
|
53
|
+
readonly origins: Readonly<Record<string, "package" | "defaults" | "agent">>;
|
|
54
|
+
readonly sha: string;
|
|
55
|
+
readonly registry: readonly AgentFieldMeta[];
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/** GET and PUT /api/agents/defaults: the defaults.md text and the same field registry. */
|
|
59
|
+
export interface AgentDefaults {
|
|
60
|
+
readonly source: string;
|
|
61
|
+
readonly registry: readonly AgentFieldMeta[];
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export interface ConversationRow {
|
|
65
|
+
readonly id: string;
|
|
66
|
+
readonly title: string;
|
|
67
|
+
readonly threadId?: string | null;
|
|
68
|
+
readonly createdAt?: string;
|
|
69
|
+
readonly agentId: string | null;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
export interface SourceSplit {
|
|
73
|
+
readonly ok: boolean;
|
|
74
|
+
readonly preamble: string;
|
|
75
|
+
readonly entries: readonly { readonly key: string | null; readonly raw: string }[];
|
|
76
|
+
readonly body: string;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
export interface EditorState {
|
|
80
|
+
readonly kind: EditorKind;
|
|
81
|
+
readonly registry: readonly AgentFieldRow[];
|
|
82
|
+
readonly id: string | null;
|
|
83
|
+
readonly name: string | null;
|
|
84
|
+
readonly convId: string | null;
|
|
85
|
+
readonly stale: boolean;
|
|
86
|
+
readonly own: Record<string, unknown>;
|
|
87
|
+
readonly rawByKey: Readonly<Record<string, string>>;
|
|
88
|
+
readonly touched: ReadonlySet<string>;
|
|
89
|
+
readonly unparsed: ReadonlySet<string>;
|
|
90
|
+
readonly unknownRaw: string;
|
|
91
|
+
readonly unknownTouched: boolean;
|
|
92
|
+
readonly preamble: string;
|
|
93
|
+
readonly body: string;
|
|
94
|
+
readonly bodyTouched: boolean;
|
|
95
|
+
readonly inherited: Readonly<Record<string, unknown>>;
|
|
96
|
+
readonly origins: Readonly<Record<string, string>>;
|
|
97
|
+
readonly dirty: boolean;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
export type EditorRow =
|
|
101
|
+
| { readonly kind: "group"; readonly name: string; readonly label: string; readonly help: string }
|
|
102
|
+
| {
|
|
103
|
+
readonly kind: "field";
|
|
104
|
+
readonly name: string;
|
|
105
|
+
readonly label: string;
|
|
106
|
+
readonly help: string;
|
|
107
|
+
readonly widget: FieldWidget;
|
|
108
|
+
readonly type: FieldType;
|
|
109
|
+
readonly required: boolean;
|
|
110
|
+
readonly value: unknown;
|
|
111
|
+
readonly own: boolean;
|
|
112
|
+
readonly origin: FieldOrigin;
|
|
113
|
+
readonly originLabel: string;
|
|
114
|
+
readonly clearable: boolean;
|
|
115
|
+
readonly unparsed: boolean;
|
|
116
|
+
readonly options?: readonly string[];
|
|
117
|
+
readonly min?: number;
|
|
118
|
+
readonly max?: number;
|
|
119
|
+
};
|
|
120
|
+
|
|
121
|
+
/** What widgetValue() reads from a registry row or an editor field row. */
|
|
122
|
+
export type WidgetSpec = Pick<AgentFieldRow, "widget" | "required" | "label"> & Partial<Pick<AgentFieldRow, "options" | "min" | "max">>;
|
|
123
|
+
export interface EditorError { readonly name: string; readonly message: string }
|
|
124
|
+
export interface ApiRequest { readonly method: "GET" | "POST" | "PUT"; readonly path: string; readonly headers?: Readonly<Record<string, string>>; readonly body?: string }
|
|
125
|
+
|
|
126
|
+
export interface SidebarItem {
|
|
127
|
+
readonly convId: string;
|
|
128
|
+
readonly agentId: string;
|
|
129
|
+
readonly name: string;
|
|
130
|
+
readonly description: string;
|
|
131
|
+
readonly icon: string;
|
|
132
|
+
readonly active: boolean;
|
|
133
|
+
readonly running: boolean;
|
|
134
|
+
readonly pending: boolean;
|
|
135
|
+
readonly stale: boolean;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
export interface SidebarView {
|
|
139
|
+
readonly current: string;
|
|
140
|
+
readonly running: ReadonlySet<string>;
|
|
141
|
+
readonly busy: (convId: string) => boolean;
|
|
142
|
+
readonly pending: ReadonlyMap<string, number>;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
export interface HeaderView { readonly agent: boolean; readonly agentId: string | null; readonly icon: string; readonly name: string; readonly stale: boolean }
|
|
146
|
+
export interface TrashRow { readonly entry: string; readonly id: string | null; readonly name: string; readonly description: string; readonly icon: string; readonly deletedAt: string | null }
|
|
147
|
+
export interface FileRow { readonly path: string; readonly type: "file" | "directory"; readonly size: number; readonly depth: number; readonly name: string; readonly label: string; readonly source: boolean }
|
|
148
|
+
export interface AgentUpdateResult<P, C> { readonly agents: readonly P[]; readonly conversations: readonly C[]; readonly removedConv: string | null; readonly restoredConv: string | null }
|
|
149
|
+
|
|
150
|
+
export const AGENTS_PATH: string;
|
|
151
|
+
export const DEFAULTS_PATH: string;
|
|
152
|
+
export const TRASH_PATH: string;
|
|
153
|
+
export const AGENT_MAX_BYTES: number;
|
|
154
|
+
export const DESCRIPTION_MAX: number;
|
|
155
|
+
export const MEMORY_MAX_BYTES: number;
|
|
156
|
+
export const THINKING_LEVELS: readonly string[];
|
|
157
|
+
export const ORIGIN_LABELS: Readonly<Record<FieldOrigin, string>>;
|
|
158
|
+
export const DEFAULT_ICON: string;
|
|
159
|
+
export const STALE_LABEL: string;
|
|
160
|
+
export const STALE_TITLE: string;
|
|
161
|
+
export const DELETE_HINT: string;
|
|
162
|
+
export const EMPTY_AGENTS_LINE: string;
|
|
163
|
+
export const EMPTY_TRASH_LINE: string;
|
|
164
|
+
export const EMPTY_FILES_LINE: string;
|
|
165
|
+
export const NEW_AGENT_BODY: string;
|
|
166
|
+
export const FIELD_PRESENTATION: Readonly<Record<string, FieldPresentation>>;
|
|
167
|
+
|
|
168
|
+
export function fieldAt(fields: unknown, name: string): unknown;
|
|
169
|
+
export function editorFields(registry: readonly AgentFieldMeta[] | null | undefined): AgentFieldRow[];
|
|
170
|
+
export function topLevelKeys(registry: readonly AgentFieldMeta[]): string[];
|
|
171
|
+
export function parseYaml(text: string): unknown;
|
|
172
|
+
export function emitScalar(value: unknown): string;
|
|
173
|
+
export function emitEntry(key: string, value: unknown): string;
|
|
174
|
+
export function splitSource(source: string): SourceSplit;
|
|
175
|
+
export function editorState(detail: Partial<AgentDetail> | Partial<AgentDefaults> | null | undefined, kind?: EditorKind): EditorState;
|
|
176
|
+
export function packageDefaults(registry: readonly AgentFieldMeta[]): Record<string, unknown>;
|
|
177
|
+
export function editorRows(state: EditorState): EditorRow[];
|
|
178
|
+
export function widgetValue(field: WidgetSpec, input: unknown): { value?: unknown; error?: string };
|
|
179
|
+
export function setField(state: EditorState, name: string, value: unknown): EditorState;
|
|
180
|
+
export function clearField(state: EditorState, name: string): EditorState;
|
|
181
|
+
export function setBody(state: EditorState, body: string): EditorState;
|
|
182
|
+
export function setUnknownRaw(state: EditorState, unknownRaw: string): EditorState;
|
|
183
|
+
export function composeSource(state: EditorState): string;
|
|
184
|
+
export function validateEditor(state: EditorState): EditorError[];
|
|
185
|
+
export function saveRequest(state: EditorState): ApiRequest;
|
|
186
|
+
export function deleteEnabled(typed: string, name: string | null | undefined): boolean;
|
|
187
|
+
export function deleteRequest(id: string, name: string): ApiRequest;
|
|
188
|
+
export function cloneRequest(id: string): ApiRequest;
|
|
189
|
+
export function restoreRequest(entry: string): ApiRequest;
|
|
190
|
+
export function filesPath(id: string): string;
|
|
191
|
+
export function sourceUploadPath(id: string): string;
|
|
192
|
+
export function errorLine(body: unknown, status: number): string;
|
|
193
|
+
export function applyAgentUpdate<P extends { id: string; convId?: string }, C extends ConversationRow>(agents: readonly P[], conversations: readonly C[], data: unknown, now?: number): AgentUpdateResult<P | AgentSummary, C | ConversationRow>;
|
|
194
|
+
export function applyAgentApplied<P extends { convId: string; definitionSha: string; stale: boolean }>(agents: readonly P[], convId: string, data: unknown): P[];
|
|
195
|
+
export function mainConversations<C extends ConversationRow>(conversations: readonly C[]): C[];
|
|
196
|
+
export function shownConversation(conversations: readonly ConversationRow[], id: string): string;
|
|
197
|
+
export function agentInitial(name: unknown): string;
|
|
198
|
+
export function sidebarItems(conversations: readonly ConversationRow[], agents: readonly Partial<AgentSummary>[], view: SidebarView): SidebarItem[];
|
|
199
|
+
export function headerView(conv: ConversationRow | null | undefined, agents: readonly Partial<AgentSummary>[]): HeaderView;
|
|
200
|
+
export function trashRows(entries: unknown): TrashRow[];
|
|
201
|
+
export function fileRows(entries: unknown): FileRow[];
|