@norman-else/dsh-claude 0.1.26 → 0.1.27

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.
@@ -1,4 +1,4 @@
1
- import { l as redactText } from "./events-CY6V9oQ4.mjs";
1
+ import { l as redactText } from "./events-lDt9nTUw.mjs";
2
2
  //#region src/command-bridge.ts
3
3
  const COMMAND_NAME = /^[a-z][a-z0-9_-]*$/u;
4
4
  const MAX_DESCRIPTION_CHARS = 300;
@@ -68,4 +68,4 @@ function projectClaudeCommands(catalog, target) {
68
68
  //#endregion
69
69
  export { projectClaudeCommands as n, CLAUDE_COMMANDS_SERVICE as t };
70
70
 
71
- //# sourceMappingURL=command-bridge-vyZoEeEO.mjs.map
71
+ //# sourceMappingURL=command-bridge-BYj0VF4J.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"command-bridge-vyZoEeEO.mjs","names":[],"sources":["../src/command-bridge.ts"],"sourcesContent":["import type { SlashCommand } from '@anthropic-ai/claude-agent-sdk'\nimport type { CommandDescriptor } from '@deepseek-ai/dsh-commands'\nimport { redactText } from './events.ts'\n\nconst COMMAND_NAME = /^[a-z][a-z0-9_-]*$/u\nconst MAX_DESCRIPTION_CHARS = 300\nconst MAX_HINT_CHARS = 120\n\n/** DSH registry names reject `:`, but plugin-qualified Claude skills carry it\n * (e.g. `awesome-skills:ci-deploy`). Derive a registry-safe public name while\n * forwarding keeps the exact Claude-side name. */\nfunction registryName(claudeName: string): string | undefined {\n if (COMMAND_NAME.test(claudeName)) return claudeName\n const short = claudeName.includes(':') ? claudeName.slice(claudeName.lastIndexOf(':') + 1) : undefined\n if (short !== undefined && COMMAND_NAME.test(short)) return short\n const sanitized = claudeName.replaceAll(':', '-').toLowerCase()\n return COMMAND_NAME.test(sanitized) ? sanitized : undefined\n}\n\n/**\n * Names registered as CLIENT-side commandUi contributions (not host\n * commands). The DSH web command palette fails its whole command group when\n * a host command name equals a contribution name\n * (\"contribution /model collides with a host command\"), so these can never\n * be registered by this bridge. Keep in sync with the client bundles:\n * dsh-client-ui-model-selection registers \"model\".\n */\nconst CLIENT_CONTRIBUTION_NAMES: ReadonlySet<string> = new Set(['model'])\n\nexport interface ClaudeCommandTarget {\n list(): readonly Pick<CommandDescriptor, 'name'>[]\n}\n\n/**\n * Agent-scope command-directory contract. The commands service is unreachable\n * from the host plugin's view of `agent.ctx` (\"without inject\"), but the\n * preset route plugin runs inside the agent's preset composition with\n * `commands` injected. The Host reads only the exact agent's effective names\n * for collision checks; Claude catalog entries are never registered there.\n */\nexport interface ClaudeAgentCommandService {\n list(agent: unknown): readonly Pick<CommandDescriptor, 'name'>[]\n}\n\n/**\n * Cordis service name the preset route provides (behind an entry-local\n * isolate realm, so each session gets its own instance) and the host reads\n * back with dsh-agent-presets' official `serviceForAgent(ctx, agent, name)`\n * — the supported cross-scope read for callers that already hold the agent.\n */\nexport const CLAUDE_COMMANDS_SERVICE = 'claudeCommands'\n\ndeclare module '@deepseek-ai/cordis' {\n interface Context {\n claudeCommands?: ClaudeAgentCommandService\n }\n}\n\nexport interface ClaudeCommandView {\n publicName: string\n claudeName: string\n description: string\n hint?: string\n prefixed: boolean\n}\n\nfunction bounded(value: string, maxChars: number): string {\n return redactText(value, maxChars)\n}\n\nexport function projectClaudeCommands(\n catalog: readonly SlashCommand[],\n target: ClaudeCommandTarget,\n): readonly ClaudeCommandView[] {\n const reservedNames = new Set(target.list().map(command => command.name))\n // Client contributions are absent from the Host directory but share the\n // same slash menu, so reserve their public names explicitly.\n for (const name of CLIENT_CONTRIBUTION_NAMES) reservedNames.add(name)\n const assigned = new Set<string>()\n const views: ClaudeCommandView[] = []\n for (const command of catalog) {\n const names = [command.name, ...(command.aliases ?? [])]\n const commandBases = new Set<string>()\n for (const claudeName of names) {\n const base = registryName(claudeName)\n if (base === undefined || commandBases.has(base)) continue\n commandBases.add(base)\n let publicName = base\n let prefixed = false\n if (reservedNames.has(publicName) || assigned.has(publicName)) {\n publicName = `claude-${base}`\n prefixed = true\n }\n if (!COMMAND_NAME.test(publicName) || reservedNames.has(publicName) || assigned.has(publicName)) continue\n const description = bounded(command.description || `Claude Code /${command.name}`, MAX_DESCRIPTION_CHARS)\n const hint = bounded(command.argumentHint ?? '', MAX_HINT_CHARS)\n views.push({\n publicName,\n claudeName,\n description,\n ...(hint.length === 0 ? {} : { hint }),\n prefixed,\n })\n assigned.add(publicName)\n }\n }\n return views\n}\n"],"mappings":";;AAIA,MAAM,eAAe;AACrB,MAAM,wBAAwB;AAC9B,MAAM,iBAAiB;;;;AAKvB,SAAS,aAAa,YAAwC;CAC5D,IAAI,aAAa,KAAK,UAAU,GAAG,OAAO;CAC1C,MAAM,QAAQ,WAAW,SAAS,GAAG,IAAI,WAAW,MAAM,WAAW,YAAY,GAAG,IAAI,CAAC,IAAI,KAAA;CAC7F,IAAI,UAAU,KAAA,KAAa,aAAa,KAAK,KAAK,GAAG,OAAO;CAC5D,MAAM,YAAY,WAAW,WAAW,KAAK,GAAG,CAAC,CAAC,YAAY;CAC9D,OAAO,aAAa,KAAK,SAAS,IAAI,YAAY,KAAA;AACpD;;;;;;;;;AAUA,MAAM,4CAAiD,IAAI,IAAI,CAAC,OAAO,CAAC;;;;;;;AAuBxE,MAAa,0BAA0B;AAgBvC,SAAS,QAAQ,OAAe,UAA0B;CACxD,OAAO,WAAW,OAAO,QAAQ;AACnC;AAEA,SAAgB,sBACd,SACA,QAC8B;CAC9B,MAAM,gBAAgB,IAAI,IAAI,OAAO,KAAK,CAAC,CAAC,KAAI,YAAW,QAAQ,IAAI,CAAC;CAGxE,KAAK,MAAM,QAAQ,2BAA2B,cAAc,IAAI,IAAI;CACpE,MAAM,2BAAW,IAAI,IAAY;CACjC,MAAM,QAA6B,CAAC;CACpC,KAAK,MAAM,WAAW,SAAS;EAC7B,MAAM,QAAQ,CAAC,QAAQ,MAAM,GAAI,QAAQ,WAAW,CAAC,CAAE;EACvD,MAAM,+BAAe,IAAI,IAAY;EACrC,KAAK,MAAM,cAAc,OAAO;GAC9B,MAAM,OAAO,aAAa,UAAU;GACpC,IAAI,SAAS,KAAA,KAAa,aAAa,IAAI,IAAI,GAAG;GAClD,aAAa,IAAI,IAAI;GACrB,IAAI,aAAa;GACjB,IAAI,WAAW;GACf,IAAI,cAAc,IAAI,UAAU,KAAK,SAAS,IAAI,UAAU,GAAG;IAC7D,aAAa,UAAU;IACvB,WAAW;GACb;GACA,IAAI,CAAC,aAAa,KAAK,UAAU,KAAK,cAAc,IAAI,UAAU,KAAK,SAAS,IAAI,UAAU,GAAG;GACjG,MAAM,cAAc,QAAQ,QAAQ,eAAe,gBAAgB,QAAQ,QAAQ,qBAAqB;GACxG,MAAM,OAAO,QAAQ,QAAQ,gBAAgB,IAAI,cAAc;GAC/D,MAAM,KAAK;IACT;IACA;IACA;IACA,GAAI,KAAK,WAAW,IAAI,CAAC,IAAI,EAAE,KAAK;IACpC;GACF,CAAC;GACD,SAAS,IAAI,UAAU;EACzB;CACF;CACA,OAAO;AACT"}
1
+ {"version":3,"file":"command-bridge-BYj0VF4J.mjs","names":[],"sources":["../src/command-bridge.ts"],"sourcesContent":["import type { SlashCommand } from '@anthropic-ai/claude-agent-sdk'\nimport type { CommandDescriptor } from '@deepseek-ai/dsh-commands'\nimport { redactText } from './events.ts'\n\nconst COMMAND_NAME = /^[a-z][a-z0-9_-]*$/u\nconst MAX_DESCRIPTION_CHARS = 300\nconst MAX_HINT_CHARS = 120\n\n/** DSH registry names reject `:`, but plugin-qualified Claude skills carry it\n * (e.g. `awesome-skills:ci-deploy`). Derive a registry-safe public name while\n * forwarding keeps the exact Claude-side name. */\nfunction registryName(claudeName: string): string | undefined {\n if (COMMAND_NAME.test(claudeName)) return claudeName\n const short = claudeName.includes(':') ? claudeName.slice(claudeName.lastIndexOf(':') + 1) : undefined\n if (short !== undefined && COMMAND_NAME.test(short)) return short\n const sanitized = claudeName.replaceAll(':', '-').toLowerCase()\n return COMMAND_NAME.test(sanitized) ? sanitized : undefined\n}\n\n/**\n * Names registered as CLIENT-side commandUi contributions (not host\n * commands). The DSH web command palette fails its whole command group when\n * a host command name equals a contribution name\n * (\"contribution /model collides with a host command\"), so these can never\n * be registered by this bridge. Keep in sync with the client bundles:\n * dsh-client-ui-model-selection registers \"model\".\n */\nconst CLIENT_CONTRIBUTION_NAMES: ReadonlySet<string> = new Set(['model'])\n\nexport interface ClaudeCommandTarget {\n list(): readonly Pick<CommandDescriptor, 'name'>[]\n}\n\n/**\n * Agent-scope command-directory contract. The commands service is unreachable\n * from the host plugin's view of `agent.ctx` (\"without inject\"), but the\n * preset route plugin runs inside the agent's preset composition with\n * `commands` injected. The Host reads only the exact agent's effective names\n * for collision checks; Claude catalog entries are never registered there.\n */\nexport interface ClaudeAgentCommandService {\n list(agent: unknown): readonly Pick<CommandDescriptor, 'name'>[]\n}\n\n/**\n * Cordis service name the preset route provides (behind an entry-local\n * isolate realm, so each session gets its own instance) and the host reads\n * back with dsh-agent-presets' official `serviceForAgent(ctx, agent, name)`\n * — the supported cross-scope read for callers that already hold the agent.\n */\nexport const CLAUDE_COMMANDS_SERVICE = 'claudeCommands'\n\ndeclare module '@deepseek-ai/cordis' {\n interface Context {\n claudeCommands?: ClaudeAgentCommandService\n }\n}\n\nexport interface ClaudeCommandView {\n publicName: string\n claudeName: string\n description: string\n hint?: string\n prefixed: boolean\n}\n\nfunction bounded(value: string, maxChars: number): string {\n return redactText(value, maxChars)\n}\n\nexport function projectClaudeCommands(\n catalog: readonly SlashCommand[],\n target: ClaudeCommandTarget,\n): readonly ClaudeCommandView[] {\n const reservedNames = new Set(target.list().map(command => command.name))\n // Client contributions are absent from the Host directory but share the\n // same slash menu, so reserve their public names explicitly.\n for (const name of CLIENT_CONTRIBUTION_NAMES) reservedNames.add(name)\n const assigned = new Set<string>()\n const views: ClaudeCommandView[] = []\n for (const command of catalog) {\n const names = [command.name, ...(command.aliases ?? [])]\n const commandBases = new Set<string>()\n for (const claudeName of names) {\n const base = registryName(claudeName)\n if (base === undefined || commandBases.has(base)) continue\n commandBases.add(base)\n let publicName = base\n let prefixed = false\n if (reservedNames.has(publicName) || assigned.has(publicName)) {\n publicName = `claude-${base}`\n prefixed = true\n }\n if (!COMMAND_NAME.test(publicName) || reservedNames.has(publicName) || assigned.has(publicName)) continue\n const description = bounded(command.description || `Claude Code /${command.name}`, MAX_DESCRIPTION_CHARS)\n const hint = bounded(command.argumentHint ?? '', MAX_HINT_CHARS)\n views.push({\n publicName,\n claudeName,\n description,\n ...(hint.length === 0 ? {} : { hint }),\n prefixed,\n })\n assigned.add(publicName)\n }\n }\n return views\n}\n"],"mappings":";;AAIA,MAAM,eAAe;AACrB,MAAM,wBAAwB;AAC9B,MAAM,iBAAiB;;;;AAKvB,SAAS,aAAa,YAAwC;CAC5D,IAAI,aAAa,KAAK,UAAU,GAAG,OAAO;CAC1C,MAAM,QAAQ,WAAW,SAAS,GAAG,IAAI,WAAW,MAAM,WAAW,YAAY,GAAG,IAAI,CAAC,IAAI,KAAA;CAC7F,IAAI,UAAU,KAAA,KAAa,aAAa,KAAK,KAAK,GAAG,OAAO;CAC5D,MAAM,YAAY,WAAW,WAAW,KAAK,GAAG,CAAC,CAAC,YAAY;CAC9D,OAAO,aAAa,KAAK,SAAS,IAAI,YAAY,KAAA;AACpD;;;;;;;;;AAUA,MAAM,4CAAiD,IAAI,IAAI,CAAC,OAAO,CAAC;;;;;;;AAuBxE,MAAa,0BAA0B;AAgBvC,SAAS,QAAQ,OAAe,UAA0B;CACxD,OAAO,WAAW,OAAO,QAAQ;AACnC;AAEA,SAAgB,sBACd,SACA,QAC8B;CAC9B,MAAM,gBAAgB,IAAI,IAAI,OAAO,KAAK,CAAC,CAAC,KAAI,YAAW,QAAQ,IAAI,CAAC;CAGxE,KAAK,MAAM,QAAQ,2BAA2B,cAAc,IAAI,IAAI;CACpE,MAAM,2BAAW,IAAI,IAAY;CACjC,MAAM,QAA6B,CAAC;CACpC,KAAK,MAAM,WAAW,SAAS;EAC7B,MAAM,QAAQ,CAAC,QAAQ,MAAM,GAAI,QAAQ,WAAW,CAAC,CAAE;EACvD,MAAM,+BAAe,IAAI,IAAY;EACrC,KAAK,MAAM,cAAc,OAAO;GAC9B,MAAM,OAAO,aAAa,UAAU;GACpC,IAAI,SAAS,KAAA,KAAa,aAAa,IAAI,IAAI,GAAG;GAClD,aAAa,IAAI,IAAI;GACrB,IAAI,aAAa;GACjB,IAAI,WAAW;GACf,IAAI,cAAc,IAAI,UAAU,KAAK,SAAS,IAAI,UAAU,GAAG;IAC7D,aAAa,UAAU;IACvB,WAAW;GACb;GACA,IAAI,CAAC,aAAa,KAAK,UAAU,KAAK,cAAc,IAAI,UAAU,KAAK,SAAS,IAAI,UAAU,GAAG;GACjG,MAAM,cAAc,QAAQ,QAAQ,eAAe,gBAAgB,QAAQ,QAAQ,qBAAqB;GACxG,MAAM,OAAO,QAAQ,QAAQ,gBAAgB,IAAI,cAAc;GAC/D,MAAM,KAAK;IACT;IACA;IACA;IACA,GAAI,KAAK,WAAW,IAAI,CAAC,IAAI,EAAE,KAAK;IACpC;GACF,CAAC;GACD,SAAS,IAAI,UAAU;EACzB;CACF;CACA,OAAO;AACT"}
@@ -9,6 +9,7 @@ const TASK_TOOL_NAMES = /* @__PURE__ */ new Set(["Task", "Agent"]);
9
9
  const SDK_VERSION = "0.3.247";
10
10
  const CLAUDE_DOCTOR_PATH = "/plugins/dsh-claude/doctor";
11
11
  const CLAUDE_UPDATE_CHECK_PATH = "/plugins/dsh-claude/update/check";
12
+ const CLAUDE_USAGE_PATH = "/plugins/dsh-claude/usage";
12
13
  const CLAUDE_UPDATE_PATH = "/plugins/dsh-claude/update";
13
14
  const CLAUDE_PROJECTION_PATH = "/plugins/dsh-claude/projection";
14
15
  const CLAUDE_GLOBAL_SETTINGS_PATH = "/plugins/dsh-claude/settings/global";
@@ -189,6 +190,6 @@ function latestClaudeSessionBinding(events) {
189
190
  }
190
191
  }
