@schoolai/shipyard 3.2.1 → 3.2.2-nightly.20260422.0
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/{auth-W5HLP2KF.js → auth-LS3NBD42.js} +3 -3
- package/dist/{chunk-Q6ZPJJM4.js → chunk-67VJDX7G.js} +3 -3
- package/dist/{chunk-YOMKRHVO.js → chunk-CCW5QAUH.js} +2 -2
- package/dist/{chunk-3UYLOJ6E.js → chunk-DNIC3FOH.js} +3 -3
- package/dist/{chunk-3UYLOJ6E.js.map → chunk-DNIC3FOH.js.map} +1 -1
- package/dist/{chunk-IHSXN66C.js → chunk-GLH3V7NG.js} +2 -2
- package/dist/{chunk-JSUYB74F.js → chunk-JE3N5IWM.js} +5 -5
- package/dist/{chunk-CF336H24.js → chunk-M5M6VC5F.js} +2 -2
- package/dist/{chunk-CF336H24.js.map → chunk-M5M6VC5F.js.map} +1 -1
- package/dist/{chunk-FMMRZTOF.js → chunk-YZVF3NFD.js} +5 -2
- package/dist/chunk-YZVF3NFD.js.map +1 -0
- package/dist/index.js +8 -8
- package/dist/login-WPEVCQRE.js +19 -0
- package/dist/{logout-XYE7EJM7.js → logout-M7F7HXUU.js} +5 -5
- package/dist/{mcp-servers-XWOPKJ6F.js → mcp-servers-MUVTAMDT.js} +4 -4
- package/dist/{roi-YQ6OLVHX.js → roi-ZCVNBSTO.js} +3 -3
- package/dist/{serve-P5WC5JIT.js → serve-X4R5CDHD.js} +676 -288
- package/dist/{serve-P5WC5JIT.js.map → serve-X4R5CDHD.js.map} +1 -1
- package/dist/{start-2K7HFXHV.js → start-ARX3COMF.js} +8 -8
- package/package.json +2 -2
- package/dist/chunk-FMMRZTOF.js.map +0 -1
- package/dist/login-KSI4GTLM.js +0 -19
- /package/dist/{auth-W5HLP2KF.js.map → auth-LS3NBD42.js.map} +0 -0
- /package/dist/{chunk-Q6ZPJJM4.js.map → chunk-67VJDX7G.js.map} +0 -0
- /package/dist/{chunk-YOMKRHVO.js.map → chunk-CCW5QAUH.js.map} +0 -0
- /package/dist/{chunk-IHSXN66C.js.map → chunk-GLH3V7NG.js.map} +0 -0
- /package/dist/{chunk-JSUYB74F.js.map → chunk-JE3N5IWM.js.map} +0 -0
- /package/dist/{login-KSI4GTLM.js.map → login-WPEVCQRE.js.map} +0 -0
- /package/dist/{logout-XYE7EJM7.js.map → logout-M7F7HXUU.js.map} +0 -0
- /package/dist/{mcp-servers-XWOPKJ6F.js.map → mcp-servers-MUVTAMDT.js.map} +0 -0
- /package/dist/{roi-YQ6OLVHX.js.map → roi-ZCVNBSTO.js.map} +0 -0
- /package/dist/{start-2K7HFXHV.js.map → start-ARX3COMF.js.map} +0 -0
|
@@ -47,11 +47,11 @@ import {
|
|
|
47
47
|
VaultKeyPutRequestSchema,
|
|
48
48
|
VaultKeyPutResponseSchema,
|
|
49
49
|
classifyClaudeCodeCompatibility
|
|
50
|
-
} from "./chunk-
|
|
50
|
+
} from "./chunk-YZVF3NFD.js";
|
|
51
51
|
import "./chunk-EHQITHQX.js";
|
|
52
52
|
import {
|
|
53
53
|
loadAuthToken
|
|
54
|
-
} from "./chunk-
|
|
54
|
+
} from "./chunk-GLH3V7NG.js";
|
|
55
55
|
import {
|
|
56
56
|
DiscoveryStateSchema,
|
|
57
57
|
detectMCPServers,
|
|
@@ -62,19 +62,19 @@ import {
|
|
|
62
62
|
redactEnv,
|
|
63
63
|
resolveEnabledMcpServers,
|
|
64
64
|
resolveStdioEnv
|
|
65
|
-
} from "./chunk-
|
|
65
|
+
} from "./chunk-67VJDX7G.js";
|
|
66
66
|
import {
|
|
67
67
|
createChildLogger,
|
|
68
68
|
flushLogger,
|
|
69
69
|
logger
|
|
70
|
-
} from "./chunk-
|
|
70
|
+
} from "./chunk-DNIC3FOH.js";
|
|
71
71
|
import {
|
|
72
72
|
external_exports,
|
|
73
73
|
getShipyardHome,
|
|
74
74
|
isVanillaAgentMode,
|
|
75
75
|
toJSONSchema,
|
|
76
76
|
validateEnv
|
|
77
|
-
} from "./chunk-
|
|
77
|
+
} from "./chunk-M5M6VC5F.js";
|
|
78
78
|
import {
|
|
79
79
|
detectSkills
|
|
80
80
|
} from "./chunk-DPMRSLYJ.js";
|
|
@@ -87,7 +87,7 @@ import { mkdir as mkdir24, realpath as realpath2 } from "fs/promises";
|
|
|
87
87
|
import { join as join55 } from "path";
|
|
88
88
|
import { query as query2 } from "@anthropic-ai/claude-agent-sdk";
|
|
89
89
|
|
|
90
|
-
// ../../node_modules/.pnpm/@loro-extended+change@6.0.0-beta.0_loro-crdt@1.
|
|
90
|
+
// ../../node_modules/.pnpm/@loro-extended+change@6.0.0-beta.0_loro-crdt@1.11.1/node_modules/@loro-extended/change/dist/index.js
|
|
91
91
|
import { LoroDoc } from "loro-crdt";
|
|
92
92
|
import { isContainer, isContainerId } from "loro-crdt";
|
|
93
93
|
import {
|
|
@@ -5226,7 +5226,7 @@ function getImplicitContext() {
|
|
|
5226
5226
|
return result;
|
|
5227
5227
|
}
|
|
5228
5228
|
|
|
5229
|
-
// ../../node_modules/.pnpm/@loro-extended+repo@6.0.0-beta.0_loro-crdt@1.
|
|
5229
|
+
// ../../node_modules/.pnpm/@loro-extended+repo@6.0.0-beta.0_loro-crdt@1.11.1/node_modules/@loro-extended/repo/dist/index.js
|
|
5230
5230
|
import { LoroDoc as LoroDoc2 } from "loro-crdt";
|
|
5231
5231
|
import { VersionVector } from "loro-crdt";
|
|
5232
5232
|
import { VersionVector as VersionVector2 } from "loro-crdt";
|
|
@@ -6941,7 +6941,7 @@ var makeCreator = (arg) => {
|
|
|
6941
6941
|
var create = makeCreator();
|
|
6942
6942
|
var constructorString = Object.prototype.constructor.toString();
|
|
6943
6943
|
|
|
6944
|
-
// ../../node_modules/.pnpm/@loro-extended+repo@6.0.0-beta.0_loro-crdt@1.
|
|
6944
|
+
// ../../node_modules/.pnpm/@loro-extended+repo@6.0.0-beta.0_loro-crdt@1.11.1/node_modules/@loro-extended/repo/dist/index.js
|
|
6945
6945
|
import { EphemeralStore } from "loro-crdt";
|
|
6946
6946
|
import {
|
|
6947
6947
|
LoroDoc as LoroDoc22
|
|
@@ -12461,7 +12461,7 @@ function encodeCBOR(data) {
|
|
|
12461
12461
|
return output;
|
|
12462
12462
|
}
|
|
12463
12463
|
|
|
12464
|
-
// ../../node_modules/.pnpm/@loro-extended+wire-format@0.1.1-beta.0_loro-crdt@1.
|
|
12464
|
+
// ../../node_modules/.pnpm/@loro-extended+wire-format@0.1.1-beta.0_loro-crdt@1.11.1/node_modules/@loro-extended/wire-format/dist/index.js
|
|
12465
12465
|
import { VersionVector as VersionVector4 } from "loro-crdt";
|
|
12466
12466
|
var WIRE_VERSION = 2;
|
|
12467
12467
|
var HEADER_SIZE = 6;
|
|
@@ -13249,7 +13249,7 @@ var FragmentReassembler = class {
|
|
|
13249
13249
|
}
|
|
13250
13250
|
};
|
|
13251
13251
|
|
|
13252
|
-
// ../../node_modules/.pnpm/@loro-extended+adapter-webrtc@6.0.0-beta.0_loro-crdt@1.
|
|
13252
|
+
// ../../node_modules/.pnpm/@loro-extended+adapter-webrtc@6.0.0-beta.0_loro-crdt@1.11.1/node_modules/@loro-extended/adapter-webrtc/dist/index.js
|
|
13253
13253
|
var DEFAULT_FRAGMENT_THRESHOLD = 200 * 1024;
|
|
13254
13254
|
var WebRtcDataChannelAdapter = class extends Adapter {
|
|
13255
13255
|
/**
|
|
@@ -29036,7 +29036,7 @@ function resolveTaskIdFromResult(resultText) {
|
|
|
29036
29036
|
return match2?.[1] ?? null;
|
|
29037
29037
|
}
|
|
29038
29038
|
function ccFileToStructuredTask(file) {
|
|
29039
|
-
const
|
|
29039
|
+
const createdAt = file.createdAt ?? 0;
|
|
29040
29040
|
return {
|
|
29041
29041
|
id: file.id,
|
|
29042
29042
|
subject: file.subject,
|
|
@@ -29046,8 +29046,8 @@ function ccFileToStructuredTask(file) {
|
|
|
29046
29046
|
owner: file.owner,
|
|
29047
29047
|
blocks: file.blocks,
|
|
29048
29048
|
blockedBy: file.blockedBy,
|
|
29049
|
-
createdAt
|
|
29050
|
-
updatedAt: file.updatedAt ??
|
|
29049
|
+
createdAt,
|
|
29050
|
+
updatedAt: file.updatedAt ?? createdAt
|
|
29051
29051
|
};
|
|
29052
29052
|
}
|
|
29053
29053
|
var CC_STATUS_MAP = {
|
|
@@ -29719,8 +29719,12 @@ async function detectModels() {
|
|
|
29719
29719
|
try {
|
|
29720
29720
|
await run("which", ["claude"]);
|
|
29721
29721
|
const supported = getSupportedEfforts();
|
|
29722
|
-
const filter = (efforts) => efforts.filter((e) => supported.has(e));
|
|
29723
|
-
const pickDefault = (desired, efforts) =>
|
|
29722
|
+
const filter = (efforts) => efforts.filter((e) => e === "auto" || supported.has(e));
|
|
29723
|
+
const pickDefault = (desired, efforts) => {
|
|
29724
|
+
if (efforts.includes(desired)) return desired;
|
|
29725
|
+
const concrete = efforts.filter((e) => e !== "auto");
|
|
29726
|
+
return concrete[concrete.length - 1] ?? "low";
|
|
29727
|
+
};
|
|
29724
29728
|
const opusEfforts = filter(["low", "medium", "high", "xhigh", "max", "auto"]);
|
|
29725
29729
|
const sonnetEfforts = filter(["low", "medium", "high", "max", "auto"]);
|
|
29726
29730
|
models.push(
|
|
@@ -31450,7 +31454,7 @@ function nanoid(size2 = 21) {
|
|
|
31450
31454
|
}
|
|
31451
31455
|
|
|
31452
31456
|
// src/services/bootstrap/signaling.ts
|
|
31453
|
-
var DAEMON_NPM_VERSION = true ? "3.2.
|
|
31457
|
+
var DAEMON_NPM_VERSION = true ? "3.2.2" : "unknown";
|
|
31454
31458
|
function createDaemonSignaling(config2) {
|
|
31455
31459
|
const agentId = config2.agentId ?? nanoid();
|
|
31456
31460
|
function send(msg) {
|
|
@@ -37227,12 +37231,19 @@ var SANDBOX_COLOR_PALETTE = `## Color Palette
|
|
|
37227
37231
|
- Group by **category**, not sequence. All items of one type share one ramp.
|
|
37228
37232
|
- Gray for neutral/structural elements.
|
|
37229
37233
|
- Reserve blue/green/red for semantic meaning (info, success, danger). Use teal, amber, coral for general categories.
|
|
37230
|
-
- When placing text on a colored background (badges, pills, cards, tags), use
|
|
37234
|
+
- When placing text on a colored background (badges, pills, cards, tags), use a stop from the far end of the same ramp: on a light fill (stop 50) use stop 800 for text; on a dark fill (stop 800) use stop 50 or 200 for text. Never plain black or generic gray.
|
|
37231
37235
|
|
|
37232
37236
|
**Light/dark mode stop assignment** \u2014 use only stops from the table, never off-table hex values:
|
|
37233
37237
|
- **Light mode**: 50 fill + 600 stroke + **800 title / 600 subtitle**
|
|
37234
|
-
- **Dark mode**: 800 fill + 200 stroke + **
|
|
37238
|
+
- **Dark mode**: 800 fill + 200 stroke + **50 title / 200 subtitle**
|
|
37235
37239
|
- Title and subtitle MUST use different stops \u2014 same stop reads flat even with different font weights. When a box has both a title and subtitle, the weight difference alone is not enough; you need two distinct color stops for visual hierarchy.
|
|
37240
|
+
- Never use stop 400 as body text in either mode \u2014 it's a mid-tone that fails WCAG AA (~3:1) on near-white or near-black page backgrounds. Stop 400 is fine as a fill or as text on a 50/200 background; just not as text on the page bg.
|
|
37241
|
+
- Stop 600 is tuned for light-mode subtitles on 50 fills \u2014 do not reach for it as text in dark mode, where it approaches the 800 fill and reads muddy.
|
|
37242
|
+
- For muted text, always use \`var(--text-muted)\` \u2014 never a hand-picked gray hex (e.g. gray-400/600) as a muted substitute.
|
|
37243
|
+
|
|
37244
|
+
**Container contrast on dark mode** \u2014 an 800-fill box on a near-black page bg nearly disappears (gray-800 on gray-950 reads at ~1.8:1 container-vs-bg). When placing 800-fill boxes in dark mode, do ONE of:
|
|
37245
|
+
- Add a stroke using stop 200 from the same ramp (recommended \u2014 the 200 stroke is luminous enough to define the container edge).
|
|
37246
|
+
- Place the box on \`var(--bg-surface)\` or \`var(--bg-overlay)\` instead of the raw page bg.
|
|
37236
37247
|
|
|
37237
37248
|
**Dark mode is mandatory** \u2014 mental test: if the background were near-black, would every text element still be readable? Always use \`var(--text)\` or \`var(--text-muted)\` for text, never hardcoded dark colors.`;
|
|
37238
37249
|
var SANDBOX_ANTI_SLOP = `## Design Quality
|
|
@@ -50290,7 +50301,7 @@ var PluginKey = class {
|
|
|
50290
50301
|
// src/services/plan/plan-content-bridge.ts
|
|
50291
50302
|
import { LoroMap as LoroMap4 } from "loro-crdt";
|
|
50292
50303
|
|
|
50293
|
-
// ../../node_modules/.pnpm/loro-prosemirror@0.4.2_loro-crdt@1.
|
|
50304
|
+
// ../../node_modules/.pnpm/loro-prosemirror@0.4.2_loro-crdt@1.11.1_prosemirror-model@1.25.4_prosemirror-state@1.4.4_prosemirror-view@1.41.6/node_modules/loro-prosemirror/dist/index.mjs
|
|
50294
50305
|
import { LoroMap as LoroMap3, LoroText as LoroText3, LoroList as LoroList3, isContainer as isContainer2, EphemeralStore as EphemeralStore2, Cursor, Awareness, UndoManager } from "loro-crdt";
|
|
50295
50306
|
|
|
50296
50307
|
// ../../node_modules/.pnpm/prosemirror-view@1.41.6/node_modules/prosemirror-view/dist/index.js
|
|
@@ -55621,7 +55632,7 @@ var simpleDiffString = (a, b2) => {
|
|
|
55621
55632
|
};
|
|
55622
55633
|
var simpleDiff = simpleDiffString;
|
|
55623
55634
|
|
|
55624
|
-
// ../../node_modules/.pnpm/loro-prosemirror@0.4.2_loro-crdt@1.
|
|
55635
|
+
// ../../node_modules/.pnpm/loro-prosemirror@0.4.2_loro-crdt@1.11.1_prosemirror-model@1.25.4_prosemirror-state@1.4.4_prosemirror-view@1.41.6/node_modules/loro-prosemirror/dist/index.mjs
|
|
55625
55636
|
var ROOT_DOC_KEY = "doc";
|
|
55626
55637
|
var ATTRIBUTES_KEY = "attributes";
|
|
55627
55638
|
var CHILDREN_KEY = "children";
|
|
@@ -72907,10 +72918,27 @@ function hasErrorCode(err, code2) {
|
|
|
72907
72918
|
const record = err;
|
|
72908
72919
|
return record.code === code2;
|
|
72909
72920
|
}
|
|
72921
|
+
function isCliFlagEffort(effort) {
|
|
72922
|
+
switch (effort) {
|
|
72923
|
+
case "low":
|
|
72924
|
+
case "medium":
|
|
72925
|
+
case "high":
|
|
72926
|
+
case "xhigh":
|
|
72927
|
+
case "max":
|
|
72928
|
+
return true;
|
|
72929
|
+
case "auto":
|
|
72930
|
+
return false;
|
|
72931
|
+
default: {
|
|
72932
|
+
const _exhaustive = effort;
|
|
72933
|
+
return _exhaustive;
|
|
72934
|
+
}
|
|
72935
|
+
}
|
|
72936
|
+
}
|
|
72910
72937
|
var FALLBACK_PRIORITY = ["high", "max", "medium", "low"];
|
|
72911
72938
|
var warnedEffortCoercions = /* @__PURE__ */ new Set();
|
|
72912
72939
|
function resolveSupportedEffort(effort, log) {
|
|
72913
72940
|
if (!effort) return void 0;
|
|
72941
|
+
if (!isCliFlagEffort(effort)) return void 0;
|
|
72914
72942
|
const supported = getSupportedEfforts();
|
|
72915
72943
|
if (supported.has(effort)) return effort;
|
|
72916
72944
|
const fallback = FALLBACK_PRIORITY.find((e) => supported.has(e));
|
|
@@ -85417,7 +85445,7 @@ var PlanHandler = class {
|
|
|
85417
85445
|
this.#deps.enqueueAsync(async () => {
|
|
85418
85446
|
try {
|
|
85419
85447
|
await this.#preparePlanForResolution(toolUseId, opts?.comments ?? []);
|
|
85420
|
-
|
|
85448
|
+
this.#applyPermissionMode(opts?.permissionMode);
|
|
85421
85449
|
} catch (err) {
|
|
85422
85450
|
this.#deps.log({
|
|
85423
85451
|
event: "plan_continue_preparation_failed",
|
|
@@ -85682,14 +85710,9 @@ var PlanHandler = class {
|
|
|
85682
85710
|
});
|
|
85683
85711
|
});
|
|
85684
85712
|
}
|
|
85685
|
-
|
|
85713
|
+
#applyPermissionMode(permissionMode) {
|
|
85686
85714
|
if (!permissionMode) return;
|
|
85687
|
-
this.#deps.
|
|
85688
|
-
await this.#deps.getSubprocess()?.setPermissionMode(permissionMode);
|
|
85689
|
-
this.#deps.getSendControlMessage()?.({
|
|
85690
|
-
type: "settings_ack",
|
|
85691
|
-
settings: { permissionMode }
|
|
85692
|
-
});
|
|
85715
|
+
this.#deps.applyTaskSettings({ permissionMode });
|
|
85693
85716
|
}
|
|
85694
85717
|
async #injectDecisionMessage(decision, feedback) {
|
|
85695
85718
|
const reviewer = this.#deps.getHumanParticipantName();
|
|
@@ -86274,6 +86297,22 @@ function planCompactionSnapshot(input) {
|
|
|
86274
86297
|
}
|
|
86275
86298
|
|
|
86276
86299
|
// src/services/task/resource-push-manager.ts
|
|
86300
|
+
async function writeResourceSynthetics(deps, messages) {
|
|
86301
|
+
const allContent = [];
|
|
86302
|
+
for (const msg of messages) {
|
|
86303
|
+
await deps.store.appendMessage({
|
|
86304
|
+
channelId: deps.channelId,
|
|
86305
|
+
messageId: crypto.randomUUID(),
|
|
86306
|
+
participantId: deps.humanParticipantId,
|
|
86307
|
+
senderKind: "human",
|
|
86308
|
+
content: msg.content,
|
|
86309
|
+
timestamp: Date.now(),
|
|
86310
|
+
isSynthetic: true
|
|
86311
|
+
});
|
|
86312
|
+
for (const block2 of msg.content) allContent.push(block2);
|
|
86313
|
+
}
|
|
86314
|
+
return allContent;
|
|
86315
|
+
}
|
|
86277
86316
|
var MAX_PUSHES_PER_TURN = 3;
|
|
86278
86317
|
var ResourcePushManager = class {
|
|
86279
86318
|
#deps;
|
|
@@ -86424,20 +86463,14 @@ var ResourcePushManager = class {
|
|
|
86424
86463
|
});
|
|
86425
86464
|
}
|
|
86426
86465
|
async #writeSynthetics(messages) {
|
|
86427
|
-
|
|
86428
|
-
|
|
86429
|
-
|
|
86466
|
+
return writeResourceSynthetics(
|
|
86467
|
+
{
|
|
86468
|
+
store: this.#deps.store,
|
|
86430
86469
|
channelId: this.#deps.channelId,
|
|
86431
|
-
|
|
86432
|
-
|
|
86433
|
-
|
|
86434
|
-
|
|
86435
|
-
timestamp: Date.now(),
|
|
86436
|
-
isSynthetic: true
|
|
86437
|
-
});
|
|
86438
|
-
for (const block2 of msg.content) allContent.push(block2);
|
|
86439
|
-
}
|
|
86440
|
-
return allContent;
|
|
86470
|
+
humanParticipantId: this.#deps.humanParticipantId
|
|
86471
|
+
},
|
|
86472
|
+
messages
|
|
86473
|
+
);
|
|
86441
86474
|
}
|
|
86442
86475
|
#deliverToSubprocess(allContent, uriCount) {
|
|
86443
86476
|
const state = this.#deps.getState();
|
|
@@ -87284,6 +87317,9 @@ var SideThreadRegistry = class {
|
|
|
87284
87317
|
);
|
|
87285
87318
|
entry.thread = thread;
|
|
87286
87319
|
this.#threads.set(params.threadId, entry);
|
|
87320
|
+
if (params.initialPermissionMode !== void 0) {
|
|
87321
|
+
thread.applyPermissionMode(params.initialPermissionMode);
|
|
87322
|
+
}
|
|
87287
87323
|
this.#persist().catch((err) => {
|
|
87288
87324
|
const errMsg = err instanceof Error ? err.message : String(err);
|
|
87289
87325
|
this.#deps.log({
|
|
@@ -88002,7 +88038,7 @@ async function removeStaleFiles(dir, targetIds, hashes) {
|
|
|
88002
88038
|
}
|
|
88003
88039
|
for (const entry of entries) {
|
|
88004
88040
|
if (!entry.endsWith(".json")) continue;
|
|
88005
|
-
const id = entry.
|
|
88041
|
+
const id = entry.slice(0, -".json".length);
|
|
88006
88042
|
if (!targetIds.has(id) && hashes.has(id)) {
|
|
88007
88043
|
await unlink7(join41(dir, entry)).catch(() => {
|
|
88008
88044
|
});
|
|
@@ -88010,14 +88046,15 @@ async function removeStaleFiles(dir, targetIds, hashes) {
|
|
|
88010
88046
|
}
|
|
88011
88047
|
}
|
|
88012
88048
|
}
|
|
88013
|
-
function createCCTaskFileWriter(
|
|
88049
|
+
function createCCTaskFileWriter(initialDir, log) {
|
|
88014
88050
|
const lastWrittenHashes = /* @__PURE__ */ new Map();
|
|
88051
|
+
let currentDir = initialDir;
|
|
88015
88052
|
let debounceTimer = null;
|
|
88016
88053
|
let pendingTasks = null;
|
|
88017
88054
|
let disposed = false;
|
|
88018
88055
|
let flushInProgress = Promise.resolve();
|
|
88019
88056
|
const DEBOUNCE_MS3 = 200;
|
|
88020
|
-
async function flush(tasks) {
|
|
88057
|
+
async function flush(dir, tasks) {
|
|
88021
88058
|
if (disposed) return;
|
|
88022
88059
|
try {
|
|
88023
88060
|
await mkdir17(dir, { recursive: true });
|
|
@@ -88033,7 +88070,7 @@ function createCCTaskFileWriter(dir, log) {
|
|
|
88033
88070
|
});
|
|
88034
88071
|
}
|
|
88035
88072
|
}
|
|
88036
|
-
function scheduleFlush(tasks) {
|
|
88073
|
+
function scheduleFlush(dir, tasks) {
|
|
88037
88074
|
pendingTasks = tasks;
|
|
88038
88075
|
if (debounceTimer) clearTimeout(debounceTimer);
|
|
88039
88076
|
debounceTimer = setTimeout(() => {
|
|
@@ -88041,19 +88078,42 @@ function createCCTaskFileWriter(dir, log) {
|
|
|
88041
88078
|
const snapshot = pendingTasks;
|
|
88042
88079
|
pendingTasks = null;
|
|
88043
88080
|
if (snapshot) {
|
|
88044
|
-
flushInProgress = flushInProgress.then(() => flush(snapshot));
|
|
88081
|
+
flushInProgress = flushInProgress.then(() => flush(dir, snapshot));
|
|
88045
88082
|
}
|
|
88046
88083
|
}, DEBOUNCE_MS3);
|
|
88047
88084
|
}
|
|
88048
88085
|
return {
|
|
88049
|
-
dir
|
|
88086
|
+
get dir() {
|
|
88087
|
+
return currentDir;
|
|
88088
|
+
},
|
|
88050
88089
|
writeMergedTasks(tasks) {
|
|
88051
88090
|
if (disposed) return;
|
|
88052
|
-
|
|
88091
|
+
if (currentDir === null) {
|
|
88092
|
+
pendingTasks = tasks;
|
|
88093
|
+
return;
|
|
88094
|
+
}
|
|
88095
|
+
scheduleFlush(currentDir, tasks);
|
|
88096
|
+
},
|
|
88097
|
+
async setDir(dir) {
|
|
88098
|
+
if (disposed) return;
|
|
88099
|
+
if (currentDir !== null) return;
|
|
88100
|
+
currentDir = dir;
|
|
88101
|
+
if (debounceTimer) {
|
|
88102
|
+
clearTimeout(debounceTimer);
|
|
88103
|
+
debounceTimer = null;
|
|
88104
|
+
}
|
|
88105
|
+
const snapshot = pendingTasks;
|
|
88106
|
+
pendingTasks = null;
|
|
88107
|
+
if (snapshot) {
|
|
88108
|
+
const flushPromise = flush(dir, snapshot);
|
|
88109
|
+
flushInProgress = flushInProgress.then(() => flushPromise);
|
|
88110
|
+
await flushPromise;
|
|
88111
|
+
}
|
|
88053
88112
|
},
|
|
88054
88113
|
dispose() {
|
|
88055
88114
|
disposed = true;
|
|
88056
88115
|
if (debounceTimer) clearTimeout(debounceTimer);
|
|
88116
|
+
debounceTimer = null;
|
|
88057
88117
|
pendingTasks = null;
|
|
88058
88118
|
}
|
|
88059
88119
|
};
|
|
@@ -88105,6 +88165,39 @@ function applyDepEdgeAdditions(merged, depEdges) {
|
|
|
88105
88165
|
}
|
|
88106
88166
|
}
|
|
88107
88167
|
}
|
|
88168
|
+
function applyOverlayToMap(base3, overlay) {
|
|
88169
|
+
const merged = new Map(base3);
|
|
88170
|
+
for (const userTask of overlay.userTasks) {
|
|
88171
|
+
const baseTask = merged.get(userTask.id);
|
|
88172
|
+
if (!baseTask || baseTask.updatedAt <= userTask.updatedAt) {
|
|
88173
|
+
merged.set(userTask.id, userTask);
|
|
88174
|
+
}
|
|
88175
|
+
}
|
|
88176
|
+
for (const id of overlay.removedIds) merged.delete(id);
|
|
88177
|
+
for (const [id, status] of Object.entries(overlay.statusOverrides)) {
|
|
88178
|
+
const task = merged.get(id);
|
|
88179
|
+
if (task) merged.set(id, { ...task, status });
|
|
88180
|
+
}
|
|
88181
|
+
applyDepEdgeRemovals(merged, overlay.removedDepEdges);
|
|
88182
|
+
applyDepEdgeAdditions(merged, overlay.depEdges);
|
|
88183
|
+
return merged;
|
|
88184
|
+
}
|
|
88185
|
+
function computeTodoProgress(tasks) {
|
|
88186
|
+
if (tasks.size === 0) return void 0;
|
|
88187
|
+
let completed = 0;
|
|
88188
|
+
let currentActivity = null;
|
|
88189
|
+
for (const task of tasks.values()) {
|
|
88190
|
+
if (task.status === "completed" || task.status === "cancelled") completed++;
|
|
88191
|
+
if (task.status === "in_progress" && !currentActivity) {
|
|
88192
|
+
currentActivity = task.activeForm ?? task.subject;
|
|
88193
|
+
}
|
|
88194
|
+
}
|
|
88195
|
+
return {
|
|
88196
|
+
todoCompleted: completed,
|
|
88197
|
+
todoTotal: tasks.size,
|
|
88198
|
+
currentActivity
|
|
88199
|
+
};
|
|
88200
|
+
}
|
|
88108
88201
|
var StructuredTaskTracker = class {
|
|
88109
88202
|
#deps;
|
|
88110
88203
|
#structuredTasks = /* @__PURE__ */ new Map();
|
|
@@ -88114,9 +88207,12 @@ var StructuredTaskTracker = class {
|
|
|
88114
88207
|
#currentOverlay = null;
|
|
88115
88208
|
#ccTaskWatcherDispose = null;
|
|
88116
88209
|
#suppressWriteThrough = false;
|
|
88117
|
-
#ccTaskFileWriter
|
|
88210
|
+
#ccTaskFileWriter;
|
|
88211
|
+
#restoreInProgress = false;
|
|
88118
88212
|
constructor(deps) {
|
|
88119
88213
|
this.#deps = deps;
|
|
88214
|
+
this.#ccTaskFileWriter = createCCTaskFileWriter(null, deps.log);
|
|
88215
|
+
this.#restoreInProgress = deps.restoreInProgress ?? false;
|
|
88120
88216
|
}
|
|
88121
88217
|
get currentOverlay() {
|
|
88122
88218
|
return this.#currentOverlay;
|
|
@@ -88124,8 +88220,7 @@ var StructuredTaskTracker = class {
|
|
|
88124
88220
|
dispose() {
|
|
88125
88221
|
this.#ccTaskWatcherDispose?.();
|
|
88126
88222
|
this.#ccTaskWatcherDispose = null;
|
|
88127
|
-
this.#ccTaskFileWriter
|
|
88128
|
-
this.#ccTaskFileWriter = null;
|
|
88223
|
+
this.#ccTaskFileWriter.dispose();
|
|
88129
88224
|
}
|
|
88130
88225
|
clearInMemoryTasks() {
|
|
88131
88226
|
this.#structuredTasks.clear();
|
|
@@ -88136,14 +88231,14 @@ var StructuredTaskTracker = class {
|
|
|
88136
88231
|
detachFileWatcher() {
|
|
88137
88232
|
this.#ccTaskWatcherDispose?.();
|
|
88138
88233
|
this.#ccTaskWatcherDispose = null;
|
|
88139
|
-
this.#ccTaskFileWriter
|
|
88140
|
-
this.#ccTaskFileWriter = null;
|
|
88234
|
+
this.#ccTaskFileWriter.dispose();
|
|
88235
|
+
this.#ccTaskFileWriter = createCCTaskFileWriter(null, this.#deps.log);
|
|
88141
88236
|
}
|
|
88142
88237
|
/**
|
|
88143
88238
|
* Attach a file watcher for CC task files. Called after init_received
|
|
88144
88239
|
* when we know the CC session ID, or immediately for resumed sessions.
|
|
88145
88240
|
*/
|
|
88146
|
-
attachFileWatcher(sessionId) {
|
|
88241
|
+
async attachFileWatcher(sessionId) {
|
|
88147
88242
|
if (this.#ccTaskWatcherDispose) {
|
|
88148
88243
|
this.#deps.log({
|
|
88149
88244
|
event: "file_watcher_already_attached",
|
|
@@ -88153,7 +88248,7 @@ var StructuredTaskTracker = class {
|
|
|
88153
88248
|
return;
|
|
88154
88249
|
}
|
|
88155
88250
|
const watcher = createCCTaskFileWatcher(sessionId, this.#deps.log);
|
|
88156
|
-
this.#ccTaskFileWriter
|
|
88251
|
+
await this.#ccTaskFileWriter.setDir(watcher.dir);
|
|
88157
88252
|
this.#initFileWatcher(watcher);
|
|
88158
88253
|
if (this.#currentOverlay) {
|
|
88159
88254
|
this.#flushStructuredTasks();
|
|
@@ -88161,9 +88256,24 @@ var StructuredTaskTracker = class {
|
|
|
88161
88256
|
}
|
|
88162
88257
|
/**
|
|
88163
88258
|
* Apply an overlay on top of CC tasks. Stores the overlay and re-flushes.
|
|
88259
|
+
* Exits restore mode — applyOverlay is the terminal piece of restoration
|
|
88260
|
+
* (disk reconcile is idempotent with the seeded overlay, so we flush once
|
|
88261
|
+
* here with full inputs instead of producing a partial pre-overlay flush).
|
|
88164
88262
|
*/
|
|
88165
88263
|
applyOverlay(overlay) {
|
|
88166
88264
|
this.#currentOverlay = overlay;
|
|
88265
|
+
this.#restoreInProgress = false;
|
|
88266
|
+
this.#flushStructuredTasks();
|
|
88267
|
+
}
|
|
88268
|
+
/**
|
|
88269
|
+
* Signal that restoration has finished for tasks that have no persisted
|
|
88270
|
+
* overlay to apply. Unblocks #flushStructuredTasks and runs one flush with
|
|
88271
|
+
* the current state (disk-only, no overlay). Must be called by the
|
|
88272
|
+
* restoration caller whenever applyOverlay will not be invoked.
|
|
88273
|
+
*/
|
|
88274
|
+
markRestoreComplete() {
|
|
88275
|
+
if (!this.#restoreInProgress) return;
|
|
88276
|
+
this.#restoreInProgress = false;
|
|
88167
88277
|
this.#flushStructuredTasks();
|
|
88168
88278
|
}
|
|
88169
88279
|
processStructuredTaskEvents(content) {
|
|
@@ -88272,48 +88382,16 @@ var StructuredTaskTracker = class {
|
|
|
88272
88382
|
* 6. Compute todo progress from the merged result
|
|
88273
88383
|
* 7. Push to updateStructuredTasks
|
|
88274
88384
|
*/
|
|
88275
|
-
#applyOverlayToMap(base3, overlay) {
|
|
88276
|
-
const merged = new Map(base3);
|
|
88277
|
-
for (const userTask of overlay.userTasks) {
|
|
88278
|
-
const baseTask = merged.get(userTask.id);
|
|
88279
|
-
if (!baseTask || baseTask.updatedAt <= userTask.updatedAt) {
|
|
88280
|
-
merged.set(userTask.id, userTask);
|
|
88281
|
-
}
|
|
88282
|
-
}
|
|
88283
|
-
for (const id of overlay.removedIds) merged.delete(id);
|
|
88284
|
-
for (const [id, status] of Object.entries(overlay.statusOverrides)) {
|
|
88285
|
-
const task = merged.get(id);
|
|
88286
|
-
if (task) merged.set(id, { ...task, status, updatedAt: Date.now() });
|
|
88287
|
-
}
|
|
88288
|
-
applyDepEdgeRemovals(merged, overlay.removedDepEdges);
|
|
88289
|
-
applyDepEdgeAdditions(merged, overlay.depEdges);
|
|
88290
|
-
return merged;
|
|
88291
|
-
}
|
|
88292
88385
|
#flushStructuredTasks() {
|
|
88386
|
+
if (this.#restoreInProgress) return;
|
|
88293
88387
|
const overlay = this.#currentOverlay ?? DEFAULT_TASK_OVERLAY;
|
|
88294
|
-
const merged =
|
|
88295
|
-
const todoProgress =
|
|
88388
|
+
const merged = applyOverlayToMap(this.#structuredTasks, overlay);
|
|
88389
|
+
const todoProgress = computeTodoProgress(merged);
|
|
88296
88390
|
this.#deps.updateStructuredTasks(Object.fromEntries(merged), todoProgress);
|
|
88297
88391
|
if (!this.#suppressWriteThrough) {
|
|
88298
|
-
this.#ccTaskFileWriter
|
|
88392
|
+
this.#ccTaskFileWriter.writeMergedTasks(merged);
|
|
88299
88393
|
}
|
|
88300
88394
|
}
|
|
88301
|
-
#computeTodoProgress(tasks) {
|
|
88302
|
-
if (tasks.size === 0) return void 0;
|
|
88303
|
-
let completed = 0;
|
|
88304
|
-
let currentActivity = null;
|
|
88305
|
-
for (const task of tasks.values()) {
|
|
88306
|
-
if (task.status === "completed" || task.status === "cancelled") completed++;
|
|
88307
|
-
if (task.status === "in_progress" && !currentActivity) {
|
|
88308
|
-
currentActivity = task.activeForm ?? task.subject;
|
|
88309
|
-
}
|
|
88310
|
-
}
|
|
88311
|
-
return {
|
|
88312
|
-
todoCompleted: completed,
|
|
88313
|
-
todoTotal: tasks.size,
|
|
88314
|
-
currentActivity
|
|
88315
|
-
};
|
|
88316
|
-
}
|
|
88317
88395
|
#initFileWatcher(watcher) {
|
|
88318
88396
|
this.#ccTaskWatcherDispose = watcher.watch((tasks) => {
|
|
88319
88397
|
this.#reconcileFromDisk(tasks);
|
|
@@ -89509,7 +89587,8 @@ var Task = class {
|
|
|
89509
89587
|
this.#structuredTaskTracker = new StructuredTaskTracker({
|
|
89510
89588
|
taskId: deps.taskId,
|
|
89511
89589
|
log: deps.log,
|
|
89512
|
-
updateStructuredTasks: deps.updateStructuredTasks
|
|
89590
|
+
updateStructuredTasks: deps.updateStructuredTasks,
|
|
89591
|
+
restoreInProgress: deps.restoreInProgress ?? false
|
|
89513
89592
|
});
|
|
89514
89593
|
this.#pushManager = new ResourcePushManager({
|
|
89515
89594
|
taskId: deps.taskId,
|
|
@@ -89545,7 +89624,15 @@ var Task = class {
|
|
|
89545
89624
|
queueKey: mainQueueKey(deps.taskId)
|
|
89546
89625
|
});
|
|
89547
89626
|
if (deps.existingSessionId) {
|
|
89548
|
-
|
|
89627
|
+
const sessionIdForLog = deps.existingSessionId;
|
|
89628
|
+
this.#structuredTaskTracker.attachFileWatcher(sessionIdForLog).catch((err) => {
|
|
89629
|
+
deps.log({
|
|
89630
|
+
event: "attach_file_watcher_failed",
|
|
89631
|
+
taskId: deps.taskId,
|
|
89632
|
+
sessionId: sessionIdForLog,
|
|
89633
|
+
error: err instanceof Error ? err.message : String(err)
|
|
89634
|
+
});
|
|
89635
|
+
});
|
|
89549
89636
|
}
|
|
89550
89637
|
this.#subagentManager = new SubagentManager({
|
|
89551
89638
|
taskId: deps.taskId,
|
|
@@ -89578,10 +89665,6 @@ var Task = class {
|
|
|
89578
89665
|
humanParticipantId: deps.humanParticipantId,
|
|
89579
89666
|
planRepo: deps.planRepo,
|
|
89580
89667
|
annotationStore: deps.annotationStore,
|
|
89581
|
-
/** Delegate to Thread's encapsulated setPermissionMode */
|
|
89582
|
-
getSubprocess: () => ({
|
|
89583
|
-
setPermissionMode: (mode) => this.#mainThread?.setSubprocessPermissionMode(mode) ?? Promise.resolve()
|
|
89584
|
-
}),
|
|
89585
89668
|
getSendControlMessage: () => this.#broadcastToAllPeers,
|
|
89586
89669
|
/**
|
|
89587
89670
|
* Route PlanHandler messages through the permission queue so they
|
|
@@ -89599,12 +89682,7 @@ var Task = class {
|
|
|
89599
89682
|
log: deps.log,
|
|
89600
89683
|
metricsCollector: deps.metricsCollector,
|
|
89601
89684
|
getLatestSettings: () => this.#latestSettings,
|
|
89602
|
-
|
|
89603
|
-
this.#latestSettings = { ...this.#latestSettings, ...patch };
|
|
89604
|
-
if (patch.permissionMode) {
|
|
89605
|
-
this.#mainThread?.applyPermissionMode(patch.permissionMode);
|
|
89606
|
-
}
|
|
89607
|
-
},
|
|
89685
|
+
applyTaskSettings: (settings) => this.#deps.applyTaskSettings(settings),
|
|
89608
89686
|
pushSyntheticMessage: (content) => {
|
|
89609
89687
|
this.#mainThread?.pushSyntheticMessage(content);
|
|
89610
89688
|
},
|
|
@@ -90039,19 +90117,35 @@ var Task = class {
|
|
|
90039
90117
|
return count + collabCount;
|
|
90040
90118
|
}
|
|
90041
90119
|
applyPermissionMode(mode) {
|
|
90042
|
-
this
|
|
90043
|
-
...this.#latestSettings,
|
|
90044
|
-
permissionMode: mode
|
|
90045
|
-
};
|
|
90046
|
-
this.#mainThread.applyPermissionMode(mode);
|
|
90120
|
+
this.applySettings({ permissionMode: mode });
|
|
90047
90121
|
}
|
|
90048
90122
|
applySettings(settings) {
|
|
90049
90123
|
this.#latestSettings = { ...this.#latestSettings, ...settings };
|
|
90050
|
-
if (settings.permissionMode) {
|
|
90051
|
-
|
|
90124
|
+
if (settings.permissionMode !== void 0) {
|
|
90125
|
+
const mode = settings.permissionMode;
|
|
90126
|
+
this.#mainThread.applyPermissionMode(mode);
|
|
90127
|
+
this.#propagateToSideThreads(
|
|
90128
|
+
(t) => t.applyPermissionMode(mode),
|
|
90129
|
+
"side_thread_permission_mode_failed"
|
|
90130
|
+
);
|
|
90131
|
+
}
|
|
90132
|
+
if (settings.model !== void 0) {
|
|
90133
|
+
const model = settings.model;
|
|
90134
|
+
this.#mainThread.setModel(model);
|
|
90135
|
+
this.#propagateToSideThreads((t) => t.setModel(model), "side_thread_model_failed");
|
|
90052
90136
|
}
|
|
90053
|
-
|
|
90054
|
-
|
|
90137
|
+
}
|
|
90138
|
+
#propagateToSideThreads(action, event) {
|
|
90139
|
+
for (const thread of this.#sideThreads.liveThreads()) {
|
|
90140
|
+
try {
|
|
90141
|
+
action(thread);
|
|
90142
|
+
} catch (err) {
|
|
90143
|
+
this.#deps.log({
|
|
90144
|
+
event,
|
|
90145
|
+
taskId: this.#deps.taskId,
|
|
90146
|
+
error: err instanceof Error ? err.message : String(err)
|
|
90147
|
+
});
|
|
90148
|
+
}
|
|
90055
90149
|
}
|
|
90056
90150
|
}
|
|
90057
90151
|
async setMcpServers(servers) {
|
|
@@ -90505,7 +90599,10 @@ var Task = class {
|
|
|
90505
90599
|
return this.#sideThreads.getMetadata(threadId);
|
|
90506
90600
|
}
|
|
90507
90601
|
createSideThread(params) {
|
|
90508
|
-
return this.#sideThreads.create(
|
|
90602
|
+
return this.#sideThreads.create({
|
|
90603
|
+
...params,
|
|
90604
|
+
initialPermissionMode: params.initialPermissionMode ?? this.#latestSettings.permissionMode
|
|
90605
|
+
});
|
|
90509
90606
|
}
|
|
90510
90607
|
async listSideThreads() {
|
|
90511
90608
|
return this.#sideThreads.list();
|
|
@@ -90676,13 +90773,21 @@ Use this context to maintain continuity. You have already done this work \u2014
|
|
|
90676
90773
|
try {
|
|
90677
90774
|
const taskResource = await registry.resolve(taskUri);
|
|
90678
90775
|
if ("text" in taskResource && !taskResource.text.includes('task-count="0"')) {
|
|
90679
|
-
this.#pushManager.markPushed(taskUri);
|
|
90680
90776
|
const rootMsg = buildRootMessage(
|
|
90681
90777
|
taskUri,
|
|
90682
90778
|
taskResource,
|
|
90683
90779
|
"Task Plan",
|
|
90684
90780
|
(/* @__PURE__ */ new Date()).toISOString()
|
|
90685
90781
|
);
|
|
90782
|
+
await writeResourceSynthetics(
|
|
90783
|
+
{
|
|
90784
|
+
store: this.#deps.store,
|
|
90785
|
+
channelId: this.#deps.channelId,
|
|
90786
|
+
humanParticipantId: this.#deps.humanParticipantId
|
|
90787
|
+
},
|
|
90788
|
+
[rootMsg]
|
|
90789
|
+
);
|
|
90790
|
+
this.#pushManager.markPushed(taskUri);
|
|
90686
90791
|
return rootMsg.content;
|
|
90687
90792
|
}
|
|
90688
90793
|
} catch {
|
|
@@ -90798,7 +90903,14 @@ Use this context to maintain continuity. You have already done this work \u2014
|
|
|
90798
90903
|
{
|
|
90799
90904
|
const sessionId = this.#mainThread.sessionId;
|
|
90800
90905
|
if (sessionId) {
|
|
90801
|
-
this.#structuredTaskTracker.attachFileWatcher(sessionId)
|
|
90906
|
+
this.#structuredTaskTracker.attachFileWatcher(sessionId).catch((err) => {
|
|
90907
|
+
this.#deps.log({
|
|
90908
|
+
event: "attach_file_watcher_failed",
|
|
90909
|
+
taskId: this.#deps.taskId,
|
|
90910
|
+
sessionId,
|
|
90911
|
+
error: err instanceof Error ? err.message : String(err)
|
|
90912
|
+
});
|
|
90913
|
+
});
|
|
90802
90914
|
}
|
|
90803
90915
|
}
|
|
90804
90916
|
this.#pushManager.onTurnStart();
|
|
@@ -91162,6 +91274,10 @@ Use this context to maintain continuity. You have already done this work \u2014
|
|
|
91162
91274
|
applyOverlay(overlay) {
|
|
91163
91275
|
this.#structuredTaskTracker.applyOverlay(overlay);
|
|
91164
91276
|
}
|
|
91277
|
+
/** See StructuredTaskTracker.markRestoreComplete. */
|
|
91278
|
+
markStructuredTaskRestoreComplete() {
|
|
91279
|
+
this.#structuredTaskTracker.markRestoreComplete();
|
|
91280
|
+
}
|
|
91165
91281
|
/** ---------------------------------------------------------------- */
|
|
91166
91282
|
/** Resource resolution */
|
|
91167
91283
|
/** ---------------------------------------------------------------- */
|
|
@@ -91643,6 +91759,7 @@ function buildInitialOverlayFromTemplate(template, now) {
|
|
|
91643
91759
|
subject: item2.content,
|
|
91644
91760
|
description: item2.description,
|
|
91645
91761
|
status: "pending",
|
|
91762
|
+
owner: "user",
|
|
91646
91763
|
blocks: [],
|
|
91647
91764
|
blockedBy: item2.deps,
|
|
91648
91765
|
createdAt: now,
|
|
@@ -91650,18 +91767,21 @@ function buildInitialOverlayFromTemplate(template, now) {
|
|
|
91650
91767
|
}));
|
|
91651
91768
|
return { ...DEFAULT_TASK_OVERLAY, userTasks };
|
|
91652
91769
|
}
|
|
91653
|
-
function applyInitialOverlayToOrchestrator(args) {
|
|
91654
|
-
const { taskId, initialOverlay, orchestratorRef, isTaskRegistered } = args;
|
|
91655
|
-
if (orchestratorRef
|
|
91656
|
-
|
|
91657
|
-
|
|
91770
|
+
async function applyInitialOverlayToOrchestrator(args) {
|
|
91771
|
+
const { taskId, initialOverlay, orchestratorRef, isTaskRegistered, taskStateStore } = args;
|
|
91772
|
+
if (!orchestratorRef || !isTaskRegistered(taskId)) {
|
|
91773
|
+
args.log({
|
|
91774
|
+
event: "initial_overlay_skipped",
|
|
91775
|
+
taskId,
|
|
91776
|
+
reason: orchestratorRef ? "task_removed" : "orchestrator_not_registered"
|
|
91777
|
+
});
|
|
91658
91778
|
return;
|
|
91659
91779
|
}
|
|
91660
|
-
|
|
91661
|
-
|
|
91662
|
-
|
|
91663
|
-
|
|
91664
|
-
|
|
91780
|
+
orchestratorRef.applyOverlay(initialOverlay);
|
|
91781
|
+
const merged = applyOverlayToMap(/* @__PURE__ */ new Map(), initialOverlay);
|
|
91782
|
+
const todoProgress = computeTodoProgress(merged);
|
|
91783
|
+
await taskStateStore.updateStructuredTasks(taskId, Object.fromEntries(merged), todoProgress);
|
|
91784
|
+
args.notifyTaskResourceChange(taskId);
|
|
91665
91785
|
}
|
|
91666
91786
|
|
|
91667
91787
|
// src/services/task/manager/task-manager.ts
|
|
@@ -91931,6 +92051,7 @@ var TaskManager = class {
|
|
|
91931
92051
|
task.orchestrator.applyPermissionMode(mode);
|
|
91932
92052
|
}
|
|
91933
92053
|
applyTaskSettings(taskId, settings) {
|
|
92054
|
+
if (Object.keys(settings).length === 0) return;
|
|
91934
92055
|
const composerPatch = {};
|
|
91935
92056
|
if (settings.model !== void 0) composerPatch.model = settings.model;
|
|
91936
92057
|
if (settings.reasoningEffort !== void 0)
|
|
@@ -91948,8 +92069,12 @@ var TaskManager = class {
|
|
|
91948
92069
|
});
|
|
91949
92070
|
}
|
|
91950
92071
|
const task = this.#tasks.get(taskId);
|
|
91951
|
-
if (!task)
|
|
92072
|
+
if (!task) {
|
|
92073
|
+
this.#deps.log({ event: "task_settings_no_orchestrator", taskId });
|
|
92074
|
+
return;
|
|
92075
|
+
}
|
|
91952
92076
|
task.orchestrator.applySettings(settings);
|
|
92077
|
+
this.broadcastControl({ type: "settings_ack", settings, taskId });
|
|
91953
92078
|
}
|
|
91954
92079
|
getPermissionMode(taskId) {
|
|
91955
92080
|
const task = this.#tasks.get(taskId);
|
|
@@ -92021,12 +92146,13 @@ var TaskManager = class {
|
|
|
92021
92146
|
initialOverlay
|
|
92022
92147
|
});
|
|
92023
92148
|
if (initialOverlay) {
|
|
92024
|
-
applyInitialOverlayToOrchestrator({
|
|
92149
|
+
await applyInitialOverlayToOrchestrator({
|
|
92025
92150
|
taskId: params.taskId,
|
|
92026
92151
|
initialOverlay,
|
|
92027
92152
|
orchestratorRef,
|
|
92028
92153
|
isTaskRegistered: (id) => this.#tasks.has(id),
|
|
92029
92154
|
notifyTaskResourceChange: this.#deps.notifyTaskResourceChange,
|
|
92155
|
+
taskStateStore: this.#deps.taskStateStore,
|
|
92030
92156
|
log: this.#deps.log
|
|
92031
92157
|
});
|
|
92032
92158
|
}
|
|
@@ -92082,6 +92208,27 @@ var TaskManager = class {
|
|
|
92082
92208
|
if (this.#tasks.has(taskId)) return;
|
|
92083
92209
|
const cwd = opts.cwd;
|
|
92084
92210
|
const mode = opts.mode ?? "task";
|
|
92211
|
+
let orchestratorRef = null;
|
|
92212
|
+
const restoreHydration = async () => {
|
|
92213
|
+
const record = await this.#deps.taskStateStore.getTask(taskId);
|
|
92214
|
+
if (!orchestratorRef || !this.#tasks.has(taskId)) return;
|
|
92215
|
+
if (record?.taskOverlay) {
|
|
92216
|
+
orchestratorRef.applyOverlay(record.taskOverlay);
|
|
92217
|
+
} else {
|
|
92218
|
+
orchestratorRef.markStructuredTaskRestoreComplete();
|
|
92219
|
+
}
|
|
92220
|
+
if (record?.composerSettings) {
|
|
92221
|
+
applyPersistedComposerSettings(orchestratorRef, record.composerSettings);
|
|
92222
|
+
}
|
|
92223
|
+
};
|
|
92224
|
+
const hydrationPromise = restoreHydration().catch((err) => {
|
|
92225
|
+
this.#deps.log({
|
|
92226
|
+
event: "overlay_restore_failed",
|
|
92227
|
+
taskId,
|
|
92228
|
+
error: err instanceof Error ? err.message : String(err)
|
|
92229
|
+
});
|
|
92230
|
+
orchestratorRef?.markStructuredTaskRestoreComplete();
|
|
92231
|
+
});
|
|
92085
92232
|
const orchestrator = this.#createTask(taskId, channelId, {
|
|
92086
92233
|
initialState: opts.initialState,
|
|
92087
92234
|
existingSessionId: opts.existingSessionId,
|
|
@@ -92091,21 +92238,15 @@ var TaskManager = class {
|
|
|
92091
92238
|
initialTokenCount: opts.initialTokenCount,
|
|
92092
92239
|
initialPlanDetection: opts.initialPlanDetection,
|
|
92093
92240
|
mode,
|
|
92241
|
+
hydrationPromise,
|
|
92094
92242
|
initialRoiStartedEmitted: opts.initialRoiStartedEmitted,
|
|
92095
92243
|
taskCreatedAt: opts.taskCreatedAt,
|
|
92096
|
-
initialTurnCount: opts.initialTurnCount
|
|
92244
|
+
initialTurnCount: opts.initialTurnCount,
|
|
92245
|
+
restoreInProgress: true
|
|
92097
92246
|
});
|
|
92247
|
+
orchestratorRef = orchestrator;
|
|
92098
92248
|
this.#tasks.set(taskId, { taskId, channelId, cwd, mode, orchestrator });
|
|
92099
92249
|
this.#flushPendingStreamSubs(taskId, orchestrator);
|
|
92100
|
-
this.#deps.taskStateStore.getTask(taskId).then((record) => {
|
|
92101
|
-
if (record?.taskOverlay) orchestrator.applyOverlay(record.taskOverlay);
|
|
92102
|
-
}).catch((err) => {
|
|
92103
|
-
this.#deps.log({
|
|
92104
|
-
event: "overlay_restore_failed",
|
|
92105
|
-
taskId,
|
|
92106
|
-
error: err instanceof Error ? err.message : String(err)
|
|
92107
|
-
});
|
|
92108
|
-
});
|
|
92109
92250
|
this.#deps.log({ event: "task_restored", taskId, channelId, initialState: opts.initialState });
|
|
92110
92251
|
}
|
|
92111
92252
|
async removeTask(taskId) {
|
|
@@ -92550,6 +92691,7 @@ var TaskManager = class {
|
|
|
92550
92691
|
onAuthNotLoggedIn: () => this.#onAuthNotLoggedIn?.(),
|
|
92551
92692
|
onRateLimitEvent: (info) => this.#onRateLimitEvent?.(info),
|
|
92552
92693
|
sendControlMessage: this.#controlChannels.size > 0 ? this.#buildBroadcastFn() : void 0,
|
|
92694
|
+
applyTaskSettings: (settings) => this.applyTaskSettings(taskId, settings),
|
|
92553
92695
|
resourceRegistry: this.#deps.resourceRegistry,
|
|
92554
92696
|
planRepo: this.#deps.planRepo,
|
|
92555
92697
|
annotationStore: this.#deps.annotationStore,
|
|
@@ -92594,10 +92736,19 @@ var TaskManager = class {
|
|
|
92594
92736
|
},
|
|
92595
92737
|
onForwardedAck: (correlationId) => {
|
|
92596
92738
|
this.#forwardedAckEmitters.get(taskId)?.(correlationId);
|
|
92597
|
-
}
|
|
92739
|
+
},
|
|
92740
|
+
restoreInProgress: opts?.restoreInProgress ?? false
|
|
92598
92741
|
});
|
|
92599
92742
|
}
|
|
92600
92743
|
};
|
|
92744
|
+
function applyPersistedComposerSettings(orchestrator, settings) {
|
|
92745
|
+
const patch = {};
|
|
92746
|
+
if (settings.model != null) patch.model = settings.model;
|
|
92747
|
+
if (settings.reasoningEffort != null) patch.reasoningEffort = settings.reasoningEffort;
|
|
92748
|
+
if (settings.permissionMode != null) patch.permissionMode = settings.permissionMode;
|
|
92749
|
+
if (settings.fastMode != null) patch.fastMode = settings.fastMode;
|
|
92750
|
+
if (Object.keys(patch).length > 0) orchestrator.applySettings(patch);
|
|
92751
|
+
}
|
|
92601
92752
|
|
|
92602
92753
|
// src/services/task/manager/task-state-store.ts
|
|
92603
92754
|
import { join as join45 } from "path";
|
|
@@ -95464,6 +95615,13 @@ function filterOutboundForCollab(msg, collabTaskId) {
|
|
|
95464
95615
|
/** Explicitly filtered: pr_state nests taskId inside `data` */
|
|
95465
95616
|
case "pr_state":
|
|
95466
95617
|
return msg.data.taskId === collabTaskId ? msg : null;
|
|
95618
|
+
/**
|
|
95619
|
+
* settings_ack: task-scoped variant reaches collab peers if the task
|
|
95620
|
+
* matches; daemon-wide variant (no taskId) is host-only.
|
|
95621
|
+
*/
|
|
95622
|
+
case "settings_ack":
|
|
95623
|
+
if (msg.taskId === void 0) return null;
|
|
95624
|
+
return msg.taskId === collabTaskId ? msg : null;
|
|
95467
95625
|
/** Suppressed: host-only settings and schedules */
|
|
95468
95626
|
case "user_settings_snapshot":
|
|
95469
95627
|
case "user_settings_updated":
|
|
@@ -95485,7 +95643,6 @@ function filterOutboundForCollab(msg, collabTaskId) {
|
|
|
95485
95643
|
/** Suppressed: daemon-scoped messages with no taskId */
|
|
95486
95644
|
case "capabilities":
|
|
95487
95645
|
case "rate_limit_info":
|
|
95488
|
-
case "settings_ack":
|
|
95489
95646
|
case "enhance_prompt_chunk":
|
|
95490
95647
|
case "mcp_server_auth_url":
|
|
95491
95648
|
case "mcp_server_status":
|
|
@@ -96706,7 +96863,7 @@ function friendlyExecError(err) {
|
|
|
96706
96863
|
async function refreshPluginCapabilities(daemon, tokenStore) {
|
|
96707
96864
|
const [updatedMarketplace, updatedMcp, updatedSkills] = await Promise.all([
|
|
96708
96865
|
detectMarketplacePlugins(),
|
|
96709
|
-
import("./mcp-servers-
|
|
96866
|
+
import("./mcp-servers-MUVTAMDT.js").then(
|
|
96710
96867
|
(m2) => m2.detectMCPServers(daemon.capabilities.environments, tokenStore)
|
|
96711
96868
|
),
|
|
96712
96869
|
import("./skills-NCKYNLUS.js").then(
|
|
@@ -97706,7 +97863,6 @@ function wireControlChannel(rawChannel, daemon, logAdapter, deps) {
|
|
|
97706
97863
|
}
|
|
97707
97864
|
if (Object.keys(applied).length === 0) return;
|
|
97708
97865
|
daemon.taskManager.applyTaskSettings(taskId, applied);
|
|
97709
|
-
handler.sendControl({ type: "settings_ack", settings: applied, taskId });
|
|
97710
97866
|
logAdapter({ event: "task_settings_updated", taskId, settings: applied });
|
|
97711
97867
|
},
|
|
97712
97868
|
onRequestCapabilities: () => {
|
|
@@ -98660,13 +98816,21 @@ function handleParticipantLeft(room, userId, deps) {
|
|
|
98660
98816
|
}
|
|
98661
98817
|
function handleWebrtcSignaling(room, type, targetUserId, payload, deps) {
|
|
98662
98818
|
const peerId = namespacePeerId(room.roomId, targetUserId);
|
|
98819
|
+
const generationId = `collab:${peerId}`;
|
|
98820
|
+
deps.log({
|
|
98821
|
+
event: "collab_generationid_placeholder",
|
|
98822
|
+
roomId: room.roomId,
|
|
98823
|
+
targetUserId,
|
|
98824
|
+
type,
|
|
98825
|
+
generationId
|
|
98826
|
+
});
|
|
98663
98827
|
const actions = {
|
|
98664
98828
|
// eslint-disable-next-line no-restricted-syntax -- SDP is opaque z.unknown() from signaling
|
|
98665
|
-
offer: () => room.peerManager.handleOffer(peerId, payload),
|
|
98829
|
+
offer: () => room.peerManager.handleOffer(peerId, payload, generationId),
|
|
98666
98830
|
// eslint-disable-next-line no-restricted-syntax -- SDP is opaque z.unknown() from signaling
|
|
98667
|
-
answer: () => room.peerManager.handleAnswer(peerId, payload),
|
|
98831
|
+
answer: () => room.peerManager.handleAnswer(peerId, payload, generationId),
|
|
98668
98832
|
// eslint-disable-next-line no-restricted-syntax -- ICE candidate is opaque z.unknown() from signaling
|
|
98669
|
-
ice: () => room.peerManager.handleIce(peerId, payload)
|
|
98833
|
+
ice: () => room.peerManager.handleIce(peerId, payload, generationId)
|
|
98670
98834
|
};
|
|
98671
98835
|
actions[type]().catch((err) => {
|
|
98672
98836
|
deps.log({ event: `collab_${type}_failed`, roomId: room.roomId, error: String(err) });
|
|
@@ -98810,6 +98974,7 @@ function createCollabRoomManager(deps) {
|
|
|
98810
98974
|
const delayMs = expiresAt - Date.now();
|
|
98811
98975
|
if (delayMs <= 0 || !Number.isFinite(delayMs)) {
|
|
98812
98976
|
deps.log({ event: "collab_expired", roomId, expiresAt });
|
|
98977
|
+
peerManager.destroy();
|
|
98813
98978
|
collabRepoHandle.destroy().catch((err) => {
|
|
98814
98979
|
deps.log({
|
|
98815
98980
|
event: "collab_repo_destroy_failed",
|
|
@@ -100432,14 +100597,132 @@ function createPeerManager(config2) {
|
|
|
100432
100597
|
}
|
|
100433
100598
|
return factoryPromise;
|
|
100434
100599
|
}
|
|
100435
|
-
function
|
|
100600
|
+
async function resolveLiveEntry(fromMachineId, messageType, generationId) {
|
|
100601
|
+
const direct = peers.get(fromMachineId);
|
|
100602
|
+
if (direct) return direct;
|
|
100603
|
+
const pending = pendingCreates.get(fromMachineId);
|
|
100604
|
+
if (!pending) {
|
|
100605
|
+
if (messageType === "answer") {
|
|
100606
|
+
config2.log.warn({ fromMachineId, generationId }, "Received answer for unknown peer");
|
|
100607
|
+
} else {
|
|
100608
|
+
config2.log.debug(
|
|
100609
|
+
{ fromMachineId, generationId },
|
|
100610
|
+
"Received ICE candidate for unknown peer"
|
|
100611
|
+
);
|
|
100612
|
+
}
|
|
100613
|
+
return null;
|
|
100614
|
+
}
|
|
100615
|
+
let resolved;
|
|
100616
|
+
try {
|
|
100617
|
+
resolved = await pending;
|
|
100618
|
+
} catch {
|
|
100619
|
+
return null;
|
|
100620
|
+
}
|
|
100621
|
+
if (peers.get(fromMachineId) !== resolved) return null;
|
|
100622
|
+
return resolved;
|
|
100623
|
+
}
|
|
100624
|
+
function discardSupersededPc(pc, event, fields) {
|
|
100625
|
+
pc.onconnectionstatechange = null;
|
|
100626
|
+
pc.onicecandidate = null;
|
|
100627
|
+
pc.ondatachannel = null;
|
|
100628
|
+
pc.close();
|
|
100629
|
+
config2.logAdapter?.({ event, ...fields });
|
|
100630
|
+
}
|
|
100631
|
+
function abortInitiatorCreate(pc, targetMachineId) {
|
|
100632
|
+
pendingCreates.delete(targetMachineId);
|
|
100633
|
+
peers.delete(targetMachineId);
|
|
100634
|
+
pc.onconnectionstatechange = null;
|
|
100635
|
+
pc.onicecandidate = null;
|
|
100636
|
+
pc.ondatachannel = null;
|
|
100637
|
+
pc.close();
|
|
100638
|
+
}
|
|
100639
|
+
function setupInitiatorDataChannel(pc, targetMachineId, myGenerationId) {
|
|
100640
|
+
if (!pc.createDataChannel) {
|
|
100641
|
+
throw new Error("PeerConnection does not support createDataChannel");
|
|
100642
|
+
}
|
|
100643
|
+
let channel;
|
|
100644
|
+
try {
|
|
100645
|
+
channel = pc.createDataChannel(LORO_SYNC_LABEL, { ordered: true });
|
|
100646
|
+
} catch (err) {
|
|
100647
|
+
config2.log.warn(
|
|
100648
|
+
{ targetMachineId, err: String(err) },
|
|
100649
|
+
"createDataChannel failed (connection likely closing)"
|
|
100650
|
+
);
|
|
100651
|
+
abortInitiatorCreate(pc, targetMachineId);
|
|
100652
|
+
throw err;
|
|
100653
|
+
}
|
|
100654
|
+
const rawChannel = channel;
|
|
100655
|
+
const peerId = machineIdToPeerId(targetMachineId);
|
|
100656
|
+
const logAdapter = config2.logAdapter ?? noopLogAdapter;
|
|
100657
|
+
const guarded = installLoroGuard(
|
|
100658
|
+
rawChannel,
|
|
100659
|
+
config2.webrtcAdapter,
|
|
100660
|
+
peerId,
|
|
100661
|
+
config2.log,
|
|
100662
|
+
logAdapter
|
|
100663
|
+
);
|
|
100664
|
+
if (guarded) {
|
|
100665
|
+
disposeLoroGuard(peerId);
|
|
100666
|
+
loroGuards.set(peerId, guarded);
|
|
100667
|
+
}
|
|
100668
|
+
installInitiatorOnOpen(
|
|
100669
|
+
rawChannel,
|
|
100670
|
+
channel,
|
|
100671
|
+
targetMachineId,
|
|
100672
|
+
peerId,
|
|
100673
|
+
myGenerationId,
|
|
100674
|
+
logAdapter
|
|
100675
|
+
);
|
|
100676
|
+
}
|
|
100677
|
+
function installInitiatorOnOpen(rawChannel, channel, targetMachineId, peerId, myGenerationId, logAdapter) {
|
|
100678
|
+
rawChannel.onopen = () => {
|
|
100679
|
+
const attachResult = safeAttachDataChannel(
|
|
100680
|
+
() => {
|
|
100681
|
+
config2.webrtcAdapter.attachDataChannel(
|
|
100682
|
+
peerId,
|
|
100683
|
+
// eslint-disable-next-line no-restricted-syntax -- RTCDataChannel from node-datachannel satisfies the adapter interface
|
|
100684
|
+
channel
|
|
100685
|
+
);
|
|
100686
|
+
},
|
|
100687
|
+
{
|
|
100688
|
+
log: (entry) => logAdapter({ ...entry, peerId: String(peerId) }),
|
|
100689
|
+
onStopped: () => {
|
|
100690
|
+
const currentEntry = peers.get(targetMachineId);
|
|
100691
|
+
if (currentEntry?.generationId !== myGenerationId) return;
|
|
100692
|
+
config2.log.warn(
|
|
100693
|
+
{ targetMachineId, peerId: String(peerId) },
|
|
100694
|
+
"Loro adapter in stopped state (initiator onopen); tearing down peer"
|
|
100695
|
+
);
|
|
100696
|
+
tearDownPeer(targetMachineId, "loro_adapter_stopped");
|
|
100697
|
+
}
|
|
100698
|
+
}
|
|
100699
|
+
);
|
|
100700
|
+
if (!attachResult.ok && attachResult.reason === "other") {
|
|
100701
|
+
config2.log.warn(
|
|
100702
|
+
{ targetMachineId, err: String(attachResult.error) },
|
|
100703
|
+
"attachDataChannel failed with non-stopped error"
|
|
100704
|
+
);
|
|
100705
|
+
}
|
|
100706
|
+
};
|
|
100707
|
+
}
|
|
100708
|
+
function tearDownPeer(machineId, reason) {
|
|
100436
100709
|
const peerId = machineIdToPeerId(machineId);
|
|
100437
100710
|
disposeLoroGuard(peerId);
|
|
100438
100711
|
config2.webrtcAdapter.detachDataChannel(peerId);
|
|
100439
|
-
const
|
|
100440
|
-
if (
|
|
100441
|
-
|
|
100442
|
-
|
|
100712
|
+
const entry = peers.get(machineId);
|
|
100713
|
+
if (entry) {
|
|
100714
|
+
config2.logAdapter?.({
|
|
100715
|
+
event: "peer_tear_down",
|
|
100716
|
+
machineId,
|
|
100717
|
+
generationId: entry.generationId,
|
|
100718
|
+
ageMs: Date.now() - entry.createdAt,
|
|
100719
|
+
lastState: entry.lastState,
|
|
100720
|
+
reason: reason ?? "explicit"
|
|
100721
|
+
});
|
|
100722
|
+
entry.pc.onconnectionstatechange = null;
|
|
100723
|
+
entry.pc.onicecandidate = null;
|
|
100724
|
+
entry.pc.ondatachannel = null;
|
|
100725
|
+
entry.pc.close();
|
|
100443
100726
|
peers.delete(machineId);
|
|
100444
100727
|
}
|
|
100445
100728
|
}
|
|
@@ -100455,6 +100738,7 @@ function createPeerManager(config2) {
|
|
|
100455
100738
|
logAdapter
|
|
100456
100739
|
);
|
|
100457
100740
|
if (guarded) {
|
|
100741
|
+
disposeLoroGuard(peerId);
|
|
100458
100742
|
loroGuards.set(peerId, guarded);
|
|
100459
100743
|
}
|
|
100460
100744
|
const attachResult = safeAttachDataChannel(
|
|
@@ -100472,7 +100756,7 @@ function createPeerManager(config2) {
|
|
|
100472
100756
|
{ machineId, peerId: String(peerId) },
|
|
100473
100757
|
"Loro adapter in stopped state; tearing down peer"
|
|
100474
100758
|
);
|
|
100475
|
-
tearDownPeer(machineId);
|
|
100759
|
+
tearDownPeer(machineId, "loro_adapter_stopped");
|
|
100476
100760
|
}
|
|
100477
100761
|
}
|
|
100478
100762
|
);
|
|
@@ -100481,7 +100765,7 @@ function createPeerManager(config2) {
|
|
|
100481
100765
|
{ machineId, peerId: String(peerId), err: String(attachResult.error) },
|
|
100482
100766
|
"attachDataChannel failed with non-stopped error; tearing down peer"
|
|
100483
100767
|
);
|
|
100484
|
-
tearDownPeer(machineId);
|
|
100768
|
+
tearDownPeer(machineId, "attach_failed");
|
|
100485
100769
|
}
|
|
100486
100770
|
}
|
|
100487
100771
|
function handleDataChannel(machineId, event) {
|
|
@@ -100578,181 +100862,277 @@ function createPeerManager(config2) {
|
|
|
100578
100862
|
);
|
|
100579
100863
|
}
|
|
100580
100864
|
}
|
|
100581
|
-
function setupPeerHandlers(machineId, pc) {
|
|
100865
|
+
function setupPeerHandlers(machineId, pc, generationId) {
|
|
100582
100866
|
pc.onicecandidate = (event) => {
|
|
100583
100867
|
if (event.candidate) {
|
|
100584
|
-
config2.onIceCandidate(
|
|
100585
|
-
|
|
100586
|
-
|
|
100587
|
-
|
|
100588
|
-
|
|
100868
|
+
config2.onIceCandidate(
|
|
100869
|
+
machineId,
|
|
100870
|
+
{
|
|
100871
|
+
candidate: event.candidate.candidate,
|
|
100872
|
+
sdpMid: event.candidate.sdpMid,
|
|
100873
|
+
sdpMLineIndex: event.candidate.sdpMLineIndex
|
|
100874
|
+
},
|
|
100875
|
+
generationId
|
|
100876
|
+
);
|
|
100589
100877
|
}
|
|
100590
100878
|
};
|
|
100591
100879
|
pc.ondatachannel = (event) => {
|
|
100592
100880
|
handleDataChannel(machineId, event);
|
|
100593
100881
|
};
|
|
100594
100882
|
pc.onconnectionstatechange = () => {
|
|
100595
|
-
const
|
|
100596
|
-
|
|
100597
|
-
|
|
100598
|
-
|
|
100883
|
+
const currentEntry = peers.get(machineId);
|
|
100884
|
+
if (currentEntry?.generationId !== generationId) {
|
|
100885
|
+
config2.logAdapter?.({
|
|
100886
|
+
event: "stale_state_change_ignored",
|
|
100887
|
+
machineId,
|
|
100888
|
+
staleGenerationId: generationId,
|
|
100889
|
+
currentGenerationId: currentEntry?.generationId
|
|
100890
|
+
});
|
|
100891
|
+
return;
|
|
100892
|
+
}
|
|
100893
|
+
const newState = pc.connectionState;
|
|
100894
|
+
const prevState = currentEntry.lastState;
|
|
100895
|
+
const durationMs = Date.now() - currentEntry.lastStateAt;
|
|
100896
|
+
config2.logAdapter?.({
|
|
100897
|
+
event: "webrtc_state_transition",
|
|
100898
|
+
machineId,
|
|
100899
|
+
generationId,
|
|
100900
|
+
prevState,
|
|
100901
|
+
newState,
|
|
100902
|
+
durationMs
|
|
100903
|
+
});
|
|
100904
|
+
config2.log.info(
|
|
100905
|
+
{ machineId, generationId, prevState, newState },
|
|
100906
|
+
"Peer connection state changed"
|
|
100907
|
+
);
|
|
100908
|
+
currentEntry.lastState = newState;
|
|
100909
|
+
currentEntry.lastStateAt = Date.now();
|
|
100910
|
+
if (newState === "failed" || newState === "closed") {
|
|
100911
|
+
tearDownPeer(machineId, newState);
|
|
100599
100912
|
}
|
|
100600
100913
|
};
|
|
100601
100914
|
}
|
|
100602
100915
|
return {
|
|
100603
|
-
async handleOffer(fromMachineId, offer) {
|
|
100604
|
-
config2.log.info({ fromMachineId }, "Handling WebRTC offer");
|
|
100916
|
+
async handleOffer(fromMachineId, offer, generationId) {
|
|
100605
100917
|
const existing = peers.get(fromMachineId);
|
|
100606
|
-
|
|
100607
|
-
|
|
100608
|
-
|
|
100609
|
-
|
|
100610
|
-
|
|
100611
|
-
|
|
100612
|
-
|
|
100613
|
-
|
|
100614
|
-
|
|
100615
|
-
|
|
100616
|
-
|
|
100617
|
-
|
|
100618
|
-
|
|
100619
|
-
|
|
100620
|
-
|
|
100918
|
+
config2.log.info(
|
|
100919
|
+
{
|
|
100920
|
+
fromMachineId,
|
|
100921
|
+
generationId,
|
|
100922
|
+
hadExistingPeer: !!existing,
|
|
100923
|
+
priorGenerationId: existing?.generationId
|
|
100924
|
+
},
|
|
100925
|
+
"Handling WebRTC offer"
|
|
100926
|
+
);
|
|
100927
|
+
config2.logAdapter?.({
|
|
100928
|
+
event: "webrtc_offer_received",
|
|
100929
|
+
generationId,
|
|
100930
|
+
fromMachineId,
|
|
100931
|
+
hadExistingPeer: !!existing,
|
|
100932
|
+
priorGenerationId: existing?.generationId
|
|
100933
|
+
});
|
|
100934
|
+
const inflight = pendingCreates.get(fromMachineId);
|
|
100935
|
+
if (inflight) {
|
|
100936
|
+
await Promise.race([
|
|
100937
|
+
inflight.catch(() => void 0),
|
|
100938
|
+
new Promise((resolve4) => setTimeout(resolve4, 500))
|
|
100939
|
+
]);
|
|
100940
|
+
}
|
|
100941
|
+
if (existing || peers.has(fromMachineId)) {
|
|
100942
|
+
config2.log.debug({ fromMachineId, generationId }, "Closing existing peer connection");
|
|
100943
|
+
tearDownPeer(fromMachineId, "replaced_by_offer");
|
|
100944
|
+
}
|
|
100945
|
+
const now = Date.now();
|
|
100946
|
+
let promise;
|
|
100947
|
+
promise = (async () => {
|
|
100948
|
+
try {
|
|
100949
|
+
const factory = await getFactory();
|
|
100950
|
+
const pc = factory();
|
|
100951
|
+
setupPeerHandlers(fromMachineId, pc, generationId);
|
|
100952
|
+
await pc.setRemoteDescription(offer);
|
|
100953
|
+
const answer = await pc.createAnswer();
|
|
100954
|
+
await pc.setLocalDescription(answer);
|
|
100955
|
+
const entry = {
|
|
100956
|
+
pc,
|
|
100957
|
+
generationId,
|
|
100958
|
+
createdAt: now,
|
|
100959
|
+
lastState: "new",
|
|
100960
|
+
lastStateAt: now
|
|
100961
|
+
};
|
|
100962
|
+
if (pendingCreates.get(fromMachineId) !== promise) {
|
|
100963
|
+
discardSupersededPc(pc, "webrtc_offer_superseded", { fromMachineId, generationId });
|
|
100964
|
+
return entry;
|
|
100965
|
+
}
|
|
100966
|
+
peers.set(fromMachineId, entry);
|
|
100967
|
+
pendingCreates.delete(fromMachineId);
|
|
100968
|
+
config2.onAnswer(fromMachineId, { type: "answer", sdp: answer.sdp }, generationId);
|
|
100969
|
+
return entry;
|
|
100970
|
+
} catch (err) {
|
|
100971
|
+
if (pendingCreates.get(fromMachineId) === promise) {
|
|
100972
|
+
pendingCreates.delete(fromMachineId);
|
|
100973
|
+
}
|
|
100974
|
+
config2.logAdapter?.({
|
|
100975
|
+
event: "webrtc_offer_handshake_failed",
|
|
100976
|
+
fromMachineId,
|
|
100977
|
+
generationId,
|
|
100978
|
+
err: String(err)
|
|
100979
|
+
});
|
|
100980
|
+
throw err;
|
|
100981
|
+
}
|
|
100621
100982
|
})();
|
|
100622
100983
|
pendingCreates.set(fromMachineId, promise);
|
|
100623
100984
|
setTimeout(() => {
|
|
100624
100985
|
if (pendingCreates.get(fromMachineId) === promise) {
|
|
100625
100986
|
pendingCreates.delete(fromMachineId);
|
|
100626
|
-
config2.
|
|
100987
|
+
config2.logAdapter?.({
|
|
100988
|
+
event: "webrtc_handshake_timeout",
|
|
100989
|
+
machineId: fromMachineId,
|
|
100990
|
+
generationId
|
|
100991
|
+
});
|
|
100992
|
+
config2.log.warn({ fromMachineId, generationId }, "WebRTC handshake timed out");
|
|
100627
100993
|
}
|
|
100628
100994
|
}, HANDSHAKE_TIMEOUT_MS);
|
|
100629
100995
|
await promise;
|
|
100630
100996
|
},
|
|
100631
100997
|
async initiateOffer(targetMachineId) {
|
|
100632
|
-
|
|
100998
|
+
const generationId = crypto.randomUUID();
|
|
100999
|
+
config2.log.info({ targetMachineId, generationId }, "Initiating WebRTC offer");
|
|
100633
101000
|
const existing = peers.get(targetMachineId);
|
|
100634
101001
|
if (existing) {
|
|
100635
|
-
tearDownPeer(targetMachineId);
|
|
101002
|
+
tearDownPeer(targetMachineId, "replaced_by_initiate");
|
|
100636
101003
|
}
|
|
100637
|
-
const
|
|
100638
|
-
|
|
100639
|
-
|
|
100640
|
-
setupPeerHandlers(targetMachineId, pc);
|
|
100641
|
-
if (!pc.createDataChannel) {
|
|
100642
|
-
throw new Error("PeerConnection does not support createDataChannel");
|
|
100643
|
-
}
|
|
100644
|
-
if (!pc.createOffer) {
|
|
100645
|
-
throw new Error("PeerConnection does not support createOffer");
|
|
100646
|
-
}
|
|
100647
|
-
let channel;
|
|
101004
|
+
const now = Date.now();
|
|
101005
|
+
let promise;
|
|
101006
|
+
promise = (async () => {
|
|
100648
101007
|
try {
|
|
100649
|
-
|
|
100650
|
-
|
|
100651
|
-
|
|
100652
|
-
|
|
100653
|
-
|
|
100654
|
-
|
|
101008
|
+
const factory = await getFactory();
|
|
101009
|
+
const pc = factory();
|
|
101010
|
+
setupPeerHandlers(targetMachineId, pc, generationId);
|
|
101011
|
+
if (!pc.createOffer) {
|
|
101012
|
+
throw new Error("PeerConnection does not support createOffer");
|
|
101013
|
+
}
|
|
101014
|
+
setupInitiatorDataChannel(pc, targetMachineId, generationId);
|
|
101015
|
+
const offer = await pc.createOffer();
|
|
101016
|
+
await pc.setLocalDescription(offer);
|
|
101017
|
+
const entry = {
|
|
101018
|
+
pc,
|
|
101019
|
+
generationId,
|
|
101020
|
+
createdAt: now,
|
|
101021
|
+
lastState: "new",
|
|
101022
|
+
lastStateAt: now
|
|
101023
|
+
};
|
|
101024
|
+
if (pendingCreates.get(targetMachineId) !== promise) {
|
|
101025
|
+
disposeLoroGuard(machineIdToPeerId(targetMachineId));
|
|
101026
|
+
discardSupersededPc(pc, "webrtc_initiate_superseded", {
|
|
101027
|
+
targetMachineId,
|
|
101028
|
+
generationId
|
|
101029
|
+
});
|
|
101030
|
+
return entry;
|
|
101031
|
+
}
|
|
101032
|
+
peers.set(targetMachineId, entry);
|
|
100655
101033
|
pendingCreates.delete(targetMachineId);
|
|
100656
|
-
|
|
100657
|
-
|
|
100658
|
-
|
|
101034
|
+
config2.onOffer?.(targetMachineId, { type: "offer", sdp: offer.sdp }, generationId);
|
|
101035
|
+
return entry;
|
|
101036
|
+
} catch (err) {
|
|
101037
|
+
if (pendingCreates.get(targetMachineId) === promise) {
|
|
101038
|
+
pendingCreates.delete(targetMachineId);
|
|
101039
|
+
}
|
|
101040
|
+
config2.logAdapter?.({
|
|
101041
|
+
event: "webrtc_initiate_handshake_failed",
|
|
101042
|
+
targetMachineId,
|
|
101043
|
+
generationId,
|
|
101044
|
+
err: String(err)
|
|
101045
|
+
});
|
|
100659
101046
|
throw err;
|
|
100660
101047
|
}
|
|
100661
|
-
const rawChannel = channel;
|
|
100662
|
-
const peerId = machineIdToPeerId(targetMachineId);
|
|
100663
|
-
const logAdapter = config2.logAdapter ?? noopLogAdapter;
|
|
100664
|
-
const guarded = installLoroGuard(
|
|
100665
|
-
rawChannel,
|
|
100666
|
-
config2.webrtcAdapter,
|
|
100667
|
-
peerId,
|
|
100668
|
-
config2.log,
|
|
100669
|
-
logAdapter
|
|
100670
|
-
);
|
|
100671
|
-
if (guarded) {
|
|
100672
|
-
loroGuards.set(peerId, guarded);
|
|
100673
|
-
}
|
|
100674
|
-
const myPc = pc;
|
|
100675
|
-
rawChannel.onopen = () => {
|
|
100676
|
-
const attachResult = safeAttachDataChannel(
|
|
100677
|
-
() => {
|
|
100678
|
-
config2.webrtcAdapter.attachDataChannel(
|
|
100679
|
-
peerId,
|
|
100680
|
-
// eslint-disable-next-line no-restricted-syntax -- RTCDataChannel from node-datachannel satisfies the adapter interface
|
|
100681
|
-
channel
|
|
100682
|
-
);
|
|
100683
|
-
},
|
|
100684
|
-
{
|
|
100685
|
-
log: (entry) => logAdapter({ ...entry, peerId: String(peerId) }),
|
|
100686
|
-
onStopped: () => {
|
|
100687
|
-
const existingPc = peers.get(targetMachineId);
|
|
100688
|
-
if (existingPc !== myPc) return;
|
|
100689
|
-
config2.log.warn(
|
|
100690
|
-
{ targetMachineId, peerId: String(peerId) },
|
|
100691
|
-
"Loro adapter in stopped state (initiator onopen); tearing down peer"
|
|
100692
|
-
);
|
|
100693
|
-
tearDownPeer(targetMachineId);
|
|
100694
|
-
}
|
|
100695
|
-
}
|
|
100696
|
-
);
|
|
100697
|
-
if (!attachResult.ok && attachResult.reason === "other") {
|
|
100698
|
-
config2.log.warn(
|
|
100699
|
-
{ targetMachineId, err: String(attachResult.error) },
|
|
100700
|
-
"attachDataChannel failed with non-stopped error"
|
|
100701
|
-
);
|
|
100702
|
-
}
|
|
100703
|
-
};
|
|
100704
|
-
const offer = await pc.createOffer();
|
|
100705
|
-
await pc.setLocalDescription(offer);
|
|
100706
|
-
peers.set(targetMachineId, pc);
|
|
100707
|
-
pendingCreates.delete(targetMachineId);
|
|
100708
|
-
config2.onOffer?.(targetMachineId, { type: "offer", sdp: offer.sdp });
|
|
100709
|
-
return pc;
|
|
100710
101048
|
})();
|
|
100711
101049
|
pendingCreates.set(targetMachineId, promise);
|
|
100712
101050
|
setTimeout(() => {
|
|
100713
101051
|
if (pendingCreates.get(targetMachineId) === promise) {
|
|
100714
101052
|
pendingCreates.delete(targetMachineId);
|
|
100715
|
-
config2.
|
|
101053
|
+
config2.logAdapter?.({
|
|
101054
|
+
event: "webrtc_handshake_timeout",
|
|
101055
|
+
machineId: targetMachineId,
|
|
101056
|
+
generationId
|
|
101057
|
+
});
|
|
101058
|
+
config2.log.warn(
|
|
101059
|
+
{ targetMachineId, generationId },
|
|
101060
|
+
"WebRTC handshake timed out (initiator)"
|
|
101061
|
+
);
|
|
100716
101062
|
}
|
|
100717
101063
|
}, HANDSHAKE_TIMEOUT_MS);
|
|
100718
101064
|
await promise;
|
|
100719
101065
|
},
|
|
100720
|
-
async handleAnswer(fromMachineId, answer) {
|
|
100721
|
-
|
|
100722
|
-
if (!
|
|
100723
|
-
|
|
100724
|
-
|
|
100725
|
-
|
|
100726
|
-
|
|
100727
|
-
|
|
100728
|
-
|
|
100729
|
-
}
|
|
101066
|
+
async handleAnswer(fromMachineId, answer, generationId) {
|
|
101067
|
+
const entry = await resolveLiveEntry(fromMachineId, "answer", generationId);
|
|
101068
|
+
if (!entry) return;
|
|
101069
|
+
if (entry.generationId !== generationId) {
|
|
101070
|
+
config2.logAdapter?.({
|
|
101071
|
+
event: "webrtc_answer_stale",
|
|
101072
|
+
fromMachineId,
|
|
101073
|
+
currentGenerationId: entry.generationId,
|
|
101074
|
+
staleGenerationId: generationId
|
|
101075
|
+
});
|
|
101076
|
+
config2.log.debug(
|
|
101077
|
+
{
|
|
101078
|
+
fromMachineId,
|
|
101079
|
+
currentGenerationId: entry.generationId,
|
|
101080
|
+
staleGenerationId: generationId
|
|
101081
|
+
},
|
|
101082
|
+
"Dropping stale WebRTC answer"
|
|
101083
|
+
);
|
|
101084
|
+
return;
|
|
100730
101085
|
}
|
|
100731
|
-
await pc.setRemoteDescription(answer);
|
|
101086
|
+
await entry.pc.setRemoteDescription(answer);
|
|
100732
101087
|
},
|
|
100733
|
-
async handleIce(fromMachineId, candidate) {
|
|
100734
|
-
|
|
100735
|
-
if (!
|
|
100736
|
-
|
|
100737
|
-
|
|
100738
|
-
|
|
100739
|
-
|
|
100740
|
-
|
|
100741
|
-
|
|
100742
|
-
}
|
|
101088
|
+
async handleIce(fromMachineId, candidate, generationId) {
|
|
101089
|
+
const entry = await resolveLiveEntry(fromMachineId, "ice", generationId);
|
|
101090
|
+
if (!entry) return;
|
|
101091
|
+
if (entry.generationId !== generationId) {
|
|
101092
|
+
config2.logAdapter?.({
|
|
101093
|
+
event: "webrtc_ice_candidate_stale",
|
|
101094
|
+
fromMachineId,
|
|
101095
|
+
currentGenerationId: entry.generationId,
|
|
101096
|
+
staleGenerationId: generationId
|
|
101097
|
+
});
|
|
101098
|
+
config2.log.debug(
|
|
101099
|
+
{
|
|
101100
|
+
fromMachineId,
|
|
101101
|
+
currentGenerationId: entry.generationId,
|
|
101102
|
+
staleGenerationId: generationId
|
|
101103
|
+
},
|
|
101104
|
+
"Dropping stale ICE candidate"
|
|
101105
|
+
);
|
|
101106
|
+
return;
|
|
101107
|
+
}
|
|
101108
|
+
try {
|
|
101109
|
+
await entry.pc.addIceCandidate(candidate);
|
|
101110
|
+
} catch (err) {
|
|
101111
|
+
config2.logAdapter?.({
|
|
101112
|
+
event: "webrtc_ice_apply_failed",
|
|
101113
|
+
fromMachineId,
|
|
101114
|
+
generationId,
|
|
101115
|
+
err: String(err)
|
|
101116
|
+
});
|
|
101117
|
+
config2.log.warn(
|
|
101118
|
+
{ fromMachineId, generationId, err: String(err) },
|
|
101119
|
+
"addIceCandidate failed"
|
|
101120
|
+
);
|
|
100743
101121
|
}
|
|
100744
|
-
await pc.addIceCandidate(candidate);
|
|
100745
101122
|
},
|
|
100746
101123
|
updateIceServers(servers) {
|
|
100747
101124
|
config2.iceServers = servers;
|
|
100748
101125
|
factoryPromise = null;
|
|
100749
101126
|
},
|
|
100750
101127
|
closePeer(targetId) {
|
|
100751
|
-
tearDownPeer(targetId);
|
|
101128
|
+
tearDownPeer(targetId, "close_peer");
|
|
100752
101129
|
},
|
|
100753
101130
|
destroy() {
|
|
100754
101131
|
for (const machineId of [...peers.keys()]) {
|
|
100755
|
-
tearDownPeer(machineId);
|
|
101132
|
+
tearDownPeer(machineId, "destroy");
|
|
101133
|
+
}
|
|
101134
|
+
for (const guard of loroGuards.values()) {
|
|
101135
|
+
guard.dispose();
|
|
100756
101136
|
}
|
|
100757
101137
|
peers.clear();
|
|
100758
101138
|
pendingCreates.clear();
|
|
@@ -102423,7 +102803,15 @@ function wireAutoOpenBrowser(connection, signalingClient, authToken, signalingUr
|
|
|
102423
102803
|
var WARN_EVENTS = /* @__PURE__ */ new Set([
|
|
102424
102804
|
"subprocess_force_killed",
|
|
102425
102805
|
"file_watcher_events_dropped",
|
|
102426
|
-
"mcp_server_toggle_failed"
|
|
102806
|
+
"mcp_server_toggle_failed",
|
|
102807
|
+
/**
|
|
102808
|
+
* WebRTC transient failures that match the `_failed` heuristic but are
|
|
102809
|
+
* expected during normal reconnect scenarios (stale/closed PCs, network
|
|
102810
|
+
* flap). Classify as warn to avoid flooding error-level telemetry.
|
|
102811
|
+
*/
|
|
102812
|
+
"webrtc_ice_apply_failed",
|
|
102813
|
+
"webrtc_offer_handshake_failed",
|
|
102814
|
+
"webrtc_initiate_handshake_failed"
|
|
102427
102815
|
]);
|
|
102428
102816
|
var DEBUG_EVENTS = /* @__PURE__ */ new Set([
|
|
102429
102817
|
"port_detection_poll",
|
|
@@ -102444,19 +102832,19 @@ function routeSignalingMessage(peerManager, signalingHandle, log) {
|
|
|
102444
102832
|
switch (msg.type) {
|
|
102445
102833
|
case "webrtc-offer":
|
|
102446
102834
|
if (msg.fromMachineId)
|
|
102447
|
-
peerManager?.handleOffer(msg.fromMachineId, toSDPDescription(msg.offer)).catch(
|
|
102835
|
+
peerManager?.handleOffer(msg.fromMachineId, toSDPDescription(msg.offer), msg.generationId).catch(
|
|
102448
102836
|
(err) => log.error({ event: "webrtc_offer_failed", error: String(err) })
|
|
102449
102837
|
);
|
|
102450
102838
|
break;
|
|
102451
102839
|
case "webrtc-answer":
|
|
102452
102840
|
if (msg.fromMachineId)
|
|
102453
|
-
peerManager?.handleAnswer(msg.fromMachineId, toSDPDescription(msg.answer)).catch(
|
|
102841
|
+
peerManager?.handleAnswer(msg.fromMachineId, toSDPDescription(msg.answer), msg.generationId).catch(
|
|
102454
102842
|
(err) => log.error({ event: "webrtc_answer_failed", error: String(err) })
|
|
102455
102843
|
);
|
|
102456
102844
|
break;
|
|
102457
102845
|
case "webrtc-ice":
|
|
102458
102846
|
if (msg.fromMachineId)
|
|
102459
|
-
peerManager?.handleIce(msg.fromMachineId, toICECandidate(msg.candidate)).catch((err) => log.error({ event: "webrtc_ice_failed", error: String(err) }));
|
|
102847
|
+
peerManager?.handleIce(msg.fromMachineId, toICECandidate(msg.candidate), msg.generationId).catch((err) => log.error({ event: "webrtc_ice_failed", error: String(err) }));
|
|
102460
102848
|
break;
|
|
102461
102849
|
case "ice-servers":
|
|
102462
102850
|
peerManager?.updateIceServers(msg.iceServers);
|
|
@@ -102478,10 +102866,8 @@ function routeSignalingMessage(peerManager, signalingHandle, log) {
|
|
|
102478
102866
|
case "error":
|
|
102479
102867
|
case "authenticated":
|
|
102480
102868
|
break;
|
|
102481
|
-
default:
|
|
102482
|
-
|
|
102483
|
-
void _exhaustive;
|
|
102484
|
-
}
|
|
102869
|
+
default:
|
|
102870
|
+
assertNever(msg);
|
|
102485
102871
|
}
|
|
102486
102872
|
};
|
|
102487
102873
|
}
|
|
@@ -102633,18 +103019,20 @@ function buildPeerManager(deps) {
|
|
|
102633
103019
|
return createPeerManager({
|
|
102634
103020
|
webrtcAdapter: personalWebrtcAdapter,
|
|
102635
103021
|
logAdapter,
|
|
102636
|
-
onAnswer: (targetMachineId, answer) => {
|
|
103022
|
+
onAnswer: (targetMachineId, answer, generationId) => {
|
|
102637
103023
|
signalingHandle.connection.send({
|
|
102638
103024
|
type: "webrtc-answer",
|
|
102639
103025
|
targetMachineId,
|
|
102640
|
-
answer
|
|
103026
|
+
answer,
|
|
103027
|
+
generationId
|
|
102641
103028
|
});
|
|
102642
103029
|
},
|
|
102643
|
-
onIceCandidate: (targetMachineId, candidate) => {
|
|
103030
|
+
onIceCandidate: (targetMachineId, candidate, generationId) => {
|
|
102644
103031
|
signalingHandle.connection.send({
|
|
102645
103032
|
type: "webrtc-ice",
|
|
102646
103033
|
targetMachineId,
|
|
102647
|
-
candidate
|
|
103034
|
+
candidate,
|
|
103035
|
+
generationId
|
|
102648
103036
|
});
|
|
102649
103037
|
},
|
|
102650
103038
|
onPeerDataChannel: (machineId) => {
|
|
@@ -103304,4 +103692,4 @@ export {
|
|
|
103304
103692
|
_testing,
|
|
103305
103693
|
serve
|
|
103306
103694
|
};
|
|
103307
|
-
//# sourceMappingURL=serve-
|
|
103695
|
+
//# sourceMappingURL=serve-X4R5CDHD.js.map
|