spec-cat 0.1.21 → 0.1.23
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/.output/nitro.json +1 -1
- package/.output/public/_nuxt/{qt0TxNaY.js → -EMqkm_u.js} +1 -1
- package/.output/public/_nuxt/{N3ZxlKm3.js → BDd_kh9e.js} +1 -1
- package/.output/public/_nuxt/BJKzOiTU.js +1 -0
- package/.output/public/_nuxt/{DqNujSig.js → Bk4uZfKR.js} +1 -1
- package/.output/public/_nuxt/C9Qk2Hno.js +1 -0
- package/.output/public/_nuxt/CDLI67Cr.js +1 -0
- package/.output/public/_nuxt/CLPz4Oer.js +1 -0
- package/.output/public/_nuxt/CYVeOpC3.js +1 -0
- package/.output/public/_nuxt/CZbP5QXb.js +1 -0
- package/.output/public/_nuxt/DQtVbA-s.js +150 -0
- package/.output/public/_nuxt/DXWMFGmi.js +1 -0
- package/.output/public/_nuxt/DgiJut-o.js +1 -0
- package/.output/public/_nuxt/{DUodkQcr.js → FbKhJXKu.js} +3 -3
- package/.output/public/_nuxt/builds/latest.json +1 -1
- package/.output/public/_nuxt/builds/meta/53b5c409-86a8-4624-8a0f-5bf31ab82deb.json +1 -0
- package/.output/public/_nuxt/default.eSO6fRPf.css +1 -0
- package/.output/public/_nuxt/entry.BtencOYX.css +1 -0
- package/.output/public/_nuxt/useTheme.Dp77PlfC.css +1 -0
- package/.output/server/chunks/_/conversationStore.mjs +13 -1
- package/.output/server/chunks/_/conversationStore.mjs.map +1 -1
- package/.output/server/chunks/_/git.mjs +7 -6
- package/.output/server/chunks/_/git.mjs.map +1 -1
- package/.output/server/chunks/_/git2.mjs.map +1 -1
- package/.output/server/chunks/_/gitApiHelpers.mjs +43 -0
- package/.output/server/chunks/_/gitApiHelpers.mjs.map +1 -0
- package/.output/server/chunks/_/jobPersister.mjs +326 -0
- package/.output/server/chunks/_/jobPersister.mjs.map +1 -0
- package/.output/server/chunks/_/jobQueue.mjs +714 -0
- package/.output/server/chunks/_/jobQueue.mjs.map +1 -0
- package/.output/server/chunks/build/client.precomputed.mjs +1 -1
- package/.output/server/chunks/build/client.precomputed.mjs.map +1 -1
- package/.output/server/chunks/nitro/nitro.mjs +693 -657
- package/.output/server/chunks/routes/_ws.mjs +133 -527
- package/.output/server/chunks/routes/_ws.mjs.map +1 -1
- package/.output/server/chunks/routes/api/git/branch-rename.post.mjs +5 -18
- package/.output/server/chunks/routes/api/git/branch-rename.post.mjs.map +1 -1
- package/.output/server/chunks/routes/api/git/branch.delete.mjs +5 -18
- package/.output/server/chunks/routes/api/git/branch.delete.mjs.map +1 -1
- package/.output/server/chunks/routes/api/git/branches.get.mjs +5 -18
- package/.output/server/chunks/routes/api/git/branches.get.mjs.map +1 -1
- package/.output/server/chunks/routes/api/git/checkout.post.mjs +5 -19
- package/.output/server/chunks/routes/api/git/checkout.post.mjs.map +1 -1
- package/.output/server/chunks/routes/api/git/cherry-pick.post.mjs +5 -18
- package/.output/server/chunks/routes/api/git/cherry-pick.post.mjs.map +1 -1
- package/.output/server/chunks/routes/api/git/clean.post.mjs +5 -19
- package/.output/server/chunks/routes/api/git/clean.post.mjs.map +1 -1
- package/.output/server/chunks/routes/api/git/commit/_id_.get.mjs +23 -26
- package/.output/server/chunks/routes/api/git/commit/_id_.get.mjs.map +1 -1
- package/.output/server/chunks/routes/api/git/commit.post.mjs +5 -19
- package/.output/server/chunks/routes/api/git/commit.post.mjs.map +1 -1
- package/.output/server/chunks/routes/api/git/diff.get.mjs +5 -24
- package/.output/server/chunks/routes/api/git/diff.get.mjs.map +1 -1
- package/.output/server/chunks/routes/api/git/fetch.post.mjs +5 -18
- package/.output/server/chunks/routes/api/git/fetch.post.mjs.map +1 -1
- package/.output/server/chunks/routes/api/git/file-diff.get.mjs +5 -24
- package/.output/server/chunks/routes/api/git/file-diff.get.mjs.map +1 -1
- package/.output/server/chunks/routes/api/git/graph.get.mjs +9 -20
- package/.output/server/chunks/routes/api/git/graph.get.mjs.map +1 -1
- package/.output/server/chunks/routes/api/git/log.get.mjs +5 -24
- package/.output/server/chunks/routes/api/git/log.get.mjs.map +1 -1
- package/.output/server/chunks/routes/api/git/merge-base.get.mjs +5 -24
- package/.output/server/chunks/routes/api/git/merge-base.get.mjs.map +1 -1
- package/.output/server/chunks/routes/api/git/merge.post.mjs +5 -18
- package/.output/server/chunks/routes/api/git/merge.post.mjs.map +1 -1
- package/.output/server/chunks/routes/api/git/pull.post.mjs +14 -26
- package/.output/server/chunks/routes/api/git/pull.post.mjs.map +1 -1
- package/.output/server/chunks/routes/api/git/push.post.mjs +15 -27
- package/.output/server/chunks/routes/api/git/push.post.mjs.map +1 -1
- package/.output/server/chunks/routes/api/git/rebase.post.mjs +10 -22
- package/.output/server/chunks/routes/api/git/rebase.post.mjs.map +1 -1
- package/.output/server/chunks/routes/api/git/remote.delete.mjs +5 -18
- package/.output/server/chunks/routes/api/git/remote.delete.mjs.map +1 -1
- package/.output/server/chunks/routes/api/git/remote.post.mjs +10 -22
- package/.output/server/chunks/routes/api/git/remote.post.mjs.map +1 -1
- package/.output/server/chunks/routes/api/git/remote.put.mjs +5 -18
- package/.output/server/chunks/routes/api/git/remote.put.mjs.map +1 -1
- package/.output/server/chunks/routes/api/git/remotes.get.mjs +5 -18
- package/.output/server/chunks/routes/api/git/remotes.get.mjs.map +1 -1
- package/.output/server/chunks/routes/api/git/reset.post.mjs +12 -24
- package/.output/server/chunks/routes/api/git/reset.post.mjs.map +1 -1
- package/.output/server/chunks/routes/api/git/revert.post.mjs +5 -18
- package/.output/server/chunks/routes/api/git/revert.post.mjs.map +1 -1
- package/.output/server/chunks/routes/api/git/show.get.mjs +5 -24
- package/.output/server/chunks/routes/api/git/show.get.mjs.map +1 -1
- package/.output/server/chunks/routes/api/git/stage.post.mjs +9 -22
- package/.output/server/chunks/routes/api/git/stage.post.mjs.map +1 -1
- package/.output/server/chunks/routes/api/git/stash-apply.post.mjs +5 -19
- package/.output/server/chunks/routes/api/git/stash-apply.post.mjs.map +1 -1
- package/.output/server/chunks/routes/api/git/stash-branch.post.mjs +5 -19
- package/.output/server/chunks/routes/api/git/stash-branch.post.mjs.map +1 -1
- package/.output/server/chunks/routes/api/git/stash-drop.post.mjs +5 -19
- package/.output/server/chunks/routes/api/git/stash-drop.post.mjs.map +1 -1
- package/.output/server/chunks/routes/api/git/stash-pop.post.mjs +5 -19
- package/.output/server/chunks/routes/api/git/stash-pop.post.mjs.map +1 -1
- package/.output/server/chunks/routes/api/git/stash.get.mjs +5 -25
- package/.output/server/chunks/routes/api/git/stash.get.mjs.map +1 -1
- package/.output/server/chunks/routes/api/git/stash.post.mjs +5 -19
- package/.output/server/chunks/routes/api/git/stash.post.mjs.map +1 -1
- package/.output/server/chunks/routes/api/git/state.get.mjs +5 -25
- package/.output/server/chunks/routes/api/git/state.get.mjs.map +1 -1
- package/.output/server/chunks/routes/api/git/status.get.mjs +5 -25
- package/.output/server/chunks/routes/api/git/status.get.mjs.map +1 -1
- package/.output/server/chunks/routes/api/git/tag/_name_.get.mjs +5 -18
- package/.output/server/chunks/routes/api/git/tag/_name_.get.mjs.map +1 -1
- package/.output/server/chunks/routes/api/git/tag-push.post.mjs +10 -22
- package/.output/server/chunks/routes/api/git/tag-push.post.mjs.map +1 -1
- package/.output/server/chunks/routes/api/git/tag.delete.mjs +5 -18
- package/.output/server/chunks/routes/api/git/tag.delete.mjs.map +1 -1
- package/.output/server/chunks/routes/api/git/tag.post.mjs +17 -29
- package/.output/server/chunks/routes/api/git/tag.post.mjs.map +1 -1
- package/.output/server/chunks/routes/api/git/unstage.post.mjs +5 -19
- package/.output/server/chunks/routes/api/git/unstage.post.mjs.map +1 -1
- package/.output/server/chunks/routes/api/index.get.mjs +25 -14
- package/.output/server/chunks/routes/api/index.get.mjs.map +1 -1
- package/.output/server/chunks/routes/api/index.get2.mjs +14 -141
- package/.output/server/chunks/routes/api/index.get2.mjs.map +1 -1
- package/.output/server/chunks/routes/api/index.get3.mjs +167 -0
- package/.output/server/chunks/routes/api/index.get3.mjs.map +1 -0
- package/.output/server/chunks/routes/api/index.post.mjs +77 -116
- package/.output/server/chunks/routes/api/index.post.mjs.map +1 -1
- package/.output/server/chunks/routes/api/index.post2.mjs +149 -0
- package/.output/server/chunks/routes/api/index.post2.mjs.map +1 -0
- package/.output/server/chunks/routes/api/jobs/_id/cancel.post.mjs +48 -0
- package/.output/server/chunks/routes/api/jobs/_id/cancel.post.mjs.map +1 -0
- package/.output/server/chunks/routes/api/jobs/_id_.get.mjs +55 -0
- package/.output/server/chunks/routes/api/jobs/_id_.get.mjs.map +1 -0
- package/.output/server/chunks/routes/api/repository/status.get.mjs +1 -1
- package/.output/server/package.json +1 -1
- package/package.json +1 -1
- package/.output/public/_nuxt/BIw1AQHU.js +0 -150
- package/.output/public/_nuxt/DBtLi_wJ.js +0 -1
- package/.output/public/_nuxt/DSDWvT5-.js +0 -1
- package/.output/public/_nuxt/K5rMM4le.js +0 -1
- package/.output/public/_nuxt/builds/meta/c0768eef-2dd5-410c-8648-edbd9e6c218c.json +0 -1
- package/.output/public/_nuxt/ddKcAgQK.js +0 -1
- package/.output/public/_nuxt/default.CZoNL3P_.css +0 -1
- package/.output/public/_nuxt/entry.DLBgeD7S.css +0 -1
- package/.output/public/_nuxt/sxXWehCn.js +0 -1
|
@@ -1,12 +1,6 @@
|
|
|
1
|
-
import { n as defineWebSocketHandler
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import { promisify } from 'node:util';
|
|
5
|
-
import { join } from 'node:path';
|
|
6
|
-
import { r as resolveServerProviderSelection, a as guardProviderCapability } from '../_/aiProviderSelection.mjs';
|
|
7
|
-
import { s as streamChatWithProvider, h as hasCodexPermissionError, a as hasCodexMissingRolloutPathError, b as summarizeProviderProcessError } from '../_/providerProcessError.mjs';
|
|
8
|
-
import { g as getProvider } from '../_/aiProviderRegistry.mjs';
|
|
9
|
-
import { n as normalizeToolName, e as extractPermissionRequestFromProcessOutput, a as normalizeTools, c as checkForPermissionRequest, i as isRenderableEvent } from '../_/uiAdapter.mjs';
|
|
1
|
+
import { n as defineWebSocketHandler } from '../nitro/nitro.mjs';
|
|
2
|
+
import { e as eventBus, G as GLOBAL_CHANNEL, n as normalizeImageAttachments, j as jobQueue } from '../_/jobQueue.mjs';
|
|
3
|
+
import { s as startPersisting } from '../_/jobPersister.mjs';
|
|
10
4
|
import 'node:http';
|
|
11
5
|
import 'node:https';
|
|
12
6
|
import 'node:crypto';
|
|
@@ -22,212 +16,69 @@ import 'tls';
|
|
|
22
16
|
import 'url';
|
|
23
17
|
import 'node:events';
|
|
24
18
|
import 'node:buffer';
|
|
19
|
+
import 'node:fs';
|
|
20
|
+
import 'node:path';
|
|
25
21
|
import 'node:os';
|
|
26
22
|
import 'node:module';
|
|
27
23
|
import 'node:fs/promises';
|
|
28
24
|
import 'node:url';
|
|
25
|
+
import 'node:child_process';
|
|
26
|
+
import 'node:util';
|
|
27
|
+
import '../_/aiProviderSelection.mjs';
|
|
28
|
+
import '../_/aiProviderRegistry.mjs';
|
|
29
|
+
import '../_/providerProcessError.mjs';
|
|
30
|
+
import '../_/uiAdapter.mjs';
|
|
31
|
+
import '../_/conversationStore.mjs';
|
|
29
32
|
|
|
30
|
-
const
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
if (!
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
33
|
+
const peerConnections = /* @__PURE__ */ new Map();
|
|
34
|
+
function getPeerConnection(peerId) {
|
|
35
|
+
let conn = peerConnections.get(peerId);
|
|
36
|
+
if (!conn) {
|
|
37
|
+
conn = { conversationId: null, unsubscribe: null, unsubscribeGlobal: null };
|
|
38
|
+
peerConnections.set(peerId, conn);
|
|
39
|
+
}
|
|
40
|
+
return conn;
|
|
41
|
+
}
|
|
42
|
+
function subscribePeerToConversation(peer, conversationId) {
|
|
43
|
+
const conn = getPeerConnection(peer.id);
|
|
44
|
+
if (conn.conversationId === conversationId && conn.unsubscribe) {
|
|
45
|
+
return;
|
|
38
46
|
}
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
branchName = knownBranch;
|
|
42
|
-
} else {
|
|
43
|
-
const convId = worktreePath.slice(WORKTREE_PREFIX.length);
|
|
44
|
-
if (!convId) {
|
|
45
|
-
return { recovered: false, error: `Cannot derive branch name from worktree path: ${worktreePath}` };
|
|
46
|
-
}
|
|
47
|
-
branchName = `sc/${convId}`;
|
|
47
|
+
if (conn.unsubscribe) {
|
|
48
|
+
conn.unsubscribe();
|
|
48
49
|
}
|
|
49
|
-
|
|
50
|
-
|
|
50
|
+
conn.conversationId = conversationId;
|
|
51
|
+
conn.unsubscribe = eventBus.subscribe(conversationId, (event) => {
|
|
51
52
|
try {
|
|
52
|
-
|
|
53
|
-
} catch {
|
|
54
|
-
|
|
55
|
-
}
|
|
56
|
-
await execAsync(`git worktree add "${worktreePath}" "${branchName}"`, { cwd: projectDir });
|
|
57
|
-
console.log(`[ensureChatWorktree] Recovered worktree: ${worktreePath} \u2192 ${branchName}`);
|
|
58
|
-
return { recovered: true };
|
|
59
|
-
} catch (err) {
|
|
60
|
-
const message = err instanceof Error ? err.message : String(err);
|
|
61
|
-
console.error(`[ensureChatWorktree] Recovery failed for ${worktreePath}:`, message);
|
|
62
|
-
return { recovered: false, error: `Worktree recovery failed: ${message}` };
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
async function loadSpecContext(projectDir, featureId) {
|
|
67
|
-
const featurePath = join(projectDir, "specs", featureId);
|
|
68
|
-
const specPath = join(featurePath, "spec.md");
|
|
69
|
-
if (!existsSync(specPath)) {
|
|
70
|
-
return null;
|
|
71
|
-
}
|
|
72
|
-
const sections = [];
|
|
73
|
-
sections.push(`# Feature Context: ${featureId}`);
|
|
74
|
-
sections.push("");
|
|
75
|
-
sections.push("You are working on the feature described in the following specification files.");
|
|
76
|
-
sections.push("You MUST read these files first to understand the feature requirements:");
|
|
77
|
-
sections.push("");
|
|
78
|
-
sections.push(`- specs/${featureId}/spec.md`);
|
|
79
|
-
const planPath = join(featurePath, "plan.md");
|
|
80
|
-
if (existsSync(planPath)) {
|
|
81
|
-
sections.push(`- specs/${featureId}/plan.md`);
|
|
82
|
-
}
|
|
83
|
-
const tasksPath = join(featurePath, "tasks.md");
|
|
84
|
-
if (existsSync(tasksPath)) {
|
|
85
|
-
sections.push(`- specs/${featureId}/tasks.md`);
|
|
86
|
-
}
|
|
87
|
-
sections.push("");
|
|
88
|
-
sections.push("## Spec-Driven Workflow (MANDATORY)");
|
|
89
|
-
sections.push("");
|
|
90
|
-
sections.push("This chat is linked to a feature spec. You MUST follow the spec-driven workflow:");
|
|
91
|
-
sections.push("");
|
|
92
|
-
sections.push("1. **Read the spec files above** before doing anything else.");
|
|
93
|
-
sections.push("2. **When the user requests changes or new functionality:**");
|
|
94
|
-
sections.push(" - First, update the relevant spec file(s) (spec.md, plan.md, tasks.md) to reflect the new requirements.");
|
|
95
|
-
sections.push(" - Then, implement the code changes according to the updated spec.");
|
|
96
|
-
sections.push(" - Never skip the spec update step. The spec is the source of truth.");
|
|
97
|
-
sections.push("3. **When the user requests a bug fix:**");
|
|
98
|
-
sections.push(" - Check if the bug contradicts the spec. If so, fix the code to match the spec.");
|
|
99
|
-
sections.push(" - If the spec itself is wrong, update the spec first, then fix the code.");
|
|
100
|
-
sections.push("4. **FR Traceability:** Every change must be traceable from spec \u2192 plan \u2192 task \u2192 implementation.");
|
|
101
|
-
return sections.join("\n");
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
function isApprovalMode(mode) {
|
|
105
|
-
return mode === "ask" || mode === "plan";
|
|
106
|
-
}
|
|
107
|
-
function deriveApprovalRequestFromEvent(event, approvedTools, providerId, mode) {
|
|
108
|
-
if (!isApprovalMode(mode)) return null;
|
|
109
|
-
return checkForPermissionRequest(event, approvedTools, providerId);
|
|
110
|
-
}
|
|
111
|
-
function deriveApprovalRequestFromProcessOutput(nonJsonOutput, mode) {
|
|
112
|
-
if (!isApprovalMode(mode)) return null;
|
|
113
|
-
const inferred = extractPermissionRequestFromProcessOutput(nonJsonOutput);
|
|
114
|
-
if (!inferred) return null;
|
|
115
|
-
const tools = normalizeTools(inferred.tools);
|
|
116
|
-
return {
|
|
117
|
-
type: "permission_request",
|
|
118
|
-
tool: tools[0] || "Permission",
|
|
119
|
-
tools,
|
|
120
|
-
description: inferred.description
|
|
121
|
-
};
|
|
122
|
-
}
|
|
123
|
-
function approveTools(approvedTools, tools) {
|
|
124
|
-
for (const tool of tools) {
|
|
125
|
-
const normalized = normalizeToolName(tool);
|
|
126
|
-
if (normalized) {
|
|
127
|
-
approvedTools.add(normalized);
|
|
128
|
-
}
|
|
129
|
-
}
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
const peerStates = /* @__PURE__ */ new Map();
|
|
133
|
-
const MAX_ATTACHMENT_COUNT = 4;
|
|
134
|
-
const MAX_ATTACHMENT_SIZE_BYTES = 5 * 1024 * 1024;
|
|
135
|
-
const SPECKIT_AUTONOMY_DIRECTIVE = [
|
|
136
|
-
"Speckit Execution Mode (MANDATORY):",
|
|
137
|
-
"- Do not ask the user for confirmation, follow-up, or permission to proceed.",
|
|
138
|
-
'- Do not end with questions like "Would you like me to..." or "Shall I...".',
|
|
139
|
-
"- For remediation and traceability gaps, directly edit the relevant spec files now (spec.md, plan.md, tasks.md) when writable.",
|
|
140
|
-
"- Prefer concrete file edits over recommendations; provide a brief change summary after edits are complete.",
|
|
141
|
-
"- Only stop without edits if blocked by a hard constraint (missing files, permission failure), and report the blocker explicitly."
|
|
142
|
-
].join("\n");
|
|
143
|
-
function getPeerState(peerId) {
|
|
144
|
-
let state = peerStates.get(peerId);
|
|
145
|
-
if (!state) {
|
|
146
|
-
state = {
|
|
147
|
-
proc: null,
|
|
148
|
-
procGeneration: 0,
|
|
149
|
-
pendingMessage: null,
|
|
150
|
-
approvedTools: /* @__PURE__ */ new Set(),
|
|
151
|
-
pendingTools: [],
|
|
152
|
-
providerSessionId: null
|
|
153
|
-
};
|
|
154
|
-
peerStates.set(peerId, state);
|
|
155
|
-
}
|
|
156
|
-
return state;
|
|
157
|
-
}
|
|
158
|
-
function isSpeckitCommand(message) {
|
|
159
|
-
return message.trim().startsWith("/speckit.");
|
|
160
|
-
}
|
|
161
|
-
function killProc(proc) {
|
|
162
|
-
try {
|
|
163
|
-
proc.kill();
|
|
164
|
-
} catch {
|
|
165
|
-
}
|
|
166
|
-
}
|
|
167
|
-
function normalizeImageAttachments(attachments) {
|
|
168
|
-
if (!Array.isArray(attachments)) return [];
|
|
169
|
-
return attachments.slice(0, MAX_ATTACHMENT_COUNT).map((item) => {
|
|
170
|
-
if (!item || typeof item !== "object") return null;
|
|
171
|
-
const record = item;
|
|
172
|
-
const id = typeof record.id === "string" ? record.id : "";
|
|
173
|
-
const name = typeof record.name === "string" ? record.name : "image";
|
|
174
|
-
const mimeType = typeof record.mimeType === "string" ? record.mimeType : "";
|
|
175
|
-
const size = typeof record.size === "number" ? record.size : 0;
|
|
176
|
-
const dataUrl = typeof record.dataUrl === "string" ? record.dataUrl : "";
|
|
177
|
-
if (!id || !mimeType.startsWith("image/") || size <= 0 || size > MAX_ATTACHMENT_SIZE_BYTES || !dataUrl.startsWith("data:image/")) {
|
|
178
|
-
return null;
|
|
53
|
+
peer.send(JSON.stringify(event));
|
|
54
|
+
} catch (err) {
|
|
55
|
+
console.error("[WS] Failed to send event to peer:", err);
|
|
179
56
|
}
|
|
180
|
-
return { id, name, mimeType, size, dataUrl };
|
|
181
|
-
}).filter((entry) => entry !== null);
|
|
182
|
-
}
|
|
183
|
-
function buildProviderMessage(baseMessage, attachments) {
|
|
184
|
-
if (attachments.length === 0) return baseMessage;
|
|
185
|
-
const lines = [];
|
|
186
|
-
if (baseMessage.trim().length > 0) {
|
|
187
|
-
lines.push(baseMessage);
|
|
188
|
-
lines.push("");
|
|
189
|
-
} else {
|
|
190
|
-
lines.push("User sent image attachments without additional text.");
|
|
191
|
-
lines.push("");
|
|
192
|
-
}
|
|
193
|
-
lines.push("Attached images (data URLs):");
|
|
194
|
-
attachments.forEach((attachment, index) => {
|
|
195
|
-
lines.push(`${index + 1}. ${attachment.name} (${attachment.mimeType}, ${attachment.size} bytes)`);
|
|
196
|
-
lines.push(attachment.dataUrl);
|
|
197
|
-
lines.push("");
|
|
198
57
|
});
|
|
199
|
-
lines.push("Use the attached images as part of your answer.");
|
|
200
|
-
return lines.join("\n");
|
|
201
|
-
}
|
|
202
|
-
function sendAssistantText(peer, text, sessionId) {
|
|
203
|
-
peer.send(JSON.stringify({
|
|
204
|
-
type: "ui_event",
|
|
205
|
-
event: {
|
|
206
|
-
type: "block_start",
|
|
207
|
-
sessionId: sessionId || void 0,
|
|
208
|
-
blockId: `blk-${Date.now()}`,
|
|
209
|
-
blockType: "text",
|
|
210
|
-
text
|
|
211
|
-
}
|
|
212
|
-
}));
|
|
213
|
-
peer.send(JSON.stringify({
|
|
214
|
-
type: "ui_event",
|
|
215
|
-
event: {
|
|
216
|
-
type: "block_end",
|
|
217
|
-
sessionId: sessionId || void 0,
|
|
218
|
-
blockId: ""
|
|
219
|
-
}
|
|
220
|
-
}));
|
|
221
58
|
}
|
|
222
59
|
const _ws = defineWebSocketHandler({
|
|
223
|
-
open(
|
|
60
|
+
open(peer) {
|
|
61
|
+
console.log("[WS] Peer connected:", peer.id);
|
|
62
|
+
const conn = getPeerConnection(peer.id);
|
|
63
|
+
conn.unsubscribeGlobal = eventBus.subscribe(GLOBAL_CHANNEL, (event) => {
|
|
64
|
+
try {
|
|
65
|
+
const payload = JSON.stringify(event);
|
|
66
|
+
if (event.type === "notification") {
|
|
67
|
+
console.log("[WS] Forwarding global notification to peer", peer.id, ":", event.notificationEvent);
|
|
68
|
+
}
|
|
69
|
+
peer.send(payload);
|
|
70
|
+
} catch (err) {
|
|
71
|
+
console.error("[WS] Failed to send global event to peer:", err);
|
|
72
|
+
}
|
|
73
|
+
});
|
|
224
74
|
},
|
|
225
75
|
close(peer) {
|
|
226
|
-
const
|
|
227
|
-
if (
|
|
228
|
-
|
|
76
|
+
const conn = peerConnections.get(peer.id);
|
|
77
|
+
if (conn) {
|
|
78
|
+
if (conn.unsubscribe) conn.unsubscribe();
|
|
79
|
+
if (conn.unsubscribeGlobal) conn.unsubscribeGlobal();
|
|
229
80
|
}
|
|
230
|
-
|
|
81
|
+
peerConnections.delete(peer.id);
|
|
231
82
|
},
|
|
232
83
|
error(peer, error) {
|
|
233
84
|
console.error("[WS] Error for peer", peer.id, ":", error);
|
|
@@ -260,67 +111,13 @@ const _ws = defineWebSocketHandler({
|
|
|
260
111
|
handleResetContext(peer);
|
|
261
112
|
return;
|
|
262
113
|
}
|
|
114
|
+
if (msg.type === "subscribe") {
|
|
115
|
+
handleSubscribe(peer, msg);
|
|
116
|
+
return;
|
|
117
|
+
}
|
|
263
118
|
}
|
|
264
119
|
});
|
|
265
|
-
function
|
|
266
|
-
var _a, _b;
|
|
267
|
-
const state = getPeerState(peer.id);
|
|
268
|
-
console.log("[WS] Permission response:", {
|
|
269
|
-
allow: msg.allow,
|
|
270
|
-
pendingTools: state.pendingTools,
|
|
271
|
-
approvedTools: Array.from(state.approvedTools),
|
|
272
|
-
sessionId: state.providerSessionId,
|
|
273
|
-
providerId: (_a = state.pendingMessage) == null ? void 0 : _a.providerId,
|
|
274
|
-
providerModelKey: (_b = state.pendingMessage) == null ? void 0 : _b.providerModelKey
|
|
275
|
-
});
|
|
276
|
-
if (msg.allow && state.pendingMessage) {
|
|
277
|
-
approveTools(state.approvedTools, state.pendingTools);
|
|
278
|
-
console.log("[WS] Tools approved:", state.pendingTools, "- Total approved:", Array.from(state.approvedTools));
|
|
279
|
-
state.pendingTools = [];
|
|
280
|
-
const resumeMode = state.pendingMessage.permissionMode || "ask";
|
|
281
|
-
runProvider(peer, state, {
|
|
282
|
-
...state.pendingMessage,
|
|
283
|
-
permissionMode: resumeMode
|
|
284
|
-
});
|
|
285
|
-
} else {
|
|
286
|
-
state.pendingTools = [];
|
|
287
|
-
state.pendingMessage = null;
|
|
288
|
-
peer.send(JSON.stringify({ type: "done", requestId: "denied", denied: true }));
|
|
289
|
-
}
|
|
290
|
-
}
|
|
291
|
-
function handleAbort(peer) {
|
|
292
|
-
const state = getPeerState(peer.id);
|
|
293
|
-
console.log("[WS] Abort requested for peer:", peer.id);
|
|
294
|
-
if (state.proc) {
|
|
295
|
-
state.procGeneration++;
|
|
296
|
-
killProc(state.proc);
|
|
297
|
-
state.proc = null;
|
|
298
|
-
}
|
|
299
|
-
state.pendingMessage = null;
|
|
300
|
-
state.pendingTools = [];
|
|
301
|
-
peer.send(JSON.stringify({ type: "aborted" }));
|
|
302
|
-
console.log("[WS] Abort completed for peer:", peer.id);
|
|
303
|
-
}
|
|
304
|
-
function clearProviderSession(state) {
|
|
305
|
-
if (state.proc) {
|
|
306
|
-
state.procGeneration++;
|
|
307
|
-
killProc(state.proc);
|
|
308
|
-
state.proc = null;
|
|
309
|
-
}
|
|
310
|
-
state.providerSessionId = null;
|
|
311
|
-
state.approvedTools.clear();
|
|
312
|
-
state.pendingMessage = null;
|
|
313
|
-
state.pendingTools = [];
|
|
314
|
-
}
|
|
315
|
-
function handleResetContext(peer) {
|
|
316
|
-
const state = getPeerState(peer.id);
|
|
317
|
-
console.log("[WS] Reset context requested for peer:", peer.id);
|
|
318
|
-
clearProviderSession(state);
|
|
319
|
-
peer.send(JSON.stringify({ type: "context_reset" }));
|
|
320
|
-
console.log("[WS] Context reset completed for peer:", peer.id);
|
|
321
|
-
}
|
|
322
|
-
async function handleChatMessage(peer, msg) {
|
|
323
|
-
const state = getPeerState(peer.id);
|
|
120
|
+
function handleChatMessage(peer, msg) {
|
|
324
121
|
const attachments = normalizeImageAttachments(msg.attachments);
|
|
325
122
|
if (typeof msg.message !== "string") {
|
|
326
123
|
console.error("[WS] Invalid chat message - missing or invalid message property:", msg);
|
|
@@ -339,291 +136,100 @@ async function handleChatMessage(peer, msg) {
|
|
|
339
136
|
}));
|
|
340
137
|
return;
|
|
341
138
|
}
|
|
342
|
-
|
|
343
|
-
if (speckitCommand) {
|
|
344
|
-
console.log("[WS] Speckit command detected - auto-resetting context for peer:", peer.id);
|
|
345
|
-
clearProviderSession(state);
|
|
346
|
-
} else {
|
|
347
|
-
if (state.proc) {
|
|
348
|
-
state.procGeneration++;
|
|
349
|
-
killProc(state.proc);
|
|
350
|
-
state.proc = null;
|
|
351
|
-
}
|
|
352
|
-
}
|
|
353
|
-
state.pendingMessage = msg;
|
|
354
|
-
state.pendingTools = [];
|
|
355
|
-
if (!speckitCommand && msg.sessionId) {
|
|
356
|
-
state.providerSessionId = msg.sessionId;
|
|
357
|
-
} else if (!speckitCommand) {
|
|
358
|
-
state.approvedTools.clear();
|
|
359
|
-
state.providerSessionId = null;
|
|
360
|
-
}
|
|
361
|
-
console.log("[WS] Chat message received:", {
|
|
362
|
-
hasSessionId: !!msg.sessionId,
|
|
363
|
-
sessionId: state.providerSessionId,
|
|
364
|
-
approvedTools: Array.from(state.approvedTools),
|
|
365
|
-
attachmentCount: attachments.length,
|
|
366
|
-
providerId: msg.providerId,
|
|
367
|
-
providerModelKey: msg.providerModelKey,
|
|
368
|
-
isSpeckitCommand: speckitCommand
|
|
369
|
-
});
|
|
370
|
-
runProvider(peer, state, msg);
|
|
371
|
-
}
|
|
372
|
-
async function runProvider(peer, state, msg, isRetry = false, forceEphemeral = false) {
|
|
373
|
-
const requestedSelection = msg.providerId ? { providerId: msg.providerId, modelKey: msg.providerModelKey || "" } : { providerId: "claude", modelKey: msg.providerModelKey || "" };
|
|
374
|
-
const selection = await resolveServerProviderSelection(requestedSelection);
|
|
375
|
-
const provider = getProvider(selection.providerId);
|
|
376
|
-
if (!provider) {
|
|
139
|
+
if (!msg.conversationId) {
|
|
377
140
|
peer.send(JSON.stringify({
|
|
378
141
|
type: "error",
|
|
379
|
-
error:
|
|
142
|
+
error: "Invalid message: conversationId is required",
|
|
380
143
|
requestId: msg.requestId
|
|
381
144
|
}));
|
|
382
145
|
return;
|
|
383
146
|
}
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
147
|
+
subscribePeerToConversation(peer, msg.conversationId);
|
|
148
|
+
const jobMessage = {
|
|
149
|
+
message: msg.message,
|
|
150
|
+
conversationId: msg.conversationId,
|
|
151
|
+
attachments: attachments.length > 0 ? attachments : void 0,
|
|
152
|
+
requestId: msg.requestId,
|
|
153
|
+
sessionId: msg.sessionId,
|
|
154
|
+
permissionMode: msg.permissionMode,
|
|
155
|
+
cwd: msg.cwd,
|
|
156
|
+
worktreeBranch: msg.worktreeBranch,
|
|
157
|
+
featureId: msg.featureId,
|
|
158
|
+
providerId: msg.providerId,
|
|
159
|
+
providerModelKey: msg.providerModelKey
|
|
160
|
+
};
|
|
161
|
+
startPersisting(msg.conversationId, msg.message);
|
|
162
|
+
jobQueue.submit(jobMessage);
|
|
163
|
+
}
|
|
164
|
+
function handlePermissionResponse(peer, msg) {
|
|
165
|
+
const conn = getPeerConnection(peer.id);
|
|
166
|
+
if (!conn.conversationId) return;
|
|
167
|
+
console.log("[WS] Permission response:", { allow: msg.allow, conversationId: conn.conversationId });
|
|
168
|
+
jobQueue.respondToPermission(conn.conversationId, msg.allow);
|
|
169
|
+
}
|
|
170
|
+
function handleAbort(peer) {
|
|
171
|
+
const conn = getPeerConnection(peer.id);
|
|
172
|
+
console.log("[WS] Abort requested for peer:", peer.id);
|
|
173
|
+
if (conn.conversationId) {
|
|
174
|
+
jobQueue.abort(conn.conversationId);
|
|
175
|
+
}
|
|
176
|
+
peer.send(JSON.stringify({ type: "aborted" }));
|
|
177
|
+
console.log("[WS] Abort completed for peer:", peer.id);
|
|
178
|
+
}
|
|
179
|
+
function handleSubscribe(peer, msg) {
|
|
180
|
+
if (!msg.conversationId) {
|
|
181
|
+
peer.send(JSON.stringify({ type: "error", error: "conversationId is required for subscribe" }));
|
|
395
182
|
return;
|
|
396
183
|
}
|
|
397
|
-
|
|
398
|
-
const
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
const
|
|
402
|
-
|
|
403
|
-
"permissions",
|
|
404
|
-
"Use auto/bypass permission mode or choose a provider that supports permission prompts."
|
|
405
|
-
);
|
|
406
|
-
if ("failure" in permissionGuard) {
|
|
184
|
+
subscribePeerToConversation(peer, msg.conversationId);
|
|
185
|
+
const activeJob = jobQueue.getActiveJob(msg.conversationId);
|
|
186
|
+
if (activeJob) {
|
|
187
|
+
const cursor = typeof msg.cursor === "number" && msg.cursor >= 0 ? msg.cursor : 0;
|
|
188
|
+
const bufferedEvents = activeJob.events.slice(cursor);
|
|
189
|
+
if (bufferedEvents.length > 0) {
|
|
407
190
|
peer.send(JSON.stringify({
|
|
408
|
-
type: "
|
|
409
|
-
|
|
410
|
-
|
|
191
|
+
type: "replay_start",
|
|
192
|
+
jobId: activeJob.id,
|
|
193
|
+
jobStatus: activeJob.status,
|
|
194
|
+
eventCount: bufferedEvents.length,
|
|
195
|
+
cursor
|
|
411
196
|
}));
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
} else if (result.error) {
|
|
197
|
+
for (const event of bufferedEvents) {
|
|
198
|
+
try {
|
|
199
|
+
peer.send(JSON.stringify(event));
|
|
200
|
+
} catch {
|
|
201
|
+
break;
|
|
202
|
+
}
|
|
203
|
+
}
|
|
420
204
|
peer.send(JSON.stringify({
|
|
421
|
-
type: "
|
|
422
|
-
|
|
423
|
-
|
|
205
|
+
type: "replay_end",
|
|
206
|
+
jobId: activeJob.id,
|
|
207
|
+
nextCursor: activeJob.events.length
|
|
208
|
+
}));
|
|
209
|
+
} else {
|
|
210
|
+
peer.send(JSON.stringify({
|
|
211
|
+
type: "subscribed",
|
|
212
|
+
conversationId: msg.conversationId,
|
|
213
|
+
jobId: activeJob.id,
|
|
214
|
+
jobStatus: activeJob.status
|
|
424
215
|
}));
|
|
425
|
-
return;
|
|
426
|
-
}
|
|
427
|
-
}
|
|
428
|
-
const usedResumeFlag = !isRetry && !!state.providerSessionId;
|
|
429
|
-
const resumeSessionId = usedResumeFlag ? state.providerSessionId : void 0;
|
|
430
|
-
let systemPrompt;
|
|
431
|
-
const speckitCommand = isSpeckitCommand(msg.message);
|
|
432
|
-
if (msg.featureId && !usedResumeFlag) {
|
|
433
|
-
try {
|
|
434
|
-
const specContext = await loadSpecContext(projectDir, msg.featureId);
|
|
435
|
-
if (specContext) {
|
|
436
|
-
systemPrompt = specContext;
|
|
437
|
-
}
|
|
438
|
-
} catch (error) {
|
|
439
|
-
console.error("[WS] Failed to load spec context:", error);
|
|
440
216
|
}
|
|
441
|
-
}
|
|
442
|
-
if (speckitCommand && !usedResumeFlag) {
|
|
443
|
-
systemPrompt = systemPrompt ? `${systemPrompt}
|
|
444
|
-
|
|
445
|
-
${SPECKIT_AUTONOMY_DIRECTIVE}` : SPECKIT_AUTONOMY_DIRECTIVE;
|
|
446
|
-
}
|
|
447
|
-
console.log("[WS] Running provider stream:", selection.providerId, selection.modelKey, isRetry ? "(retry)" : "");
|
|
448
|
-
const generation = state.procGeneration;
|
|
449
|
-
let permissionRequested = false;
|
|
450
|
-
let emittedRenderableContent = false;
|
|
451
|
-
let emittedTerminalErrorEvent = false;
|
|
452
|
-
const attachments = normalizeImageAttachments(msg.attachments);
|
|
453
|
-
const providerMessage = buildProviderMessage(msg.message, attachments);
|
|
454
|
-
try {
|
|
455
|
-
state.proc = await streamChatWithProvider(
|
|
456
|
-
{
|
|
457
|
-
message: providerMessage,
|
|
458
|
-
selection,
|
|
459
|
-
cwd: workingDirectory,
|
|
460
|
-
permissionMode: mode,
|
|
461
|
-
approvedTools: Array.from(state.approvedTools),
|
|
462
|
-
resumeSessionId,
|
|
463
|
-
systemPrompt,
|
|
464
|
-
ephemeral: forceEphemeral && selection.providerId === "codex"
|
|
465
|
-
},
|
|
466
|
-
{
|
|
467
|
-
onProviderJson(parsed) {
|
|
468
|
-
var _a;
|
|
469
|
-
const events = provider.toCanonicalEvents(parsed);
|
|
470
|
-
for (const event of events) {
|
|
471
|
-
if (event.sessionId) {
|
|
472
|
-
state.providerSessionId = event.sessionId;
|
|
473
|
-
}
|
|
474
|
-
if (isRenderableEvent(event)) {
|
|
475
|
-
emittedRenderableContent = true;
|
|
476
|
-
}
|
|
477
|
-
if (event.type === "error" || event.type === "turn_result" && event.subtype !== "success") {
|
|
478
|
-
emittedTerminalErrorEvent = true;
|
|
479
|
-
}
|
|
480
|
-
if ((mode === "ask" || mode === "plan") && !permissionRequested) {
|
|
481
|
-
const permRequest = deriveApprovalRequestFromEvent(
|
|
482
|
-
event,
|
|
483
|
-
state.approvedTools,
|
|
484
|
-
selection.providerId,
|
|
485
|
-
mode
|
|
486
|
-
);
|
|
487
|
-
if (permRequest) {
|
|
488
|
-
permissionRequested = true;
|
|
489
|
-
state.pendingTools = permRequest.tools || [permRequest.tool];
|
|
490
|
-
peer.send(JSON.stringify({
|
|
491
|
-
type: "permission_request",
|
|
492
|
-
tool: permRequest.tool,
|
|
493
|
-
tools: state.pendingTools,
|
|
494
|
-
description: permRequest.description || `Permission required: ${permRequest.tool}`
|
|
495
|
-
}));
|
|
496
|
-
(_a = state.proc) == null ? void 0 : _a.kill();
|
|
497
|
-
return;
|
|
498
|
-
}
|
|
499
|
-
}
|
|
500
|
-
peer.send(JSON.stringify({ type: "ui_event", event }));
|
|
501
|
-
}
|
|
502
|
-
},
|
|
503
|
-
onClose({ exitCode, signal, nonJsonOutput }) {
|
|
504
|
-
if (state.procGeneration !== generation) {
|
|
505
|
-
return;
|
|
506
|
-
}
|
|
507
|
-
try {
|
|
508
|
-
if (!permissionRequested) {
|
|
509
|
-
if (exitCode !== 0 && exitCode !== null) {
|
|
510
|
-
console.error("[WS] Provider process exited unexpectedly", {
|
|
511
|
-
providerId: selection.providerId,
|
|
512
|
-
modelKey: selection.modelKey,
|
|
513
|
-
permissionMode: mode,
|
|
514
|
-
requestId: msg.requestId,
|
|
515
|
-
exitCode,
|
|
516
|
-
signal,
|
|
517
|
-
nonJsonOutput: nonJsonOutput.slice(-25)
|
|
518
|
-
});
|
|
519
|
-
const inferred = deriveApprovalRequestFromProcessOutput(nonJsonOutput, mode);
|
|
520
|
-
if (inferred) {
|
|
521
|
-
permissionRequested = true;
|
|
522
|
-
state.pendingTools = inferred.tools || [inferred.tool];
|
|
523
|
-
peer.send(JSON.stringify({
|
|
524
|
-
type: "permission_request",
|
|
525
|
-
tool: inferred.tool,
|
|
526
|
-
tools: state.pendingTools,
|
|
527
|
-
description: inferred.description
|
|
528
|
-
}));
|
|
529
|
-
state.proc = null;
|
|
530
|
-
return;
|
|
531
|
-
}
|
|
532
|
-
const hasPermissionError = hasCodexPermissionError(nonJsonOutput);
|
|
533
|
-
const missingRolloutPath = hasCodexMissingRolloutPathError(nonJsonOutput);
|
|
534
|
-
if (missingRolloutPath && !hasPermissionError && !isRetry) {
|
|
535
|
-
peer.send(JSON.stringify({
|
|
536
|
-
type: "session_reset",
|
|
537
|
-
reason: "Codex session state was missing rollout data. Retrying with a fresh ephemeral session."
|
|
538
|
-
}));
|
|
539
|
-
state.providerSessionId = null;
|
|
540
|
-
state.proc = null;
|
|
541
|
-
runProvider(peer, state, msg, true, true);
|
|
542
|
-
return;
|
|
543
|
-
}
|
|
544
|
-
if (usedResumeFlag && !isRetry) {
|
|
545
|
-
const retryWithEphemeral = selection.providerId === "codex";
|
|
546
|
-
peer.send(JSON.stringify({
|
|
547
|
-
type: "session_reset",
|
|
548
|
-
reason: retryWithEphemeral ? `Session resume failed (exit code ${exitCode}). Retrying with a fresh ephemeral session.` : `Session resume failed (exit code ${exitCode}). Retrying with a fresh session.`
|
|
549
|
-
}));
|
|
550
|
-
state.providerSessionId = null;
|
|
551
|
-
state.proc = null;
|
|
552
|
-
runProvider(peer, state, msg, true, retryWithEphemeral);
|
|
553
|
-
return;
|
|
554
|
-
}
|
|
555
|
-
const summary = summarizeProviderProcessError(nonJsonOutput, 700);
|
|
556
|
-
if (!summary && emittedTerminalErrorEvent && emittedRenderableContent) {
|
|
557
|
-
peer.send(JSON.stringify({ type: "done", requestId: msg.requestId }));
|
|
558
|
-
state.pendingMessage = null;
|
|
559
|
-
return;
|
|
560
|
-
}
|
|
561
|
-
const details = summary ? ` \u2014 ${summary}` : "";
|
|
562
|
-
peer.send(JSON.stringify({
|
|
563
|
-
type: "error",
|
|
564
|
-
error: `Provider process exited unexpectedly (code: ${exitCode}${signal ? ", signal: " + signal : ""})${details}`,
|
|
565
|
-
requestId: msg.requestId
|
|
566
|
-
}));
|
|
567
|
-
} else if (exitCode === null && signal) {
|
|
568
|
-
const summary = summarizeProviderProcessError(nonJsonOutput, 700);
|
|
569
|
-
const details = summary ? ` \u2014 ${summary}` : "";
|
|
570
|
-
peer.send(JSON.stringify({
|
|
571
|
-
type: "error",
|
|
572
|
-
error: `Provider process was killed by signal ${signal}${details}`,
|
|
573
|
-
requestId: msg.requestId
|
|
574
|
-
}));
|
|
575
|
-
console.error("[WS] Provider process killed by signal", {
|
|
576
|
-
providerId: selection.providerId,
|
|
577
|
-
modelKey: selection.modelKey,
|
|
578
|
-
permissionMode: mode,
|
|
579
|
-
requestId: msg.requestId,
|
|
580
|
-
signal,
|
|
581
|
-
nonJsonOutput: nonJsonOutput.slice(-25)
|
|
582
|
-
});
|
|
583
|
-
} else {
|
|
584
|
-
if (!emittedRenderableContent) {
|
|
585
|
-
const summary = summarizeProviderProcessError(nonJsonOutput, 700);
|
|
586
|
-
const fallbackText = summary ? `Provider returned no structured response.
|
|
587
|
-
|
|
588
|
-
Raw output:
|
|
589
|
-
${summary}` : "Provider completed without returning visible response content.";
|
|
590
|
-
sendAssistantText(peer, fallbackText, state.providerSessionId);
|
|
591
|
-
}
|
|
592
|
-
peer.send(JSON.stringify({ type: "done", requestId: msg.requestId }));
|
|
593
|
-
}
|
|
594
|
-
state.pendingMessage = null;
|
|
595
|
-
}
|
|
596
|
-
} finally {
|
|
597
|
-
state.proc = null;
|
|
598
|
-
}
|
|
599
|
-
},
|
|
600
|
-
onError(error) {
|
|
601
|
-
try {
|
|
602
|
-
peer.send(JSON.stringify({
|
|
603
|
-
type: "error",
|
|
604
|
-
error: `Provider process error: ${error.message}`,
|
|
605
|
-
requestId: msg.requestId
|
|
606
|
-
}));
|
|
607
|
-
} finally {
|
|
608
|
-
state.pendingMessage = null;
|
|
609
|
-
state.pendingTools = [];
|
|
610
|
-
state.proc = null;
|
|
611
|
-
}
|
|
612
|
-
}
|
|
613
|
-
}
|
|
614
|
-
);
|
|
615
|
-
} catch (error) {
|
|
616
|
-
const errorMsg = error instanceof Error ? error.message : "Failed to start provider process";
|
|
217
|
+
} else {
|
|
617
218
|
peer.send(JSON.stringify({
|
|
618
|
-
type: "
|
|
619
|
-
|
|
620
|
-
requestId: msg.requestId
|
|
219
|
+
type: "subscribed",
|
|
220
|
+
conversationId: msg.conversationId
|
|
621
221
|
}));
|
|
622
|
-
state.pendingMessage = null;
|
|
623
|
-
state.pendingTools = [];
|
|
624
|
-
state.proc = null;
|
|
625
222
|
}
|
|
626
223
|
}
|
|
224
|
+
function handleResetContext(peer) {
|
|
225
|
+
const conn = getPeerConnection(peer.id);
|
|
226
|
+
console.log("[WS] Reset context requested for peer:", peer.id);
|
|
227
|
+
if (conn.conversationId) {
|
|
228
|
+
jobQueue.resetContext(conn.conversationId);
|
|
229
|
+
}
|
|
230
|
+
peer.send(JSON.stringify({ type: "context_reset" }));
|
|
231
|
+
console.log("[WS] Context reset completed for peer:", peer.id);
|
|
232
|
+
}
|
|
627
233
|
|
|
628
234
|
export { _ws as default };
|
|
629
235
|
//# sourceMappingURL=_ws.mjs.map
|