@botharness/ui 1.0.2 → 1.1.0

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/lib/client.js CHANGED
@@ -3139,6 +3139,76 @@ window.__ModuleLoader__.load({
3139
3139
  return policy;
3140
3140
  });
3141
3141
  }
3142
+ function isScheduleTrigger(value) {
3143
+ const trigger = asRecord$1(value);
3144
+ if (trigger?.["kind"] === "every") return Number.isSafeInteger(trigger["everySeconds"]);
3145
+ if (typeof trigger?.["timeZone"] !== "string") return false;
3146
+ if (trigger["kind"] === "cron") return typeof trigger["expression"] === "string";
3147
+ if (typeof trigger["time"] !== "string") return false;
3148
+ if (trigger["kind"] === "daily") return true;
3149
+ if (trigger["kind"] === "weekly") return Array.isArray(trigger["weekdays"]) && trigger["weekdays"].every((day) => Number.isSafeInteger(day));
3150
+ return trigger["kind"] === "once" && typeof trigger["date"] === "string";
3151
+ }
3152
+ function parseScheduleFiring(value) {
3153
+ const firing = asRecord$1(value);
3154
+ if (typeof firing?.["id"] !== "string" || typeof firing["scheduleId"] !== "string" || !["planned", "manual"].includes(String(firing["trigger"])) || typeof firing["occurrenceAt"] !== "string" || typeof firing["firedAt"] !== "string" || ![
3155
+ "pending",
3156
+ "observed",
3157
+ "handled",
3158
+ "coalesced",
3159
+ "skipped"
3160
+ ].includes(String(firing["state"]))) throw new Error("invalid Bot Schedule firing");
3161
+ return firing;
3162
+ }
3163
+ function parseSchedule(value) {
3164
+ const schedule = asRecord$1(value);
3165
+ if (typeof schedule?.["id"] !== "string" || typeof schedule["botSlug"] !== "string" || typeof schedule["title"] !== "string" || typeof schedule["prompt"] !== "string" || !isScheduleTrigger(schedule["trigger"]) || typeof schedule["enabled"] !== "boolean" || !["human", "personabot"].includes(String(schedule["creator"])) || typeof schedule["locked"] !== "boolean" || schedule["nextRunAt"] !== void 0 && typeof schedule["nextRunAt"] !== "string") throw new Error("invalid Bot Schedule");
3166
+ if (schedule["lastFiring"] !== void 0) parseScheduleFiring(schedule["lastFiring"]);
3167
+ return schedule;
3168
+ }
3169
+ async function loadBotSchedules(call, slug) {
3170
+ const raw = asRecord$1(await unwrap(call, "scheduleList", { slug }))?.["schedules"];
3171
+ if (!Array.isArray(raw)) throw new Error("invalid Bot Schedules");
3172
+ return raw.map(parseSchedule);
3173
+ }
3174
+ async function createBotSchedule(call, slug, input) {
3175
+ return parseSchedule(asRecord$1(await unwrap(call, "scheduleCreate", {
3176
+ slug,
3177
+ input
3178
+ }))?.["schedule"]);
3179
+ }
3180
+ async function updateBotSchedule(call, slug, id, change) {
3181
+ return parseSchedule(asRecord$1(await unwrap(call, "scheduleUpdate", {
3182
+ slug,
3183
+ id,
3184
+ change
3185
+ }))?.["schedule"]);
3186
+ }
3187
+ async function deleteBotSchedule(call, slug, id) {
3188
+ await unwrap(call, "scheduleDelete", {
3189
+ slug,
3190
+ id
3191
+ });
3192
+ }
3193
+ async function loadBotScheduleHistory(call, slug, id) {
3194
+ const raw = asRecord$1(await unwrap(call, "scheduleHistory", {
3195
+ slug,
3196
+ id
3197
+ }))?.["firings"];
3198
+ if (!Array.isArray(raw)) throw new Error("invalid Bot Schedule history");
3199
+ return raw.map(parseScheduleFiring);
3200
+ }
3201
+ async function runBotScheduleNow(call, slug, id) {
3202
+ return parseScheduleFiring(asRecord$1(await unwrap(call, "scheduleRunNow", {
3203
+ slug,
3204
+ id
3205
+ }))?.["firing"]);
3206
+ }
3207
+ async function previewBotSchedule(call, trigger) {
3208
+ const raw = asRecord$1(await unwrap(call, "schedulePreview", { trigger }))?.["occurrences"];
3209
+ if (!Array.isArray(raw) || !raw.every((value) => typeof value === "string")) throw new Error("invalid Bot Schedule preview");
3210
+ return raw;
3211
+ }
3142
3212
  async function setBotSourcePolicy(call, slug, edit) {
3143
3213
  await unwrap(call, "botSourcePolicySet", {
3144
3214
  slug,
@@ -3209,8 +3279,27 @@ window.__ModuleLoader__.load({
3209
3279
  ...typeof description === "string" ? { description } : {},
3210
3280
  ...typeof avatar === "string" ? { avatar } : {},
3211
3281
  ...isAvatarAppearance(record["appearance"]) ? { appearance: record["appearance"] } : isRetainedAvatarAppearance(record["appearance"]) ? { appearanceUnsupported: true } : {},
3212
- ...typeof record["paused"] === "boolean" ? { paused: record["paused"] } : {}
3213
- };
3282
+ ...typeof record["paused"] === "boolean" ? { paused: record["paused"] } : {},
3283
+ ...parseStandingLimits(record["standingLimits"]) ?? {}
3284
+ };
3285
+ }
3286
+ function parseStandingLimits(value) {
3287
+ const limits = asRecord$1(value);
3288
+ const soul = limits?.["soul"];
3289
+ const coreMemory = limits?.["coreMemory"];
3290
+ if (typeof soul !== "number" || typeof coreMemory !== "number") return void 0;
3291
+ return { standingLimits: {
3292
+ soul,
3293
+ coreMemory
3294
+ } };
3295
+ }
3296
+ async function setStandingLimits(call, slug, limits) {
3297
+ const bot = parseBotSummary(asRecord$1(await unwrap(call, "standingLimitsSet", {
3298
+ slug,
3299
+ ...limits
3300
+ }))?.["bot"]);
3301
+ if (bot === void 0) throw new Error("Invalid PersonaBot result");
3302
+ return bot;
3214
3303
  }
3215
3304
  function parseBotSummaries(value) {
3216
3305
  const bots = asRecord$1(value)?.["bots"];
@@ -3740,6 +3829,27 @@ window.__ModuleLoader__.load({
3740
3829
  displayName: item["displayName"]
3741
3830
  };
3742
3831
  }
3832
+ const TELEMETRY_LOCKS = [
3833
+ "config",
3834
+ "DO_NOT_TRACK",
3835
+ "BOTHARNESS_TELEMETRY"
3836
+ ];
3837
+ function parseTelemetryStatus(value) {
3838
+ const item = asRecord$1(value);
3839
+ if (typeof item?.["enabled"] !== "boolean") throw new Error("invalid telemetry status");
3840
+ const lockedBy = TELEMETRY_LOCKS.find((lock) => lock === item["lockedBy"]);
3841
+ return {
3842
+ enabled: item["enabled"],
3843
+ preference: item["preference"] !== false,
3844
+ ...lockedBy === void 0 ? {} : { lockedBy }
3845
+ };
3846
+ }
3847
+ async function loadTelemetryStatus(call, signal) {
3848
+ return parseTelemetryStatus(await unwrap(call, "telemetryStatus", {}, signal));
3849
+ }
3850
+ async function setTelemetryPreference(call, enabled) {
3851
+ return parseTelemetryStatus(await unwrap(call, "telemetrySet", { enabled }));
3852
+ }
3743
3853
  async function loadHumanIdentity(call, signal) {
3744
3854
  return parseHumanIdentity(await unwrap(call, "humanIdentity", {}, signal));
3745
3855
  }
@@ -4117,7 +4227,8 @@ window.__ModuleLoader__.load({
4117
4227
  "sourceMessageId",
4118
4228
  "assignmentSessionId",
4119
4229
  "assignmentPurpose",
4120
- "authorBotSlug"
4230
+ "authorBotSlug",
4231
+ "scheduleId"
4121
4232
  ]) if (row[key] !== void 0 && typeof row[key] !== "string") return void 0;
4122
4233
  if (row["assignmentReportState"] !== void 0 && ![
4123
4234
  "progress",
@@ -4271,6 +4382,19 @@ window.__ModuleLoader__.load({
4271
4382
  async function setRosterHidden(call, hidden, signal) {
4272
4383
  await unwrap(call, "hiddenSet", { hidden }, signal);
4273
4384
  }
4385
+ function parseStandingUsage(value) {
4386
+ if (!Array.isArray(value)) return [];
4387
+ return value.flatMap((entry) => {
4388
+ const row = asRecord$1(entry);
4389
+ if (row === void 0 || typeof row["path"] !== "string" || row["role"] !== "soul" && row["role"] !== "coreMemory" || typeof row["chars"] !== "number" || typeof row["limit"] !== "number") return [];
4390
+ return [{
4391
+ path: row["path"],
4392
+ role: row["role"],
4393
+ chars: row["chars"],
4394
+ limit: row["limit"]
4395
+ }];
4396
+ });
4397
+ }
4274
4398
  function parseMemoryCommit(value) {
4275
4399
  const row = asRecord$1(value);
4276
4400
  if (row === void 0 || typeof row["botSlug"] !== "string" || typeof row["sha"] !== "string" || !(row["parentSha"] === null || typeof row["parentSha"] === "string") || ![
@@ -4287,7 +4411,12 @@ window.__ModuleLoader__.load({
4287
4411
  async function loadMemorySnapshot(call, channelId) {
4288
4412
  const snapshot = asRecord$1(asRecord$1(await unwrap(call, "memorySnapshot", { channelId }))?.["snapshot"]);
4289
4413
  if (snapshot === void 0 || !(snapshot["head"] === null || typeof snapshot["head"] === "string") || !Array.isArray(snapshot["files"]) || !snapshot["files"].every((path) => typeof path === "string") || typeof snapshot["provisional"] !== "boolean") throw new Error("invalid Memory snapshot");
4290
- return snapshot;
4414
+ return {
4415
+ head: snapshot["head"],
4416
+ files: snapshot["files"],
4417
+ provisional: snapshot["provisional"],
4418
+ standing: parseStandingUsage(snapshot["standing"])
4419
+ };
4291
4420
  }
4292
4421
  async function loadWorkspaceFileTarget(call, slug, grantId) {
4293
4422
  const target = asRecord$1(asRecord$1(await unwrap(call, "workspaceFileTarget", {
@@ -5075,27 +5204,27 @@ window.__ModuleLoader__.load({
5075
5204
  //#region packages/client/src/client/workspace-grant-events.ts
5076
5205
  const WORKSPACE_GRANTS_CHANGED = "botharness/workspace-grants-changed";
5077
5206
  const revisions = /* @__PURE__ */ new Map();
5078
- const listeners$1 = /* @__PURE__ */ new Map();
5207
+ const listeners$2 = /* @__PURE__ */ new Map();
5079
5208
  function workspaceGrantRevision(slug) {
5080
5209
  return revisions.get(slug) ?? 0;
5081
5210
  }
5082
5211
  function subscribeWorkspaceGrantChanges(slug, listener) {
5083
- let scoped = listeners$1.get(slug);
5212
+ let scoped = listeners$2.get(slug);
5084
5213
  if (scoped === void 0) {
5085
5214
  scoped = /* @__PURE__ */ new Set();
5086
- listeners$1.set(slug, scoped);
5215
+ listeners$2.set(slug, scoped);
5087
5216
  }
5088
5217
  scoped.add(listener);
5089
5218
  return () => {
5090
5219
  scoped.delete(listener);
5091
5220
  if (scoped.size === 0) {
5092
- listeners$1.delete(slug);
5221
+ listeners$2.delete(slug);
5093
5222
  revisions.delete(slug);
5094
5223
  }
5095
5224
  };
5096
5225
  }
5097
5226
  function publishWorkspaceGrantChange(slug) {
5098
- const scoped = listeners$1.get(slug);
5227
+ const scoped = listeners$2.get(slug);
5099
5228
  if (scoped !== void 0) {
5100
5229
  revisions.set(slug, workspaceGrantRevision(slug) + 1);
5101
5230
  scoped.forEach((listener) => listener());
@@ -5960,6 +6089,7 @@ window.__ModuleLoader__.load({
5960
6089
  updateModelPreset: (id, expectedRevision, name, orchestrator, assignmentDefault) => updateModelPreset(call, id, expectedRevision, name, orchestrator, assignmentDefault),
5961
6090
  applyModelPreset: (slug, presetId) => applyModelPreset(call, slug, presetId),
5962
6091
  customizeModelPlan: (slug, orchestrator) => customizeModelPlan(call, slug, orchestrator),
6092
+ setStandingLimits: (slug, limits) => setStandingLimits(call, slug, limits),
5963
6093
  setModelPlanAssignments: (slug, expectedRevision, assignmentDefault, assignmentModels) => setModelPlanAssignments(call, slug, expectedRevision, assignmentDefault, assignmentModels),
5964
6094
  listHostFolders(path, signal) {
5965
6095
  if (folderAccess?.listDirectory === void 0) throw new Error("DSH folder browser is unavailable");
@@ -6533,6 +6663,13 @@ window.__ModuleLoader__.load({
6533
6663
  messagingRevoke: (slug, grantId) => revokeMessaging(call, slug, grantId),
6534
6664
  messagingSend: (slug, grantId, requestId, text) => sendMessaging(call, slug, grantId, requestId, text),
6535
6665
  botSourcePolicies: (slug) => loadBotSourcePolicies(call, slug),
6666
+ botSchedules: (slug) => loadBotSchedules(call, slug),
6667
+ createBotSchedule: (slug, input) => createBotSchedule(call, slug, input),
6668
+ updateBotSchedule: (slug, id, change) => updateBotSchedule(call, slug, id, change),
6669
+ deleteBotSchedule: (slug, id) => deleteBotSchedule(call, slug, id),
6670
+ botScheduleHistory: (slug, id) => loadBotScheduleHistory(call, slug, id),
6671
+ runBotScheduleNow: (slug, id) => runBotScheduleNow(call, slug, id),
6672
+ botSchedulePreview: (trigger) => previewBotSchedule(call, trigger),
6536
6673
  setBotSourcePolicy: (slug, edit) => setBotSourcePolicy(call, slug, edit),
6537
6674
  resetBotSourcePolicy: (slug, sourceClass) => resetBotSourcePolicy(call, slug, sourceClass),
6538
6675
  memoryWorkingChanges: (channelId) => loadMemoryWorkingChanges(call, channelId),
@@ -7972,21 +8109,21 @@ window.__ModuleLoader__.load({
7972
8109
  }
7973
8110
  //#endregion
7974
8111
  //#region packages/client/src/client/messaging-defaults-live.ts
7975
- const listeners = /* @__PURE__ */ new Set();
8112
+ const listeners$1 = /* @__PURE__ */ new Set();
7976
8113
  let source;
7977
8114
  const notify = () => {
7978
- for (const listener of [...listeners]) listener();
8115
+ for (const listener of [...listeners$1]) listener();
7979
8116
  };
7980
8117
  function subscribeMessagingDefaults(listener) {
7981
- listeners.add(listener);
8118
+ listeners$1.add(listener);
7982
8119
  if (!source && typeof EventSource !== "undefined") {
7983
8120
  source = new EventSource("/api/botharness/stream?scope=roster");
7984
8121
  source.addEventListener("roster/changed", notify);
7985
8122
  source.addEventListener("open", notify);
7986
8123
  }
7987
8124
  return () => {
7988
- listeners.delete(listener);
7989
- if (!listeners.size) {
8125
+ listeners$1.delete(listener);
8126
+ if (!listeners$1.size) {
7990
8127
  source?.close();
7991
8128
  source = void 0;
7992
8129
  }
@@ -8370,6 +8507,100 @@ window.__ModuleLoader__.load({
8370
8507
  });
8371
8508
  }
8372
8509
  //#endregion
8510
+ //#region packages/client/src/client/telemetry-settings.tsx
8511
+ const LOCK_NOTES = {
8512
+ config: "telemetry.row.lockedConfig",
8513
+ DO_NOT_TRACK: "telemetry.row.lockedDoNotTrack",
8514
+ BOTHARNESS_TELEMETRY: "telemetry.row.lockedEnv"
8515
+ };
8516
+ function TelemetrySettings({ call, t }) {
8517
+ const [status, setStatus] = (0, react.useState)();
8518
+ const [busy, setBusy] = (0, react.useState)(false);
8519
+ const [error, setError] = (0, react.useState)(false);
8520
+ const mounted = (0, react.useRef)(false);
8521
+ const request = (0, react.useRef)(0);
8522
+ const mount = useMountedResource(() => {
8523
+ mounted.current = true;
8524
+ const controller = new AbortController();
8525
+ const version = ++request.current;
8526
+ loadTelemetryStatus(call, controller.signal).then((value) => {
8527
+ if (!controller.signal.aborted && version === request.current) setStatus(value);
8528
+ }).catch(() => {
8529
+ if (!controller.signal.aborted && version === request.current) setError(true);
8530
+ });
8531
+ return () => {
8532
+ mounted.current = false;
8533
+ controller.abort();
8534
+ };
8535
+ }, [call]);
8536
+ const toggle = async (enabled) => {
8537
+ if (status === void 0 || status.lockedBy !== void 0) return;
8538
+ const previous = status;
8539
+ const version = ++request.current;
8540
+ setBusy(true);
8541
+ setError(false);
8542
+ setStatus({
8543
+ ...status,
8544
+ enabled,
8545
+ preference: enabled
8546
+ });
8547
+ try {
8548
+ const value = await setTelemetryPreference(call, enabled);
8549
+ if (mounted.current && version === request.current) setStatus(value);
8550
+ } catch {
8551
+ if (mounted.current && version === request.current) {
8552
+ setStatus(previous);
8553
+ setError(true);
8554
+ }
8555
+ } finally {
8556
+ if (mounted.current) setBusy(false);
8557
+ }
8558
+ };
8559
+ const lockedBy = status?.lockedBy;
8560
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
8561
+ className: "bh-settings-row bh-telemetry-row",
8562
+ ref: mount,
8563
+ "data-telemetry-setting": true,
8564
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
8565
+ className: "bh-settings-row-text",
8566
+ children: [
8567
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
8568
+ className: "bh-settings-row-title",
8569
+ children: t("telemetry.row.title")
8570
+ }),
8571
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
8572
+ className: "bh-settings-row-desc",
8573
+ children: [
8574
+ t("telemetry.row.description"),
8575
+ " ",
8576
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("a", {
8577
+ href: t("telemetry.notice.privacyUrl"),
8578
+ target: "_blank",
8579
+ rel: "noopener noreferrer",
8580
+ children: t("telemetry.row.privacy")
8581
+ })
8582
+ ]
8583
+ }),
8584
+ lockedBy === void 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
8585
+ className: "bh-settings-row-desc",
8586
+ "data-telemetry-lock": lockedBy,
8587
+ children: t(LOCK_NOTES[lockedBy])
8588
+ }),
8589
+ error ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
8590
+ className: "bh-telemetry-row-error",
8591
+ role: "alert",
8592
+ children: t("telemetry.row.error")
8593
+ }) : null
8594
+ ]
8595
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Switch, {
8596
+ checked: status?.enabled ?? false,
8597
+ onChange: (enabled) => void toggle(enabled),
8598
+ disabled: status === void 0 || busy || lockedBy !== void 0,
8599
+ label: t("telemetry.row.title")
8600
+ })]
8601
+ });
8602
+ }
8603
+ //#endregion
8373
8604
  //#region packages/client/src/client/assignment-concurrency-setting.tsx
8374
8605
  function AssignmentConcurrencySetting({ t, limit, writable, save }) {
8375
8606
  const id = (0, react.useId)();
@@ -8802,6 +9033,24 @@ window.__ModuleLoader__.load({
8802
9033
  //#endregion
8803
9034
  //#region packages/client/src/client/release-notes.ts
8804
9035
  const RELEASE_NOTES_SEEN_KEY = "botharness.releaseNotes.seenVersion";
9036
+ const RESTART_POLL_MS = 2e3;
9037
+ const RESTART_POLL_LIMIT = 90;
9038
+ const DEFAULT_RESTART_WATCH = {
9039
+ wait: (ms) => new Promise((resolve) => {
9040
+ setTimeout(resolve, ms);
9041
+ }),
9042
+ reload: () => {
9043
+ globalThis.location?.reload();
9044
+ }
9045
+ };
9046
+ const INSTALL_FAILURES = [
9047
+ "unavailable",
9048
+ "invalid-version",
9049
+ "network",
9050
+ "incompatible",
9051
+ "build-blocked",
9052
+ "failed"
9053
+ ];
8805
9054
  function record(value) {
8806
9055
  return typeof value === "object" && value !== null && !Array.isArray(value) ? value : void 0;
8807
9056
  }
@@ -8835,12 +9084,52 @@ window.__ModuleLoader__.load({
8835
9084
  status: "available",
8836
9085
  current,
8837
9086
  latest,
8838
- releases
9087
+ releases,
9088
+ installable: source["installable"] === true
9089
+ } : void 0;
9090
+ }
9091
+ case "restart-required": {
9092
+ const installed = source["installed"];
9093
+ return typeof installed === "string" ? {
9094
+ status: "restart-required",
9095
+ current,
9096
+ installed,
9097
+ restartable: source["restartable"] === true
8839
9098
  } : void 0;
8840
9099
  }
8841
9100
  default: return;
8842
9101
  }
8843
9102
  }
9103
+ function parseReleaseInstall(value) {
9104
+ const source = record(value);
9105
+ if (source?.["status"] === "installed") {
9106
+ const { current, installed } = source;
9107
+ return typeof current === "string" && typeof installed === "string" ? {
9108
+ status: "installed",
9109
+ current,
9110
+ installed,
9111
+ restartable: source["restartable"] === true
9112
+ } : void 0;
9113
+ }
9114
+ const reason = INSTALL_FAILURES.find((candidate) => candidate === source?.["reason"]);
9115
+ if (source?.["status"] !== "failed" || reason === void 0) return void 0;
9116
+ const { diagnostic, logPath } = source;
9117
+ return {
9118
+ status: "failed",
9119
+ reason,
9120
+ ...typeof diagnostic === "string" ? { diagnostic } : {},
9121
+ ...typeof logPath === "string" ? { logPath } : {}
9122
+ };
9123
+ }
9124
+ function parseReleaseRestart(value) {
9125
+ const source = record(value);
9126
+ if (source?.["status"] === "restarting") return { status: "restarting" };
9127
+ const reason = source?.["reason"];
9128
+ return source?.["status"] === "failed" && (reason === "unavailable" || reason === "not-pending") ? {
9129
+ status: "failed",
9130
+ reason
9131
+ } : void 0;
9132
+ }
8844
9133
  function readSeen(storage) {
8845
9134
  try {
8846
9135
  const value = storage?.getItem(RELEASE_NOTES_SEEN_KEY);
@@ -8853,16 +9142,20 @@ window.__ModuleLoader__.load({
8853
9142
  source;
8854
9143
  call;
8855
9144
  storage;
9145
+ watch;
8856
9146
  started;
8857
- constructor(call, storage) {
9147
+ constructor(call, storage, watch = DEFAULT_RESTART_WATCH) {
8858
9148
  this.call = call;
8859
9149
  this.storage = storage;
9150
+ this.watch = watch;
8860
9151
  this.source = (0, _deepseek_ai_dsh_client_store.createSnapshotStore)({
8861
9152
  version: void 0,
8862
9153
  announcement: void 0,
8863
9154
  firstRun: false,
8864
9155
  viewing: void 0,
8865
- update: { status: "idle" }
9156
+ update: { status: "idle" },
9157
+ install: { status: "idle" },
9158
+ restart: { status: "idle" }
8866
9159
  });
8867
9160
  }
8868
9161
  start() {
@@ -8908,6 +9201,79 @@ window.__ModuleLoader__.load({
8908
9201
  };
8909
9202
  });
8910
9203
  }
9204
+ async install(version) {
9205
+ if (this.source.getSnapshot().install.status === "installing") return;
9206
+ this.source.update((draft) => {
9207
+ draft.install = {
9208
+ status: "installing",
9209
+ version
9210
+ };
9211
+ });
9212
+ let outcome;
9213
+ try {
9214
+ const result = await this.call("releaseInstall", { version });
9215
+ outcome = result.ok ? parseReleaseInstall(result.value) : void 0;
9216
+ } catch {
9217
+ outcome = void 0;
9218
+ }
9219
+ this.source.update((draft) => {
9220
+ if (outcome?.status === "installed") {
9221
+ draft.install = { status: "idle" };
9222
+ draft.update = {
9223
+ status: "restart-required",
9224
+ current: outcome.current,
9225
+ installed: outcome.installed,
9226
+ restartable: outcome.restartable
9227
+ };
9228
+ return;
9229
+ }
9230
+ draft.install = outcome ?? {
9231
+ status: "failed",
9232
+ reason: "failed"
9233
+ };
9234
+ });
9235
+ }
9236
+ async restart() {
9237
+ if (this.source.getSnapshot().restart.status === "restarting") return;
9238
+ this.source.update((draft) => {
9239
+ draft.restart = { status: "restarting" };
9240
+ });
9241
+ const update = this.source.getSnapshot().update;
9242
+ const running = update.status === "restart-required" ? update.current : void 0;
9243
+ let outcome;
9244
+ try {
9245
+ const result = await this.call("releaseRestart", {});
9246
+ outcome = result.ok ? parseReleaseRestart(result.value) : void 0;
9247
+ } catch {
9248
+ outcome = { status: "restarting" };
9249
+ }
9250
+ if (outcome?.status !== "restarting") {
9251
+ this.source.update((draft) => {
9252
+ draft.restart = { status: "failed" };
9253
+ });
9254
+ return;
9255
+ }
9256
+ await this.awaitRestart(running);
9257
+ }
9258
+ async awaitRestart(running) {
9259
+ for (let attempt = 0; attempt < RESTART_POLL_LIMIT; attempt += 1) {
9260
+ await this.watch.wait(RESTART_POLL_MS);
9261
+ let version;
9262
+ try {
9263
+ const result = await this.call("releaseInfo", {});
9264
+ version = result.ok ? parseReleaseInfo(result.value)?.version : void 0;
9265
+ } catch {
9266
+ version = void 0;
9267
+ }
9268
+ if (version !== void 0 && version !== running) {
9269
+ this.watch.reload();
9270
+ return;
9271
+ }
9272
+ }
9273
+ this.source.update((draft) => {
9274
+ draft.restart = { status: "failed" };
9275
+ });
9276
+ }
8911
9277
  async announce() {
8912
9278
  const seen = readSeen(this.storage);
8913
9279
  const result = await this.call("releaseInfo", seen === void 0 ? {} : { since: seen });
@@ -9108,18 +9474,97 @@ window.__ModuleLoader__.load({
9108
9474
  latest: update.latest
9109
9475
  });
9110
9476
  case "current": return t("release.status.current", { version });
9477
+ case "restart-required": return t("release.status.restart", {
9478
+ version,
9479
+ installed: update.installed
9480
+ });
9111
9481
  case "unavailable": return t("release.status.unavailable", { version });
9112
9482
  default: return t("release.status.checking", { version });
9113
9483
  }
9114
9484
  }
9485
+ const INSTALL_FAILURE_KEYS = {
9486
+ unavailable: "release.install.failed.unavailable",
9487
+ "invalid-version": "release.install.failed.generic",
9488
+ network: "release.install.failed.network",
9489
+ incompatible: "release.install.failed.incompatible",
9490
+ "build-blocked": "release.install.failed.buildBlocked",
9491
+ failed: "release.install.failed.generic"
9492
+ };
9493
+ function ManualUpdate({ latest, t }) {
9494
+ const [copied, setCopied] = (0, react.useState)(false);
9495
+ const command = releaseUpdateCommand(latest);
9496
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
9497
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
9498
+ className: "bh-settings-row-desc",
9499
+ children: t("release.update.cli")
9500
+ }),
9501
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
9502
+ className: "bh-release-command",
9503
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("code", { children: command }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
9504
+ size: "sm",
9505
+ variant: "outline",
9506
+ onClick: () => {
9507
+ navigator.clipboard?.writeText(command).then(() => {
9508
+ setCopied(true);
9509
+ });
9510
+ },
9511
+ children: t(copied ? "release.update.copied" : "release.update.copy")
9512
+ })]
9513
+ }),
9514
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
9515
+ className: "bh-settings-row-desc",
9516
+ children: t("release.update.desktop")
9517
+ })
9518
+ ] });
9519
+ }
9520
+ function runsInDesktop() {
9521
+ return "dshDesktop" in globalThis;
9522
+ }
9523
+ function RestartGuide({ restartable, state, onRestart, t }) {
9524
+ if (runsInDesktop()) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
9525
+ className: "bh-settings-row-desc",
9526
+ children: t("release.restart.desktop")
9527
+ });
9528
+ if (!restartable || state.status === "failed") return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
9529
+ state.status === "failed" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
9530
+ className: "bh-release-install-error",
9531
+ role: "alert",
9532
+ "data-release-restart-error": true,
9533
+ children: t("release.restart.failed")
9534
+ }) : null,
9535
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
9536
+ className: "bh-settings-row-desc",
9537
+ children: t("release.restart.web")
9538
+ }),
9539
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
9540
+ className: "bh-settings-row-desc",
9541
+ children: t("release.restart.desktop")
9542
+ })
9543
+ ] });
9544
+ const restarting = state.status === "restarting";
9545
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
9546
+ className: "bh-release-update-head",
9547
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
9548
+ className: "bh-settings-row-desc bh-release-restart-hint",
9549
+ children: t(restarting ? "release.restart.progress" : "release.restart.hint")
9550
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
9551
+ size: "sm",
9552
+ variant: "primary",
9553
+ disabled: restarting,
9554
+ "data-release-restart-action": true,
9555
+ onClick: onRestart,
9556
+ children: t(restarting ? "release.restart.running" : "release.restart.action")
9557
+ })]
9558
+ });
9559
+ }
9115
9560
  function ReleaseSettings({ releaseNotes, t }) {
9116
9561
  const snapshot = useReleaseNotes(releaseNotes);
9117
- const [copied, setCopied] = (0, react.useState)(false);
9118
9562
  const mount = useMountedResource(() => {
9119
9563
  releaseNotes.start().then(() => releaseNotes.checkUpdate());
9120
9564
  }, [releaseNotes]);
9121
9565
  const update = snapshot.update;
9122
- const command = update.status === "available" ? releaseUpdateCommand(update.latest) : void 0;
9566
+ const install = snapshot.install;
9567
+ const installing = install.status === "installing";
9123
9568
  return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
9124
9569
  className: "bh-release-settings",
9125
9570
  ref: mount,
@@ -9150,7 +9595,7 @@ window.__ModuleLoader__.load({
9150
9595
  }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
9151
9596
  size: "sm",
9152
9597
  variant: "outline",
9153
- disabled: update.status === "checking" || update.status === "development",
9598
+ disabled: installing || update.status === "checking" || update.status === "development" || update.status === "restart-required",
9154
9599
  onClick: () => {
9155
9600
  releaseNotes.checkUpdate(true);
9156
9601
  },
@@ -9158,47 +9603,81 @@ window.__ModuleLoader__.load({
9158
9603
  })]
9159
9604
  })]