191
192
  //#endregion
192
- export { CLAUDE_REPOSITORY_SETUP_PATH as C, CLAUDE_UPDATE_PATH as D, CLAUDE_UPDATE_CHECK_PATH as E, SDK_VERSION as O, CLAUDE_REPOSITORY_FILE_PATH as S, CLAUDE_REVIEW_COMMENT_PATH as T, CLAUDE_GLOBAL_SETTINGS_PATH as _, latestClaudeTasks as a, CLAUDE_REPOSITORY_ACTION_PATH as b, normalizeTasksEvent as c, CLAUDE_ACTIVITY_EVENT as d, CLAUDE_ASK_PATH as f, CLAUDE_DOCTOR_PATH as g, CLAUDE_CODE_PROVIDER_IDS as h, latestClaudeSessionBinding as i, TASK_TOOL_NAMES as k, redactText as l, CLAUDE_CODE_PROVIDER as m, currentClaudeActivityCursor as n, normalizeActivity as o, CLAUDE_CODE_PRESET_ID as p, latestClaudeContextUsage as r, normalizeContextUsage as s, boundText as t, safeDetail as u, CLAUDE_JIRA_PATH as v, CLAUDE_REPOSITORY_STATUS_PATH as w, CLAUDE_REPOSITORY_FEEDBACK_PATH as x, CLAUDE_PROJECTION_PATH as y };
193
+ export { TASK_TOOL_NAMES as A, CLAUDE_REPOSITORY_SETUP_PATH as C, CLAUDE_UPDATE_PATH as D, CLAUDE_UPDATE_CHECK_PATH as E, CLAUDE_USAGE_PATH as O, CLAUDE_REPOSITORY_FILE_PATH as S, CLAUDE_REVIEW_COMMENT_PATH as T, CLAUDE_GLOBAL_SETTINGS_PATH as _, latestClaudeTasks as a, CLAUDE_REPOSITORY_ACTION_PATH as b, normalizeTasksEvent as c, CLAUDE_ACTIVITY_EVENT as d, CLAUDE_ASK_PATH as f, CLAUDE_DOCTOR_PATH as g, CLAUDE_CODE_PROVIDER_IDS as h, latestClaudeSessionBinding as i, SDK_VERSION as k, redactText as l, CLAUDE_CODE_PROVIDER as m, currentClaudeActivityCursor as n, normalizeActivity as o, CLAUDE_CODE_PRESET_ID as p, latestClaudeContextUsage as r, normalizeContextUsage as s, boundText as t, safeDetail as u, CLAUDE_JIRA_PATH as v, CLAUDE_REPOSITORY_STATUS_PATH as w, CLAUDE_REPOSITORY_FEEDBACK_PATH as x, CLAUDE_PROJECTION_PATH as y };
193
194
 
