@cuongtran001/kanna 1.2.0 → 1.3.1
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/dist/client/assets/{arc-BLk1CZKB.js → arc-DXtN3EWJ.js} +1 -1
- package/dist/client/assets/{architectureDiagram-3BPJPVTR-B01ZQYte.js → architectureDiagram-3BPJPVTR-CIGqW1z-.js} +1 -1
- package/dist/client/assets/{blockDiagram-GPEHLZMM-diL6a3cq.js → blockDiagram-GPEHLZMM-BafNS_H6.js} +1 -1
- package/dist/client/assets/{c4Diagram-AAUBKEIU-BY1verL8.js → c4Diagram-AAUBKEIU-YdAgRSis.js} +1 -1
- package/dist/client/assets/channel-BEYTrUAm.js +1 -0
- package/dist/client/assets/{chunk-2J33WTMH-psdZuajw.js → chunk-2J33WTMH-eiP5y5G-.js} +1 -1
- package/dist/client/assets/{chunk-4BX2VUAB-CrwQVBc5.js → chunk-4BX2VUAB-CvLGzPMF.js} +1 -1
- package/dist/client/assets/{chunk-55IACEB6-9-aj4jKO.js → chunk-55IACEB6-Y-S9Fb8A.js} +1 -1
- package/dist/client/assets/{chunk-727SXJPM-1FNK9_nB.js → chunk-727SXJPM-6sJH1RI9.js} +1 -1
- package/dist/client/assets/{chunk-AQP2D5EJ-DFhvd_Ek.js → chunk-AQP2D5EJ-hV1r_IXO.js} +1 -1
- package/dist/client/assets/{chunk-FMBD7UC4-B7MLrDa7.js → chunk-FMBD7UC4-CC3L28S5.js} +1 -1
- package/dist/client/assets/{chunk-ND2GUHAM-D4Df05QE.js → chunk-ND2GUHAM-BZ3czhdH.js} +1 -1
- package/dist/client/assets/{chunk-QZHKN3VN-Iq17Dznm.js → chunk-QZHKN3VN-Bjyd6Yr-.js} +1 -1
- package/dist/client/assets/classDiagram-4FO5ZUOK-CvnF5Ud6.js +1 -0
- package/dist/client/assets/classDiagram-v2-Q7XG4LA2-CvnF5Ud6.js +1 -0
- package/dist/client/assets/{cose-bilkent-S5V4N54A-BmaZhtXy.js → cose-bilkent-S5V4N54A-CfxdDghE.js} +1 -1
- package/dist/client/assets/{dagre-BM42HDAG-BCmpfnfP.js → dagre-BM42HDAG-B8CddsbB.js} +1 -1
- package/dist/client/assets/{diagram-2AECGRRQ-BIEcKowS.js → diagram-2AECGRRQ-F0yS0fVB.js} +1 -1
- package/dist/client/assets/{diagram-5GNKFQAL-CRBABBfv.js → diagram-5GNKFQAL-cW0i0qlh.js} +1 -1
- package/dist/client/assets/{diagram-KO2AKTUF-BRxUk16N.js → diagram-KO2AKTUF-BzAKMeJT.js} +1 -1
- package/dist/client/assets/{diagram-LMA3HP47-DPBs4xl1.js → diagram-LMA3HP47-qdC4XqIl.js} +1 -1
- package/dist/client/assets/{diagram-OG6HWLK6-BE9rgVv8.js → diagram-OG6HWLK6-CGcpi6Dh.js} +1 -1
- package/dist/client/assets/{erDiagram-TEJ5UH35-D3vgA1xJ.js → erDiagram-TEJ5UH35-CxNTDicM.js} +1 -1
- package/dist/client/assets/{flowDiagram-I6XJVG4X-Cgu-8XXC.js → flowDiagram-I6XJVG4X-qkGa5TqY.js} +1 -1
- package/dist/client/assets/{ganttDiagram-6RSMTGT7-CgjXVyTR.js → ganttDiagram-6RSMTGT7-D9mVPoQi.js} +1 -1
- package/dist/client/assets/{gitGraphDiagram-PVQCEYII-zD5D8B-i.js → gitGraphDiagram-PVQCEYII-DA0grAXe.js} +1 -1
- package/dist/client/assets/{index-DBTdhMbJ.js → index-BqUY1Bm4.js} +1 -1
- package/dist/client/assets/index-CLsOWXff.css +32 -0
- package/dist/client/assets/{index-zQLLeAfl.js → index-TU1k6ov6.js} +253 -243
- package/dist/client/assets/{infoDiagram-5YYISTIA-BaFHqT4u.js → infoDiagram-5YYISTIA-CGljtLs3.js} +1 -1
- package/dist/client/assets/{ishikawaDiagram-YF4QCWOH-2yAC1br2.js → ishikawaDiagram-YF4QCWOH-CtC0xrmG.js} +1 -1
- package/dist/client/assets/{journeyDiagram-JHISSGLW-DacQ-ND5.js → journeyDiagram-JHISSGLW-URLnVZeN.js} +1 -1
- package/dist/client/assets/{kanban-definition-UN3LZRKU-Qlbwm3z4.js → kanban-definition-UN3LZRKU-C2Q6aqQh.js} +1 -1
- package/dist/client/assets/{linear-DeafqpgO.js → linear-ByKHtsIx.js} +1 -1
- package/dist/client/assets/{mermaid.core-KYiCPJDj.js → mermaid.core-Dl_f2KLc.js} +4 -4
- package/dist/client/assets/{mindmap-definition-RKZ34NQL-B1V-T6d3.js → mindmap-definition-RKZ34NQL-CaJ9IDLI.js} +1 -1
- package/dist/client/assets/{pieDiagram-4H26LBE5-DOF9OFRu.js → pieDiagram-4H26LBE5-mmYaf2Oa.js} +1 -1
- package/dist/client/assets/{quadrantDiagram-W4KKPZXB-lH1mB7ko.js → quadrantDiagram-W4KKPZXB-B_PO6OCC.js} +1 -1
- package/dist/client/assets/{requirementDiagram-4Y6WPE33-BaTsJ_sS.js → requirementDiagram-4Y6WPE33-M4huwJt1.js} +1 -1
- package/dist/client/assets/{sankeyDiagram-5OEKKPKP-Pfxib5iI.js → sankeyDiagram-5OEKKPKP-B0hb1sWh.js} +1 -1
- package/dist/client/assets/{sequenceDiagram-3UESZ5HK-BQMEKkBC.js → sequenceDiagram-3UESZ5HK-BAqSptnA.js} +1 -1
- package/dist/client/assets/{stateDiagram-AJRCARHV-CuldNnzp.js → stateDiagram-AJRCARHV-DTNkvPi5.js} +1 -1
- package/dist/client/assets/stateDiagram-v2-BHNVJYJU-CVN3oGTF.js +1 -0
- package/dist/client/assets/{timeline-definition-PNZ67QCA-DP7u0YAg.js → timeline-definition-PNZ67QCA-MgAjHz8Q.js} +1 -1
- package/dist/client/assets/{vennDiagram-CIIHVFJN-QlrjpNeQ.js → vennDiagram-CIIHVFJN-DmKoO51W.js} +1 -1
- package/dist/client/assets/{wardley-L42UT6IY-BqdJ_sAM.js → wardley-L42UT6IY-CWcyHnYs.js} +1 -1
- package/dist/client/assets/{wardleyDiagram-YWT4CUSO-Wi3kaQ5X.js → wardleyDiagram-YWT4CUSO-BGIGmFP9.js} +1 -1
- package/dist/client/assets/{xychartDiagram-2RQKCTM6-D-KXMyCG.js → xychartDiagram-2RQKCTM6-D20YIvEl.js} +1 -1
- package/dist/client/index.html +2 -2
- package/package.json +1 -1
- package/src/server/agent-coordinator-types.ts +158 -0
- package/src/server/agent-coordinator.ts +919 -0
- package/src/server/agent-deps-builders.ts +425 -0
- package/src/server/agent.oauth-rotation.test.ts +101 -0
- package/src/server/agent.test.ts +24 -8
- package/src/server/agent.ts +35 -4963
- package/src/server/auto-continue/read-model.test.ts +2 -2
- package/src/server/auto-continue/read-model.ts +4 -1
- package/src/server/claude-autocontinue-commands.test.ts +494 -0
- package/src/server/claude-autocontinue-commands.ts +256 -0
- package/src/server/claude-cancel-handler.test.ts +472 -0
- package/src/server/claude-cancel-handler.ts +241 -0
- package/src/server/claude-chat-management.test.ts +395 -0
- package/src/server/claude-chat-management.ts +251 -0
- package/src/server/claude-harness-stream.test.ts +273 -0
- package/src/server/claude-harness-stream.ts +158 -0
- package/src/server/claude-loop-orch-commands.test.ts +579 -0
- package/src/server/claude-loop-orch-commands.ts +466 -0
- package/src/server/claude-message-normalizer.test.ts +213 -0
- package/src/server/claude-message-normalizer.ts +436 -0
- package/src/server/claude-prompt-helpers.test.ts +248 -0
- package/src/server/claude-prompt-helpers.ts +139 -0
- package/src/server/claude-sdk-queue.test.ts +169 -0
- package/src/server/claude-sdk-queue.ts +82 -0
- package/src/server/claude-send-command.test.ts +414 -0
- package/src/server/claude-send-command.ts +420 -0
- package/src/server/claude-session-config-helpers.test.ts +279 -0
- package/src/server/claude-session-config-helpers.ts +151 -0
- package/src/server/claude-session-config.test.ts +250 -0
- package/src/server/claude-session-config.ts +159 -0
- package/src/server/claude-session-error-handler.test.ts +498 -0
- package/src/server/claude-session-error-handler.ts +361 -0
- package/src/server/claude-session-lifecycle.test.ts +669 -0
- package/src/server/claude-session-lifecycle.ts +283 -0
- package/src/server/claude-session-rebuild.test.ts +224 -0
- package/src/server/claude-session-rebuild.ts +101 -0
- package/src/server/claude-session-runner.test.ts +523 -0
- package/src/server/claude-session-runner.ts +521 -0
- package/src/server/claude-session-spawner.test.ts +401 -0
- package/src/server/claude-session-spawner.ts +401 -0
- package/src/server/claude-session-start.test.ts +205 -0
- package/src/server/claude-session-start.ts +277 -0
- package/src/server/claude-session-state-queries.test.ts +344 -0
- package/src/server/claude-session-state-queries.ts +160 -0
- package/src/server/claude-session-state.ts +104 -0
- package/src/server/claude-slash-commands.test.ts +277 -0
- package/src/server/claude-slash-commands.ts +234 -0
- package/src/server/claude-spawn-helpers.test.ts +221 -0
- package/src/server/claude-spawn-helpers.ts +179 -0
- package/src/server/claude-steer-log.test.ts +243 -0
- package/src/server/claude-steer-log.ts +58 -0
- package/src/server/claude-subagent-tool-response.test.ts +213 -0
- package/src/server/claude-subagent-tool-response.ts +165 -0
- package/src/server/claude-subagent-wiring.test.ts +263 -0
- package/src/server/claude-subagent-wiring.ts +324 -0
- package/src/server/claude-tool-respond.test.ts +321 -0
- package/src/server/claude-tool-respond.ts +142 -0
- package/src/server/claude-turn-runner.test.ts +310 -0
- package/src/server/claude-turn-runner.ts +228 -0
- package/src/server/claude-turn-starter.test.ts +266 -0
- package/src/server/claude-turn-starter.ts +566 -0
- package/src/server/claude-usage-math.test.ts +169 -0
- package/src/server/claude-usage-math.ts +132 -0
- package/src/server/diff-store-branch-ops.adapter.test.ts +332 -0
- package/src/server/diff-store-branch-ops.adapter.ts +602 -0
- package/src/server/diff-store-commit-ops.adapter.test.ts +358 -0
- package/src/server/diff-store-commit-ops.adapter.ts +258 -0
- package/src/server/diff-store-errors.ts +85 -0
- package/src/server/diff-store-file-ops.adapter.ts +212 -0
- package/src/server/diff-store-git-branch.adapter.ts +686 -0
- package/src/server/diff-store-io.adapter.ts +27 -0
- package/src/server/diff-store-parse.test.ts +212 -0
- package/src/server/diff-store-parse.ts +122 -0
- package/src/server/diff-store-state.ts +82 -0
- package/src/server/diff-store.ts +76 -1854
- package/src/server/event-store-apply.test.ts +280 -0
- package/src/server/event-store-apply.ts +132 -0
- package/src/server/event-store-chat-lifecycle.test.ts +408 -0
- package/src/server/event-store-chat-lifecycle.ts +441 -0
- package/src/server/event-store-entity-write.ts +356 -0
- package/src/server/event-store-helpers.test.ts +230 -0
- package/src/server/event-store-helpers.ts +235 -0
- package/src/server/event-store-init.ts +276 -0
- package/src/server/event-store-messages.adapter.test.ts +365 -0
- package/src/server/event-store-messages.adapter.ts +213 -0
- package/src/server/event-store-orch-subscription.ts +124 -0
- package/src/server/event-store-orch.test.ts +462 -0
- package/src/server/event-store-orch.ts +312 -0
- package/src/server/event-store-peripheral-events.adapter.test.ts +254 -0
- package/src/server/event-store-peripheral-events.adapter.ts +105 -0
- package/src/server/event-store-snapshot.test.ts +597 -0
- package/src/server/event-store-snapshot.ts +658 -0
- package/src/server/event-store-subagent.test.ts +408 -0
- package/src/server/event-store-subagent.ts +198 -0
- package/src/server/event-store-tool-requests.test.ts +210 -0
- package/src/server/event-store-tool-requests.ts +131 -0
- package/src/server/event-store-transcript-write.adapter.ts +285 -0
- package/src/server/event-store-write-ops.test.ts +554 -0
- package/src/server/event-store-write-ops.ts +529 -0
- package/src/server/event-store.test.ts +35 -0
- package/src/server/event-store.ts +251 -2284
- package/src/server/events.ts +3 -0
- package/src/server/harness-types.ts +15 -1
- package/src/server/oauth-errors.ts +19 -0
- package/src/server/read-models.ts +26 -1
- package/src/server/subagent-orchestrator.ts +3 -0
- package/src/server/ws-router-agent-ctrl.test.ts +187 -0
- package/src/server/ws-router-agent-ctrl.ts +137 -0
- package/src/server/ws-router-broadcast.ts +488 -0
- package/src/server/ws-router-chat.test.ts +461 -0
- package/src/server/ws-router-chat.ts +279 -0
- package/src/server/ws-router-defaults.test.ts +283 -0
- package/src/server/ws-router-defaults.ts +434 -0
- package/src/server/ws-router-diff.test.ts +167 -0
- package/src/server/ws-router-diff.ts +250 -0
- package/src/server/ws-router-envelope.ts +329 -0
- package/src/server/ws-router-misc.test.ts +422 -0
- package/src/server/ws-router-misc.ts +265 -0
- package/src/server/ws-router-orch.test.ts +190 -0
- package/src/server/ws-router-orch.ts +93 -0
- package/src/server/ws-router-project.test.ts +403 -0
- package/src/server/ws-router-project.ts +256 -0
- package/src/server/ws-router-push.test.ts +193 -0
- package/src/server/ws-router-push.ts +120 -0
- package/src/server/ws-router-settings.test.ts +173 -0
- package/src/server/ws-router-settings.ts +416 -0
- package/src/server/ws-router-skills.test.ts +194 -0
- package/src/server/ws-router-skills.ts +204 -0
- package/src/server/ws-router-utils.ts +224 -0
- package/src/server/ws-router.ts +339 -2254
- package/src/shared/app-settings-types.ts +365 -0
- package/src/shared/core-types.ts +27 -0
- package/src/shared/git-diff-types.ts +165 -0
- package/src/shared/loop-progress.test.ts +106 -0
- package/src/shared/loop-progress.ts +88 -0
- package/src/shared/mcp-types.ts +105 -0
- package/src/shared/provider-model-types.ts +437 -0
- package/src/shared/subagent-types.ts +198 -0
- package/src/shared/tool-call-types.ts +314 -0
- package/src/shared/transcript-types.ts +270 -0
- package/src/shared/types.ts +27 -1668
- package/dist/client/assets/channel-BMkhjiLI.js +0 -1
- package/dist/client/assets/classDiagram-4FO5ZUOK-C5Kg1XOD.js +0 -1
- package/dist/client/assets/classDiagram-v2-Q7XG4LA2-C5Kg1XOD.js +0 -1
- package/dist/client/assets/index-H6C-f2Vp.css +0 -32
- package/dist/client/assets/stateDiagram-v2-BHNVJYJU-Ca2U11af.js +0 -1
|
@@ -0,0 +1,566 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Turn spawning pipeline for AgentCoordinator.
|
|
3
|
+
*
|
|
4
|
+
* Extracted from agent.ts to reduce file size. Contains the two adjacent
|
|
5
|
+
* private methods that form the "spawn and route" logic:
|
|
6
|
+
* - startTurnForChat — validates state, appends user prompt, records turn_started
|
|
7
|
+
* - startTurnAfterTurnStarted — picks provider, spawns session, routes to codec
|
|
8
|
+
*
|
|
9
|
+
* All IO is delegated through the StartTurnDeps interface; this file is pure
|
|
10
|
+
* orchestration and therefore does NOT need an `.adapter.ts` suffix.
|
|
11
|
+
*/
|
|
12
|
+
import type {
|
|
13
|
+
AgentProvider,
|
|
14
|
+
ChatAttachment,
|
|
15
|
+
ResolvedStackBinding,
|
|
16
|
+
Subagent,
|
|
17
|
+
TranscriptEntry,
|
|
18
|
+
ClaudeDriverPreference,
|
|
19
|
+
} from "../shared/types"
|
|
20
|
+
import { isCodexReasoningEffort, providerUsesSdkSession } from "../shared/types"
|
|
21
|
+
import { isClaudeSdkProvider } from "./provider-catalog"
|
|
22
|
+
import type { ChatRecord, ProjectRecord } from "./events"
|
|
23
|
+
import type { ActiveTurn, ClaudeSessionState } from "./claude-session-state"
|
|
24
|
+
import type { AnyValue } from "../shared/errors"
|
|
25
|
+
import type { HarnessTurn, HarnessToolRequest } from "./harness-types"
|
|
26
|
+
import type { EventStore } from "./event-store"
|
|
27
|
+
import type { CodexAppServerManager } from "./codex-app-server"
|
|
28
|
+
import type { SubagentOrchestrator } from "./subagent-orchestrator"
|
|
29
|
+
import { OAuthPoolUnavailableError } from "./oauth-errors"
|
|
30
|
+
import { buildPromptText } from "./claude-prompt-helpers"
|
|
31
|
+
import { buildHistoryPrimer, shouldInjectPrimer } from "./history-primer"
|
|
32
|
+
import { fallbackTitleFromMessage } from "./generate-title"
|
|
33
|
+
import { parseMentions, type ParsedMention } from "./mention-parser"
|
|
34
|
+
import { resolveSpawnPaths, resolveStackProjects } from "./claude-session-config"
|
|
35
|
+
import { timestamped } from "./claude-message-normalizer"
|
|
36
|
+
import {
|
|
37
|
+
logClaudeSteer,
|
|
38
|
+
logSendToStartingProfile,
|
|
39
|
+
type SendToStartingProfile,
|
|
40
|
+
} from "./claude-steer-log"
|
|
41
|
+
import { log } from "../shared/log"
|
|
42
|
+
import { LOG_PREFIX } from "../shared/branding"
|
|
43
|
+
|
|
44
|
+
// ---------------------------------------------------------------------------
|
|
45
|
+
// Dep types
|
|
46
|
+
// ---------------------------------------------------------------------------
|
|
47
|
+
|
|
48
|
+
/** Args for the inner startClaudeTurn dep — mirrors the private method signature. */
|
|
49
|
+
export interface StartClaudeTurnArgs {
|
|
50
|
+
chatId: string
|
|
51
|
+
projectId: string
|
|
52
|
+
localPath: string
|
|
53
|
+
additionalDirectories?: string[]
|
|
54
|
+
stackProjects?: ResolvedStackBinding[]
|
|
55
|
+
model: string
|
|
56
|
+
effort?: string
|
|
57
|
+
planMode: boolean
|
|
58
|
+
sessionToken: string | null
|
|
59
|
+
forkSession: boolean
|
|
60
|
+
onToolRequest: (request: HarnessToolRequest) => Promise<AnyValue>
|
|
61
|
+
provider: AgentProvider
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/** Args for recreateActiveTurnFromSession dep — mirrors the private method signature. */
|
|
65
|
+
export interface RecreateActiveTurnArgs {
|
|
66
|
+
chatId: string
|
|
67
|
+
provider: AgentProvider
|
|
68
|
+
model: string
|
|
69
|
+
effort?: string
|
|
70
|
+
serviceTier?: "fast"
|
|
71
|
+
planMode: boolean
|
|
72
|
+
clientTraceId?: string
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/** AppSettings snapshot fields consumed by this module. */
|
|
76
|
+
export interface StartTurnAppSettings {
|
|
77
|
+
globalPromptAppend?: string
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* All AgentCoordinator fields / methods accessed by the turn spawning pipeline.
|
|
82
|
+
* Passed as a single deps argument to the two extracted functions.
|
|
83
|
+
*/
|
|
84
|
+
export interface StartTurnDeps {
|
|
85
|
+
// Maps (mutable — methods read and write these)
|
|
86
|
+
activeTurns: Map<string, ActiveTurn>
|
|
87
|
+
claudeSessions: Map<string, ClaudeSessionState>
|
|
88
|
+
drainingStreams: Map<string, { turn: HarnessTurn }>
|
|
89
|
+
mentionedSubagentIdsByChat: Map<string, string[]>
|
|
90
|
+
|
|
91
|
+
// Service objects
|
|
92
|
+
store: EventStore
|
|
93
|
+
codexManager: CodexAppServerManager
|
|
94
|
+
subagentOrchestrator: Pick<SubagentOrchestrator, "clearChatCancellation">
|
|
95
|
+
|
|
96
|
+
// Callbacks for private AgentCoordinator methods
|
|
97
|
+
clearDrainingStream: (chatId: string) => void
|
|
98
|
+
emitStateChange: (chatId: string, options?: { immediate?: boolean }) => void
|
|
99
|
+
resolveClaudeDriverPreference: () => ClaudeDriverPreference
|
|
100
|
+
getSubagents: () => Subagent[]
|
|
101
|
+
getAppSettingsSnapshot: () => StartTurnAppSettings
|
|
102
|
+
/** Fired in background (return value discarded). */
|
|
103
|
+
generateTitleInBackground: (chatId: string, content: string, localPath: string, optimisticTitle: string) => Promise<void>
|
|
104
|
+
recreateActiveTurnFromSession: (args: RecreateActiveTurnArgs) => ActiveTurn | undefined
|
|
105
|
+
startClaudeTurn: (args: StartClaudeTurnArgs) => Promise<HarnessTurn>
|
|
106
|
+
findLastUserMessageId: (chatId: string) => string | null
|
|
107
|
+
/** Fires the runTurn loop (return value discarded). */
|
|
108
|
+
runTurn: (active: ActiveTurn) => void
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// ---------------------------------------------------------------------------
|
|
112
|
+
// Arg types (mirror the private method signatures)
|
|
113
|
+
// ---------------------------------------------------------------------------
|
|
114
|
+
|
|
115
|
+
export interface StartTurnForChatArgs {
|
|
116
|
+
chatId: string
|
|
117
|
+
provider: AgentProvider
|
|
118
|
+
content: string
|
|
119
|
+
attachments: ChatAttachment[]
|
|
120
|
+
model: string
|
|
121
|
+
effort?: string
|
|
122
|
+
serviceTier?: "fast"
|
|
123
|
+
planMode: boolean
|
|
124
|
+
appendUserPrompt: boolean
|
|
125
|
+
steered?: boolean
|
|
126
|
+
autoContinue?: { scheduleId: string }
|
|
127
|
+
userClearedContext?: boolean
|
|
128
|
+
profile?: SendToStartingProfile | null
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
interface StartTurnAfterTurnStartedCtx {
|
|
132
|
+
args: StartTurnForChatArgs
|
|
133
|
+
chat: ChatRecord
|
|
134
|
+
project: ProjectRecord
|
|
135
|
+
existingMessages: TranscriptEntry[]
|
|
136
|
+
shouldGenerateTitle: boolean
|
|
137
|
+
optimisticTitle: string | null
|
|
138
|
+
appendedUserMessageId: string | null
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
// ---------------------------------------------------------------------------
|
|
142
|
+
// Extracted functions
|
|
143
|
+
// ---------------------------------------------------------------------------
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* Extracted from AgentCoordinator.startTurnForChat.
|
|
147
|
+
*
|
|
148
|
+
* Validates pre-conditions, appends the user prompt, records turn_started,
|
|
149
|
+
* then delegates to startTurnAfterTurnStarted.
|
|
150
|
+
*/
|
|
151
|
+
export async function startTurnForChat(
|
|
152
|
+
deps: StartTurnDeps,
|
|
153
|
+
args: StartTurnForChatArgs,
|
|
154
|
+
): Promise<void> {
|
|
155
|
+
logSendToStartingProfile(args.profile, "start_turn.begin", {
|
|
156
|
+
chatId: args.chatId,
|
|
157
|
+
provider: args.provider,
|
|
158
|
+
appendUserPrompt: args.appendUserPrompt,
|
|
159
|
+
planMode: args.planMode,
|
|
160
|
+
})
|
|
161
|
+
|
|
162
|
+
// Close any lingering draining stream before starting a new turn.
|
|
163
|
+
const draining = deps.drainingStreams.get(args.chatId)
|
|
164
|
+
if (draining) {
|
|
165
|
+
draining.turn.close()
|
|
166
|
+
deps.clearDrainingStream(args.chatId)
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
// A new user turn implicitly clears any prior cancellation marker —
|
|
170
|
+
// otherwise a Stop-then-resend cycle wedges every delegate_subagent
|
|
171
|
+
// call in this chat with "Chat cancelled before run started" until
|
|
172
|
+
// process restart. Mirrors the clear already done by
|
|
173
|
+
// runMentionsForUserMessage for the @mention path.
|
|
174
|
+
deps.subagentOrchestrator.clearChatCancellation(args.chatId)
|
|
175
|
+
|
|
176
|
+
const chat = deps.store.requireChat(args.chatId)
|
|
177
|
+
if (deps.activeTurns.has(args.chatId)) {
|
|
178
|
+
throw new Error("Chat is already running")
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
if (chat.provider !== args.provider) {
|
|
182
|
+
await deps.store.setChatProvider(args.chatId, args.provider)
|
|
183
|
+
logSendToStartingProfile(args.profile, "start_turn.provider_set", {
|
|
184
|
+
chatId: args.chatId,
|
|
185
|
+
provider: args.provider,
|
|
186
|
+
})
|
|
187
|
+
}
|
|
188
|
+
await deps.store.setPlanMode(args.chatId, args.planMode)
|
|
189
|
+
logSendToStartingProfile(args.profile, "start_turn.plan_mode_set", {
|
|
190
|
+
chatId: args.chatId,
|
|
191
|
+
planMode: args.planMode,
|
|
192
|
+
})
|
|
193
|
+
|
|
194
|
+
const existingMessages = deps.store.getMessages(args.chatId)
|
|
195
|
+
const shouldGenerateTitle = args.appendUserPrompt && chat.title === "New Chat" && existingMessages.length === 0
|
|
196
|
+
const optimisticTitle = shouldGenerateTitle ? fallbackTitleFromMessage(args.content) : null
|
|
197
|
+
|
|
198
|
+
if (optimisticTitle) {
|
|
199
|
+
await deps.store.renameChat(args.chatId, optimisticTitle)
|
|
200
|
+
logSendToStartingProfile(args.profile, "start_turn.optimistic_title_set", {
|
|
201
|
+
chatId: args.chatId,
|
|
202
|
+
title: optimisticTitle,
|
|
203
|
+
})
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
const project = deps.store.getProject(chat.projectId)
|
|
207
|
+
if (!project) {
|
|
208
|
+
throw new Error("Project not found")
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
let appendedUserMessageId: string | null = null
|
|
212
|
+
if (args.appendUserPrompt) {
|
|
213
|
+
const parsedMentions = parseMentions(args.content, deps.getSubagents())
|
|
214
|
+
const subagentMentions = parsedMentions
|
|
215
|
+
.filter((mention): mention is Extract<ParsedMention, { kind: "subagent" }> => mention.kind === "subagent")
|
|
216
|
+
.map((mention) => ({ subagentId: mention.subagentId, raw: mention.raw }))
|
|
217
|
+
deps.mentionedSubagentIdsByChat.set(
|
|
218
|
+
args.chatId,
|
|
219
|
+
subagentMentions.map((m) => m.subagentId),
|
|
220
|
+
)
|
|
221
|
+
const unknownSubagentMentions = parsedMentions
|
|
222
|
+
.filter((mention): mention is Extract<ParsedMention, { kind: "unknown-subagent" }> => mention.kind === "unknown-subagent")
|
|
223
|
+
.map((mention) => ({ name: mention.name, raw: mention.raw }))
|
|
224
|
+
const userPromptEntry = timestamped(
|
|
225
|
+
{
|
|
226
|
+
kind: "user_prompt",
|
|
227
|
+
content: args.content,
|
|
228
|
+
attachments: args.attachments,
|
|
229
|
+
steered: args.steered,
|
|
230
|
+
autoContinue: args.autoContinue,
|
|
231
|
+
...(subagentMentions.length > 0 ? { subagentMentions } : {}),
|
|
232
|
+
...(unknownSubagentMentions.length > 0 ? { unknownSubagentMentions } : {}),
|
|
233
|
+
},
|
|
234
|
+
Date.now()
|
|
235
|
+
)
|
|
236
|
+
await deps.store.appendMessage(args.chatId, userPromptEntry)
|
|
237
|
+
appendedUserMessageId = userPromptEntry._id
|
|
238
|
+
logSendToStartingProfile(args.profile, "start_turn.user_prompt_appended", {
|
|
239
|
+
chatId: args.chatId,
|
|
240
|
+
entryId: userPromptEntry._id,
|
|
241
|
+
})
|
|
242
|
+
}
|
|
243
|
+
await deps.store.recordTurnStarted(args.chatId, {
|
|
244
|
+
provider: args.provider,
|
|
245
|
+
model: args.model,
|
|
246
|
+
...(args.effort !== undefined ? { effort: args.effort } : {}),
|
|
247
|
+
...(args.serviceTier !== undefined ? { serviceTier: args.serviceTier } : {}),
|
|
248
|
+
planMode: args.planMode,
|
|
249
|
+
driver: deps.resolveClaudeDriverPreference(),
|
|
250
|
+
})
|
|
251
|
+
logSendToStartingProfile(args.profile, "start_turn.turn_started_recorded", {
|
|
252
|
+
chatId: args.chatId,
|
|
253
|
+
})
|
|
254
|
+
|
|
255
|
+
try {
|
|
256
|
+
await startTurnAfterTurnStarted(deps, {
|
|
257
|
+
args,
|
|
258
|
+
chat,
|
|
259
|
+
project,
|
|
260
|
+
existingMessages,
|
|
261
|
+
shouldGenerateTitle,
|
|
262
|
+
optimisticTitle,
|
|
263
|
+
appendedUserMessageId,
|
|
264
|
+
})
|
|
265
|
+
} catch (error) {
|
|
266
|
+
const message = error instanceof Error ? error.message : String(error)
|
|
267
|
+
const isOAuthRefusal = error instanceof OAuthPoolUnavailableError
|
|
268
|
+
log.error(`${LOG_PREFIX} startTurnForChat failed after turn_started`, {
|
|
269
|
+
chatId: args.chatId,
|
|
270
|
+
provider: args.provider,
|
|
271
|
+
model: args.model,
|
|
272
|
+
planMode: args.planMode,
|
|
273
|
+
error: message,
|
|
274
|
+
stack: error instanceof Error ? error.stack : undefined,
|
|
275
|
+
kind: isOAuthRefusal ? "oauth_pool_unavailable" : "unknown",
|
|
276
|
+
})
|
|
277
|
+
// OAuth-pool refusal: persist the formatted refusal (with chat-link
|
|
278
|
+
// markdown produced by `buildPoolUnavailableMessage`) as a `result`
|
|
279
|
+
// transcript entry so the UI's transcript renders it inline and
|
|
280
|
+
// durably, instead of relying on the ephemeral commandError banner
|
|
281
|
+
// that gets wiped by the next chat snapshot tick.
|
|
282
|
+
if (isOAuthRefusal) {
|
|
283
|
+
try {
|
|
284
|
+
await deps.store.appendMessage(
|
|
285
|
+
args.chatId,
|
|
286
|
+
timestamped({
|
|
287
|
+
kind: "result",
|
|
288
|
+
subtype: "error",
|
|
289
|
+
isError: true,
|
|
290
|
+
durationMs: 0,
|
|
291
|
+
result: message,
|
|
292
|
+
})
|
|
293
|
+
)
|
|
294
|
+
} catch (appendErr) {
|
|
295
|
+
log.error(`${LOG_PREFIX} append refusal result entry failed`, {
|
|
296
|
+
chatId: args.chatId,
|
|
297
|
+
appendErr: appendErr instanceof Error ? appendErr.message : String(appendErr),
|
|
298
|
+
})
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
try {
|
|
302
|
+
await deps.store.recordTurnFailed(args.chatId, message)
|
|
303
|
+
} catch (recordErr) {
|
|
304
|
+
log.error(`${LOG_PREFIX} recordTurnFailed also failed`, {
|
|
305
|
+
chatId: args.chatId,
|
|
306
|
+
recordErr: recordErr instanceof Error ? recordErr.message : String(recordErr),
|
|
307
|
+
})
|
|
308
|
+
}
|
|
309
|
+
deps.activeTurns.delete(args.chatId)
|
|
310
|
+
deps.emitStateChange(args.chatId, { immediate: true })
|
|
311
|
+
// Swallow refusals — the transcript entry above is the user-facing
|
|
312
|
+
// signal. Re-throwing would surface a transient commandError banner
|
|
313
|
+
// that races with snapshot ticks and visibly flickers (see #235).
|
|
314
|
+
if (isOAuthRefusal) {
|
|
315
|
+
return
|
|
316
|
+
}
|
|
317
|
+
throw error
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
/**
|
|
322
|
+
* Extracted from AgentCoordinator.startTurnAfterTurnStarted.
|
|
323
|
+
*
|
|
324
|
+
* Picks provider, resolves session tokens / priming, spawns the SDK/PTY
|
|
325
|
+
* claude session or Codex turn, registers the ActiveTurn, and routes to
|
|
326
|
+
* the codec (runTurn vs sendPrompt on the SDK queue).
|
|
327
|
+
*/
|
|
328
|
+
async function startTurnAfterTurnStarted(
|
|
329
|
+
deps: StartTurnDeps,
|
|
330
|
+
ctx: StartTurnAfterTurnStartedCtx,
|
|
331
|
+
): Promise<void> {
|
|
332
|
+
const { args, chat, project, existingMessages, shouldGenerateTitle, optimisticTitle, appendedUserMessageId } = ctx
|
|
333
|
+
if (shouldGenerateTitle) {
|
|
334
|
+
void deps.generateTitleInBackground(args.chatId, args.content, project.localPath, optimisticTitle ?? "New Chat")
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
const onToolRequest = async (request: HarnessToolRequest): Promise<AnyValue> => {
|
|
338
|
+
let active = deps.activeTurns.get(args.chatId)
|
|
339
|
+
if (!active) {
|
|
340
|
+
// The prior turn's `result` event already deleted the activeTurn, but
|
|
341
|
+
// the Claude SDK fired another `canUseTool` — happens when the SDK
|
|
342
|
+
// self-resumes after a background task notification. Re-promote a
|
|
343
|
+
// minimal activeTurn from the live session so the question renders
|
|
344
|
+
// instead of failing with "Chat turn ended unexpectedly".
|
|
345
|
+
active = deps.recreateActiveTurnFromSession(args)
|
|
346
|
+
if (!active) {
|
|
347
|
+
throw new Error("Chat turn ended unexpectedly")
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
active.status = "waiting_for_user"
|
|
352
|
+
active.waitStartedAt = Date.now()
|
|
353
|
+
deps.emitStateChange(args.chatId)
|
|
354
|
+
|
|
355
|
+
// Capture in a const to give TypeScript a stable narrowed reference inside
|
|
356
|
+
// the Promise executor (re-assignment to `active` after the if-block could
|
|
357
|
+
// theoretically happen but doesn't in practice; const makes the intent clear).
|
|
358
|
+
const narrowedActive = active
|
|
359
|
+
return await new Promise<AnyValue>((resolve) => {
|
|
360
|
+
narrowedActive.pendingTool = {
|
|
361
|
+
toolUseId: request.tool.toolId,
|
|
362
|
+
tool: request.tool,
|
|
363
|
+
resolve,
|
|
364
|
+
}
|
|
365
|
+
})
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
const targetProvider: AgentProvider = args.provider
|
|
369
|
+
const existingToken = chat.sessionTokensByProvider[targetProvider] ?? null
|
|
370
|
+
const pendingForkToken = chat.pendingForkSessionToken?.provider === targetProvider
|
|
371
|
+
? chat.pendingForkSessionToken.token
|
|
372
|
+
: null
|
|
373
|
+
const shouldPrime = shouldInjectPrimer(
|
|
374
|
+
chat.sessionTokensByProvider,
|
|
375
|
+
targetProvider,
|
|
376
|
+
Boolean(args.userClearedContext),
|
|
377
|
+
)
|
|
378
|
+
const userPromptText = buildPromptText(args.content, args.attachments)
|
|
379
|
+
const primer = shouldPrime
|
|
380
|
+
? buildHistoryPrimer(existingMessages, targetProvider, userPromptText)
|
|
381
|
+
: null
|
|
382
|
+
const promptContent = primer ?? userPromptText
|
|
383
|
+
|
|
384
|
+
let turn: HarnessTurn
|
|
385
|
+
if (isClaudeSdkProvider(args.provider)) {
|
|
386
|
+
logSendToStartingProfile(args.profile, "start_turn.provider_boot.begin", {
|
|
387
|
+
chatId: args.chatId,
|
|
388
|
+
provider: args.provider,
|
|
389
|
+
model: args.model,
|
|
390
|
+
})
|
|
391
|
+
const spawn = resolveSpawnPaths(chat, project.localPath)
|
|
392
|
+
turn = await deps.startClaudeTurn({
|
|
393
|
+
chatId: args.chatId,
|
|
394
|
+
projectId: project.id,
|
|
395
|
+
localPath: spawn.cwd,
|
|
396
|
+
additionalDirectories: spawn.additionalDirectories,
|
|
397
|
+
stackProjects: resolveStackProjects(chat, (id) => deps.store.getProject(id)?.title),
|
|
398
|
+
model: args.model,
|
|
399
|
+
effort: args.effort,
|
|
400
|
+
planMode: args.planMode,
|
|
401
|
+
sessionToken: pendingForkToken ?? existingToken,
|
|
402
|
+
forkSession: pendingForkToken != null,
|
|
403
|
+
onToolRequest,
|
|
404
|
+
provider: args.provider,
|
|
405
|
+
})
|
|
406
|
+
logSendToStartingProfile(args.profile, "start_turn.provider_boot.ready", {
|
|
407
|
+
chatId: args.chatId,
|
|
408
|
+
provider: args.provider,
|
|
409
|
+
model: args.model,
|
|
410
|
+
})
|
|
411
|
+
} else {
|
|
412
|
+
logSendToStartingProfile(args.profile, "start_turn.provider_boot.begin", {
|
|
413
|
+
chatId: args.chatId,
|
|
414
|
+
provider: args.provider,
|
|
415
|
+
model: args.model,
|
|
416
|
+
})
|
|
417
|
+
// Codex single-cwd: peer worktrees not passed to startSession. Cross-root writes use grantRoot.
|
|
418
|
+
const sessionToken = await deps.codexManager.startSession({
|
|
419
|
+
chatId: args.chatId,
|
|
420
|
+
cwd: resolveSpawnPaths(chat, project.localPath).cwd,
|
|
421
|
+
projectId: project.id,
|
|
422
|
+
model: args.model,
|
|
423
|
+
serviceTier: args.serviceTier,
|
|
424
|
+
sessionToken: existingToken,
|
|
425
|
+
pendingForkSessionToken: pendingForkToken,
|
|
426
|
+
})
|
|
427
|
+
if (pendingForkToken && sessionToken) {
|
|
428
|
+
await deps.store.setPendingForkSessionToken(args.chatId, null)
|
|
429
|
+
}
|
|
430
|
+
logSendToStartingProfile(args.profile, "start_turn.session_ready", {
|
|
431
|
+
chatId: args.chatId,
|
|
432
|
+
provider: args.provider,
|
|
433
|
+
model: args.model,
|
|
434
|
+
})
|
|
435
|
+
turn = await deps.codexManager.startTurn({
|
|
436
|
+
chatId: args.chatId,
|
|
437
|
+
content: promptContent,
|
|
438
|
+
model: args.model,
|
|
439
|
+
effort: isCodexReasoningEffort(args.effort) ? args.effort : undefined,
|
|
440
|
+
serviceTier: args.serviceTier,
|
|
441
|
+
planMode: args.planMode,
|
|
442
|
+
onToolRequest,
|
|
443
|
+
developerInstructions: deps.getAppSettingsSnapshot().globalPromptAppend,
|
|
444
|
+
})
|
|
445
|
+
logSendToStartingProfile(args.profile, "start_turn.provider_boot.ready", {
|
|
446
|
+
chatId: args.chatId,
|
|
447
|
+
provider: args.provider,
|
|
448
|
+
model: args.model,
|
|
449
|
+
})
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
const active: ActiveTurn = {
|
|
453
|
+
chatId: args.chatId,
|
|
454
|
+
provider: args.provider,
|
|
455
|
+
turn,
|
|
456
|
+
model: args.model,
|
|
457
|
+
effort: args.effort,
|
|
458
|
+
serviceTier: args.serviceTier,
|
|
459
|
+
planMode: args.planMode,
|
|
460
|
+
status: args.provider === "claude" ? "running" : "starting",
|
|
461
|
+
pendingTool: null,
|
|
462
|
+
postToolFollowUp: null,
|
|
463
|
+
hasFinalResult: false,
|
|
464
|
+
cancelRequested: false,
|
|
465
|
+
cancelRecorded: false,
|
|
466
|
+
clientTraceId: args.profile?.traceId,
|
|
467
|
+
profilingStartedAt: args.profile?.startedAt,
|
|
468
|
+
waitStartedAt: null,
|
|
469
|
+
userMessageId: appendedUserMessageId ?? deps.findLastUserMessageId(args.chatId),
|
|
470
|
+
}
|
|
471
|
+
deps.activeTurns.set(args.chatId, active)
|
|
472
|
+
logSendToStartingProfile(args.profile, "start_turn.active_turn_registered", {
|
|
473
|
+
chatId: args.chatId,
|
|
474
|
+
status: active.status,
|
|
475
|
+
})
|
|
476
|
+
deps.emitStateChange(args.chatId, { immediate: active.status === "starting" })
|
|
477
|
+
logSendToStartingProfile(args.profile, "start_turn.state_change_emitted", {
|
|
478
|
+
chatId: args.chatId,
|
|
479
|
+
status: active.status,
|
|
480
|
+
})
|
|
481
|
+
|
|
482
|
+
if (turn.getAccountInfo) {
|
|
483
|
+
void turn.getAccountInfo()
|
|
484
|
+
.then(async (accountInfo) => {
|
|
485
|
+
const session = deps.claudeSessions.get(args.chatId)
|
|
486
|
+
if (args.provider === "openrouter") {
|
|
487
|
+
// OpenRouter routes through the SDK with ANTHROPIC_AUTH_TOKEN set to
|
|
488
|
+
// the OpenRouter key, so the SDK self-reports tokenSource
|
|
489
|
+
// "ANTHROPIC_AUTH_TOKEN" with no account — mislabeling the chat as
|
|
490
|
+
// Anthropic. Override with the OpenRouter identity instead.
|
|
491
|
+
if (!session) return
|
|
492
|
+
if (session.accountInfoLoaded) return
|
|
493
|
+
session.accountInfoLoaded = true
|
|
494
|
+
await deps.store.appendMessage(args.chatId, timestamped({
|
|
495
|
+
kind: "account_info",
|
|
496
|
+
accountInfo: {
|
|
497
|
+
tokenSource: "openrouter",
|
|
498
|
+
...(session.openrouterKeyMasked ? { oauthKeyMasked: session.openrouterKeyMasked } : {}),
|
|
499
|
+
...(session.openrouterModel ? { organization: session.openrouterModel } : {}),
|
|
500
|
+
},
|
|
501
|
+
}))
|
|
502
|
+
deps.emitStateChange(args.chatId)
|
|
503
|
+
} else {
|
|
504
|
+
if (!accountInfo) return
|
|
505
|
+
let augmented = accountInfo
|
|
506
|
+
if (args.provider === "claude") {
|
|
507
|
+
if (!session) return
|
|
508
|
+
if (session.accountInfoLoaded) return
|
|
509
|
+
session.accountInfoLoaded = true
|
|
510
|
+
// Mirror the PTY driver's deriveAccountInfoFromOauth: when the
|
|
511
|
+
// turn was started with a kanna OAuth-pool token, surface its
|
|
512
|
+
// name as organization and tag the source so the UI renders
|
|
513
|
+
// "Pool token" identically across drivers. SDK-reported extras
|
|
514
|
+
// (email, subscriptionType) are preserved.
|
|
515
|
+
if (session.activeTokenId) {
|
|
516
|
+
augmented = {
|
|
517
|
+
...accountInfo,
|
|
518
|
+
tokenSource: "kanna-oauth-pool",
|
|
519
|
+
...(session.oauthLabel ? { organization: session.oauthLabel } : {}),
|
|
520
|
+
...(session.oauthKeyMasked ? { oauthKeyMasked: session.oauthKeyMasked } : {}),
|
|
521
|
+
}
|
|
522
|
+
} else if (session.oauthKeyMasked && !accountInfo.oauthKeyMasked) {
|
|
523
|
+
augmented = { ...accountInfo, oauthKeyMasked: session.oauthKeyMasked }
|
|
524
|
+
}
|
|
525
|
+
}
|
|
526
|
+
await deps.store.appendMessage(args.chatId, timestamped({ kind: "account_info", accountInfo: augmented }))
|
|
527
|
+
deps.emitStateChange(args.chatId)
|
|
528
|
+
}
|
|
529
|
+
})
|
|
530
|
+
.catch(() => undefined)
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
if (providerUsesSdkSession(args.provider)) {
|
|
534
|
+
// claude and openrouter both deliver their prompt through the SDK
|
|
535
|
+
// session queue; gating this on `=== "claude"` is what left openrouter's
|
|
536
|
+
// prompt undelivered, hanging every openrouter turn until the watchdog.
|
|
537
|
+
const session = deps.claudeSessions.get(args.chatId)
|
|
538
|
+
if (!session) {
|
|
539
|
+
throw new Error("SDK session was not initialized")
|
|
540
|
+
}
|
|
541
|
+
const promptSeq = session.nextPromptSeq + 1
|
|
542
|
+
session.nextPromptSeq = promptSeq
|
|
543
|
+
session.pendingPromptSeqs.push(promptSeq)
|
|
544
|
+
// A new turn starts: clear any stale cancellation marker so a previous
|
|
545
|
+
// cancel that never produced a tail result can't suppress this turn's
|
|
546
|
+
// real result.
|
|
547
|
+
session.cancelledResultPending = 0
|
|
548
|
+
active.claudePromptSeq = promptSeq
|
|
549
|
+
logClaudeSteer("claude_prompt_sent", {
|
|
550
|
+
chatId: args.chatId,
|
|
551
|
+
sessionId: session.id,
|
|
552
|
+
promptSeq,
|
|
553
|
+
activeStatus: active.status,
|
|
554
|
+
contentPreview: args.content.slice(0, 160),
|
|
555
|
+
pendingPromptSeqs: [...session.pendingPromptSeqs],
|
|
556
|
+
})
|
|
557
|
+
await session.session.sendPrompt(promptContent)
|
|
558
|
+
session.lastUsedAt = Date.now()
|
|
559
|
+
logSendToStartingProfile(args.profile, "start_turn.claude_prompt_sent", {
|
|
560
|
+
chatId: args.chatId,
|
|
561
|
+
})
|
|
562
|
+
return
|
|
563
|
+
}
|
|
564
|
+
|
|
565
|
+
void deps.runTurn(active)
|
|
566
|
+
}
|