@norman-else/dsh-claude 0.1.5 → 0.1.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/lib/client.d.ts CHANGED
@@ -34,6 +34,8 @@ window.__ModuleLoader__.load({
34
34
  readonly securityBody: "Claude tool permissions are decided through the DSH approval UI. This version does not claim kernel-level write isolation for ~/.claude or paths outside the workspace.";
35
35
  readonly tasks: "Tasks";
36
36
  readonly tasksPanel: "Background tasks";
37
+ readonly tasksPanelTurn: "Tasks for this run";
38
+ readonly tasksTurnNumber: "Turn {turn}";
37
39
  readonly tasksEmpty: "No subagents or background tasks";
38
40
  readonly tasksOpen: "Show background tasks";
39
41
  readonly tasksClose: "Close tasks panel";
@@ -44,6 +46,9 @@ window.__ModuleLoader__.load({
44
46
  readonly tasksViewActivity: "View activity";
45
47
  readonly tasksHideActivity: "Hide activity";
46
48
  readonly tasksRunningCount: "{count} running task(s)";
49
+ readonly tasksTurnRunning: "{count} task(s) running";
50
+ readonly tasksTurnCompleted: "{count} task(s) completed";
51
+ readonly tasksTurnFailed: "{failed} failed, {completed} completed";
47
52
  readonly tasksToolUses: "{count} tool use(s)";
48
53
  readonly tasksCompleted: "Completed";
49
54
  readonly tasksFailed: "Failed";
package/lib/client.js CHANGED
@@ -3,12 +3,9 @@ window.__ModuleLoader__.load({
3
3
  factory: (require) => {
4
4
  var module = { exports: {} };
5
5
  module.exports;
6
- var { useCallback, useEffect, useMemo, useState, useSyncExternalStore } = require("react");
6
+ var { useCallback, useEffect, useMemo, useState } = require("react");
7
7
  var { Fragment, jsx, jsxs } = require("react/jsx-runtime");
8
8
  var { DisclosureRow, IconApiOutline14, IconThinkOutline14, StateDot } = require("@deepseek-ai/dsh-client-ui-primitives");
9
- function isClaudeProvider(value) {
10
- return value === "claude" || value === "claude-code-cli";
11
- }
12
9
  /** Claude's subagent dispatch tools; rendered as plugin-owned group cards
13
10
  * gathering subagent activity instead of native tool cards. */
14
11
  const TASK_TOOL_NAMES = /* @__PURE__ */ new Set(["Task", "Agent"]);
@@ -58,6 +55,7 @@ window.__ModuleLoader__.load({
58
55
  let id;
59
56
  if ((isTaskActivity(value) || updatesExistingTask) && existingTaskId !== void 0) id = existingTaskId;
60
57
  else if (value.kind === "subagent" && value.parentToolUseId !== void 0) id = `task-${value.parentToolUseId}`;
58
+ else if (value.kind === "subagent" && value.taskId !== void 0) id = `subagent-task-${value.taskId}`;
61
59
  else if (value.kind === "subagent" && value.toolUseId !== void 0) id = `call-${value.toolUseId}`;
62
60
  else id = `act-${value.turn}-${value.step}-${value.ordinal}`;
63
61
  const index = byId.get(id);
@@ -126,7 +124,7 @@ window.__ModuleLoader__.load({
126
124
  id: `${event.data.turn}:${event.data.step}`,
127
125
  role: "start"
128
126
  };
129
- if (event.type !== "assistant/message" || !isClaudeProvider(event.data.message.source.provider)) return null;
127
+ if (event.type !== "assistant/message" || event.data.message.source.provider !== "claude") return null;
130
128
  return {
131
129
  id: `${event.data.turn}:${event.data.step}`,
132
130
  role: "update"
@@ -172,7 +170,7 @@ window.__ModuleLoader__.load({
172
170
  id: String(event.data.turn),
173
171
  role: "start"
174
172
  };
175
- if (event.type !== "assistant/message" || !isClaudeProvider(event.data.message.source.provider)) return null;
173
+ if (event.type !== "assistant/message" || event.data.message.source.provider !== "claude") return null;
176
174
  return {
177
175
  id: String(event.data.turn),
178
176
  role: "update"
@@ -294,11 +292,19 @@ window.__ModuleLoader__.load({
294
292
  borderBottom: "1px solid var(--dsw-alias-border-l2)"
295
293
  };
296
294
  const tasksHeading = {
295
+ display: "block",
297
296
  color: "var(--dsw-alias-label-primary)",
298
297
  fontSize: 14,
299
298
  lineHeight: "20px",
300
299
  fontWeight: 600
301
300
  };
301
+ const tasksTurnMeta = {
302
+ display: "block",
303
+ marginTop: 1,
304
+ color: "var(--dsw-alias-label-tertiary)",
305
+ fontSize: 11,
306
+ lineHeight: "16px"
307
+ };
302
308
  const tasksClose = {
303
309
  width: 26,
304
310
  height: 26,
@@ -510,47 +516,7 @@ window.__ModuleLoader__.load({
510
516
  color: "var(--dsw-static-blue-450)",
511
517
  fontSize: 9
512
518
  };
513
- const tasksTriggerHoverCss = [
514
- ".dsh-claude-tasks-trigger:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover)}",
515
- ".dsh-claude-tasks-trigger:focus:not(:focus-visible){outline:none;border-color:var(--dsw-alias-border-l2)}",
516
- ".dsh-claude-tasks-trigger[aria-pressed=\"true\"]:focus:not(:focus-visible){border-color:var(--dsw-alias-border-l3)}",
517
- ".dsh-claude-tasks-trigger>span,.dsh-claude-tasks-trigger>svg{flex:none}"
518
- ].join("");
519
- const tasksHeaderButton = {
520
- minWidth: 111,
521
- height: 32,
522
- display: "inline-flex",
523
- alignItems: "center",
524
- justifyContent: "center",
525
- gap: 4,
526
- padding: "6px 12px",
527
- border: "1px solid var(--dsw-alias-border-l2)",
528
- borderRadius: 18,
529
- color: "var(--dsw-alias-label-primary)",
530
- fontFamily: "var(--dsw-font-family)",
531
- fontSize: 13,
532
- fontWeight: 400,
533
- lineHeight: "20px",
534
- whiteSpace: "nowrap",
535
- cursor: "pointer"
536
- };
537
- const tasksHeaderButtonActive = {
538
- borderColor: "var(--dsw-alias-border-l3)",
539
- background: "var(--dsw-alias-interactive-bg-hover)"
540
- };
541
- const tasksBadgeInline = {
542
- minWidth: 14,
543
- height: 14,
544
- display: "grid",
545
- placeItems: "center",
546
- padding: "0 3px",
547
- borderRadius: 999,
548
- background: "var(--dsw-static-blue-450)",
549
- color: "var(--dsw-alias-label-on-accent, #fff)",
550
- fontSize: 9,
551
- lineHeight: "1",
552
- fontWeight: 600
553
- };
519
+ const tasksTurnLauncherDone = { color: "var(--dsw-alias-state-success-primary, var(--dsw-alias-label-tertiary))" };
554
520
  //#endregion
555
521
  //#region src/client/token-format.ts
556
522
  function formatTokenCount(tokens) {
@@ -576,8 +542,21 @@ window.__ModuleLoader__.load({
576
542
  function activitiesForTask(activities, taskId) {
577
543
  return activities.filter((activity) => activity.taskId === taskId);
578
544
  }
579
- function runningTasksForTurn(tasks, turn) {
580
- return tasks.filter((task) => task.status === "running" && task.originTurn === turn);
545
+ function tasksForTurn(tasks, turn) {
546
+ return tasks.filter((task) => task.originTurn === turn);
547
+ }
548
+ function summarizeTurnTasks(tasks) {
549
+ if (tasks.length === 0) return void 0;
550
+ const running = tasks.filter((task) => task.status === "running").length;
551
+ const failed = tasks.filter((task) => task.status === "failed" || task.status === "stopped" || task.status === "killed").length;
552
+ const completed = tasks.filter((task) => task.status === "completed").length;
553
+ return {
554
+ state: running > 0 ? "running" : failed > 0 ? "failed" : "completed",
555
+ count: tasks.length,
556
+ running,
557
+ failed,
558
+ completed
559
+ };
581
560
  }
582
561
  function statusGlyph(status) {
583
562
  if (status === "running") return "●";
@@ -732,12 +711,16 @@ window.__ModuleLoader__.load({
732
711
  })]
733
712
  });
734
713
  }
735
- function ClaudeTasksPanel({ useClaudeProjection, t, closeDetails }) {
714
+ function ClaudeTasksPanel({ useClaudeProjection, t, closeDetails, turn }) {
736
715
  const projection = useClaudeProjection((value) => value);
716
+ const tasks = useMemo(() => tasksForTurn(projection.tasks?.tasks ?? [], turn), [projection.tasks, turn]);
737
717
  useEffect(() => {
738
- if (!projection.owned) closeDetails();
739
- }, [closeDetails, projection.owned]);
740
- const tasks = projection.tasks?.tasks ?? [];
718
+ if (!projection.owned || tasks.length === 0) closeDetails();
719
+ }, [
720
+ closeDetails,
721
+ projection.owned,
722
+ tasks.length
723
+ ]);
741
724
  const [finishedCollapsed, setFinishedCollapsed] = useState(false);
742
725
  const [dismissedSettledIds, setDismissedSettledIds] = useState(() => /* @__PURE__ */ new Set());
743
726
  const groups = useMemo(() => visibleTaskGroups(tasks, dismissedSettledIds), [tasks, dismissedSettledIds]);
@@ -748,10 +731,13 @@ window.__ModuleLoader__.load({
748
731
  style: tasksPanel,
749
732
  children: [/* @__PURE__ */ jsxs("div", {
750
733
  style: tasksHeader,
751
- children: [/* @__PURE__ */ jsx("span", {
734
+ children: [/* @__PURE__ */ jsxs("div", { children: [/* @__PURE__ */ jsx("span", {
752
735
  style: tasksHeading,
753
- children: t("tasksPanel")
754
- }), /* @__PURE__ */ jsx("button", {
736
+ children: t("tasksPanelTurn")
737
+ }), /* @__PURE__ */ jsx("span", {
738
+ style: tasksTurnMeta,
739
+ children: t("tasksTurnNumber", { turn })
740
+ })] }), /* @__PURE__ */ jsx("button", {
755
741
  type: "button",
756
742
  style: tasksClose,
757
743
  "aria-label": t("tasksClose"),
@@ -800,74 +786,35 @@ window.__ModuleLoader__.load({
800
786
  })]
801
787
  });
802
788
  }
803
- function ClaudeTasksHeaderButton({ useClaudeProjection, t, isOpen, toggle, subscribe }) {
804
- const open = useSyncExternalStore(subscribe, isOpen, isOpen);
805
- const projection = useClaudeProjection((value) => value);
806
- if (!projection.owned) return null;
807
- const runningCount = projection.tasks?.tasks.filter((task) => task.status === "running").length ?? 0;
808
- return /* @__PURE__ */ jsxs("button", {
809
- type: "button",
810
- className: "dsh-claude-tasks-trigger",
811
- style: {
812
- ...tasksHeaderButton,
813
- ...open ? tasksHeaderButtonActive : {}
814
- },
815
- "aria-label": t("tasksOpen"),
816
- "aria-pressed": open,
817
- onClick: (event) => {
818
- toggle();
819
- event.currentTarget.blur();
820
- },
821
- children: [
822
- /* @__PURE__ */ jsx("style", { children: tasksTriggerHoverCss }),
823
- /* @__PURE__ */ jsxs("svg", {
824
- width: "14",
825
- height: "14",
826
- viewBox: "0 0 24 24",
827
- fill: "none",
828
- stroke: "currentColor",
829
- strokeWidth: "2",
830
- strokeLinecap: "round",
831
- strokeLinejoin: "round",
832
- "aria-hidden": "true",
833
- children: [
834
- /* @__PURE__ */ jsx("path", { d: "m3 17 2 2 4-4" }),
835
- /* @__PURE__ */ jsx("path", { d: "m3 7 2 2 4-4" }),
836
- /* @__PURE__ */ jsx("path", { d: "M13 6h8" }),
837
- /* @__PURE__ */ jsx("path", { d: "M13 12h8" }),
838
- /* @__PURE__ */ jsx("path", { d: "M13 18h8" })
839
- ]
840
- }),
841
- /* @__PURE__ */ jsx("span", { children: t("tasks") }),
842
- runningCount === 0 ? null : /* @__PURE__ */ jsx("span", {
843
- className: "dsh-claude-act-running",
844
- style: tasksBadgeInline,
845
- "aria-hidden": "true",
846
- children: runningCount
847
- })
848
- ]
849
- });
850
- }
851
789
  //#endregion
852
790
  //#region src/client/ClaudeActivityTail.tsx
853
791
  function ClaudeActivityTail({ matched, useClaudeProjection, t, openTasks }) {
854
792
  const projection = useClaudeProjection((value) => value);
855
- const runningTasks = useMemo(() => runningTasksForTurn(projection.tasks?.tasks ?? [], matched.turn), [projection.tasks, matched.turn]);
856
- if (runningTasks.length === 0) return null;
793
+ const summary = useMemo(() => summarizeTurnTasks(tasksForTurn(projection.tasks?.tasks ?? [], matched.turn)), [projection.tasks, matched.turn]);
794
+ if (summary === void 0) return null;
795
+ const label = summary.state === "running" ? t("tasksTurnRunning", { count: summary.running }) : summary.state === "failed" ? t("tasksTurnFailed", {
796
+ failed: summary.failed,
797
+ completed: summary.completed
798
+ }) : t("tasksTurnCompleted", { count: summary.completed });
799
+ const glyph = summary.state === "running" ? "●" : summary.state === "failed" ? "×" : "✓";
857
800
  return /* @__PURE__ */ jsx("div", {
858
801
  "data-claude-activity-tail": matched.turn,
859
- children: runningTasks.length === 0 ? null : /* @__PURE__ */ jsxs("button", {
802
+ children: /* @__PURE__ */ jsxs("button", {
860
803
  type: "button",
861
804
  style: tasksTurnLauncher,
862
- onClick: openTasks,
805
+ onClick: () => openTasks(matched.turn),
863
806
  children: [
864
807
  /* @__PURE__ */ jsx("span", {
865
- className: "dsh-claude-act-running",
866
- style: tasksTurnLauncherDot,
808
+ className: summary.state === "running" ? "dsh-claude-act-running" : void 0,
809
+ style: {
810
+ ...tasksTurnLauncherDot,
811
+ ...summary.state === "failed" ? iconChipError : {},
812
+ ...summary.state === "completed" ? tasksTurnLauncherDone : {}
813
+ },
867
814
  "aria-hidden": "true",
868
- children: "●"
815
+ children: glyph
869
816
  }),
870
- /* @__PURE__ */ jsx("span", { children: t("tasksRunningCount", { count: runningTasks.length }) }),
817
+ /* @__PURE__ */ jsx("span", { children: label }),
871
818
  /* @__PURE__ */ jsx("span", {
872
819
  "aria-hidden": "true",
873
820
  children: "›"
@@ -1219,6 +1166,8 @@ window.__ModuleLoader__.load({
1219
1166
  securityBody: "Claude 工具权限通过 DSH 审批界面决定;当前版本不宣称对 ~/.claude 与工作区之外路径提供内核级写入隔离。",
1220
1167
  tasks: "任务",
1221
1168
  tasksPanel: "后台任务",
1169
+ tasksPanelTurn: "本次执行的任务",
1170
+ tasksTurnNumber: "第 {turn} 轮",
1222
1171
  tasksEmpty: "当前没有子代理或后台任务",
1223
1172
  tasksOpen: "查看后台任务",
1224
1173
  tasksClose: "关闭任务面板",
@@ -1229,6 +1178,9 @@ window.__ModuleLoader__.load({
1229
1178
  tasksViewActivity: "查看活动",
1230
1179
  tasksHideActivity: "收起活动",
1231
1180
  tasksRunningCount: "{count} 个运行中任务",
1181
+ tasksTurnRunning: "{count} 个任务执行中",
1182
+ tasksTurnCompleted: "{count} 个任务已完成",
1183
+ tasksTurnFailed: "{failed} 个任务失败,{completed} 个已完成",
1232
1184
  tasksToolUses: "{count} 次工具调用",
1233
1185
  tasksCompleted: "已完成",
1234
1186
  tasksFailed: "失败",
@@ -1266,6 +1218,8 @@ window.__ModuleLoader__.load({
1266
1218
  securityBody: "Claude tool permissions are decided through the DSH approval UI. This version does not claim kernel-level write isolation for ~/.claude or paths outside the workspace.",
1267
1219
  tasks: "Tasks",
1268
1220
  tasksPanel: "Background tasks",
1221
+ tasksPanelTurn: "Tasks for this run",
1222
+ tasksTurnNumber: "Turn {turn}",
1269
1223
  tasksEmpty: "No subagents or background tasks",
1270
1224
  tasksOpen: "Show background tasks",
1271
1225
  tasksClose: "Close tasks panel",
@@ -1276,6 +1230,9 @@ window.__ModuleLoader__.load({
1276
1230
  tasksViewActivity: "View activity",
1277
1231
  tasksHideActivity: "Hide activity",
1278
1232
  tasksRunningCount: "{count} running task(s)",
1233
+ tasksTurnRunning: "{count} task(s) running",
1234
+ tasksTurnCompleted: "{count} task(s) completed",
1235
+ tasksTurnFailed: "{failed} failed, {completed} completed",
1279
1236
  tasksToolUses: "{count} tool use(s)",
1280
1237
  tasksCompleted: "Completed",
1281
1238
  tasksFailed: "Failed",
@@ -1315,21 +1272,16 @@ window.__ModuleLoader__.load({
1315
1272
  locale: namespace
1316
1273
  }, ClaudeActivityNode));
1317
1274
  const layout = ctx.get("layout");
1318
- const tasksPanelListeners = /* @__PURE__ */ new Set();
1319
1275
  let disposeTasksDetails;
1320
- let tasksPanelSession;
1321
- const notifyTasksPanel = () => {
1322
- for (const fn of [...tasksPanelListeners]) fn();
1323
- };
1276
+ let tasksPanelTarget;
1324
1277
  const closeTasksPanel = () => {
1325
1278
  if (disposeTasksDetails === void 0) return;
1326
1279
  disposeTasksDetails();
1327
1280
  disposeTasksDetails = void 0;
1328
- tasksPanelSession = void 0;
1281
+ tasksPanelTarget = void 0;
1329
1282
  layout?.closeDetails();
1330
- notifyTasksPanel();
1331
1283
  };
1332
- const openTasksPanel = (sessionId) => {
1284
+ const openTasksPanel = (sessionId, turn) => {
1333
1285
  closeTasksPanel();
1334
1286
  try {
1335
1287
  disposeTasksDetails = ctx.slots.register({
@@ -1338,24 +1290,23 @@ window.__ModuleLoader__.load({
1338
1290
  locale: namespace,
1339
1291
  inject: () => ({
1340
1292
  t,
1293
+ turn,
1341
1294
  closeDetails: closeTasksPanel
1342
1295
  })
1343
1296
  }, ClaudeTasksPanel);
1344
1297
  } catch {
1345
1298
  return;
1346
1299
  }
1347
- tasksPanelSession = sessionId;
1300
+ tasksPanelTarget = {
1301
+ sessionId,
1302
+ turn
1303
+ };
1348
1304
  layout?.openDetails();
1349
- notifyTasksPanel();
1350
- };
1351
- const toggleTasksPanel = (sessionId) => {
1352
- if (tasksPanelSession === sessionId) closeTasksPanel();
1353
- else openTasksPanel(sessionId);
1354
1305
  };
1355
1306
  ctx.effect(() => {
1356
1307
  if (typeof document === "undefined" || typeof MutationObserver === "undefined") return () => closeTasksPanel();
1357
1308
  const observer = new MutationObserver(() => {
1358
- if (tasksPanelSession !== void 0 && document.querySelector("[data-details-collapsed]") !== null) closeTasksPanel();
1309
+ if (tasksPanelTarget !== void 0 && document.querySelector("[data-details-collapsed]") !== null) closeTasksPanel();
1359
1310
  });
1360
1311
  observer.observe(document.body, {
1361
1312
  attributes: true,
@@ -1367,37 +1318,17 @@ window.__ModuleLoader__.load({
1367
1318
  closeTasksPanel();
1368
1319
  };
1369
1320
  }, "dsh-claude: tasks panel lifecycle");
1370
- const tasksLauncher = (sessionId) => ({
1371
- isOpen: () => tasksPanelSession === sessionId,
1372
- toggle: () => toggleTasksPanel(sessionId),
1373
- subscribe: (fn) => {
1374
- tasksPanelListeners.add(fn);
1375
- return () => {
1376
- tasksPanelListeners.delete(fn);
1377
- };
1378
- }
1379
- });
1380
1321
  ctx.slots.inject("conversation.chat.turnTail", () => ctx.slots.register({
1381
1322
  name: "conversation.chat.turnTail",
1382
1323
  select: selectClaudeTurn,
1383
1324
  inject: (sessionId) => ({
1384
1325
  t,
1385
- openTasks: () => openTasksPanel(sessionId)
1326
+ openTasks: (turn) => openTasksPanel(sessionId, turn)
1386
1327
  })
1387
1328
  }, ClaudeActivityTail));
1388
1329
  if (sessions !== void 0) ctx.effect(() => sessions.list.subscribe(() => {
1389
- if (tasksPanelSession !== void 0 && sessions.list.getSnapshot().current !== tasksPanelSession) closeTasksPanel();
1330
+ if (tasksPanelTarget !== void 0 && sessions.list.getSnapshot().current !== tasksPanelTarget.sessionId) closeTasksPanel();
1390
1331
  }), "dsh-claude: tasks panel session tracking");
1391
- ctx.slots.inject("conversation.session.header.utilities", () => ctx.slots.register({
1392
- name: "conversation.session.header.utilities",
1393
- id: "claude-tasks",
1394
- order: -10,
1395
- label: () => t("tasksOpen"),
1396
- inject: (sessionId) => ({
1397
- t,
1398
- ...tasksLauncher(sessionId)
1399
- })
1400
- }, ClaudeTasksHeaderButton));
1401
1332
  ctx.slots.inject("settings.section", () => ctx.slots.register({
1402
1333
  name: "settings.section",
1403
1334
  id: "claude-code",