@schoolai/shipyard 3.0.0-rc.20260413.2 → 3.0.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/index.js +2 -2
- package/dist/{serve-IGUWRAXT.js → serve-LL6LUMZI.js} +245 -111
- package/dist/{serve-IGUWRAXT.js.map → serve-LL6LUMZI.js.map} +1 -1
- package/dist/{start-MJUFHZ3L.js → start-G6D7XOCQ.js} +2 -2
- package/package.json +1 -1
- /package/dist/{start-MJUFHZ3L.js.map → start-G6D7XOCQ.js.map} +0 -0
package/dist/index.js
CHANGED
|
@@ -106,7 +106,7 @@ async function handleSubcommand() {
|
|
|
106
106
|
return true;
|
|
107
107
|
}
|
|
108
108
|
if (subcommand === "start") {
|
|
109
|
-
const { startCommand } = await import("./start-
|
|
109
|
+
const { startCommand } = await import("./start-G6D7XOCQ.js");
|
|
110
110
|
await startCommand();
|
|
111
111
|
return true;
|
|
112
112
|
}
|
|
@@ -118,7 +118,7 @@ async function main() {
|
|
|
118
118
|
const args = parseCliArgs();
|
|
119
119
|
if (args.serve) {
|
|
120
120
|
await loadAuthFromConfig(env);
|
|
121
|
-
const { serve } = await import("./serve-
|
|
121
|
+
const { serve } = await import("./serve-LL6LUMZI.js");
|
|
122
122
|
return serve({ isDev: env.SHIPYARD_DEV });
|
|
123
123
|
}
|
|
124
124
|
logger.error("Use `shipyard start` to run the daemon. Use --help for usage.");
|
|
@@ -25302,6 +25302,7 @@ var TaskRecordSchema = external_exports.object({
|
|
|
25302
25302
|
currentActivity: external_exports.string().nullable(),
|
|
25303
25303
|
cwd: external_exports.string(),
|
|
25304
25304
|
acknowledgedAt: external_exports.number().nullable().optional(),
|
|
25305
|
+
attentionAt: external_exports.number().nullable().optional(),
|
|
25305
25306
|
pinned: external_exports.boolean().optional(),
|
|
25306
25307
|
scheduleId: external_exports.string().optional(),
|
|
25307
25308
|
scheduleName: external_exports.string().optional(),
|
|
@@ -29569,7 +29570,7 @@ var IFRAME_BLOCKING_HEADERS = /* @__PURE__ */ new Set([
|
|
|
29569
29570
|
"content-security-policy",
|
|
29570
29571
|
"content-security-policy-report-only"
|
|
29571
29572
|
]);
|
|
29572
|
-
var HISTORY_GUARD = `<script>(function(){var p=history.pushState,r=history.replaceState;history.pushState=function(){try{p.apply(this,arguments)}catch(e){if(!(e instanceof DOMException&&e.name==="SecurityError"))throw e}};history.replaceState=function(){try{r.apply(this,arguments)}catch(e){if(!(e instanceof DOMException&&e.name==="SecurityError"))throw e}}})();</script>`;
|
|
29573
|
+
var HISTORY_GUARD = `<script>(function(){var p=history.pushState,r=history.replaceState;history.pushState=function(){try{p.apply(this,arguments)}catch(e){if(!(e instanceof DOMException&&e.name==="SecurityError"))throw e}};history.replaceState=function(){try{r.apply(this,arguments)}catch(e){if(!(e instanceof DOMException&&e.name==="SecurityError"))throw e}};var la=Location.prototype.assign,lr=Location.prototype.replace;function patchLoc(orig,hFn){return function(url){if(typeof url==="string"&&url.charAt(0)==="#"){try{hFn.call(history,null,"",location.origin+location.pathname+location.search+url)}catch(e){location.hash=url}return}try{return orig.call(this,url)}catch(e){if(!(e instanceof DOMException&&e.name==="SecurityError"))throw e}}}try{Location.prototype.assign=patchLoc(la,p);Location.prototype.replace=patchLoc(lr,r)}catch(e){}})();</script>`;
|
|
29573
29574
|
function injectBaseHref(html, origin) {
|
|
29574
29575
|
const baseTag = `<base href="${origin}/">`;
|
|
29575
29576
|
const injection = HISTORY_GUARD + baseTag + ANNOTATION_BRIDGE_TAG;
|
|
@@ -30236,6 +30237,12 @@ async function resolveLinearToken(deps) {
|
|
|
30236
30237
|
}
|
|
30237
30238
|
return null;
|
|
30238
30239
|
}
|
|
30240
|
+
var LinearAuthError = class extends Error {
|
|
30241
|
+
constructor(message) {
|
|
30242
|
+
super(message);
|
|
30243
|
+
this.name = "LinearAuthError";
|
|
30244
|
+
}
|
|
30245
|
+
};
|
|
30239
30246
|
async function graphql(token, query3, variables) {
|
|
30240
30247
|
const res = await fetch(LINEAR_API_URL, {
|
|
30241
30248
|
method: "POST",
|
|
@@ -30247,7 +30254,7 @@ async function graphql(token, query3, variables) {
|
|
|
30247
30254
|
});
|
|
30248
30255
|
if (!res.ok) {
|
|
30249
30256
|
if (res.status === 401) {
|
|
30250
|
-
throw new
|
|
30257
|
+
throw new LinearAuthError("Linear authentication expired");
|
|
30251
30258
|
}
|
|
30252
30259
|
throw new Error(`Linear API error: ${res.status} ${res.statusText}`);
|
|
30253
30260
|
}
|
|
@@ -30480,6 +30487,9 @@ async function handleLinearPluginRequest(method, params, deps) {
|
|
|
30480
30487
|
} catch (err) {
|
|
30481
30488
|
const message = err instanceof Error ? err.message : String(err);
|
|
30482
30489
|
deps.log({ event: "linear_plugin_error", method, error: message });
|
|
30490
|
+
if (err instanceof LinearAuthError) {
|
|
30491
|
+
return { authError: message };
|
|
30492
|
+
}
|
|
30483
30493
|
return { error: message };
|
|
30484
30494
|
}
|
|
30485
30495
|
}
|
|
@@ -30723,7 +30733,28 @@ function handlePluginDataRequest(requestId, pluginId, method, params, sendContro
|
|
|
30723
30733
|
});
|
|
30724
30734
|
}
|
|
30725
30735
|
};
|
|
30726
|
-
plugin.onPluginDataRequest(method, params, pluginDeps).then(
|
|
30736
|
+
plugin.onPluginDataRequest(method, params, pluginDeps).then((result) => {
|
|
30737
|
+
if ("authError" in result && result.authError) {
|
|
30738
|
+
const oauthConfig = plugin.oauthConfig;
|
|
30739
|
+
if (oauthConfig && deps.tokenStore) {
|
|
30740
|
+
const vaultKey = oauthConfig.vaultTokenKey;
|
|
30741
|
+
const baseName = vaultKey.replace(/-oauth$/, "");
|
|
30742
|
+
void deps.tokenStore.deleteToken(vaultKey);
|
|
30743
|
+
if (baseName !== vaultKey) {
|
|
30744
|
+
void deps.tokenStore.deleteToken(baseName);
|
|
30745
|
+
}
|
|
30746
|
+
}
|
|
30747
|
+
sendControl({
|
|
30748
|
+
type: "plugin_auth_status",
|
|
30749
|
+
pluginId,
|
|
30750
|
+
status: "error",
|
|
30751
|
+
error: result.authError
|
|
30752
|
+
});
|
|
30753
|
+
respond({ error: result.authError });
|
|
30754
|
+
} else {
|
|
30755
|
+
respond(result);
|
|
30756
|
+
}
|
|
30757
|
+
}).catch((err) => {
|
|
30727
30758
|
respond({ error: err instanceof Error ? err.message : String(err) });
|
|
30728
30759
|
});
|
|
30729
30760
|
}
|
|
@@ -31583,6 +31614,28 @@ var McpProxyServer = class {
|
|
|
31583
31614
|
return { name, status: "pending" };
|
|
31584
31615
|
});
|
|
31585
31616
|
}
|
|
31617
|
+
/** Whether a server is tracked (connected or failed). */
|
|
31618
|
+
hasConfigured(name) {
|
|
31619
|
+
return this.#allConfigured.has(name);
|
|
31620
|
+
}
|
|
31621
|
+
/**
|
|
31622
|
+
* Dynamically add and connect a server after initialization.
|
|
31623
|
+
* Used when an HTTP/SSE server is toggled on after daemon start.
|
|
31624
|
+
*/
|
|
31625
|
+
async addServer(name, config2) {
|
|
31626
|
+
this.#allConfigured.set(name, config2);
|
|
31627
|
+
try {
|
|
31628
|
+
await this.connectServer(name, config2);
|
|
31629
|
+
} catch (err) {
|
|
31630
|
+
if (!this.#connectErrors.has(name)) this.#connectErrors.set(name, "transport");
|
|
31631
|
+
this.#deps.log({
|
|
31632
|
+
event: "mcp_proxy_connect_failed",
|
|
31633
|
+
serverName: name,
|
|
31634
|
+
url: config2.url,
|
|
31635
|
+
error: err instanceof Error ? err.message : String(err)
|
|
31636
|
+
});
|
|
31637
|
+
}
|
|
31638
|
+
}
|
|
31586
31639
|
/** Disconnect a single server. */
|
|
31587
31640
|
async disconnectServer(name) {
|
|
31588
31641
|
const server = this.#servers.get(name);
|
|
@@ -31751,15 +31804,6 @@ async function sweepStaleTasks(taskStateStore, taskManager, log) {
|
|
|
31751
31804
|
}
|
|
31752
31805
|
}
|
|
31753
31806
|
}
|
|
31754
|
-
async function resealAcknowledgements(taskStateStore, log) {
|
|
31755
|
-
const tasks = await taskStateStore.listTasks();
|
|
31756
|
-
for (const [taskId, task] of Object.entries(tasks)) {
|
|
31757
|
-
if (task.acknowledgedAt != null && task.updatedAt > task.acknowledgedAt) {
|
|
31758
|
-
await taskStateStore.acknowledge(taskId);
|
|
31759
|
-
log({ event: "acknowledgement_resealed", taskId });
|
|
31760
|
-
}
|
|
31761
|
-
}
|
|
31762
|
-
}
|
|
31763
31807
|
|
|
31764
31808
|
// src/services/credentials/credentials-sync.ts
|
|
31765
31809
|
import {
|
|
@@ -65293,10 +65337,12 @@ var PreWarmManager = class {
|
|
|
65293
65337
|
serverCount: statuses.length,
|
|
65294
65338
|
servers: statuses.map((s2) => ({ name: s2.name, status: s2.status, error: s2.error }))
|
|
65295
65339
|
});
|
|
65296
|
-
|
|
65297
|
-
|
|
65298
|
-
|
|
65299
|
-
|
|
65340
|
+
if (!this.#deps.hasLiveSubprocesses()) {
|
|
65341
|
+
const proxyStatuses = this.#deps.getProxyStatuses();
|
|
65342
|
+
const proxyNames = new Set(proxyStatuses.map((s2) => s2.name));
|
|
65343
|
+
const merged = [...statuses.filter((s2) => !proxyNames.has(s2.name)), ...proxyStatuses];
|
|
65344
|
+
this.#deps.broadcastControl({ type: "mcp_server_status", servers: merged });
|
|
65345
|
+
}
|
|
65300
65346
|
this.#disableNativePluginsForProxy(statuses);
|
|
65301
65347
|
}).catch((err) => {
|
|
65302
65348
|
const msg = err instanceof Error ? err.message : String(err);
|
|
@@ -75475,7 +75521,7 @@ var AgentSessionManager = class {
|
|
|
75475
75521
|
var DEFAULTS = {
|
|
75476
75522
|
assistantHeadChars: 400,
|
|
75477
75523
|
assistantTailChars: 400,
|
|
75478
|
-
|
|
75524
|
+
errorResultMaxChars: 200,
|
|
75479
75525
|
maxTotalChars: 5e4
|
|
75480
75526
|
};
|
|
75481
75527
|
function serializeConversationContext(messages, options) {
|
|
@@ -75504,7 +75550,7 @@ function partitionBlockLines(blockLines) {
|
|
|
75504
75550
|
const textLines = [];
|
|
75505
75551
|
const toolLines = [];
|
|
75506
75552
|
for (const line of blockLines) {
|
|
75507
|
-
if (line.startsWith(" [
|
|
75553
|
+
if (line.startsWith(" [") || line.startsWith(" \u2192")) {
|
|
75508
75554
|
toolLines.push(line);
|
|
75509
75555
|
} else {
|
|
75510
75556
|
textLines.push(line);
|
|
@@ -75525,22 +75571,39 @@ function serializeBlock(block2, senderKind, opts) {
|
|
|
75525
75571
|
case "resource":
|
|
75526
75572
|
return serializeEmbeddedResource(block2);
|
|
75527
75573
|
case "thinking":
|
|
75528
|
-
return
|
|
75574
|
+
return null;
|
|
75529
75575
|
case "compaction_boundary":
|
|
75530
75576
|
return ` [compaction boundary: ${block2.messageCount} messages compacted]`;
|
|
75531
75577
|
case "diff_review_feedback":
|
|
75532
|
-
|
|
75578
|
+
return serializeDiffReviewFeedback(block2);
|
|
75533
75579
|
case "annotation_feedback":
|
|
75580
|
+
return serializeAnnotationFeedback(block2);
|
|
75581
|
+
case "preview_annotation_feedback":
|
|
75582
|
+
return serializePreviewAnnotationFeedback(block2);
|
|
75534
75583
|
case "task_feedback":
|
|
75584
|
+
return ` [task feedback: ${block2.changes.map((c) => `${c.subject} (${c.changeType})`).join(", ")}]`;
|
|
75535
75585
|
case "review_comment":
|
|
75586
|
+
return ` [review: ${block2.body}]`;
|
|
75536
75587
|
case "review_decision":
|
|
75537
|
-
return ` [${block2.
|
|
75588
|
+
return ` [review decision: ${block2.decision}${block2.feedback ? ` \u2014 ${block2.feedback}` : ""}]`;
|
|
75538
75589
|
default: {
|
|
75539
75590
|
block2;
|
|
75540
75591
|
return null;
|
|
75541
75592
|
}
|
|
75542
75593
|
}
|
|
75543
75594
|
}
|
|
75595
|
+
function serializeDiffReviewFeedback(block2) {
|
|
75596
|
+
const comments = block2.comments.map((c) => `${c.filePath}:${c.lineNumber ?? ""} \u2014 ${c.body}`).join("; ");
|
|
75597
|
+
return ` [diff feedback: ${comments}]`;
|
|
75598
|
+
}
|
|
75599
|
+
function serializeAnnotationFeedback(block2) {
|
|
75600
|
+
const bodies = block2.annotations.map((a) => a.body).join("; ");
|
|
75601
|
+
return ` [annotation feedback: ${bodies}]`;
|
|
75602
|
+
}
|
|
75603
|
+
function serializePreviewAnnotationFeedback(block2) {
|
|
75604
|
+
const comments = block2.comments.map((c) => `${c.url}: ${c.body}`).join("; ");
|
|
75605
|
+
return ` [preview feedback: ${comments}]`;
|
|
75606
|
+
}
|
|
75544
75607
|
function serializeBlocks(blocks, senderKind, opts) {
|
|
75545
75608
|
const result = [];
|
|
75546
75609
|
for (const block2 of blocks) {
|
|
@@ -75559,26 +75622,50 @@ function serializeTextBlock(text2, senderKind, opts) {
|
|
|
75559
75622
|
return `${head} [truncated ${truncatedChars} chars] ${tail}`;
|
|
75560
75623
|
}
|
|
75561
75624
|
function serializeToolUse(block2) {
|
|
75562
|
-
const
|
|
75563
|
-
|
|
75564
|
-
}
|
|
75565
|
-
|
|
75566
|
-
|
|
75567
|
-
|
|
75568
|
-
|
|
75569
|
-
|
|
75570
|
-
|
|
75571
|
-
|
|
75625
|
+
const name = shortToolName(block2.toolName);
|
|
75626
|
+
const key = extractKeyParam(block2.toolName, block2.input);
|
|
75627
|
+
return key ? ` [${name}: ${key}]` : ` [${name}]`;
|
|
75628
|
+
}
|
|
75629
|
+
function shortToolName(toolName) {
|
|
75630
|
+
if (toolName.startsWith("mcp__")) {
|
|
75631
|
+
const parts = toolName.split("__");
|
|
75632
|
+
return parts[parts.length - 1] ?? toolName;
|
|
75633
|
+
}
|
|
75634
|
+
return toolName;
|
|
75635
|
+
}
|
|
75636
|
+
function extractKeyParam(toolName, input) {
|
|
75637
|
+
const baseName = shortToolName(toolName);
|
|
75638
|
+
switch (baseName) {
|
|
75639
|
+
case "Read":
|
|
75640
|
+
case "Edit":
|
|
75641
|
+
case "Write":
|
|
75642
|
+
return truncParam(input.file_path);
|
|
75643
|
+
case "Bash":
|
|
75644
|
+
return truncParam(input.command, 80);
|
|
75645
|
+
case "Grep":
|
|
75646
|
+
return truncParam(input.pattern);
|
|
75647
|
+
case "Glob":
|
|
75648
|
+
return truncParam(input.pattern);
|
|
75649
|
+
case "Agent":
|
|
75650
|
+
return truncParam(input.prompt, 80);
|
|
75651
|
+
case "WebSearch":
|
|
75652
|
+
return truncParam(input.query);
|
|
75653
|
+
case "WebFetch":
|
|
75654
|
+
return truncParam(input.url);
|
|
75655
|
+
default: {
|
|
75656
|
+
const firstStr = Object.values(input).find((v2) => typeof v2 === "string");
|
|
75657
|
+
return typeof firstStr === "string" ? truncParam(firstStr, 60) : null;
|
|
75572
75658
|
}
|
|
75573
75659
|
}
|
|
75574
|
-
|
|
75575
|
-
|
|
75660
|
+
}
|
|
75661
|
+
function truncParam(value, max = 120) {
|
|
75662
|
+
if (typeof value !== "string") return null;
|
|
75663
|
+
return value.length > max ? value.slice(0, max) : value;
|
|
75576
75664
|
}
|
|
75577
75665
|
function serializeToolResult(block2, opts) {
|
|
75578
|
-
|
|
75579
|
-
const
|
|
75580
|
-
|
|
75581
|
-
return ` \u2192 ${status}: ${content}`;
|
|
75666
|
+
if (!block2.isError) return null;
|
|
75667
|
+
const content = block2.content.length > opts.errorResultMaxChars ? block2.content.slice(0, opts.errorResultMaxChars) : block2.content;
|
|
75668
|
+
return ` \u2192 error: ${content}`;
|
|
75582
75669
|
}
|
|
75583
75670
|
function serializeEmbeddedResource(block2) {
|
|
75584
75671
|
const uri = block2.resource.uri ?? "unknown";
|
|
@@ -76866,6 +76953,8 @@ var PlanHandler = class {
|
|
|
76866
76953
|
#processedPlanToolUseIds = /* @__PURE__ */ new Set();
|
|
76867
76954
|
/** Tracks whether we have already published the plan (first ExitPlanMode). */
|
|
76868
76955
|
#planPublished = false;
|
|
76956
|
+
/** Plan files this task's agent created (from Write tool results). */
|
|
76957
|
+
#createdPlanFiles = /* @__PURE__ */ new Set();
|
|
76869
76958
|
constructor(deps) {
|
|
76870
76959
|
this.#deps = deps;
|
|
76871
76960
|
}
|
|
@@ -76876,30 +76965,18 @@ var PlanHandler = class {
|
|
|
76876
76965
|
return this.#lastPlanDetection;
|
|
76877
76966
|
}
|
|
76878
76967
|
/**
|
|
76879
|
-
*
|
|
76880
|
-
*
|
|
76881
|
-
* the bridge and sends plan_detected so the browser shows the TipTap editor.
|
|
76968
|
+
* Record a plan file path created by this task's agent.
|
|
76969
|
+
* Called when a Write tool result mentions a file in ~/.claude/plans/.
|
|
76882
76970
|
*/
|
|
76883
|
-
|
|
76884
|
-
if (this.#planFileBridge) return;
|
|
76971
|
+
trackCreatedFile(filePath) {
|
|
76885
76972
|
const plansDir = join28(homedir4(), ".claude", "plans");
|
|
76886
|
-
if (
|
|
76887
|
-
|
|
76888
|
-
const files = readdirSync(plansDir).filter((f2) => f2.endsWith(".md")).map((f2) => ({
|
|
76889
|
-
name: f2,
|
|
76890
|
-
path: join28(plansDir, f2),
|
|
76891
|
-
mtime: statSync2(join28(plansDir, f2)).mtimeMs
|
|
76892
|
-
})).sort((a, b2) => b2.mtime - a.mtime);
|
|
76893
|
-
const recent = files[0];
|
|
76894
|
-
if (!recent) return;
|
|
76895
|
-
const syntheticToolUseId = `plan-reattach-${Date.now()}`;
|
|
76973
|
+
if (filePath.startsWith(plansDir) && filePath.endsWith(".md")) {
|
|
76974
|
+
this.#createdPlanFiles.add(filePath);
|
|
76896
76975
|
this.#deps.log({
|
|
76897
|
-
event: "
|
|
76976
|
+
event: "plan_file_tracked",
|
|
76898
76977
|
taskId: this.#deps.taskId,
|
|
76899
|
-
filePath
|
|
76978
|
+
filePath
|
|
76900
76979
|
});
|
|
76901
|
-
this.#handlePlanDetected(syntheticToolUseId, recent.path);
|
|
76902
|
-
} catch {
|
|
76903
76980
|
}
|
|
76904
76981
|
}
|
|
76905
76982
|
/**
|
|
@@ -76997,22 +77074,16 @@ var PlanHandler = class {
|
|
|
76997
77074
|
taskId: this.#deps.taskId,
|
|
76998
77075
|
toolUseId: block2.toolUseId
|
|
76999
77076
|
});
|
|
77000
|
-
const
|
|
77001
|
-
if (
|
|
77002
|
-
const files = readdirSync(plansDir).filter((f2) => f2.endsWith(".md")).map((f2) => ({
|
|
77003
|
-
name: f2,
|
|
77004
|
-
path: join28(plansDir, f2),
|
|
77005
|
-
mtime: statSync2(join28(plansDir, f2)).mtimeMs
|
|
77006
|
-
})).sort((a, b2) => b2.mtime - a.mtime);
|
|
77007
|
-
const recent = files[0];
|
|
77008
|
-
if (recent && Date.now() - recent.mtime < 3e4) {
|
|
77077
|
+
const match2 = this.#findTaskPlanFile();
|
|
77078
|
+
if (match2) {
|
|
77009
77079
|
this.#deps.log({
|
|
77010
77080
|
event: "plan_file_found_from_message",
|
|
77011
77081
|
taskId: this.#deps.taskId,
|
|
77012
77082
|
toolUseId: block2.toolUseId,
|
|
77013
|
-
filePath:
|
|
77083
|
+
filePath: match2,
|
|
77084
|
+
source: this.#createdPlanFiles.size > 0 ? "tracked" : "mtime_fallback"
|
|
77014
77085
|
});
|
|
77015
|
-
this.#handlePlanDetected(block2.toolUseId,
|
|
77086
|
+
this.#handlePlanDetected(block2.toolUseId, match2);
|
|
77016
77087
|
}
|
|
77017
77088
|
}
|
|
77018
77089
|
}
|
|
@@ -77045,6 +77116,7 @@ var PlanHandler = class {
|
|
|
77045
77116
|
this.#lastPlanDetection = null;
|
|
77046
77117
|
this.#processedPlanToolUseIds.clear();
|
|
77047
77118
|
this.#pendingPlanAllowedPrompts.clear();
|
|
77119
|
+
this.#createdPlanFiles.clear();
|
|
77048
77120
|
this.#planFileBridge?.dispose();
|
|
77049
77121
|
this.#planFileBridge = null;
|
|
77050
77122
|
}
|
|
@@ -77059,37 +77131,23 @@ var PlanHandler = class {
|
|
|
77059
77131
|
this.#planFileBridge = null;
|
|
77060
77132
|
this.#processedPlanToolUseIds.clear();
|
|
77061
77133
|
this.#pendingPlanAllowedPrompts.clear();
|
|
77134
|
+
this.#createdPlanFiles.clear();
|
|
77062
77135
|
}
|
|
77063
77136
|
#pollForPlanFile(toolUseId) {
|
|
77064
|
-
const plansDir = join28(homedir4(), ".claude", "plans");
|
|
77065
|
-
if (!existsSync4(plansDir)) {
|
|
77066
|
-
this.#deps.log({ event: "plan_dir_not_found", taskId: this.#deps.taskId, plansDir });
|
|
77067
|
-
return;
|
|
77068
|
-
}
|
|
77069
|
-
const startTime = Date.now();
|
|
77070
|
-
const lookbackMs = 3e4;
|
|
77071
77137
|
let attempts = 0;
|
|
77072
77138
|
const checkForPlanFile = () => {
|
|
77073
|
-
|
|
77074
|
-
|
|
77075
|
-
|
|
77076
|
-
|
|
77077
|
-
|
|
77078
|
-
|
|
77079
|
-
|
|
77080
|
-
|
|
77081
|
-
|
|
77082
|
-
|
|
77083
|
-
|
|
77084
|
-
|
|
77085
|
-
attempts
|
|
77086
|
-
});
|
|
77087
|
-
this.#handlePlanDetected(toolUseId, filePath);
|
|
77088
|
-
return true;
|
|
77089
|
-
}
|
|
77090
|
-
}
|
|
77091
|
-
} catch {
|
|
77092
|
-
return false;
|
|
77139
|
+
const match2 = this.#findTaskPlanFile();
|
|
77140
|
+
if (match2) {
|
|
77141
|
+
this.#deps.log({
|
|
77142
|
+
event: "plan_file_found",
|
|
77143
|
+
taskId: this.#deps.taskId,
|
|
77144
|
+
toolUseId,
|
|
77145
|
+
filePath: match2,
|
|
77146
|
+
attempts,
|
|
77147
|
+
source: this.#createdPlanFiles.size > 0 ? "tracked" : "mtime_fallback"
|
|
77148
|
+
});
|
|
77149
|
+
this.#handlePlanDetected(toolUseId, match2);
|
|
77150
|
+
return true;
|
|
77093
77151
|
}
|
|
77094
77152
|
return false;
|
|
77095
77153
|
};
|
|
@@ -77114,6 +77172,35 @@ var PlanHandler = class {
|
|
|
77114
77172
|
}, PLAN_POLL_INTERVAL_MS);
|
|
77115
77173
|
this.#pollInterval = interval;
|
|
77116
77174
|
}
|
|
77175
|
+
/**
|
|
77176
|
+
* Find a plan file for this task. Prefers files tracked from Write tool
|
|
77177
|
+
* results (task-scoped). Falls back to global mtime scan for backwards
|
|
77178
|
+
* compat with sessions that started before tracking was added.
|
|
77179
|
+
*/
|
|
77180
|
+
#findTaskPlanFile() {
|
|
77181
|
+
if (this.#createdPlanFiles.size > 0) {
|
|
77182
|
+
const newest = [...this.#createdPlanFiles].map((f2) => {
|
|
77183
|
+
try {
|
|
77184
|
+
return { path: f2, mtime: statSync2(f2).mtimeMs };
|
|
77185
|
+
} catch {
|
|
77186
|
+
return null;
|
|
77187
|
+
}
|
|
77188
|
+
}).filter((f2) => f2 !== null).sort((a, b2) => b2.mtime - a.mtime)[0];
|
|
77189
|
+
return newest?.path ?? null;
|
|
77190
|
+
}
|
|
77191
|
+
const plansDir = join28(homedir4(), ".claude", "plans");
|
|
77192
|
+
if (!existsSync4(plansDir)) return null;
|
|
77193
|
+
try {
|
|
77194
|
+
const files = readdirSync(plansDir).filter((f2) => f2.endsWith(".md")).map((f2) => ({
|
|
77195
|
+
path: join28(plansDir, f2),
|
|
77196
|
+
mtime: statSync2(join28(plansDir, f2)).mtimeMs
|
|
77197
|
+
})).sort((a, b2) => b2.mtime - a.mtime);
|
|
77198
|
+
const recent = files[0];
|
|
77199
|
+
if (recent && Date.now() - recent.mtime < 3e4) return recent.path;
|
|
77200
|
+
} catch {
|
|
77201
|
+
}
|
|
77202
|
+
return null;
|
|
77203
|
+
}
|
|
77117
77204
|
#handlePlanDetected(toolUseId, filePath) {
|
|
77118
77205
|
if (this.#processedPlanToolUseIds.has(toolUseId)) {
|
|
77119
77206
|
this.#deps.log({
|
|
@@ -79251,6 +79338,7 @@ var McpPoller = class {
|
|
|
79251
79338
|
start() {
|
|
79252
79339
|
this.stop();
|
|
79253
79340
|
this.#deps.log({ event: "mcp_poll_start", taskId: this.#deps.taskId });
|
|
79341
|
+
this.pollOnce();
|
|
79254
79342
|
this.#schedulePoll(MCP_POLL_IDLE_MS);
|
|
79255
79343
|
}
|
|
79256
79344
|
enterFastMode() {
|
|
@@ -81603,6 +81691,7 @@ Use this context to maintain continuity. You have already done this work \u2014
|
|
|
81603
81691
|
this.#deps.updateTodoProgress(todoProgress);
|
|
81604
81692
|
}
|
|
81605
81693
|
this.#structuredTaskTracker.processStructuredTaskEvents(event.content);
|
|
81694
|
+
this.#trackPlanFileFromToolUse(event.content);
|
|
81606
81695
|
this.#planHandler.detectPlanEvents(event.content);
|
|
81607
81696
|
this.#trackWorktreeToolUse(event.content);
|
|
81608
81697
|
this.#subagentManager.trackAgentToolUse(event.content);
|
|
@@ -81614,6 +81703,7 @@ Use this context to maintain continuity. You have already done this work \u2014
|
|
|
81614
81703
|
this.#structuredTaskTracker.resolveStructuredTaskCreates(event.content);
|
|
81615
81704
|
this.#handleWorktreeToolResults(event.content);
|
|
81616
81705
|
this.#subagentManager.handleSubagentToolResults(event.content);
|
|
81706
|
+
this.#trackPlanFileCreation(event.content);
|
|
81617
81707
|
break;
|
|
81618
81708
|
case "api_usage_snapshot":
|
|
81619
81709
|
this.#lastApiUsage = {
|
|
@@ -81840,6 +81930,26 @@ Use this context to maintain continuity. You have already done this work \u2014
|
|
|
81840
81930
|
this.#deps.onCwdChanged(this.#deps.taskId, newCwd);
|
|
81841
81931
|
}
|
|
81842
81932
|
}
|
|
81933
|
+
#trackPlanFileFromToolUse(content) {
|
|
81934
|
+
for (const block2 of content) {
|
|
81935
|
+
if (block2.type !== "tool_use") continue;
|
|
81936
|
+
if (block2.toolName !== "Write") continue;
|
|
81937
|
+
const filePath = block2.input?.file_path;
|
|
81938
|
+
if (typeof filePath === "string") {
|
|
81939
|
+
this.#planHandler.trackCreatedFile(filePath);
|
|
81940
|
+
}
|
|
81941
|
+
}
|
|
81942
|
+
}
|
|
81943
|
+
#trackPlanFileCreation(content) {
|
|
81944
|
+
const writeResultPattern = /File created successfully at: (\/[^\s"')]+\.claude\/plans\/[^\s"')]+\.md)\b/;
|
|
81945
|
+
for (const block2 of content) {
|
|
81946
|
+
if (block2.type !== "tool_result" || block2.isError) continue;
|
|
81947
|
+
const match2 = block2.content.match(writeResultPattern);
|
|
81948
|
+
if (match2?.[1]) {
|
|
81949
|
+
this.#planHandler.trackCreatedFile(match2[1]);
|
|
81950
|
+
}
|
|
81951
|
+
}
|
|
81952
|
+
}
|
|
81843
81953
|
applyOverlay(overlay) {
|
|
81844
81954
|
this.#structuredTaskTracker.applyOverlay(overlay);
|
|
81845
81955
|
}
|
|
@@ -82939,13 +83049,10 @@ function applyStatusTransition(task, status, now) {
|
|
|
82939
83049
|
...task,
|
|
82940
83050
|
status,
|
|
82941
83051
|
updatedAt: now,
|
|
82942
|
-
taskStartedAt: status === "in_progress" ? now : null
|
|
83052
|
+
taskStartedAt: status === "in_progress" ? now : null,
|
|
83053
|
+
attentionAt: status === "input_required" ? now : null
|
|
82943
83054
|
};
|
|
82944
83055
|
}
|
|
82945
|
-
function hasTodoProgressChanged(task, todoProgress) {
|
|
82946
|
-
if (!todoProgress) return false;
|
|
82947
|
-
return task.todoCompleted !== todoProgress.todoCompleted || task.todoTotal !== todoProgress.todoTotal || task.currentActivity !== todoProgress.currentActivity;
|
|
82948
|
-
}
|
|
82949
83056
|
function buildTaskStateStore(dataDir) {
|
|
82950
83057
|
const store = buildJsonDocumentStore({
|
|
82951
83058
|
filePath: join35(dataDir, "tasks.json"),
|
|
@@ -83042,9 +83149,6 @@ function buildTaskStateStore(dataDir) {
|
|
|
83042
83149
|
async updateStructuredTasks(taskId, tasks, todoProgress) {
|
|
83043
83150
|
const task = await store.get(taskId);
|
|
83044
83151
|
if (!task) return;
|
|
83045
|
-
const progressChanged = hasTodoProgressChanged(task, todoProgress);
|
|
83046
|
-
const tasksChanged = JSON.stringify(task.structuredTasks ?? {}) !== JSON.stringify(tasks);
|
|
83047
|
-
if (!progressChanged && !tasksChanged) return;
|
|
83048
83152
|
await store.set(taskId, {
|
|
83049
83153
|
...task,
|
|
83050
83154
|
structuredTasks: tasks,
|
|
@@ -83059,7 +83163,6 @@ function buildTaskStateStore(dataDir) {
|
|
|
83059
83163
|
async updateTaskOverlay(taskId, overlay) {
|
|
83060
83164
|
const task = await store.get(taskId);
|
|
83061
83165
|
if (!task) return;
|
|
83062
|
-
if (JSON.stringify(task.taskOverlay ?? {}) === JSON.stringify(overlay)) return;
|
|
83063
83166
|
await store.set(taskId, {
|
|
83064
83167
|
...task,
|
|
83065
83168
|
taskOverlay: overlay,
|
|
@@ -83737,7 +83840,8 @@ async function createDaemon(deps) {
|
|
|
83737
83840
|
broadcastControl: (msg) => taskManager.broadcastControl(msg),
|
|
83738
83841
|
resolveMcpServers,
|
|
83739
83842
|
getProxyStatuses: () => proxyRef.current?.getServerStatuses() ?? [],
|
|
83740
|
-
getProxyManagedNames: () => proxyRef.current?.getAllManagedNames() ?? []
|
|
83843
|
+
getProxyManagedNames: () => proxyRef.current?.getAllManagedNames() ?? [],
|
|
83844
|
+
hasLiveSubprocesses: () => taskManager.hasLiveSubprocesses
|
|
83741
83845
|
});
|
|
83742
83846
|
taskManager.setPreWarmManager(preWarmManager);
|
|
83743
83847
|
const scheduleStore = buildScheduleStore(deps.dataDir);
|
|
@@ -83858,7 +83962,6 @@ async function createDaemon(deps) {
|
|
|
83858
83962
|
}
|
|
83859
83963
|
await rehydrateFromPersistence(sessionPersistence, taskManager, deps.log, taskStateStore);
|
|
83860
83964
|
await sweepStaleTasks(taskStateStore, taskManager, deps.log);
|
|
83861
|
-
await resealAcknowledgements(taskStateStore, deps.log);
|
|
83862
83965
|
const taskStoreUnsub = taskStateStore.subscribe((event) => {
|
|
83863
83966
|
switch (event.kind) {
|
|
83864
83967
|
case "set": {
|
|
@@ -85791,6 +85894,28 @@ function applyMcpServerToggles(daemon, prevDisabled, nextDisabled, log) {
|
|
|
85791
85894
|
if (!nextDisabled.has(name)) {
|
|
85792
85895
|
log({ event: "mcp_server_enabling", serverName: name });
|
|
85793
85896
|
daemon.taskManager.toggleMcpServer(name, true);
|
|
85897
|
+
if (daemon.proxyServer && !daemon.proxyServer.hasConfigured(name)) {
|
|
85898
|
+
const proxyServer = daemon.proxyServer;
|
|
85899
|
+
const server = daemon.capabilities.mcpServers?.find(
|
|
85900
|
+
(s2) => s2.name === name && (s2.type === "http" || s2.type === "sse") && s2.url
|
|
85901
|
+
);
|
|
85902
|
+
if (server?.url) {
|
|
85903
|
+
log({ event: "mcp_proxy_dynamic_add", serverName: name, url: server.url });
|
|
85904
|
+
proxyServer.addServer(name, { url: server.url, callbackPort: server.oauth?.callbackPort }).then(() => {
|
|
85905
|
+
daemon.taskManager.triggerFastMcpPolling();
|
|
85906
|
+
daemon.taskManager.broadcastControl({
|
|
85907
|
+
type: "mcp_server_status",
|
|
85908
|
+
servers: proxyServer.getServerStatuses()
|
|
85909
|
+
});
|
|
85910
|
+
}).catch((err) => {
|
|
85911
|
+
log({
|
|
85912
|
+
event: "mcp_proxy_dynamic_add_failed",
|
|
85913
|
+
serverName: name,
|
|
85914
|
+
error: err instanceof Error ? err.message : String(err)
|
|
85915
|
+
});
|
|
85916
|
+
});
|
|
85917
|
+
}
|
|
85918
|
+
}
|
|
85794
85919
|
}
|
|
85795
85920
|
}
|
|
85796
85921
|
const resolved = daemon.resolveMcpServers() ?? {};
|
|
@@ -86148,11 +86273,17 @@ function wireControlChannel(rawChannel, daemon, logAdapter, deps) {
|
|
|
86148
86273
|
)
|
|
86149
86274
|
};
|
|
86150
86275
|
handler.sendControl(buildCapabilitiesMessage());
|
|
86151
|
-
|
|
86152
|
-
|
|
86153
|
-
|
|
86154
|
-
|
|
86155
|
-
|
|
86276
|
+
{
|
|
86277
|
+
const subprocessStatuses = daemon.preWarmManager.getLastMcpStatuses() ?? [];
|
|
86278
|
+
const proxyStatuses = daemon.proxyServer?.getServerStatuses() ?? [];
|
|
86279
|
+
const proxyNames = new Set(proxyStatuses.map((s2) => s2.name));
|
|
86280
|
+
const merged = [
|
|
86281
|
+
...subprocessStatuses.filter((s2) => !proxyNames.has(s2.name)),
|
|
86282
|
+
...proxyStatuses
|
|
86283
|
+
];
|
|
86284
|
+
if (merged.length > 0) {
|
|
86285
|
+
handler.sendControl({ type: "mcp_server_status", servers: merged });
|
|
86286
|
+
}
|
|
86156
86287
|
}
|
|
86157
86288
|
const resolved = daemon.resolveMcpServers();
|
|
86158
86289
|
if (resolved) {
|
|
@@ -86534,6 +86665,9 @@ function wireControlChannel(rawChannel, daemon, logAdapter, deps) {
|
|
|
86534
86665
|
const merged = [...subprocessStatuses.filter((s2) => !proxyNames.has(s2.name)), ...proxyStatuses];
|
|
86535
86666
|
if (merged.length > 0) {
|
|
86536
86667
|
handler.sendControl({ type: "mcp_server_status", servers: merged });
|
|
86668
|
+
if (subprocessStatuses.length === 0 && retriesLeft > 0) {
|
|
86669
|
+
setTimeout(() => pushMcpStatus(retriesLeft - 1), 3e3);
|
|
86670
|
+
}
|
|
86537
86671
|
return;
|
|
86538
86672
|
}
|
|
86539
86673
|
if (retriesLeft > 0) {
|
|
@@ -90568,4 +90702,4 @@ export {
|
|
|
90568
90702
|
classifyLogLevel,
|
|
90569
90703
|
serve
|
|
90570
90704
|
};
|
|
90571
|
-
//# sourceMappingURL=serve-
|
|
90705
|
+
//# sourceMappingURL=serve-LL6LUMZI.js.map
|