194
- //# sourceMappingURL=events-CY6V9oQ4.mjs.map
195
+ //# sourceMappingURL=events-lDt9nTUw.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"events-lDt9nTUw.mjs","names":[],"sources":["../src/constants.ts","../src/events.ts"],"sourcesContent":["export const CLAUDE_CODE_PROVIDER = 'claude'\nexport const CLAUDE_CODE_PRESET_ID = 'claude'\nexport const CLAUDE_CODE_PROVIDER_IDS = [CLAUDE_CODE_PROVIDER] as const\nexport const CLAUDE_SESSION_BOUND_EVENT = 'claude-code/session-bound'\nexport const CLAUDE_ACTIVITY_EVENT = 'claude-code/activity'\nexport const CLAUDE_CONTEXT_USAGE_EVENT = 'claude-code/context-usage'\nexport const CLAUDE_TASKS_EVENT = 'claude-code/tasks'\n/** Claude's subagent dispatch tools; rendered as plugin-owned group cards\n * gathering subagent activity instead of native tool cards. */\nexport const TASK_TOOL_NAMES: ReadonlySet<string> = new Set(['Task', 'Agent'])\nexport const SDK_VERSION = '0.3.247'\nexport const CLAUDE_DOCTOR_PATH = '/plugins/dsh-claude/doctor'\nexport const CLAUDE_UPDATE_CHECK_PATH = '/plugins/dsh-claude/update/check'\nexport const CLAUDE_USAGE_PATH = '/plugins/dsh-claude/usage'\nexport const CLAUDE_UPDATE_PATH = '/plugins/dsh-claude/update'\nexport const CLAUDE_PROJECTION_PATH = '/plugins/dsh-claude/projection'\nexport const CLAUDE_GLOBAL_SETTINGS_PATH = '/plugins/dsh-claude/settings/global'\nexport const CLAUDE_REPOSITORY_SETUP_PATH = '/plugins/dsh-claude/repository/setup'\nexport const CLAUDE_REPOSITORY_ACTION_PATH = '/plugins/dsh-claude/repository/action'\nexport const CLAUDE_REVIEW_COMMENT_PATH = '/plugins/dsh-claude/review-comments'\nexport const CLAUDE_REPOSITORY_FEEDBACK_PATH = '/plugins/dsh-claude/repository/feedback'\nexport const CLAUDE_REPOSITORY_STATUS_PATH = '/plugins/dsh-claude/repository/status'\nexport const CLAUDE_REPOSITORY_FILE_PATH = '/plugins/dsh-claude/repository/file'\nexport const CLAUDE_JIRA_PATH = '/plugins/dsh-claude/jira'\nexport const CLAUDE_ASK_PATH = '/plugins/dsh-claude/ask'\n","import type { SessionEvent } from '@deepseek-ai/dsh-session'\nimport {\n CLAUDE_ACTIVITY_EVENT,\n CLAUDE_CONTEXT_USAGE_EVENT,\n CLAUDE_SESSION_BOUND_EVENT,\n CLAUDE_TASKS_EVENT,\n} from './constants.ts'\n\nexport type ClaudeActivityKind =\n | 'text'\n | 'status'\n | 'thinking'\n | 'tool-call'\n | 'tool-result'\n | 'permission'\n | 'question'\n | 'subagent'\n | 'usage'\n | 'warning'\n | 'error'\n\nexport type ClaudeActivityPhase =\n | 'started'\n | 'updated'\n | 'completed'\n | 'denied'\n | 'failed'\n\nexport interface ClaudeUsage {\n inputTokens?: number\n outputTokens?: number\n cacheReadTokens?: number\n cacheCreationTokens?: number\n cumulativeCostUsd?: number\n}\n\nexport interface ClaudeSessionBoundEvent {\n claudeSessionId: string\n cliVersion?: string\n sdkVersion: string\n cwd: string\n}\n\nexport interface ClaudeActivityEvent {\n turn: number\n step: number\n ordinal: number\n kind: ClaudeActivityKind\n phase?: ClaudeActivityPhase\n /** Claude task-board identity for lifecycle activity; never a transcript path. */\n taskId?: string\n toolUseId?: string\n /** Enclosing Claude tool call for subagent-nested activity. */\n parentToolUseId?: string\n toolName?: string\n title?: string\n summary?: string\n detail?: string\n /** Redacted visible Claude prose used by the plugin-owned interleaved transcript. */\n text?: string\n isError?: boolean\n usage?: ClaudeUsage\n}\n\nexport interface ClaudeContextUsageCategory {\n name: string\n tokens: number\n color: string\n isDeferred?: boolean\n}\n\nexport interface ClaudeContextUsageEvent {\n model: string\n totalTokens: number\n maxTokens: number\n percentage: number\n categories: readonly ClaudeContextUsageCategory[]\n}\n\nexport interface ClaudeContextUsageInput {\n model: unknown\n totalTokens: unknown\n maxTokens: unknown\n percentage: unknown\n categories: readonly {\n name?: unknown\n tokens?: unknown\n color?: unknown\n isDeferred?: unknown\n }[]\n}\n\ndeclare module '@deepseek-ai/dsh-session/types' {\n interface SessionEventMap {\n 'claude-code/session-bound': ClaudeSessionBoundEvent\n 'claude-code/activity': ClaudeActivityEvent\n 'claude-code/context-usage': ClaudeContextUsageEvent\n 'claude-code/tasks': ClaudeTasksEvent\n }\n}\n\nconst SECRET_KEY = /(?:^|[_-])(password|passwd|secret|token|api[_-]?key|authorization|credential|private[_-]?key|session[_-]?key|env|environ|environment)(?:$|[_-])/i\nconst MAX_SUMMARY_CHARS = 1_000\nconst MAX_DETAIL_CHARS = 4_000\nconst MAX_TRANSCRIPT_TEXT_CHARS = 64_000\nconst MAX_DEPTH = 6\nconst MAX_ARRAY_ITEMS = 40\nconst MAX_OBJECT_KEYS = 60\nconst REDACTED = '[REDACTED]'\nconst TRUNCATED = '…[truncated]'\nconst SECRET_ASSIGNMENT = /((?:password|passwd|secret|token|api[_-]?key|authorization|credential|private[_-]?key|session[_-]?key)\\s*(?:=|:)\\s*)(?:\"[^\"]*\"|'[^']*'|[^\\s,;&]+)/giu\nconst BEARER_TOKEN = /(\\bbearer\\s+)[A-Za-z0-9._~+/=-]+/giu\nconst PREFIXED_TOKEN = /\\b(?:sk-(?:ant-|proj-)?|xox[baprs]-|ghp_|github_pat_)[A-Za-z0-9_-]{8,}/giu\nconst JWT_TOKEN = /\\beyJ[A-Za-z0-9_-]+\\.[A-Za-z0-9_-]+\\.[A-Za-z0-9_-]+\\b/gu\nconst URL_USERINFO = /([a-z][a-z0-9+.-]*:\\/\\/[^:\\s/@]+:)[^@\\s/]+@/giu\nconst URL_SECRET_PARAM = /([?&](?:password|secret|token|api[_-]?key|access[_-]?token|refresh[_-]?token)=)[^&#\\s]+/giu\n\nexport function boundText(value: string, maxChars: number): string {\n if (value.length <= maxChars) return value\n return `${value.slice(0, Math.max(0, maxChars - TRUNCATED.length))}${TRUNCATED}`\n}\n\nexport function redactText(value: string, maxChars = MAX_DETAIL_CHARS): string {\n return boundText(\n value\n .replace(JWT_TOKEN, REDACTED)\n .replace(PREFIXED_TOKEN, REDACTED)\n .replace(BEARER_TOKEN, `$1${REDACTED}`)\n .replace(URL_USERINFO, `$1${REDACTED}@`)\n .replace(URL_SECRET_PARAM, `$1${REDACTED}`)\n .replace(SECRET_ASSIGNMENT, `$1${REDACTED}`),\n maxChars,\n )\n}\n\nexport function redactValue(value: unknown, depth = 0, seen = new WeakSet<object>()): unknown {\n if (depth > MAX_DEPTH) return '[max-depth]'\n if (value === null || typeof value === 'boolean' || typeof value === 'number') return value\n if (typeof value === 'string') return redactText(value)\n if (typeof value === 'bigint') return value.toString()\n if (typeof value === 'undefined') return null\n if (typeof value === 'function' || typeof value === 'symbol') return `[${typeof value}]`\n if (value instanceof Error) {\n return { name: value.name, message: redactText(value.message, MAX_SUMMARY_CHARS) }\n }\n if (typeof value !== 'object') return String(value)\n if (seen.has(value)) return '[circular]'\n seen.add(value)\n try {\n if (Array.isArray(value)) {\n const items = value.slice(0, MAX_ARRAY_ITEMS).map(item => redactValue(item, depth + 1, seen))\n if (value.length > MAX_ARRAY_ITEMS) items.push(`[${value.length - MAX_ARRAY_ITEMS} more items]`)\n return items\n }\n const result: Record<string, unknown> = {}\n const entries = Object.entries(value as Record<string, unknown>)\n for (const [key, item] of entries.slice(0, MAX_OBJECT_KEYS)) {\n result[key] = SECRET_KEY.test(key) ? REDACTED : redactValue(item, depth + 1, seen)\n }\n if (entries.length > MAX_OBJECT_KEYS) result.__truncatedKeys = entries.length - MAX_OBJECT_KEYS\n return result\n } finally {\n seen.delete(value)\n }\n}\n\nexport function safeDetail(value: unknown): string | undefined {\n if (value === undefined) return undefined\n const redacted = redactValue(value)\n const text = typeof redacted === 'string' ? redacted : JSON.stringify(redacted)\n return boundText(text, MAX_DETAIL_CHARS)\n}\n\nexport function normalizeActivity(\n activity: Omit<ClaudeActivityEvent, 'summary' | 'detail'> & {\n summary?: unknown\n detail?: unknown\n },\n): ClaudeActivityEvent {\n const normalized: ClaudeActivityEvent = {\n turn: activity.turn,\n step: activity.step,\n ordinal: activity.ordinal,\n kind: activity.kind,\n }\n if (activity.phase !== undefined) normalized.phase = activity.phase\n if (activity.taskId !== undefined) normalized.taskId = redactText(activity.taskId, 128)\n if (activity.toolUseId !== undefined) normalized.toolUseId = redactText(activity.toolUseId, 256)\n if (activity.parentToolUseId !== undefined) normalized.parentToolUseId = redactText(activity.parentToolUseId, 256)\n if (activity.toolName !== undefined) normalized.toolName = redactText(activity.toolName, 256)\n if (activity.title !== undefined) normalized.title = redactText(activity.title, MAX_SUMMARY_CHARS)\n if (activity.summary !== undefined) {\n normalized.summary = redactText(\n typeof activity.summary === 'string' ? activity.summary : safeDetail(activity.summary) ?? '',\n MAX_SUMMARY_CHARS,\n )\n }\n const detail = safeDetail(activity.detail)\n if (detail !== undefined) normalized.detail = detail\n if (activity.text !== undefined) normalized.text = redactText(activity.text, MAX_TRANSCRIPT_TEXT_CHARS)\n if (activity.isError !== undefined) normalized.isError = activity.isError\n if (activity.usage !== undefined) normalized.usage = { ...activity.usage }\n return normalized\n}\n\nconst MAX_CONTEXT_CATEGORIES = 24\nconst FALLBACK_CONTEXT_COLOR = '#8b95a5'\nconst SAFE_CONTEXT_COLOR = /^#[0-9a-f]{3,8}$/iu\n\nfunction nonNegativeInteger(value: unknown): number {\n return typeof value === 'number' && Number.isFinite(value)\n ? Math.max(0, Math.floor(value))\n : 0\n}\n\nexport function normalizeContextUsage(input: ClaudeContextUsageInput): ClaudeContextUsageEvent {\n return {\n model: redactText(typeof input.model === 'string' ? input.model : 'unknown', 128),\n totalTokens: nonNegativeInteger(input.totalTokens),\n maxTokens: nonNegativeInteger(input.maxTokens),\n percentage: Math.min(100, nonNegativeInteger(input.percentage)),\n categories: input.categories.slice(0, MAX_CONTEXT_CATEGORIES).map(category => ({\n name: redactText(typeof category.name === 'string' ? category.name : 'Unknown', 128),\n tokens: nonNegativeInteger(category.tokens),\n color: typeof category.color === 'string' && SAFE_CONTEXT_COLOR.test(category.color)\n ? category.color\n : FALLBACK_CONTEXT_COLOR,\n ...(category.isDeferred === true ? { isDeferred: true } : {}),\n })),\n }\n}\n\nexport function latestClaudeContextUsage(\n events: readonly SessionEvent[],\n): ClaudeContextUsageEvent | undefined {\n for (let index = events.length - 1; index >= 0; index -= 1) {\n const event = events[index]\n if (event?.type === CLAUDE_CONTEXT_USAGE_EVENT) return event.data as ClaudeContextUsageEvent\n }\n return undefined\n}\n\nexport type ClaudeTaskStatus = 'running' | 'completed' | 'failed' | 'stopped' | 'killed'\n\nexport interface ClaudeTaskUsage {\n totalTokens?: number\n toolUses?: number\n durationMs?: number\n}\n\nexport interface ClaudeTaskInfo {\n taskId: string\n description: string\n status: ClaudeTaskStatus\n /** DSH turn during which this task was first observed, when known. */\n originTurn?: number\n subagentType?: string\n taskType?: string\n lastToolName?: string\n summary?: string\n usage?: ClaudeTaskUsage\n /** True while the task runs detached (background command/subagent). */\n backgrounded?: boolean\n}\n\n/** Level snapshot of one session's Claude task board, REPLACE semantics. */\nexport interface ClaudeTasksEvent {\n tasks: readonly ClaudeTaskInfo[]\n}\n\nconst MAX_TASKS_PER_SNAPSHOT = 50\nconst MAX_TASK_TEXT_CHARS = 300\n\nconst TASK_STATUSES: ReadonlySet<string> = new Set(['running', 'completed', 'failed', 'stopped', 'killed'])\n\nfunction normalizeTaskUsage(input: ClaudeTaskUsage | undefined): ClaudeTaskUsage | undefined {\n if (input === undefined) return undefined\n const usage: ClaudeTaskUsage = {}\n if (input.totalTokens !== undefined) usage.totalTokens = nonNegativeInteger(input.totalTokens)\n if (input.toolUses !== undefined) usage.toolUses = nonNegativeInteger(input.toolUses)\n if (input.durationMs !== undefined) usage.durationMs = nonNegativeInteger(input.durationMs)\n return Object.keys(usage).length === 0 ? undefined : usage\n}\n\nexport function normalizeTasksEvent(tasks: readonly ClaudeTaskInfo[]): ClaudeTasksEvent {\n return {\n tasks: tasks.slice(0, MAX_TASKS_PER_SNAPSHOT).map(task => {\n const usage = normalizeTaskUsage(task.usage)\n return {\n taskId: redactText(String(task.taskId), 128),\n description: redactText(String(task.description), MAX_TASK_TEXT_CHARS),\n status: TASK_STATUSES.has(task.status) ? task.status : 'running',\n ...(task.originTurn === undefined ? {} : { originTurn: nonNegativeInteger(task.originTurn) }),\n ...(task.subagentType === undefined ? {} : { subagentType: redactText(task.subagentType, 64) }),\n ...(task.taskType === undefined ? {} : { taskType: redactText(task.taskType, 64) }),\n ...(task.lastToolName === undefined ? {} : { lastToolName: redactText(task.lastToolName, 64) }),\n ...(task.summary === undefined ? {} : { summary: redactText(task.summary, MAX_TASK_TEXT_CHARS) }),\n ...(usage === undefined ? {} : { usage }),\n ...(task.backgrounded === true ? { backgrounded: true } : {}),\n }\n }),\n }\n}\n\nexport function latestClaudeTasks(\n events: readonly SessionEvent[],\n): ClaudeTasksEvent | undefined {\n for (let index = events.length - 1; index >= 0; index -= 1) {\n const event = events[index]\n if (event?.type === CLAUDE_TASKS_EVENT) return event.data as ClaudeTasksEvent\n }\n return undefined\n}\n\nexport type ClaudeActivityInput = Omit<\n ClaudeActivityEvent,\n 'turn' | 'step' | 'ordinal' | 'summary' | 'detail'\n> & {\n summary?: unknown\n detail?: unknown\n}\n\nexport interface ClaudeActivityCursor {\n turn: number\n step: number\n nextOrdinal: number\n}\n\n/** Derive the current DSH turn/step; activity ordinals are completed from the sidecar. */\nexport function currentClaudeActivityCursor(events: readonly SessionEvent[]): ClaudeActivityCursor {\n let turn = 0\n let step = 0\n for (const event of events) {\n if (event.type !== 'step/start') continue\n const data = event.data as { turn: number; step: number }\n turn = data.turn\n step = data.step\n }\n if (turn < 1 || step < 1) {\n throw new Error('dsh-claude: Claude activity requires an open DSH step')\n }\n return { turn, step, nextOrdinal: 0 }\n}\n\nexport function latestClaudeSessionBinding(\n events: readonly SessionEvent[],\n): ClaudeSessionBoundEvent | undefined {\n for (let index = events.length - 1; index >= 0; index -= 1) {\n const event = events[index]\n if (event?.type === CLAUDE_SESSION_BOUND_EVENT) {\n return event.data as ClaudeSessionBoundEvent\n }\n }\n return undefined\n}\n"],"mappings":"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"}
package/lib/index.d.mts CHANGED
@@ -253,6 +253,10 @@ declare class ClaudeSupervisor {
253
253
  supportedCommands(agent: Agent, model?: string): Promise<readonly SlashCommand[]>;
254
254
  contextUsage(agent: Agent, model?: string): Promise<SDKControlGetContextUsageResponse>;
255
255
  contextWindow(model: string): number | undefined;
256
+ /** Raw `/usage` payload, read over a session's existing process. Only the
257
+ * idle-time metadata bridge uses this; a user-triggered refresh runs
258
+ * probePlanUsage instead so it never waits on, or perturbs, a session. */
259
+ planUsage(agent: Agent, model?: string): Promise<unknown>;
256
260
  runTurn(request: ClaudeTurnRequest): Promise<AsyncIterable<ClaudeTurnStreamEvent>>;
257
261
  disposeSession(sessionId: string): Promise<void>;
258
262
  dispose(): Promise<void>;
package/lib/index.mjs CHANGED
@@ -1,6 +1,6 @@
1
- import { C as CLAUDE_REPOSITORY_SETUP_PATH, D as CLAUDE_UPDATE_PATH, E as CLAUDE_UPDATE_CHECK_PATH, S as CLAUDE_REPOSITORY_FILE_PATH, T as CLAUDE_REVIEW_COMMENT_PATH, _ as CLAUDE_GLOBAL_SETTINGS_PATH, a as latestClaudeTasks, b as CLAUDE_REPOSITORY_ACTION_PATH, c as normalizeTasksEvent, d as CLAUDE_ACTIVITY_EVENT, f as CLAUDE_ASK_PATH, g as CLAUDE_DOCTOR_PATH, h as CLAUDE_CODE_PROVIDER_IDS, i as latestClaudeSessionBinding, k as TASK_TOOL_NAMES, l as redactText, m as CLAUDE_CODE_PROVIDER, n as currentClaudeActivityCursor, o as normalizeActivity, p as CLAUDE_CODE_PRESET_ID, r as latestClaudeContextUsage, s as normalizeContextUsage, t as boundText, u as safeDetail, v as CLAUDE_JIRA_PATH, w as CLAUDE_REPOSITORY_STATUS_PATH, x as CLAUDE_REPOSITORY_FEEDBACK_PATH, y as CLAUDE_PROJECTION_PATH } from "./events-CY6V9oQ4.mjs";
2
- import { n as projectClaudeCommands, t as CLAUDE_COMMANDS_SERVICE } from "./command-bridge-vyZoEeEO.mjs";
3
- import { a as resolveClaudeExecutable, n as ensureManagedPreset, o as runClaudeDoctor, t as ManagedPresetConflictError } from "./preset-installer-BMRaIHLu.mjs";
1
+ import { A as TASK_TOOL_NAMES, C as CLAUDE_REPOSITORY_SETUP_PATH, D as CLAUDE_UPDATE_PATH, E as CLAUDE_UPDATE_CHECK_PATH, O as CLAUDE_USAGE_PATH, S as CLAUDE_REPOSITORY_FILE_PATH, T as CLAUDE_REVIEW_COMMENT_PATH, _ as CLAUDE_GLOBAL_SETTINGS_PATH, a as latestClaudeTasks, b as CLAUDE_REPOSITORY_ACTION_PATH, c as normalizeTasksEvent, d as CLAUDE_ACTIVITY_EVENT, f as CLAUDE_ASK_PATH, g as CLAUDE_DOCTOR_PATH, h as CLAUDE_CODE_PROVIDER_IDS, i as latestClaudeSessionBinding, l as redactText, m as CLAUDE_CODE_PROVIDER, n as currentClaudeActivityCursor, o as normalizeActivity, p as CLAUDE_CODE_PRESET_ID, r as latestClaudeContextUsage, s as normalizeContextUsage, t as boundText, u as safeDetail, v as CLAUDE_JIRA_PATH, w as CLAUDE_REPOSITORY_STATUS_PATH, x as CLAUDE_REPOSITORY_FEEDBACK_PATH, y as CLAUDE_PROJECTION_PATH } from "./events-lDt9nTUw.mjs";
2
+ import { n as projectClaudeCommands, t as CLAUDE_COMMANDS_SERVICE } from "./command-bridge-BYj0VF4J.mjs";
3
+ import { a as resolveClaudeExecutable, n as ensureManagedPreset, o as runClaudeDoctor, t as ManagedPresetConflictError } from "./preset-installer-CPOH9lAr.mjs";
4
4
  import z from "@deepseek-ai/schemastery";
5
5
  import { createHash, randomUUID } from "node:crypto";
6
6
  import { chmod, mkdir, opendir, readFile, realpath, rename, rm, stat, writeFile } from "node:fs/promises";
@@ -26,7 +26,7 @@ function emptyProjection() {
26
26
  activities: []
27
27
  };
28
28
  }