9160
9605
  }),
9161
- update.status === "available" && command !== void 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
9606
+ update.status === "available" ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
9162
9607
  className: "bh-release-update",
9163
9608
  "data-release-update": update.latest,
9164
9609
  children: [
9165
9610
  /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
9166
9611
  className: "bh-release-update-head",
9167
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
9168
- className: "bh-release-update-title",
9169
- children: t("release.update.title", { latest: update.latest })
9170
- }), update.releases.length > 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
9171
- size: "sm",
9172
- variant: "outline",
9173
- onClick: () => {
9174
- releaseNotes.view(update.releases);
9175
- },
9176
- children: t("release.update.notes")
9177
- }) : null]
9178
- }),
9179
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
9180
- className: "bh-settings-row-desc",
9181
- children: t("release.update.cli")
9182
- }),
9183
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
9184
- className: "bh-release-command",
9185
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("code", { children: command }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
9186
- size: "sm",
9187
- variant: "outline",
9188
- onClick: () => {
9189
- navigator.clipboard?.writeText(command).then(() => {
9190
- setCopied(true);
9191
- });
9192
- },
9193
- children: t(copied ? "release.update.copied" : "release.update.copy")
9194
- })]
9612
+ children: [
9613
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
9614
+ className: "bh-release-update-title",
9615
+ children: t("release.update.title", { latest: update.latest })
9616
+ }),
9617
+ update.releases.length > 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
9618
+ size: "sm",
9619
+ variant: "outline",
9620
+ onClick: () => {
9621
+ releaseNotes.view(update.releases);
9622
+ },
9623
+ children: t("release.update.notes")
9624
+ }) : null,
9625
+ update.installable ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
9626
+ size: "sm",
9627
+ variant: "primary",
9628
+ disabled: installing,
9629
+ "data-release-install": true,
9630
+ onClick: () => {
9631
+ releaseNotes.install(update.latest);
9632
+ },
9633
+ children: t(installing ? "release.install.running" : "release.install.action")
9634
+ }) : null
9635
+ ]
9195
9636
  }),
9196
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
9637
+ update.installable && install.status !== "failed" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
9197
9638
  className: "bh-settings-row-desc",
9198
- children: t("release.update.desktop", { spec: `deepseekbot@${update.latest}` })
9199
- })
9639
+ children: t(installing ? "release.install.progress" : "release.install.hint")
9640
+ }) : null,
9641
+ install.status === "failed" ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
9642
+ className: "bh-release-install-error",
9643
+ role: "alert",
9644
+ "data-release-install-error": true,
9645
+ children: [
9646
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { children: t(INSTALL_FAILURE_KEYS[install.reason]) }),
9647
+ install.diagnostic === void 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("pre", {
9648
+ className: "bh-release-install-diagnostic",
9649
+ children: install.diagnostic
9650
+ }),
9651
+ install.logPath === void 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
9652
+ className: "bh-settings-row-desc",
9653
+ children: t("release.install.log", { path: install.logPath })
9654
+ })
9655
+ ]
9656
+ }) : null,
9657
+ !update.installable || install.status === "failed" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ManualUpdate, {
9658
+ latest: update.latest,
9659
+ t
9660
+ }) : null
9200
9661
  ]
9201
9662
  }) : null,
9663
+ update.status === "restart-required" ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
9664
+ className: "bh-release-update",
9665
+ "data-release-restart": update.installed,
9666
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
9667
+ className: "bh-release-update-head",
9668
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
9669
+ className: "bh-release-update-title",
9670
+ children: t("release.restart.title", { installed: update.installed })
9671
+ })
9672
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(RestartGuide, {
9673
+ restartable: update.restartable,
9674
+ state: snapshot.restart,
9675
+ onRestart: () => {
9676
+ releaseNotes.restart();
9677
+ },
9678
+ t
9679
+ })]
9680
+ }) : null,
9202
9681
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ReleaseNotesDialog, {
9203
9682
  releases: snapshot.viewing,
9204
9683
  title: t("release.update.dialog", { latest: update.status === "available" ? update.latest : "" }),
@@ -9212,6 +9691,64 @@ window.__ModuleLoader__.load({
9212
9691
  });
9213
9692
  }
9214
9693
  //#endregion
9694
+ //#region packages/client/src/client/telemetry-notice.ts
9695
+ const TELEMETRY_NOTICE_SEEN_KEY = "botharness.telemetryNotice.seen";
9696
+ function seen(storage) {
9697
+ try {
9698
+ return storage?.getItem(TELEMETRY_NOTICE_SEEN_KEY) === "1";
9699
+ } catch {
9700
+ return false;
9701
+ }
9702
+ }
9703
+ var TelemetryNoticeController = class {
9704
+ source;
9705
+ call;
9706
+ storage;
9707
+ settings;
9708
+ started;
9709
+ constructor(call, storage, openSettings) {
9710
+ this.call = call;
9711
+ this.storage = storage;
9712
+ this.settings = openSettings;
9713
+ this.source = (0, _deepseek_ai_dsh_client_store.createSnapshotStore)({ open: false });
9714
+ }
9715
+ start(after) {
9716
+ this.started ??= this.check(after).catch((error) => {
9717
+ this.started = void 0;
9718
+ console.warn("botharness: failed to read telemetry status", error);
9719
+ });
9720
+ return this.started;
9721
+ }
9722
+ dismiss() {
9723
+ try {
9724
+ this.storage?.setItem(TELEMETRY_NOTICE_SEEN_KEY, "1");
9725
+ } catch {
9726
+ console.warn("botharness: failed to remember the telemetry notice");
9727
+ }
9728
+ this.source.update((draft) => {
9729
+ draft.open = false;
9730
+ });
9731
+ }
9732
+ get canOpenSettings() {
9733
+ return this.settings !== void 0;
9734
+ }
9735
+ openSettings() {
9736
+ this.dismiss();
9737
+ this.settings?.();
9738
+ }
9739
+ async check(after) {
9740
+ if (seen(this.storage)) return;
9741
+ await after?.catch(() => void 0);
9742
+ const result = await this.call("telemetryStatus", {});
9743
+ if (!result.ok) throw new Error(result.error.message);
9744
+ const value = result.value;
9745
+ if (!(typeof value === "object" && value !== null && value["enabled"] === true)) return;
9746
+ this.source.update((draft) => {
9747
+ draft.open = true;
9748
+ });
9749
+ }
9750
+ };
9751
+ //#endregion
9215
9752
  //#region node_modules/.pnpm/@botharness+botui-core@0.3.0/node_modules/@botharness/botui-core/dist/index.js
9216
9753
  const TAU$1 = Math.PI * 2;
