@norman-else/dsh-claude 0.1.6 → 0.1.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -9,8 +9,8 @@ Run the local Claude Code CLI as a first-class main conversation inside DeepSeek
9
9
  - A `Claude` Agent Preset in the normal new-session preset picker.
10
10
  - A `claude` DSH model provider with Claude Code's `default`, `opus[1m]`, `fable`, `sonnet`, and `haiku` choices.
11
11
  - Long-lived Claude processes with per-session serialization, idle eviction, cancellation, and persisted Claude session resume.
12
- - DSH approval prompts for Claude tool permission requests.
13
- - Durable, redacted plugin-sidecar activity for thinking summaries, tool calls/results, permissions, subagents, status, usage, and errors.
12
+ - DSH approval prompts for Claude tool permission requests and native DSH question forms for Claude clarifications.
13
+ - Durable, redacted plugin-sidecar activity for thinking summaries, tool calls/results, permissions, question lifecycle, subagents, status, usage, and errors.
14
14
  - Native turn-tail activity cards and a Settings → Claude Code Doctor panel.
15
15
  - A safe CLI for Doctor and managed preset install/remove.
16
16
 
@@ -41,7 +41,7 @@ The package ships a read-only system preset and, during Host activation, install
41
41
  2. Choose **Claude** in the Agent Preset picker.
42
42
  3. Choose **Default (recommended)**, **Opus (1M context)**, **Fable**, **Sonnet**, or **Haiku**.
43
43
  4. Send a normal text prompt.
44
- 5. Answer Claude tool permissions through the existing DSH approval UI.
44
+ 5. Answer Claude tool permissions through the existing DSH approval UI and Claude clarifying questions through DSH's native question form.
45
45
  6. Expand **Claude Code activity** beneath a completed assistant turn to inspect the redacted execution trail.
46
46
 
47
47
  Native DSH presets remain available and keep the native DSH agent loop.
@@ -60,6 +60,7 @@ Every Claude permission callback is bridged to `ctx.approval.request(...)`:
60
60
  - reject, cancel, missing answerer, and audit failure all deny the action.
61
61
  - DSH `never` approval policy therefore fails closed.
62
62
  - DSH access modes map to Claude permission modes: `read-only` → `plan`, `workspace-write` → `acceptEdits`, and explicitly acknowledged `danger-full-access` → `bypassPermissions`.
63
+ - Claude `AskUserQuestion` is always routed to `ctx.userQuestions`, including under Full access; cancellation or an unavailable question surface denies the interaction, and answer content is not copied into the sidecar.
63
64
 
64
65
  This is a permission-policy bridge, not kernel-level workspace confinement. DSH `0.1.0-rc.5` exposes one writable sandbox root, while full Claude compatibility also requires writable `~/.claude`. The plugin therefore does **not** claim that paths outside the workspace are technically unwritable. It still uses DSH managed subprocess ownership for explicit argv, credential-shaped environment scrubbing, cancellation, and whole-process-tree termination.
65
66
 