29
- function record$10(value) {
29
+ function record$11(value) {
30
30
  return value !== null && typeof value === "object" && !Array.isArray(value) ? value : void 0;
31
31
  }
32
32
  function finiteInteger(value) {
@@ -36,7 +36,7 @@ function string$4(value, max) {
36
36
  return typeof value === "string" && value.length > 0 && value.length <= max;
37
37
  }
38
38
  function binding(value) {
39
- const input = record$10(value);
39
+ const input = record$11(value);
40
40
  if (input === void 0 || !string$4(input.claudeSessionId, 512) || !string$4(input.sdkVersion, 128) || !string$4(input.cwd, 4096) || input.cliVersion !== void 0 && !string$4(input.cliVersion, 128)) return void 0;
41
41
  return {
42
42
  claudeSessionId: input.claudeSessionId,
@@ -66,22 +66,22 @@ const ACTIVITY_PHASES = /* @__PURE__ */ new Set([
66
66
  "failed"
67
67
  ]);
68
68
  function activity(value) {
69
- const input = record$10(value);
69
+ const input = record$11(value);
70
70
  if (input === void 0 || !finiteInteger(input.turn) || !finiteInteger(input.step) || !finiteInteger(input.ordinal) || typeof input.kind !== "string" || !ACTIVITY_KINDS.has(input.kind) || input.phase !== void 0 && (typeof input.phase !== "string" || !ACTIVITY_PHASES.has(input.phase))) return void 0;
71
71
  return normalizeActivity(input);
72
72
  }
73
73
  function contextUsage(value) {
74
- const input = record$10(value);
74
+ const input = record$11(value);
75
75
  if (input === void 0 || !Array.isArray(input.categories)) return void 0;
76
76
  return normalizeContextUsage(input);
77
77
  }
78
78
  function tasks(value) {
79
- const input = record$10(value);
79
+ const input = record$11(value);
80
80
  if (input === void 0 || !Array.isArray(input.tasks)) return void 0;
81
81
  return normalizeTasksEvent(input.tasks);
82
82
  }
83
83
  function parseClaudeSidecar(value) {
84
- const input = record$10(value);
84
+ const input = record$11(value);
85
85
  if (input === void 0 || input.schemaVersion !== SIDECAR_SCHEMA_VERSION || !finiteInteger(input.revision) || !Array.isArray(input.activities) || input.activities.length > MAX_ACTIVITIES) throw new Error("dsh-claude: invalid sidecar document");
86
86
  const activities = input.activities.map(activity);
87
87
  if (activities.some((item) => item === void 0)) throw new Error("dsh-claude: invalid sidecar activity");
@@ -654,7 +654,7 @@ function createUserQuestionBridge(userQuestions, activeContext) {
654
654
  }
655
655
  //#endregion
656
656
  //#region src/sdk-messages.ts
657
- function record$9(value) {
657
+ function record$10(value) {
658
658
  return value !== null && typeof value === "object" ? value : void 0;
659
659
  }
660
660
  function string$3(value) {
@@ -669,7 +669,7 @@ function taskUsageOf(usage) {
669
669
  return Object.keys(normalized).length === 0 ? void 0 : normalized;
670
670
  }
671
671
  function resultUsage(message) {
672
- const usage = record$9(message.usage);
672
+ const usage = record$10(message.usage);
673
673
  const normalized = {};
674
674
  if (usage !== void 0) {
675
675
  if (typeof usage.input_tokens === "number") normalized.inputTokens = usage.input_tokens;
@@ -681,7 +681,7 @@ function resultUsage(message) {
681
681
  return normalized;
682
682
  }
683
683
  function normalizeAssistant(message) {
684
- const content = record$9(message.message)?.content;
684
+ const content = record$10(message.message)?.content;
685
685
  if (!Array.isArray(content)) return [{
686
686
  kind: "protocol-error",
687
687
  title: "Malformed Claude assistant message",
@@ -690,7 +690,7 @@ function normalizeAssistant(message) {
690
690
  const parentToolUseId = string$3(message.parent_tool_use_id);
691
691
  const normalized = [];
692
692
  for (const item of content) {
693
- const block = record$9(item);
693
+ const block = record$10(item);
694
694
  if (block === void 0) continue;
695
695
  if (block.type === "text") {
696
696
  const text = string$3(block.text);
@@ -723,7 +723,7 @@ function normalizeAssistant(message) {
723
723
  }
724
724
  function normalizeUser(message) {
725
725
  if (message.isReplay === true) return [];
726
- const content = record$9(message.message)?.content;
726
+ const content = record$10(message.message)?.content;
727
727
  if (typeof content === "string") return [];
728
728
  if (!Array.isArray(content)) return [{
729
729
  kind: "protocol-error",
@@ -733,7 +733,7 @@ function normalizeUser(message) {
733
733
  const parentToolUseId = string$3(message.parent_tool_use_id);
734
734
  const normalized = [];
735
735
  for (const item of content) {
736
- const block = record$9(item);
736
+ const block = record$10(item);
737
737
  if (block?.type !== "tool_result") continue;
738
738
  const toolUseId = string$3(block.tool_use_id);
739
739
  if (toolUseId === void 0) continue;
@@ -814,7 +814,7 @@ function normalizeSystem(message) {
814
814
  const summary = string$3(message.summary);
815
815
  const subagentType = string$3(message.subagent_type);
816
816
  const lastToolName = string$3(message.last_tool_name);
817
- const usage = taskUsageOf(record$9(message.usage));
817
+ const usage = taskUsageOf(record$10(message.usage));
818
818
  return [{
819
819
  kind: "subagent",
820
820
  title: summary ?? description ?? "Claude subagent update",
@@ -830,7 +830,7 @@ function normalizeSystem(message) {
830
830
  }];
831
831
  }
832
832
  if (subtype === "task_updated") {
833
- const patch = record$9(message.patch);
833
+ const patch = record$10(message.patch);
834
834
  const status = string$3(patch?.status);
835
835
  const taskId = string$3(message.task_id);
836
836
  const description = string$3(patch?.description);
@@ -853,7 +853,7 @@ function normalizeSystem(message) {
853
853
  const taskId = string$3(message.task_id);
854
854
  const summary = string$3(message.summary);
855
855
  const taskStatus = failed ? "failed" : stopped ? "stopped" : "completed";
856
- const usage = taskUsageOf(record$9(message.usage));
856
+ const usage = taskUsageOf(record$10(message.usage));
857
857
  return [{
858
858
  kind: "subagent",
859
859
  title: summary ?? taskId ?? "Claude subagent finished",
@@ -868,7 +868,7 @@ function normalizeSystem(message) {
868
868
  if (subtype === "background_tasks_changed") return [{
869
869
  kind: "background-tasks",
870
870
  tasks: (Array.isArray(message.tasks) ? message.tasks : []).flatMap((item) => {
871
- const entry = record$9(item);
871
+ const entry = record$10(item);
872
872
  const taskId = string$3(entry?.task_id);
873
873
  const description = string$3(entry?.description);
874
874
  const taskType = string$3(entry?.task_type);
@@ -911,10 +911,10 @@ const RESULT_ERROR_SUBTYPES = /* @__PURE__ */ new Set([
911
911
  function normalizeSdkMessage(message) {
912
912
  const value = message;
913
913
  if (value.type === "stream_event") {
914
- const event = record$9(value.event);
914
+ const event = record$10(value.event);
915
915
  const parentToolUseId = string$3(value.parent_tool_use_id);
916
916
  if (event?.type === "content_block_delta") {
917
- const delta = record$9(event.delta);
917
+ const delta = record$10(event.delta);
918
918
  if (delta?.type === "text_delta") {
919
919
  const text = string$3(delta.text);
920
920
  return text === void 0 ? [] : [{
@@ -949,7 +949,7 @@ function normalizeSdkMessage(message) {
949
949
  const errors = Array.isArray(value.errors) ? value.errors.filter((item) => typeof item === "string") : void 0;
950
950
  const terminalReason = string$3(value.terminal_reason);
951
951
  const userMessageUuid = string$3(value.user_message_uuid);
952
- const permissionDenials = Array.isArray(value.permission_denials) ? value.permission_denials.map((item) => record$9(item)).filter((item) => item !== void 0).map((item) => {
952
+ const permissionDenials = Array.isArray(value.permission_denials) ? value.permission_denials.map((item) => record$10(item)).filter((item) => item !== void 0).map((item) => {
953
953
  const toolName = string$3(item.tool_name);
954
954
  const toolUseId = string$3(item.tool_use_id);
955
955
  return toolName === void 0 || toolUseId === void 0 ? void 0 : {
@@ -975,7 +975,7 @@ function normalizeSdkMessage(message) {
975
975
  detail: value.error ?? value.output
976
976
  }];
977
977
  if (value.type === "rate_limit_event") {
978
- const status = string$3(record$9(value.rate_limit_info)?.status);
978
+ const status = string$3(record$10(value.rate_limit_info)?.status);
979
979
  return [{
980
980
  kind: "status",
981
981
  title: status !== void 0 && status !== "allowed" ? "Claude rate limit is blocking requests" : "Claude rate limit status changed",
@@ -989,6 +989,117 @@ function normalizeSdkMessage(message) {
989
989
  }];
990
990
  }
991
991
  //#endregion
992
+ //#region src/plan-usage.ts
993
+ /** Claude.ai plan rate-limit windows behind the CLI's `/usage` panel.
994
+ *
995
+ * The Agent SDK exposes them through an experimental query method whose name
996
+ * advertises its own instability, so every read is guarded: a missing method,
997
+ * a shape change, or an API-key session all degrade to `available: false`
998
+ * instead of breaking the settings page. */
999
+ /** The SDK's own name for the unstable `/usage` control request; it is
1000
+ * documented to change when the API stabilizes, so it lives in one place. */
1001
+ const PLAN_USAGE_METHOD = "usage_EXPERIMENTAL_MAY_CHANGE_DO_NOT_RELY_ON_THIS_API_YET";
1002
+ /** A throwaway probe should not outlive a wedged control request. */
1003
+ const PLAN_USAGE_TIMEOUT_MS = 2e4;
1004
+ /** Invoke the experimental usage control request, or say why we cannot. */
1005
+ function readPlanUsageFrom(query) {
1006
+ const read = query[PLAN_USAGE_METHOD];
1007
+ if (typeof read !== "function") throw new Error("dsh-claude: this Claude Agent SDK build exposes no plan usage API");
1008
+ return read.call(query);
1009
+ }
1010
+ /** Fixed windows in display order; the SDK omits the ones a plan does not have. */
1011
+ const FIXED_WINDOWS = [
1012
+ "five_hour",
1013
+ "seven_day",
1014
+ "seven_day_opus",
1015
+ "seven_day_sonnet"
1016
+ ];
1017
+ function record$9(value) {
1018
+ return typeof value === "object" && value !== null && !Array.isArray(value) ? value : void 0;
1019
+ }
1020
+ /** Utilization is documented as 0-100; clamp so a server glitch cannot render
1021
+ * a bar wider than its track or a negative width. */
1022
+ function utilization(value) {
1023
+ return typeof value === "number" && Number.isFinite(value) ? Math.min(100, Math.max(0, value)) : void 0;
1024
+ }
1025
+ function resetsAt(value) {
1026
+ return typeof value === "string" && value.length > 0 ? value : void 0;
1027
+ }
1028
+ function window(id, source, label) {
1029
+ const entry = record$9(source);
1030
+ if (entry === void 0) return void 0;
1031
+ const used = utilization(entry.utilization);
1032
+ const reset = resetsAt(entry.resets_at);
1033
+ if (used === void 0 && reset === void 0) return void 0;
1034
+ return {
1035
+ id,
1036
+ ...label === void 0 ? {} : { label },
1037
+ ...used === void 0 ? {} : { utilization: used },
1038
+ ...reset === void 0 ? {} : { resetsAt: reset }
1039
+ };
1040
+ }
1041
+ /** Project the SDK's `/usage` response onto the windows the settings card shows. */
1042
+ function normalizePlanUsage(value, fetchedAt) {
1043
+ const response = record$9(value);
1044
+ const subscription = typeof response?.subscription_type === "string" ? response.subscription_type : void 0;
1045
+ const limits = record$9(response?.rate_limits);
1046
+ if (response?.rate_limits_available !== true || limits === void 0) return {
1047
+ available: false,
1048
+ ...subscription === void 0 ? {} : { subscription },
1049
+ windows: [],
1050
+ fetchedAt
1051
+ };
1052
+ const windows = [...FIXED_WINDOWS.map((id) => window(id, limits[id])), ...(Array.isArray(limits.model_scoped) ? limits.model_scoped : []).map((entry, index) => {
1053
+ const name = record$9(entry)?.display_name;
1054
+ return window(`model:${typeof name === "string" ? name : index}`, entry, typeof name === "string" ? name : void 0);
1055
+ })].filter((entry) => entry !== void 0);
1056
+ return {
1057
+ available: windows.length > 0,
1058
+ ...subscription === void 0 ? {} : { subscription },
1059
+ windows,
1060
+ fetchedAt
1061
+ };
1062
+ }
1063
+ /** Read plan limits from a throwaway Claude process.
1064
+ *
1065
+ * Deliberately NOT routed through the supervisor. Borrowing a session's
1066
+ * process fails outright while that session is mid-turn, and for a session
1067
+ * with no process yet it would resume the whole conversation — and possibly
1068
+ * evict another idle session to make room — just to read three numbers. This
1069
+ * query carries no tools, no permission bridge, and no session binding: it
1070
+ * starts, answers one control request, and is killed. */
1071
+ async function probePlanUsage(executablePath, fetchedAt, factory = query) {
1072
+ const lifetime = new AbortController();
1073
+ const timer = setTimeout(() => lifetime.abort(), PLAN_USAGE_TIMEOUT_MS);
1074
+ timer.unref?.();
1075
+ const query$1 = factory({
1076
+ prompt: (async function* () {
1077
+ await new Promise(() => {});
1078
+ })(),
1079
+ options: {
1080
+ cwd: process.cwd(),
1081
+ abortController: lifetime,
1082
+ ...executablePath.length === 0 ? {} : { pathToClaudeCodeExecutable: executablePath }
1083
+ }
1084
+ });
1085
+ try {
1086
+ (async () => {
1087
+ for await (const _ of query$1);
1088
+ })().catch(() => void 0);
1089
+ return normalizePlanUsage(await readPlanUsageFrom(query$1), fetchedAt);
1090
+ } finally {
1091
+ clearTimeout(timer);
1092
+ lifetime.abort();
1093
+ }
1094
+ }
1095
+ let latest;
1096
+ function recordPlanUsage(report) {
1097
+ latest = report;
1098
+ }
1099
+ function latestPlanUsage() {
1100
+ return latest;
1101
+ }
1102
+ //#endregion
992
1103
  //#region src/spawn.ts
993
1104
  const CLAUDE_PROCESS_GRACE_MS = 2e3;
994
1105
  const CLAUDE_STDERR_TAIL_BYTES = 32768;
@@ -1206,6 +1317,12 @@ var ClaudeSupervisor = class {
1206
1317
  contextWindow(model) {
1207
1318
  return this.#contextWindows.get(model);
1208
1319
  }
1320
+ /** Raw `/usage` payload, read over a session's existing process. Only the
1321
+ * idle-time metadata bridge uses this; a user-triggered refresh runs
1322
+ * probePlanUsage instead so it never waits on, or perturbs, a session. */
1323
+ planUsage(agent, model = this.#config.defaultModel) {
1324
+ return this.#runMetadata(agent, model, (query) => readPlanUsageFrom(query));
1325
+ }
1209
1326
  runTurn(request) {
1210
1327
  const interruption = this.#interruptions.get(request.agent.id);
1211
1328
  if (interruption !== void 0) return interruption.then(() => this.runTurn(request));
@@ -2385,7 +2502,7 @@ function json(res, status, value) {
2385
2502
  //#region src/doctor-routes.ts
2386
2503
  const CLAUDE_DOCTOR_PROBE_TIMEOUT_MS = 15e3;
2387
2504
  const claudeBridgeDiagnostics = /* @__PURE__ */ new WeakMap();
2388
- function safeMessage$1(error) {
2505
+ function safeMessage$2(error) {
2389
2506
  return redactText(error instanceof Error ? error.message : String(error), 1e3);
2390
2507
  }
2391
2508
  /** Live command-bridge diagnostics: which agents exist, their presets, and how
@@ -2401,14 +2518,14 @@ function commandDiagnostics(ctx) {
2401
2518
  const preset = ctx.agentPresets.composedPreset(agent.ctx);
2402
2519
  if (preset !== void 0) info.preset = preset;
2403
2520
  } catch (error) {
2404
- info.error = safeMessage$1(error);
2521
+ info.error = safeMessage$2(error);
2405
2522
  }
2406
2523
  try {
2407
2524
  const list = ctx.commands.list(agent);
2408
2525
  info.commandCount = list.length;
2409
2526
  info.sample = list.slice(0, 10).map((command) => command.name);
2410
2527
  } catch (error) {
2411
- info.error = info.error === void 0 ? safeMessage$1(error) : `${info.error}; ${safeMessage$1(error)}`;
2528
+ info.error = info.error === void 0 ? safeMessage$2(error) : `${info.error}; ${safeMessage$2(error)}`;
2412
2529
  }
2413
2530
  const bridge = claudeBridgeDiagnostics.get(agent);
2414
2531
  if (bridge !== void 0) info.bridge = bridge;
@@ -2416,7 +2533,7 @@ function commandDiagnostics(ctx) {
2416
2533
  })
2417
2534
  };
2418
2535
  } catch (error) {
2419
- return { error: safeMessage$1(error) };
2536
+ return { error: safeMessage$2(error) };
2420
2537
  }
2421
2538
  }
2422
2539
  function registerClaudeDoctorRoutes(ctx, runtime, supervisor, config, resolutionError) {
@@ -2440,7 +2557,7 @@ function registerClaudeDoctorRoutes(ctx, runtime, supervisor, config, resolution
2440
2557
  version: { status: "not-run" },
2441
2558
  authentication: { status: "not-run" },
2442
2559
  handshake: "not-run",
2443
- message: safeMessage$1(resolutionError),
2560
+ message: safeMessage$2(resolutionError),
2444
2561
  limits: {
2445
2562
  idleTimeoutMs: config.idleTimeoutMs,
2446
2563
  maxProcesses: config.maxProcesses
@@ -2470,7 +2587,7 @@ function registerClaudeDoctorRoutes(ctx, runtime, supervisor, config, resolution
2470
2587
  commandBridge: commandDiagnostics(ctx)
2471
2588
  });
2472
2589
  } catch (error) {
2473
- json(res, 500, { error: safeMessage$1(error) });
2590
+ json(res, 500, { error: safeMessage$2(error) });
2474
2591
  }
2475
2592
  }
2476
2593
  }), "dsh-claude: Doctor route");
@@ -3507,7 +3624,10 @@ var RepositorySetupService = class {
3507
3624
  return resolve(root);
3508
3625
  }
3509
3626
  #git() {
3510
- this.#gitPath ??= this.#runtime.resolveExecutable("git");
3627
+ this.#gitPath ??= this.#runtime.resolveExecutable("git", void 0, AbortSignal.timeout(GIT_TIMEOUT_MS$2)).catch((error) => {
3628
+ this.#gitPath = void 0;
3629
+ throw error;
3630
+ });
3511
3631
  return this.#gitPath;
3512
3632
  }
3513
3633
  async #run(executable, args, cwd, timeoutMs = GIT_TIMEOUT_MS$2) {
@@ -5158,7 +5278,7 @@ const MAX_MANIFEST_BYTES = 262144;
5158
5278
  const MAX_UPDATE_OUTPUT_BYTES = 32768;
5159
5279
  const SEMVER = /^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-([0-9A-Za-z.-]+))?(?:\+[0-9A-Za-z.-]+)?$/;
5160
5280
  const PROFILE_NAME = /^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$/;
5161
- function safeMessage(error) {
5281
+ function safeMessage$1(error) {
5162
5282
  return redactText(error instanceof Error ? error.message : String(error), 500);
5163
5283
  }
5164
5284
  async function readManifest(path) {
@@ -5320,7 +5440,7 @@ async function checkPluginUpdate(deps = {}) {
5320
5440
  state: "error",
5321
5441
  canUpdate: false,
5322
5442
  restartRequired: false,
5323
- message: safeMessage(error)
5443
+ message: safeMessage$1(error)
5324
5444
  };
5325
5445
  }
5326
5446
  }
@@ -5367,7 +5487,7 @@ async function updatePlugin(deps = {}) {
5367
5487
  const outcome = await handle.done;
5368
5488
  if (outcome.exitCode !== 0) {
5369
5489
  const detail = handle.collected.stderr?.readFrom(0).text ?? "";
5370
- throw new Error(`DSH plugin update failed (${outcome.exitCode ?? outcome.signal ?? "unknown exit"}): ${safeMessage(detail)}`);
5490
+ throw new Error(`DSH plugin update failed (${outcome.exitCode ?? outcome.signal ?? "unknown exit"}): ${safeMessage$1(detail)}`);
5371
5491
  }
5372
5492
  await verifyInstalledVersion(installation, latest);
5373
5493
  if (deps.requestRestart !== void 0) setTimeout(() => deps.requestRestart?.(), 100).unref?.();
@@ -5404,12 +5524,44 @@ function registerClaudeUpdateRoutes(ctx, runtime, deps = {}) {
5404
5524
  try {
5405
5525
  json(res, 200, await route.run());
5406
5526
  } catch (error) {
5407
- json(res, 500, { error: safeMessage(error) });
5527
+ json(res, 500, { error: safeMessage$1(error) });
5408
5528
  }
5409
5529
  }
5410
5530
  }), `dsh-claude: ${route.method} ${route.path}`);
5411
5531
  }
5412
5532
  //#endregion
5533
+ //#region src/plan-usage-routes.ts
5534
+ const EMPTY = {
5535
+ available: false,
5536
+ windows: [],
5537
+ fetchedAt: 0
5538
+ };
5539
+ function safeMessage(error) {
5540
+ return redactText(error instanceof Error ? error.message : String(error), 1e3);
5541
+ }
5542
+ /** Plan usage: GET serves the value the metadata bridge last cached (free —
5543
+ * it rides an already-running session), POST reads fresh numbers from a
5544
+ * throwaway probe process so a refresh never depends on, or disturbs, a live
5545
+ * Claude session. */
5546
+ function registerPlanUsageRoute(ctx, probe, now = Date.now) {
5547
+ ctx.effect(() => ctx.webServer.register({
5548
+ kind: "exact",
5549
+ path: CLAUDE_USAGE_PATH,
5550
+ handler: async (req, res) => {
5551
+ if (req.method !== "GET" && req.method !== "POST") return json(res, 405, { error: "method not allowed" });
5552
+ if (!trustedRequest(req)) return json(res, 403, { error: "forbidden" });
5553
+ if (req.method === "GET") return json(res, 200, latestPlanUsage() ?? EMPTY);
5554
+ try {
5555
+ const report = await probe(now());
5556
+ recordPlanUsage(report);
5557
+ json(res, 200, report);
5558
+ } catch (error) {
5559
+ json(res, 500, { error: safeMessage(error) });
5560
+ }
5561
+ }
5562
+ }), "dsh-claude: plan usage route");
5563
+ }
5564
+ //#endregion
5413
5565
  //#region src/global-settings.ts
5414
5566
  const MAX_SETTINGS_BYTES = 262144;
5415
5567
  const MAX_REQUEST_BYTES = 8192;
@@ -5793,6 +5945,13 @@ function mountClaudeMetadata(ctx, supervisor, agent, model, sidecar, publishComm
5793
5945
  } catch (error) {
5794
5946
  warn("context usage", error);
5795
5947
  }
5948
+ if (stopped) return;
5949
+ try {
5950
+ const plan = await supervisor.planUsage(agent, model);
5951
+ if (!stopped) recordPlanUsage(normalizePlanUsage(plan, Date.now()));
5952
+ } catch (error) {
5953
+ warn("plan usage", error);
5954
+ }
5796
5955
  });
5797
5956
  };
5798
5957
  return agent.ctx.effect(() => {
@@ -5965,6 +6124,7 @@ async function apply(ctx, config) {
5965
6124
  return agent !== void 0 && webCtx.agentPresets.composedPreset(agent.ctx) === "claude";
5966
6125
  };
5967
6126
  registerReviewCommentRoute(webCtx, reviewComments, ownsClaudeSession);
6127
+ registerPlanUsageRoute(webCtx, (fetchedAt) => probePlanUsage(supervisorConfig.executablePath, fetchedAt));
5968
6128
  registerClaudeProjectionRoute(webCtx, sidecar, ownsClaudeSession, (sessionId) => commandCatalogs.get(sessionId) ?? [], async (sessionId) => {
5969
6129
  const agent = webCtx.agents.get(sessionId);
5970
6130
  if (agent === void 0 || webCtx.agentPresets.composedPreset(agent.ctx) !== "claude") return void 0;