@norman-else/dsh-claude 0.1.23 → 0.1.25

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-DhrAr5gh.mjs";
1
+ import { l as redactText } from "./events-BhU0iJUi.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-Br25ODdm.mjs.map
71
+ //# sourceMappingURL=command-bridge-B48egrCr.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"command-bridge-B48egrCr.mjs","names":[],"sources":["../src/command-bridge.ts"],"sourcesContent":["import type { SlashCommand } from '@anthropic-ai/claude-agent-sdk'\r\nimport type { CommandDescriptor } from '@deepseek-ai/dsh-commands'\r\nimport { redactText } from './events.ts'\r\n\r\nconst COMMAND_NAME = /^[a-z][a-z0-9_-]*$/u\r\nconst MAX_DESCRIPTION_CHARS = 300\r\nconst MAX_HINT_CHARS = 120\r\n\r\n/** DSH registry names reject `:`, but plugin-qualified Claude skills carry it\r\n * (e.g. `awesome-skills:ci-deploy`). Derive a registry-safe public name while\r\n * forwarding keeps the exact Claude-side name. */\r\nfunction registryName(claudeName: string): string | undefined {\r\n if (COMMAND_NAME.test(claudeName)) return claudeName\r\n const short = claudeName.includes(':') ? claudeName.slice(claudeName.lastIndexOf(':') + 1) : undefined\r\n if (short !== undefined && COMMAND_NAME.test(short)) return short\r\n const sanitized = claudeName.replaceAll(':', '-').toLowerCase()\r\n return COMMAND_NAME.test(sanitized) ? sanitized : undefined\r\n}\r\n\r\n/**\r\n * Names registered as CLIENT-side commandUi contributions (not host\r\n * commands). The DSH web command palette fails its whole command group when\r\n * a host command name equals a contribution name\r\n * (\"contribution /model collides with a host command\"), so these can never\r\n * be registered by this bridge. Keep in sync with the client bundles:\r\n * dsh-client-ui-model-selection registers \"model\".\r\n */\r\nconst CLIENT_CONTRIBUTION_NAMES: ReadonlySet<string> = new Set(['model'])\r\n\r\nexport interface ClaudeCommandTarget {\r\n list(): readonly Pick<CommandDescriptor, 'name'>[]\r\n}\r\n\r\n/**\r\n * Agent-scope command-directory contract. The commands service is unreachable\r\n * from the host plugin's view of `agent.ctx` (\"without inject\"), but the\r\n * preset route plugin runs inside the agent's preset composition with\r\n * `commands` injected. The Host reads only the exact agent's effective names\r\n * for collision checks; Claude catalog entries are never registered there.\r\n */\r\nexport interface ClaudeAgentCommandService {\r\n list(agent: unknown): readonly Pick<CommandDescriptor, 'name'>[]\r\n}\r\n\r\n/**\r\n * Cordis service name the preset route provides (behind an entry-local\r\n * isolate realm, so each session gets its own instance) and the host reads\r\n * back with dsh-agent-presets' official `serviceForAgent(ctx, agent, name)`\r\n * — the supported cross-scope read for callers that already hold the agent.\r\n */\r\nexport const CLAUDE_COMMANDS_SERVICE = 'claudeCommands'\r\n\r\ndeclare module '@deepseek-ai/cordis' {\r\n interface Context {\r\n claudeCommands?: ClaudeAgentCommandService\r\n }\r\n}\r\n\r\nexport interface ClaudeCommandView {\r\n publicName: string\r\n claudeName: string\r\n description: string\r\n hint?: string\r\n prefixed: boolean\r\n}\r\n\r\nfunction bounded(value: string, maxChars: number): string {\r\n return redactText(value, maxChars)\r\n}\r\n\r\nexport function projectClaudeCommands(\r\n catalog: readonly SlashCommand[],\r\n target: ClaudeCommandTarget,\r\n): readonly ClaudeCommandView[] {\r\n const reservedNames = new Set(target.list().map(command => command.name))\r\n // Client contributions are absent from the Host directory but share the\r\n // same slash menu, so reserve their public names explicitly.\r\n for (const name of CLIENT_CONTRIBUTION_NAMES) reservedNames.add(name)\r\n const assigned = new Set<string>()\r\n const views: ClaudeCommandView[] = []\r\n for (const command of catalog) {\r\n const names = [command.name, ...(command.aliases ?? [])]\r\n const commandBases = new Set<string>()\r\n for (const claudeName of names) {\r\n const base = registryName(claudeName)\r\n if (base === undefined || commandBases.has(base)) continue\r\n commandBases.add(base)\r\n let publicName = base\r\n let prefixed = false\r\n if (reservedNames.has(publicName) || assigned.has(publicName)) {\r\n publicName = `claude-${base}`\r\n prefixed = true\r\n }\r\n if (!COMMAND_NAME.test(publicName) || reservedNames.has(publicName) || assigned.has(publicName)) continue\r\n const description = bounded(command.description || `Claude Code /${command.name}`, MAX_DESCRIPTION_CHARS)\r\n const hint = bounded(command.argumentHint ?? '', MAX_HINT_CHARS)\r\n views.push({\r\n publicName,\r\n claudeName,\r\n description,\r\n ...(hint.length === 0 ? {} : { hint }),\r\n prefixed,\r\n })\r\n assigned.add(publicName)\r\n }\r\n }\r\n return views\r\n}\r\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"}
@@ -16,6 +16,8 @@ const CLAUDE_REPOSITORY_SETUP_PATH = "/plugins/dsh-claude/repository/setup";
16
16
  const CLAUDE_REPOSITORY_ACTION_PATH = "/plugins/dsh-claude/repository/action";