@@ -1 +1 @@
1
- {"version":3,"file":"events-DP0Ov_Re.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_PROJECTION_PATH = '/plugins/dsh-claude/projection'\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 | 'status'\r\n | 'thinking'\r\n | 'tool-call'\r\n | 'tool-result'\r\n | 'permission'\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 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_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.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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"}
1
+ {"version":3,"file":"events-DP0Ov_Re.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_PROJECTION_PATH = '/plugins/dsh-claude/projection'\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 | '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 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_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.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":";AAAA,MAAa,uBAAuB;AACpC,MAAa,wBAAwB;AACrC,MAAa,2BAA2B,CAAC,oBAAoB;AAE7D,MAAa,wBAAwB;;;AAKrC,MAAa,kCAAuC,IAAI,IAAI,CAAC,QAAQ,OAAO,CAAC;AAC7E,MAAa,cAAc;AAC3B,MAAa,qBAAqB;AAClC,MAAa,yBAAyB;;;ACsFtC,MAAM,aAAa;AACnB,MAAM,oBAAoB;AAC1B,MAAM,mBAAmB;AACzB,MAAM,YAAY;AAClB,MAAM,kBAAkB;AACxB,MAAM,kBAAkB;AACxB,MAAM,WAAW;AACjB,MAAM,YAAY;AAClB,MAAM,oBAAoB;AAC1B,MAAM,eAAe;AACrB,MAAM,iBAAiB;AACvB,MAAM,YAAY;AAClB,MAAM,eAAe;AACrB,MAAM,mBAAmB;AAEzB,SAAgB,UAAU,OAAe,UAA0B;CACjE,IAAI,MAAM,UAAU,UAAU,OAAO;CACrC,OAAO,GAAG,MAAM,MAAM,GAAG,KAAK,IAAI,GAAG,WAAW,EAAgB,CAAC,IAAI;AACvE;AAEA,SAAgB,WAAW,OAAe,WAAW,kBAA0B;CAC7E,OAAO,UACL,MACG,QAAQ,WAAW,QAAQ,CAAC,CAC5B,QAAQ,gBAAgB,QAAQ,CAAC,CACjC,QAAQ,cAAc,KAAK,UAAU,CAAC,CACtC,QAAQ,cAAc,KAAK,SAAS,EAAE,CAAC,CACvC,QAAQ,kBAAkB,KAAK,UAAU,CAAC,CAC1C,QAAQ,mBAAmB,KAAK,UAAU,GAC7C,QACF;AACF;AAEA,SAAgB,YAAY,OAAgB,QAAQ,GAAG,uBAAO,IAAI,QAAgB,GAAY;CAC5F,IAAI,QAAQ,WAAW,OAAO;CAC9B,IAAI,UAAU,QAAQ,OAAO,UAAU,aAAa,OAAO,UAAU,UAAU,OAAO;CACtF,IAAI,OAAO,UAAU,UAAU,OAAO,WAAW,KAAK;CACtD,IAAI,OAAO,UAAU,UAAU,OAAO,MAAM,SAAS;CACrD,IAAI,OAAO,UAAU,aAAa,OAAO;CACzC,IAAI,OAAO,UAAU,cAAc,OAAO,UAAU,UAAU,OAAO,IAAI,OAAO,MAAM;CACtF,IAAI,iBAAiB,OACnB,OAAO;EAAE,MAAM,MAAM;EAAM,SAAS,WAAW,MAAM,SAAS,iBAAiB;CAAE;CAEnF,IAAI,OAAO,UAAU,UAAU,OAAO,OAAO,KAAK;CAClD,IAAI,KAAK,IAAI,KAAK,GAAG,OAAO;CAC5B,KAAK,IAAI,KAAK;CACd,IAAI;EACF,IAAI,MAAM,QAAQ,KAAK,GAAG;GACxB,MAAM,QAAQ,MAAM,MAAM,GAAG,eAAe,CAAC,CAAC,KAAI,SAAQ,YAAY,MAAM,QAAQ,GAAG,IAAI,CAAC;GAC5F,IAAI,MAAM,SAAS,iBAAiB,MAAM,KAAK,IAAI,MAAM,SAAS,gBAAgB,aAAa;GAC/F,OAAO;EACT;EACA,MAAM,SAAkC,CAAC;EACzC,MAAM,UAAU,OAAO,QAAQ,KAAgC;EAC/D,KAAK,MAAM,CAAC,KAAK,SAAS,QAAQ,MAAM,GAAG,eAAe,GACxD,OAAO,OAAO,WAAW,KAAK,GAAG,IAAI,WAAW,YAAY,MAAM,QAAQ,GAAG,IAAI;EAEnF,IAAI,QAAQ,SAAS,iBAAiB,OAAO,kBAAkB,QAAQ,SAAS;EAChF,OAAO;CACT,UAAU;EACR,KAAK,OAAO,KAAK;CACnB;AACF;AAEA,SAAgB,WAAW,OAAoC;CAC7D,IAAI,UAAU,KAAA,GAAW,OAAO,KAAA;CAChC,MAAM,WAAW,YAAY,KAAK;CAElC,OAAO,UADM,OAAO,aAAa,WAAW,WAAW,KAAK,UAAU,QAAQ,GACvD,gBAAgB;AACzC;AAEA,SAAgB,kBACd,UAIqB;CACrB,MAAM,aAAkC;EACtC,MAAM,SAAS;EACf,MAAM,SAAS;EACf,SAAS,SAAS;EAClB,MAAM,SAAS;CACjB;CACA,IAAI,SAAS,UAAU,KAAA,GAAW,WAAW,QAAQ,SAAS;CAC9D,IAAI,SAAS,WAAW,KAAA,GAAW,WAAW,SAAS,WAAW,SAAS,QAAQ,GAAG;CACtF,IAAI,SAAS,cAAc,KAAA,GAAW,WAAW,YAAY,WAAW,SAAS,WAAW,GAAG;CAC/F,IAAI,SAAS,oBAAoB,KAAA,GAAW,WAAW,kBAAkB,WAAW,SAAS,iBAAiB,GAAG;CACjH,IAAI,SAAS,aAAa,KAAA,GAAW,WAAW,WAAW,WAAW,SAAS,UAAU,GAAG;CAC5F,IAAI,SAAS,UAAU,KAAA,GAAW,WAAW,QAAQ,WAAW,SAAS,OAAO,iBAAiB;CACjG,IAAI,SAAS,YAAY,KAAA,GACvB,WAAW,UAAU,WACnB,OAAO,SAAS,YAAY,WAAW,SAAS,UAAU,WAAW,SAAS,OAAO,KAAK,IAC1F,iBACF;CAEF,MAAM,SAAS,WAAW,SAAS,MAAM;CACzC,IAAI,WAAW,KAAA,GAAW,WAAW,SAAS;CAC9C,IAAI,SAAS,YAAY,KAAA,GAAW,WAAW,UAAU,SAAS;CAClE,IAAI,SAAS,UAAU,KAAA,GAAW,WAAW,QAAQ,EAAE,GAAG,SAAS,MAAM;CACzE,OAAO;AACT;AAEA,MAAM,yBAAyB;AAC/B,MAAM,yBAAyB;AAC/B,MAAM,qBAAqB;AAE3B,SAAS,mBAAmB,OAAwB;CAClD,OAAO,OAAO,UAAU,YAAY,OAAO,SAAS,KAAK,IACrD,KAAK,IAAI,GAAG,KAAK,MAAM,KAAK,CAAC,IAC7B;AACN;AAEA,SAAgB,sBAAsB,OAAyD;CAC7F,OAAO;EACL,OAAO,WAAW,OAAO,MAAM,UAAU,WAAW,MAAM,QAAQ,WAAW,GAAG;EAChF,aAAa,mBAAmB,MAAM,WAAW;EACjD,WAAW,mBAAmB,MAAM,SAAS;EAC7C,YAAY,KAAK,IAAI,KAAK,mBAAmB,MAAM,UAAU,CAAC;EAC9D,YAAY,MAAM,WAAW,MAAM,GAAG,sBAAsB,CAAC,CAAC,KAAI,cAAa;GAC7E,MAAM,WAAW,OAAO,SAAS,SAAS,WAAW,SAAS,OAAO,WAAW,GAAG;GACnF,QAAQ,mBAAmB,SAAS,MAAM;GAC1C,OAAO,OAAO,SAAS,UAAU,YAAY,mBAAmB,KAAK,SAAS,KAAK,IAC/E,SAAS,QACT;GACJ,GAAI,SAAS,eAAe,OAAO,EAAE,YAAY,KAAK,IAAI,CAAC;EAC7D,EAAE;CACJ;AACF;AAEA,SAAgB,yBACd,QACqC;CACrC,KAAK,IAAI,QAAQ,OAAO,SAAS,GAAG,SAAS,GAAG,SAAS,GAAG;EAC1D,MAAM,QAAQ,OAAO;EACrB,IAAI,OAAO,SAAA,6BAAqC,OAAO,MAAM;CAC/D;AAEF;AA8BA,MAAM,yBAAyB;AAC/B,MAAM,sBAAsB;AAE5B,MAAM,gCAAqC,IAAI,IAAI;CAAC;CAAW;CAAa;CAAU;CAAW;AAAQ,CAAC;AAE1G,SAAS,mBAAmB,OAAiE;CAC3F,IAAI,UAAU,KAAA,GAAW,OAAO,KAAA;CAChC,MAAM,QAAyB,CAAC;CAChC,IAAI,MAAM,gBAAgB,KAAA,GAAW,MAAM,cAAc,mBAAmB,MAAM,WAAW;CAC7F,IAAI,MAAM,aAAa,KAAA,GAAW,MAAM,WAAW,mBAAmB,MAAM,QAAQ;CACpF,IAAI,MAAM,eAAe,KAAA,GAAW,MAAM,aAAa,mBAAmB,MAAM,UAAU;CAC1F,OAAO,OAAO,KAAK,KAAK,CAAC,CAAC,WAAW,IAAI,KAAA,IAAY;AACvD;AAEA,SAAgB,oBAAoB,OAAoD;CACtF,OAAO,EACL,OAAO,MAAM,MAAM,GAAG,sBAAsB,CAAC,CAAC,KAAI,SAAQ;EACxD,MAAM,QAAQ,mBAAmB,KAAK,KAAK;EAC3C,OAAO;GACL,QAAQ,WAAW,OAAO,KAAK,MAAM,GAAG,GAAG;GAC3C,aAAa,WAAW,OAAO,KAAK,WAAW,GAAG,mBAAmB;GACrE,QAAQ,cAAc,IAAI,KAAK,MAAM,IAAI,KAAK,SAAS;GACvD,GAAI,KAAK,eAAe,KAAA,IAAY,CAAC,IAAI,EAAE,YAAY,mBAAmB,KAAK,UAAU,EAAE;GAC3F,GAAI,KAAK,iBAAiB,KAAA,IAAY,CAAC,IAAI,EAAE,cAAc,WAAW,KAAK,cAAc,EAAE,EAAE;GAC7F,GAAI,KAAK,aAAa,KAAA,IAAY,CAAC,IAAI,EAAE,UAAU,WAAW,KAAK,UAAU,EAAE,EAAE;GACjF,GAAI,KAAK,iBAAiB,KAAA,IAAY,CAAC,IAAI,EAAE,cAAc,WAAW,KAAK,cAAc,EAAE,EAAE;GAC7F,GAAI,KAAK,YAAY,KAAA,IAAY,CAAC,IAAI,EAAE,SAAS,WAAW,KAAK,SAAS,mBAAmB,EAAE;GAC/F,GAAI,UAAU,KAAA,IAAY,CAAC,IAAI,EAAE,MAAM;GACvC,GAAI,KAAK,iBAAiB,OAAO,EAAE,cAAc,KAAK,IAAI,CAAC;EAC7D;CACF,CAAC,EACH;AACF;AAEA,SAAgB,kBACd,QAC8B;CAC9B,KAAK,IAAI,QAAQ,OAAO,SAAS,GAAG,SAAS,GAAG,SAAS,GAAG;EAC1D,MAAM,QAAQ,OAAO;EACrB,IAAI,OAAO,SAAA,qBAA6B,OAAO,MAAM;CACvD;AAEF;;AAiBA,SAAgB,4BAA4B,QAAuD;CACjG,IAAI,OAAO;CACX,IAAI,OAAO;CACX,KAAK,MAAM,SAAS,QAAQ;EAC1B,IAAI,MAAM,SAAS,cAAc;EACjC,MAAM,OAAO,MAAM;EACnB,OAAO,KAAK;EACZ,OAAO,KAAK;CACd;CACA,IAAI,OAAO,KAAK,OAAO,GACrB,MAAM,IAAI,MAAM,uDAAuD;CAEzE,OAAO;EAAE;EAAM;EAAM,aAAa;CAAE;AACtC;AAEA,SAAgB,2BACd,QACqC;CACrC,KAAK,IAAI,QAAQ,OAAO,SAAS,GAAG,SAAS,GAAG,SAAS,GAAG;EAC1D,MAAM,QAAQ,OAAO;EACrB,IAAI,OAAO,SAAA,6BACT,OAAO,MAAM;CAEjB;AAEF"}
package/lib/index.d.mts CHANGED
@@ -6,6 +6,7 @@ import { Agent } from "@deepseek-ai/dsh-agent";
6
6
  import { CommandDefinition, CommandDescriptor } from "@deepseek-ai/dsh-commands";
