@di-code/coding-agent 0.1.2 → 0.1.4

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.
Files changed (81) hide show
  1. package/README.md +499 -143
  2. package/dist/cli.d.ts +1 -0
  3. package/dist/cli.d.ts.map +1 -1
  4. package/dist/cli.js +15 -0
  5. package/dist/cli.js.map +1 -1
  6. package/dist/core/clipboard-image.d.ts +13 -0
  7. package/dist/core/clipboard-image.d.ts.map +1 -0
  8. package/dist/core/clipboard-image.js +99 -0
  9. package/dist/core/clipboard-image.js.map +1 -0
  10. package/dist/core/context-builder.d.ts.map +1 -1
  11. package/dist/core/context-builder.js +6 -2
  12. package/dist/core/context-builder.js.map +1 -1
  13. package/dist/core/image-input.d.ts +13 -0
  14. package/dist/core/image-input.d.ts.map +1 -0
  15. package/dist/core/image-input.js +147 -0
  16. package/dist/core/image-input.js.map +1 -0
  17. package/dist/core/session/session-storage.d.ts.map +1 -1
  18. package/dist/core/session/session-storage.js +1 -0
  19. package/dist/core/session/session-storage.js.map +1 -1
  20. package/dist/core/session.d.ts +19 -2
  21. package/dist/core/session.d.ts.map +1 -1
  22. package/dist/core/session.js +101 -4
  23. package/dist/core/session.js.map +1 -1
  24. package/dist/core/tools/bash.d.ts +2 -2
  25. package/dist/core/tools/bash.d.ts.map +1 -1
  26. package/dist/core/tools/bash.js.map +1 -1
  27. package/dist/core/tools/edit-diff.d.ts +25 -0
  28. package/dist/core/tools/edit-diff.d.ts.map +1 -0
  29. package/dist/core/tools/edit-diff.js +136 -0
  30. package/dist/core/tools/edit-diff.js.map +1 -0
  31. package/dist/core/tools/edit.d.ts +13 -4
  32. package/dist/core/tools/edit.d.ts.map +1 -1
  33. package/dist/core/tools/edit.js +64 -30
  34. package/dist/core/tools/edit.js.map +1 -1
  35. package/dist/core/tools/read.d.ts +2 -2
  36. package/dist/core/tools/read.d.ts.map +1 -1
  37. package/dist/core/tools/read.js.map +1 -1
  38. package/dist/core/tools/write.d.ts +2 -2
  39. package/dist/core/tools/write.d.ts.map +1 -1
  40. package/dist/core/tools/write.js.map +1 -1
  41. package/dist/entry.js +9 -2
  42. package/dist/entry.js.map +1 -1
  43. package/dist/main.d.ts +3 -1
  44. package/dist/main.d.ts.map +1 -1
  45. package/dist/main.js +42 -8
  46. package/dist/main.js.map +1 -1
  47. package/dist/modes/interactive-components.d.ts +3 -0
  48. package/dist/modes/interactive-components.d.ts.map +1 -1
  49. package/dist/modes/interactive-components.js +76 -25
  50. package/dist/modes/interactive-components.js.map +1 -1
  51. package/dist/modes/interactive-diff.d.ts +8 -0
  52. package/dist/modes/interactive-diff.d.ts.map +1 -0
  53. package/dist/modes/interactive-diff.js +78 -0
  54. package/dist/modes/interactive-diff.js.map +1 -0
  55. package/dist/modes/interactive-layout.d.ts +6 -1
  56. package/dist/modes/interactive-layout.d.ts.map +1 -1
  57. package/dist/modes/interactive-layout.js +21 -1
  58. package/dist/modes/interactive-layout.js.map +1 -1
  59. package/dist/modes/interactive-state.d.ts +17 -2
  60. package/dist/modes/interactive-state.d.ts.map +1 -1
  61. package/dist/modes/interactive-state.js +103 -3
  62. package/dist/modes/interactive-state.js.map +1 -1
  63. package/dist/modes/interactive.d.ts +18 -0
  64. package/dist/modes/interactive.d.ts.map +1 -1
  65. package/dist/modes/interactive.js +289 -24
  66. package/dist/modes/interactive.js.map +1 -1
  67. package/dist/provider-onboarding.d.ts +8 -1
  68. package/dist/provider-onboarding.d.ts.map +1 -1
  69. package/dist/provider-onboarding.js +53 -23
  70. package/dist/provider-onboarding.js.map +1 -1
  71. package/dist/rpc-entry.js +1 -1
  72. package/dist/rpc-entry.js.map +1 -1
  73. package/dist/startup.d.ts +11 -3
  74. package/dist/startup.d.ts.map +1 -1
  75. package/dist/startup.js +240 -84
  76. package/dist/startup.js.map +1 -1
  77. package/dist/utils/syntax-highlight.d.ts +2 -0
  78. package/dist/utils/syntax-highlight.d.ts.map +1 -0
  79. package/dist/utils/syntax-highlight.js +55 -0
  80. package/dist/utils/syntax-highlight.js.map +1 -0
  81. package/package.json +9 -4