17
17
  const CLAUDE_REVIEW_COMMENT_PATH = "/plugins/dsh-claude/review-comments";
18
18
  const CLAUDE_REPOSITORY_FEEDBACK_PATH = "/plugins/dsh-claude/repository/feedback";
19
+ const CLAUDE_REPOSITORY_STATUS_PATH = "/plugins/dsh-claude/repository/status";
20
+ const CLAUDE_JIRA_PATH = "/plugins/dsh-claude/jira";
19
21
  //#endregion
20
22
  //#region src/events.ts
21
23
  const SECRET_KEY = /(?:^|[_-])(password|passwd|secret|token|api[_-]?key|authorization|credential|private[_-]?key|session[_-]?key|env|environ|environment)(?:$|[_-])/i;
@@ -185,6 +187,6 @@ function latestClaudeSessionBinding(events) {
185
187
  }
186
188
  }
187
189
  //#endregion
188
- export { CLAUDE_UPDATE_PATH as C, CLAUDE_UPDATE_CHECK_PATH as S, TASK_TOOL_NAMES as T, CLAUDE_PROJECTION_PATH as _, latestClaudeTasks as a, CLAUDE_REPOSITORY_SETUP_PATH as b, normalizeTasksEvent as c, CLAUDE_ACTIVITY_EVENT as d, CLAUDE_CODE_PRESET_ID as f, CLAUDE_GLOBAL_SETTINGS_PATH as g, CLAUDE_DOCTOR_PATH as h, latestClaudeSessionBinding as i, redactText as l, CLAUDE_CODE_PROVIDER_IDS as m, currentClaudeActivityCursor as n, normalizeActivity as o, CLAUDE_CODE_PROVIDER as p, latestClaudeContextUsage as r, normalizeContextUsage as s, boundText as t, safeDetail as u, CLAUDE_REPOSITORY_ACTION_PATH as v, SDK_VERSION as w, CLAUDE_REVIEW_COMMENT_PATH as x, CLAUDE_REPOSITORY_FEEDBACK_PATH as y };
190
+ export { CLAUDE_REVIEW_COMMENT_PATH as C, TASK_TOOL_NAMES as D, SDK_VERSION as E, CLAUDE_REPOSITORY_STATUS_PATH as S, CLAUDE_UPDATE_PATH as T, CLAUDE_JIRA_PATH as _, latestClaudeTasks as a, CLAUDE_REPOSITORY_FEEDBACK_PATH as b, normalizeTasksEvent as c, CLAUDE_ACTIVITY_EVENT as d, CLAUDE_CODE_PRESET_ID as f, CLAUDE_GLOBAL_SETTINGS_PATH as g, CLAUDE_DOCTOR_PATH as h, latestClaudeSessionBinding as i, redactText as l, CLAUDE_CODE_PROVIDER_IDS as m, currentClaudeActivityCursor as n, normalizeActivity as o, CLAUDE_CODE_PROVIDER as p, latestClaudeContextUsage as r, normalizeContextUsage as s, boundText as t, safeDetail as u, CLAUDE_PROJECTION_PATH as v, CLAUDE_UPDATE_CHECK_PATH as w, CLAUDE_REPOSITORY_SETUP_PATH as x, CLAUDE_REPOSITORY_ACTION_PATH as y };
189
191
 