7
7
  import { SessionEvent } from "@deepseek-ai/dsh-session";
8
8
  import { ApprovalService } from "@deepseek-ai/dsh-user-approval";
9
+ import { UserQuestionService } from "@deepseek-ai/dsh-user-questions";
9
10
  //#region src/command-bridge.d.ts
10
11
  /**
11
12
  * Agent-scope command service contract. The commands service is unreachable
@@ -26,7 +27,7 @@ declare module '@deepseek-ai/cordis' {
26
27
  }
27
28
  //#endregion
28
29
  //#region src/events.d.ts
29
- type ClaudeActivityKind = 'status' | 'thinking' | 'tool-call' | 'tool-result' | 'permission' | 'subagent' | 'usage' | 'warning' | 'error';
30
+ type ClaudeActivityKind = 'status' | 'thinking' | 'tool-call' | 'tool-result' | 'permission' | 'question' | 'subagent' | 'usage' | 'warning' | 'error';
30
31
  type ClaudeActivityPhase = 'started' | 'updated' | 'completed' | 'denied' | 'failed';
31
32
  interface ClaudeUsage {
32
33
  inputTokens?: number;
@@ -199,6 +200,7 @@ declare class ClaudeSupervisor {
199
200
  constructor(dependencies: {
200
201
  runtime: Pick<SubprocessRuntime, 'spawn'>;
201
202
  approval: Pick<ApprovalService, 'request'>;
203
+ userQuestions: Pick<UserQuestionService, 'ask'>;
202
204
  config: ClaudeSupervisorConfig;
203
205
  queryFactory?: ClaudeQueryFactory;
204
206
  runDetached?: <T>(operation: () => T) => T;
package/lib/index.mjs CHANGED
@@ -45,6 +45,7 @@ const ACTIVITY_KINDS = /* @__PURE__ */ new Set([
45
45
  "tool-call",
46
46
  "tool-result",
47
47
  "permission",
48
+ "question",
48
49
  "subagent",
49
50
  "usage",
50
51
  "warning",
@@ -336,8 +337,14 @@ function mapApprovalOutcome(outcome, input, toolUseID) {
336
337
  decisionClassification: "user_reject"
337
338
  };
338
339
  }
339
- function createPermissionBridge(approval, activeContext) {
340
+ function createPermissionBridge(approval, activeContext, userQuestion) {
340
341
  return async (toolName, input, options) => {
342
+ if (toolName === "AskUserQuestion") return userQuestion === void 0 ? {
343
+ behavior: "deny",
344
+ message: "DeepSeek Harness user questions are unavailable; the question was cancelled.",
345
+ toolUseID: options.toolUseID,
346
+ decisionClassification: "user_reject"
347
+ } : userQuestion(input, options);
341
348
  const active = activeContext();
342
349
  if (active === void 0) return {
343
350
  behavior: "deny",
@@ -401,6 +408,120 @@ function createPermissionBridge(approval, activeContext) {
401
408
  };
402
409
  }
403
410
  //#endregion
411
+ //#region src/user-question.ts
412
+ const FAILURE_MESSAGE = "DeepSeek Harness could not collect an answer; the question was cancelled.";
413
+ const INVALID_MESSAGE = "Claude Code sent an invalid user-question request; the question was cancelled.";
414
+ function failureMessage(error) {
415
+ const code = error !== null && typeof error === "object" && "code" in error ? error.code : void 0;
416
+ return typeof code === "string" && /^[A-Z][A-Z0-9_]*$/.test(code) ? `${FAILURE_MESSAGE} (${code})` : FAILURE_MESSAGE;
417
+ }
418
+ function deny(message, toolUseID) {
419
+ return {
420
+ behavior: "deny",
421
+ message,
422
+ toolUseID,
423
+ decisionClassification: "user_reject"
424
+ };
425
+ }
426
+ function optionalText(value) {
427
+ return typeof value === "string" && value.length > 0 ? value : void 0;
428
+ }
429
+ function parseQuestions(input, toolUseID) {
430
+ if (!Array.isArray(input.questions) || input.questions.length === 0 || input.questions.length > 20) return void 0;
431
+ const seen = /* @__PURE__ */ new Set();
432
+ const questions = [];
433
+ for (const [index, value] of input.questions.entries()) {
434
+ if (value === null || typeof value !== "object" || Array.isArray(value)) return void 0;
435
+ const item = value;
436
+ if (typeof item.question !== "string" || item.question.length === 0 || seen.has(item.question)) return void 0;
437
+ seen.add(item.question);
438
+ if (item.options !== void 0 && !Array.isArray(item.options)) return void 0;
439
+ const options = (item.options ?? []).map((option) => {
440
+ if (option === null || typeof option !== "object" || Array.isArray(option)) return void 0;
441
+ const record = option;
442
+ if (typeof record.label !== "string" || record.label.length === 0) return void 0;
443
+ const description = optionalText(record.description);
444
+ return {
445
+ label: record.label,
446
+ ...description === void 0 ? {} : { description }
447
+ };
448
+ });
449
+ if (options.some((option) => option === void 0)) return void 0;
450
+ const header = optionalText(item.header);
451
+ questions.push({
452
+ id: `${toolUseID}:${index}`,
453
+ question: item.question,
454
+ ...header === void 0 ? {} : { header },
455
+ options,
456
+ multiSelect: item.multiSelect === true
457
+ });
458
+ }
459
+ return questions;
460
+ }
461
+ function answerText(answer, multiSelect) {
462
+ if (answer === void 0) return "";
463
+ const custom = optionalText(answer.custom);
464
+ if (!multiSelect && custom !== void 0) return custom;
465
+ return [...answer.selected, ...custom === void 0 ? [] : [custom]].join(", ");
466
+ }
467
+ function createUserQuestionBridge(userQuestions, activeContext) {
468
+ return async (input, options) => {
469
+ const active = activeContext();
470
+ if (active === void 0) return deny("No active DeepSeek Harness turn owns this Claude Code question.", options.toolUseID);
471
+ const questions = parseQuestions(input, options.toolUseID);
472
+ if (questions === void 0) return deny(INVALID_MESSAGE, options.toolUseID);
473
+ active.markActivity?.();
474
+ try {
475
+ await active.appendActivity({
476
+ kind: "question",
477
+ phase: "started",
478
+ toolUseId: options.toolUseID,
479
+ toolName: "AskUserQuestion",
480
+ title: "Claude asked a question",
481
+ summary: questions.length === 1 ? questions[0].question : `Claude asked ${questions.length} questions`
482
+ });
483
+ const response = await userQuestions.ask({
484
+ questions,
485
+ agent: active.agent,
486
+ signal: options.signal
487
+ });
488
+ const answersById = new Map(response.answers.map((answer) => [answer.id, answer]));
489
+ const answers = Object.fromEntries(questions.map((question) => [question.question, answerText(answersById.get(question.id), question.multiSelect === true)]));
490
+ await active.appendActivity({
491
+ kind: "question",
492
+ phase: "completed",
493
+ toolUseId: options.toolUseID,
494
+ toolName: "AskUserQuestion",
495
+ title: "Claude asked a question",
496
+ summary: "Answered in DeepSeek Harness"
497
+ });
498
+ return {
499
+ behavior: "allow",
500
+ updatedInput: {
501
+ ...input,
502
+ answers
503
+ },
504
+ toolUseID: options.toolUseID,
505
+ decisionClassification: "user_temporary"
506
+ };
507
+ } catch (error) {
508
+ const message = failureMessage(error);
509
+ try {
510
+ await active.appendActivity({
511
+ kind: "question",
512
+ phase: "failed",
513
+ toolUseId: options.toolUseID,
514
+ toolName: "AskUserQuestion",
515
+ title: "Claude asked a question",
516
+ summary: message,
517
+ isError: true
518
+ });
519
+ } catch {}
520
+ return deny(message, options.toolUseID);
521
+ }
522
+ };
523
+ }
524
+ //#endregion
404
525
  //#region src/sdk-messages.ts
405
526
  function record(value) {
406
527
  return value !== null && typeof value === "object" ? value : void 0;
@@ -908,6 +1029,7 @@ var ClaudeSupervisor = class {
908
1029
  #entries = /* @__PURE__ */ new Map();
909
1030
  #runtime;
910
1031
  #approval;
1032
+ #userQuestions;
911
1033
  #config;
912
1034
  #queryFactory;
913
1035
  #runDetached;
@@ -919,6 +1041,7 @@ var ClaudeSupervisor = class {
919
1041
  constructor(dependencies) {
920
1042
  this.#runtime = dependencies.runtime;
921
1043
  this.#approval = dependencies.approval;
1044
+ this.#userQuestions = dependencies.userQuestions;
922
1045
  this.#config = dependencies.config;
923
1046
  this.#queryFactory = dependencies.queryFactory ?? ((params) => query(params));
924
1047
  this.#runDetached = dependencies.runDetached ?? ((operation) => operation());
@@ -1154,7 +1277,7 @@ var ClaudeSupervisor = class {
1154
1277
  taskSnapshotAt: 0,
1155
1278
  taskSnapshotTimer: void 0
1156
1279
  };
1157
- const canUseTool = createPermissionBridge(this.#approval, () => {
1280
+ const activeInteraction = () => {
1158
1281
  const active = entry.active;
1159
1282
  return active === void 0 ? void 0 : {
1160
1283
  agent: active.agent,
@@ -1171,7 +1294,9 @@ var ClaudeSupervisor = class {
1171
1294
  },
1172
1295
  appendActivity: (activity) => this.#appendActivity(active, activity)
1173
1296
  };
1174
- });
1297
+ };
1298
+ const userQuestion = createUserQuestionBridge(this.#userQuestions, activeInteraction);
1299
+ const canUseTool = createPermissionBridge(this.#approval, activeInteraction, userQuestion);
1175
1300
  const options = {
1176
1301
  pathToClaudeCodeExecutable: this.#config.executablePath,
1177
1302
  cwd,
@@ -2081,7 +2206,8 @@ const inject = [
2081
2206
  "agentPresets",
2082
2207
  "commands",
2083
2208
  "subprocess",
2084
- "approval"
2209
+ "approval",
2210
+ "userQuestions"
2085
2211
  ];
2086
2212
  const Config = z.object({
2087
2213
  executablePath: z.string().default(""),
@@ -2213,6 +2339,7 @@ async function apply(ctx, config) {
2213
2339
  const supervisor = new ClaudeSupervisor({
2214
2340
  runtime: ctx.subprocess,
2215
2341
  approval: ctx.approval,
2342
+ userQuestions: ctx.userQuestions,
2216
2343
  config: supervisorConfig,
2217
2344
  runDetached: (operation) => ctx.agents.withoutInitiator(operation),
2218
2345
  sidecar