@@ -1 +1 @@
1
- {"version":3,"file":"interactive-state.js","sourceRoot":"","sources":["../../src/modes/interactive-state.ts"],"names":[],"mappings":"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","sourcesContent":["import type { Message } from \"@di-code/ai\";\nimport type { AgentSessionEvent, SessionUsage } from \"../core/session.ts\";\n\nexport interface InteractiveMessage {\n\treadonly role: \"user\" | \"assistant\";\n\treadonly text: string;\n}\n\nexport type InteractiveProcessItem =\n\t| { readonly type: \"thinking\"; readonly id: \"thinking\" }\n\t| {\n\t\t\treadonly type: \"tool\";\n\t\t\treadonly id: string;\n\t\t\treadonly command: string;\n\t\t\treadonly status: \"running\" | \"done\" | \"error\";\n\t };\n\nexport interface InteractiveState {\n\treadonly messages: readonly string[];\n\treadonly messageItems: readonly InteractiveMessage[];\n\treadonly processItems: readonly InteractiveProcessItem[];\n\treadonly spinnerFrame: number;\n\treadonly streamingText: string;\n\treadonly toolStatus: readonly string[];\n\treadonly busy: boolean;\n\treadonly error?: string;\n\treadonly queue: readonly string[];\n\treadonly status?: string;\n\treadonly compacting: boolean;\n\treadonly retrying: boolean;\n\treadonly usage: SessionUsage;\n}\n\nfunction textOf(message: Message): string {\n\ttype TextBlock = Extract<Message[\"content\"][number], { type: \"text\" }>;\n\treturn message.content\n\t\t.filter((content): content is TextBlock => content.type === \"text\")\n\t\t.map((content) => content.text)\n\t\t.join(\"\");\n}\n\nexport class InteractiveProjection {\n\tprivate readonly messages: string[] = [];\n\tprivate readonly messageItems: InteractiveMessage[] = [];\n\tprivate readonly processItems: InteractiveProcessItem[] = [];\n\tprivate readonly toolStatus = new Map<string, string>();\n\tprivate spinnerFrame = 0;\n\tprivate streamingText = \"\";\n\tprivate busy = false;\n\tprivate error: string | undefined;\n\tprivate queue: string[] = [];\n\tprivate status: string | undefined;\n\tprivate compacting = false;\n\tprivate retrying = false;\n\tprivate usage: SessionUsage = {\n\t\trequestCount: 0,\n\t\tinputTokens: 0,\n\t\toutputTokens: 0,\n\t\tcacheReadTokens: 0,\n\t\tcacheWriteTokens: 0,\n\t\ttotalTokens: 0,\n\t\tcost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 },\n\t\testimatedContextTokens: 0,\n\t\tcontextWindow: 0,\n\t\treserveTokens: 0,\n\t\ttriggerTokens: 0,\n\t};\n\n\tget state(): InteractiveState {\n\t\treturn {\n\t\t\tmessages: [...this.messages],\n\t\t\tmessageItems: this.messageItems.map((message) => ({ ...message })),\n\t\t\tprocessItems: this.processItems.map((item) => ({ ...item })),\n\t\t\tspinnerFrame: this.spinnerFrame,\n\t\t\tstreamingText: this.streamingText,\n\t\t\ttoolStatus: [...this.toolStatus.values()],\n\t\t\tbusy: this.busy,\n\t\t\terror: this.error,\n\t\t\tqueue: [...this.queue],\n\t\t\tstatus: this.status,\n\t\t\tcompacting: this.compacting,\n\t\t\tretrying: this.retrying,\n\t\t\tusage: { ...this.usage, cost: { ...this.usage.cost } },\n\t\t};\n\t}\n\n\tsetError(message: string | undefined): void {\n\t\tthis.error = message;\n\t}\n\n\tclearVisibleMessages(): void {\n\t\tthis.messages.length = 0;\n\t\tthis.messageItems.length = 0;\n\t\tthis.streamingText = \"\";\n\t\tthis.processItems.length = 0;\n\t\tthis.toolStatus.clear();\n\t\tthis.error = undefined;\n\t}\n\n\tsetQueue(queue: readonly string[]): void {\n\t\tthis.queue = [...queue];\n\t}\n\n\tsetStatus(status: string | undefined): void {\n\t\tthis.status = status;\n\t}\n\n\tsetCompacting(value: boolean): void {\n\t\tthis.compacting = value;\n\t}\n\n\tsetRetrying(value: boolean): void {\n\t\tthis.retrying = value;\n\t}\n\n\tsetUsage(usage: SessionUsage): void {\n\t\tthis.usage = structuredClone(usage);\n\t}\n\n\tadvanceSpinner(): boolean {\n\t\tif (!this.busy) return false;\n\t\tthis.spinnerFrame = (this.spinnerFrame + 1) % 4;\n\t\treturn true;\n\t}\n\n\tclearTransientProcess(): void {\n\t\tthis.processItems.length = 0;\n\t\tthis.toolStatus.clear();\n\t\tthis.spinnerFrame = 0;\n\t}\n\n\treplaceTranscript(messages: readonly Message[]): void {\n\t\tthis.messages.length = 0;\n\t\tthis.messageItems.length = 0;\n\t\tthis.processItems.length = 0;\n\t\tthis.toolStatus.clear();\n\t\tthis.spinnerFrame = 0;\n\t\tthis.streamingText = \"\";\n\t\tthis.busy = false;\n\t\tthis.error = undefined;\n\t\tfor (const message of messages) this.appendCompletedMessage(message);\n\t}\n\n\tapply(event: AgentSessionEvent): void {\n\t\tswitch (event.type) {\n\t\t\tcase \"agent_start\":\n\t\t\t\tthis.busy = true;\n\t\t\t\tthis.error = undefined;\n\t\t\t\tthis.retrying = false;\n\t\t\t\tthis.processItems.length = 0;\n\t\t\t\tthis.processItems.push({ type: \"thinking\", id: \"thinking\" });\n\t\t\t\tthis.toolStatus.clear();\n\t\t\t\tthis.spinnerFrame = 0;\n\t\t\t\treturn;\n\t\t\tcase \"compaction_start\":\n\t\t\t\tthis.compacting = true;\n\t\t\t\treturn;\n\t\t\tcase \"compaction_end\":\n\t\t\t\tthis.compacting = false;\n\t\t\t\tif (!event.success) this.error = event.errorMessage;\n\t\t\t\treturn;\n\t\t\tcase \"usage_update\":\n\t\t\t\tthis.setUsage(event.usage);\n\t\t\t\treturn;\n\t\t\tcase \"message_start\":\n\t\t\t\tif (event.message.role === \"assistant\") this.streamingText = \"\";\n\t\t\t\treturn;\n\t\t\tcase \"message_update\":\n\t\t\t\tif (event.event.type === \"text_delta\") {\n\t\t\t\t\tthis.removeThinking();\n\t\t\t\t\tthis.streamingText += event.event.delta;\n\t\t\t\t}\n\t\t\t\treturn;\n\t\t\tcase \"tool_execution_start\":\n\t\t\t\tthis.removeThinking();\n\t\t\t\tthis.processItems.push({\n\t\t\t\t\ttype: \"tool\",\n\t\t\t\t\tid: event.toolCallId,\n\t\t\t\t\tcommand: formatToolCommand(event.toolName, event.arguments),\n\t\t\t\t\tstatus: \"running\",\n\t\t\t\t});\n\t\t\t\tthis.toolStatus.set(event.toolCallId, `${event.toolName}: running`);\n\t\t\t\treturn;\n\t\t\tcase \"tool_execution_end\":\n\t\t\t\tthis.updateToolStatus(event.toolCallId, event.result.isError ? \"error\" : \"done\");\n\t\t\t\tthis.toolStatus.set(event.toolCallId, `${event.toolName}: ${event.result.isError ? \"error\" : \"done\"}`);\n\t\t\t\treturn;\n\t\t\tcase \"message_end\":\n\t\t\t\tthis.appendCompletedMessage(event.message);\n\t\t\t\treturn;\n\t\t\tcase \"agent_end\":\n\t\t\t\tthis.busy = false;\n\t\t\t\tthis.retrying = false;\n\t\t\t\tthis.processItems.length = 0;\n\t\t\t\tthis.toolStatus.clear();\n\t\t\t\tthis.spinnerFrame = 0;\n\t\t\t\treturn;\n\t\t\tcase \"turn_start\":\n\t\t\t\tif (this.busy && !this.processItems.some((item) => item.type === \"thinking\")) {\n\t\t\t\t\tthis.processItems.push({ type: \"thinking\", id: \"thinking\" });\n\t\t\t\t\tthis.spinnerFrame = 0;\n\t\t\t\t}\n\t\t\t\treturn;\n\t\t\tcase \"turn_end\":\n\t\t\t\treturn;\n\t\t}\n\t}\n\n\tprivate removeThinking(): void {\n\t\tconst index = this.processItems.findIndex((item) => item.type === \"thinking\");\n\t\tif (index >= 0) this.processItems.splice(index, 1);\n\t}\n\n\tprivate updateToolStatus(id: string, status: \"done\" | \"error\"): void {\n\t\tconst index = this.processItems.findIndex((item) => item.type === \"tool\" && item.id === id);\n\t\tconst item = this.processItems[index];\n\t\tif (index < 0 || item?.type !== \"tool\") return;\n\t\tthis.processItems[index] = { ...item, status };\n\t}\n\n\tprivate appendCompletedMessage(message: Message): void {\n\t\tif (message.role === \"tool_result\") return;\n\t\tconst text = textOf(message);\n\t\tif (message.role === \"user\") {\n\t\t\tif (text.length > 0) {\n\t\t\t\tthis.messages.push(text);\n\t\t\t\tthis.messageItems.push({ role: \"user\", text });\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\t\tif (text.length > 0 && message.stopReason !== \"tool_use\") {\n\t\t\tthis.messages.push(text);\n\t\t\tthis.messageItems.push({ role: \"assistant\", text });\n\t\t}\n\t\tif (message.stopReason === \"error\" || message.stopReason === \"aborted\") this.error = message.errorMessage;\n\t\tthis.streamingText = \"\";\n\t}\n}\n\nfunction formatToolCommand(toolName: string, argumentsValue: Record<string, unknown>): string {\n\tconst serialized = JSON.stringify(argumentsValue);\n\treturn serialized === \"{}\" ? toolName : `${toolName} ${serialized}`;\n}\n"]}
1
+ {"version":3,"file":"interactive-state.js","sourceRoot":"","sources":["../../src/modes/interactive-state.ts"],"names":[],"mappings":"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","sourcesContent":["import type { Message } from \"@di-code/ai\";\nimport type { AgentSessionEvent, SessionUsage } from \"../core/session.ts\";\nimport { computeEditsDiff, type Edit } from \"../core/tools/edit-diff.ts\";\n\nexport type InteractiveMessage =\n\t| { readonly role: \"user\" | \"assistant\"; readonly text: string }\n\t| {\n\t\t\treadonly role: \"file_change\";\n\t\t\treadonly id: string;\n\t\t\treadonly path: string;\n\t\t\treadonly kind: \"edit\" | \"write\";\n\t\t\treadonly removed: readonly string[];\n\t\t\treadonly added: readonly string[];\n\t\t\treadonly diff?: string;\n\t\t\treadonly firstChangedLine?: number;\n\t };\n\ntype FileChangeCandidate = Extract<InteractiveMessage, { role: \"file_change\" }>;\n\nexport type InteractiveProcessItem =\n\t| { readonly type: \"thinking\"; readonly id: \"thinking\" }\n\t| {\n\t\t\treadonly type: \"tool\";\n\t\t\treadonly id: string;\n\t\t\treadonly command: string;\n\t\t\treadonly status: \"running\" | \"done\" | \"error\";\n\t };\n\nexport interface InteractiveState {\n\treadonly messages: readonly string[];\n\treadonly messageItems: readonly InteractiveMessage[];\n\treadonly processItems: readonly InteractiveProcessItem[];\n\treadonly spinnerFrame: number;\n\treadonly streamingText: string;\n\treadonly toolStatus: readonly string[];\n\treadonly busy: boolean;\n\treadonly error?: string;\n\treadonly queue: readonly string[];\n\treadonly status?: string;\n\treadonly compacting: boolean;\n\treadonly retrying: boolean;\n\treadonly usage: SessionUsage;\n}\n\nfunction textOf(message: Message): string {\n\ttype TextBlock = Extract<Message[\"content\"][number], { type: \"text\" }>;\n\treturn message.content\n\t\t.filter((content): content is TextBlock => content.type === \"text\")\n\t\t.map((content) => content.text)\n\t\t.join(\"\");\n}\n\nexport class InteractiveProjection {\n\tprivate readonly messages: string[] = [];\n\tprivate readonly messageItems: InteractiveMessage[] = [];\n\tprivate readonly pendingFileChanges = new Map<string, FileChangeCandidate>();\n\tprivate readonly processItems: InteractiveProcessItem[] = [];\n\tprivate readonly toolStatus = new Map<string, string>();\n\tprivate spinnerFrame = 0;\n\tprivate streamingText = \"\";\n\tprivate busy = false;\n\tprivate error: string | undefined;\n\tprivate queue: string[] = [];\n\tprivate status: string | undefined;\n\tprivate compacting = false;\n\tprivate retrying = false;\n\tprivate previewRoot: string | undefined;\n\tprivate previewChange: (() => void) | undefined;\n\tprivate usage: SessionUsage = {\n\t\trequestCount: 0,\n\t\tinputTokens: 0,\n\t\toutputTokens: 0,\n\t\tcacheReadTokens: 0,\n\t\tcacheWriteTokens: 0,\n\t\ttotalTokens: 0,\n\t\tcost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 },\n\t\testimatedContextTokens: 0,\n\t\tcontextWindow: 0,\n\t\treserveTokens: 0,\n\t\ttriggerTokens: 0,\n\t};\n\n\tget state(): InteractiveState {\n\t\tconst visibleMessageItems = [...this.messageItems, ...this.pendingFileChanges.values()];\n\t\treturn {\n\t\t\tmessages: [...this.messages],\n\t\t\tmessageItems: visibleMessageItems.map((message) =>\n\t\t\t\tmessage.role === \"file_change\"\n\t\t\t\t\t? { ...message, removed: [...message.removed], added: [...message.added] }\n\t\t\t\t\t: { ...message },\n\t\t\t),\n\t\t\tprocessItems: this.processItems.map((item) => ({ ...item })),\n\t\t\tspinnerFrame: this.spinnerFrame,\n\t\t\tstreamingText: this.streamingText,\n\t\t\ttoolStatus: [...this.toolStatus.values()],\n\t\t\tbusy: this.busy,\n\t\t\terror: this.error,\n\t\t\tqueue: [...this.queue],\n\t\t\tstatus: this.status,\n\t\t\tcompacting: this.compacting,\n\t\t\tretrying: this.retrying,\n\t\t\tusage: { ...this.usage, cost: { ...this.usage.cost } },\n\t\t};\n\t}\n\n\tsetError(message: string | undefined): void {\n\t\tthis.error = message;\n\t}\n\n\tclearVisibleMessages(): void {\n\t\tthis.messages.length = 0;\n\t\tthis.messageItems.length = 0;\n\t\tthis.pendingFileChanges.clear();\n\t\tthis.streamingText = \"\";\n\t\tthis.processItems.length = 0;\n\t\tthis.toolStatus.clear();\n\t\tthis.error = undefined;\n\t}\n\n\tsetQueue(queue: readonly string[]): void {\n\t\tthis.queue = [...queue];\n\t}\n\n\tsetStatus(status: string | undefined): void {\n\t\tthis.status = status;\n\t}\n\n\tsetCompacting(value: boolean): void {\n\t\tthis.compacting = value;\n\t}\n\n\tsetRetrying(value: boolean): void {\n\t\tthis.retrying = value;\n\t}\n\n\tsetUsage(usage: SessionUsage): void {\n\t\tthis.usage = structuredClone(usage);\n\t}\n\n\tconfigureFilePreview(root: string, onChange: () => void): void {\n\t\tthis.previewRoot = root;\n\t\tthis.previewChange = onChange;\n\t}\n\n\tadvanceSpinner(): boolean {\n\t\tif (!this.busy) return false;\n\t\tthis.spinnerFrame = (this.spinnerFrame + 1) % 4;\n\t\treturn true;\n\t}\n\n\tclearTransientProcess(): void {\n\t\tthis.processItems.length = 0;\n\t\tthis.toolStatus.clear();\n\t\tthis.spinnerFrame = 0;\n\t}\n\n\treplaceTranscript(messages: readonly Message[]): void {\n\t\tthis.messages.length = 0;\n\t\tthis.messageItems.length = 0;\n\t\tthis.pendingFileChanges.clear();\n\t\tthis.processItems.length = 0;\n\t\tthis.toolStatus.clear();\n\t\tthis.spinnerFrame = 0;\n\t\tthis.streamingText = \"\";\n\t\tthis.busy = false;\n\t\tthis.error = undefined;\n\t\tfor (const message of messages) this.appendCompletedMessage(message);\n\t}\n\n\tapply(event: AgentSessionEvent): void {\n\t\tswitch (event.type) {\n\t\t\tcase \"agent_start\":\n\t\t\t\tthis.busy = true;\n\t\t\t\tthis.error = undefined;\n\t\t\t\tthis.retrying = false;\n\t\t\t\tthis.processItems.length = 0;\n\t\t\t\tthis.processItems.push({ type: \"thinking\", id: \"thinking\" });\n\t\t\t\tthis.toolStatus.clear();\n\t\t\t\tthis.spinnerFrame = 0;\n\t\t\t\treturn;\n\t\t\tcase \"compaction_start\":\n\t\t\t\tthis.compacting = true;\n\t\t\t\treturn;\n\t\t\tcase \"compaction_end\":\n\t\t\t\tthis.compacting = false;\n\t\t\t\tif (!event.success) this.error = event.errorMessage;\n\t\t\t\treturn;\n\t\t\tcase \"usage_update\":\n\t\t\t\tthis.setUsage(event.usage);\n\t\t\t\treturn;\n\t\t\tcase \"queue_update\":\n\t\t\t\treturn;\n\t\t\tcase \"message_start\":\n\t\t\t\tif (event.message.role === \"assistant\") this.streamingText = \"\";\n\t\t\t\treturn;\n\t\t\tcase \"message_update\":\n\t\t\t\tif (event.event.type === \"text_delta\") {\n\t\t\t\t\tif (event.event.delta.length > 0) this.removeThinking();\n\t\t\t\t\tthis.streamingText += event.event.delta;\n\t\t\t\t}\n\t\t\t\treturn;\n\t\t\tcase \"tool_execution_start\":\n\t\t\t\tthis.removeThinking();\n\t\t\t\tthis.processItems.push({\n\t\t\t\t\ttype: \"tool\",\n\t\t\t\t\tid: event.toolCallId,\n\t\t\t\t\tcommand: formatToolCommand(event.toolName, event.arguments),\n\t\t\t\t\tstatus: \"running\",\n\t\t\t\t});\n\t\t\t\tthis.toolStatus.set(event.toolCallId, `${event.toolName}: running`);\n\t\t\t\tthis.storeFileChange(event.toolCallId, event.toolName, event.arguments);\n\t\t\t\treturn;\n\t\t\tcase \"tool_execution_end\":\n\t\t\t\tthis.updateToolStatus(event.toolCallId, event.result.isError ? \"error\" : \"done\");\n\t\t\t\tthis.toolStatus.set(event.toolCallId, `${event.toolName}: ${event.result.isError ? \"error\" : \"done\"}`);\n\t\t\t\tthis.commitFileChange(event.toolCallId, event.result.isError, event.result.details);\n\t\t\t\treturn;\n\t\t\tcase \"message_end\":\n\t\t\t\tthis.appendCompletedMessage(event.message);\n\t\t\t\treturn;\n\t\t\tcase \"agent_end\":\n\t\t\t\tthis.busy = false;\n\t\t\t\tthis.retrying = false;\n\t\t\t\tthis.pendingFileChanges.clear();\n\t\t\t\tthis.processItems.length = 0;\n\t\t\t\tthis.toolStatus.clear();\n\t\t\t\tthis.spinnerFrame = 0;\n\t\t\t\treturn;\n\t\t\tcase \"turn_start\":\n\t\t\t\tif (this.busy && !this.processItems.some((item) => item.type === \"thinking\")) {\n\t\t\t\t\tthis.processItems.push({ type: \"thinking\", id: \"thinking\" });\n\t\t\t\t\tthis.spinnerFrame = 0;\n\t\t\t\t}\n\t\t\t\treturn;\n\t\t\tcase \"turn_end\":\n\t\t\t\treturn;\n\t\t}\n\t}\n\n\tprivate removeThinking(): void {\n\t\tconst index = this.processItems.findIndex((item) => item.type === \"thinking\");\n\t\tif (index >= 0) this.processItems.splice(index, 1);\n\t}\n\n\tprivate updateToolStatus(id: string, status: \"done\" | \"error\"): void {\n\t\tconst index = this.processItems.findIndex((item) => item.type === \"tool\" && item.id === id);\n\t\tconst item = this.processItems[index];\n\t\tif (index < 0 || item?.type !== \"tool\") return;\n\t\tthis.processItems[index] = { ...item, status };\n\t}\n\n\tprivate appendCompletedMessage(message: Message): void {\n\t\tif (message.role === \"tool_result\") {\n\t\t\tthis.commitFileChange(message.toolCallId, message.isError, message.details);\n\t\t\treturn;\n\t\t}\n\t\tconst text = textOf(message);\n\t\tif (message.role === \"user\") {\n\t\t\tif (text.length > 0) {\n\t\t\t\tthis.messages.push(text);\n\t\t\t\tthis.messageItems.push({ role: \"user\", text });\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\t\tif (text.length > 0 && message.stopReason !== \"tool_use\") {\n\t\t\tthis.messages.push(text);\n\t\t\tthis.messageItems.push({ role: \"assistant\", text });\n\t\t}\n\t\tfor (const content of message.content) {\n\t\t\tif (content.type === \"tool_call\") this.storeFileChange(content.id, content.name, content.arguments);\n\t\t}\n\t\tif (message.stopReason === \"error\" || message.stopReason === \"aborted\") this.error = message.errorMessage;\n\t\tthis.streamingText = \"\";\n\t}\n\n\tprivate storeFileChange(id: string, toolName: string, argumentsValue: Record<string, unknown>): void {\n\t\tconst path = argumentsValue.path;\n\t\tif (typeof path !== \"string\") return;\n\t\tif (toolName === \"edit\") {\n\t\t\tconst edits = parseEditArguments(argumentsValue);\n\t\t\tif (!edits) return;\n\t\t\tconst fallback = edits.flatMap((edit) => ({\n\t\t\t\tremoved: edit.oldText.replaceAll(\"\\r\\n\", \"\\n\").split(\"\\n\"),\n\t\t\t\tadded: edit.newText.replaceAll(\"\\r\\n\", \"\\n\").split(\"\\n\"),\n\t\t\t}));\n\t\t\tthis.pendingFileChanges.set(id, {\n\t\t\t\trole: \"file_change\",\n\t\t\t\tid,\n\t\t\t\tpath,\n\t\t\t\tkind: \"edit\",\n\t\t\t\tremoved: fallback.flatMap((edit) => edit.removed),\n\t\t\t\tadded: fallback.flatMap((edit) => edit.added),\n\t\t\t});\n\t\t\tif (this.previewRoot) {\n\t\t\t\tvoid computeEditsDiff(path, edits, this.previewRoot).then((preview) => {\n\t\t\t\t\tconst current = this.pendingFileChanges.get(id);\n\t\t\t\t\tif (!current || \"error\" in preview) return;\n\t\t\t\t\tthis.pendingFileChanges.set(id, {\n\t\t\t\t\t\t...current,\n\t\t\t\t\t\tdiff: preview.diff,\n\t\t\t\t\t\tfirstChangedLine: preview.firstChangedLine,\n\t\t\t\t\t});\n\t\t\t\t\tthis.previewChange?.();\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\t\tif (toolName === \"write\" && typeof argumentsValue.content === \"string\") {\n\t\t\tthis.pendingFileChanges.set(id, {\n\t\t\t\trole: \"file_change\",\n\t\t\t\tid,\n\t\t\t\tpath,\n\t\t\t\tkind: \"write\",\n\t\t\t\tremoved: [],\n\t\t\t\tadded: argumentsValue.content.replaceAll(\"\\r\\n\", \"\\n\").split(\"\\n\"),\n\t\t\t});\n\t\t}\n\t}\n\n\tprivate commitFileChange(id: string, isError: boolean, details: unknown = undefined): void {\n\t\tconst change = this.pendingFileChanges.get(id);\n\t\tthis.pendingFileChanges.delete(id);\n\t\tif (!change || isError) return;\n\t\tconst metadata = isEditDetails(details) ? details : undefined;\n\t\tthis.messageItems.push(\n\t\t\tmetadata ? { ...change, diff: metadata.diff, firstChangedLine: metadata.firstChangedLine } : change,\n\t\t);\n\t}\n}\n\nfunction parseEditArguments(argumentsValue: Record<string, unknown>): Edit[] | undefined {\n\tif (Array.isArray(argumentsValue.edits)) {\n\t\tconst edits: Edit[] = [];\n\t\tfor (const value of argumentsValue.edits) {\n\t\t\tif (!isRecord(value) || typeof value.oldText !== \"string\" || typeof value.newText !== \"string\") return undefined;\n\t\t\tedits.push({ oldText: value.oldText, newText: value.newText });\n\t\t}\n\t\treturn edits.length > 0 ? edits : undefined;\n\t}\n\treturn typeof argumentsValue.oldText === \"string\" && typeof argumentsValue.newText === \"string\"\n\t\t? [{ oldText: argumentsValue.oldText, newText: argumentsValue.newText }]\n\t\t: undefined;\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n\treturn typeof value === \"object\" && value !== null;\n}\n\nfunction isEditDetails(value: unknown): value is { diff: string; firstChangedLine?: number } {\n\treturn (\n\t\tisRecord(value) &&\n\t\ttypeof value.diff === \"string\" &&\n\t\t(value.firstChangedLine === undefined || typeof value.firstChangedLine === \"number\")\n\t);\n}\n\nfunction formatToolCommand(toolName: string, argumentsValue: Record<string, unknown>): string {\n\tconst serialized = JSON.stringify(argumentsValue);\n\treturn serialized === \"{}\" ? toolName : `${toolName} ${serialized}`;\n}\n"]}
@@ -1,6 +1,7 @@
1
1
  import { type TUI } from "@di-code/tui";