190
- //# sourceMappingURL=events-DhrAr5gh.mjs.map
192
+ //# sourceMappingURL=events-BhU0iJUi.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"events-BhU0iJUi.mjs","names":[],"sources":["../src/constants.ts","../src/events.ts"],"sourcesContent":["export const CLAUDE_CODE_PROVIDER = 'claude'\r\nexport const CLAUDE_CODE_PRESET_ID = 'claude'\r\nexport const CLAUDE_CODE_PROVIDER_IDS = [CLAUDE_CODE_PROVIDER] as const\r\nexport const CLAUDE_SESSION_BOUND_EVENT = 'claude-code/session-bound'\r\nexport const CLAUDE_ACTIVITY_EVENT = 'claude-code/activity'\r\nexport const CLAUDE_CONTEXT_USAGE_EVENT = 'claude-code/context-usage'\r\nexport const CLAUDE_TASKS_EVENT = 'claude-code/tasks'\r\n/** Claude's subagent dispatch tools; rendered as plugin-owned group cards\r\n * gathering subagent activity instead of native tool cards. */\r\nexport const TASK_TOOL_NAMES: ReadonlySet<string> = new Set(['Task', 'Agent'])\r\nexport const SDK_VERSION = '0.3.233'\r\nexport const CLAUDE_DOCTOR_PATH = '/plugins/dsh-claude/doctor'\r\nexport const CLAUDE_UPDATE_CHECK_PATH = '/plugins/dsh-claude/update/check'\r\nexport const CLAUDE_UPDATE_PATH = '/plugins/dsh-claude/update'\r\nexport const CLAUDE_PROJECTION_PATH = '/plugins/dsh-claude/projection'\r\nexport const CLAUDE_GLOBAL_SETTINGS_PATH = '/plugins/dsh-claude/settings/global'\r\nexport const CLAUDE_REPOSITORY_SETUP_PATH = '/plugins/dsh-claude/repository/setup'\r\nexport const CLAUDE_REPOSITORY_ACTION_PATH = '/plugins/dsh-claude/repository/action'\r\nexport const CLAUDE_REVIEW_COMMENT_PATH = '/plugins/dsh-claude/review-comments'\r\nexport const CLAUDE_REPOSITORY_FEEDBACK_PATH = '/plugins/dsh-claude/repository/feedback'\r\nexport const CLAUDE_REPOSITORY_STATUS_PATH = '/plugins/dsh-claude/repository/status'\r\nexport const CLAUDE_JIRA_PATH = '/plugins/dsh-claude/jira'\r\n","import type { SessionEvent } from '@deepseek-ai/dsh-session'\r\nimport {\r\n CLAUDE_ACTIVITY_EVENT,\r\n CLAUDE_CONTEXT_USAGE_EVENT,\r\n CLAUDE_SESSION_BOUND_EVENT,\r\n CLAUDE_TASKS_EVENT,\r\n} from './constants.ts'\r\n\r\nexport type ClaudeActivityKind =\r\n | 'text'\r\n | 'status'\r\n | 'thinking'\r\n | 'tool-call'\r\n | 'tool-result'\r\n | 'permission'\r\n | 'question'\r\n | 'subagent'\r\n | 'usage'\r\n | 'warning'\r\n | 'error'\r\n\r\nexport type ClaudeActivityPhase =\r\n | 'started'\r\n | 'updated'\r\n | 'completed'\r\n | 'denied'\r\n | 'failed'\r\n\r\nexport interface ClaudeUsage {\r\n inputTokens?: number\r\n outputTokens?: number\r\n cacheReadTokens?: number\r\n cacheCreationTokens?: number\r\n cumulativeCostUsd?: number\r\n}\r\n\r\nexport interface ClaudeSessionBoundEvent {\r\n claudeSessionId: string\r\n cliVersion?: string\r\n sdkVersion: string\r\n cwd: string\r\n}\r\n\r\nexport interface ClaudeActivityEvent {\r\n turn: number\r\n step: number\r\n ordinal: number\r\n kind: ClaudeActivityKind\r\n phase?: ClaudeActivityPhase\r\n /** Claude task-board identity for lifecycle activity; never a transcript path. */\r\n taskId?: string\r\n toolUseId?: string\r\n /** Enclosing Claude tool call for subagent-nested activity. */\r\n parentToolUseId?: string\r\n toolName?: string\r\n title?: string\r\n summary?: string\r\n detail?: string\r\n /** Redacted visible Claude prose used by the plugin-owned interleaved transcript. */\r\n text?: string\r\n isError?: boolean\r\n usage?: ClaudeUsage\r\n}\r\n\r\nexport interface ClaudeContextUsageCategory {\r\n name: string\r\n tokens: number\r\n color: string\r\n isDeferred?: boolean\r\n}\r\n\r\nexport interface ClaudeContextUsageEvent {\r\n model: string\r\n totalTokens: number\r\n maxTokens: number\r\n percentage: number\r\n categories: readonly ClaudeContextUsageCategory[]\r\n}\r\n\r\nexport interface ClaudeContextUsageInput {\r\n model: unknown\r\n totalTokens: unknown\r\n maxTokens: unknown\r\n percentage: unknown\r\n categories: readonly {\r\n name?: unknown\r\n tokens?: unknown\r\n color?: unknown\r\n isDeferred?: unknown\r\n }[]\r\n}\r\n\r\ndeclare module '@deepseek-ai/dsh-session/types' {\r\n interface SessionEventMap {\r\n 'claude-code/session-bound': ClaudeSessionBoundEvent\r\n 'claude-code/activity': ClaudeActivityEvent\r\n 'claude-code/context-usage': ClaudeContextUsageEvent\r\n 'claude-code/tasks': ClaudeTasksEvent\r\n }\r\n}\r\n\r\nconst SECRET_KEY = /(?:^|[_-])(password|passwd|secret|token|api[_-]?key|authorization|credential|private[_-]?key|session[_-]?key|env|environ|environment)(?:$|[_-])/i\r\nconst MAX_SUMMARY_CHARS = 1_000\r\nconst MAX_DETAIL_CHARS = 4_000\r\nconst MAX_TRANSCRIPT_TEXT_CHARS = 64_000\r\nconst MAX_DEPTH = 6\r\nconst MAX_ARRAY_ITEMS = 40\r\nconst MAX_OBJECT_KEYS = 60\r\nconst REDACTED = '[REDACTED]'\r\nconst TRUNCATED = '…[truncated]'\r\nconst SECRET_ASSIGNMENT = /((?:password|passwd|secret|token|api[_-]?key|authorization|credential|private[_-]?key|session[_-]?key)\\s*(?:=|:)\\s*)(?:\"[^\"]*\"|'[^']*'|[^\\s,;&]+)/giu\r\nconst BEARER_TOKEN = /(\\bbearer\\s+)[A-Za-z0-9._~+/=-]+/giu\r\nconst PREFIXED_TOKEN = /\\b(?:sk-(?:ant-|proj-)?|xox[baprs]-|ghp_|github_pat_)[A-Za-z0-9_-]{8,}/giu\r\nconst JWT_TOKEN = /\\beyJ[A-Za-z0-9_-]+\\.[A-Za-z0-9_-]+\\.[A-Za-z0-9_-]+\\b/gu\r\nconst URL_USERINFO = /([a-z][a-z0-9+.-]*:\\/\\/[^:\\s/@]+:)[^@\\s/]+@/giu\r\nconst URL_SECRET_PARAM = /([?&](?:password|secret|token|api[_-]?key|access[_-]?token|refresh[_-]?token)=)[^&#\\s]+/giu\r\n\r\nexport function boundText(value: string, maxChars: number): string {\r\n if (value.length <= maxChars) return value\r\n return `${value.slice(0, Math.max(0, maxChars - TRUNCATED.length))}${TRUNCATED}`\r\n}\r\n\r\nexport function redactText(value: string, maxChars = MAX_DETAIL_CHARS): string {\r\n return boundText(\r\n value\r\n .replace(JWT_TOKEN, REDACTED)\r\n .replace(PREFIXED_TOKEN, REDACTED)\r\n .replace(BEARER_TOKEN, `$1${REDACTED}`)\r\n .replace(URL_USERINFO, `$1${REDACTED}@`)\r\n .replace(URL_SECRET_PARAM, `$1${REDACTED}`)\r\n .replace(SECRET_ASSIGNMENT, `$1${REDACTED}`),\r\n maxChars,\r\n )\r\n}\r\n\r\nexport function redactValue(value: unknown, depth = 0, seen = new WeakSet<object>()): unknown {\r\n if (depth > MAX_DEPTH) return '[max-depth]'\r\n if (value === null || typeof value === 'boolean' || typeof value === 'number') return value\r\n if (typeof value === 'string') return redactText(value)\r\n if (typeof value === 'bigint') return value.toString()\r\n if (typeof value === 'undefined') return null\r\n if (typeof value === 'function' || typeof value === 'symbol') return `[${typeof value}]`\r\n if (value instanceof Error) {\r\n return { name: value.name, message: redactText(value.message, MAX_SUMMARY_CHARS) }\r\n }\r\n if (typeof value !== 'object') return String(value)\r\n if (seen.has(value)) return '[circular]'\r\n seen.add(value)\r\n try {\r\n if (Array.isArray(value)) {\r\n const items = value.slice(0, MAX_ARRAY_ITEMS).map(item => redactValue(item, depth + 1, seen))\r\n if (value.length > MAX_ARRAY_ITEMS) items.push(`[${value.length - MAX_ARRAY_ITEMS} more items]`)\r\n return items\r\n }\r\n const result: Record<string, unknown> = {}\r\n const entries = Object.entries(value as Record<string, unknown>)\r\n for (const [key, item] of entries.slice(0, MAX_OBJECT_KEYS)) {\r\n result[key] = SECRET_KEY.test(key) ? REDACTED : redactValue(item, depth + 1, seen)\r\n }\r\n if (entries.length > MAX_OBJECT_KEYS) result.__truncatedKeys = entries.length - MAX_OBJECT_KEYS\r\n return result\r\n } finally {\r\n seen.delete(value)\r\n }\r\n}\r\n\r\nexport function safeDetail(value: unknown): string | undefined {\r\n if (value === undefined) return undefined\r\n const redacted = redactValue(value)\r\n const text = typeof redacted === 'string' ? redacted : JSON.stringify(redacted)\r\n return boundText(text, MAX_DETAIL_CHARS)\r\n}\r\n\r\nexport function normalizeActivity(\r\n activity: Omit<ClaudeActivityEvent, 'summary' | 'detail'> & {\r\n summary?: unknown\r\n detail?: unknown\r\n },\r\n): ClaudeActivityEvent {\r\n const normalized: ClaudeActivityEvent = {\r\n turn: activity.turn,\r\n step: activity.step,\r\n ordinal: activity.ordinal,\r\n kind: activity.kind,\r\n }\r\n if (activity.phase !== undefined) normalized.phase = activity.phase\r\n if (activity.taskId !== undefined) normalized.taskId = redactText(activity.taskId, 128)\r\n if (activity.toolUseId !== undefined) normalized.toolUseId = redactText(activity.toolUseId, 256)\r\n if (activity.parentToolUseId !== undefined) normalized.parentToolUseId = redactText(activity.parentToolUseId, 256)\r\n if (activity.toolName !== undefined) normalized.toolName = redactText(activity.toolName, 256)\r\n if (activity.title !== undefined) normalized.title = redactText(activity.title, MAX_SUMMARY_CHARS)\r\n if (activity.summary !== undefined) {\r\n normalized.summary = redactText(\r\n typeof activity.summary === 'string' ? activity.summary : safeDetail(activity.summary) ?? '',\r\n MAX_SUMMARY_CHARS,\r\n )\r\n }\r\n const detail = safeDetail(activity.detail)\r\n if (detail !== undefined) normalized.detail = detail\r\n if (activity.text !== undefined) normalized.text = redactText(activity.text, MAX_TRANSCRIPT_TEXT_CHARS)\r\n if (activity.isError !== undefined) normalized.isError = activity.isError\r\n if (activity.usage !== undefined) normalized.usage = { ...activity.usage }\r\n return normalized\r\n}\r\n\r\nconst MAX_CONTEXT_CATEGORIES = 24\r\nconst FALLBACK_CONTEXT_COLOR = '#8b95a5'\r\nconst SAFE_CONTEXT_COLOR = /^#[0-9a-f]{3,8}$/iu\r\n\r\nfunction nonNegativeInteger(value: unknown): number {\r\n return typeof value === 'number' && Number.isFinite(value)\r\n ? Math.max(0, Math.floor(value))\r\n : 0\r\n}\r\n\r\nexport function normalizeContextUsage(input: ClaudeContextUsageInput): ClaudeContextUsageEvent {\r\n return {\r\n model: redactText(typeof input.model === 'string' ? input.model : 'unknown', 128),\r\n totalTokens: nonNegativeInteger(input.totalTokens),\r\n maxTokens: nonNegativeInteger(input.maxTokens),\r\n percentage: Math.min(100, nonNegativeInteger(input.percentage)),\r\n categories: input.categories.slice(0, MAX_CONTEXT_CATEGORIES).map(category => ({\r\n name: redactText(typeof category.name === 'string' ? category.name : 'Unknown', 128),\r\n tokens: nonNegativeInteger(category.tokens),\r\n color: typeof category.color === 'string' && SAFE_CONTEXT_COLOR.test(category.color)\r\n ? category.color\r\n : FALLBACK_CONTEXT_COLOR,\r\n ...(category.isDeferred === true ? { isDeferred: true } : {}),\r\n })),\r\n }\r\n}\r\n\r\nexport function latestClaudeContextUsage(\r\n events: readonly SessionEvent[],\r\n): ClaudeContextUsageEvent | undefined {\r\n for (let index = events.length - 1; index >= 0; index -= 1) {\r\n const event = events[index]\r\n if (event?.type === CLAUDE_CONTEXT_USAGE_EVENT) return event.data as ClaudeContextUsageEvent\r\n }\r\n return undefined\r\n}\r\n\r\nexport type ClaudeTaskStatus = 'running' | 'completed' | 'failed' | 'stopped' | 'killed'\r\n\r\nexport interface ClaudeTaskUsage {\r\n totalTokens?: number\r\n toolUses?: number\r\n durationMs?: number\r\n}\r\n\r\nexport interface ClaudeTaskInfo {\r\n taskId: string\r\n description: string\r\n status: ClaudeTaskStatus\r\n /** DSH turn during which this task was first observed, when known. */\r\n originTurn?: number\r\n subagentType?: string\r\n taskType?: string\r\n lastToolName?: string\r\n summary?: string\r\n usage?: ClaudeTaskUsage\r\n /** True while the task runs detached (background command/subagent). */\r\n backgrounded?: boolean\r\n}\r\n\r\n/** Level snapshot of one session's Claude task board, REPLACE semantics. */\r\nexport interface ClaudeTasksEvent {\r\n tasks: readonly ClaudeTaskInfo[]\r\n}\r\n\r\nconst MAX_TASKS_PER_SNAPSHOT = 50\r\nconst MAX_TASK_TEXT_CHARS = 300\r\n\r\nconst TASK_STATUSES: ReadonlySet<string> = new Set(['running', 'completed', 'failed', 'stopped', 'killed'])\r\n\r\nfunction normalizeTaskUsage(input: ClaudeTaskUsage | undefined): ClaudeTaskUsage | undefined {\r\n if (input === undefined) return undefined\r\n const usage: ClaudeTaskUsage = {}\r\n if (input.totalTokens !== undefined) usage.totalTokens = nonNegativeInteger(input.totalTokens)\r\n if (input.toolUses !== undefined) usage.toolUses = nonNegativeInteger(input.toolUses)\r\n if (input.durationMs !== undefined) usage.durationMs = nonNegativeInteger(input.durationMs)\r\n return Object.keys(usage).length === 0 ? undefined : usage\r\n}\r\n\r\nexport function normalizeTasksEvent(tasks: readonly ClaudeTaskInfo[]): ClaudeTasksEvent {\r\n return {\r\n tasks: tasks.slice(0, MAX_TASKS_PER_SNAPSHOT).map(task => {\r\n const usage = normalizeTaskUsage(task.usage)\r\n return {\r\n taskId: redactText(String(task.taskId), 128),\r\n description: redactText(String(task.description), MAX_TASK_TEXT_CHARS),\r\n status: TASK_STATUSES.has(task.status) ? task.status : 'running',\r\n ...(task.originTurn === undefined ? {} : { originTurn: nonNegativeInteger(task.originTurn) }),\r\n ...(task.subagentType === undefined ? {} : { subagentType: redactText(task.subagentType, 64) }),\r\n ...(task.taskType === undefined ? {} : { taskType: redactText(task.taskType, 64) }),\r\n ...(task.lastToolName === undefined ? {} : { lastToolName: redactText(task.lastToolName, 64) }),\r\n ...(task.summary === undefined ? {} : { summary: redactText(task.summary, MAX_TASK_TEXT_CHARS) }),\r\n ...(usage === undefined ? {} : { usage }),\r\n ...(task.backgrounded === true ? { backgrounded: true } : {}),\r\n }\r\n }),\r\n }\r\n}\r\n\r\nexport function latestClaudeTasks(\r\n events: readonly SessionEvent[],\r\n): ClaudeTasksEvent | undefined {\r\n for (let index = events.length - 1; index >= 0; index -= 1) {\r\n const event = events[index]\r\n if (event?.type === CLAUDE_TASKS_EVENT) return event.data as ClaudeTasksEvent\r\n }\r\n return undefined\r\n}\r\n\r\nexport type ClaudeActivityInput = Omit<\r\n ClaudeActivityEvent,\r\n 'turn' | 'step' | 'ordinal' | 'summary' | 'detail'\r\n> & {\r\n summary?: unknown\r\n detail?: unknown\r\n}\r\n\r\nexport interface ClaudeActivityCursor {\r\n turn: number\r\n step: number\r\n nextOrdinal: number\r\n}\r\n\r\n/** Derive the current DSH turn/step; activity ordinals are completed from the sidecar. */\r\nexport function currentClaudeActivityCursor(events: readonly SessionEvent[]): ClaudeActivityCursor {\r\n let turn = 0\r\n let step = 0\r\n for (const event of events) {\r\n if (event.type !== 'step/start') continue\r\n const data = event.data as { turn: number; step: number }\r\n turn = data.turn\r\n step = data.step\r\n }\r\n if (turn < 1 || step < 1) {\r\n throw new Error('dsh-claude: Claude activity requires an open DSH step')\r\n }\r\n return { turn, step, nextOrdinal: 0 }\r\n}\r\n\r\nexport function latestClaudeSessionBinding(\r\n events: readonly SessionEvent[],\r\n): ClaudeSessionBoundEvent | undefined {\r\n for (let index = events.length - 1; index >= 0; index -= 1) {\r\n const event = events[index]\r\n if (event?.type === CLAUDE_SESSION_BOUND_EVENT) {\r\n return event.data as ClaudeSessionBoundEvent\r\n }\r\n }\r\n return undefined\r\n}\r\n"],"mappings":"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"}