@cuongtran001/kanna 1.63.2 → 1.63.3
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 +5 -9
- package/dist/client/assets/{BoardsPage-BJalB-bc.js → BoardsPage-AckCuswi.js} +1 -1
- package/dist/client/assets/BoardsRoutePage-DzKwKy3f.js +1 -0
- package/dist/client/assets/ChatPage-DsaH8CTz.js +93 -0
- package/dist/client/assets/{CronJobRow-CPbO_jwU.js → CronJobRow-Bx9gRqKW.js} +1 -1
- package/dist/client/assets/{CronJobsPage-Ck3SNgGi.js → CronJobsPage-v0-2X1oX.js} +1 -1
- package/dist/client/assets/{FilePreviewSheet-XEuUtDPn.js → FilePreviewSheet-B6SDxSD0.js} +2 -2
- package/dist/client/assets/{FlowSurface-BCbeMTJe.js → FlowSurface-CLC-ZFar.js} +1 -1
- package/dist/client/assets/{GenUIBlock-BJy_dfs-.js → GenUIBlock-CC79Ujro.js} +2 -2
- package/dist/client/assets/{KannaSocketProvider-D7Lzlt35.js → KannaSocketProvider-B2sHsWP5.js} +1 -1
- package/dist/client/assets/{LocalProjectsPage-9LJkQjhT.js → LocalProjectsPage-DYBkcEAm.js} +1 -1
- package/dist/client/assets/SettingsPage-CZqMkMRW.js +9 -0
- package/dist/client/assets/{StackBoardsRoutePage-t_EaFNG4.js → StackBoardsRoutePage-Clviz0hX.js} +1 -1
- package/dist/client/assets/{ToolCallMessage-RbF9XT2B.js → ToolCallMessage-BfNbWGmc.js} +1 -1
- package/dist/client/assets/{VChartSurface-CKuPelsn.js → VChartSurface-CUbxyBo3.js} +1 -1
- package/dist/client/assets/{WorkflowsPage-B6qUmQA3.js → WorkflowsPage-rengzb97.js} +1 -1
- package/dist/client/assets/{WorkflowsSection-DxO0S4An.js → WorkflowsSection-DuXdLsMO.js} +1 -1
- package/dist/client/assets/{chart-renderer-DjYZvNxh.js → chart-renderer-D7ukB2wg.js} +1 -1
- package/dist/client/assets/{chatSounds-FD2DQzF6.js → chatSounds-BLg2xPDZ.js} +1 -1
- package/dist/client/assets/chevron-left-RbFvmeyK.js +1 -0
- package/dist/client/assets/{clipboard.adapter-GN9nnYak.js → clipboard.adapter-CgdNwn-1.js} +1 -1
- package/dist/client/assets/{columnStyle-DjA7hA9k.js → columnStyle-CMCF0ds6.js} +1 -1
- package/dist/client/assets/{createScopedStore-BoLB9Rhc.js → createScopedStore-BrD5NHWx.js} +1 -1
- package/dist/client/assets/{dist-CimtOoGK.js → dist-DNSTHlSo.js} +1 -1
- package/dist/client/assets/editor-icons-DY4uKeEY.js +1 -0
- package/dist/client/assets/{ellipsis-Cd7sO0LO.js → ellipsis-OHg0x_x1.js} +1 -1
- package/dist/client/assets/{flow-kinds-CW2TU_83.js → flow-kinds-Ki2DP9nP.js} +1 -1
- package/dist/client/assets/{folder-C7sXCaHj.js → folder-CnWzWIG5.js} +1 -1
- package/dist/client/assets/{formatters-wSVyvDFZ.js → formatters-BGORekd-.js} +1 -1
- package/dist/client/assets/{genui-EzBIAYFE.js → genui-C9i6j0XZ.js} +1 -1
- package/dist/client/assets/{globe-xmRpmpma.js → globe-BjR1tyE4.js} +1 -1
- package/dist/client/assets/{host-DCaA9W-Z.js → host-D9tJwZXl.js} +1 -1
- package/dist/client/assets/{index-CDqjh7nt.js → index-CesRxZ0X.js} +7 -7
- package/dist/client/assets/index-CxDE9VOy.css +1 -0
- package/dist/client/assets/{info-D4MMvZFe.js → info-DNYekoE-.js} +1 -1
- package/dist/client/assets/{input-C4fvDiQ4.js → input-DgW9uRZN.js} +1 -1
- package/dist/client/assets/{lexicalToReact-sPUyOWk6.js → lexicalToReact-CZvp9Pgz.js} +3 -3
- package/dist/client/assets/{log-aTaDVF_e.js → log-DnEvbp3E.js} +1 -1
- package/dist/client/assets/{monitor-_CRKcV1h.js → monitor-qbWfUdBG.js} +1 -1
- package/dist/client/assets/{pencil-fNBeUUCf.js → pencil-C965rrpd.js} +1 -1
- package/dist/client/assets/{pendingActionsStore-COTh4Ztq.js → pendingActionsStore-CxhI19V9.js} +1 -1
- package/dist/client/assets/{play-BAmkJfn6.js → play-uofLR7yT.js} +1 -1
- package/dist/client/assets/{plus-c75CkNEO.js → plus-D4CI6rj1.js} +1 -1
- package/dist/client/assets/{popover-CHM4skzh.js → popover-Bc646hKn.js} +1 -1
- package/dist/client/assets/{pushClient-BJH_ITRB.js → pushClient-CxnHu9h4.js} +1 -1
- package/dist/client/assets/runDetached-DfQXHAaY.js +1 -0
- package/dist/client/assets/{segmented-control-DNOwRupT.js → segmented-control-CKGX29Kl.js} +1 -1
- package/dist/client/assets/{select-dLHiAls6.js → select-BjeajpVt.js} +1 -1
- package/dist/client/assets/{settings-header-button-CGKCzFyk.js → settings-header-button-BYvHR6Xl.js} +1 -1
- package/dist/client/assets/{sidebarPendingActions-Dyp80Lv_.js → sidebarPendingActions-xgMTnfRY.js} +1 -1
- package/dist/client/assets/{square-pen-B1IxmScM.js → square-pen-DmppQdTd.js} +1 -1
- package/dist/client/assets/{state-mark-BhTZfcYe.js → state-mark-C3kEL1hX.js} +1 -1
- package/dist/client/assets/{status-pill-CwhJhaMy.js → status-pill-Bo1Nb6HX.js} +1 -1
- package/dist/client/assets/{terminal-Bnj7qFGw.js → terminal-DfCF-J4p.js} +1 -1
- package/dist/client/assets/{timer.adapter-DHt2zwsL.js → timer.adapter-C2RhP4d6.js} +1 -1
- package/dist/client/assets/{tools-3gJcj3hK.js → tools-t5T_3UBg.js} +1 -1
- package/dist/client/assets/{triangle-alert-CWrc0CIO.js → triangle-alert-CYQhobyY.js} +1 -1
- package/dist/client/assets/useAppGlobalState-DUa3pkCF.js +1 -0
- package/dist/client/assets/useKannaState-FcieR5qb.js +5 -0
- package/dist/client/assets/useTheme-D9sKDPEm.js +1 -0
- package/dist/client/index.html +35 -35
- package/package.json +2 -2
- package/src/server/agent-coordinator-types.ts +0 -6
- package/src/server/agent-coordinator.ts +6 -45
- package/src/server/agent.compaction.test.ts +1 -13
- package/src/server/agent.oauth-account-info.test.ts +0 -3
- package/src/server/agent.oauth-release.test.ts +0 -2
- package/src/server/agent.oauth-rotation.test.ts +0 -9
- package/src/server/agent.oauth-token-share-cap.test.ts +0 -1
- package/src/server/agent.openrouter-model.test.ts +0 -2
- package/src/server/agent.openrouter-watchdog.test.ts +0 -3
- package/src/server/agent.sdk-workflow-register.test.ts +2 -64
- package/src/server/agent.test.ts +1 -319
- package/src/server/app-settings.test.ts +14 -17
- package/src/server/app-settings.ts +22 -37
- package/src/server/auto-continue/e2e.test.ts +0 -1
- package/src/server/background-task-output-registry.test.ts +1 -1
- package/src/server/background-task-output-registry.ts +1 -1
- package/src/server/claude-cancel-handler.test.ts +0 -58
- package/src/server/claude-cancel-handler.ts +1 -17
- package/src/server/claude-harness-stream.test.ts +134 -0
- package/src/server/{claude-pty/jsonl-path.test.ts → claude-projects-path.adapter.test.ts} +1 -19
- package/src/server/{claude-pty/jsonl-path.adapter.ts → claude-projects-path.adapter.ts} +0 -11
- package/src/server/claude-send-command.test.ts +0 -1
- package/src/server/claude-session-config-helpers.test.ts +1 -90
- package/src/server/claude-session-config-helpers.ts +1 -38
- package/src/server/claude-session-error-handler.test.ts +0 -1
- package/src/server/claude-session-lifecycle.test.ts +2 -36
- package/src/server/claude-session-lifecycle.ts +2 -8
- package/src/server/claude-session-runner.test.ts +8 -36
- package/src/server/claude-session-runner.ts +1 -26
- package/src/server/claude-session-spawner.test.ts +5 -64
- package/src/server/claude-session-spawner.ts +49 -115
- package/src/server/claude-session-start.test.ts +1 -14
- package/src/server/claude-session-start.ts +0 -8
- package/src/server/claude-session-state-queries.test.ts +1 -2
- package/src/server/claude-session-state.test.ts +0 -1
- package/src/server/claude-session-state.ts +0 -4
- package/src/server/claude-subagent-wiring.test.ts +2 -68
- package/src/server/claude-subagent-wiring.ts +3 -50
- package/src/server/claude-turn-starter-types.ts +0 -3
- package/src/server/claude-turn-starter.test.ts +0 -28
- package/src/server/claude-turn-starter.ts +0 -5
- package/src/server/event-store.test.ts +0 -2
- package/src/server/events.ts +1 -2
- package/src/server/harness-types.ts +1 -2
- package/src/server/import-subagent-drill.e2e.test.ts +1 -1
- package/src/server/imported-subagents-dir.test.ts +1 -1
- package/src/server/imported-subagents-dir.ts +1 -1
- package/src/server/kanna-mcp.test.ts +0 -75
- package/src/server/kanna-mcp.ts +1 -47
- package/src/server/server.test.ts +5 -7
- package/src/server/server.ts +2 -18
- package/src/server/subagent-orchestrator.test.ts +0 -1
- package/src/server/subagent-provider-run.test.ts +48 -10
- package/src/server/subagent-provider-run.ts +12 -3
- package/src/server/tool-callback.test.ts +1 -1
- package/src/server/ws-router-agent-ctrl.test.ts +0 -45
- package/src/server/ws-router-agent-ctrl.ts +1 -31
- package/src/server/ws-router-broadcast.ts +0 -27
- package/src/server/ws-router-defaults.test.ts +2 -3
- package/src/server/ws-router-defaults.ts +2 -3
- package/src/server/ws-router-envelope.ts +0 -15
- package/src/server/ws-router-observability.test.ts +1 -1
- package/src/server/ws-router-settings.test.ts +2 -2
- package/src/server/ws-router.test.ts +2 -2
- package/src/server/ws-router.ts +0 -8
- package/src/shared/app-settings-types.ts +9 -20
- package/src/shared/claude-api-error-classification.ts +1 -1
- package/src/shared/protocol.ts +1 -11
- package/dist/client/assets/BoardsRoutePage-BTTjJX-Y.js +0 -1
- package/dist/client/assets/ChatPage-DH8wtQyD.js +0 -93
- package/dist/client/assets/SettingsPage-CKrZ6dGQ.js +0 -9
- package/dist/client/assets/chevron-left-CJ0qv9kF.js +0 -1
- package/dist/client/assets/editor-icons-CONDQZof.js +0 -1
- package/dist/client/assets/index-C-UYaDk6.css +0 -1
- package/dist/client/assets/runDetached-DqQ3k2ji.js +0 -1
- package/dist/client/assets/useAppGlobalState-DKOGrO5l.js +0 -1
- package/dist/client/assets/useKannaState-D7R_-dM6.js +0 -5
- package/dist/client/assets/useTheme-B57LIKyx.js +0 -1
- package/src/server/agent.pty-rotation.test.ts +0 -322
- package/src/server/claude-pty/auth.test.ts +0 -38
- package/src/server/claude-pty/auth.ts +0 -17
- package/src/server/claude-pty/claude-session-registry.adapter.ts +0 -68
- package/src/server/claude-pty/claude-session-registry.test.ts +0 -149
- package/src/server/claude-pty/driver.test.ts +0 -1153
- package/src/server/claude-pty/driver.ts +0 -829
- package/src/server/claude-pty/env.test.ts +0 -79
- package/src/server/claude-pty/env.ts +0 -20
- package/src/server/claude-pty/jsonl-to-event.test.ts +0 -882
- package/src/server/claude-pty/jsonl-to-event.ts +0 -357
- package/src/server/claude-pty/parity-matrix.test.ts +0 -437
- package/src/server/claude-pty/pid-registry.adapter.ts +0 -164
- package/src/server/claude-pty/pid-registry.test.ts +0 -178
- package/src/server/claude-pty/preflight/binary-fingerprint.adapter.ts +0 -20
- package/src/server/claude-pty/preflight/binary-fingerprint.test.ts +0 -32
- package/src/server/claude-pty/pty-instance-registry.test.ts +0 -208
- package/src/server/claude-pty/pty-instance-registry.ts +0 -172
- package/src/server/claude-pty/pty-memory-sampler.adapter.test.ts +0 -103
- package/src/server/claude-pty/pty-memory-sampler.adapter.ts +0 -86
- package/src/server/claude-pty/pty-process.adapter.ts +0 -60
- package/src/server/claude-pty/pty-process.test.ts +0 -49
- package/src/server/claude-pty/resolve-binary.adapter.ts +0 -106
- package/src/server/claude-pty/resolve-binary.test.ts +0 -118
- package/src/server/claude-pty/runtime-dir.adapter.ts +0 -19
- package/src/server/claude-pty/sandbox/detect.adapter.ts +0 -17
- package/src/server/claude-pty/sandbox/detect.test.ts +0 -17
- package/src/server/claude-pty/sandbox/platform.test.ts +0 -35
- package/src/server/claude-pty/sandbox/platform.ts +0 -27
- package/src/server/claude-pty/sandbox/preflight-io.adapter.ts +0 -14
- package/src/server/claude-pty/sandbox/preflight.test.ts +0 -69
- package/src/server/claude-pty/sandbox/preflight.ts +0 -44
- package/src/server/claude-pty/sandbox/profile-linux.adapter.ts +0 -63
- package/src/server/claude-pty/sandbox/profile-linux.test.ts +0 -90
- package/src/server/claude-pty/sandbox/profile-macos.adapter.ts +0 -72
- package/src/server/claude-pty/sandbox/profile-macos.test.ts +0 -68
- package/src/server/claude-pty/sandbox/wrap.adapter.ts +0 -58
- package/src/server/claude-pty/sandbox/wrap.test.ts +0 -82
- package/src/server/claude-pty/settings-writer.adapter.ts +0 -23
- package/src/server/claude-pty/settings-writer.test.ts +0 -22
- package/src/server/claude-pty/smoke-test-io.adapter.ts +0 -28
- package/src/server/claude-pty/smoke-test.test.ts +0 -199
- package/src/server/claude-pty/smoke-test.ts +0 -182
- package/src/server/claude-pty/tui-control.test.ts +0 -300
- package/src/server/claude-pty/tui-control.ts +0 -174
- package/src/server/claude-pty/tui-source.adapter.test.ts +0 -374
- package/src/server/claude-pty/tui-source.adapter.ts +0 -294
- package/src/server/http-server.adapter.test.ts +0 -18
- package/src/server/http-server.adapter.ts +0 -39
- package/src/server/kanna-mcp-http.test.ts +0 -274
- package/src/server/kanna-mcp-http.ts +0 -194
- package/src/server/kanna-mcp-tools/ask-user-question.test.ts +0 -108
- package/src/server/kanna-mcp-tools/ask-user-question.ts +0 -55
- package/src/server/kanna-mcp-tools/exit-plan-mode.test.ts +0 -61
- package/src/server/kanna-mcp-tools/exit-plan-mode.ts +0 -49
- package/src/server/mcp-zod-compat.adapter.ts +0 -20
- package/src/shared/pty-instance.ts +0 -43
- /package/src/server/{claude-pty/output-ring.test.ts → output-ring.test.ts} +0 -0
- /package/src/server/{claude-pty/output-ring.ts → output-ring.ts} +0 -0
|
@@ -6,7 +6,7 @@ import { importSessionsByIds } from "./claude-session-importer.adapter"
|
|
|
6
6
|
import { createTestEventStore } from "./storage/test-helpers"
|
|
7
7
|
import { createSubagentTranscriptRegistry } from "./subagent-transcript-registry"
|
|
8
8
|
import { handleObservabilityCommand } from "./ws-router-observability"
|
|
9
|
-
import { encodeCwd } from "./claude-
|
|
9
|
+
import { encodeCwd } from "./claude-projects-path.adapter"
|
|
10
10
|
import type { ServerEnvelope } from "../shared/protocol"
|
|
11
11
|
|
|
12
12
|
describe("subagent drill-in for imported chats (e2e)", () => {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { describe, expect, test } from "bun:test"
|
|
2
2
|
import { deriveImportedSubagentsDir } from "./imported-subagents-dir"
|
|
3
|
-
import { encodeCwd } from "./claude-
|
|
3
|
+
import { encodeCwd } from "./claude-projects-path.adapter"
|
|
4
4
|
|
|
5
5
|
const REAL_CWD = process.cwd()
|
|
6
6
|
const encodedCwd = encodeCwd(REAL_CWD)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { homedir } from "node:os"
|
|
2
2
|
import path from "node:path"
|
|
3
|
-
import { computeProjectDir } from "./claude-
|
|
3
|
+
import { computeProjectDir } from "./claude-projects-path.adapter"
|
|
4
4
|
|
|
5
5
|
export function deriveImportedSubagentsDir(args: {
|
|
6
6
|
cwd: string
|
|
@@ -106,39 +106,6 @@ const makeArgs = (toolCallback?: Parameters<typeof buildKannaMcpTools>[0]["toolC
|
|
|
106
106
|
tunnelGateway: null,
|
|
107
107
|
})
|
|
108
108
|
|
|
109
|
-
test("feature flag off → ask_user_question / exit_plan_mode NOT registered", () => {
|
|
110
|
-
delete process.env.KANNA_MCP_TOOL_CALLBACKS
|
|
111
|
-
const tools = buildKannaMcpTools(makeArgs(undefined))
|
|
112
|
-
const names = tools.map((t) => t.name)
|
|
113
|
-
expect(names).not.toContain("ask_user_question")
|
|
114
|
-
expect(names).not.toContain("exit_plan_mode")
|
|
115
|
-
})
|
|
116
|
-
|
|
117
|
-
test("feature flag on → tools registered when toolCallback present", () => {
|
|
118
|
-
process.env.KANNA_MCP_TOOL_CALLBACKS = "1"
|
|
119
|
-
const stub: Parameters<typeof buildKannaMcpTools>[0]["toolCallback"] = {
|
|
120
|
-
submit: async () => ({ status: "answered", decision: { kind: "deny" as const, reason: "test" } }),
|
|
121
|
-
answer: async () => {},
|
|
122
|
-
cancel: async () => {},
|
|
123
|
-
cancelAllForChat: async () => {},
|
|
124
|
-
recoverOnStartup: async () => {},
|
|
125
|
-
}
|
|
126
|
-
const tools = buildKannaMcpTools(makeArgs(stub))
|
|
127
|
-
const names = tools.map((t) => t.name)
|
|
128
|
-
expect(names).toContain("ask_user_question")
|
|
129
|
-
expect(names).toContain("exit_plan_mode")
|
|
130
|
-
delete process.env.KANNA_MCP_TOOL_CALLBACKS
|
|
131
|
-
})
|
|
132
|
-
|
|
133
|
-
test("feature flag on but toolCallback absent → tools NOT registered", () => {
|
|
134
|
-
process.env.KANNA_MCP_TOOL_CALLBACKS = "1"
|
|
135
|
-
const tools = buildKannaMcpTools(makeArgs(undefined))
|
|
136
|
-
const names = tools.map((t) => t.name)
|
|
137
|
-
expect(names).not.toContain("ask_user_question")
|
|
138
|
-
expect(names).not.toContain("exit_plan_mode")
|
|
139
|
-
delete process.env.KANNA_MCP_TOOL_CALLBACKS
|
|
140
|
-
})
|
|
141
|
-
|
|
142
109
|
test("feature flag on → all 8 new mcp__kanna__* tools registered", () => {
|
|
143
110
|
process.env.KANNA_MCP_TOOL_CALLBACKS = "1"
|
|
144
111
|
try {
|
|
@@ -169,48 +136,6 @@ test("feature flag on → all 8 new mcp__kanna__* tools registered", () => {
|
|
|
169
136
|
})
|
|
170
137
|
|
|
171
138
|
|
|
172
|
-
const callbackStub = (): Parameters<typeof buildKannaMcpTools>[0]["toolCallback"] => ({
|
|
173
|
-
submit: async () => ({ status: "answered", decision: { kind: "deny" as const, reason: "test" } }),
|
|
174
|
-
answer: async () => {},
|
|
175
|
-
cancel: async () => {},
|
|
176
|
-
cancelAllForChat: async () => {},
|
|
177
|
-
recoverOnStartup: async () => {},
|
|
178
|
-
})
|
|
179
|
-
|
|
180
|
-
test("forceInteractiveToolCallbacks → ask_user_question / exit_plan_mode registered with env flag UNSET", () => {
|
|
181
|
-
delete process.env.KANNA_MCP_TOOL_CALLBACKS
|
|
182
|
-
const tools = buildKannaMcpTools({
|
|
183
|
-
...makeArgs(callbackStub()),
|
|
184
|
-
forceInteractiveToolCallbacks: true,
|
|
185
|
-
})
|
|
186
|
-
const names = tools.map((t) => t.name)
|
|
187
|
-
expect(names).toContain("ask_user_question")
|
|
188
|
-
expect(names).toContain("exit_plan_mode")
|
|
189
|
-
})
|
|
190
|
-
|
|
191
|
-
test("forceInteractiveToolCallbacks does NOT register the 8 built-in shims (env flag UNSET)", () => {
|
|
192
|
-
delete process.env.KANNA_MCP_TOOL_CALLBACKS
|
|
193
|
-
const tools = buildKannaMcpTools({
|
|
194
|
-
...makeArgs(callbackStub()),
|
|
195
|
-
forceInteractiveToolCallbacks: true,
|
|
196
|
-
})
|
|
197
|
-
const names = tools.map((t) => t.name)
|
|
198
|
-
for (const n of ["read", "glob", "grep", "bash", "edit", "write", "webfetch", "websearch"]) {
|
|
199
|
-
expect(names).not.toContain(n)
|
|
200
|
-
}
|
|
201
|
-
})
|
|
202
|
-
|
|
203
|
-
test("forceInteractiveToolCallbacks but toolCallback absent → nothing registered (fail-safe)", () => {
|
|
204
|
-
delete process.env.KANNA_MCP_TOOL_CALLBACKS
|
|
205
|
-
const tools = buildKannaMcpTools({
|
|
206
|
-
...makeArgs(undefined),
|
|
207
|
-
forceInteractiveToolCallbacks: true,
|
|
208
|
-
})
|
|
209
|
-
const names = tools.map((t) => t.name)
|
|
210
|
-
expect(names).not.toContain("ask_user_question")
|
|
211
|
-
expect(names).not.toContain("exit_plan_mode")
|
|
212
|
-
})
|
|
213
|
-
|
|
214
139
|
describe("buildDelegateProgressEmitter", () => {
|
|
215
140
|
function makeEntry(over: Partial<TranscriptEntry> = {}): TranscriptEntry {
|
|
216
141
|
return { _id: "e1", createdAt: 1, kind: "assistant_text", text: "x", ...over } as TranscriptEntry
|
package/src/server/kanna-mcp.ts
CHANGED
|
@@ -20,8 +20,6 @@ import { buildProjectFileContentUrl, buildLocalFileContentUrl } from "../shared/
|
|
|
20
20
|
import { inferAttachmentContentType, inferProjectFileContentType } from "./uploads"
|
|
21
21
|
import type { TranscriptEntry } from "../shared/types"
|
|
22
22
|
import type { TunnelGateway } from "./cloudflare-tunnel/gateway"
|
|
23
|
-
import { createAskUserQuestionTool } from "./kanna-mcp-tools/ask-user-question"
|
|
24
|
-
import { createExitPlanModeTool } from "./kanna-mcp-tools/exit-plan-mode"
|
|
25
23
|
import { createReadTool } from "./kanna-mcp-tools/read.adapter"
|
|
26
24
|
import { createGlobTool } from "./kanna-mcp-tools/glob.adapter"
|
|
27
25
|
import { createGrepTool } from "./kanna-mcp-tools/grep.adapter"
|
|
@@ -81,7 +79,6 @@ export interface KannaMcpArgs extends OfferDownloadArgs {
|
|
|
81
79
|
chatPolicy?: ChatPermissionPolicy
|
|
82
80
|
subagentOrchestrator?: SubagentOrchestrator
|
|
83
81
|
delegationContext?: KannaMcpDelegationContext
|
|
84
|
-
forceInteractiveToolCallbacks?: boolean
|
|
85
82
|
restrictedAllowedPaths?: readonly string[]
|
|
86
83
|
setupLoop?: (input: LoopSetupInput) => Promise<SetupLoopHandlerResult>
|
|
87
84
|
stopLoop?: () => Promise<void>
|
|
@@ -1046,50 +1043,7 @@ export function buildKannaMcpTools(args: KannaMcpArgs): KannaSdkToolList {
|
|
|
1046
1043
|
)
|
|
1047
1044
|
}
|
|
1048
1045
|
|
|
1049
|
-
const
|
|
1050
|
-
const interactiveEnabled =
|
|
1051
|
-
(envCallbacksEnabled || args.forceInteractiveToolCallbacks === true) && Boolean(args.toolCallback)
|
|
1052
|
-
const builtinShimsEnabled = envCallbacksEnabled && Boolean(args.toolCallback)
|
|
1053
|
-
|
|
1054
|
-
if (interactiveEnabled && args.toolCallback) {
|
|
1055
|
-
const askTool = createAskUserQuestionTool({ toolCallback: args.toolCallback })
|
|
1056
|
-
const exitPlanTool = createExitPlanModeTool({ toolCallback: args.toolCallback })
|
|
1057
|
-
|
|
1058
|
-
tools.push(
|
|
1059
|
-
tool(
|
|
1060
|
-
askTool.name,
|
|
1061
|
-
"Ask the user a question with multiple choice answers",
|
|
1062
|
-
askTool.schema.shape,
|
|
1063
|
-
async (input, extra) => {
|
|
1064
|
-
const requestId = isRecord(extra) && (typeof extra.requestId === "string" || typeof extra.requestId === "number") ? extra.requestId : undefined
|
|
1065
|
-
const toolUseId = requestId != null ? String(requestId) : randomUUID()
|
|
1066
|
-
return await askTool.handler(input, {
|
|
1067
|
-
chatId: chatId ?? "",
|
|
1068
|
-
sessionId,
|
|
1069
|
-
toolUseId,
|
|
1070
|
-
cwd,
|
|
1071
|
-
chatPolicy,
|
|
1072
|
-
})
|
|
1073
|
-
},
|
|
1074
|
-
),
|
|
1075
|
-
tool(
|
|
1076
|
-
exitPlanTool.name,
|
|
1077
|
-
"Submit a plan for user approval before continuing",
|
|
1078
|
-
exitPlanTool.schema.shape,
|
|
1079
|
-
async (input, extra) => {
|
|
1080
|
-
const requestId = isRecord(extra) && (typeof extra.requestId === "string" || typeof extra.requestId === "number") ? extra.requestId : undefined
|
|
1081
|
-
const toolUseId = requestId != null ? String(requestId) : randomUUID()
|
|
1082
|
-
return await exitPlanTool.handler(input, {
|
|
1083
|
-
chatId: chatId ?? "",
|
|
1084
|
-
sessionId,
|
|
1085
|
-
toolUseId,
|
|
1086
|
-
cwd,
|
|
1087
|
-
chatPolicy,
|
|
1088
|
-
})
|
|
1089
|
-
},
|
|
1090
|
-
),
|
|
1091
|
-
)
|
|
1092
|
-
}
|
|
1046
|
+
const builtinShimsEnabled = process.env.KANNA_MCP_TOOL_CALLBACKS === "1" && Boolean(args.toolCallback)
|
|
1093
1047
|
|
|
1094
1048
|
if (builtinShimsEnabled && args.toolCallback) {
|
|
1095
1049
|
const readTool = createReadTool({ toolCallback: args.toolCallback })
|
|
@@ -3,7 +3,7 @@ import {
|
|
|
3
3
|
AUTH_DEFAULTS,
|
|
4
4
|
CLAUDE_AUTH_DEFAULTS,
|
|
5
5
|
CLAUDE_DRIVER_DEFAULTS,
|
|
6
|
-
|
|
6
|
+
CLAUDE_LIFECYCLE_DEFAULTS,
|
|
7
7
|
CLOUDFLARE_TUNNEL_DEFAULTS,
|
|
8
8
|
PACKAGE_UPDATE_SETTINGS_DEFAULTS,
|
|
9
9
|
PLUGIN_SETTINGS_DEFAULTS,
|
|
@@ -58,7 +58,7 @@ function makeSnapshot(overrides: Partial<AppSettingsSnapshot> = {}): AppSettings
|
|
|
58
58
|
customMcpServers: [],
|
|
59
59
|
customModels: [],
|
|
60
60
|
textSnippets: [],
|
|
61
|
-
claudeDriver: { ...CLAUDE_DRIVER_DEFAULTS, lifecycle: { ...
|
|
61
|
+
claudeDriver: { ...CLAUDE_DRIVER_DEFAULTS, lifecycle: { ...CLAUDE_LIFECYCLE_DEFAULTS } },
|
|
62
62
|
globalPromptAppend: "",
|
|
63
63
|
shareDefaultTtlHours: 24,
|
|
64
64
|
subagentRuntime: { runTimeoutMs: 600_000, defaultLoopSubagentId: null },
|
|
@@ -97,17 +97,15 @@ describe("buildAgentAppSettingsView", () => {
|
|
|
97
97
|
expect(view.providerDefaults.claude.modelOptions.contextWindow).toBe("1m")
|
|
98
98
|
})
|
|
99
99
|
|
|
100
|
-
test("forwards claudeDriver
|
|
100
|
+
test("forwards claudeDriver lifecycle", () => {
|
|
101
101
|
const view = buildAgentAppSettingsView(
|
|
102
102
|
makeSnapshot({
|
|
103
103
|
claudeDriver: {
|
|
104
|
-
|
|
105
|
-
lifecycle: { ...CLAUDE_PTY_LIFECYCLE_DEFAULTS, idleTimeoutMs: 1234, maxConcurrent: 7 },
|
|
104
|
+
lifecycle: { ...CLAUDE_LIFECYCLE_DEFAULTS, idleTimeoutMs: 1234, maxConcurrent: 7 },
|
|
106
105
|
},
|
|
107
106
|
}),
|
|
108
107
|
)
|
|
109
108
|
|
|
110
|
-
expect(view.claudeDriver.preference).toBe("pty")
|
|
111
109
|
expect(view.claudeDriver.lifecycle.idleTimeoutMs).toBe(1234)
|
|
112
110
|
expect(view.claudeDriver.lifecycle.maxConcurrent).toBe(7)
|
|
113
111
|
})
|
|
@@ -117,7 +115,7 @@ describe("buildAgentAppSettingsView", () => {
|
|
|
117
115
|
expect(view.globalPromptAppend).toBe("")
|
|
118
116
|
})
|
|
119
117
|
|
|
120
|
-
test("forwards customMcpServers so
|
|
118
|
+
test("forwards customMcpServers so the agent receives the user's MCP entries", () => {
|
|
121
119
|
const context7: McpServerConfig = {
|
|
122
120
|
id: "mcp-context7",
|
|
123
121
|
name: "context7",
|
package/src/server/server.ts
CHANGED
|
@@ -30,8 +30,6 @@ import { fetchOpenRouterModelsRaw } from "./openrouter-models-io.adapter"
|
|
|
30
30
|
import { getMachineDisplayName } from "./machine-name.adapter"
|
|
31
31
|
import { TerminalManager } from "./terminal-manager"
|
|
32
32
|
import { TerminalPidRegistry } from "./terminal-pid-registry.adapter"
|
|
33
|
-
import { ClaudePtyRegistry } from "./claude-pty/pid-registry.adapter"
|
|
34
|
-
import { createPtyInstanceRegistry } from "./claude-pty/pty-instance-registry"
|
|
35
33
|
import { createBoardRegistry } from "./board-registry"
|
|
36
34
|
import { createBoardStore } from "./board-store.adapter"
|
|
37
35
|
import { createBoardSync } from "./board-sync"
|
|
@@ -215,8 +213,6 @@ async function createApplicationServices(options: StartKannaServerOptions): Prom
|
|
|
215
213
|
if (reapedTerminals.length > 0) {
|
|
216
214
|
log.info(`[kanna] reaped ${reapedTerminals.length} orphan terminal process group(s) from previous run`)
|
|
217
215
|
}
|
|
218
|
-
const claudePtyRegistry = new ClaudePtyRegistry(path.join(store.dataDir, "claude-pty.json"))
|
|
219
|
-
const ptyInstanceRegistry = createPtyInstanceRegistry()
|
|
220
216
|
const boardStore = createBoardStore({ filePath: path.join(store.dataDir, "boards.db") })
|
|
221
217
|
const boardRegistry = createBoardRegistry({ store: boardStore })
|
|
222
218
|
const boardSync = createBoardSync({
|
|
@@ -236,9 +232,8 @@ async function createApplicationServices(options: StartKannaServerOptions): Prom
|
|
|
236
232
|
})
|
|
237
233
|
const subagentTranscriptRegistry = createSubagentTranscriptRegistry()
|
|
238
234
|
const backgroundTaskOutputRegistry = createBackgroundTaskOutputRegistry(backgroundTaskOutputIo)
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
log.info(`[kanna] reaped ${reapedClaudePty.length} orphan claude PTY process group(s) from previous run`)
|
|
235
|
+
if (process.env.KANNA_CLAUDE_DRIVER === "pty") {
|
|
236
|
+
log.warn("[kanna] KANNA_CLAUDE_DRIVER=pty is ignored: the PTY driver was removed and Claude chats run on the Agent SDK. Unset the variable to silence this warning.")
|
|
242
237
|
}
|
|
243
238
|
const keybindings = new KeybindingsManager()
|
|
244
239
|
const appSettings = new AppSettingsManager(path.join(store.dataDir, "settings.json"))
|
|
@@ -390,8 +385,6 @@ async function createApplicationServices(options: StartKannaServerOptions): Prom
|
|
|
390
385
|
tunnelGateway,
|
|
391
386
|
oauthPool,
|
|
392
387
|
toolCallback,
|
|
393
|
-
claudePtyRegistry,
|
|
394
|
-
ptyInstanceRegistry,
|
|
395
388
|
workflowRegistry,
|
|
396
389
|
boardRegistry,
|
|
397
390
|
backgroundTaskOutputRegistry,
|
|
@@ -526,7 +519,6 @@ async function createApplicationServices(options: StartKannaServerOptions): Prom
|
|
|
526
519
|
machineDisplayName,
|
|
527
520
|
updateManager,
|
|
528
521
|
pushManager,
|
|
529
|
-
ptyInstances: ptyInstanceRegistry,
|
|
530
522
|
workflowRegistry,
|
|
531
523
|
boardRegistry,
|
|
532
524
|
boardSync,
|
|
@@ -538,14 +530,6 @@ async function createApplicationServices(options: StartKannaServerOptions): Prom
|
|
|
538
530
|
backgroundTaskOutputRegistry,
|
|
539
531
|
subagentTranscriptRegistry,
|
|
540
532
|
followedSessionRegistry,
|
|
541
|
-
killPtyInstance: async (chatId: string) => {
|
|
542
|
-
try {
|
|
543
|
-
await agent.killPtyInstance(chatId)
|
|
544
|
-
return { ok: true }
|
|
545
|
-
} catch (err) {
|
|
546
|
-
return { ok: false, error: err instanceof Error ? err.message : String(err) }
|
|
547
|
-
}
|
|
548
|
-
},
|
|
549
533
|
sessionShare: sessionShareService,
|
|
550
534
|
packageUpdateManager,
|
|
551
535
|
genuiDatasets,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { describe, expect, test } from "bun:test"
|
|
2
2
|
import type { JsonValue } from "../shared/json"
|
|
3
|
-
import type { ClaudeModelOptions, Subagent, TranscriptEntry } from "../shared/types"
|
|
3
|
+
import type { ClaudeModelOptions, CustomModelEntry, Subagent, TranscriptEntry } from "../shared/types"
|
|
4
4
|
import type { HarnessEvent, HarnessTurn, HarnessToolRequest } from "./harness-types"
|
|
5
5
|
import type { StartCodexSessionArgs, CodexSessionScope } from "./codex-app-server"
|
|
6
6
|
import { buildSubagentProviderRun, composeInitialPrompt, composeSubagentSystemPrompt, drainOneTurn, type BuildSubagentProviderRunArgs } from "./subagent-provider-run"
|
|
@@ -96,6 +96,37 @@ function makeArgs(over: Partial<BuildSubagentProviderRunArgs> = {}): BuildSubage
|
|
|
96
96
|
}
|
|
97
97
|
|
|
98
98
|
|
|
99
|
+
const HAIKU_WITH_1M: CustomModelEntry = {
|
|
100
|
+
id: "claude-haiku-5-5",
|
|
101
|
+
label: "Claude Haiku 5.5",
|
|
102
|
+
provider: "claude",
|
|
103
|
+
contextWindowOptions: [{ id: "200k", label: "200k" }, { id: "1m", label: "1M" }],
|
|
104
|
+
createdAt: 1,
|
|
105
|
+
updatedAt: 1,
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
async function modelPassedToSession(args: BuildSubagentProviderRunArgs): Promise<string | undefined> {
|
|
109
|
+
let model: string | undefined
|
|
110
|
+
const run = buildSubagentProviderRun({
|
|
111
|
+
...args,
|
|
112
|
+
startClaudeSession: async (sessionArgs) => {
|
|
113
|
+
model = sessionArgs.model
|
|
114
|
+
return {
|
|
115
|
+
provider: "claude" as const,
|
|
116
|
+
stream: makeHarnessTurn([]).stream,
|
|
117
|
+
interrupt: async () => {},
|
|
118
|
+
close: () => {},
|
|
119
|
+
closed: Promise.resolve(),
|
|
120
|
+
sendPrompt: async () => {},
|
|
121
|
+
setModel: async () => {},
|
|
122
|
+
setPermissionMode: async () => {},
|
|
123
|
+
}
|
|
124
|
+
},
|
|
125
|
+
})
|
|
126
|
+
await run.start(() => {}, () => {})
|
|
127
|
+
return model
|
|
128
|
+
}
|
|
129
|
+
|
|
99
130
|
describe("composeInitialPrompt", () => {
|
|
100
131
|
const subagent = makeSubagent({ name: "reviewer" })
|
|
101
132
|
|
|
@@ -205,7 +236,6 @@ describe("buildSubagentProviderRun – Claude", () => {
|
|
|
205
236
|
closed: Promise.resolve(),
|
|
206
237
|
setModel: async () => {},
|
|
207
238
|
setPermissionMode: async () => {},
|
|
208
|
-
getSupportedCommands: async () => [],
|
|
209
239
|
}),
|
|
210
240
|
})
|
|
211
241
|
|
|
@@ -239,7 +269,6 @@ describe("buildSubagentProviderRun – Claude", () => {
|
|
|
239
269
|
sendPrompt: async () => {},
|
|
240
270
|
setModel: async () => {},
|
|
241
271
|
setPermissionMode: async () => {},
|
|
242
|
-
getSupportedCommands: async () => [],
|
|
243
272
|
}
|
|
244
273
|
},
|
|
245
274
|
})
|
|
@@ -248,6 +277,22 @@ describe("buildSubagentProviderRun – Claude", () => {
|
|
|
248
277
|
expect(captured?.systemPromptOverride).toBe("You are alpha.\n\n## Workspace instructions\n\nAlways TDD.")
|
|
249
278
|
})
|
|
250
279
|
|
|
280
|
+
test("runs a Claude subagent at the 1M context window it is configured for", async () => {
|
|
281
|
+
const model = await modelPassedToSession(makeArgs({
|
|
282
|
+
subagent: makeSubagent({ model: "claude-haiku-5-5" }),
|
|
283
|
+
customModels: [HAIKU_WITH_1M],
|
|
284
|
+
}))
|
|
285
|
+
expect(model).toBe("claude-haiku-5-5[1m]")
|
|
286
|
+
})
|
|
287
|
+
|
|
288
|
+
test("keeps the bare model id when the model does not offer the 1M window", async () => {
|
|
289
|
+
const model = await modelPassedToSession(makeArgs({
|
|
290
|
+
subagent: makeSubagent({ model: "claude-haiku-5-5" }),
|
|
291
|
+
customModels: [{ ...HAIKU_WITH_1M, contextWindowOptions: [{ id: "200k", label: "200k" }] }],
|
|
292
|
+
}))
|
|
293
|
+
expect(model).toBe("claude-haiku-5-5")
|
|
294
|
+
})
|
|
295
|
+
|
|
251
296
|
test("leaves systemPromptOverride untouched when no globalPromptAppend", async () => {
|
|
252
297
|
let captured: { systemPromptOverride?: string } | undefined
|
|
253
298
|
const args = makeArgs({
|
|
@@ -262,7 +307,6 @@ describe("buildSubagentProviderRun – Claude", () => {
|
|
|
262
307
|
sendPrompt: async () => {},
|
|
263
308
|
setModel: async () => {},
|
|
264
309
|
setPermissionMode: async () => {},
|
|
265
|
-
getSupportedCommands: async () => [],
|
|
266
310
|
}
|
|
267
311
|
},
|
|
268
312
|
})
|
|
@@ -297,7 +341,6 @@ describe("buildSubagentProviderRun – Claude", () => {
|
|
|
297
341
|
closed: Promise.resolve(),
|
|
298
342
|
setModel: async () => {},
|
|
299
343
|
setPermissionMode: async () => {},
|
|
300
|
-
getSupportedCommands: async () => [],
|
|
301
344
|
}),
|
|
302
345
|
})
|
|
303
346
|
|
|
@@ -341,7 +384,6 @@ describe("buildSubagentProviderRun – Claude", () => {
|
|
|
341
384
|
sendPrompt: async () => {},
|
|
342
385
|
setModel: async () => {},
|
|
343
386
|
setPermissionMode: async () => {},
|
|
344
|
-
getSupportedCommands: async () => [],
|
|
345
387
|
}
|
|
346
388
|
},
|
|
347
389
|
})
|
|
@@ -506,7 +548,6 @@ describe("buildSubagentProviderRun – keep-alive Claude", () => {
|
|
|
506
548
|
closed: Promise.resolve(),
|
|
507
549
|
setModel: async () => {},
|
|
508
550
|
setPermissionMode: async () => {},
|
|
509
|
-
getSupportedCommands: async () => [],
|
|
510
551
|
pushChannelPrompt: async (text: string) => {
|
|
511
552
|
pushed.push(text)
|
|
512
553
|
pushEvent(makeTextEvent(`r:${text}`))
|
|
@@ -547,7 +588,6 @@ describe("buildSubagentProviderRun – keep-alive Claude", () => {
|
|
|
547
588
|
sendPrompt: async () => {},
|
|
548
589
|
setModel: async () => {},
|
|
549
590
|
setPermissionMode: async () => {},
|
|
550
|
-
getSupportedCommands: async () => [],
|
|
551
591
|
pushChannelPrompt: async (_text: string) => {},
|
|
552
592
|
}
|
|
553
593
|
},
|
|
@@ -571,7 +611,6 @@ describe("buildSubagentProviderRun – keep-alive Claude", () => {
|
|
|
571
611
|
closed: Promise.resolve(),
|
|
572
612
|
setModel: async () => {},
|
|
573
613
|
setPermissionMode: async () => {},
|
|
574
|
-
getSupportedCommands: async () => [],
|
|
575
614
|
}),
|
|
576
615
|
})
|
|
577
616
|
|
|
@@ -599,7 +638,6 @@ describe("buildSubagentProviderRun – keep-alive Claude", () => {
|
|
|
599
638
|
closed: Promise.resolve(),
|
|
600
639
|
setModel: async () => {},
|
|
601
640
|
setPermissionMode: async () => {},
|
|
602
|
-
getSupportedCommands: async () => [],
|
|
603
641
|
}),
|
|
604
642
|
})
|
|
605
643
|
|
|
@@ -3,11 +3,13 @@ import type { HarnessEvent, HarnessToolRequest, HarnessTurn } from "./harness-ty
|
|
|
3
3
|
import type { CodexAppServerManager } from "./codex-app-server"
|
|
4
4
|
import type {
|
|
5
5
|
AgentProvider,
|
|
6
|
+
CustomModelEntry,
|
|
6
7
|
ProviderUsage,
|
|
7
8
|
ResolvedStackBinding,
|
|
8
9
|
Subagent,
|
|
9
10
|
TranscriptEntry,
|
|
10
11
|
} from "../shared/types"
|
|
12
|
+
import { normalizeClaudeContextWindow, resolveClaudeApiModelId } from "../shared/types"
|
|
11
13
|
import {
|
|
12
14
|
buildCodexDeveloperInstructions,
|
|
13
15
|
renderInstructionSections,
|
|
@@ -54,7 +56,6 @@ export interface BuildSubagentProviderRunArgs {
|
|
|
54
56
|
getArmedLoop?: (chatId: string) => ArmedLoopInfo | null
|
|
55
57
|
chatTaskStore?: ChatTaskStorePort
|
|
56
58
|
}) => Promise<ClaudeSessionHandle>
|
|
57
|
-
claudeDriverIsPty?: boolean
|
|
58
59
|
subagentOrchestrator?: SubagentOrchestrator
|
|
59
60
|
delegationContext?: KannaMcpDelegationContext
|
|
60
61
|
getArmedLoop?: (chatId: string) => ArmedLoopInfo | null
|
|
@@ -65,6 +66,7 @@ export interface BuildSubagentProviderRunArgs {
|
|
|
65
66
|
pickOauthToken: () => { token: string; baseUrl?: string } | null
|
|
66
67
|
readOpenRouterKey?: () => Promise<string | null>
|
|
67
68
|
projectId: string
|
|
69
|
+
customModels?: readonly CustomModelEntry[]
|
|
68
70
|
globalPromptAppend?: string
|
|
69
71
|
stackProjects?: ResolvedStackBinding[]
|
|
70
72
|
instructions?: Omit<KannaSystemPromptOptions, "stackProjects" | "globalPromptAppend">
|
|
@@ -85,7 +87,7 @@ export function buildSubagentProviderRun(args: BuildSubagentProviderRunArgs): Pr
|
|
|
85
87
|
systemPrompt: args.subagent.systemPrompt,
|
|
86
88
|
preamble: args.primer,
|
|
87
89
|
maxTurns: args.subagent.maxTurns,
|
|
88
|
-
nativeMaxTurns: args.subagent.provider === "claude"
|
|
90
|
+
nativeMaxTurns: args.subagent.provider === "claude",
|
|
89
91
|
authReady: async () => args.authReady(args.subagent.provider),
|
|
90
92
|
async start(onChunk, onEntry, opts) {
|
|
91
93
|
const initialPrompt = composeInitialPrompt(args.subagent, args.primer, args.userInstruction)
|
|
@@ -128,6 +130,13 @@ export function composeInitialPrompt(
|
|
|
128
130
|
return `(no prior context — proceed based on your system prompt and the @agent/${subagent.name} mention)`
|
|
129
131
|
}
|
|
130
132
|
|
|
133
|
+
function claudeSubagentModelId(args: BuildSubagentProviderRunArgs): string {
|
|
134
|
+
const { model, provider, modelOptions } = args.subagent
|
|
135
|
+
if (provider !== "claude") return model
|
|
136
|
+
const requested = "contextWindow" in modelOptions ? modelOptions.contextWindow : undefined
|
|
137
|
+
return resolveClaudeApiModelId(model, normalizeClaudeContextWindow(model, requested, args.customModels))
|
|
138
|
+
}
|
|
139
|
+
|
|
131
140
|
async function runClaudeSubagent(opts: {
|
|
132
141
|
args: BuildSubagentProviderRunArgs
|
|
133
142
|
initialPrompt: string
|
|
@@ -142,7 +151,7 @@ async function runClaudeSubagent(opts: {
|
|
|
142
151
|
projectId: args.projectId,
|
|
143
152
|
localPath: args.cwd,
|
|
144
153
|
additionalDirectories: args.additionalDirectories,
|
|
145
|
-
model: args
|
|
154
|
+
model: claudeSubagentModelId(args),
|
|
146
155
|
effort: args.subagent.modelOptions?.reasoningEffort,
|
|
147
156
|
planMode: false,
|
|
148
157
|
sessionToken: null,
|
|
@@ -126,7 +126,7 @@ describe("tool-callback durable protocol", () => {
|
|
|
126
126
|
})
|
|
127
127
|
const p = svc.submit(baseInput)
|
|
128
128
|
await new Promise<void>((r) => setTimeout(r, 0))
|
|
129
|
-
await svc.cancelAllForChat("chat-1", "
|
|
129
|
+
await svc.cancelAllForChat("chat-1", "session shutdown")
|
|
130
130
|
const res = await p
|
|
131
131
|
expect(res.status).toBe("canceled")
|
|
132
132
|
})
|
|
@@ -10,7 +10,6 @@ function makeAgent(overrides: Partial<AgentCtrlAgentDep> = {}): AgentCtrlAgentDe
|
|
|
10
10
|
rescheduleAutoContinue: mock(async () => {}),
|
|
11
11
|
cancelAutoContinue: mock(async () => {}),
|
|
12
12
|
runCronCommand: mock(async () => null),
|
|
13
|
-
cancel: mock(async () => {}),
|
|
14
13
|
...overrides,
|
|
15
14
|
}
|
|
16
15
|
}
|
|
@@ -27,14 +26,12 @@ function makeTunnel(overrides: Partial<TunnelGatewayDep> = {}): TunnelGatewayDep
|
|
|
27
26
|
function makeDeps(
|
|
28
27
|
agentOverrides?: Partial<AgentCtrlAgentDep>,
|
|
29
28
|
tunnel?: TunnelGatewayDep | undefined,
|
|
30
|
-
killPty?: AgentCtrlCommandDeps["killPtyInstance"],
|
|
31
29
|
): AgentCtrlCommandDeps & { sent: unknown[]; broadcasts: string[] } {
|
|
32
30
|
const sent: unknown[] = []
|
|
33
31
|
const broadcasts: string[] = []
|
|
34
32
|
return {
|
|
35
33
|
agent: makeAgent(agentOverrides),
|
|
36
34
|
tunnelGateway: tunnel,
|
|
37
|
-
killPtyInstance: killPty,
|
|
38
35
|
send: (envelope) => { sent.push(envelope) },
|
|
39
36
|
broadcastChatAndSidebar: async (chatId) => { broadcasts.push(chatId) },
|
|
40
37
|
sent,
|
|
@@ -128,46 +125,4 @@ describe("handleAgentCtrlCommand", () => {
|
|
|
128
125
|
expect((tunnel.retry as ReturnType<typeof mock>)).toHaveBeenCalledWith("c-7", "t-4")
|
|
129
126
|
expect(deps.broadcasts).toEqual(["c-7"])
|
|
130
127
|
})
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
test("pty.cancel — acks {ok:true} on success", async () => {
|
|
134
|
-
const deps = makeDeps()
|
|
135
|
-
const cmd: ClientCommand = { type: "pty.cancel", chatId: "c-8" }
|
|
136
|
-
const handled = await handleAgentCtrlCommand(deps, cmd, "r8")
|
|
137
|
-
expect(handled).toBe(true)
|
|
138
|
-
expect((deps.agent.cancel as ReturnType<typeof mock>)).toHaveBeenCalledWith("c-8")
|
|
139
|
-
const ack = deps.sent[0] as { result: { ok: boolean } }
|
|
140
|
-
expect(ack.result.ok).toBe(true)
|
|
141
|
-
})
|
|
142
|
-
|
|
143
|
-
test("pty.cancel — acks {ok:false} when agent.cancel throws", async () => {
|
|
144
|
-
const deps = makeDeps({ cancel: mock(async () => { throw new Error("boom") }) })
|
|
145
|
-
const cmd: ClientCommand = { type: "pty.cancel", chatId: "c-9" }
|
|
146
|
-
const handled = await handleAgentCtrlCommand(deps, cmd, "r9")
|
|
147
|
-
expect(handled).toBe(true)
|
|
148
|
-
const ack = deps.sent[0] as { result: { ok: boolean; error: string } }
|
|
149
|
-
expect(ack.result.ok).toBe(false)
|
|
150
|
-
expect(ack.result.error).toBe("boom")
|
|
151
|
-
})
|
|
152
|
-
|
|
153
|
-
test("pty.kill — returns {ok:false} error when killPtyInstance is absent", async () => {
|
|
154
|
-
const deps = makeDeps(undefined, undefined, undefined)
|
|
155
|
-
const cmd: ClientCommand = { type: "pty.kill", chatId: "c-10" }
|
|
156
|
-
const handled = await handleAgentCtrlCommand(deps, cmd, "r10")
|
|
157
|
-
expect(handled).toBe(true)
|
|
158
|
-
const ack = deps.sent[0] as { result: { ok: boolean; error: string } }
|
|
159
|
-
expect(ack.result.ok).toBe(false)
|
|
160
|
-
expect(ack.result.error).toContain("not available")
|
|
161
|
-
})
|
|
162
|
-
|
|
163
|
-
test("pty.kill — delegates to killPtyInstance and acks with its result", async () => {
|
|
164
|
-
const killFn = mock(async (_chatId: string) => ({ ok: true }))
|
|
165
|
-
const deps = makeDeps(undefined, undefined, killFn)
|
|
166
|
-
const cmd: ClientCommand = { type: "pty.kill", chatId: "c-11" }
|
|
167
|
-
const handled = await handleAgentCtrlCommand(deps, cmd, "r11")
|
|
168
|
-
expect(handled).toBe(true)
|
|
169
|
-
expect(killFn).toHaveBeenCalledWith("c-11")
|
|
170
|
-
const ack = deps.sent[0] as { result: { ok: boolean } }
|
|
171
|
-
expect(ack.result.ok).toBe(true)
|
|
172
|
-
})
|
|
173
128
|
})
|
|
@@ -7,7 +7,6 @@ export interface AgentCtrlAgentDep {
|
|
|
7
7
|
rescheduleAutoContinue(chatId: string, scheduleId: string, scheduledAt: number): Promise<void>
|
|
8
8
|
cancelAutoContinue(chatId: string, scheduleId: string, reason: "user" | "chat_deleted"): Promise<void>
|
|
9
9
|
runCronCommand(chatId: string, result: import("../shared/cron/types").CronParseResult): Promise<string | null>
|
|
10
|
-
cancel(chatId: string): Promise<void>
|
|
11
10
|
}
|
|
12
11
|
|
|
13
12
|
export interface TunnelGatewayDep {
|
|
@@ -19,7 +18,6 @@ export interface TunnelGatewayDep {
|
|
|
19
18
|
export interface AgentCtrlCommandDeps {
|
|
20
19
|
agent: AgentCtrlAgentDep
|
|
21
20
|
tunnelGateway: TunnelGatewayDep | undefined
|
|
22
|
-
killPtyInstance: ((chatId: string) => Promise<{ ok: boolean; error?: string }>) | undefined
|
|
23
21
|
send: (envelope: ServerEnvelope) => void
|
|
24
22
|
broadcastChatAndSidebar: (chatId: string) => Promise<void>
|
|
25
23
|
}
|
|
@@ -30,7 +28,7 @@ export async function handleAgentCtrlCommand(
|
|
|
30
28
|
command: ClientCommand,
|
|
31
29
|
id: string,
|
|
32
30
|
): Promise<boolean> {
|
|
33
|
-
const { agent, tunnelGateway,
|
|
31
|
+
const { agent, tunnelGateway, send, broadcastChatAndSidebar } = deps
|
|
34
32
|
|
|
35
33
|
switch (command.type) {
|
|
36
34
|
case "autoContinue.accept": {
|
|
@@ -98,34 +96,6 @@ export async function handleAgentCtrlCommand(
|
|
|
98
96
|
await broadcastChatAndSidebar(command.chatId)
|
|
99
97
|
return true
|
|
100
98
|
}
|
|
101
|
-
case "pty.cancel": {
|
|
102
|
-
try {
|
|
103
|
-
await agent.cancel(command.chatId)
|
|
104
|
-
send({ v: PROTOCOL_VERSION, type: "ack", id, result: { ok: true } })
|
|
105
|
-
} catch (err) {
|
|
106
|
-
send({
|
|
107
|
-
v: PROTOCOL_VERSION,
|
|
108
|
-
type: "ack",
|
|
109
|
-
id,
|
|
110
|
-
result: { ok: false, error: err instanceof Error ? err.message : String(err) },
|
|
111
|
-
})
|
|
112
|
-
}
|
|
113
|
-
return true
|
|
114
|
-
}
|
|
115
|
-
case "pty.kill": {
|
|
116
|
-
if (!killPtyInstance) {
|
|
117
|
-
send({
|
|
118
|
-
v: PROTOCOL_VERSION,
|
|
119
|
-
type: "ack",
|
|
120
|
-
id,
|
|
121
|
-
result: { ok: false, error: "pty kill not available" },
|
|
122
|
-
})
|
|
123
|
-
return true
|
|
124
|
-
}
|
|
125
|
-
const result = await killPtyInstance(command.chatId)
|
|
126
|
-
send({ v: PROTOCOL_VERSION, type: "ack", id, result })
|
|
127
|
-
return true
|
|
128
|
-
}
|
|
129
99
|
default:
|
|
130
100
|
return false
|
|
131
101
|
}
|