9217
9754
  function clamp$1(v, a, b) {
@@ -10812,7 +11349,7 @@ window.__ModuleLoader__.load({
10812
11349
  "im.senderLabel": "发送人 ID",
10813
11350
  "im.platformLabel": "平台",
10814
11351
  "im.platform.weixin": "微信",
10815
- "im.receiveHintDM": "只接收扫码绑定者的微信私聊文本,进入此 Bot 的 Inbox,不占用本地 DM 历史。",
11352
+ "im.receiveHintDM": "接收扫码绑定者的微信私聊文字,以及已支持的文件、图片和语音转写,进入此 Bot 的 Inbox,不占用本地 DM 历史。",
10816
11353
  "im.receptionDM": "微信私聊接收已连接",
10817
11354
  "im.receiveEnableDM": "接收微信私聊",
10818
11355
  "im.receiveDisableDM": "关闭微信私聊收件",
@@ -10951,10 +11488,16 @@ window.__ModuleLoader__.load({
10951
11488
  "im.reception.connecting": "正在连接群收件",
10952
11489
  "im.reception.receiving": "群消息接收已连接",
10953
11490
  "im.reception.unavailable": "群收件不可用;请检查连接后重新开启",
11491
+ "im.imageRetry": "重新加载图片",
10954
11492
  "im.fileDownload": "下载文件",
10955
11493
  "im.fileDownloading": "正在下载…",
10956
11494
  "im.fileError": "文件无法下载,请检查消息来源和连接权限后重试。",
10957
11495
  "im.sourceTitle": "外部消息",
11496
+ "im.voiceTranscriptPlatform": "微信语音 · 平台转写",
11497
+ "im.voiceTranscriptUnavailable": "微信语音 · 未提供转写",
11498
+ "im.voiceTranscriptUnavailableHint": "微信未提供这条语音的转写文本。当前无法从音频识别内容,请补发文字。",
11499
+ "im.voiceDuration": "{seconds} 秒",
11500
+ "im.voiceItemId": "语音内容 ID:{id}",
10958
11501
  "im.messageDetails": "消息详情",
10959
11502
  "im.originDetails": "来源详情",
10960
11503
  "im.threadLabel": "话题",
@@ -11203,6 +11746,12 @@ window.__ModuleLoader__.load({
11203
11746
  "profile.avatar.change": "更换头像",
11204
11747
  "profile.avatar.design": "设计头像",
11205
11748
  "profile.avatar.designDescription": "选择部件和颜色,保存后应用到所有头像。",
11749
+ "profile.avatar.section": "头像",
11750
+ "profile.avatar.designTitle": "设计像素头像",
11751
+ "profile.avatar.uploadTitle": "上传图片",
11752
+ "profile.avatar.uploadDescription": "支持 PNG、JPEG、WebP,上传后可裁剪。",
11753
+ "profile.avatar.upload": "选择图片",
11754
+ "profile.avatar.current": "使用中",
11206
11755
  "profile.avatar.head": "脸型",
11207
11756
  "profile.avatar.pose": "朝向",
11208
11757
  "profile.avatar.backdrop": "背景装饰",
@@ -11565,6 +12114,22 @@ window.__ModuleLoader__.load({
11565
12114
  "memory.repairing": "正在修复…",
11566
12115
  "memory.repairDone": "已恢复。备份位置:",
11567
12116
  "memory.empty": "还没有记忆文件",
12117
+ "standing.badge": "常驻",
12118
+ "standing.usage": "{chars}/{limit}",
12119
+ "standing.usageTitle": "已用 {percent}%:{chars}/{limit} 字符",
12120
+ "standing.soulTitle": "Soul:每个新 Session 开始时都会放进 system prompt,代表 Bot 是谁。",
12121
+ "standing.coreMemoryTitle": "Core Memory:每个新 Session 开始时都会放进 system prompt,让 Bot 一开始就知道自己记得什么。",
12122
+ "standingLimits.title": "常驻记忆上限",
12123
+ "standingLimits.description": "SOUL.md 和 MEMORY.md 会在每个新 Session 开始时放进 system prompt。超过上限的部分会被截断,并提醒 Bot 精简;文件本身不会被改动。新上限从下一个 Session 或下一次压缩开始生效。",
12124
+ "standingLimits.soul": "Soul",
12125
+ "standingLimits.coreMemory": "Core Memory",
12126
+ "standingLimits.unit": "字符",
12127
+ "standingLimits.size": "约 {chinese} 个汉字,或约 {words} 个英文单词",
12128
+ "standingLimits.range": "请输入 {min} 到 {max} 之间的整数",
12129
+ "standingLimits.save": "保存上限",
12130
+ "standingLimits.saving": "保存中…",
12131
+ "standingLimits.saved": "已保存,下一个 Session 生效",
12132
+ "standingLimits.reset": "恢复默认",
11568
12133
  "memory.binaryPreview": "二进制文件或过大的文件可由 Bot 使用原生工具读取;此处不提供文本预览。",
11569
12134
  "memory.fileMissing": "文件已不存在;刷新记忆文件列表。",
11570
12135
  "memory.save": "保存",
@@ -11648,10 +12213,95 @@ window.__ModuleLoader__.load({
11648
12213
  "inbox.state.needs-repair": "需要修复",
11649
12214
  "inbox.state.handled": "已处理",
11650
12215
  "inbox.state.ignored": "已忽略",
12216
+ "entry.schedules": "定时任务",
12217
+ "inbox.schedule": "定时任务",
12218
+ "schedule.loading": "正在读取定时任务…",
12219
+ "schedule.missing": "这个定时任务已被删除。",
12220
+ "schedule.add": "新建定时任务",
12221
+ "schedule.edit": "编辑定时任务",
12222
+ "schedule.close": "关闭",
12223
+ "schedule.delete": "删除",
12224
+ "schedule.deleteConfirm": "确认删除",
12225
+ "schedule.cancel": "取消",
12226
+ "schedule.create": "创建",
12227
+ "schedule.save": "保存",
12228
+ "schedule.openSession": "打开会话",
12229
+ "schedule.enable": "启用「{title}」",
12230
+ "schedule.form.title": "名称",
12231
+ "schedule.form.titlePlaceholder": "例如:每日早报",
12232
+ "schedule.form.prompt": "要 Bot 做什么",
12233
+ "schedule.form.promptPlaceholder": "到点时交给 Orchestrator 的任务说明",
12234
+ "schedule.form.cadence": "频率",
12235
+ "schedule.form.unit.minutes": "按分钟",
12236
+ "schedule.form.unit.hours": "按小时",
12237
+ "schedule.form.unit.daily": "每天",
12238
+ "schedule.form.time": "时间",
12239
+ "schedule.form.timeZone": "时区",
12240
+ "schedule.form.everyHours": "每隔几小时",
12241
+ "schedule.form.everyMinutes": "每隔几分钟",
12242
+ "schedule.form.cost": "间隔短于 15 分钟会频繁唤醒 Bot,可能产生较多模型费用。",
12243
+ "schedule.state.pending": "待处理",
12244
+ "schedule.state.observed": "已查看",
12245
+ "schedule.state.handled": "已处理",
12246
+ "schedule.state.coalesced": "已合并",
12247
+ "schedule.state.skipped": "已跳过",
12248
+ "schedule.creator.human": "由你创建",
12249
+ "schedule.creator.personabot": "由 Bot 创建",
12250
+ "schedule.locked": "已锁定,Bot 只能查看",
12251
+ "schedule.lock": "锁定「{title}」,Bot 不能修改",
12252
+ "schedule.unlock": "解除「{title}」的锁定",
12253
+ "schedule.form.lock": "锁定",
12254
+ "schedule.form.lockHint": "锁定后,Bot 只能查看这个定时任务,不能修改或删除。",
12255
+ "schedule.next": "下次 {time}",
12256
+ "schedule.paused": "已暂停",
12257
+ "schedule.empty": "还没有定时任务",
12258
+ "schedule.history": "最近触发",
12259
+ "schedule.historyEmpty": "还没有触发记录",
12260
+ "schedule.history.manual": "手动",
12261
+ "schedule.cadence.minute": "每分钟",
12262
+ "schedule.cadence.minutes": "每 {n} 分钟",
12263
+ "schedule.cadence.hour": "每小时",
12264
+ "schedule.cadence.hours": "每 {n} 小时",
12265
+ "schedule.cadence.daily": "每天 {time}",
12266
+ "schedule.cadence.weekly": "{days} {time}",
12267
+ "schedule.cadence.once": "单次 {time}",
12268
+ "schedule.cadence.cron": "Cron {expression}",
12269
+ "schedule.weekday.1": "周一",
12270
+ "schedule.weekday.2": "周二",
12271
+ "schedule.weekday.3": "周三",
12272
+ "schedule.weekday.4": "周四",
12273
+ "schedule.weekday.5": "周五",
12274
+ "schedule.weekday.6": "周六",
12275
+ "schedule.weekday.7": "周日",
12276
+ "schedule.weekdays.workdays": "工作日",
12277
+ "schedule.weekdays.weekend": "周末",
12278
+ "schedule.weekdays.everyDay": "每天",
12279
+ "schedule.weekdays.separator": "、",
12280
+ "schedule.form.unit.weekly": "每周",
12281
+ "schedule.form.unit.once": "单次",
12282
+ "schedule.form.unit.cron": "Cron",
12283
+ "schedule.form.weekdays": "星期",
12284
+ "schedule.form.date": "日期",
12285
+ "schedule.form.cron": "Cron 表达式",
12286
+ "schedule.form.cronHint": "5 段:分 时 日 月 星期,例如 0 9 * * 1-5 表示工作日 9:00。",
12287
+ "schedule.form.onceHint": "触发一次后自动停用。",
12288
+ "schedule.preview": "接下来",
12289
+ "schedule.preview.loading": "正在计算…",
12290
+ "schedule.preview.invalid": "无法按这个设置计算触发时间:{reason}",
12291
+ "schedule.preview.incomplete": "填写完整后显示接下来的触发时间",
12292
+ "schedule.runNow": "立即运行「{title}」",
12293
+ "schedule.runNow.started": "已触发,Bot 马上处理",
12294
+ "schedule.runNow.tooltip": "立即运行一次,不影响下次计划时间",
12295
+ "schedule.lock.tooltip": "锁定,Bot 只能查看不能修改",
12296
+ "schedule.toggle.pause": "暂停这个定时任务",
12297
+ "schedule.toggle.resume": "启用这个定时任务",
12298
+ "schedule.done": "已完成",
11651
12299
  "entry.sessions": "会话",
11652
12300
  "entry.workspaceGrants": "工作区授权",
11653
12301
  "grant.safeDefault": "Bot 可读取有效授权文件夹。开启文件夹的写入权限后,也可直接处理其中的文件;命令执行仍需单独审批。",
11654
12302
  "grant.orchestratorWrite": "允许 Bot 写入此文件夹",
12303
+ "grant.chip.write": "可写",
12304
+ "grant.chip.read": "只读",
11655
12305
  "grant.memory": "Memory Repository",
11656
12306
  "grant.internal": "内部 · 可读写",
11657
12307
  "grant.readAccess": "项目文件夹",
@@ -11829,7 +12479,50 @@ window.__ModuleLoader__.load({
11829
12479
  "release.update.cli": "在终端运行以下命令更新,完成后重启 DSH:",
11830
12480
  "release.update.copy": "复制",
11831
12481
  "release.update.copied": "已复制",
11832
- "release.update.desktop": "桌面版:打开「插件 → 添加插件」,输入 {spec} 安装,然后重启 DSH。"
12482
+ "release.update.desktop": "桌面版:DSH 的插件页暂时不能升级已安装的插件。请在终端运行上面的命令,把 web 换成桌面版使用的 Profile 名(DSH 数据目录 profiles/ 下的文件夹名),然后重启 DSH。",
12483
+ "release.status.restart": "当前运行 {version},已安装 {installed},重启 DSH 后生效",
12484
+ "release.install.action": "立即更新",
12485
+ "release.install.running": "正在更新…",
12486
+ "release.install.hint": "直接在这里安装新版本,装完重启 DSH 即可生效。",
12487
+ "release.install.progress": "正在通过 DSH 插件管理下载并安装,请不要关闭 DSH…",
12488
+ "release.install.failed.unavailable": "当前 DSH 不支持在这里直接更新,请用下面的方式更新。",
12489
+ "release.install.failed.network": "无法从 npm 下载新版本,请检查网络后重试,或用下面的方式更新。",
12490
+ "release.install.failed.incompatible": "新版本与当前 DSH 版本不兼容,请先升级 DSH 再更新。",
12491
+ "release.install.failed.buildBlocked": "安装需要批准依赖的构建脚本,请在 DSH 的插件页处理,或用下面的方式更新。",
12492
+ "release.install.failed.generic": "更新失败,请用下面的方式更新。",
12493
+ "release.install.log": "完整日志:{path}",
12494
+ "release.restart.title": "已更新到 {installed},重启 DSH 后生效",
12495
+ "release.restart.web": "Web:在运行 dsh web 的终端按 Ctrl+C 停止,再运行一次 dsh web,然后刷新页面。",
12496
+ "release.restart.desktop": "桌面版:完全退出 DSH 后重新打开。",
12497
+ "release.restart.action": "立即重启",
12498
+ "release.restart.running": "正在重启…",
12499
+ "release.restart.hint": "重启会中断正在运行的任务,页面会自动重新连接。",
12500
+ "release.restart.progress": "正在重启 DSH,页面会在几秒后自动重新连接…",
12501
+ "release.restart.failed": "没能自动重启,请按下面的方式手动重启。",
12502
+ "telemetry.notice.title": "关于匿名使用统计",
12503
+ "telemetry.notice.description": "DeepSeekBot 默认发送匿名使用统计,帮助我们了解功能的使用情况并改进插件。",
12504
+ "telemetry.notice.collected.label": "收集什么",
12505
+ "telemetry.notice.collected": "插件启动时的插件版本、DSH 版本、操作系统和架构,功能使用次数,以及后台出错时的错误类型和代码位置;只关联一个随机生成的安装 ID。",
12506
+ "telemetry.notice.never.label": "从不收集",
12507
+ "telemetry.notice.never": "名称、Persona 或 Memory 内容、对话内容、文件路径、凭据和 IP 地址。",
12508
+ "telemetry.notice.why.label": "为什么",
12509
+ "telemetry.notice.why": "了解哪些功能真正被使用,决定接下来改进什么。数据只由 DSH 后台发送,不经浏览器。",
12510
+ "telemetry.notice.optOut.label": "如何关闭",
12511
+ "telemetry.notice.optOut": "在「Bot 设置 → 匿名使用统计」中随时关闭,立即生效、无需重启。也可在 BotHarness core 插件配置中设置 `telemetry: false`,或设置环境变量 `DO_NOT_TRACK=1` 或 `BOTHARNESS_TELEMETRY=0`。",
12512
+ "telemetry.notice.settings": "打开 Bot 设置",
12513
+ "telemetry.notice.privacy": "隐私说明",
12514
+ "telemetry.notice.privacyUrl": "https://deepseekbot.botharness.ai/privacy",
12515
+ "telemetry.notice.source": "查看源码",
12516
+ "telemetry.notice.sourceUrl": "https://github.com/BotHarness/BotHarness/tree/main/packages/core/src/telemetry",
12517
+ "telemetry.notice.done": "知道了",
12518
+ "telemetry.notice.close": "关闭",
12519
+ "telemetry.row.title": "匿名使用统计",
12520
+ "telemetry.row.description": "发送插件版本、系统和功能使用次数等匿名数据,帮助改进 DeepSeekBot。",
12521
+ "telemetry.row.privacy": "隐私说明",
12522
+ "telemetry.row.lockedConfig": "已由 core 插件配置 telemetry: false 关闭,需在插件配置中修改。",
12523
+ "telemetry.row.lockedDoNotTrack": "已由环境变量 DO_NOT_TRACK=1 关闭。",
12524
+ "telemetry.row.lockedEnv": "已由环境变量 BOTHARNESS_TELEMETRY=0 关闭。",
12525
+ "telemetry.row.error": "保存失败,请重试。"
11833
12526
  };
11834
12527
  function zhTranslate(key, params) {
11835
12528
  let text = zh[key] ?? key;
@@ -12421,7 +13114,7 @@ My relationship with the Human, established lore and topics to avoid: [fill in].
12421
13114
  "im.senderLabel": "Sender ID",
12422
13115
  "im.platformLabel": "Platform",
12423
13116
  "im.platform.weixin": "WeChat",
12424
- "im.receiveHintDM": "Receive plain text from the QR-paired owner into this Bot’s Inbox, without adding local DM history.",
13117
+ "im.receiveHintDM": "Receive the QR-paired owner’s text and supported files, images and voice transcripts into this Bot’s Inbox, without adding local DM history.",
12425
13118
  "im.receptionDM": "WeChat DM intake connected",
12426
13119
  "im.receiveEnableDM": "Receive WeChat DMs",
12427
13120
  "im.receiveDisableDM": "Disable WeChat DM intake",
@@ -12560,10 +13253,16 @@ My relationship with the Human, established lore and topics to avoid: [fill in].
12560
13253
  "im.reception.connecting": "Connecting group reception",
12561
13254
  "im.reception.receiving": "Group reception connected",
12562
13255
  "im.reception.unavailable": "Group reception unavailable; check the connection and enable again",
13256
+ "im.imageRetry": "Retry image",
12563
13257
  "im.fileDownload": "Download file",
12564
13258
  "im.fileDownloading": "Downloading…",
12565
13259
  "im.fileError": "Unable to download the file. Check source availability and connection permissions before retrying.",
12566
13260
  "im.sourceTitle": "External message",
13261
+ "im.voiceTranscriptPlatform": "WeChat voice · platform transcript",
13262
+ "im.voiceTranscriptUnavailable": "WeChat voice · no transcript",
13263
+ "im.voiceTranscriptUnavailableHint": "WeChat did not provide a transcript for this voice message. Audio recognition is unavailable here; please send the text.",
13264
+ "im.voiceDuration": "{seconds} s",
13265
+ "im.voiceItemId": "Voice item ID: {id}",
12567
13266
  "im.messageDetails": "Message details",
12568
13267
  "im.originDetails": "Origin details",
12569
13268
  "im.threadLabel": "Topic",
@@ -12812,6 +13511,12 @@ My relationship with the Human, established lore and topics to avoid: [fill in].
12812
13511
  "profile.avatar.change": "Change avatar",
12813
13512
  "profile.avatar.design": "Design avatar",
12814
13513
  "profile.avatar.designDescription": "Choose parts and colors, then save to update every avatar.",
13514
+ "profile.avatar.section": "Avatar",
13515
+ "profile.avatar.designTitle": "Design a pixel avatar",
13516
+ "profile.avatar.uploadTitle": "Upload an image",
13517
+ "profile.avatar.uploadDescription": "PNG, JPEG or WebP. You can crop it after uploading.",
13518
+ "profile.avatar.upload": "Choose image",
13519
+ "profile.avatar.current": "In use",
12815
13520
  "profile.avatar.head": "Face",
12816
13521
  "profile.avatar.pose": "Angle",
12817
13522
  "profile.avatar.backdrop": "Backdrop",
@@ -13174,6 +13879,22 @@ My relationship with the Human, established lore and topics to avoid: [fill in].
13174
13879
  "memory.repairing": "Repairing...",
13175
13880
  "memory.repairDone": "Restored. Backup location:",
13176
13881
  "memory.empty": "No memory files yet",
13882
+ "standing.badge": "Standing",
13883
+ "standing.usage": "{chars}/{limit}",
13884
+ "standing.usageTitle": "{percent}% used: {chars}/{limit} chars",
13885
+ "standing.soulTitle": "Soul: part of the system prompt at the start of every new Session; it says who the Bot is.",
13886
+ "standing.coreMemoryTitle": "Core Memory: part of the system prompt at the start of every new Session, so the Bot starts knowing what it remembers.",
13887
+ "standingLimits.title": "Standing memory limits",
13888
+ "standingLimits.description": "SOUL.md and MEMORY.md go into the system prompt at the start of every new Session. Text over a limit is cut off with a note asking the Bot to consolidate; the file itself is not changed. A new limit applies from the next Session or the next compaction.",
13889
+ "standingLimits.soul": "Soul",
13890
+ "standingLimits.coreMemory": "Core Memory",
13891
+ "standingLimits.unit": "chars",
13892
+ "standingLimits.size": "About {chinese} Chinese characters, or about {words} English words",
13893
+ "standingLimits.range": "Enter a whole number from {min} to {max}",
13894
+ "standingLimits.save": "Save limits",
13895
+ "standingLimits.saving": "Saving…",
13896
+ "standingLimits.saved": "Saved. Applies from the next Session",
13897
+ "standingLimits.reset": "Restore defaults",
13177
13898
  "memory.binaryPreview": "The Bot can read binary or oversized files with native tools. Text preview is unavailable here.",
13178
13899
  "memory.fileMissing": "The file is no longer present. Refresh the Memory files list.",
13179
13900
  "memory.save": "Save",
@@ -13257,10 +13978,95 @@ My relationship with the Human, established lore and topics to avoid: [fill in].
13257
13978
  "inbox.state.needs-repair": "Needs repair",
13258
13979
  "inbox.state.handled": "Handled",
13259
13980
  "inbox.state.ignored": "Ignored",
13981
+ "entry.schedules": "Schedules",
13982
+ "inbox.schedule": "Schedule",
13983
+ "schedule.loading": "Loading schedules…",
13984
+ "schedule.missing": "This schedule no longer exists.",
13985
+ "schedule.add": "New schedule",
13986
+ "schedule.edit": "Edit schedule",
13987
+ "schedule.close": "Close",
13988
+ "schedule.delete": "Delete",
13989
+ "schedule.deleteConfirm": "Confirm delete",
13990
+ "schedule.cancel": "Cancel",
13991
+ "schedule.create": "Create",
13992
+ "schedule.save": "Save",
13993
+ "schedule.openSession": "Open session",
13994
+ "schedule.enable": "Enable “{title}”",
13995
+ "schedule.form.title": "Name",
13996
+ "schedule.form.titlePlaceholder": "e.g. Morning briefing",
13997
+ "schedule.form.prompt": "What the Bot should do",
13998
+ "schedule.form.promptPlaceholder": "The task handed to the Orchestrator when it is due",
13999
+ "schedule.form.cadence": "Cadence",
14000
+ "schedule.form.unit.minutes": "Minutes",
14001
+ "schedule.form.unit.hours": "Hours",
14002
+ "schedule.form.unit.daily": "Daily",
14003
+ "schedule.form.time": "Time",
14004
+ "schedule.form.timeZone": "Time zone",
14005
+ "schedule.form.everyHours": "Every N hours",
14006
+ "schedule.form.everyMinutes": "Every N minutes",
14007
+ "schedule.form.cost": "Intervals under 15 minutes wake the Bot often and can add noticeable model cost.",
14008
+ "schedule.state.pending": "Pending",
14009
+ "schedule.state.observed": "Observed",
14010
+ "schedule.state.handled": "Handled",
14011
+ "schedule.state.coalesced": "Coalesced",
14012
+ "schedule.state.skipped": "Skipped",
14013
+ "schedule.creator.human": "Created by you",
14014
+ "schedule.creator.personabot": "Created by the Bot",
14015
+ "schedule.locked": "Locked: read-only for the Bot",
14016
+ "schedule.lock": "Lock “{title}” so the Bot cannot change it",
14017
+ "schedule.unlock": "Unlock “{title}”",
14018
+ "schedule.form.lock": "Lock",
14019
+ "schedule.form.lockHint": "When locked, the Bot can see this schedule but cannot change or delete it.",
14020
+ "schedule.next": "Next {time}",
14021
+ "schedule.paused": "Paused",
14022
+ "schedule.empty": "No schedules yet",
14023
+ "schedule.history": "Recent firings",
14024
+ "schedule.historyEmpty": "No firings yet",
14025
+ "schedule.history.manual": "Manual",
14026
+ "schedule.cadence.minute": "Every minute",
14027
+ "schedule.cadence.minutes": "Every {n} minutes",
14028
+ "schedule.cadence.hour": "Every hour",
14029
+ "schedule.cadence.hours": "Every {n} hours",
14030
+ "schedule.cadence.daily": "Daily at {time}",
14031
+ "schedule.cadence.weekly": "{days} at {time}",
14032
+ "schedule.cadence.once": "Once on {time}",
14033
+ "schedule.cadence.cron": "Cron {expression}",
14034
+ "schedule.weekday.1": "Mon",
14035
+ "schedule.weekday.2": "Tue",
14036
+ "schedule.weekday.3": "Wed",
14037
+ "schedule.weekday.4": "Thu",
14038
+ "schedule.weekday.5": "Fri",
14039
+ "schedule.weekday.6": "Sat",
14040
+ "schedule.weekday.7": "Sun",
14041
+ "schedule.weekdays.workdays": "Weekdays",
14042
+ "schedule.weekdays.weekend": "Weekends",
14043
+ "schedule.weekdays.everyDay": "Every day",
14044
+ "schedule.weekdays.separator": ", ",
14045
+ "schedule.form.unit.weekly": "Weekly",
14046
+ "schedule.form.unit.once": "Once",
14047
+ "schedule.form.unit.cron": "Cron",
14048
+ "schedule.form.weekdays": "Days of the week",
14049
+ "schedule.form.date": "Date",
14050
+ "schedule.form.cron": "Cron expression",
14051
+ "schedule.form.cronHint": "Five fields: minute hour day month weekday. For example 0 9 * * 1-5 is 9:00 on weekdays.",
14052
+ "schedule.form.onceHint": "Turns itself off after it fires once.",
14053
+ "schedule.preview": "Coming up",
14054
+ "schedule.preview.loading": "Calculating…",
14055
+ "schedule.preview.invalid": "Cannot work out the next runs: {reason}",
14056
+ "schedule.preview.incomplete": "Fill in the cadence to see the next runs",
14057
+ "schedule.runNow": "Run “{title}” now",
14058
+ "schedule.runNow.started": "Started, the Bot is on it",
14059
+ "schedule.runNow.tooltip": "Run once now; the next planned run stays the same",
14060
+ "schedule.lock.tooltip": "Lock so the Bot can view but not change it",
14061
+ "schedule.toggle.pause": "Pause this schedule",
14062
+ "schedule.toggle.resume": "Turn this schedule on",
14063
+ "schedule.done": "Done",
13260
14064
  "entry.sessions": "Sessions",
13261
14065
  "entry.workspaceGrants": "Workspace Grants",
13262
14066
  "grant.safeDefault": "The Bot can read authorized folders. Enable write access to let it process files there; commands still require separate approval.",
13263
14067
  "grant.orchestratorWrite": "Allow Bot to write in this folder",
14068
+ "grant.chip.write": "Writable",
14069
+ "grant.chip.read": "Read-only",
13264
14070
  "grant.memory": "Memory Repository",
13265
14071
  "grant.internal": "Internal · read/write",
13266
14072
  "grant.readAccess": "Project folder",
@@ -13438,7 +14244,50 @@ My relationship with the Human, established lore and topics to avoid: [fill in].
13438
14244
  "release.update.cli": "Run this command in a terminal, then restart DSH:",
13439
14245
  "release.update.copy": "Copy",
13440
14246
  "release.update.copied": "Copied",
13441
- "release.update.desktop": "Desktop app: open Plugins → Add plugin, install {spec}, then restart DSH."
14247
+ "release.update.desktop": "Desktop app: the DSH Plugins page cannot upgrade an installed plugin yet. Run the command above with web replaced by the Desktop profile name (the folder under profiles/ in the DSH data directory), then restart DSH.",
14248
+ "release.status.restart": "Running {version}; {installed} is installed and loads after a DSH restart",
14249
+ "release.install.action": "Update now",
14250
+ "release.install.running": "Updating…",
14251
+ "release.install.hint": "Install the new version from here, then restart DSH to load it.",
14252
+ "release.install.progress": "Downloading and installing through the DSH Plugin Manager. Keep DSH running…",
14253
+ "release.install.failed.unavailable": "This DSH cannot update the plugin from here. Use one of the options below.",
14254
+ "release.install.failed.network": "Could not download the new version from npm. Check your network and retry, or use an option below.",
14255
+ "release.install.failed.incompatible": "The new version does not support this DSH version. Update DSH first.",
14256
+ "release.install.failed.buildBlocked": "The install needs dependency build scripts approved. Approve them on the DSH Plugins page, or use an option below.",
14257
+ "release.install.failed.generic": "The update failed. Use one of the options below.",
14258
+ "release.install.log": "Full log: {path}",
14259
+ "release.restart.title": "Updated to {installed}. Restart DSH to finish",
14260
+ "release.restart.web": "Web: press Ctrl+C in the terminal running dsh web, run dsh web again, then reload this page.",
14261
+ "release.restart.desktop": "Desktop app: quit DSH completely and open it again.",
14262
+ "release.restart.action": "Restart now",
14263
+ "release.restart.running": "Restarting…",
14264
+ "release.restart.hint": "Restarting stops running tasks. This page reconnects on its own.",
14265
+ "release.restart.progress": "Restarting DSH. This page reconnects in a few seconds…",
14266
+ "release.restart.failed": "DSH could not restart itself. Restart it by hand as shown below.",
14267
+ "telemetry.notice.title": "Anonymous usage statistics",
14268
+ "telemetry.notice.description": "DeepSeekBot sends anonymous usage statistics by default, so we can see how features are used and improve the plugin.",
14269
+ "telemetry.notice.collected.label": "What",
14270
+ "telemetry.notice.collected": "Plugin version, DSH version, operating system and architecture when the plugin starts, feature usage counts, and the error type and code location when the Host fails, tied only to a randomly generated install ID.",
14271
+ "telemetry.notice.never.label": "Never",
14272
+ "telemetry.notice.never": "Names, Persona or Memory content, conversation text, file paths, credentials or IP addresses.",
14273
+ "telemetry.notice.why.label": "Why",
14274
+ "telemetry.notice.why": "To learn which features are actually used and decide what to improve next. Only the DSH Host sends data, never the browser.",
14275
+ "telemetry.notice.optOut.label": "Turn off",
14276
+ "telemetry.notice.optOut": "Switch it off any time in Bot settings → Anonymous usage statistics; it takes effect at once, without a restart. You can also set `telemetry: false` in the BotHarness core plugin config, or the environment variable `DO_NOT_TRACK=1` or `BOTHARNESS_TELEMETRY=0`.",
14277
+ "telemetry.notice.settings": "Open Bot settings",
14278
+ "telemetry.notice.privacy": "Privacy",
14279
+ "telemetry.notice.privacyUrl": "https://deepseekbot.botharness.ai/en/privacy",
14280
+ "telemetry.notice.source": "View source",
14281
+ "telemetry.notice.sourceUrl": "https://github.com/BotHarness/BotHarness/tree/main/packages/core/src/telemetry",
14282
+ "telemetry.notice.done": "Got it",
14283
+ "telemetry.notice.close": "Close",
14284
+ "telemetry.row.title": "Anonymous usage statistics",
14285
+ "telemetry.row.description": "Send anonymous data such as plugin version, system and feature usage counts to help improve DeepSeekBot.",
14286
+ "telemetry.row.privacy": "Privacy",
14287
+ "telemetry.row.lockedConfig": "Turned off by telemetry: false in the core plugin config; change it there.",
14288
+ "telemetry.row.lockedDoNotTrack": "Turned off by the environment variable DO_NOT_TRACK=1.",
14289
+ "telemetry.row.lockedEnv": "Turned off by the environment variable BOTHARNESS_TELEMETRY=0.",
14290
+ "telemetry.row.error": "Could not save. Try again."
13442
14291
  };
13443
14292
  //#endregion
13444
14293
  //#region node_modules/.pnpm/@botharness+pixel-morph@0.2.0/node_modules/@botharness/pixel-morph/dist/index.js
@@ -16642,6 +17491,7 @@ My relationship with the Human, established lore and topics to avoid: [fill in].
16642
17491
  displayName: marketplaceName(selected),
16643
17492
  gitUrl: selected.cloneUrl,
16644
17493
  roles: selected.roles,
17494
+ origin: "marketplace",
16645
17495
  ...selected.description === null ? {} : { description: selected.description }
16646
17496
  }).then(() => {
16647
17497
  setInstalling(false);
@@ -20851,6 +21701,355 @@ My relationship with the Human, established lore and topics to avoid: [fill in].
20851
21701
  });
20852
21702
  }
20853
21703
  //#endregion
21704
+ //#region packages/client/src/client/channel-sidebar-icon.tsx
21705
+ /**
21706
+ * Vendored Channel sidebar glyphs from lucide-react@1.46.0 (ISC, ADR-0032).
21707
+ *
21708
+ * Source: the matching lucide `dist/esm/icons/*.mjs` glyph data. Vendored at the
21709
+ * stock 2-unit stroke without adding a `lucide-react` runtime dependency.
21710
+ *
21711
+ * ISC License
21712
+ *
21713
+ * Copyright (c) for portions of Lucide are held by Cole Bemis 2013-2022 as part of Feather (MIT).
21714
+ * All other copyright (c) for Lucide are held by Lucide Contributors 2022.
21715
+ *
21716
+ * Permission to use, copy, modify, and/or distribute this software for any
21717
+ * purpose with or without fee is hereby granted, provided that the above
21718
+ * copyright notice and this permission notice appear in all copies.
21719
+ *
21720
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
21721
+ * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
21722
+ * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
21723
+ * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
21724
+ * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
21725
+ * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
21726
+ * PERFORMANCE OF THIS SOFTWARE.
21727
+ */
21728
+ const glyphs = {
21729
+ "alarm-clock": [
21730
+ ["circle", {
21731
+ cx: "12",
21732
+ cy: "13",
21733
+ r: "8"
21734
+ }],
21735
+ ["path", { d: "M12 9v4l2 2" }],
21736
+ ["path", { d: "M5 3 2 6" }],
21737
+ ["path", { d: "m22 6-3-3" }],
21738
+ ["path", { d: "M6.38 18.7 4 21" }],
21739
+ ["path", { d: "M17.64 18.67 20 21" }]
21740
+ ],
21741
+ user: [["path", { d: "M19 21v-2a4 4 0 0 0-4-4H9a4 4 0 0 0-4 4v2" }], ["circle", {
21742
+ cx: "12",
21743
+ cy: "7",
21744
+ r: "4"
21745
+ }]],
21746
+ bot: [
21747
+ ["path", { d: "M12 8V4H8" }],
21748
+ ["rect", {
21749
+ width: "16",
21750
+ height: "12",
21751
+ x: "4",
21752
+ y: "8",
21753
+ rx: "2"
21754
+ }],
21755
+ ["path", { d: "M2 14h2" }],
21756
+ ["path", { d: "M20 14h2" }],
21757
+ ["path", { d: "M15 13v2" }],
21758
+ ["path", { d: "M9 13v2" }]
21759
+ ],
21760
+ "list-checks": [
21761
+ ["path", { d: "M13 5h8" }],
21762
+ ["path", { d: "M13 12h8" }],
21763
+ ["path", { d: "M13 19h8" }],
21764
+ ["path", { d: "m3 17 2 2 4-4" }],
21765
+ ["path", { d: "m3 7 2 2 4-4" }]
21766
+ ],
21767
+ palette: [
21768
+ ["path", { d: "M12 22a1 1 0 0 1 0-20 10 9 0 0 1 10 9 5 5 0 0 1-5 5h-2.25a1.75 1.75 0 0 0-1.4 2.8l.3.4a1.75 1.75 0 0 1-1.4 2.8z" }],
21769
+ ["circle", {
21770
+ cx: "13.5",
21771
+ cy: "6.5",
21772
+ r: ".5",
21773
+ fill: "currentColor"
21774
+ }],
21775
+ ["circle", {
21776
+ cx: "17.5",
21777
+ cy: "10.5",
21778
+ r: ".5",
21779
+ fill: "currentColor"
21780
+ }],
21781
+ ["circle", {
21782
+ cx: "6.5",
21783
+ cy: "12.5",
21784
+ r: ".5",
21785
+ fill: "currentColor"
21786
+ }],
21787
+ ["circle", {
21788
+ cx: "8.5",
21789
+ cy: "7.5",
21790
+ r: ".5",
21791
+ fill: "currentColor"
21792
+ }]
21793
+ ],
21794
+ "image-up": [
21795
+ ["path", { d: "M10.3 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2v10l-3.1-3.1a2 2 0 0 0-2.814.014L6 21" }],
21796
+ ["path", { d: "m14 19.5 3-3 3 3" }],
21797
+ ["path", { d: "M17 22v-5.5" }],
21798
+ ["circle", {
21799
+ cx: "9",
21800
+ cy: "9",
21801
+ r: "2"
21802
+ }]
21803
+ ],
21804
+ lock: [["rect", {
21805
+ width: "18",
21806
+ height: "11",
21807
+ x: "3",
21808
+ y: "11",
21809
+ rx: "2",
21810
+ ry: "2"
21811
+ }], ["path", { d: "M7 11V7a5 5 0 0 1 10 0v4" }]],
21812
+ play: [["path", { d: "M5 5a2 2 0 0 1 3.008-1.728l11.997 6.998a2 2 0 0 1 .003 3.458l-12 7A2 2 0 0 1 5 19z" }]],
21813
+ "lock-open": [["rect", {
21814
+ width: "18",
21815
+ height: "11",
21816
+ x: "3",
21817
+ y: "11",
21818
+ rx: "2",
21819
+ ry: "2"
21820
+ }], ["path", { d: "M7 11V7a5 5 0 0 1 9.9-1" }]],
21821
+ eye: [["path", { d: "M2.062 12.348a1 1 0 0 1 0-.696 10.75 10.75 0 0 1 19.876 0 1 1 0 0 1 0 .696 10.75 10.75 0 0 1-19.876 0" }], ["circle", {
21822
+ cx: "12",
21823
+ cy: "12",
21824
+ r: "3"
21825
+ }]],
21826
+ "eye-off": [
21827
+ ["path", { d: "M10.733 5.076a10.744 10.744 0 0 1 11.205 6.575 1 1 0 0 1 0 .696 10.747 10.747 0 0 1-1.444 2.49" }],
21828
+ ["path", { d: "M14.084 14.158a3 3 0 0 1-4.242-4.242" }],
21829
+ ["path", { d: "M17.479 17.499a10.75 10.75 0 0 1-15.417-5.151 1 1 0 0 1 0-.696 10.75 10.75 0 0 1 4.446-5.143" }],
21830
+ ["path", { d: "m2 2 20 20" }]
21831
+ ],
21832
+ files: [
21833
+ ["path", { d: "M15 2h-4a2 2 0 0 0-2 2v11a2 2 0 0 0 2 2h8a2 2 0 0 0 2-2V8" }],
21834
+ ["path", { d: "M16.706 2.706A2.4 2.4 0 0 0 15 2v5a1 1 0 0 0 1 1h5a2.4 2.4 0 0 0-.706-1.706z" }],
21835
+ ["path", { d: "M5 7a2 2 0 0 0-2 2v11a2 2 0 0 0 2 2h8a2 2 0 0 0 1.732-1" }]
21836
+ ],
21837
+ "git-branch": [
21838
+ ["path", { d: "M15 6a9 9 0 0 0-9 9V3" }],
21839
+ ["circle", {
21840
+ cx: "18",
21841
+ cy: "6",
21842
+ r: "3"
21843
+ }],
21844
+ ["circle", {
21845
+ cx: "6",
21846
+ cy: "18",
21847
+ r: "3"
21848
+ }]
21849
+ ],
21850
+ "messages-square": [["path", { d: "M16 10a2 2 0 0 1-2 2H6.828a2 2 0 0 0-1.414.586l-2.202 2.202A.71.71 0 0 1 2 14.286V4a2 2 0 0 1 2-2h10a2 2 0 0 1 2 2z" }], ["path", { d: "M20 9a2 2 0 0 1 2 2v10.286a.71.71 0 0 1-1.212.502l-2.202-2.202A2 2 0 0 0 17.172 19H10a2 2 0 0 1-2-2v-1" }]],
21851
+ inbox: [["polyline", { points: "22 12 16 12 14 15 10 15 8 12 2 12" }], ["path", { d: "M5.45 5.11 2 12v6a2 2 0 0 0 2 2h16a2 2 0 0 0 2-2v-6l-3.45-6.89A2 2 0 0 0 16.76 4H7.24a2 2 0 0 0-1.79 1.11z" }]],
21852
+ "folder-key": [
21853
+ ["path", { d: "M13 20H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h3.9a2 2 0 0 1 1.69.9l.81 1.2a2 2 0 0 0 1.67.9H20a2 2 0 0 1 2 2v1.36" }],
21854
+ ["path", { d: "M19 12v6" }],
21855
+ ["path", { d: "M19 14h2" }],
21856
+ ["circle", {
21857
+ cx: "19",
21858
+ cy: "20",
21859
+ r: "2"
21860
+ }]
21861
+ ],
21862
+ users: [
21863
+ ["path", { d: "M16 21v-2a4 4 0 0 0-4-4H6a4 4 0 0 0-4 4v2" }],
21864
+ ["path", { d: "M16 3.128a4 4 0 0 1 0 7.744" }],
21865
+ ["path", { d: "M22 21v-2a4 4 0 0 0-3-3.87" }],
21866
+ ["circle", {
21867
+ cx: "9",
21868
+ cy: "7",
21869
+ r: "4"
21870
+ }]
21871
+ ],
21872
+ "settings-2": [
21873
+ ["path", { d: "M14 17H5" }],
21874
+ ["path", { d: "M19 7h-9" }],
21875
+ ["circle", {
21876
+ cx: "17",
21877
+ cy: "17",
21878
+ r: "3"
21879
+ }],
21880
+ ["circle", {
21881
+ cx: "7",
21882
+ cy: "7",
21883
+ r: "3"
21884
+ }]
21885
+ ],
21886
+ settings: [["path", { d: "M9.671 4.136a2.34 2.34 0 0 1 4.659 0 2.34 2.34 0 0 0 3.319 1.915 2.34 2.34 0 0 1 2.33 4.033 2.34 2.34 0 0 0 0 3.831 2.34 2.34 0 0 1-2.33 4.033 2.34 2.34 0 0 0-3.319 1.915 2.34 2.34 0 0 1-4.659 0 2.34 2.34 0 0 0-3.32-1.915 2.34 2.34 0 0 1-2.33-4.033 2.34 2.34 0 0 0 0-3.831A2.34 2.34 0 0 1 6.35 6.051a2.34 2.34 0 0 0 3.319-1.915" }], ["circle", {
21887
+ cx: "12",
21888
+ cy: "12",
21889
+ r: "3"
21890
+ }]],
21891
+ monitor: [
21892
+ ["rect", {
21893
+ width: "20",
21894
+ height: "14",
21895
+ x: "2",
21896
+ y: "3",
21897
+ rx: "2"
21898
+ }],
21899
+ ["line", {
21900
+ x1: "8",
21901
+ x2: "16",
21902
+ y1: "21",
21903
+ y2: "21"
21904
+ }],
21905
+ ["line", {
21906
+ x1: "12",
21907
+ x2: "12",
21908
+ y1: "17",
21909
+ y2: "21"
21910
+ }]
21911
+ ],
21912
+ globe: [
21913
+ ["circle", {
21914
+ cx: "12",
21915
+ cy: "12",
21916
+ r: "10"
21917
+ }],
21918
+ ["path", { d: "M12 2a14.5 14.5 0 0 0 0 20 14.5 14.5 0 0 0 0-20" }],
21919
+ ["path", { d: "M2 12h20" }]
21920
+ ],
21921
+ "panels-top-left": [
21922
+ ["rect", {
21923
+ width: "18",
21924
+ height: "18",
21925
+ x: "3",
21926
+ y: "3",
21927
+ rx: "2"
21928
+ }],
21929
+ ["path", { d: "M3 9h18" }],
21930
+ ["path", { d: "M9 21V9" }]
21931
+ ],
21932
+ "grip-vertical": [
21933
+ ["circle", {
21934
+ cx: "9",
21935
+ cy: "12",
21936
+ r: "1"
21937
+ }],
21938
+ ["circle", {
21939
+ cx: "9",
21940
+ cy: "5",
21941
+ r: "1"
21942
+ }],
21943
+ ["circle", {
21944
+ cx: "9",
21945
+ cy: "19",
21946
+ r: "1"
21947
+ }],
21948
+ ["circle", {
21949
+ cx: "15",
21950
+ cy: "12",
21951
+ r: "1"
21952
+ }],
21953
+ ["circle", {
21954
+ cx: "15",
21955
+ cy: "5",
21956
+ r: "1"
21957
+ }],
21958
+ ["circle", {
21959
+ cx: "15",
21960
+ cy: "19",
21961
+ r: "1"
21962
+ }]
21963
+ ],
21964
+ check: [["path", { d: "M20 6 9 17l-5-5" }]]
21965
+ };
21966
+ function ChannelSidebarIcon({ name = "panels-top-left", size = 16 }) {
21967
+ const nodes = Object.hasOwn(glyphs, name) ? glyphs[name] : glyphs["panels-top-left"];
21968
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("svg", {
21969
+ width: size,
21970
+ height: size,
21971
+ viewBox: "0 0 24 24",
21972
+ fill: "none",
21973
+ stroke: "currentColor",
21974
+ strokeWidth: 2,
21975
+ strokeLinecap: "round",
21976
+ strokeLinejoin: "round",
21977
+ "aria-hidden": "true",
21978
+ children: nodes.map(([tag, attributes], index) => (0, react.createElement)(tag, {
21979
+ ...attributes,
21980
+ key: index
21981
+ }))
21982
+ });
21983
+ }
21984
+ //#endregion
21985
+ //#region packages/client/src/client/sidebar-card.tsx
21986
+ function SidebarCardList({ label, className, children }) {
21987
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("ul", {
21988
+ className: className === void 0 ? "bh-card-list" : "bh-card-list " + className,
21989
+ "aria-label": label,
21990
+ children
21991
+ });
21992
+ }
21993
+ function SidebarCardRow({ icon, iconLabel, title, titleClassName, hint, chips, meta, trailing, detail, onClick, disabled, muted, mainClassName, dialog, expanded, controls, state }) {
21994
+ const body = /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
21995
+ className: "bh-card-icon",
21996
+ role: iconLabel === void 0 ? void 0 : "img",
21997
+ "aria-label": iconLabel,
21998
+ title: iconLabel,
21999
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ChannelSidebarIcon, {
22000
+ name: icon,
22001
+ size: 16
22002
+ })
22003
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
22004
+ className: "bh-card-body",
22005
+ children: [
22006
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
22007
+ className: titleClassName === void 0 ? "bh-card-title" : "bh-card-title " + titleClassName,
22008
+ children: title
22009
+ }),
22010
+ chips === void 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
22011
+ className: "bh-card-chips",
22012
+ children: chips
22013
+ }),
22014
+ meta === void 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
22015
+ className: "bh-card-meta",
22016
+ children: meta
22017
+ })
22018
+ ]
22019
+ })] });
22020
+ const className = mainClassName === void 0 ? "bh-card-main" : "bh-card-main " + mainClassName;
22021
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("li", {
22022
+ className: "bh-card-row",
22023
+ "data-muted": muted === true ? "true" : void 0,
22024
+ "data-state": state,
22025
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
22026
+ className: "bh-card-line",
22027
+ children: [onClick === void 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
22028
+ className,
22029
+ title: hint,
22030
+ children: body
22031
+ }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
22032
+ type: "button",
22033
+ className,
22034
+ title: hint,
22035
+ disabled,
22036
+ "aria-haspopup": dialog === true ? "dialog" : void 0,
22037
+ "aria-expanded": expanded,
22038
+ "aria-controls": controls,
22039
+ onClick,
22040
+ children: body
22041
+ }), trailing === void 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
22042
+ className: "bh-card-trailing",
22043
+ children: trailing
22044
+ })]
22045
+ }), detail === void 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
22046
+ id: controls,
22047
+ className: "bh-card-detail",
22048
+ children: detail
22049
+ })]
22050
+ });
22051
+ }
22052
+ //#endregion
20854
22053
  //#region packages/client/src/client/workspace-grants-entry.tsx