2
2
  import type { AgentSession, AgentSessionEvent } from "../core/session.ts";
3
3
  import type { ExtensionHost } from "../extensions/runtime.ts";
4
+ import { type InteractiveProviderOnboardingOptions } from "../provider-onboarding.ts";
4
5
  export type { AgentSessionEvent };
5
6
  export type { InteractiveMessage, InteractiveProcessItem, InteractiveState } from "./interactive-state.ts";
6
7
  export { InteractiveProjection } from "./interactive-state.ts";
@@ -10,6 +11,8 @@ export interface InteractiveModeOptions {
10
11
  readonly onExit?: () => void;
11
12
  readonly sessions?: readonly InteractiveSessionChoice[];
12
13
  readonly extensionHost?: ExtensionHost;
14
+ readonly providerOnboarding?: Omit<InteractiveProviderOnboardingOptions, "tui">;
15
+ readonly readClipboardImagePath?: (directory?: string) => Promise<string | null>;
13
16
  }
14
17
  export interface InteractiveSessionChoice {
15
18
  readonly id: string;
@@ -25,6 +28,10 @@ export declare class InteractiveMode {
25
28
  private readonly tui;
26
29
  private readonly sessionChoices;
27
30
  private readonly extensionHost?;
31
+ private readonly providerOnboarding?;
32
+ private readonly readClipboardImagePath;
33
+ private clipboardDirectory;
34
+ private readonly clipboardFiles;
28
35
  private unsubscribeSession?;
29
36
  private sessionSwitching;
30
37
  private promptInFlight;
@@ -33,17 +40,20 @@ export declare class InteractiveMode {
33
40
  private started;
34
41
  private lastFailedPrompt?;
35
42
  private queuedPrompts;
43
+ private steeringPrompts;
36
44
  private readonly onExit?;
37
45
  private theme;
38
46
  private overlay?;
39
47
  private autocompleteOverlay?;
40
48
  private spinnerTimer?;
41
49
  private shutdownEmitted;
50
+ private preserveClipboardFiles;
42
51
  constructor(options: InteractiveModeOptions);
43
52
  start(initialPrompt?: string): void;
44
53
  stop(): void;
45
54
  private exit;
46
55
  private handleCommand;
56
+ private attachClipboardImage;
47
57
  private openThemeSelector;
48
58
  private openModelSelector;
49
59
  private openSessionSelector;
@@ -58,7 +68,15 @@ export declare class InteractiveMode {
58
68
  private subscribeToSession;
59
69
  private switchSession;
60
70
  private submit;
71
+ private submitSteering;
72
+ private refreshQueue;
73
+ private cleanupUnreferencedClipboardFiles;
74
+ private cleanupClipboardFiles;
61
75
  private handleSlashCommand;
76
+ private openProviderLogin;
77
+ private logoutProvider;
78
+ private listSlashCommands;
79
+ private isLoadedSkillCommand;
62
80
  private formatUsage;
63
81
  private runManualCompaction;
64
82
  }
@@ -1 +1 @@
1
- {"version":3,"file":"interactive.d.ts","sourceRoot":"","sources":["../../src/modes/interactive.ts"],"names":[],"mappings":"AAAA,OAAO,EAON,KAAK,GAAG,EACR,MAAM,cAAc,CAAC;AACtB,OAAO,KAAK,EAAE,YAAY,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AAC1E,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAC;AAY9D,YAAY,EAAE,iBAAiB,EAAE,CAAC;AAClC,YAAY,EAAE,kBAAkB,EAAE,sBAAsB,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAC3G,OAAO,EAAE,qBAAqB,EAAE,MAAM,wBAAwB,CAAC;AAE/D,MAAM,WAAW,sBAAsB;IACtC,QAAQ,CAAC,OAAO,EAAE,YAAY,CAAC;IAC/B,QAAQ,CAAC,GAAG,EAAE,GAAG,CAAC;IAClB,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,IAAI,CAAC;IAC7B,QAAQ,CAAC,QAAQ,CAAC,EAAE,SAAS,wBAAwB,EAAE,CAAC;IACxD,QAAQ,CAAC,aAAa,CAAC,EAAE,aAAa,CAAC;CACvC;AAED,MAAM,WAAW,wBAAwB;IACxC,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAC9B,IAAI,IAAI,YAAY,GAAG,OAAO,CAAC,YAAY,CAAC,CAAC;CAC7C;AAED,qBAAa,eAAe;IAC3B,OAAO,CAAC,QAAQ,CAAC,UAAU,CAA+B;IAC1D,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAoB;IACzC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAS;IAChC,OAAO,CAAC,OAAO,CAAe;IAC9B,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAM;IAC1B,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAsC;IACrE,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAC,CAAgB;IAC/C,OAAO,CAAC,kBAAkB,CAAC,CAAa;IACxC,OAAO,CAAC,gBAAgB,CAAS;IACjC,OAAO,CAAC,cAAc,CAAS;IAC/B,OAAO,CAAC,kBAAkB,CAAS;IACnC,OAAO,CAAC,WAAW,CAAC,CAAkB;IACtC,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,gBAAgB,CAAC,CAAS;IAClC,OAAO,CAAC,aAAa,CAAgB;IACrC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAa;IACrC,OAAO,CAAC,KAAK,CAA4B;IACzC,OAAO,CAAC,OAAO,CAAC,CAAgB;IAChC,OAAO,CAAC,mBAAmB,CAAC,CAAgB;IAC5C,OAAO,CAAC,YAAY,CAAC,CAAiC;IACtD,OAAO,CAAC,eAAe,CAAS;gBAEpB,OAAO,EAAE,sBAAsB;IAoD3C,KAAK,CAAC,aAAa,CAAC,EAAE,MAAM,GAAG,IAAI;IAoBnC,IAAI,IAAI,IAAI;IAkBZ,OAAO,CAAC,IAAI;IAKZ,OAAO,CAAC,aAAa;IAwBrB,OAAO,CAAC,iBAAiB;IAezB,OAAO,CAAC,iBAAiB;IAsBzB,OAAO,CAAC,mBAAmB;IA6B3B,OAAO,CAAC,oBAAoB;IA+B5B,OAAO,CAAC,WAAW;IAYnB,OAAO,CAAC,YAAY;IAKpB,OAAO,CAAC,yBAAyB;IAoBjC,OAAO,CAAC,wBAAwB;IAKhC,OAAO,CAAC,OAAO;IAKf,OAAO,CAAC,iBAAiB;IAQzB,OAAO,CAAC,gBAAgB;IAKxB,OAAO,CAAC,kBAAkB;YASZ,aAAa;YA4Bb,MAAM;IAgDpB,OAAO,CAAC,kBAAkB;IAiD1B,OAAO,CAAC,WAAW;YAKL,mBAAmB;CAmBjC"}
1
+ {"version":3,"file":"interactive.d.ts","sourceRoot":"","sources":["../../src/modes/interactive.ts"],"names":[],"mappings":"AAEA,OAAO,EAWN,KAAK,GAAG,EACR,MAAM,cAAc,CAAC;AAStB,OAAO,KAAK,EAAE,YAAY,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AAC1E,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAC;AAC9D,OAAO,EACN,KAAK,oCAAoC,EAEzC,MAAM,2BAA2B,CAAC;AAanC,YAAY,EAAE,iBAAiB,EAAE,CAAC;AAClC,YAAY,EAAE,kBAAkB,EAAE,sBAAsB,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAC3G,OAAO,EAAE,qBAAqB,EAAE,MAAM,wBAAwB,CAAC;AAgD/D,MAAM,WAAW,sBAAsB;IACtC,QAAQ,CAAC,OAAO,EAAE,YAAY,CAAC;IAC/B,QAAQ,CAAC,GAAG,EAAE,GAAG,CAAC;IAClB,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,IAAI,CAAC;IAC7B,QAAQ,CAAC,QAAQ,CAAC,EAAE,SAAS,wBAAwB,EAAE,CAAC;IACxD,QAAQ,CAAC,aAAa,CAAC,EAAE,aAAa,CAAC;IACvC,QAAQ,CAAC,kBAAkB,CAAC,EAAE,IAAI,CAAC,oCAAoC,EAAE,KAAK,CAAC,CAAC;IAChF,QAAQ,CAAC,sBAAsB,CAAC,EAAE,CAAC,SAAS,CAAC,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;CACjF;AAED,MAAM,WAAW,wBAAwB;IACxC,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAC9B,IAAI,IAAI,YAAY,GAAG,OAAO,CAAC,YAAY,CAAC,CAAC;CAC7C;AAED,qBAAa,eAAe;IAC3B,OAAO,CAAC,QAAQ,CAAC,UAAU,CAA+B;IAC1D,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAoB;IACzC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAS;IAChC,OAAO,CAAC,OAAO,CAAe;IAC9B,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAM;IAC1B,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAsC;IACrE,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAC,CAAgB;IAC/C,OAAO,CAAC,QAAQ,CAAC,kBAAkB,CAAC,CAAoD;IACxF,OAAO,CAAC,QAAQ,CAAC,sBAAsB,CAAiD;IACxF,OAAO,CAAC,kBAAkB,CAAS;IACnC,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAqB;IACpD,OAAO,CAAC,kBAAkB,CAAC,CAAa;IACxC,OAAO,CAAC,gBAAgB,CAAS;IACjC,OAAO,CAAC,cAAc,CAAS;IAC/B,OAAO,CAAC,kBAAkB,CAAS;IACnC,OAAO,CAAC,WAAW,CAAC,CAAkB;IACtC,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,gBAAgB,CAAC,CAAS;IAClC,OAAO,CAAC,aAAa,CAAgB;IACrC,OAAO,CAAC,eAAe,CAAgB;IACvC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAa;IACrC,OAAO,CAAC,KAAK,CAA4B;IACzC,OAAO,CAAC,OAAO,CAAC,CAAgB;IAChC,OAAO,CAAC,mBAAmB,CAAC,CAAgB;IAC5C,OAAO,CAAC,YAAY,CAAC,CAAiC;IACtD,OAAO,CAAC,eAAe,CAAS;IAChC,OAAO,CAAC,sBAAsB,CAAS;gBAE3B,OAAO,EAAE,sBAAsB;IAyD3C,KAAK,CAAC,aAAa,CAAC,EAAE,MAAM,GAAG,IAAI;IAwBnC,IAAI,IAAI,IAAI;IAmBZ,OAAO,CAAC,IAAI;IAKZ,OAAO,CAAC,aAAa;YA0CP,oBAAoB;IAelC,OAAO,CAAC,iBAAiB;IAezB,OAAO,CAAC,iBAAiB;IAsBzB,OAAO,CAAC,mBAAmB;IA6B3B,OAAO,CAAC,oBAAoB;IA+B5B,OAAO,CAAC,WAAW;IAYnB,OAAO,CAAC,YAAY;IAKpB,OAAO,CAAC,yBAAyB;IA0BjC,OAAO,CAAC,wBAAwB;IAKhC,OAAO,CAAC,OAAO;IAKf,OAAO,CAAC,iBAAiB;IAQzB,OAAO,CAAC,gBAAgB;IAKxB,OAAO,CAAC,kBAAkB;YAeZ,aAAa;YAgCb,MAAM;YA8DN,cAAc;IAmC5B,OAAO,CAAC,YAAY;YAON,iCAAiC;YAOjC,qBAAqB;IAMnC,OAAO,CAAC,kBAAkB;IA2D1B,OAAO,CAAC,iBAAiB;YAgCX,cAAc;IA4C5B,OAAO,CAAC,iBAAiB;IAQzB,OAAO,CAAC,oBAAoB;IAK5B,OAAO,CAAC,WAAW;YAKL,mBAAmB;CAmBjC"}
@@ -1,8 +1,62 @@
1
- import { CombinedAutocompleteProvider, Editor, SelectList, SettingsList, } from "@di-code/tui";
1
+ import { statSync } from "node:fs";
2
+ import { extname, resolve } from "node:path";
3
+ import { CombinedAutocompleteProvider, Editor, Key, matchesKey, SelectList, SettingsList, } from "@di-code/tui";
4
+ import { cleanupStaleClipboardImages, clipboardImageDirectory, isClipboardImagePath, readClipboardImagePath, removeClipboardImage, } from "../core/clipboard-image.js";
5
+ import { extractImageAttachments } from "../core/image-input.js";
6
+ import { showInteractiveProviderOnboarding, } from "../provider-onboarding.js";
7
+ import { loadStartupConfiguration, removeGlobalProviderApiKey, resolveStartupRuntime } from "../startup.js";
2
8
  import { AutocompleteMenu, InteractiveChat, InteractiveComposer, InteractiveFooter, InteractiveHeader, } from "./interactive-components.js";
3
9
  import { InteractiveLayout } from "./interactive-layout.js";
4
10
  import { InteractiveProjection } from "./interactive-state.js";
5
11
  export { InteractiveProjection } from "./interactive-state.js";
12
+ const IMAGE_EXTENSIONS = new Set([".gif", ".jpeg", ".jpg", ".png", ".webp"]);
13
+ const PASTE_IMAGE_KEY = process.platform === "win32" ? Key.alt("v") : Key.ctrl("v");
14
+ const PASTE_IMAGE_SHORTCUT = process.platform === "win32" ? "Alt+V" : "Ctrl+V";
15
+ const BUILTIN_SLASH_COMMANDS = [
16
+ { name: "help", description: "Show interactive commands" },
17
+ { name: "clear", description: "Clear visible messages" },
18
+ { name: "model", description: "Open the model selector" },
19
+ { name: "session", description: "Open the session selector" },
20
+ { name: "theme", description: "Open the theme selector" },
21
+ { name: "settings", description: "Open the settings selector" },
22
+ { name: "login", description: "Choose a Provider and save its global API key" },
23
+ { name: "logout", description: "Remove the current Provider global API key" },
24
+ { name: "compact", description: "Compact the persisted context now" },
25
+ { name: "usage", description: "Show token and cost usage" },
26
+ { name: "retry", description: "Retry the last failed prompt" },
27
+ { name: "steer", description: "Steer the running agent" },
28
+ ];
29
+ function asImageAttachmentReference(pasted, cwd) {
30
+ if (pasted.includes("\n"))
31
+ return pasted;
32
+ const candidate = pasted.trim().replace(/^(?:"([\s\S]*)"|'([\s\S]*)')$/, "$1$2");
33
+ if (!candidate || !IMAGE_EXTENSIONS.has(extname(candidate).toLowerCase()))
34
+ return pasted;
35
+ try {
36
+ if (!statSync(resolve(cwd, candidate)).isFile())
37
+ return pasted;
38
+ }
39
+ catch {
40
+ return pasted;
41
+ }
42
+ return `@"${candidate.replaceAll('"', '\\"')}" `;
43
+ }
44
+ function normalizeDroppedImagePrompt(prompt, cwd) {
45
+ const normalized = asImageAttachmentReference(prompt, cwd);
46
+ if (normalized !== prompt)
47
+ return normalized;
48
+ const prefix = /^((?:(?:[A-Za-z]:[\\/])|(?:\.\.?[\\/])|\/)[\s\S]*?\.(?:gif|jpe?g|png|webp))([\s\S]*)$/i.exec(prompt);
49
+ if (!prefix)
50
+ return prompt;
51
+ const imageReference = asImageAttachmentReference(prefix[1] ?? "", cwd);
52
+ return imageReference === prefix[1]
53
+ ? prompt
54
+ : `${imageReference}${normalizeDroppedImagePrompt(prefix[2] ?? "", cwd)}`;
55
+ }
56
+ function steeringCommandArgument(prompt) {
57
+ const match = /^\/steer(?:\s+([\s\S]*))?$/i.exec(prompt);
58
+ return match ? (match[1] ?? "").trim() : undefined;
59
+ }
6
60
  export class InteractiveMode {
7
61
  projection = new InteractiveProjection();
8
62
  root;
@@ -11,6 +65,10 @@ export class InteractiveMode {
11
65
  tui;
12
66
  sessionChoices;
13
67
  extensionHost;
68
+ providerOnboarding;
69
+ readClipboardImagePath;
70
+ clipboardDirectory;
71
+ clipboardFiles = new Set();
14
72
  unsubscribeSession;
15
73
  sessionSwitching = false;
16
74
  promptInFlight = false;
@@ -19,33 +77,27 @@ export class InteractiveMode {
19
77
  started = false;
20
78
  lastFailedPrompt;
21
79
  queuedPrompts = [];
80
+ steeringPrompts = [];
22
81
  onExit;
23
82
  theme = "dark";
24
83
  overlay;
25
84
  autocompleteOverlay;
26
85
  spinnerTimer;
27
86
  shutdownEmitted = false;
87
+ preserveClipboardFiles = false;
28
88
  constructor(options) {
29
89
  this.session = options.session;
90
+ this.projection.configureFilePreview(this.session.allowedRoot, () => this.refresh());
30
91
  this.tui = options.tui;
31
92
  this.onExit = options.onExit;
32
93
  this.sessionChoices = [...(options.sessions ?? [])];
33
94
  this.extensionHost = options.extensionHost;
34
- const commands = [
35
- { name: "help", description: "Show interactive commands" },
36
- { name: "clear", description: "Clear visible messages" },
37
- { name: "model", description: "Open the model selector" },
38
- { name: "session", description: "Open the session selector" },
39
- { name: "theme", description: "Open the theme selector" },
40
- { name: "settings", description: "Open the settings selector" },
41
- { name: "compact", description: "Compact the persisted context now" },
42
- { name: "usage", description: "Show token and cost usage" },
43
- { name: "retry", description: "Retry the last failed prompt" },
44
- ];
45
- const allCommands = [...commands, ...(this.extensionHost?.listCommands() ?? [])];
95
+ this.providerOnboarding = options.providerOnboarding;
96
+ this.readClipboardImagePath = options.readClipboardImagePath ?? readClipboardImagePath;
97
+ this.clipboardDirectory = clipboardImageDirectory(this.session.allowedRoot);
46
98
  const autocomplete = {
47
- getSuggestions: (context, autocompleteOptions) => new CombinedAutocompleteProvider(allCommands, this.session.allowedRoot).getSuggestions(context, autocompleteOptions),
48
- applyCompletion: (context, item, prefix) => new CombinedAutocompleteProvider(allCommands, this.session.allowedRoot).applyCompletion(context, item, prefix),
99
+ getSuggestions: (context, autocompleteOptions) => new CombinedAutocompleteProvider(this.listSlashCommands(), this.session.allowedRoot).getSuggestions(context, autocompleteOptions),
100
+ applyCompletion: (context, item, prefix) => new CombinedAutocompleteProvider(this.listSlashCommands(), this.session.allowedRoot).applyCompletion(context, item, prefix),
49
101
  };
50
102
  this.editor = new Editor({
51
103
  maxHeight: 3,
@@ -55,11 +107,20 @@ export class InteractiveMode {
55
107
  this.editor.onEscape = () => this.activeAbort?.abort();
56
108
  this.editor.onCommand = (data) => this.handleCommand(data);
57
109
  this.editor.onInterrupt = () => this.exit();
110
+ this.editor.onChange = () => {
111
+ if (!this.preserveClipboardFiles) {
112
+ void this.cleanupUnreferencedClipboardFiles().catch((cause) => {
113
+ this.projection.setError(cause instanceof Error ? cause.message : String(cause));
114
+ this.refresh();
115
+ });
116
+ }
117
+ };
58
118
  this.editor.onAutocompleteChange = () => this.updateAutocompleteOverlay();
59
119
  const readViewState = () => ({
60
120
  ...this.projection.state,
61
- model: this.session.modelId,
121
+ model: `${this.session.modelId}${this.session.thinkingLevel ? `(${this.session.thinkingLevel})` : ""}`,
62
122
  theme: this.theme,
123
+ pasteImageShortcut: PASTE_IMAGE_SHORTCUT,
63
124
  });
64
125
  this.root = new InteractiveLayout({
65
126
  header: new InteractiveHeader(readViewState),
@@ -80,6 +141,10 @@ export class InteractiveMode {
80
141
  this.projection.replaceTranscript(this.session.transcript);
81
142
  this.projection.setUsage(this.session.usage);
82
143
  this.subscribeToSession();
144
+ void cleanupStaleClipboardImages(this.session.allowedRoot).catch((cause) => {
145
+ this.projection.setError(cause instanceof Error ? cause.message : String(cause));
146
+ this.refresh();
147
+ });
83
148
  try {
84
149
  this.tui.start();
85
150
  }
@@ -105,6 +170,7 @@ export class InteractiveMode {
105
170
  this.closeAutocompleteOverlay();
106
171
  this.unsubscribeSession?.();
107
172
  this.unsubscribeSession = undefined;
173
+ void this.cleanupClipboardFiles();
108
174
  this.tui.stop({ finalLines: this.root.renderTranscript(this.tui.columns) });
109
175
  if (!this.shutdownEmitted) {
110
176
  this.shutdownEmitted = true;
@@ -116,6 +182,21 @@ export class InteractiveMode {
116
182
  this.onExit?.();
117
183
  }
118
184
  handleCommand(data) {
185
+ if (matchesKey(data, Key.alt("s"))) {
186
+ void this.submitSteering(this.editor.getValue());
187
+ return true;
188
+ }
189
+ if (data === "\x1b[Z") {
190
+ try {
191
+ const level = this.session.cycleThinkingLevel();
192
+ this.projection.setStatus(level ? `thinking=${level}` : "Current model does not support thinking levels.");
193
+ }
194
+ catch (cause) {
195
+ this.projection.setError(cause instanceof Error ? cause.message : String(cause));
196
+ }
197
+ this.refresh();
198
+ return true;
199
+ }
119
200
  if (data === "\x14") {
120
201
  this.openThemeSelector();
121
202
  return true;
@@ -136,8 +217,29 @@ export class InteractiveMode {
136
217
  void this.submit(this.lastFailedPrompt, true);
137
218
  return true;
138
219
  }
220
+ if (matchesKey(data, PASTE_IMAGE_KEY)) {
221
+ void this.attachClipboardImage();
222
+ return true;
223
+ }
139
224
  return false;
140
225
  }
226
+ async attachClipboardImage() {
227
+ try {
228
+ const path = await this.readClipboardImagePath(this.clipboardDirectory);
229
+ if (!path) {
230
+ this.projection.setStatus("Clipboard has no supported image.");
231
+ }
232
+ else {
233
+ this.editor.insertTextAtCursor(path);
234
+ if (isClipboardImagePath(path, this.clipboardDirectory))
235
+ this.clipboardFiles.add(path);
236
+ }
237
+ }
238
+ catch (cause) {
239
+ this.projection.setError(cause instanceof Error ? cause.message : String(cause));
240
+ }
241
+ this.refresh();
242
+ }
141
243
  openThemeSelector() {
142
244
  const list = new SelectList([
143
245
  { value: "dark", label: "Dark", description: "Dark terminal theme" },
@@ -256,10 +358,16 @@ export class InteractiveMode {
256
358
  items: this.editor.getAutocompleteItems(),
257
359
  index: this.editor.getAutocompleteIndex(),
258
360
  }));
361
+ const editorBounds = this.root.getEditorBounds(this.tui.columns);
259
362
  this.autocompleteOverlay = this.tui.showOverlay(menu, {
260
363
  width: "55%",
261
364
  maxHeight: 8,
262
365
  anchor: "bottom-left",
366
+ placement: {
367
+ anchorRow: editorBounds.end - 1,
368
+ avoidStartRow: editorBounds.start,
369
+ preferred: "below",
370
+ },
263
371
  margin: 1,
264
372
  nonCapturing: true,
265
373
  });
@@ -291,6 +399,12 @@ export class InteractiveMode {
291
399
  this.unsubscribeSession = this.session.subscribeSession((event) => {
292
400
  if (!this.started)
293
401
  return;
402
+ if (event.type === "queue_update") {
403
+ this.steeringPrompts = [...event.steering];
404
+ this.refreshQueue();
405
+ this.refresh();
406
+ return;
407
+ }
294
408
  this.projection.apply(event);
295
409
  this.refresh();
296
410
  });
@@ -310,8 +424,12 @@ export class InteractiveMode {
310
424
  return;
311
425
  this.unsubscribeSession?.();
312
426
  this.session = next;
427
+ this.projection.configureFilePreview(next.allowedRoot, () => this.refresh());
428
+ this.clipboardDirectory = clipboardImageDirectory(next.allowedRoot);
429
+ void cleanupStaleClipboardImages(next.allowedRoot);
313
430
  this.queuedPrompts = [];
314
- this.projection.setQueue([]);
431
+ this.steeringPrompts = [];
432
+ this.refreshQueue();
315
433
  this.projection.replaceTranscript(next.transcript);
316
434
  this.projection.setUsage(next.usage);
317
435
  this.projection.setStatus(`session=${choice.id}`);
@@ -329,7 +447,12 @@ export class InteractiveMode {
329
447
  const prompt = text.trim();
330
448
  if (prompt.length === 0 || !this.started)
331
449
  return;
332
- if (prompt.startsWith("/")) {
450
+ const steeringArgument = steeringCommandArgument(prompt);
451
+ if (steeringArgument !== undefined) {
452
+ await this.submitSteering(steeringArgument);
453
+ return;
454
+ }
455
+ if (prompt.startsWith("/") && !this.isLoadedSkillCommand(prompt)) {
333
456
  this.editor.setValue("");
334
457
  this.handleSlashCommand(prompt);
335
458
  return;
@@ -346,7 +469,7 @@ export class InteractiveMode {
346
469
  }
347
470
  if (this.promptInFlight) {
348
471
  this.queuedPrompts.push(prompt);
349
- this.projection.setQueue(this.queuedPrompts);
472
+ this.refreshQueue();
350
473
  this.root.invalidate();
351
474
  this.tui.requestRender();
352
475
  return;
@@ -354,30 +477,86 @@ export class InteractiveMode {
354
477
  this.promptInFlight = true;
355
478
  this.activeAbort = new AbortController();
356
479
  this.projection.setRetrying(retry);
357
- this.editor.setValue("");
358
480
  try {
359
- const result = await this.session.prompt(prompt, this.activeAbort.signal);
481
+ const input = await extractImageAttachments(normalizeDroppedImagePrompt(prompt, this.session.allowedRoot), this.session.allowedRoot);
482
+ this.preserveClipboardFiles = true;
483
+ this.editor.setValue("");
484
+ const result = await this.session.promptWithImages(input.text, input.images, this.activeAbort.signal);
360
485
  if (result.stopReason === "error" || result.stopReason === "aborted")
361
486
  this.lastFailedPrompt = prompt;
362
- else
487
+ else {
363
488
  this.lastFailedPrompt = undefined;
489
+ await this.cleanupClipboardFiles();
490
+ }
364
491
  }
365
492
  catch (cause) {
366
493
  this.lastFailedPrompt = prompt;
367
494
  this.projection.setError(cause instanceof Error ? cause.message : String(cause));
368
495
  }
369
496
  finally {
497
+ this.preserveClipboardFiles = false;
370
498
  this.promptInFlight = false;
371
499
  this.activeAbort = undefined;
372
500
  this.projection.setRetrying(false);
373
501
  const next = this.queuedPrompts.shift();
374
- this.projection.setQueue(this.queuedPrompts);
502
+ this.refreshQueue();
375
503
  this.root.invalidate();
376
504
  this.tui.requestRender();
377
505
  if (next && this.started)
378
506
  void this.submit(next);
379
507
  }
380
508
  }
509
+ async submitSteering(text) {
510
+ const prompt = text.trim();
511
+ if (prompt.length === 0) {
512
+ this.projection.setError("Steering content must not be empty.");
513
+ this.refresh();
514
+ return;
515
+ }
516
+ if (this.sessionSwitching) {
517
+ this.projection.setError("A session is opening; wait before steering the agent.");
518
+ this.refresh();
519
+ return;
520
+ }
521
+ if (this.compactionInFlight) {
522
+ this.projection.setError("A compaction is running; wait before steering the agent.");
523
+ this.refresh();
524
+ return;
525
+ }
526
+ if (!this.promptInFlight) {
527
+ this.projection.setError("Steering is only available while a prompt is running.");
528
+ this.refresh();
529
+ return;
530
+ }
531
+ try {
532
+ const input = await extractImageAttachments(normalizeDroppedImagePrompt(prompt, this.session.allowedRoot), this.session.allowedRoot);
533
+ await this.session.steerWithImages(input.text, input.images);
534
+ this.editor.setValue("");
535
+ }
536
+ catch (cause) {
537
+ this.projection.setError(cause instanceof Error ? cause.message : String(cause));
538
+ }
539
+ this.refresh();
540
+ }
541
+ refreshQueue() {
542
+ this.projection.setQueue([
543
+ ...this.steeringPrompts.map((prompt) => `steer: ${prompt}`),
544
+ ...this.queuedPrompts.map((prompt) => `queued: ${prompt}`),
545
+ ]);
546
+ }
547
+ async cleanupUnreferencedClipboardFiles() {
548
+ const text = this.editor.getValue();
549
+ const files = [...this.clipboardFiles].filter((path) => !text.includes(path));
550
+ await Promise.all(files.map((path) => removeClipboardImage(path, this.clipboardDirectory)));
551
+ for (const path of files)
552
+ this.clipboardFiles.delete(path);
553
+ }
554
+ async cleanupClipboardFiles() {
555
+ const files = [...this.clipboardFiles];
556
+ await Promise.all(files.map((path) => removeClipboardImage(path, this.clipboardDirectory)));
557
+ for (const path of files)
558
+ this.clipboardFiles.delete(path);
559
+ }
381
560
  handleSlashCommand(input) {
382
561
  const trimmed = input.slice(1).trim();
383
562
  const [rawCommand = "", ...argParts] = trimmed.split(/\s+/);
@@ -385,7 +564,9 @@ export class InteractiveMode {
385
564
  const args = argParts.join(" ");
386
565
  switch (command) {
387
566
  case "help":
388
- this.projection.setStatus("commands: /clear /model /session /theme /settings /compact /usage /retry");
567
+ this.projection.setStatus(`commands: ${this.listSlashCommands()
568
+ .map((item) => `/${item.name}`)
569
+ .join(" ")}`);
389
570
  break;
390
571
  case "clear":
391
572
  this.projection.clearVisibleMessages();
@@ -403,6 +584,12 @@ export class InteractiveMode {
403
584
  case "settings":
404
585
  this.openSettingsSelector();
405
586
  return;
587
+ case "login":
588
+ this.openProviderLogin();
589
+ return;
590
+ case "logout":
591
+ void this.logoutProvider();
592
+ return;
406
593
  case "compact":
407
594
  void this.runManualCompaction();
408
595
  return;
@@ -428,6 +615,84 @@ export class InteractiveMode {
428
615
  }
429
616
  this.refresh();
430
617
  }
618
+ openProviderLogin() {
619
+ if (!this.providerOnboarding) {
620
+ this.projection.setError("Provider login is unavailable in this session.");
621
+ this.refresh();
622
+ return;
623
+ }
624
+ if (this.promptInFlight || this.sessionSwitching) {
625
+ this.projection.setError("Cannot change Provider while a prompt is running.");
626
+ this.refresh();
627
+ return;
628
+ }
629
+ this.editor.cancelAutocomplete();
630
+ this.closeAutocompleteOverlay();
631
+ void showInteractiveProviderOnboarding({ ...this.providerOnboarding, tui: this.tui })
632
+ .then((runtime) => {
633
+ if (!this.started)
634
+ return;
635
+ this.tui.setFocus(this.editor);
636
+ if (!runtime) {
637
+ this.refresh();
638
+ return;
639
+ }
640
+ this.session.setRuntime(runtime.provider, runtime.model);
641
+ this.projection.setStatus(`provider=${runtime.provider.id} model=${runtime.model.id}`);
642
+ this.refresh();
643
+ })
644
+ .catch((cause) => {
645
+ if (this.started)
646
+ this.tui.setFocus(this.editor);
647
+ this.projection.setError(cause instanceof Error ? cause.message : String(cause));
648
+ this.refresh();
649
+ });
650
+ }
651
+ async logoutProvider() {
652
+ if (!this.providerOnboarding) {
653
+ this.projection.setError("Provider logout is unavailable in this session.");
654
+ this.refresh();
655
+ return;
656
+ }
657
+ if (this.promptInFlight || this.sessionSwitching) {
658
+ this.projection.setError("Cannot change Provider while a prompt is running.");
659
+ this.refresh();
660
+ return;
661
+ }
662
+ const providerId = this.session.providerId;
663
+ const modelId = this.session.modelId;
664
+ try {
665
+ const removed = await removeGlobalProviderApiKey(this.providerOnboarding.agentDir, providerId);
666
+ if (!removed) {
667
+ this.projection.setStatus(`no global API key stored for provider=${providerId}`);
668
+ this.refresh();
669
+ return;
670
+ }
671
+ const configuration = await loadStartupConfiguration(this.session.allowedRoot, this.providerOnboarding.configuration.environment, this.providerOnboarding.agentDir);
672
+ const runtime = resolveStartupRuntime({
673
+ ...configuration.environment,
674
+ DI_CODE_PROVIDER: providerId,
675
+ DI_CODE_MODEL: modelId,
676
+ }, configuration.providers, configuration.defaults);
677
+ this.session.setRuntime(runtime.provider, runtime.model);
678
+ this.projection.setStatus(`global API key removed for provider=${providerId}; environment variables may still apply`);
679
+ }
680
+ catch (cause) {
681
+ this.projection.setError(cause instanceof Error ? cause.message : String(cause));
682
+ }
683
+ this.refresh();
684
+ }
685
+ listSlashCommands() {
686
+ const skills = this.session.availableSkills.map((skill) => ({
687
+ name: `skill:${skill.name}`,
688
+ description: skill.description,
689
+ }));
690
+ return [...BUILTIN_SLASH_COMMANDS, ...(this.extensionHost?.listCommands() ?? []), ...skills];
691
+ }
692
+ isLoadedSkillCommand(input) {
693
+ const name = /^\/skill:([a-z0-9]+(?:-[a-z0-9]+)*)(?:\s|$)/.exec(input)?.[1];
694
+ return name !== undefined && this.session.availableSkills.some((skill) => skill.name === name);
695
+ }
431
696
  formatUsage() {
432
697
  const usage = this.session.usage;
433
698
  return `usage: requests=${usage.requestCount} input=${usage.inputTokens} output=${usage.outputTokens} total=${usage.totalTokens} cost=$${usage.cost.total.toFixed(4)} context=${usage.estimatedContextTokens}/${usage.contextWindow}`;