20855
22054
  const workspaceCaches = /* @__PURE__ */ new WeakMap();
20856
22055
  function cachedWorkspace(actions, botSlug) {
@@ -20895,45 +22094,38 @@ My relationship with the Human, established lore and topics to avoid: [fill in].
20895
22094
  } catch {}
20896
22095
  return rule.scopeKey;
20897
22096
  }
20898
- function FolderRow({ name, path, remove, removeLabel, disabled, detail, pathAction }) {
22097
+ function FolderRow({ name, path, icon = "folder-key", chips, remove, removeLabel, disabled, detail, pathAction }) {
20899
22098
  const [expanded, setExpanded] = (0, react.useState)(false);
20900
22099
  const detailId = (0, react.useId)();
20901
- return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
20902
- className: "bh-workspace-folder-row",
20903
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
20904
- className: "bh-workspace-folder-main",
20905
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
20906
- type: "button",
20907
- className: "bh-workspace-folder-toggle",
20908
- "aria-expanded": expanded,
20909
- "aria-controls": detailId,
20910
- onClick: () => setExpanded((value) => !value),
20911
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconChevronDownOutlineRegular, {
20912
- size: 14,
20913
- className: expanded ? "bh-workspace-folder-chevron bh-expanded" : "bh-workspace-folder-chevron"
20914
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
20915
- className: "bh-workspace-folder-name",
20916
- title: name,
20917
- children: name
20918
- })]
20919
- }), remove === void 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
20920
- type: "button",
20921
- className: "bh-workspace-folder-remove",
20922
- "aria-label": removeLabel,
20923
- title: removeLabel,
20924
- disabled,
20925
- onClick: remove,
20926
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconCloseOutlineRegular, { size: 16 })
20927
- })]
20928
- }), expanded ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
20929
- id: detailId,
22100
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(SidebarCardRow, {
22101
+ icon,
22102
+ title: name,
22103
+ hint: name,
22104
+ mainClassName: "bh-workspace-folder-toggle",
22105
+ expanded,
22106
+ controls: detailId,
22107
+ onClick: () => setExpanded((value) => !value),
22108
+ ...chips === void 0 ? {} : { chips },
22109
+ trailing: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [remove === void 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
22110
+ type: "button",
22111
+ className: "bh-workspace-folder-remove",
22112
+ "aria-label": removeLabel,
22113
+ title: removeLabel,
22114
+ disabled,
22115
+ onClick: remove,
22116
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconCloseOutlineRegular, { size: 16 })
22117
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconChevronDownOutlineRegular, {
22118
+ size: 14,
22119
+ className: expanded ? "bh-workspace-folder-chevron bh-expanded" : "bh-workspace-folder-chevron"
22120
+ })] }),
22121
+ ...expanded ? { detail: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
20930
22122
  className: "bh-workspace-folder-detail",
20931
22123
  children: [pathAction ?? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
20932
22124
  className: "bh-workspace-folder-path",
20933
22125
  title: path,
20934
22126
  children: path
20935
22127
  }), detail]
20936
- }) : null]
22128
+ }) } : {}
20937
22129
  });
20938
22130
  }
20939
22131
  function pickerUnavailable(cause) {
@@ -21189,9 +22381,11 @@ My relationship with the Human, established lore and topics to avoid: [fill in].
21189
22381
  role: "alert",
21190
22382
  children: error
21191
22383
  }),
21192
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
22384
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(SidebarCardList, {
21193
22385
  className: "bh-workspace-folder-table",
22386
+ label: t("entry.workspaceGrants"),
21194
22387
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(FolderRow, {
22388
+ icon: "git-branch",
21195
22389
  name: t("grant.memory"),
21196
22390
  path: memoryDir ?? "",
21197
22391
  pathAction: memoryDir === void 0 ? void 0 : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(MemoryFileActionButton, {
@@ -21205,6 +22399,13 @@ My relationship with the Human, established lore and topics to avoid: [fill in].
21205
22399
  }), active.map((grant) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(FolderRow, {
21206
22400
  name: grant.workspaceTitle,
21207
22401
  path: grant.workspacePath,
22402
+ chips: grant.orchestratorWrite === true ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Tag, {
22403
+ tone: "success",
22404
+ children: t("grant.chip.write")
22405
+ }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Tag, {
22406
+ tone: "outline",
22407
+ children: t("grant.chip.read")
22408
+ }),
21208
22409
  pathAction: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(WorkspaceFileActionButton, {
21209
22410
  actions,
21210
22411
  slug: botSlug,
@@ -21281,7 +22482,7 @@ My relationship with the Human, established lore and topics to avoid: [fill in].
21281
22482
  }) : null,
21282
22483
  !developerMode || available.length === 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("details", {
21283
22484
  className: "bh-workspace-folder-secondary",
21284
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("summary", { children: t("grant.registeredFolders") }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
22485
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("summary", { children: t("grant.registeredFolders") }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(SidebarCardList, {
21285
22486
  className: "bh-workspace-folder-table",
21286
22487
  children: available.map((workspace) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(FolderRow, {
21287
22488
  name: workspace.title,
@@ -21302,7 +22503,7 @@ My relationship with the Human, established lore and topics to avoid: [fill in].
21302
22503
  " (",
21303
22504
  revoked.length,
21304
22505
  ")"
21305
- ] }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
22506
+ ] }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(SidebarCardList, {
21306
22507
  className: "bh-workspace-folder-table",
21307
22508
  children: revoked.map((grant) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(FolderRow, {
21308
22509
  name: grant.workspaceTitle,
@@ -21310,26 +22511,26 @@ My relationship with the Human, established lore and topics to avoid: [fill in].
21310
22511
  }, grant.id))
21311
22512
  })]
21312
22513
  }),
21313
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
21314
- className: "bh-assignment-access-row",
21315
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
21316
- className: "bh-grant-request-title",
21317
- children: t("access.title")
21318
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
21319
- className: "bh-note",
21320
- children: t("access.description")
21321
- })] }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Switch, {
21322
- checked: access?.mode === "danger-full-access",
21323
- disabled: loading || busy !== void 0,
21324
- onChange: (checked) => {
21325
- if (checked) setConfirmDanger(true);
21326
- else {
21327
- setConfirmDanger(false);
21328
- mutate("safe-access", () => actions.setAssignmentAccess(botSlug, "workspace-write", false));
21329
- }
21330
- },
21331
- label: t("access.title")
21332
- })]
22514
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(SidebarCardList, {
22515
+ className: "bh-assignment-access-card",
22516
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(SidebarCardRow, {
22517
+ icon: "lock",
22518
+ title: t("access.title"),
22519
+ meta: t("access.description"),
22520
+ state: access?.mode,
22521
+ trailing: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Switch, {
22522
+ checked: access?.mode === "danger-full-access",
22523
+ disabled: loading || busy !== void 0,
22524
+ onChange: (checked) => {
22525
+ if (checked) setConfirmDanger(true);
22526
+ else {
22527
+ setConfirmDanger(false);
22528
+ mutate("safe-access", () => actions.setAssignmentAccess(botSlug, "workspace-write", false));
22529
+ }
22530
+ },
22531
+ label: t("access.title")
22532
+ })
22533
+ })
21333
22534
  }),
21334
22535
  access?.mode === "danger-full-access" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
21335
22536
  className: "bh-access-warning",
@@ -32154,195 +33355,6 @@ My relationship with the Human, established lore and topics to avoid: [fill in].
32154
33355
  return input.docked ? "dock" : "hidden";
32155
33356
  }
32156
33357
  //#endregion
32157
- //#region packages/client/src/client/channel-sidebar-icon.tsx
32158
- /**
32159
- * Vendored Channel sidebar glyphs from lucide-react@1.46.0 (ISC, ADR-0032).
32160
- *
32161
- * Source: the matching lucide `dist/esm/icons/*.mjs` glyph data. Vendored at the
32162
- * stock 2-unit stroke without adding a `lucide-react` runtime dependency.
32163
- *
32164
- * ISC License
32165
- *
32166
- * Copyright (c) for portions of Lucide are held by Cole Bemis 2013-2022 as part of Feather (MIT).
32167
- * All other copyright (c) for Lucide are held by Lucide Contributors 2022.
32168
- *
32169
- * Permission to use, copy, modify, and/or distribute this software for any
32170
- * purpose with or without fee is hereby granted, provided that the above
32171
- * copyright notice and this permission notice appear in all copies.
32172
- *
32173
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
32174
- * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
32175
- * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
32176
- * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
32177
- * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
32178
- * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
32179
- * PERFORMANCE OF THIS SOFTWARE.
32180
- */
32181
- const glyphs = {
32182
- eye: [["path", { d: "M2.062 12.348a1 1 0 0 1 0-.696 10.75 10.75 0 0 1 19.876 0 1 1 0 0 1 0 .696 10.75 10.75 0 0 1-19.876 0" }], ["circle", {
32183
- cx: "12",
32184
- cy: "12",
32185
- r: "3"
32186
- }]],
32187
- "eye-off": [
32188
- ["path", { d: "M10.733 5.076a10.744 10.744 0 0 1 11.205 6.575 1 1 0 0 1 0 .696 10.747 10.747 0 0 1-1.444 2.49" }],
32189
- ["path", { d: "M14.084 14.158a3 3 0 0 1-4.242-4.242" }],
32190
- ["path", { d: "M17.479 17.499a10.75 10.75 0 0 1-15.417-5.151 1 1 0 0 1 0-.696 10.75 10.75 0 0 1 4.446-5.143" }],
32191
- ["path", { d: "m2 2 20 20" }]
32192
- ],
32193
- files: [
32194
- ["path", { d: "M15 2h-4a2 2 0 0 0-2 2v11a2 2 0 0 0 2 2h8a2 2 0 0 0 2-2V8" }],
32195
- ["path", { d: "M16.706 2.706A2.4 2.4 0 0 0 15 2v5a1 1 0 0 0 1 1h5a2.4 2.4 0 0 0-.706-1.706z" }],
32196
- ["path", { d: "M5 7a2 2 0 0 0-2 2v11a2 2 0 0 0 2 2h8a2 2 0 0 0 1.732-1" }]
32197
- ],
32198
- "git-branch": [
32199
- ["path", { d: "M15 6a9 9 0 0 0-9 9V3" }],
32200
- ["circle", {
32201
- cx: "18",
32202
- cy: "6",
32203
- r: "3"
32204
- }],
32205
- ["circle", {
32206
- cx: "6",
32207
- cy: "18",
32208
- r: "3"
32209
- }]
32210
- ],
32211
- "messages-square": [["path", { d: "M16 10a2 2 0 0 1-2 2H6.828a2 2 0 0 0-1.414.586l-2.202 2.202A.71.71 0 0 1 2 14.286V4a2 2 0 0 1 2-2h10a2 2 0 0 1 2 2z" }], ["path", { d: "M20 9a2 2 0 0 1 2 2v10.286a.71.71 0 0 1-1.212.502l-2.202-2.202A2 2 0 0 0 17.172 19H10a2 2 0 0 1-2-2v-1" }]],
32212
- inbox: [["polyline", { points: "22 12 16 12 14 15 10 15 8 12 2 12" }], ["path", { d: "M5.45 5.11 2 12v6a2 2 0 0 0 2 2h16a2 2 0 0 0 2-2v-6l-3.45-6.89A2 2 0 0 0 16.76 4H7.24a2 2 0 0 0-1.79 1.11z" }]],
32213
- "folder-key": [
32214
- ["path", { d: "M13 20H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h3.9a2 2 0 0 1 1.69.9l.81 1.2a2 2 0 0 0 1.67.9H20a2 2 0 0 1 2 2v1.36" }],
32215
- ["path", { d: "M19 12v6" }],
32216
- ["path", { d: "M19 14h2" }],
32217
- ["circle", {
32218
- cx: "19",
32219
- cy: "20",
32220
- r: "2"
32221
- }]
32222
- ],
32223
- users: [
32224
- ["path", { d: "M16 21v-2a4 4 0 0 0-4-4H6a4 4 0 0 0-4 4v2" }],
32225
- ["path", { d: "M16 3.128a4 4 0 0 1 0 7.744" }],
32226
- ["path", { d: "M22 21v-2a4 4 0 0 0-3-3.87" }],
32227
- ["circle", {
32228
- cx: "9",
32229
- cy: "7",
32230
- r: "4"
32231
- }]
32232
- ],
32233
- "settings-2": [
32234
- ["path", { d: "M14 17H5" }],
32235
- ["path", { d: "M19 7h-9" }],
32236
- ["circle", {
32237
- cx: "17",
32238
- cy: "17",
32239
- r: "3"
32240
- }],
32241
- ["circle", {
32242
- cx: "7",
32243
- cy: "7",
32244
- r: "3"
32245
- }]
32246
- ],
32247
- settings: [["path", { d: "M9.671 4.136a2.34 2.34 0 0 1 4.659 0 2.34 2.34 0 0 0 3.319 1.915 2.34 2.34 0 0 1 2.33 4.033 2.34 2.34 0 0 0 0 3.831 2.34 2.34 0 0 1-2.33 4.033 2.34 2.34 0 0 0-3.319 1.915 2.34 2.34 0 0 1-4.659 0 2.34 2.34 0 0 0-3.32-1.915 2.34 2.34 0 0 1-2.33-4.033 2.34 2.34 0 0 0 0-3.831A2.34 2.34 0 0 1 6.35 6.051a2.34 2.34 0 0 0 3.319-1.915" }], ["circle", {
32248
- cx: "12",
32249
- cy: "12",
32250
- r: "3"
32251
- }]],
32252
- monitor: [
32253
- ["rect", {
32254
- width: "20",
32255
- height: "14",
32256
- x: "2",
32257
- y: "3",
32258
- rx: "2"
32259
- }],
32260
- ["line", {
32261
- x1: "8",
32262
- x2: "16",
32263
- y1: "21",
32264
- y2: "21"
32265
- }],
32266
- ["line", {
32267
- x1: "12",
32268
- x2: "12",
32269
- y1: "17",
32270
- y2: "21"
32271
- }]
32272
- ],
32273
- globe: [
32274
- ["circle", {
32275
- cx: "12",
32276
- cy: "12",
32277
- r: "10"
32278
- }],
32279
- ["path", { d: "M12 2a14.5 14.5 0 0 0 0 20 14.5 14.5 0 0 0 0-20" }],
32280
- ["path", { d: "M2 12h20" }]
32281
- ],
32282
- "panels-top-left": [
32283
- ["rect", {
32284
- width: "18",
32285
- height: "18",
32286
- x: "3",
32287
- y: "3",
32288
- rx: "2"
32289
- }],
32290
- ["path", { d: "M3 9h18" }],
32291
- ["path", { d: "M9 21V9" }]
32292
- ],
32293
- "grip-vertical": [
32294
- ["circle", {
32295
- cx: "9",
32296
- cy: "12",
32297
- r: "1"
32298
- }],
32299
- ["circle", {
32300
- cx: "9",
32301
- cy: "5",
32302
- r: "1"
32303
- }],
32304
- ["circle", {
32305
- cx: "9",
32306
- cy: "19",
32307
- r: "1"
32308
- }],
32309
- ["circle", {
32310
- cx: "15",
32311
- cy: "12",
32312
- r: "1"
32313
- }],
32314
- ["circle", {
32315
- cx: "15",
32316
- cy: "5",
32317
- r: "1"
32318
- }],
32319
- ["circle", {
32320
- cx: "15",
32321
- cy: "19",
32322
- r: "1"
32323
- }]
32324
- ],
32325
- check: [["path", { d: "M20 6 9 17l-5-5" }]]
32326
- };
32327
- function ChannelSidebarIcon({ name = "panels-top-left", size = 16 }) {
32328
- const nodes = Object.hasOwn(glyphs, name) ? glyphs[name] : glyphs["panels-top-left"];
32329
- return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("svg", {
32330
- width: size,
32331
- height: size,
32332
- viewBox: "0 0 24 24",
32333
- fill: "none",
32334
- stroke: "currentColor",
32335
- strokeWidth: 2,
32336
- strokeLinecap: "round",
32337
- strokeLinejoin: "round",
32338
- "aria-hidden": "true",
32339
- children: nodes.map(([tag, attributes], index) => (0, react.createElement)(tag, {
32340
- ...attributes,
32341
- key: index
32342
- }))
32343
- });
32344
- }
32345
- //#endregion
32346
33358
  //#region packages/client/src/client/channel-sidebar-order.ts
32347
33359
  function resolveEntryOrder(registered, saved) {
32348
33360
  return [.../* @__PURE__ */ new Set([...saved, ...registered])];
@@ -33344,7 +34356,7 @@ My relationship with the Human, established lore and topics to avoid: [fill in].
33344
34356
  setSending(true);
33345
34357
  setError(void 0);
33346
34358
  try {
33347
- const path = detail?.files.find((file) => file.path.endsWith(".md"))?.path ?? "PERSONA.md";
34359
+ const path = detail?.files.find((file) => file.path.endsWith(".md"))?.path ?? "SOUL.md";
33348
34360
  if (!await actions.send(t("memory.continuePrompt", {
33349
34361
  sha,
33350
34362
  branch: JSON.stringify(branch.trim()),
@@ -36157,7 +37169,7 @@ My relationship with the Human, established lore and topics to avoid: [fill in].
36157
37169
  })
36158
37170
  });
36159
37171
  }
36160
- function AvatarAppearanceEditor({ bot, channelId, onSave, t }) {
37172
+ function AvatarAppearanceEditor({ bot, channelId, onSave, onUpload, onRemoveImage, imageBusy = false, t }) {
36161
37173
  const [drafts, setDrafts] = (0, react.useState)();
36162
37174
  const [family, setFamily] = (0, react.useState)("illustrated");
36163
37175
  const [category, setCategory] = (0, react.useState)("hair");
@@ -36199,200 +37211,247 @@ My relationship with the Human, established lore and topics to avoid: [fill in].
36199
37211
  const set = (key, value) => {
36200
37212
  if (draft) update(withPart(draft, key, value));
36201
37213
  };
37214
+ const current = bot.avatar !== void 0 && bot.appearance === void 0 ? "image" : "design";
37215
+ const inUse = /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Tag, {
37216
+ tone: "success",
37217
+ children: t("profile.avatar.current")
37218
+ });
36202
37219
  return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
36203
- className: "bh-avatar-editor",
36204
- "aria-label": t("profile.avatar.design"),
36205
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
36206
- className: "bh-avatar-editor-preview",
36207
- "data-avatar-preview": true,
36208
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(PersonaBotAvatar, {
36209
- personaBotId: bot.slug,
36210
- name: bot.displayName,
36211
- src: bot.avatar,
36212
- appearance: recipe ? {
36213
- recipe,
36214
- revision: bot.appearance?.revision ?? "0".repeat(64)
36215
- } : void 0,
36216
- size: 160,
36217
- state,
36218
- activity: bot.activity,
36219
- attention: bot.attention,
36220
- indicator: false,
36221
- t
36222
- })
37220
+ className: "bh-profile-section bh-avatar-section",
37221
+ "aria-label": t("profile.avatar.section"),
37222
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h2", {
37223
+ className: "bh-profile-section-title",
37224
+ children: t("profile.avatar.section")
36223
37225
  }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
36224
- className: "bh-avatar-editor-controls",
36225
- children: [
36226
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("h3", {
36227
- className: "bh-avatar-editor-title",
36228
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: t("profile.avatar.design") }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(PersonaBotStatusBadges, {
36229
- state,
36230
- attention: bot.attention
36231
- })]
36232
- }),
36233
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", { children: t("profile.avatar.designDescription") }),
36234
- bot.appearanceUnsupported && !draft ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
36235
- className: "bh-avatar-unsupported",
36236
- "data-avatar-unsupported": true,
36237
- role: "note",
36238
- children: t("profile.avatar.unsupported")
36239
- }) : null,
36240
- draft && fields ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("fieldset", {
36241
- disabled: busy,
36242
- className: "bh-avatar-editor-fields",
36243
- children: [
36244
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
36245
- className: "bh-avatar-families",
36246
- role: "radiogroup",
36247
- "aria-label": t("profile.avatar.family"),
36248
- children: AVATAR_FAMILIES.map((value) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
36249
- type: "button",
36250
- role: "radio",
36251
- "data-avatar-family": value,
36252
- "aria-checked": family === value,
36253
- onClick: () => choose(value),
36254
- children: t(`profile.avatar.family.${value}`)
36255
- }, value))
36256
- }),
36257
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
36258
- className: "bh-avatar-categories",
36259
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
36260
- role: "tablist",
36261
- "aria-label": t("profile.avatar.parts"),
36262
- children: spec.categories.map((key) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
37226
+ className: "bh-avatar-editor",
37227
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
37228
+ className: "bh-avatar-editor-preview",
37229
+ "data-avatar-preview": true,
37230
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(PersonaBotAvatar, {
37231
+ personaBotId: bot.slug,
37232
+ name: bot.displayName,
37233
+ src: bot.avatar,
37234
+ appearance: recipe ? {
37235
+ recipe,
37236
+ revision: bot.appearance?.revision ?? "0".repeat(64)
37237
+ } : void 0,
37238
+ size: 160,
37239
+ state,
37240
+ activity: bot.activity,
37241
+ attention: bot.attention,
37242
+ indicator: false,
37243
+ t
37244
+ })
37245
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
37246
+ className: "bh-avatar-editor-controls",
37247
+ children: [
37248
+ draft ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("h3", {
37249
+ className: "bh-avatar-editor-title",
37250
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: t("profile.avatar.designTitle") }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(PersonaBotStatusBadges, {
37251
+ state,
37252
+ attention: bot.attention
37253
+ })]
37254
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", { children: t("profile.avatar.designDescription") })] }) : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [bot.appearanceUnsupported ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
37255
+ className: "bh-avatar-unsupported",
37256
+ "data-avatar-unsupported": true,
37257
+ role: "note",
37258
+ children: t("profile.avatar.unsupported")
37259
+ }) : null, /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(SidebarCardList, {
37260
+ className: "bh-avatar-methods",
37261
+ label: t("profile.avatar.section"),
37262
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(SidebarCardRow, {
37263
+ icon: "palette",
37264
+ title: t("profile.avatar.designTitle"),
37265
+ chips: current === "design" ? inUse : void 0,
37266
+ meta: t("profile.avatar.designDescription"),
37267
+ state: current === "design" ? "current" : void 0,
37268
+ detail: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
37269
+ className: "bh-profile-avatar-actions",
37270
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
37271
+ "data-avatar-edit": true,
36263
37272
  type: "button",
36264
- role: "tab",
36265
- id: `bh-avatar-tab-${key}`,
36266
- "data-avatar-category": key,
36267
- "aria-selected": category === key,
36268
- "aria-controls": "bh-avatar-panel",
36269
- tabIndex: category === key ? 0 : -1,
36270
- onClick: () => setCategory(key),
36271
- onKeyDown: (event) => {
36272
- const step = event.key === "ArrowRight" ? 1 : event.key === "ArrowLeft" ? -1 : 0;
36273
- if (!step) return;
36274
- event.preventDefault();
36275
- const list = spec.categories;
36276
- const next = list[(list.indexOf(key) + step + list.length) % list.length];
36277
- setCategory(next);
36278
- event.currentTarget.parentElement?.querySelector(`[data-avatar-category="${next}"]`)?.focus();
36279
- },
36280
- children: t(`profile.avatar.${key}`)
37273
+ className: "bh-profile-action",
37274
+ disabled: bot.appearanceUnsupported === true,
37275
+ onClick: start,
37276
+ children: t("profile.avatar.design")
37277
+ })
37278
+ })
37279
+ }), onUpload === void 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(SidebarCardRow, {
37280
+ icon: "image-up",
37281
+ title: t("profile.avatar.uploadTitle"),
37282
+ chips: current === "image" ? inUse : void 0,
37283
+ meta: t("profile.avatar.uploadDescription"),
37284
+ state: current === "image" ? "current" : void 0,
37285
+ detail: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
37286
+ className: "bh-profile-avatar-actions",
37287
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
37288
+ "data-avatar-upload": true,
37289
+ type: "button",
37290
+ className: "bh-profile-action",
37291
+ disabled: imageBusy,
37292
+ onClick: onUpload,
37293
+ children: t("profile.avatar.upload")
37294
+ }), bot.avatar === void 0 || onRemoveImage === void 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
37295
+ "data-avatar-remove": true,
37296
+ type: "button",
37297
+ className: "bh-profile-action",
37298
+ disabled: imageBusy,
37299
+ onClick: onRemoveImage,
37300
+ children: t("profile.avatar.remove")
37301
+ })]
37302
+ })
37303
+ })]
37304
+ })] }),
37305
+ draft && fields ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("fieldset", {
37306
+ disabled: busy,
37307
+ className: "bh-avatar-editor-fields",
37308
+ children: [
37309
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
37310
+ className: "bh-avatar-families",
37311
+ role: "radiogroup",
37312
+ "aria-label": t("profile.avatar.family"),
37313
+ children: AVATAR_FAMILIES.map((value) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
37314
+ type: "button",
37315
+ role: "radio",
37316
+ "data-avatar-family": value,
37317
+ "aria-checked": family === value,
37318
+ onClick: () => choose(value),
37319
+ children: t(`profile.avatar.family.${value}`)
37320
+ }, value))
37321
+ }),
37322
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
37323
+ className: "bh-avatar-categories",
37324
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
37325
+ role: "tablist",
37326
+ "aria-label": t("profile.avatar.parts"),
37327
+ children: spec.categories.map((key) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
37328
+ type: "button",
37329
+ role: "tab",
37330
+ id: `bh-avatar-tab-${key}`,
37331
+ "data-avatar-category": key,
37332
+ "aria-selected": category === key,
37333
+ "aria-controls": "bh-avatar-panel",
37334
+ tabIndex: category === key ? 0 : -1,
37335
+ onClick: () => setCategory(key),
37336
+ onKeyDown: (event) => {
37337
+ const step = event.key === "ArrowRight" ? 1 : event.key === "ArrowLeft" ? -1 : 0;
37338
+ if (!step) return;
37339
+ event.preventDefault();
37340
+ const list = spec.categories;
37341
+ const next = list[(list.indexOf(key) + step + list.length) % list.length];
37342
+ setCategory(next);
37343
+ event.currentTarget.parentElement?.querySelector(`[data-avatar-category="${next}"]`)?.focus();
37344
+ },
37345
+ children: t(`profile.avatar.${key}`)
37346
+ }, key))
37347
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
37348
+ type: "button",
37349
+ "data-avatar-shuffle": true,
37350
+ className: "bh-avatar-shuffle",
37351
+ onClick: () => update(shuffled(draft)),
37352
+ children: t("profile.avatar.shuffle")
37353
+ })]
37354
+ }),
37355
+ category === "colors" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
37356
+ className: "bh-avatar-colors",
37357
+ role: "tabpanel",
37358
+ id: "bh-avatar-panel",
37359
+ "aria-labelledby": "bh-avatar-tab-colors",
37360
+ children: spec.colors.map((key) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
37361
+ className: "bh-avatar-color-row",
37362
+ children: [
37363
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: t(`profile.avatar.${key}`) }),
37364
+ spec.swatches[key].map((value) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
37365
+ type: "button",
37366
+ className: "bh-avatar-swatch",
37367
+ "data-avatar-option": `${key}:${value}`,
37368
+ "aria-pressed": fields[key] === value,
37369
+ "aria-label": `${t(`profile.avatar.${key}`)} ${value}`,
37370
+ style: { background: value },
37371
+ onClick: () => set(key, value)
37372
+ }, value)),
37373
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
37374
+ name: key,
37375
+ type: "color",
37376
+ "aria-label": t(`profile.avatar.${key}`),
37377
+ value: String(fields[key]),
37378
+ onChange: (event) => set(key, event.currentTarget.value)
37379
+ })
37380
+ ]
36281
37381
  }, key))
36282
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
36283
- type: "button",
36284
- "data-avatar-shuffle": true,
36285
- className: "bh-avatar-shuffle",
36286
- onClick: () => update(shuffled(draft)),
36287
- children: t("profile.avatar.shuffle")
36288
- })]
36289
- }),
36290
- category === "colors" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
36291
- className: "bh-avatar-colors",
36292
- role: "tabpanel",
36293
- id: "bh-avatar-panel",
36294
- "aria-labelledby": "bh-avatar-tab-colors",
36295
- children: spec.colors.map((key) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
36296
- className: "bh-avatar-color-row",
36297
- children: [
37382
+ }) : category === "presets" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
37383
+ className: "bh-avatar-options",
37384
+ role: "tabpanel",
37385
+ id: "bh-avatar-panel",
37386
+ "aria-labelledby": "bh-avatar-tab-presets",
37387
+ children: spec.presets.map((preset, index) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(OptionTile, {
37388
+ id: `preset:${index}`,
37389
+ recipe: preset,
37390
+ selected: JSON.stringify(preset) === JSON.stringify(draft),
37391
+ label: `${t("profile.avatar.presets")} ${index + 1}`,
37392
+ onSelect: () => update({ ...preset })
37393
+ }, index))
37394
+ }) : category === "shape" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
37395
+ className: "bh-avatar-ranges",
37396
+ role: "tabpanel",
37397
+ id: "bh-avatar-panel",
37398
+ "aria-labelledby": "bh-avatar-tab-shape",
37399
+ children: Object.entries(spec.ranges).map(([key, [min, max]]) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", { children: [
36298
37400
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: t(`profile.avatar.${key}`) }),
36299
- spec.swatches[key].map((value) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
36300
- type: "button",
36301
- className: "bh-avatar-swatch",
36302
- "data-avatar-option": `${key}:${value}`,
36303
- "aria-pressed": fields[key] === value,
36304
- "aria-label": `${t(`profile.avatar.${key}`)} ${value}`,
36305
- style: { background: value },
36306
- onClick: () => set(key, value)
36307
- }, value)),
36308
37401
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
37402
+ type: "range",
36309
37403
  name: key,
36310
- type: "color",
36311
- "aria-label": t(`profile.avatar.${key}`),
36312
- value: String(fields[key]),
36313
- onChange: (event) => set(key, event.currentTarget.value)
36314
- })
36315
- ]
36316
- }, key))
36317
- }) : category === "presets" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
36318
- className: "bh-avatar-options",
36319
- role: "tabpanel",
36320
- id: "bh-avatar-panel",
36321
- "aria-labelledby": "bh-avatar-tab-presets",
36322
- children: spec.presets.map((preset, index) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(OptionTile, {
36323
- id: `preset:${index}`,
36324
- recipe: preset,
36325
- selected: JSON.stringify(preset) === JSON.stringify(draft),
36326
- label: `${t("profile.avatar.presets")} ${index + 1}`,
36327
- onSelect: () => update({ ...preset })
36328
- }, index))
36329
- }) : category === "shape" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
36330
- className: "bh-avatar-ranges",
36331
- role: "tabpanel",
36332
- id: "bh-avatar-panel",
36333
- "aria-labelledby": "bh-avatar-tab-shape",
36334
- children: Object.entries(spec.ranges).map(([key, [min, max]]) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", { children: [
36335
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: t(`profile.avatar.${key}`) }),
36336
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
36337
- type: "range",
36338
- name: key,
36339
- min,
36340
- max,
36341
- step: 1,
36342
- value: Number(fields[key]),
36343
- onChange: (event) => set(key, Number(event.currentTarget.value))
36344
- }),
36345
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("output", { children: fields[key] })
36346
- ] }, key))
36347
- }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
36348
- className: "bh-avatar-options",
36349
- role: "tabpanel",
36350
- id: "bh-avatar-panel",
36351
- "aria-labelledby": `bh-avatar-tab-${category}`,
36352
- children: (spec.parts[category] ?? []).map((value) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(OptionTile, {
36353
- id: `${category}:${value}`,
36354
- recipe: withPart(draft, category, value),
36355
- selected: fields[category] === value,
36356
- label: t(spec.option(category, value)),
36357
- onSelect: () => set(category, value)
36358
- }, value))
36359
- })
36360
- ]
36361
- }) : null,
36362
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
36363
- className: "bh-profile-avatar-actions",
36364
- children: draft ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
36365
- "data-avatar-save": true,
36366
- type: "button",
36367
- className: "bh-profile-action bh-profile-action-primary",
36368
- disabled: busy,
36369
- onClick: () => void save(),
36370
- children: t("profile.save")
36371
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
36372
- "data-avatar-cancel": true,
36373
- type: "button",
36374
- className: "bh-profile-action",
36375
- disabled: busy,
36376
- onClick: () => {
36377
- setDrafts(void 0);
36378
- setFailed(false);
36379
- },
36380
- children: t("common.cancel")
36381
- })] }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
36382
- "data-avatar-edit": true,
36383
- type: "button",
36384
- className: "bh-profile-action",
36385
- disabled: bot.appearanceUnsupported === true,
36386
- onClick: start,
36387
- children: t("profile.avatar.design")
36388
- })
36389
- }),
36390
- failed ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
36391
- role: "alert",
36392
- className: "bh-error",
36393
- children: t("profile.avatar.failed")
36394
- }) : null
36395
- ]
37404
+ min,
37405
+ max,
37406
+ step: 1,
37407
+ value: Number(fields[key]),
37408
+ onChange: (event) => set(key, Number(event.currentTarget.value))
37409
+ }),
37410
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("output", { children: fields[key] })
37411
+ ] }, key))
37412
+ }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
37413
+ className: "bh-avatar-options",
37414
+ role: "tabpanel",
37415
+ id: "bh-avatar-panel",
37416
+ "aria-labelledby": `bh-avatar-tab-${category}`,
37417
+ children: (spec.parts[category] ?? []).map((value) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(OptionTile, {
37418
+ id: `${category}:${value}`,
37419
+ recipe: withPart(draft, category, value),
37420
+ selected: fields[category] === value,
37421
+ label: t(spec.option(category, value)),
37422
+ onSelect: () => set(category, value)
37423
+ }, value))
37424
+ })
37425
+ ]
37426
+ }) : null,
37427
+ draft ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
37428
+ className: "bh-profile-avatar-actions",
37429
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
37430
+ "data-avatar-save": true,
37431
+ type: "button",
37432
+ className: "bh-profile-action bh-profile-action-primary",
37433
+ disabled: busy,
37434
+ onClick: () => void save(),
37435
+ children: t("profile.save")
37436
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
37437
+ "data-avatar-cancel": true,
37438
+ type: "button",
37439
+ className: "bh-profile-action",
37440
+ disabled: busy,
37441
+ onClick: () => {
37442
+ setDrafts(void 0);
37443
+ setFailed(false);
37444
+ },
37445
+ children: t("common.cancel")
37446
+ })]
37447
+ }) : null,
37448
+ failed ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
37449
+ role: "alert",
37450
+ className: "bh-error",
37451
+ children: t("profile.avatar.failed")
37452
+ }) : null
37453
+ ]
37454
+ })]
36396
37455
  })]
36397
37456
  });
36398
37457
  }
@@ -37138,6 +38197,177 @@ My relationship with the Human, established lore and topics to avoid: [fill in].
37138
38197
  });
37139
38198
  }
37140
38199
  //#endregion
38200
+ //#region packages/client/src/client/standing-limits-profile.tsx
38201
+ const DEFAULT_STANDING_LIMITS = {
38202
+ soul: 5e3,
38203
+ coreMemory: 3e3
38204
+ };
38205
+ const MAX_STANDING_LIMIT = 5e4;
38206
+ const CHARACTERS_PER_ENGLISH_WORD = 6;
38207
+ const formatCount = (value) => value.toLocaleString("en-US");
38208
+ function parseLimit(text) {
38209
+ if (!/^\d+$/u.test(text.trim())) return void 0;
38210
+ const value = Number(text.trim());
38211
+ return value >= 500 && value <= 5e4 ? value : void 0;
38212
+ }
38213
+ function LimitField({ label, file, value, disabled, invalid, onChange, t }) {
38214
+ const id = (0, react.useId)();
38215
+ const parsed = parseLimit(value);
38216
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
38217
+ className: "bh-standing-limit-field",
38218
+ children: [
38219
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
38220
+ className: "bh-standing-limit-label",
38221
+ htmlFor: id,
38222
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: label }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("code", { children: file })]
38223
+ }),
38224
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
38225
+ className: "bh-standing-limit-input",
38226
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Input, {
38227
+ id,
38228
+ type: "number",
38229
+ min: 500,
38230
+ max: MAX_STANDING_LIMIT,
38231
+ step: 100,
38232
+ value,
38233
+ disabled,
38234
+ "aria-describedby": `${id}-hint`,
38235
+ "aria-invalid": invalid ? true : void 0,
38236
+ onChange: (event) => onChange(event.target.value)
38237
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
38238
+ className: "bh-standing-limit-unit",
38239
+ children: t("standingLimits.unit")
38240
+ })]
38241
+ }),
38242
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
38243
+ className: "bh-settings-row-desc",
38244
+ id: `${id}-hint`,
38245
+ children: parsed === void 0 ? t("standingLimits.range", {
38246
+ min: formatCount(500),
38247
+ max: formatCount(MAX_STANDING_LIMIT)
38248
+ }) : t("standingLimits.size", {
38249
+ chinese: formatCount(parsed),
38250
+ words: formatCount(Math.round(parsed / CHARACTERS_PER_ENGLISH_WORD / 10) * 10)
38251
+ })
38252
+ })
38253
+ ]
38254
+ });
38255
+ }
38256
+ function StandingLimitsProfile({ bot, actions, t }) {
38257
+ const [saved, setSaved] = (0, react.useState)(bot.standingLimits ?? DEFAULT_STANDING_LIMITS);
38258
+ const [soul, setSoul] = (0, react.useState)(String(saved.soul));
38259
+ const [coreMemory, setCoreMemory] = (0, react.useState)(String(saved.coreMemory));
38260
+ const [busy, setBusy] = (0, react.useState)(false);
38261
+ const [error, setError] = (0, react.useState)();
38262
+ const [notice, setNotice] = (0, react.useState)(false);
38263
+ const soulLimit = parseLimit(soul);
38264
+ const coreMemoryLimit = parseLimit(coreMemory);
38265
+ const valid = soulLimit !== void 0 && coreMemoryLimit !== void 0;
38266
+ const changed = valid && (soulLimit !== saved.soul || coreMemoryLimit !== saved.coreMemory);
38267
+ const edit = (setter) => (value) => {
38268
+ setter(value);
38269
+ setError(void 0);
38270
+ setNotice(false);
38271
+ };
38272
+ const submit = async () => {
38273
+ if (!valid) return;
38274
+ setBusy(true);
38275
+ setError(void 0);
38276
+ try {
38277
+ const next = (await actions.setStandingLimits(bot.slug, {
38278
+ soul: soulLimit,
38279
+ coreMemory: coreMemoryLimit
38280
+ })).standingLimits ?? {
38281
+ soul: soulLimit,
38282
+ coreMemory: coreMemoryLimit
38283
+ };
38284
+ setSaved(next);
38285
+ setSoul(String(next.soul));
38286
+ setCoreMemory(String(next.coreMemory));
38287
+ setNotice(true);
38288
+ } catch (cause) {
38289
+ setError(errorMessage(cause));
38290
+ } finally {
38291
+ setBusy(false);
38292
+ }
38293
+ };
38294
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
38295
+ className: "bh-profile-section bh-profile-policy-section bh-standing-limits",
38296
+ "aria-label": t("standingLimits.title"),
38297
+ children: [
38298
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("h2", {
38299
+ className: "bh-profile-section-title",
38300
+ children: t("standingLimits.title")
38301
+ }),
38302
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
38303
+ className: "bh-settings-row-desc",
38304
+ children: t("standingLimits.description")
38305
+ }),
38306
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("form", {
38307
+ className: "bh-standing-limits-form",
38308
+ noValidate: true,
38309
+ onSubmit: (event) => {
38310
+ event.preventDefault();
38311
+ submit();
38312
+ },
38313
+ children: [
38314
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(LimitField, {
38315
+ label: t("standingLimits.soul"),
38316
+ file: "SOUL.md",
38317
+ value: soul,
38318
+ disabled: busy,
38319
+ invalid: soulLimit === void 0,
38320
+ onChange: edit(setSoul),
38321
+ t
38322
+ }),
38323
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(LimitField, {
38324
+ label: t("standingLimits.coreMemory"),
38325
+ file: "MEMORY.md",
38326
+ value: coreMemory,
38327
+ disabled: busy,
38328
+ invalid: coreMemoryLimit === void 0,
38329
+ onChange: edit(setCoreMemory),
38330
+ t
38331
+ }),
38332
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
38333
+ className: "bh-standing-limits-actions",
38334
+ children: [
38335
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
38336
+ type: "submit",
38337
+ size: "sm",
38338
+ variant: "primary",
38339
+ disabled: busy || !changed,
38340
+ children: t(busy ? "standingLimits.saving" : "standingLimits.save")
38341
+ }),
38342
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
38343
+ type: "button",
38344
+ size: "sm",
38345
+ variant: "outline",
38346
+ disabled: busy || soul === String(DEFAULT_STANDING_LIMITS.soul) && coreMemory === String(DEFAULT_STANDING_LIMITS.coreMemory),
38347
+ onClick: () => {
38348
+ edit(setSoul)(String(DEFAULT_STANDING_LIMITS.soul));
38349
+ setCoreMemory(String(DEFAULT_STANDING_LIMITS.coreMemory));
38350
+ },
38351
+ children: t("standingLimits.reset")
38352
+ }),
38353
+ notice && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
38354
+ className: "bh-settings-row-desc",
38355
+ role: "status",
38356
+ children: t("standingLimits.saved")
38357
+ }),
38358
+ error && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
38359
+ className: "bh-assignment-limit-error",
38360
+ role: "alert",
38361
+ children: error
38362
+ })
38363
+ ]
38364
+ })
38365
+ ]
38366
+ })
38367
+ ]
38368
+ });
38369
+ }
38370
+ //#endregion
37141
38371
  //#region packages/client/src/client/source-policy-table.tsx
37142
38372
  function editable(policy) {
37143
38373
  return [
@@ -37491,8 +38721,14 @@ My relationship with the Human, established lore and topics to avoid: [fill in].
37491
38721
  type: "button",
37492
38722
  className: "bh-profile-avatar-button",
37493
38723
  "aria-label": t("profile.avatar.change"),
37494
- disabled: avatarBusy,
37495
- onClick: () => avatarInputRef.current?.click(),
38724
+ onClick: (event) => {
38725
+ const section = event.currentTarget.closest(".bh-profile-view")?.querySelector(".bh-avatar-section");
38726
+ section?.scrollIntoView({
38727
+ block: "nearest",
38728
+ behavior: "smooth"
38729
+ });
38730
+ section?.querySelector("button:not(:disabled)")?.focus({ preventScroll: true });
38731
+ },
37496
38732
  children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(PersonaBotAvatar, {
37497
38733
  t,
37498
38734
  personaBotId: bot.slug,
@@ -37550,22 +38786,6 @@ My relationship with the Human, established lore and topics to avoid: [fill in].
37550
38786
  }),
37551
38787
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)(RoleBadges, { roles: bot.roles }),
37552
38788
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Description, { text: bot.description }),
37553
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
37554
- className: "bh-profile-avatar-actions",
37555
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
37556
- type: "button",
37557
- className: "bh-profile-action",
37558
- disabled: avatarBusy,
37559
- onClick: () => avatarInputRef.current?.click(),
37560
- children: t("profile.avatar.change")
37561
- }), bot.avatar === void 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
37562
- type: "button",
37563
- className: "bh-profile-action",
37564
- disabled: avatarBusy,
37565
- onClick: () => void removeAvatar(),
37566
- children: t("profile.avatar.remove")
37567
- })]
37568
- }),
37569
38789
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
37570
38790
  ref: avatarInputRef,
37571
38791
  className: "bh-profile-avatar-input",
@@ -37589,6 +38809,9 @@ My relationship with the Human, established lore and topics to avoid: [fill in].
37589
38809
  bot,
37590
38810
  channelId: channel.id,
37591
38811
  onSave: actions.setBotAppearance,
38812
+ onUpload: () => avatarInputRef.current?.click(),
38813
+ onRemoveImage: () => void removeAvatar(),
38814
+ imageBusy: avatarBusy,
37592
38815
  t
37593
38816
  }, `avatar-${bot.slug}`),
37594
38817
  visibleCards.length === 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
@@ -37632,6 +38855,11 @@ My relationship with the Human, established lore and topics to avoid: [fill in].
37632
38855
  actions,
37633
38856
  t
37634
38857
  }, bot.slug),
38858
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(StandingLimitsProfile, {
38859
+ bot,
38860
+ actions,
38861
+ t
38862
+ }, `standing-${bot.slug}`),
37635
38863
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)(MessagingProfile, {
37636
38864
  slug: bot.slug,
37637
38865
  actions,
@@ -39705,6 +40933,116 @@ My relationship with the Human, established lore and topics to avoid: [fill in].
39705
40933
  profileCardStorage()?.setItem(GROUP_PROFILE_CARDS_STORAGE_KEY, JSON.stringify([...ids]));
39706
40934
  }
39707
40935
  //#endregion
40936
+ //#region packages/client/src/client/telemetry-notice-view.tsx
40937
+ const ROWS = [
40938
+ {
40939
+ row: "collected",
40940
+ label: "telemetry.notice.collected.label",
40941
+ text: "telemetry.notice.collected"
40942
+ },
40943
+ {
40944
+ row: "never",
40945
+ label: "telemetry.notice.never.label",
40946
+ text: "telemetry.notice.never"
40947
+ },
40948
+ {
40949
+ row: "why",
40950
+ label: "telemetry.notice.why.label",
40951
+ text: "telemetry.notice.why"
40952
+ },
40953
+ {
40954
+ row: "opt-out",
40955
+ label: "telemetry.notice.optOut.label",
40956
+ text: "telemetry.notice.optOut"
40957
+ }
40958
+ ];
40959
+ const NO_RELEASE = void 0;
40960
+ const noSubscribe = () => () => void 0;
40961
+ const noRelease = () => NO_RELEASE;
40962
+ function open(url) {
40963
+ window.open(url, "_blank", "noopener,noreferrer");
40964
+ }
40965
+ function TelemetryNoticeBody({ t }) {
40966
+ const labels = (0, react.useMemo)(() => ({
40967
+ code: {
40968
+ copyLabel: t("message.code.copy"),
40969
+ copiedLabel: t("message.code.copied")
40970
+ },
40971
+ footnotes: t("message.footnotes")
40972
+ }), [t]);
40973
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("dl", {
40974
+ className: "bh-telemetry-notice",
40975
+ "data-telemetry-notice": true,
40976
+ children: ROWS.map(({ row, label, text }) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
40977
+ className: "bh-telemetry-notice-row",
40978
+ "data-telemetry-row": row,
40979
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("dt", { children: t(label) }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("dd", { children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.MarkdownText, {
40980
+ text: t(text),
40981
+ labels
40982
+ }) })]
40983
+ }, row))
40984
+ });
40985
+ }
40986
+ function TelemetryNoticeDialog({ open: visible, onClose, onOpenSettings, t }) {
40987
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Modal, {
40988
+ open: visible,
40989
+ onClose,
40990
+ title: t("telemetry.notice.title"),
40991
+ description: t("telemetry.notice.description"),
40992
+ closeLabel: t("telemetry.notice.close"),
40993
+ className: "bh-telemetry-dialog",
40994
+ footer: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
40995
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
40996
+ variant: "outline",
40997
+ onClick: () => {
40998
+ open(t("telemetry.notice.sourceUrl"));
40999
+ },
41000
+ children: t("telemetry.notice.source")
41001
+ }),
41002
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
41003
+ variant: "outline",
41004
+ onClick: () => {
41005
+ open(t("telemetry.notice.privacyUrl"));
41006
+ },
41007
+ children: t("telemetry.notice.privacy")
41008
+ }),
41009
+ onOpenSettings === void 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
41010
+ variant: "outline",
41011
+ onClick: onOpenSettings,
41012
+ "data-telemetry-open-settings": true,
41013
+ children: t("telemetry.notice.settings")
41014
+ }),
41015
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
41016
+ variant: "primary",
41017
+ onClick: onClose,
41018
+ children: t("telemetry.notice.done")
41019
+ })
41020
+ ] }),
41021
+ children: visible ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(TelemetryNoticeBody, { t }) : null
41022
+ });
41023
+ }
41024
+ function TelemetryNotice({ controller, releaseNotes, t }) {
41025
+ const snapshot = (0, react.useSyncExternalStore)(controller.source.subscribe, controller.source.getSnapshot);
41026
+ const release = (0, react.useSyncExternalStore)(releaseNotes?.source.subscribe ?? noSubscribe, releaseNotes?.source.getSnapshot ?? noRelease);
41027
+ const mount = useMountedResource(() => {
41028
+ controller.start(releaseNotes?.start());
41029
+ }, [controller, releaseNotes]);
41030
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
41031
+ ref: mount,
41032
+ hidden: true,
41033
+ "aria-hidden": "true"
41034
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(TelemetryNoticeDialog, {
41035
+ open: snapshot.open && release?.announcement === void 0,
41036
+ onClose: () => {
41037
+ controller.dismiss();
41038
+ },
41039
+ onOpenSettings: controller.canOpenSettings ? () => {
41040
+ controller.openSettings();
41041
+ } : void 0,
41042
+ t
41043
+ })] });
41044
+ }
41045
+ //#endregion
39708
41046
  //#region packages/client/src/client/bot-main.tsx
39709
41047
  function committedMessageIds(messages) {
39710
41048
  return new Set(messages.filter((message) => message.pending !== true && message.streaming !== true && message.failed === void 0).map((message) => message.id));
@@ -40855,7 +42193,7 @@ My relationship with the Human, established lore and topics to avoid: [fill in].
40855
42193
  t
40856
42194
  }, `${scopeKey}:${state.conversation.channel?.id ?? ""}`);
40857
42195
  }
40858
- function BotPanel({ actions, nativeSessions, channelSidebar, profileCards, nativeChatT, releaseNotes, t }) {
42196
+ function BotPanel({ actions, nativeSessions, channelSidebar, profileCards, nativeChatT, releaseNotes, telemetryNotice, t }) {
40859
42197
  const modeMount = useMountedResource(() => {
40860
42198
  store.setMode("bot");
40861
42199
  return () => {
@@ -40872,6 +42210,11 @@ My relationship with the Human, established lore and topics to avoid: [fill in].
40872
42210
  controller: releaseNotes,
40873
42211
  t
40874
42212
  }),
42213
+ telemetryNotice === void 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(TelemetryNotice, {
42214
+ controller: telemetryNotice,
42215
+ releaseNotes,
42216
+ t
42217
+ }),
40875
42218
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)(BotMain, {
40876
42219
  actions,
40877
42220
  nativeSessions,
@@ -41898,7 +43241,7 @@ My relationship with the Human, established lore and topics to avoid: [fill in].
41898
43241
  }
41899
43242
  //#endregion
41900
43243
  //#region packages/client/src/client/memory-file-tree-model.ts
41901
- function memoryFileTree(paths) {
43244
+ function memoryFileTree(paths, pinned = []) {
41902
43245
  const roots = [];
41903
43246
  const directories = /* @__PURE__ */ new Map();
41904
43247
  for (const path of paths) {
@@ -41934,7 +43277,14 @@ My relationship with the Human, established lore and topics to avoid: [fill in].
41934
43277
  for (const node of nodes) sort(node.children);
41935
43278
  };
41936
43279
  sort(roots);
41937
- return roots;
43280
+ const rank = (node) => {
43281
+ const index = node.kind === "file" ? pinned.indexOf(node.path) : -1;
43282
+ return index < 0 ? pinned.length : index;
43283
+ };
43284
+ return roots.map((node, index) => ({
43285
+ node,
43286
+ index
43287
+ })).sort((left, right) => rank(left.node) - rank(right.node) || left.index - right.index).map(({ node }) => node);
41938
43288
  }
41939
43289
  //#endregion
41940
43290
  //#region packages/client/src/client/memory-file-tree.tsx
@@ -41944,7 +43294,27 @@ My relationship with the Human, established lore and topics to avoid: [fill in].
41944
43294
  for (let index = 1; index < parts.length; index += 1) next.add(parts.slice(0, index).join("/"));
41945
43295
  return next;
41946
43296
  }
41947
- function MemoryFileTree({ paths, actions, botSlug, selectedPath, onSelect, t }) {
43297
+ function StandingBadge({ usage, t }) {
43298
+ const over = usage.chars > usage.limit;
43299
+ const counts = {
43300
+ percent: Math.round(usage.chars / usage.limit * 100),
43301
+ chars: usage.chars.toLocaleString("en-US"),
43302
+ limit: usage.limit.toLocaleString("en-US")
43303
+ };
43304
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
43305
+ className: over ? "bh-memory-standing bh-memory-standing-over" : "bh-memory-standing",
43306
+ title: t(usage.role === "soul" ? "standing.soulTitle" : "standing.coreMemoryTitle"),
43307
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Tag, {
43308
+ tone: "neutral",
43309
+ children: t("standing.badge")
43310
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
43311
+ className: "bh-memory-standing-usage",
43312
+ title: t("standing.usageTitle", counts),
43313
+ children: t("standing.usage", counts)
43314
+ })]
43315
+ });
43316
+ }
43317
+ function MemoryFileTree({ paths, standing = [], actions, botSlug, selectedPath, onSelect, t }) {
41948
43318
  const menu = useMemoryFileMenu(actions, botSlug, t);
41949
43319
  const [selection, setSelection] = (0, react.useState)({
41950
43320
  path: selectedPath,
@@ -42016,6 +43386,7 @@ My relationship with the Human, established lore and topics to avoid: [fill in].
42016
43386
  children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconEllipsisOutlineRegular, { size: 16 })
42017
43387
  })
42018
43388
  });
43389
+ const usageByPath = new Map(standing.map((usage) => [usage.path, usage]));
42019
43390
  const render = (nodes) => nodes.map((node) => {
42020
43391
  if (node.kind === "file") return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
42021
43392
  className: "bh-memory-tree-item",
@@ -42041,7 +43412,11 @@ My relationship with the Human, established lore and topics to avoid: [fill in].
42041
43412
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
42042
43413
  className: "bh-memory-tree-name",
42043
43414
  children: node.name
42044
- })
43415
+ }),
43416
+ usageByPath.has(node.path) ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(StandingBadge, {
43417
+ usage: usageByPath.get(node.path),
43418
+ t
43419
+ }) : null
42045
43420
  ]
42046
43421
  }), more(node.path)]
42047
43422
  }, node.path);
@@ -42087,7 +43462,7 @@ My relationship with the Human, established lore and topics to avoid: [fill in].
42087
43462
  "aria-label": t("entry.memoryFiles"),
42088
43463
  onKeyDown: onTreeKeyDown,
42089
43464
  children: [
42090
- render(memoryFileTree(paths)),
43465
+ render(memoryFileTree(paths, standing.map((usage) => usage.path))),
42091
43466
  menu.menu,
42092
43467
  menu.feedback
42093
43468
  ]
@@ -42173,6 +43548,7 @@ My relationship with the Human, established lore and topics to avoid: [fill in].
42173
43548
  children: t("memory.empty")
42174
43549
  }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(MemoryFileTree, {
42175
43550
  paths: snapshot.files,
43551
+ standing: snapshot.standing,
42176
43552
  actions,
42177
43553
  botSlug,
42178
43554
  selectedPath: selectedMemoryFilePath,
@@ -42205,7 +43581,7 @@ My relationship with the Human, established lore and topics to avoid: [fill in].
42205
43581
  }, index) : part;
42206
43582
  });
42207
43583
  }
42208
- function MessageCard({ message, t }) {
43584
+ function MessageCard({ message, t, media }) {
42209
43585
  const name = message.senderLabel ?? message.senderName ?? message.senderId;
42210
43586
  const date = new Date(message.at);
42211
43587
  const time = Number.isNaN(date.getTime()) ? message.at : new Intl.DateTimeFormat(void 0, {
@@ -42234,9 +43610,16 @@ My relationship with the Human, established lore and topics to avoid: [fill in].
42234
43610
  children: time
42235
43611
  })]
42236
43612
  }),
42237
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
43613
+ message.voice ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
43614
+ className: "bh-external-message-head",
43615
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Tag, {
43616
+ tone: message.voice.transcript === "platform" ? "info" : "warning",
43617
+ children: t(message.voice.transcript === "platform" ? "im.voiceTranscriptPlatform" : "im.voiceTranscriptUnavailable")
43618
+ }), message.voice.durationMs === void 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: t("im.voiceDuration", { seconds: new Intl.NumberFormat(void 0, { maximumFractionDigits: 1 }).format(message.voice.durationMs / 1e3) }) })]
43619
+ }) : null,
43620
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
42238
43621
  className: "bh-external-message-text",
42239
- children: messageText(message.text, message.mentions ?? [])
43622
+ children: [media, media && message.text.trim() === "[Image]" ? null : message.voice?.transcript === "unavailable" ? t("im.voiceTranscriptUnavailableHint") : messageText(message.text, message.mentions ?? [])]
42240
43623
  }),
42241
43624
  /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("details", {
42242
43625
  className: "bh-external-details",
@@ -42249,6 +43632,11 @@ My relationship with the Human, established lore and topics to avoid: [fill in].
42249
43632
  }) }),
42250
43633
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", { children: message.senderName ? `${message.senderName} (${message.senderId})` : message.senderId }),
42251
43634
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", { children: message.at }),
43635
+ message.voice?.itemId ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", { children: t("im.voiceItemId", { id: message.voice.itemId }) }) : null,
43636
+ media && !message.mentions?.length ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
43637
+ className: "bh-external-raw-text",
43638
+ children: message.text
43639
+ }) : null,
42252
43640
  message.mentions?.length ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", { children: message.mentions.map((mention) => `${mention.key} → ${mention.name ?? mention.id} (${mention.id})`).join(" · ") }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
42253
43641
  className: "bh-external-raw-text",
42254
43642
  children: message.text
@@ -42260,7 +43648,7 @@ My relationship with the Human, established lore and topics to avoid: [fill in].
42260
43648
  })]
42261
43649
  });
42262
43650
  }
42263
- function ExternalSourceContent({ source, t, children }) {
43651
+ function ExternalSourceContent({ source, t, children, messageMedia }) {
42264
43652
  const platform = externalPlatformLabel(source.platform, t);
42265
43653
  const hasThread = Boolean(source.event.reply.threadId ?? source.event.reply.rootId);
42266
43654
  const messages = [...source.contextMessages ?? []].sort((a, b) => a.at.localeCompare(b.at));
@@ -42375,9 +43763,11 @@ My relationship with the Human, established lore and topics to avoid: [fill in].
42375
43763
  ...source.event.actor.name ? { senderName: source.event.actor.name } : {},
42376
43764
  at: source.at,
42377
43765
  text: source.body,
42378
- mentions: source.event.mentions
43766
+ mentions: source.event.mentions,
43767
+ ...source.event.voice ? { voice: source.event.voice } : {}
42379
43768
  },
42380
- t
43769
+ t,
43770
+ media: messageMedia
42381
43771
  }), children]
42382
43772
  }),
42383
43773
  source.contextReads?.length ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
@@ -42591,6 +43981,794 @@ My relationship with the Human, established lore and topics to avoid: [fill in].
42591
43981
  })
42592
43982
  });
42593
43983
  }
43984
+ const REFRESH_MS = 3e4;
43985
+ const WEEKDAYS = [
43986
+ [1, "schedule.weekday.1"],
43987
+ [2, "schedule.weekday.2"],
43988
+ [3, "schedule.weekday.3"],
43989
+ [4, "schedule.weekday.4"],
43990
+ [5, "schedule.weekday.5"],
43991
+ [6, "schedule.weekday.6"],
43992
+ [7, "schedule.weekday.7"]
43993
+ ];
43994
+ const PREVIEW_DELAY_MS = 250;
43995
+ const STARTED_MS = 4e3;
43996
+ const TOOLTIP_DELAY_MS = 400;
43997
+ const TIME = /^\d{2}:\d{2}$/u;
43998
+ const DATE = /^\d{4}-\d{2}-\d{2}$/u;
43999
+ const createRequests = /* @__PURE__ */ new Map();
44000
+ const listeners = /* @__PURE__ */ new Set();
44001
+ function requestCreate(slug) {
44002
+ createRequests.set(slug, (createRequests.get(slug) ?? 0) + 1);
44003
+ for (const listener of listeners) listener();
44004
+ }
44005
+ function subscribeCreate(listener) {
44006
+ listeners.add(listener);
44007
+ return () => listeners.delete(listener);
44008
+ }
44009
+ function browserTimeZone() {
44010
+ try {
44011
+ return Intl.DateTimeFormat().resolvedOptions().timeZone || "UTC";
44012
+ } catch {
44013
+ return "UTC";
44014
+ }
44015
+ }
44016
+ function localDate(at) {
44017
+ return `${at.getFullYear()}-${pad(at.getMonth() + 1)}-${pad(at.getDate())}`;
44018
+ }
44019
+ function emptyScheduleForm(timeZone = browserTimeZone(), now = /* @__PURE__ */ new Date()) {
44020
+ return {
44021
+ title: "",
44022
+ prompt: "",
44023
+ unit: "hours",
44024
+ every: "1",
44025
+ time: "09:00",
44026
+ timeZone,
44027
+ weekdays: [
44028
+ 1,
44029
+ 2,
44030
+ 3,
44031
+ 4,
44032
+ 5
44033
+ ],
44034
+ date: localDate(new Date(now.getTime() + 864e5)),
44035
+ expression: "0 9 * * 1-5",
44036
+ locked: false
44037
+ };
44038
+ }
44039
+ function scheduleFormOf(schedule) {
44040
+ const base = {
44041
+ ...emptyScheduleForm(),
44042
+ title: schedule.title,
44043
+ prompt: schedule.prompt,
44044
+ locked: schedule.locked
44045
+ };
44046
+ const trigger = schedule.trigger;
44047
+ switch (trigger.kind) {
44048
+ case "every": {
44049
+ const hours = trigger.everySeconds % 3600 === 0;
44050
+ return {
44051
+ ...base,
44052
+ unit: hours ? "hours" : "minutes",
44053
+ every: String(trigger.everySeconds / (hours ? 3600 : 60))
44054
+ };
44055
+ }
44056
+ case "daily": return {
44057
+ ...base,
44058
+ unit: "daily",
44059
+ time: trigger.time,
44060
+ timeZone: trigger.timeZone
44061
+ };
44062
+ case "weekly": return {
44063
+ ...base,
44064
+ unit: "weekly",
44065
+ time: trigger.time,
44066
+ timeZone: trigger.timeZone,
44067
+ weekdays: [...trigger.weekdays]
44068
+ };
44069
+ case "once": return {
44070
+ ...base,
44071
+ unit: "once",
44072
+ date: trigger.date,
44073
+ time: trigger.time,
44074
+ timeZone: trigger.timeZone
44075
+ };
44076
+ case "cron": return {
44077
+ ...base,
44078
+ unit: "cron",
44079
+ expression: trigger.expression,
44080
+ timeZone: trigger.timeZone
44081
+ };
44082
+ }
44083
+ }
44084
+ function scheduleTriggerOf(form) {
44085
+ const timeZone = form.timeZone.trim();
44086
+ if (form.unit === "minutes" || form.unit === "hours") {
44087
+ const every = Number(form.every);
44088
+ if (!Number.isSafeInteger(every) || every < 1) return void 0;
44089
+ return {
44090
+ kind: "every",
44091
+ everySeconds: every * (form.unit === "hours" ? 3600 : 60)
44092
+ };
44093
+ }
44094
+ if (timeZone.length === 0) return void 0;
44095
+ if (form.unit === "cron") {
44096
+ const expression = form.expression.trim().replace(/\s+/gu, " ");
44097
+ return expression.length === 0 ? void 0 : {
44098
+ kind: "cron",
44099
+ expression,
44100
+ timeZone
44101
+ };
44102
+ }
44103
+ if (!TIME.test(form.time)) return void 0;
44104
+ if (form.unit === "daily") return {
44105
+ kind: "daily",
44106
+ time: form.time,
44107
+ timeZone
44108
+ };
44109
+ if (form.unit === "weekly") return form.weekdays.length === 0 ? void 0 : {
44110
+ kind: "weekly",
44111
+ time: form.time,
44112
+ timeZone,
44113
+ weekdays: [...new Set(form.weekdays)].sort((a, b) => a - b)
44114
+ };
44115
+ return DATE.test(form.date) ? {
44116
+ kind: "once",
44117
+ date: form.date,
44118
+ time: form.time,
44119
+ timeZone
44120
+ } : void 0;
44121
+ }
44122
+ function scheduleCadenceLabel(trigger, t) {
44123
+ if (trigger.kind === "daily") return t("schedule.cadence.daily", { time: trigger.time });
44124
+ if (trigger.kind === "weekly") return t("schedule.cadence.weekly", {
44125
+ days: weekdayList(trigger.weekdays, t),
44126
+ time: trigger.time
44127
+ });
44128
+ if (trigger.kind === "once") return t("schedule.cadence.once", { time: `${trigger.date.slice(5)} ${trigger.time}` });
44129
+ if (trigger.kind === "cron") return t("schedule.cadence.cron", { expression: trigger.expression });
44130
+ if (trigger.everySeconds % 3600 === 0) {
44131
+ const n = trigger.everySeconds / 3600;
44132
+ return n === 1 ? t("schedule.cadence.hour") : t("schedule.cadence.hours", { n });
44133
+ }
44134
+ const n = Math.round(trigger.everySeconds / 60);
44135
+ return n === 1 ? t("schedule.cadence.minute") : t("schedule.cadence.minutes", { n });
44136
+ }
44137
+ function weekdayList(weekdays, t) {
44138
+ const days = [...weekdays].sort((a, b) => a - b).join(",");
44139
+ if (days === "1,2,3,4,5") return t("schedule.weekdays.workdays");
44140
+ if (days === "6,7") return t("schedule.weekdays.weekend");
44141
+ if (days === "1,2,3,4,5,6,7") return t("schedule.weekdays.everyDay");
44142
+ return WEEKDAYS.filter(([day]) => weekdays.includes(day)).map(([, key]) => t(key)).join(t("schedule.weekdays.separator"));
44143
+ }
44144
+ function pad(value) {
44145
+ return String(value).padStart(2, "0");
44146
+ }
44147
+ function formatSchedulePreview(iso) {
44148
+ const at = new Date(iso);
44149
+ return `${localDate(at)} ${pad(at.getHours())}:${pad(at.getMinutes())}`;
44150
+ }
44151
+ function formatScheduleTime(iso, now = /* @__PURE__ */ new Date()) {
44152
+ const at = new Date(iso);
44153
+ const clock = `${pad(at.getHours())}:${pad(at.getMinutes())}`;
44154
+ return at.toDateString() === now.toDateString() ? clock : `${pad(at.getMonth() + 1)}-${pad(at.getDate())} ${clock}`;
44155
+ }
44156
+ const STATE_TONE = {
44157
+ pending: "warning",
44158
+ observed: "info",
44159
+ handled: "success",
44160
+ coalesced: "neutral",
44161
+ skipped: "quiet"
44162
+ };
44163
+ function FiringTag({ firing, t }) {
44164
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Tag, {
44165
+ tone: STATE_TONE[firing.state],
44166
+ children: t(`schedule.state.${firing.state}`)
44167
+ });
44168
+ }
44169
+ function BotSchedulesHeaderAction({ botSlug, t }) {
44170
+ if (botSlug === void 0) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(react_jsx_runtime.Fragment, {});
44171
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Tooltip, {
44172
+ label: t("schedule.add"),
44173
+ side: "bottom",
44174
+ delayMs: 500,
44175
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
44176
+ type: "button",
44177
+ className: "bh-channel-sidebar-entry-action",
44178
+ "aria-label": t("schedule.add"),
44179
+ "aria-haspopup": "dialog",
44180
+ onClick: () => requestCreate(botSlug),
44181
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconPlusOutlineRegular, { size: 16 })
44182
+ })
44183
+ });
44184
+ }
44185
+ function SchedulePreview({ trigger, preview, t }) {
44186
+ const key = trigger === void 0 ? "" : JSON.stringify(trigger);
44187
+ const [result, setResult] = (0, react.useState)();
44188
+ const mount = useMountedResource(() => {
44189
+ if (trigger === void 0) return;
44190
+ let live = true;
44191
+ const timer = setTimeout(() => {
44192
+ preview(trigger).then((occurrences) => {
44193
+ if (live) setResult({
44194
+ key,
44195
+ occurrences
44196
+ });
44197
+ }).catch((cause) => {
44198
+ if (live) setResult({
44199
+ key,
44200
+ error: errorMessage(cause)
44201
+ });
44202
+ });
44203
+ }, PREVIEW_DELAY_MS);
44204
+ return () => {
44205
+ live = false;
44206
+ clearTimeout(timer);
44207
+ };
44208
+ }, [key, preview]);
44209
+ const current = result?.key === key ? result : void 0;
44210
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
44211
+ ref: mount,
44212
+ className: "bh-schedule-preview",
44213
+ "aria-live": "polite",
44214
+ "data-schedule-preview": "",
44215
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
44216
+ className: "bh-schedule-preview-label",
44217
+ children: t("schedule.preview")
44218
+ }), trigger === void 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
44219
+ className: "bh-schedule-muted",
44220
+ children: t("schedule.preview.incomplete")
44221
+ }) : current === void 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
44222
+ className: "bh-schedule-muted",
44223
+ children: t("schedule.preview.loading")
44224
+ }) : current.error !== void 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
44225
+ className: "bh-schedule-error",
44226
+ role: "alert",
44227
+ children: t("schedule.preview.invalid", { reason: current.error })
44228
+ }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("ol", { children: current.occurrences.map((occurrence) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("li", { children: formatSchedulePreview(occurrence) }, occurrence)) })]
44229
+ });
44230
+ }
44231
+ function ScheduleEditor({ open, initial, editing, busy, error, onClose, onSave, onDelete, history, onOpenSession, preview, t }) {
44232
+ const id = (0, react.useId)();
44233
+ const [form, setForm] = (0, react.useState)(initial);
44234
+ const [confirmDelete, setConfirmDelete] = (0, react.useState)(false);
44235
+ const trigger = scheduleTriggerOf(form);
44236
+ const costly = trigger?.kind === "every" && trigger.everySeconds < 900;
44237
+ const valid = trigger !== void 0 && form.title.trim().length > 0 && form.prompt.trim().length > 0;
44238
+ const set = (patch) => setForm((current) => ({
44239
+ ...current,
44240
+ ...patch
44241
+ }));
44242
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Modal, {
44243
+ open,
44244
+ onClose,
44245
+ title: editing === void 0 ? t("schedule.add") : t("schedule.edit"),
44246
+ closeLabel: t("schedule.close"),
44247
+ className: "bh-schedule-modal",
44248
+ footer: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
44249
+ className: "bh-schedule-modal-footer",
44250
+ children: [
44251
+ editing === void 0 ? null : confirmDelete ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
44252
+ variant: "outline",
44253
+ size: "sm",
44254
+ disabled: busy,
44255
+ onClick: onDelete,
44256
+ children: t("schedule.deleteConfirm")
44257
+ }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
44258
+ variant: "ghost",
44259
+ size: "sm",
44260
+ disabled: busy,
44261
+ onClick: () => setConfirmDelete(true),
44262
+ children: t("schedule.delete")
44263
+ }),
44264
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { className: "bh-schedule-modal-spacer" }),
44265
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
44266
+ variant: "ghost",
44267
+ size: "sm",
44268
+ disabled: busy,
44269
+ onClick: onClose,
44270
+ children: t("schedule.cancel")
44271
+ }),
44272
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
44273
+ variant: "primary",
44274
+ size: "sm",
44275
+ disabled: busy || !valid,
44276
+ onClick: () => onSave(form),
44277
+ children: editing === void 0 ? t("schedule.create") : t("schedule.save")
44278
+ })
44279
+ ]
44280
+ }),
44281
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
44282
+ className: "bh-schedule-form",
44283
+ "data-schedule-form": "",
44284
+ children: [
44285
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
44286
+ className: "bh-schedule-field",
44287
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: t("schedule.form.title") }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Input, {
44288
+ value: form.title,
44289
+ maxLength: 120,
44290
+ placeholder: t("schedule.form.titlePlaceholder"),
44291
+ onChange: (event) => set({ title: event.target.value })
44292
+ })]
44293
+ }),
44294
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
44295
+ className: "bh-schedule-field",
44296
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: t("schedule.form.prompt") }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("textarea", {
44297
+ rows: 4,
44298
+ maxLength: 4e3,
44299
+ value: form.prompt,
44300
+ placeholder: t("schedule.form.promptPlaceholder"),
44301
+ onChange: (event) => set({ prompt: event.currentTarget.value })
44302
+ })]
44303
+ }),
44304
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.SegmentedControl, {
44305
+ id: `${id}-unit`,
44306
+ label: t("schedule.form.cadence"),
44307
+ value: form.unit,
44308
+ options: [
44309
+ {
44310
+ value: "minutes",
44311
+ label: t("schedule.form.unit.minutes")
44312
+ },
44313
+ {
44314
+ value: "hours",
44315
+ label: t("schedule.form.unit.hours")
44316
+ },
44317
+ {
44318
+ value: "daily",
44319
+ label: t("schedule.form.unit.daily")
44320
+ },
44321
+ {
44322
+ value: "weekly",
44323
+ label: t("schedule.form.unit.weekly")
44324
+ },
44325
+ {
44326
+ value: "once",
44327
+ label: t("schedule.form.unit.once")
44328
+ },
44329
+ {
44330
+ value: "cron",
44331
+ label: t("schedule.form.unit.cron")
44332
+ }
44333
+ ],
44334
+ onChange: (unit) => set({ unit })
44335
+ }),
44336
+ form.unit === "minutes" || form.unit === "hours" ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
44337
+ className: "bh-schedule-field",
44338
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: form.unit === "hours" ? t("schedule.form.everyHours") : t("schedule.form.everyMinutes") }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Input, {
44339
+ type: "number",
44340
+ min: 1,
44341
+ step: 1,
44342
+ value: form.every,
44343
+ onChange: (event) => set({ every: event.target.value })
44344
+ })]
44345
+ }) : null,
44346
+ form.unit === "weekly" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
44347
+ className: "bh-schedule-weekdays",
44348
+ role: "group",
44349
+ "aria-label": t("schedule.form.weekdays"),
44350
+ children: WEEKDAYS.map(([day, key]) => {
44351
+ const on = form.weekdays.includes(day);
44352
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
44353
+ type: "button",
44354
+ className: "bh-schedule-weekday",
44355
+ "aria-pressed": on,
44356
+ onClick: () => set({ weekdays: on ? form.weekdays.filter((value) => value !== day) : [...form.weekdays, day].sort((a, b) => a - b) }),
44357
+ children: t(key)
44358
+ }, day);
44359
+ })
44360
+ }) : null,
44361
+ form.unit === "cron" ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
44362
+ className: "bh-schedule-field",
44363
+ children: [
44364
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: t("schedule.form.cron") }),
44365
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Input, {
44366
+ value: form.expression,
44367
+ spellCheck: false,
44368
+ placeholder: "0 9 * * 1-5",
44369
+ className: "bh-schedule-cron",
44370
+ onChange: (event) => set({ expression: event.target.value })
44371
+ }),
44372
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("small", {
44373
+ className: "bh-schedule-muted",
44374
+ children: t("schedule.form.cronHint")
44375
+ })
44376
+ ]
44377
+ }) : null,
44378
+ form.unit === "minutes" || form.unit === "hours" ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
44379
+ className: "bh-schedule-row",
44380
+ children: [
44381
+ form.unit === "once" ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
44382
+ className: "bh-schedule-field",
44383
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: t("schedule.form.date") }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Input, {
44384
+ type: "date",
44385
+ value: form.date,
44386
+ onChange: (event) => set({ date: event.target.value })
44387
+ })]
44388
+ }) : null,
44389
+ form.unit === "cron" ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
44390
+ className: "bh-schedule-field",
44391
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: t("schedule.form.time") }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Input, {
44392
+ type: "time",
44393
+ value: form.time,
44394
+ onChange: (event) => set({ time: event.target.value })
44395
+ })]
44396
+ }),
44397
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
44398
+ className: "bh-schedule-field bh-schedule-zone",
44399
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: t("schedule.form.timeZone") }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Input, {
44400
+ value: form.timeZone,
44401
+ onChange: (event) => set({ timeZone: event.target.value })
44402
+ })]
44403
+ })
44404
+ ]
44405
+ }),
44406
+ form.unit === "once" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
44407
+ className: "bh-schedule-muted",
44408
+ children: t("schedule.form.onceHint")
44409
+ }) : null,
44410
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(SchedulePreview, {
44411
+ trigger,
44412
+ preview,
44413
+ t
44414
+ }),
44415
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
44416
+ className: "bh-schedule-lock",
44417
+ children: [
44418
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
44419
+ className: "bh-card-icon",
44420
+ "aria-hidden": "true",
44421
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ChannelSidebarIcon, {
44422
+ name: form.locked ? "lock" : "lock-open",
44423
+ size: 16
44424
+ })
44425
+ }),
44426
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
44427
+ className: "bh-card-body",
44428
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
44429
+ className: "bh-card-title",
44430
+ children: t("schedule.form.lock")
44431
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
44432
+ className: "bh-card-meta",
44433
+ children: t("schedule.form.lockHint")
44434
+ })]
44435
+ }),
44436
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Switch, {
44437
+ checked: form.locked,
44438
+ label: t("schedule.form.lock"),
44439
+ onChange: (locked) => set({ locked })
44440
+ })
44441
+ ]
44442
+ }),
44443
+ costly ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
44444
+ className: "bh-schedule-warning",
44445
+ role: "note",
44446
+ children: t("schedule.form.cost")
44447
+ }) : null,
44448
+ error === void 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
44449
+ className: "bh-schedule-error",
44450
+ role: "alert",
44451
+ children: error
44452
+ }),
44453
+ editing === void 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
44454
+ className: "bh-schedule-history",
44455
+ "aria-label": t("schedule.history"),
44456
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h4", { children: t("schedule.history") }), history === void 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(LoadingSkeleton, {
44457
+ kind: "sidebar",
44458
+ label: t("schedule.loading")
44459
+ }) : history.length === 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
44460
+ className: "bh-schedule-muted",
44461
+ children: t("schedule.historyEmpty")
44462
+ }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("ol", { children: history.map((firing) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("li", {
44463
+ className: "bh-schedule-firing",
44464
+ children: [
44465
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
44466
+ className: "bh-schedule-firing-time",
44467
+ children: formatScheduleTime(firing.occurrenceAt)
44468
+ }),
44469
+ firing.trigger === "manual" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Tag, {
44470
+ tone: "outline",
44471
+ children: t("schedule.history.manual")
44472
+ }) : null,
44473
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(FiringTag, {
44474
+ firing,
44475
+ t
44476
+ }),
44477
+ firing.sessionId === void 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
44478
+ type: "button",
44479
+ className: "bh-schedule-link",
44480
+ onClick: () => onOpenSession(firing.sessionId),
44481
+ children: t("schedule.openSession")
44482
+ })
44483
+ ]
44484
+ }, firing.id)) })]
44485
+ })
44486
+ ]
44487
+ })
44488
+ });
44489
+ }
44490
+ function ScheduleDialog({ botSlug, scheduleId, actions, t, onClose, onChanged }) {
44491
+ const [schedule, setSchedule] = (0, react.useState)(scheduleId === void 0 ? null : void 0);
44492
+ const [history, setHistory] = (0, react.useState)();
44493
+ const [busy, setBusy] = (0, react.useState)(false);
44494
+ const [error, setError] = (0, react.useState)();
44495
+ const mount = useMountedResource(() => {
44496
+ if (scheduleId === void 0) return;
44497
+ let live = true;
44498
+ actions.botSchedules(botSlug).then((rows) => {
44499
+ if (live) setSchedule(rows.find((row) => row.id === scheduleId) ?? null);
44500
+ }).catch((cause) => {
44501
+ if (!live) return;
44502
+ setSchedule(null);
44503
+ setError(errorMessage(cause));
44504
+ });
44505
+ actions.botScheduleHistory(botSlug, scheduleId).then((rows) => {
44506
+ if (live) setHistory(rows);
44507
+ }).catch(() => {
44508
+ if (live) setHistory([]);
44509
+ });
44510
+ return () => {
44511
+ live = false;
44512
+ };
44513
+ }, [
44514
+ actions,
44515
+ botSlug,
44516
+ scheduleId
44517
+ ]);
44518
+ const run = (action) => {
44519
+ setBusy(true);
44520
+ setError(void 0);
44521
+ action().then(() => {
44522
+ onChanged?.();
44523
+ onClose();
44524
+ }).catch((cause) => setError(errorMessage(cause))).finally(() => setBusy(false));
44525
+ };
44526
+ if (schedule === void 0) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(react_jsx_runtime.Fragment, { children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
44527
+ ref: mount,
44528
+ hidden: true
44529
+ }) });
44530
+ if (schedule === null && scheduleId !== void 0) return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
44531
+ ref: mount,
44532
+ hidden: true
44533
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Modal, {
44534
+ open: true,
44535
+ onClose,
44536
+ title: t("schedule.edit"),
44537
+ closeLabel: t("schedule.close"),
44538
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
44539
+ className: "bh-schedule-muted",
44540
+ role: "status",
44541
+ children: error ?? t("schedule.missing")
44542
+ })
44543
+ })] });
44544
+ const editing = schedule ?? void 0;
44545
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
44546
+ ref: mount,
44547
+ hidden: true
44548
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ScheduleEditor, {
44549
+ open: true,
44550
+ initial: editing === void 0 ? emptyScheduleForm() : scheduleFormOf(editing),
44551
+ editing,
44552
+ busy,
44553
+ error,
44554
+ history,
44555
+ onClose,
44556
+ onSave: (form) => {
44557
+ const trigger = scheduleTriggerOf(form);
44558
+ if (trigger === void 0) return;
44559
+ run(() => editing === void 0 ? actions.createBotSchedule(botSlug, {
44560
+ title: form.title,
44561
+ prompt: form.prompt,
44562
+ trigger,
44563
+ ...form.locked ? { locked: true } : {}
44564
+ }) : actions.updateBotSchedule(botSlug, editing.id, {
44565
+ title: form.title,
44566
+ prompt: form.prompt,
44567
+ trigger,
44568
+ ...form.locked === editing.locked ? {} : { locked: form.locked }
44569
+ }));
44570
+ },
44571
+ onDelete: () => {
44572
+ if (editing !== void 0) run(() => actions.deleteBotSchedule(botSlug, editing.id));
44573
+ },
44574
+ onOpenSession: (sessionId) => {
44575
+ onClose();
44576
+ actions.openSession(sessionId);
44577
+ },
44578
+ preview: actions.botSchedulePreview,
44579
+ t
44580
+ })] });
44581
+ }
44582
+ function BotSchedulesEntry({ botSlug, actions, t }) {
44583
+ const [schedules, setSchedules] = (0, react.useState)();
44584
+ const [loadError, setLoadError] = (0, react.useState)();
44585
+ const [editor, setEditor] = (0, react.useState)();
44586
+ const [toggling, setToggling] = (0, react.useState)();
44587
+ const [started, setStarted] = (0, react.useState)();
44588
+ const active = (0, react.useRef)(void 0);
44589
+ const editorKey = (0, react.useRef)(0);
44590
+ const createRevision = (0, react.useSyncExternalStore)(subscribeCreate, () => botSlug === void 0 ? 0 : createRequests.get(botSlug) ?? 0, () => 0);
44591
+ const seenCreate = (0, react.useRef)(createRevision);
44592
+ if (seenCreate.current !== createRevision) {
44593
+ seenCreate.current = createRevision;
44594
+ editorKey.current += 1;
44595
+ setEditor({
44596
+ key: editorKey.current,
44597
+ scheduleId: void 0
44598
+ });
44599
+ }
44600
+ const refresh = async (slug) => {
44601
+ try {
44602
+ const rows = await actions.botSchedules(slug);
44603
+ if (active.current !== slug) return;
44604
+ setSchedules(rows);
44605
+ setLoadError(void 0);
44606
+ } catch (cause) {
44607
+ if (active.current === slug) setLoadError(errorMessage(cause));
44608
+ }
44609
+ };
44610
+ const mount = useMountedResource(() => {
44611
+ active.current = botSlug;
44612
+ setSchedules(void 0);
44613
+ setLoadError(void 0);
44614
+ if (botSlug === void 0) return;
44615
+ refresh(botSlug);
44616
+ const timer = setInterval(() => void refresh(botSlug), REFRESH_MS);
44617
+ const unsubscribe = subscribeMessagingDefaults(() => void refresh(botSlug));
44618
+ return () => {
44619
+ clearInterval(timer);
44620
+ unsubscribe();
44621
+ active.current = void 0;
44622
+ };
44623
+ }, [actions, botSlug]);
44624
+ if (botSlug === void 0) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { ref: mount });
44625
+ const openEditor = (scheduleId) => {
44626
+ editorKey.current += 1;
44627
+ setEditor({
44628
+ key: editorKey.current,
44629
+ scheduleId
44630
+ });
44631
+ };
44632
+ const change = (schedule, patch) => {
44633
+ setToggling(schedule.id);
44634
+ setLoadError(void 0);
44635
+ actions.updateBotSchedule(botSlug, schedule.id, patch).then(() => refresh(botSlug)).catch((cause) => setLoadError(errorMessage(cause))).finally(() => setToggling(void 0));
44636
+ };
44637
+ const runNow = (schedule) => {
44638
+ setToggling(schedule.id);
44639
+ setLoadError(void 0);
44640
+ actions.runBotScheduleNow(botSlug, schedule.id).then(() => {
44641
+ setStarted(schedule.id);
44642
+ setTimeout(() => setStarted((current) => current === schedule.id ? void 0 : current), STARTED_MS);
44643
+ return refresh(botSlug);
44644
+ }).catch((cause) => setLoadError(errorMessage(cause))).finally(() => setToggling(void 0));
44645
+ };
44646
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
44647
+ ref: mount,
44648
+ className: "bh-schedules",
44649
+ "data-bot-schedules": "",
44650
+ children: [
44651
+ schedules === void 0 && loadError === void 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(LoadingSkeleton, {
44652
+ kind: "sidebar",
44653
+ label: t("schedule.loading")
44654
+ }) : null,
44655
+ loadError === void 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
44656
+ className: "bh-schedule-error",
44657
+ role: "alert",
44658
+ children: loadError
44659
+ }),
44660
+ schedules !== void 0 && schedules.length === 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
44661
+ className: "bh-schedule-empty",
44662
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
44663
+ className: "bh-schedule-muted",
44664
+ children: t("schedule.empty")
44665
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
44666
+ variant: "outline",
44667
+ size: "sm",
44668
+ onClick: () => requestCreate(botSlug),
44669
+ children: t("schedule.add")
44670
+ })]
44671
+ }) : null,
44672
+ schedules === void 0 || schedules.length === 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(SidebarCardList, {
44673
+ label: t("entry.schedules"),
44674
+ children: schedules.map((schedule) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(SidebarCardRow, {
44675
+ icon: "alarm-clock",
44676
+ title: schedule.title,
44677
+ muted: !schedule.enabled,
44678
+ dialog: true,
44679
+ onClick: () => openEditor(schedule.id),
44680
+ chips: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
44681
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Tag, {
44682
+ tone: schedule.enabled ? "info" : "outline",
44683
+ children: scheduleCadenceLabel(schedule.trigger, t)
44684
+ }),
44685
+ schedule.lastFiring === void 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(FiringTag, {
44686
+ firing: schedule.lastFiring,
44687
+ t
44688
+ }),
44689
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Tooltip, {
44690
+ label: t(`schedule.creator.${schedule.creator}`),
44691
+ side: "top",
44692
+ portal: true,
44693
+ delayMs: TOOLTIP_DELAY_MS,
44694
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
44695
+ className: "bh-card-glyph",
44696
+ tabIndex: 0,
44697
+ "aria-label": t(`schedule.creator.${schedule.creator}`),
44698
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ChannelSidebarIcon, {
44699
+ name: schedule.creator === "human" ? "user" : "bot",
44700
+ size: 12
44701
+ })
44702
+ })
44703
+ })
44704
+ ] }),
44705
+ meta: started === schedule.id ? t("schedule.runNow.started") : schedule.enabled && schedule.nextRunAt !== void 0 ? t("schedule.next", { time: formatScheduleTime(schedule.nextRunAt) }) : schedule.trigger.kind === "once" && schedule.lastFiring !== void 0 ? t("schedule.done") : t("schedule.paused"),
44706
+ trailing: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
44707
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Tooltip, {
44708
+ label: t("schedule.runNow.tooltip"),
44709
+ side: "top",
44710
+ portal: true,
44711
+ delayMs: TOOLTIP_DELAY_MS,
44712
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
44713
+ type: "button",
44714
+ className: "bh-card-action bh-schedule-run-now",
44715
+ disabled: toggling !== void 0,
44716
+ "aria-label": t("schedule.runNow", { title: schedule.title }),
44717
+ onClick: () => runNow(schedule),
44718
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ChannelSidebarIcon, {
44719
+ name: "play",
44720
+ size: 14
44721
+ })
44722
+ })
44723
+ }),
44724
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Tooltip, {
44725
+ label: schedule.locked ? t("schedule.locked") : t("schedule.lock.tooltip"),
44726
+ side: "top",
44727
+ portal: true,
44728
+ delayMs: TOOLTIP_DELAY_MS,
44729
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
44730
+ type: "button",
44731
+ className: "bh-card-action bh-schedule-lock-toggle",
44732
+ "data-locked": schedule.locked ? "true" : void 0,
44733
+ "aria-pressed": schedule.locked,
44734
+ disabled: toggling !== void 0,
44735
+ "aria-label": schedule.locked ? t("schedule.unlock", { title: schedule.title }) : t("schedule.lock", { title: schedule.title }),
44736
+ onClick: () => change(schedule, { locked: !schedule.locked }),
44737
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ChannelSidebarIcon, {
44738
+ name: schedule.locked ? "lock" : "lock-open",
44739
+ size: 14
44740
+ })
44741
+ })
44742
+ }),
44743
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Tooltip, {
44744
+ label: schedule.enabled ? t("schedule.toggle.pause") : t("schedule.toggle.resume"),
44745
+ side: "top",
44746
+ portal: true,
44747
+ delayMs: TOOLTIP_DELAY_MS,
44748
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
44749
+ className: "bh-schedule-switch",
44750
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Switch, {
44751
+ checked: schedule.enabled,
44752
+ disabled: toggling !== void 0,
44753
+ label: t("schedule.enable", { title: schedule.title }),
44754
+ onChange: (enabled) => change(schedule, { enabled })
44755
+ })
44756
+ })
44757
+ })
44758
+ ] })
44759
+ }, schedule.id))
44760
+ }),
44761
+ editor === void 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ScheduleDialog, {
44762
+ botSlug,
44763
+ scheduleId: editor.scheduleId,
44764
+ actions,
44765
+ t,
44766
+ onClose: () => setEditor(void 0),
44767
+ onChanged: () => void refresh(botSlug)
44768
+ }, editor.key)
44769
+ ]
44770
+ });
44771
+ }
42594
44772
  //#endregion
42595
44773
  //#region packages/client/src/client/session-rows.ts
42596
44774
  function rowStatus(owned, native) {
@@ -42648,6 +44826,14 @@ My relationship with the Human, established lore and topics to avoid: [fill in].
42648
44826
  }
42649
44827
  //#endregion
42650
44828
  //#region packages/client/src/client/sessions-entry.tsx
44829
+ const STATUS_TONE = {
44830
+ running: "success",
44831
+ stopping: "warning",
44832
+ attention: "danger",
44833
+ stopped: "quiet",
44834
+ idle: "outline",
44835
+ unavailable: "quiet"
44836
+ };
42651
44837
  function workspaceName(path) {
42652
44838
  return path?.split(/[\\/]/u).filter(Boolean).at(-1);
42653
44839
  }
@@ -42682,43 +44868,38 @@ My relationship with the Human, established lore and topics to avoid: [fill in].
42682
44868
  idle: t("sessions.status.idle"),
42683
44869
  unavailable: t("sessions.status.unavailable")
42684
44870
  };
42685
- const renderRow = (row, showWorkspace) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
42686
- type: "button",
42687
- className: "bh-session-row",
42688
- title: row.cwd,
42689
- disabled: native.byId[row.sessionId] === void 0,
42690
- onClick: () => actions.openSession(row.sessionId),
42691
- children: [
42692
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
42693
- className: "bh-session-title",
42694
- children: row.title
42695
- }),
42696
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
42697
- className: "bh-session-meta",
42698
- children: [
42699
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Tag, {
42700
- tone: "neutral",
42701
- children: row.role === "orchestrator" ? t("sessions.role.orchestrator") : t("sessions.role.assignment")
42702
- }),
42703
- row.assignmentAccessMode === "danger-full-access" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
42704
- title: t("sessions.access.dangerDetails"),
42705
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Tag, {
42706
- tone: "warning",
42707
- children: t("sessions.access.danger")
42708
- })
42709
- }) : null,
42710
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
42711
- className: "bh-session-status bh-session-status-" + row.status,
42712
- children: statusLabel[row.status]
44871
+ const renderRow = (row, showWorkspace) => {
44872
+ const role = row.role === "orchestrator" ? t("sessions.role.orchestrator") : t("sessions.role.assignment");
44873
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(SidebarCardRow, {
44874
+ icon: row.role === "orchestrator" ? "bot" : "list-checks",
44875
+ iconLabel: role,
44876
+ title: row.title,
44877
+ hint: row.cwd,
44878
+ mainClassName: "bh-session-row",
44879
+ state: row.status,
44880
+ muted: native.byId[row.sessionId] === void 0,
44881
+ disabled: native.byId[row.sessionId] === void 0,
44882
+ onClick: () => void actions.openSession(row.sessionId),
44883
+ chips: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
44884
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Tag, {
44885
+ tone: "neutral",
44886
+ children: role
44887
+ }),
44888
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Tag, {
44889
+ tone: STATUS_TONE[row.status],
44890
+ children: statusLabel[row.status]
44891
+ }),
44892
+ row.assignmentAccessMode === "danger-full-access" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
44893
+ title: t("sessions.access.dangerDetails"),
44894
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Tag, {
44895
+ tone: "warning",
44896
+ children: t("sessions.access.danger")
42713
44897
  })
42714
- ]
42715
- }),
42716
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
42717
- className: "bh-session-meta",
42718
- children: [showWorkspace ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: workspaceName(row.cwd) ?? t("sessions.workspace.unknown") }) : null, /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: formatRelativeTime(row.updatedAt, Date.now(), t) })]
42719
- })
42720
- ]
42721
- }, row.sessionId);
44898
+ }) : null
44899
+ ] }),
44900
+ meta: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [showWorkspace ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: workspaceName(row.cwd) ?? t("sessions.workspace.unknown") }) : null, /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: formatRelativeTime(row.updatedAt, Date.now(), t) })] })
44901
+ }, row.sessionId);
44902
+ };
42722
44903
  return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
42723
44904
  className: "bh-sessions",
42724
44905
  ref: mount,
@@ -42735,7 +44916,10 @@ My relationship with the Human, established lore and topics to avoid: [fill in].
42735
44916
  className: "bh-note",
42736
44917
  children: t("sessions.empty")
42737
44918
  }) : null,
42738
- preference.layout === "flat" ? rows.map((row) => renderRow(row, true)) : groups.map((group) => {
44919
+ preference.layout === "flat" ? rows.length === 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(SidebarCardList, {
44920
+ label: t("entry.sessions"),
44921
+ children: rows.map((row) => renderRow(row, true))
44922
+ }) : groups.map((group) => {
42739
44923
  const expanded = !preference.collapsedWorkspaces.includes(group.key);
42740
44924
  return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
42741
44925
  className: "bh-session-workspace-group",
@@ -42759,8 +44943,8 @@ My relationship with the Human, established lore and topics to avoid: [fill in].
42759
44943
  children: group.rows.length
42760
44944
  })
42761
44945
  ]
42762
- }), expanded ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
42763
- className: "bh-session-workspace-rows",
44946
+ }), expanded ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(SidebarCardList, {
44947
+ label: group.name ?? t("sessions.workspace.unknown"),
42764
44948
  children: group.rows.map((row) => renderRow(row, false))
42765
44949
  }) : null]
42766
44950
  }, group.key);
@@ -42774,6 +44958,24 @@ My relationship with the Human, established lore and topics to avoid: [fill in].
42774
44958
  //#endregion
42775
44959
  //#region packages/client/src/client/channel-sidebar-builtins.tsx
42776
44960
  const inactiveSubscribe = () => () => {};
44961
+ const INBOX_STATE_TONE = {
44962
+ pending: "warning",
44963
+ processing: "info",
44964
+ observed: "info",
44965
+ deferred: "neutral",
44966
+ "needs-repair": "danger",
44967
+ handled: "success",
44968
+ ignored: "quiet"
44969
+ };
44970
+ function inboxIcon(item) {
44971
+ if (item.sourceKind === "schedule") return "alarm-clock";
44972
+ if (item.sourceKind === "memory-change") return "git-branch";
44973
+ if (item.sourceKind === "assignment-report" || item.assignmentSessionId !== void 0) return "list-checks";
44974
+ if (item.externalOrigin !== void 0) return "globe";
44975
+ if (item.authorKind === "human") return "user";
44976
+ if (item.authorKind === "bot") return "bot";
44977
+ return "inbox";
44978
+ }
42777
44979
  function GroupManagementEntry(props) {
42778
44980
  const group = useClientState().conversation.channel;
42779
44981
  if (group?.type !== "group") return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(react_jsx_runtime.Fragment, {});
@@ -42971,13 +45173,28 @@ My relationship with the Human, established lore and topics to avoid: [fill in].
42971
45173
  const [externalError, setExternalError] = (0, react.useState)(false);
42972
45174
  const [fileBusy, setFileBusy] = (0, react.useState)();
42973
45175
  const [fileError, setFileError] = (0, react.useState)(false);
45176
+ const [scheduleOpen, setScheduleOpen] = (0, react.useState)(false);
42974
45177
  const fileRequest = (0, react.useRef)();
42975
- const download = async (attachmentId, name) => {
42976
- if (fileBusy !== void 0) return;
45178
+ const [imagePreviews, setImagePreviews] = (0, react.useState)({});
45179
+ const [imageErrors, setImageErrors] = (0, react.useState)({});
45180
+ const previewUrls = (0, react.useRef)(/* @__PURE__ */ new Set());
45181
+ const previewLifecycle = (0, react.useCallback)((node) => {
45182
+ if (node !== null) return;
45183
+ ++externalRequest.current;
45184
+ fileRequest.current?.abort();
45185
+ for (const url of previewUrls.current) URL.revokeObjectURL(url);
45186
+ previewUrls.current.clear();
45187
+ }, []);
45188
+ const download = async (attachmentId, name, preview = false) => {
45189
+ if (fileRequest.current !== void 0) return;
42977
45190
  const controller = new AbortController();
42978
45191
  fileRequest.current = controller;
42979
45192
  setFileBusy(attachmentId);
42980
45193
  setFileError(false);
45194
+ if (preview) setImageErrors((value) => ({
45195
+ ...value,
45196
+ [attachmentId]: false
45197
+ }));
42981
45198
  try {
42982
45199
  const response = await fetch("/api/botharness/attachment?" + new URLSearchParams({
42983
45200
  slug: item.botSlug,
@@ -42990,7 +45207,21 @@ My relationship with the Human, established lore and topics to avoid: [fill in].
42990
45207
  if (!response.ok) throw new Error("Download unavailable");
42991
45208
  const blob = await response.blob();
42992
45209
  controller.signal.throwIfAborted();
45210
+ if (preview && ![
45211
+ "image/png",
45212
+ "image/jpeg",
45213
+ "image/gif",
45214
+ "image/webp"
45215
+ ].includes(blob.type)) throw new Error("Unsupported image");
42993
45216
  const url = URL.createObjectURL(blob);
45217
+ if (preview) {
45218
+ previewUrls.current.add(url);
45219
+ setImagePreviews((value) => ({
45220
+ ...value,
45221
+ [attachmentId]: url
45222
+ }));
45223
+ return;
45224
+ }
42994
45225
  const anchor = document.createElement("a");
42995
45226
  anchor.href = url;
42996
45227
  anchor.download = name;
@@ -42999,7 +45230,13 @@ My relationship with the Human, established lore and topics to avoid: [fill in].
42999
45230
  anchor.remove();
43000
45231
  setTimeout(() => URL.revokeObjectURL(url), 1e3);
43001
45232
  } catch {
43002
- if (!controller.signal.aborted) setFileError(true);
45233
+ if (!controller.signal.aborted) {
45234
+ if (preview) setImageErrors((value) => ({
45235
+ ...value,
45236
+ [attachmentId]: true
45237
+ }));
45238
+ else setFileError(true);
45239
+ }
43003
45240
  } finally {
43004
45241
  if (fileRequest.current === controller) {
43005
45242
  fileRequest.current = void 0;
@@ -43008,19 +45245,35 @@ My relationship with the Human, established lore and topics to avoid: [fill in].
43008
45245
  }
43009
45246
  };
43010
45247
  const memoryChange = item.sourceKind === "memory-change";
43011
- const summary = memoryChange ? item.summary.replace(/^Memory changed since your last turn:\s*/u, "").replace(/^Working Memory changes since your last turn:\s*/u, "").replace(/^\?\? /u, "") : item.summary;
43012
- const author = item.externalOrigin !== void 0 ? `${externalPlatformLabel(item.externalOrigin.platform, t)} · ${externalSenderLabel(item.externalOrigin, t)}` : memoryChange ? t("inbox.memoryChange") : item.sourceKind === "assignment-report" ? t("inbox.assignment") : item.authorKind === "human" ? t("main.author.human") : item.authorKind === "bot" ? bots.find((bot) => bot.slug === item.authorBotSlug)?.displayName ?? item.authorBotSlug ?? t("inbox.bot") : t("inbox.system");
45248
+ const summary = memoryChange ? item.summary.replace(/^Memory changed since your last turn:\s*/u, "").replace(/^Working Memory changes since your last turn:\s*/u, "").replace(/^\?\? /u, "") : item.externalOrigin?.voice?.transcript === "unavailable" ? t("im.voiceTranscriptUnavailableHint") : item.summary;
45249
+ const author = item.externalOrigin !== void 0 ? `${externalPlatformLabel(item.externalOrigin.platform, t)} · ${externalSenderLabel(item.externalOrigin, t)}` : memoryChange ? t("inbox.memoryChange") : item.sourceKind === "assignment-report" ? t("inbox.assignment") : item.sourceKind === "schedule" ? t("inbox.schedule") : item.authorKind === "human" ? t("main.author.human") : item.authorKind === "bot" ? bots.find((bot) => bot.slug === item.authorBotSlug)?.displayName ?? item.authorBotSlug ?? t("inbox.bot") : t("inbox.system");
43013
45250
  const open = async () => {
43014
45251
  if (!item.sourceAvailable) return;
45252
+ if (item.scheduleId !== void 0) {
45253
+ setScheduleOpen(true);
45254
+ return;
45255
+ }
43015
45256
  if (item.externalOrigin !== void 0) {
43016
45257
  const request = ++externalRequest.current;
45258
+ fileRequest.current?.abort();
45259
+ fileRequest.current = void 0;
45260
+ setFileBusy(void 0);
45261
+ for (const url of previewUrls.current) URL.revokeObjectURL(url);
45262
+ previewUrls.current.clear();
45263
+ setImagePreviews({});
45264
+ setImageErrors({});
43017
45265
  setExternal(void 0);
43018
45266
  setExternalOpen(true);
43019
45267
  setExternalError(false);
43020
45268
  setFileError(false);
43021
45269
  try {
43022
45270
  const source = await actions.messagingSource(item.botSlug, item.id);
43023
- if (request === externalRequest.current) setExternal(source);
45271
+ if (request !== externalRequest.current) return;
45272
+ setExternal(source);
45273
+ for (const file of source.event.attachments ?? []) {
45274
+ if (request !== externalRequest.current) return;
45275
+ if (file.mediaType?.startsWith("image/")) await download(file.id, file.name, true);
45276
+ }
43024
45277
  } catch {
43025
45278
  if (request === externalRequest.current) setExternalError(true);
43026
45279
  }
@@ -43034,89 +45287,127 @@ My relationship with the Human, established lore and topics to avoid: [fill in].
43034
45287
  await actions.openChannel(item.sourceChannelId);
43035
45288
  await actions.openAround(item.sourceChannelId, item.sourceMessageId);
43036
45289
  };
43037
- const content = /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
43038
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
43039
- className: "bh-inbox-item-summary",
43040
- children: summary || t("inbox.system")
43041
- }),
43042
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
43043
- className: "bh-inbox-item-meta",
43044
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
45290
+ const imageAttachments = external?.event.attachments?.filter((file) => file.mediaType?.startsWith("image/")) ?? [];
45291
+ const unavailable = !memoryChange && !item.sourceAvailable;
45292
+ const row = /* @__PURE__ */ (0, react_jsx_runtime.jsx)(SidebarCardRow, {
45293
+ icon: inboxIcon(item),
45294
+ iconLabel: author,
45295
+ title: summary || t("inbox.system"),
45296
+ titleClassName: "bh-inbox-item-summary",
45297
+ hint: summary,
45298
+ mainClassName: memoryChange ? "bh-inbox-item bh-inbox-item-info" : "bh-inbox-item",
45299
+ state: item.state,
45300
+ muted: unavailable,
45301
+ disabled: !item.sourceAvailable,
45302
+ dialog: item.scheduleId !== void 0 || item.externalOrigin !== void 0,
45303
+ ...memoryChange ? {} : { onClick: () => void open() },
45304
+ chips: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
45305
+ item.externalOrigin?.voice ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Tag, {
45306
+ tone: item.externalOrigin.voice.transcript === "platform" ? "info" : "warning",
45307
+ children: t(item.externalOrigin.voice.transcript === "platform" ? "im.voiceTranscriptPlatform" : "im.voiceTranscriptUnavailable")
45308
+ }) : null,
45309
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Tag, {
45310
+ tone: INBOX_STATE_TONE[item.state],
45311
+ children: t(`inbox.state.${item.state}`)
45312
+ }),
45313
+ item.assignmentReportState === void 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Tag, {
45314
+ tone: "outline",
45315
+ children: t(`inbox.report.${item.assignmentReportState}`)
45316
+ })
45317
+ ] }),
45318
+ meta: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
45319
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
43045
45320
  className: "bh-inbox-item-author",
43046
45321
  title: author,
43047
45322
  children: author
43048
- }), item.assignmentReportState === void 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: t(`inbox.report.${item.assignmentReportState}`) })]
43049
- }),
43050
- item.externalOrigin === void 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
43051
- className: "bh-inbox-item-meta bh-inbox-item-origin",
43052
- children: [
43053
- item.externalOrigin.accountName,
43054
- " · ",
43055
- item.externalOrigin.conversationName
43056
- ]
43057
- }),
43058
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
43059
- className: "bh-inbox-item-footer",
43060
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
43061
- className: "bh-inbox-item-state",
43062
- "data-state": item.state,
43063
- children: t(`inbox.state.${item.state}`)
43064
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
45323
+ }),
45324
+ item.externalOrigin === void 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
45325
+ className: "bh-inbox-item-origin",
45326
+ children: [
45327
+ item.externalOrigin.accountName,
45328
+ " · ",
45329
+ item.externalOrigin.conversationName
45330
+ ]
45331
+ }),
45332
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
43065
45333
  className: "bh-inbox-item-time",
43066
45334
  children: formatRelativeTime(Date.parse(item.createdAt), Date.now(), t)
43067
- })]
43068
- }),
43069
- !memoryChange && !item.sourceAvailable ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
43070
- className: "bh-inbox-item-meta",
43071
- children: t("inbox.sourceUnavailable")
43072
- }) : null
43073
- ] });
43074
- if (memoryChange) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
43075
- className: "bh-inbox-item bh-inbox-item-info",
43076
- children: content
45335
+ }),
45336
+ unavailable ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: t("inbox.sourceUnavailable") }) : null
45337
+ ] })
43077
45338
  });
43078
- return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
43079
- type: "button",
43080
- className: "bh-inbox-item",
43081
- "data-state": item.state,
43082
- title: summary,
43083
- disabled: !item.sourceAvailable,
43084
- onClick: () => void open(),
43085
- children: content
43086
- }), externalOpen ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Modal, {
43087
- open: true,
43088
- onClose: () => {
43089
- ++externalRequest.current;
43090
- fileRequest.current?.abort();
43091
- setExternalOpen(false);
43092
- },
43093
- title: t("im.sourceTitle"),
43094
- className: "bh-external-source-modal",
43095
- closeLabel: t("common.close"),
43096
- children: externalError ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
43097
- role: "alert",
43098
- children: t("im.sourceError")
43099
- }) : external === void 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", { children: t("im.sourceLoading") }) : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(ExternalSourceContent, {
43100
- source: external,
45339
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
45340
+ row,
45341
+ scheduleOpen && item.scheduleId !== void 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ScheduleDialog, {
45342
+ botSlug: item.botSlug,
45343
+ scheduleId: item.scheduleId,
45344
+ actions,
43101
45345
  t,
43102
- children: [external.event.attachments?.map((file) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
43103
- className: "bh-external-source-file",
43104
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", { children: [file.name, file.sizeBytes !== void 0 || file.mediaType ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("small", { children: [
43105
- " ",
43106
- "·",
43107
- " ",
43108
- [file.mediaType, file.sizeBytes === void 0 ? void 0 : `${new Intl.NumberFormat().format(file.sizeBytes)} B`].filter(Boolean).join(" · ")
43109
- ] }) : null] }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
43110
- disabled: fileBusy !== void 0,
43111
- onClick: () => void download(file.id, file.name),
43112
- children: fileBusy === file.id ? t("im.fileDownloading") : t("im.fileDownload")
43113
- })]
43114
- }, file.id)), fileError ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
43115
- role: "alert",
43116
- children: t("im.fileError")
43117
- }) : null]
43118
- })
43119
- }) : null] });
45346
+ onClose: () => setScheduleOpen(false)
45347
+ }) : null,
45348
+ externalOpen ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Modal, {
45349
+ open: true,
45350
+ onClose: () => {
45351
+ ++externalRequest.current;
45352
+ fileRequest.current?.abort();
45353
+ fileRequest.current = void 0;
45354
+ setFileBusy(void 0);
45355
+ setImagePreviews({});
45356
+ setImageErrors({});
45357
+ setExternalOpen(false);
45358
+ },
45359
+ title: t("im.sourceTitle"),
45360
+ className: "bh-external-source-modal",
45361
+ closeLabel: t("common.close"),
45362
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
45363
+ ref: previewLifecycle,
45364
+ children: externalError ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
45365
+ role: "alert",
45366
+ children: t("im.sourceError")
45367
+ }) : external === void 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", { children: t("im.sourceLoading") }) : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(ExternalSourceContent, {
45368
+ source: external,
45369
+ t,
45370
+ messageMedia: imageAttachments.length ? imageAttachments.map((file) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
45371
+ className: "bh-external-source-attachment",
45372
+ children: imagePreviews[file.id] ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("img", {
45373
+ className: "bh-external-source-image",
45374
+ src: imagePreviews[file.id],
45375
+ alt: file.name
45376
+ }) : imageErrors[file.id] ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
45377
+ role: "alert",
45378
+ children: t("im.fileError")
45379
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
45380
+ disabled: fileBusy !== void 0,
45381
+ onClick: () => void download(file.id, file.name, true),
45382
+ children: t("im.imageRetry")
45383
+ })] }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
45384
+ role: "status",
45385
+ children: t("im.fileDownloading")
45386
+ })
45387
+ }, file.id)) : void 0,
45388
+ children: [external.event.attachments?.map((file) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
45389
+ className: "bh-external-source-attachment",
45390
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
45391
+ className: "bh-external-source-file",
45392
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", { children: [file.name, file.sizeBytes !== void 0 || file.mediaType ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("small", { children: [
45393
+ " ",
45394
+ "·",
45395
+ " ",
45396
+ [file.mediaType, file.sizeBytes === void 0 ? void 0 : `${new Intl.NumberFormat().format(file.sizeBytes)} B`].filter(Boolean).join(" · ")
45397
+ ] }) : null] }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
45398
+ disabled: fileBusy !== void 0,
45399
+ onClick: () => void download(file.id, file.name),
45400
+ children: fileBusy === file.id ? t("im.fileDownloading") : t("im.fileDownload")
45401
+ })]
45402
+ })
45403
+ }, file.id)), fileError ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
45404
+ role: "alert",
45405
+ children: t("im.fileError")
45406
+ }) : null]
45407
+ })
45408
+ })
45409
+ }) : null
45410
+ ] });
43120
45411
  }
43121
45412
  function BotInboxGroup({ name, items, actions, t }) {
43122
45413
  const active = items.filter((item) => item.state !== "handled" && item.state !== "ignored");
@@ -43153,21 +45444,27 @@ My relationship with the Human, established lore and topics to avoid: [fill in].
43153
45444
  })
43154
45445
  ]
43155
45446
  }),
43156
- active.map((item) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(BotInboxItemRow, {
43157
- item,
43158
- actions,
43159
- t
43160
- }, item.id)),
45447
+ active.length === 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(SidebarCardList, {
45448
+ label: name,
45449
+ children: active.map((item) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(BotInboxItemRow, {
45450
+ item,
45451
+ actions,
45452
+ t
45453
+ }, item.id))
45454
+ }),
43161
45455
  history.length > 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("details", {
43162
45456
  className: "bh-inbox-history",
43163
45457
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("summary", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconChevronDownOutlineRegular, {
43164
45458
  className: "bh-inbox-group-chevron",
43165
45459
  size: 14
43166
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: t("inbox.handledHistory", { count: history.length }) })] }), history.map((item) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(BotInboxItemRow, {
43167
- item,
43168
- actions,
43169
- t
43170
- }, item.id))]
45460
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: t("inbox.handledHistory", { count: history.length }) })] }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(SidebarCardList, {
45461
+ label: t("inbox.handledHistory", { count: history.length }),
45462
+ children: history.map((item) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(BotInboxItemRow, {
45463
+ item,
45464
+ actions,
45465
+ t
45466
+ }, item.id))
45467
+ })]
43171
45468
  }) : null
43172
45469
  ]
43173
45470
  });
@@ -43284,6 +45581,15 @@ My relationship with the Human, established lore and topics to avoid: [fill in].
43284
45581
  badge: BotInboxBadge,
43285
45582
  visible: (state) => state.botInbox.status === "loading" || state.botInbox.status === "error" || state.botInbox.items.length > 0
43286
45583
  },
45584
+ {
45585
+ id: "schedules",
45586
+ icon: "alarm-clock",
45587
+ label: t("entry.schedules"),
45588
+ order: 16,
45589
+ scope: "personabot",
45590
+ component: BotSchedulesEntry,
45591
+ headerAction: BotSchedulesHeaderAction
45592
+ },
43287
45593
  {
43288
45594
  id: "workspace-grants",
43289
45595
  icon: "folder-key",
@@ -46232,7 +48538,13 @@ html[data-botharness-motion='full'] .bh-skeleton-line {
46232
48538
  .bh-avatar-media-composed svg { overflow: visible; }
46233
48539
  .bh-illustrated-head { transform-box: fill-box; transform-origin: 50% 65%; }
46234
48540
  .bh-illustrated-gaze { transform-box: fill-box; transform-origin: center; }
46235
- .bh-avatar-editor { display: grid; grid-template-columns: 160px minmax(0, 1fr); gap: 24px; padding: 20px 0; border-top: 1px solid var(--dsw-alias-border-l2); }
48541
+ .bh-avatar-editor { display: grid; grid-template-columns: 160px minmax(0, 1fr); gap: 24px; align-items: start; }
48542
+ .bh-card-list.bh-avatar-methods { display: grid; grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); gap: 12px; overflow: visible; border: 0; border-radius: 0; }
48543
+ .bh-card-list.bh-avatar-methods > .bh-card-row { border: 1px solid var(--dsw-alias-border-l2); border-radius: 10px; }
48544
+ .bh-card-list.bh-avatar-methods > .bh-card-row + .bh-card-row { border-top: 1px solid var(--dsw-alias-border-l2); }
48545
+ .bh-card-list.bh-avatar-methods > .bh-card-row[data-state="current"] { border-color: var(--bh-accent); }
48546
+ .bh-card-list.bh-avatar-methods .bh-card-title { white-space: normal; }
48547
+ .bh-card-list.bh-avatar-methods .bh-card-detail { padding-top: 2px; }
46236
48548
  .bh-avatar-editor-preview { display: flex; align-items: center; justify-content: center; }
46237
48549
  .bh-avatar-editor-controls h3 { margin: 0; font-size: 15px; }
46238
48550
  .bh-avatar-editor-controls p { margin: 8px 0 12px; color: var(--dsw-alias-label-secondary); font-size: 13px; }
@@ -46294,6 +48606,8 @@ html[data-botharness-motion='full'] .bh-skeleton-line {
46294
48606
  display: flex;
46295
48607
  align-items: flex-start;
46296
48608
  gap: 14px;
48609
+ padding-bottom: 20px;
48610
+ border-bottom: 1px solid var(--dsw-alias-border-l2);
46297
48611
  }
46298
48612
  .bh-profile-view-heading {
46299
48613
  display: flex;
@@ -48220,28 +50534,6 @@ html[data-botharness-motion='reduce'] .bh-composer-footer {
48220
50534
  .bh-inbox-history {
48221
50535
  margin-top: 4px;
48222
50536
  }
48223
- .bh-inbox-item {
48224
- display: flex;
48225
- flex-direction: column;
48226
- gap: 6px;
48227
- width: 100%;
48228
- margin: 0;
48229
- padding: 12px 10px;
48230
- border: 0;
48231
- border-bottom: 1px solid var(--bh-inbox-divider);
48232
- border-radius: var(--bh-inbox-radius-control);
48233
- background: transparent;
48234
- color: var(--bh-inbox-label);
48235
- text-align: left;
48236
- cursor: pointer;
48237
- }
48238
- .bh-inbox-item:hover:not(:disabled):not(.bh-inbox-item-info) {
48239
- background: var(--bh-hover);
48240
- }
48241
- .bh-inbox-item-info,
48242
- .bh-inbox-item:disabled {
48243
- cursor: default;
48244
- }
48245
50537
  .bh-inbox-item:focus-visible,
48246
50538
  .bh-inbox-group-head:focus-visible,
48247
50539
  .bh-inbox-history > summary:focus-visible {
@@ -48258,41 +50550,6 @@ html[data-botharness-motion='reduce'] .bh-composer-footer {
48258
50550
  font-weight: 500;
48259
50551
  line-height: 20px;
48260
50552
  }
48261
- .bh-inbox-item-meta {
48262
- display: flex;
48263
- flex-wrap: wrap;
48264
- align-items: baseline;
48265
- gap: 4px 8px;
48266
- color: var(--bh-inbox-muted);
48267
- font-size: 11px;
48268
- line-height: 16px;
48269
- overflow-wrap: anywhere;
48270
- }
48271
- .bh-inbox-item-author {
48272
- min-width: 0;
48273
- }
48274
- .bh-inbox-item-footer {
48275
- display: flex;
48276
- flex-wrap: wrap;
48277
- align-items: baseline;
48278
- justify-content: space-between;
48279
- gap: 4px 12px;
48280
- font-size: 11px;
48281
- line-height: 16px;
48282
- }
48283
- .bh-inbox-item-state {
48284
- font-weight: 500;
48285
- color: var(--bh-inbox-muted);
48286
- }
48287
- .bh-inbox-item-state[data-state="needs-repair"] {
48288
- color: var(--bh-inbox-error);
48289
- }
48290
- .bh-inbox-item-state[data-state="processing"] {
48291
- color: var(--bh-accent);
48292
- }
48293
- .bh-inbox-item-time {
48294
- color: var(--bh-inbox-muted);
48295
- }
48296
50553
  .bh-external-source-file {
48297
50554
  display: flex;
48298
50555
  align-items: center;
@@ -48300,6 +50557,7 @@ html[data-botharness-motion='reduce'] .bh-composer-footer {
48300
50557
  min-width: 0;
48301
50558
  margin-block: 8px;
48302
50559
  }
50560
+ .bh-external-source-image { display: block; max-width: 100%; max-height: 384px; object-fit: contain; margin-block: 8px; }
48303
50561
  .bh-external-source-file > span { flex: 1; min-width: 0; overflow-wrap: anywhere; }
48304
50562
  .bh-external-source-file > button { flex-shrink: 0; }
48305
50563
  .bh-external-source-modal {
@@ -48738,50 +50996,6 @@ html[data-botharness-motion='reduce'] .bh-session-workspace-heading svg {
48738
50996
  color: var(--dsw-alias-label-tertiary);
48739
50997
  font-size: 11px;
48740
50998
  }
48741
- .bh-session-workspace-rows {
48742
- padding-left: 10px;
48743
- }
48744
- .bh-session-row {
48745
- display: block;
48746
- width: 100%;
48747
- border: 0;
48748
- border-radius: 8px;
48749
- padding: 8px;
48750
- background: transparent;
48751
- color: inherit;
48752
- text-align: left;
48753
- cursor: pointer;
48754
- }
48755
- .bh-session-row:hover {
48756
- background: var(--bh-hover);
48757
- }
48758
- .bh-session-row:disabled {
48759
- opacity: 0.65;
48760
- cursor: default;
48761
- }
48762
- .bh-session-title {
48763
- overflow: hidden;
48764
- font-weight: 600;
48765
- text-overflow: ellipsis;
48766
- white-space: nowrap;
48767
- }
48768
- .bh-session-meta {
48769
- display: flex;
48770
- align-items: center;
48771
- gap: 8px;
48772
- margin-top: 3px;
48773
- color: var(--dsw-alias-label-tertiary);
48774
- font-size: 11px;
48775
- }
48776
- .bh-session-meta > span {
48777
- min-width: 0;
48778
- overflow: hidden;
48779
- text-overflow: ellipsis;
48780
- white-space: nowrap;
48781
- }
48782
- .bh-session-status-running { color: var(--dsw-alias-state-success-primary); }
48783
- .bh-session-status-stopping { color: var(--dsw-alias-state-warn-primary); }
48784
- .bh-session-status-attention { color: var(--dsw-alias-state-error-primary); }
48785
50999
 
48786
51000
  .bh-member-row {
48787
51001
  display: flex;
@@ -49202,6 +51416,18 @@ button.bh-bot-nav > svg {
49202
51416
  }
49203
51417
  .bh-assignment-limit-controls > :first-child { width: 76px; }
49204
51418
  .bh-assignment-limit-error { color: var(--dsw-alias-state-error-primary); font-size: 12px; line-height: 18px; }
51419
+ .bh-standing-limits > .bh-settings-row-desc { margin: 0 0 12px; }
51420
+ .bh-standing-limits-form { display: grid; grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); gap: 16px; }
51421
+ .bh-standing-limit-field { display: flex; flex-direction: column; gap: 6px; min-width: 0; }
51422
+ .bh-standing-limit-label { display: flex; align-items: baseline; gap: 6px; color: var(--dsw-alias-label-primary); font-size: 13px; font-weight: 500; }
51423
+ .bh-standing-limit-label code { color: var(--dsw-alias-label-tertiary); font-size: 12px; font-weight: 400; }
51424
+ .bh-standing-limit-input { display: flex; align-items: center; gap: 8px; }
51425
+ .bh-standing-limit-input > :first-child { width: 120px; }
51426
+ .bh-standing-limit-unit { color: var(--dsw-alias-label-tertiary); font-size: 12px; }
51427
+ .bh-standing-limits-actions { grid-column: 1 / -1; display: flex; flex-wrap: wrap; align-items: center; gap: 8px; }
51428
+ .bh-telemetry-row .bh-settings-row-desc a { color: var(--dsw-alias-state-business-primary); text-decoration: none; }
51429
+ .bh-telemetry-row .bh-settings-row-desc a:hover { text-decoration: underline; }
51430
+ .bh-telemetry-row-error { color: var(--dsw-alias-state-error-primary); font-size: 12px; line-height: 18px; }
49205
51431
  .bh-settings-selector {
49206
51432
  display: inline-flex;
49207
51433
  align-items: center;
@@ -49678,6 +51904,11 @@ html[data-botharness-motion='full'] .bh-motion-preview-sample i:nth-child(3) {
49678
51904
  .bh-memory-tree-row.bh-memory-row-selected { background: var(--bh-selected); }
49679
51905
  .bh-memory-tree-chevron-space { flex: none; width: 14px; }
49680
51906
  .bh-memory-tree-name { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
51907
+ .bh-memory-tree-row:has(.bh-memory-standing) > .bh-memory-tree-name { flex: none; }
51908
+ .bh-memory-standing { display: inline-flex; align-items: center; gap: 4px; min-width: 0; margin-left: auto; flex: 0 1 auto; color: var(--dsw-alias-label-tertiary); font-size: 11px; font-variant-numeric: tabular-nums; }
51909
+ .bh-memory-standing > :first-child { flex: none; }
51910
+ .bh-memory-standing-usage { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
51911
+ .bh-memory-standing-over .bh-memory-standing-usage { color: var(--dsw-alias-state-error-primary); }
49681
51912
  .bh-memory-file-tree button:focus-visible, .bh-memory-working-list button:focus-visible { outline: 2px solid var(--dsw-alias-label-primary); outline-offset: -2px; }
49682
51913
  .bh-memory-commit-view { flex: 1; min-height: 0; overflow: auto; padding: 0 12px 12px; color: var(--dsw-alias-label-primary); }
49683
51914
  .bh-memory-commit-header { display: flex; align-items: center; gap: 10px; min-height: 52px; margin: 0 -12px 10px; padding: 8px 12px; border-bottom: 1px solid var(--dsw-alias-border-l2); background: var(--dsw-specific-sidebar-fill); }
@@ -50290,6 +52521,37 @@ button:has(.bh-panel-glyph):is(:hover, :focus-within) ~ .bh-panel-activity[data-
50290
52521
  .bh-release-dialog {
50291
52522
  width: min(680px, calc(100vw - 32px));
50292
52523
  }
52524
+ .bh-telemetry-dialog {
52525
+ width: min(560px, calc(100vw - 32px));
52526
+ }
52527
+ .bh-telemetry-notice {
52528
+ display: flex;
52529
+ flex-direction: column;
52530
+ gap: 12px;
52531
+ margin: 0;
52532
+ min-width: 0;
52533
+ }
52534
+ .bh-telemetry-notice-row {
52535
+ display: grid;
52536
+ grid-template-columns: 80px minmax(0, 1fr);
52537
+ gap: 12px;
52538
+ min-width: 0;
52539
+ }
52540
+ .bh-telemetry-notice-row > dt {
52541
+ color: var(--dsw-alias-label-tertiary);
52542
+ font-size: 13px;
52543
+ line-height: 22px;
52544
+ }
52545
+ .bh-telemetry-notice-row > dd {
52546
+ margin: 0;
52547
+ min-width: 0;
52548
+ }
52549
+ @media (max-width: 520px) {
52550
+ .bh-telemetry-notice-row {
52551
+ grid-template-columns: minmax(0, 1fr);
52552
+ gap: 2px;
52553
+ }
52554
+ }
50293
52555
  .bh-release-notes {
50294
52556
  display: flex;
50295
52557
  flex-direction: column;
@@ -50402,14 +52664,17 @@ button:has(.bh-panel-glyph):is(:hover, :focus-within) ~ .bh-panel-activity[data-
50402
52664
  .bh-release-update-head {
50403
52665
  display: flex;
50404
52666
  align-items: center;
50405
- justify-content: space-between;
50406
52667
  gap: 8px;
50407
52668
  }
50408
52669
  .bh-release-update-title {
52670
+ margin-right: auto;
50409
52671
  font-size: 14px;
50410
52672
  font-weight: 600;
50411
52673
  color: var(--dsw-alias-label-primary);
50412
52674
  }
52675
+ .bh-release-restart-hint {
52676
+ margin-right: auto;
52677
+ }
50413
52678
  .bh-release-command {
50414
52679
  display: flex;
50415
52680
  align-items: center;
@@ -50427,6 +52692,95 @@ button:has(.bh-panel-glyph):is(:hover, :focus-within) ~ .bh-panel-activity[data-
50427
52692
  font-size: 12px;
50428
52693
  white-space: nowrap;
50429
52694
  }
52695
+ .bh-release-install-error {
52696
+ display: flex;
52697
+ flex-direction: column;
52698
+ gap: 6px;
52699
+ color: var(--dsw-alias-state-error-primary);
52700
+ font-size: 13px;
52701
+ }
52702
+ .bh-release-install-diagnostic {
52703
+ max-height: 120px;
52704
+ margin: 0;
52705
+ overflow: auto;
52706
+ padding: 6px 10px;
52707
+ border-radius: 6px;
52708
+ background: var(--dsw-alias-bg-module-platform);
52709
+ color: var(--dsw-alias-label-secondary);
52710
+ font-family: var(--dsw-font-family-mono, monospace);
52711
+ font-size: 12px;
52712
+ white-space: pre-wrap;
52713
+ }
52714
+ .bh-schedules {display:flex;flex-direction:column;gap:8px;min-width:0;}
52715
+ .bh-schedule-muted,
52716
+ .bh-schedule-firing-time {color:var(--dsw-alias-label-secondary);font-size:12px;}
52717
+ .bh-schedule-muted {margin:0;}
52718
+ .bh-schedule-empty {display:flex;flex-direction:column;align-items:flex-start;gap:8px;}
52719
+ .bh-schedule-error {margin:0;color:var(--dsw-alias-state-error-primary);font-size:12px;}
52720
+ .bh-schedule-warning {margin:0;padding:8px 10px;border-radius:8px;background:var(--dsw-alias-interactive-bg-hover);color:var(--dsw-alias-label-secondary);font-size:12px;}
52721
+ .bh-schedule-modal {width:min(480px, calc(100vw - 32px));}
52722
+ .bh-schedule-form {display:flex;flex-direction:column;gap:12px;}
52723
+ .bh-schedule-field {display:flex;flex-direction:column;gap:6px;min-width:0;font-size:13px;}
52724
+ .bh-schedule-field > span {color:var(--dsw-alias-label-secondary);}
52725
+ .bh-schedule-field textarea {min-height:88px;padding:8px 10px;resize:vertical;border:1px solid var(--dsw-alias-border-l2);border-radius:8px;background:transparent;color:var(--dsw-alias-label-primary);font:inherit;}
52726
+ .bh-schedule-field textarea:focus-visible {outline:2px solid var(--dsw-alias-label-primary);outline-offset:-1px;}
52727
+ .bh-schedule-row {display:flex;gap:8px;}
52728
+ .bh-schedule-row > .bh-schedule-field {flex:1;}
52729
+ .bh-schedule-zone {flex:2;}
52730
+ .bh-schedule-history {display:flex;flex-direction:column;gap:6px;padding-top:12px;border-top:1px solid var(--dsw-alias-border-l2);}
52731
+ .bh-schedule-history h4 {margin:0;font-size:13px;font-weight:500;}
52732
+ .bh-schedule-history ol {display:flex;flex-direction:column;gap:6px;margin:0;padding:0;list-style:none;max-height:200px;overflow:auto;}
52733
+ .bh-schedule-firing {display:flex;align-items:center;gap:6px;min-width:0;}
52734
+ .bh-schedule-firing-time {flex:1;min-width:0;}
52735
+ .bh-schedule-link {padding:0;border:0;background:transparent;color:var(--dsw-alias-label-primary);font-size:12px;text-decoration:underline;cursor:pointer;}
52736
+ .bh-schedule-modal-footer {display:flex;align-items:center;gap:8px;width:100%;}
52737
+ .bh-schedule-modal-spacer {flex:1;}
52738
+ .bh-card-list {display:flex;flex-direction:column;margin:0;padding:0;list-style:none;overflow:hidden;border:1px solid var(--dsw-alias-border-l2);border-radius:10px;}
52739
+ .bh-card-list + .bh-card-list {margin-top:8px;}
52740
+ .bh-card-row {min-width:0;}
52741
+ .bh-card-row + .bh-card-row {border-top:1px solid var(--dsw-alias-border-l2);}
52742
+ .bh-card-line {display:flex;align-items:center;gap:8px;min-width:0;}
52743
+ .bh-card-row:has(> .bh-card-line > button.bh-card-main:not(:disabled):hover) {background:var(--dsw-alias-interactive-bg-hover);}
52744
+ .bh-card-main {display:flex;flex:1;align-items:flex-start;gap:8px;min-width:0;margin:0;padding:9px 10px;border:0;border-radius:0;background:transparent;color:var(--dsw-alias-label-primary);font:inherit;text-align:left;}
52745
+ button.bh-card-main {cursor:pointer;}
52746
+ button.bh-card-main:disabled {cursor:default;}
52747
+ .bh-card-main:focus-visible {outline:2px solid var(--dsw-alias-label-primary);outline-offset:-2px;}
52748
+ .bh-card-icon {display:grid;flex:none;place-items:center;padding-top:1px;color:var(--dsw-alias-label-primary);}
52749
+ .bh-card-body {display:flex;flex:1;flex-direction:column;gap:4px;min-width:0;}
52750
+ .bh-card-title {overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-size:13px;font-weight:500;line-height:20px;}
52751
+ .bh-card-title.bh-inbox-item-summary {display:-webkit-box;white-space:normal;overflow-wrap:anywhere;-webkit-box-orient:vertical;-webkit-line-clamp:3;}
52752
+ .bh-card-chips {display:flex;flex-wrap:wrap;align-items:center;gap:4px;}
52753
+ .bh-card-glyph {display:inline-grid;place-items:center;color:var(--dsw-alias-label-secondary);}
52754
+ .bh-card-meta {display:flex;flex-wrap:wrap;align-items:baseline;gap:2px 8px;min-width:0;color:var(--dsw-alias-label-secondary);font-size:12px;line-height:16px;overflow-wrap:anywhere;}
52755
+ .bh-card-trailing {display:flex;flex:none;align-items:center;gap:4px;padding-right:10px;color:var(--dsw-alias-label-secondary);}
52756
+ .bh-card-detail {padding:0 10px 10px 34px;}
52757
+ .bh-card-row[data-muted="true"] .bh-card-icon,
52758
+ .bh-card-row[data-muted="true"] .bh-card-title {color:var(--dsw-alias-label-secondary);}
52759
+ .bh-card-trailing .bh-workspace-folder-remove {width:28px;height:28px;border-radius:6px;}
52760
+ .bh-assignment-access-card .bh-card-title {white-space:normal;}
52761
+ .bh-inbox-group .bh-card-list {margin:0 0 4px;}
52762
+ .bh-session-workspace-group > .bh-card-list {margin:2px 0 6px;}
52763
+ .bh-card-detail > .bh-workspace-folder-detail {padding:0;}
52764
+ .bh-inbox-group {border-top:0;padding:2px 0;}
52765
+ .bh-card-action {display:inline-grid;place-items:center;width:26px;height:26px;padding:0;border:0;border-radius:6px;background:transparent;color:var(--dsw-alias-label-secondary);cursor:pointer;}
52766
+ .bh-card-action:hover:not(:disabled) {background:var(--dsw-alias-interactive-bg-hover);color:var(--dsw-alias-label-primary);}
52767
+ .bh-card-action:focus-visible {outline:2px solid var(--dsw-alias-label-primary);outline-offset:-2px;}
52768
+ .bh-card-action:disabled {cursor:default;}
52769
+ .bh-schedule-lock-toggle:not([data-locked]) {opacity:.55;}
52770
+ .bh-schedule-switch {display:inline-flex;}
52771
+ .bh-schedule-weekdays {display:flex;gap:4px;flex-wrap:wrap;}
52772
+ .bh-schedule-weekday {flex:1;min-width:40px;padding:6px 0;border:1px solid var(--dsw-alias-border-l2);border-radius:8px;background:transparent;color:var(--dsw-alias-label-secondary);font:inherit;font-size:12px;cursor:pointer;}
52773
+ .bh-schedule-weekday:hover {background:var(--dsw-alias-interactive-bg-hover);}
52774
+ .bh-schedule-weekday[aria-pressed="true"] {border-color:var(--dsw-alias-label-primary);background:var(--dsw-alias-label-primary);color:var(--dsw-alias-bg-base);}
52775
+ .bh-schedule-weekday:focus-visible {outline:2px solid var(--dsw-alias-label-primary);outline-offset:1px;}
52776
+ .bh-schedule-cron input, input.bh-schedule-cron {font-family:var(--dsw-font-family-mono, monospace);}
52777
+ .bh-schedule-field small {font-size:12px;}
52778
+ .bh-schedule-preview {display:flex;flex-direction:column;gap:4px;padding:8px 10px;border-radius:8px;background:var(--dsw-alias-interactive-bg-hover);font-size:12px;}
52779
+ .bh-schedule-preview-label {color:var(--dsw-alias-label-secondary);}
52780
+ .bh-schedule-preview ol {display:flex;flex-direction:column;gap:2px;margin:0;padding:0;list-style:none;font-variant-numeric:tabular-nums;color:var(--dsw-alias-label-primary);}
52781
+ .bh-schedule-lock-toggle[data-locked] {color:var(--dsw-alias-label-primary);}
52782
+ .bh-schedule-lock {display:flex;align-items:center;gap:8px;padding:9px 10px;border:1px solid var(--dsw-alias-border-l2);border-radius:10px;}
52783
+ .bh-schedule-lock .bh-card-title {white-space:normal;}
50430
52784
  `;
50431
52785
  //#endregion
50432
52786
  //#region packages/client/src/client/index.ts
@@ -50487,6 +52841,9 @@ button:has(.bh-panel-glyph):is(:hover, :focus-within) ~ .bh-panel-activity[data-
50487
52841
  });
50488
52842
  const prefs = new BotModePrefs(storage);
50489
52843
  const releaseNotes = new ReleaseNotesController(call, storage);
52844
+ const telemetryNotice = new TelemetryNoticeController(call, storage, () => {
52845
+ openBotSettings(() => [t("settings.nav")]);
52846
+ });
50490
52847
  const lastView = typeof window === "undefined" ? void 0 : consumeLastView(window, storage);
50491
52848
  let restoreBotMode = hmrView === void 0 && lastView?.mode === "bot";
50492
52849
  const nativeSessions = {
@@ -50634,6 +52991,13 @@ button:has(.bh-panel-glyph):is(:hover, :focus-within) ~ .bh-panel-activity[data-
50634
52991
  onSaved: () => actions.refreshRoster()
50635
52992
  })
50636
52993
  }, MessagingDefaultsSettings));
52994
+ settingsCtx.slots.inject("botharness.settings.item", () => settingsCtx.slots.register({
52995
+ name: "botharness.settings.item",
52996
+ id: "telemetry",
52997
+ order: 20,
52998
+ locale: LOCALE_NS,
52999
+ inject: () => ({ call })
53000
+ }, TelemetrySettings));
50637
53001
  settingsCtx.slots.inject("settings.section", () => settingsCtx.slots.register({
50638
53002
  name: "settings.section",
50639
53003
  id: "botharness",
@@ -50680,7 +53044,8 @@ button:has(.bh-panel-glyph):is(:hover, :focus-within) ~ .bh-panel-activity[data-
50680
53044
  profileCards,
50681
53045
  nativeChatT,
50682
53046
  nativeSessions,
50683
- releaseNotes
53047
+ releaseNotes,
53048
+ telemetryNotice
50684
53049
  })
50685
53050
  }, BotPanel);
50686
53051
  if (restoreBotMode) {