@adhdev/daemon-core 0.8.24 → 0.8.27
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/agent-stream/provider-adapter.d.ts +1 -0
- package/dist/boot/daemon-lifecycle.d.ts +2 -0
- package/dist/cli-adapters/pty-transport.d.ts +3 -0
- package/dist/commands/router.d.ts +24 -0
- package/dist/index.js +309 -19
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +309 -19
- package/dist/index.mjs.map +1 -1
- package/dist/providers/cli-provider-instance.d.ts +1 -0
- package/dist/providers/extension-provider-instance.d.ts +7 -0
- package/dist/sessions/reconcile.d.ts +22 -0
- package/dist/status/normalize.js +14 -2
- package/dist/status/normalize.js.map +1 -1
- package/dist/status/normalize.mjs +14 -2
- package/dist/status/normalize.mjs.map +1 -1
- package/dist/status/snapshot.d.ts +0 -2
- package/node_modules/@adhdev/session-host-core/dist/index.d.mts +72 -1
- package/node_modules/@adhdev/session-host-core/dist/index.d.ts +72 -1
- package/node_modules/@adhdev/session-host-core/dist/index.js.map +1 -1
- package/node_modules/@adhdev/session-host-core/package.json +1 -1
- package/package.json +1 -1
- package/src/agent-stream/poller.ts +4 -0
- package/src/agent-stream/provider-adapter.ts +30 -1
- package/src/boot/daemon-lifecycle.ts +3 -0
- package/src/cli-adapters/pty-transport.ts +3 -0
- package/src/cli-adapters/session-host-transport.ts +8 -0
- package/src/commands/chat-commands.ts +2 -2
- package/src/commands/handler.ts +79 -13
- package/src/commands/router.ts +132 -0
- package/src/providers/cli-provider-instance.ts +29 -1
- package/src/providers/extension-provider-instance.ts +24 -1
- package/src/sessions/reconcile.ts +85 -0
- package/src/status/normalize.ts +19 -2
- package/src/status/snapshot.ts +6 -13
|
@@ -16,6 +16,7 @@ export declare class ProviderStreamAdapter implements IAgentStreamAdapter {
|
|
|
16
16
|
constructor(provider: ProviderModule);
|
|
17
17
|
private callScript;
|
|
18
18
|
private hasScript;
|
|
19
|
+
private parseMaybeJson;
|
|
19
20
|
private summarizeRaw;
|
|
20
21
|
private isTransportError;
|
|
21
22
|
readChat(evaluate: AgentEvaluateFn): Promise<AgentStreamState>;
|
|
@@ -10,6 +10,7 @@ import { DaemonCdpManager } from '../cdp/manager.js';
|
|
|
10
10
|
import { DaemonCdpInitializer } from '../cdp/initializer.js';
|
|
11
11
|
import { DaemonCommandHandler } from '../commands/handler.js';
|
|
12
12
|
import { DaemonCommandRouter } from '../commands/router.js';
|
|
13
|
+
import type { SessionHostControlPlane } from '../commands/router.js';
|
|
13
14
|
import { DaemonCliManager, type CliTransportFactoryParams, type HostedCliRuntimeDescriptor } from '../commands/cli-manager.js';
|
|
14
15
|
import { DaemonAgentStreamManager } from '../agent-stream/manager.js';
|
|
15
16
|
import { AgentStreamPoller } from '../agent-stream/poller.js';
|
|
@@ -35,6 +36,7 @@ export interface DaemonInitConfig {
|
|
|
35
36
|
/** Router transport-specific callbacks */
|
|
36
37
|
onStatusChange?: () => void;
|
|
37
38
|
onPostChatCommand?: () => void;
|
|
39
|
+
sessionHostControl?: SessionHostControlPlane | null;
|
|
38
40
|
getCdpLogFn?: (ideType: string) => (msg: string) => void;
|
|
39
41
|
/** Additional callback after CDP manager created (transport-specific extras) */
|
|
40
42
|
onCdpManagerSetup?: (ideType: string, manager: DaemonCdpManager, managerKey: string) => void | Promise<void>;
|
|
@@ -20,6 +20,9 @@ export interface PtyRuntimeMetadata {
|
|
|
20
20
|
workspaceLabel?: string;
|
|
21
21
|
writeOwner?: PtyRuntimeWriteOwner | null;
|
|
22
22
|
attachedClients?: PtyRuntimeClientInfo[];
|
|
23
|
+
restoredFromStorage?: boolean;
|
|
24
|
+
recoveryState?: string | null;
|
|
25
|
+
recoveryError?: string | null;
|
|
23
26
|
}
|
|
24
27
|
export interface PtyRuntimeTransport {
|
|
25
28
|
readonly pid: number;
|
|
@@ -14,6 +14,28 @@ import { DaemonCliManager } from './cli-manager.js';
|
|
|
14
14
|
import type { ProviderLoader } from '../providers/provider-loader.js';
|
|
15
15
|
import type { ProviderInstanceManager } from '../providers/provider-instance-manager.js';
|
|
16
16
|
import { SessionRegistry } from '../sessions/registry.js';
|
|
17
|
+
export interface SessionHostControlPlane {
|
|
18
|
+
getDiagnostics(payload?: {
|
|
19
|
+
includeSessions?: boolean;
|
|
20
|
+
limit?: number;
|
|
21
|
+
}): Promise<any>;
|
|
22
|
+
listSessions(): Promise<any[]>;
|
|
23
|
+
stopSession(sessionId: string): Promise<any>;
|
|
24
|
+
resumeSession(sessionId: string): Promise<any>;
|
|
25
|
+
restartSession(sessionId: string): Promise<any>;
|
|
26
|
+
sendSignal(sessionId: string, signal: string): Promise<any>;
|
|
27
|
+
forceDetachClient(sessionId: string, clientId: string): Promise<any>;
|
|
28
|
+
acquireWrite(payload: {
|
|
29
|
+
sessionId: string;
|
|
30
|
+
clientId: string;
|
|
31
|
+
ownerType: 'agent' | 'user';
|
|
32
|
+
force?: boolean;
|
|
33
|
+
}): Promise<any>;
|
|
34
|
+
releaseWrite(payload: {
|
|
35
|
+
sessionId: string;
|
|
36
|
+
clientId: string;
|
|
37
|
+
}): Promise<any>;
|
|
38
|
+
}
|
|
17
39
|
export interface CommandRouterDeps {
|
|
18
40
|
commandHandler: DaemonCommandHandler;
|
|
19
41
|
cliManager: DaemonCliManager;
|
|
@@ -37,6 +59,8 @@ export interface CommandRouterDeps {
|
|
|
37
59
|
getCdpLogFn?: (ideType: string) => (msg: string) => void;
|
|
38
60
|
/** Package name for upgrade detection ('adhdev' or '@adhdev/daemon-standalone') */
|
|
39
61
|
packageName?: string;
|
|
62
|
+
/** Session host control plane */
|
|
63
|
+
sessionHostControl?: SessionHostControlPlane | null;
|
|
40
64
|
}
|
|
41
65
|
export interface CommandRouterResult {
|
|
42
66
|
success: boolean;
|
package/dist/index.js
CHANGED
|
@@ -5201,6 +5201,7 @@ var ExtensionProviderInstance = class {
|
|
|
5201
5201
|
currentStatus = "idle";
|
|
5202
5202
|
agentStreams = [];
|
|
5203
5203
|
messages = [];
|
|
5204
|
+
prevMessageHashes = /* @__PURE__ */ new Map();
|
|
5204
5205
|
activeModal = null;
|
|
5205
5206
|
currentModel = "";
|
|
5206
5207
|
currentMode = "";
|
|
@@ -5266,7 +5267,7 @@ var ExtensionProviderInstance = class {
|
|
|
5266
5267
|
onEvent(event, data) {
|
|
5267
5268
|
if (event === "stream_update") {
|
|
5268
5269
|
if (data?.streams) this.agentStreams = data.streams;
|
|
5269
|
-
if (data?.messages) this.messages = data.messages;
|
|
5270
|
+
if (data?.messages) this.messages = this.assignReceivedAt(data.messages);
|
|
5270
5271
|
if (data?.activeModal !== void 0) this.activeModal = data.activeModal;
|
|
5271
5272
|
if (data?.model) this.currentModel = data.model;
|
|
5272
5273
|
if (data?.mode) this.currentMode = data.mode;
|
|
@@ -5292,6 +5293,7 @@ var ExtensionProviderInstance = class {
|
|
|
5292
5293
|
dispose() {
|
|
5293
5294
|
this.agentStreams = [];
|
|
5294
5295
|
this.messages = [];
|
|
5296
|
+
this.prevMessageHashes.clear();
|
|
5295
5297
|
this.monitor.reset();
|
|
5296
5298
|
this.appliedEffectKeys.clear();
|
|
5297
5299
|
this.runtimeMessages = [];
|
|
@@ -5447,6 +5449,23 @@ var ExtensionProviderInstance = class {
|
|
|
5447
5449
|
this.chatId || this.instanceId
|
|
5448
5450
|
);
|
|
5449
5451
|
}
|
|
5452
|
+
/**
|
|
5453
|
+
* Assign stable receivedAt to extension messages.
|
|
5454
|
+
* Same pattern as IdeProviderInstance.readChat() prevByHash —
|
|
5455
|
+
* preserves first-seen timestamp across polling cycles.
|
|
5456
|
+
*/
|
|
5457
|
+
assignReceivedAt(messages) {
|
|
5458
|
+
const now = Date.now();
|
|
5459
|
+
const nextHashes = /* @__PURE__ */ new Map();
|
|
5460
|
+
for (const msg of messages) {
|
|
5461
|
+
const hash = `${msg.role}:${(msg.content || "").slice(0, 100)}`;
|
|
5462
|
+
const prevTime = this.prevMessageHashes.get(hash);
|
|
5463
|
+
msg.receivedAt = prevTime || now;
|
|
5464
|
+
nextHashes.set(hash, msg.receivedAt);
|
|
5465
|
+
}
|
|
5466
|
+
this.prevMessageHashes = nextHashes;
|
|
5467
|
+
return messages;
|
|
5468
|
+
}
|
|
5450
5469
|
mergeConversationMessages(messages) {
|
|
5451
5470
|
if (this.runtimeMessages.length === 0) return messages;
|
|
5452
5471
|
return [...messages, ...this.runtimeMessages.map((entry) => entry.message)].map((message, index) => ({ message, index })).sort((a, b) => {
|
|
@@ -5503,6 +5522,7 @@ ${effect.notification.body || ""}`.trim();
|
|
|
5503
5522
|
}
|
|
5504
5523
|
this.agentStreams = [];
|
|
5505
5524
|
this.messages = [];
|
|
5525
|
+
this.prevMessageHashes.clear();
|
|
5506
5526
|
this.activeModal = null;
|
|
5507
5527
|
this.currentModel = "";
|
|
5508
5528
|
this.currentMode = "";
|
|
@@ -6488,16 +6508,28 @@ function trimMessageForStatus(message, stringLimit) {
|
|
|
6488
6508
|
if (!message || typeof message !== "object") return message;
|
|
6489
6509
|
return trimStructuredStrings(message, stringLimit);
|
|
6490
6510
|
}
|
|
6511
|
+
function normalizeMessageTime(message) {
|
|
6512
|
+
if (!message || typeof message !== "object") return message;
|
|
6513
|
+
const msg = message;
|
|
6514
|
+
if (msg.receivedAt == null) {
|
|
6515
|
+
const fallback = msg.timestamp ?? msg.createdAt;
|
|
6516
|
+
if (fallback != null) {
|
|
6517
|
+
const ts2 = typeof fallback === "string" ? Date.parse(fallback) : Number(fallback);
|
|
6518
|
+
if (Number.isFinite(ts2) && ts2 > 0) msg.receivedAt = ts2;
|
|
6519
|
+
}
|
|
6520
|
+
}
|
|
6521
|
+
return msg;
|
|
6522
|
+
}
|
|
6491
6523
|
function trimMessagesForStatus(messages) {
|
|
6492
6524
|
if (!Array.isArray(messages) || messages.length === 0) return [];
|
|
6493
6525
|
const recent = messages.slice(-STATUS_ACTIVE_CHAT_MESSAGE_LIMIT);
|
|
6494
6526
|
const kept = [];
|
|
6495
6527
|
let totalBytes = 0;
|
|
6496
6528
|
for (let i = recent.length - 1; i >= 0; i -= 1) {
|
|
6497
|
-
let normalized = trimMessageForStatus(recent[i], STATUS_ACTIVE_CHAT_STRING_LIMIT);
|
|
6529
|
+
let normalized = normalizeMessageTime(trimMessageForStatus(recent[i], STATUS_ACTIVE_CHAT_STRING_LIMIT));
|
|
6498
6530
|
let size = estimateBytes(normalized);
|
|
6499
6531
|
if (size > STATUS_ACTIVE_CHAT_TOTAL_BYTES_LIMIT) {
|
|
6500
|
-
normalized = trimMessageForStatus(recent[i], STATUS_ACTIVE_CHAT_FALLBACK_STRING_LIMIT);
|
|
6532
|
+
normalized = normalizeMessageTime(trimMessageForStatus(recent[i], STATUS_ACTIVE_CHAT_FALLBACK_STRING_LIMIT));
|
|
6501
6533
|
size = estimateBytes(normalized);
|
|
6502
6534
|
}
|
|
6503
6535
|
if (kept.length > 0 && totalBytes + size > STATUS_ACTIVE_CHAT_TOTAL_BYTES_LIMIT) {
|
|
@@ -6809,6 +6841,51 @@ function buildSessionEntries(allStates, cdpManagers) {
|
|
|
6809
6841
|
return sessions;
|
|
6810
6842
|
}
|
|
6811
6843
|
|
|
6844
|
+
// src/sessions/reconcile.ts
|
|
6845
|
+
function upsertSessionTarget(sessionRegistry, target) {
|
|
6846
|
+
const existing = sessionRegistry.get(target.sessionId);
|
|
6847
|
+
if (existing && existing.parentSessionId === target.parentSessionId && existing.providerType === target.providerType && existing.transport === target.transport && existing.cdpManagerKey === target.cdpManagerKey && existing.instanceKey === target.instanceKey) {
|
|
6848
|
+
return;
|
|
6849
|
+
}
|
|
6850
|
+
sessionRegistry.register(target);
|
|
6851
|
+
}
|
|
6852
|
+
function reconcileIdeRuntimeSessions(instanceManager, sessionRegistry) {
|
|
6853
|
+
if (!instanceManager || !sessionRegistry) return;
|
|
6854
|
+
for (const instanceKey of instanceManager.listInstanceIds()) {
|
|
6855
|
+
if (!instanceKey.startsWith("ide:")) continue;
|
|
6856
|
+
const ideInstance = instanceManager.getInstance(instanceKey);
|
|
6857
|
+
if (!ideInstance || ideInstance.category !== "ide" || typeof ideInstance.getInstanceId !== "function") {
|
|
6858
|
+
continue;
|
|
6859
|
+
}
|
|
6860
|
+
const managerKey = instanceKey.slice(4);
|
|
6861
|
+
const ideType = typeof ideInstance.type === "string" && ideInstance.type.trim() ? ideInstance.type.trim() : managerKey.split("_")[0];
|
|
6862
|
+
const parentSessionId = ideInstance.getInstanceId();
|
|
6863
|
+
if (!parentSessionId) continue;
|
|
6864
|
+
upsertSessionTarget(sessionRegistry, {
|
|
6865
|
+
sessionId: parentSessionId,
|
|
6866
|
+
parentSessionId: null,
|
|
6867
|
+
providerType: ideType,
|
|
6868
|
+
transport: "cdp-page",
|
|
6869
|
+
cdpManagerKey: managerKey,
|
|
6870
|
+
instanceKey
|
|
6871
|
+
});
|
|
6872
|
+
const extensions = ideInstance.getExtensionInstances?.() || [];
|
|
6873
|
+
for (const ext of extensions) {
|
|
6874
|
+
const extType = typeof ext?.type === "string" ? ext.type.trim() : "";
|
|
6875
|
+
const extSessionId = ext?.getInstanceId?.();
|
|
6876
|
+
if (!extType || !extSessionId) continue;
|
|
6877
|
+
upsertSessionTarget(sessionRegistry, {
|
|
6878
|
+
sessionId: extSessionId,
|
|
6879
|
+
parentSessionId,
|
|
6880
|
+
providerType: extType,
|
|
6881
|
+
transport: "cdp-webview",
|
|
6882
|
+
cdpManagerKey: managerKey,
|
|
6883
|
+
instanceKey
|
|
6884
|
+
});
|
|
6885
|
+
}
|
|
6886
|
+
}
|
|
6887
|
+
}
|
|
6888
|
+
|
|
6812
6889
|
// src/commands/handler.ts
|
|
6813
6890
|
init_logger();
|
|
6814
6891
|
|
|
@@ -7075,7 +7152,7 @@ async function handleSendChat(h, args) {
|
|
|
7075
7152
|
if (isExtensionTransport(transport)) {
|
|
7076
7153
|
_log(`Extension: ${provider?.type || "unknown_extension"}`);
|
|
7077
7154
|
try {
|
|
7078
|
-
const evalResult = await h.evaluateProviderScript("sendMessage", {
|
|
7155
|
+
const evalResult = await h.evaluateProviderScript("sendMessage", { message: text }, 3e4);
|
|
7079
7156
|
if (evalResult?.result) {
|
|
7080
7157
|
const parsed = parseMaybeJson(evalResult.result);
|
|
7081
7158
|
if (didProviderConfirmSend(parsed)) {
|
|
@@ -7106,7 +7183,7 @@ async function handleSendChat(h, args) {
|
|
|
7106
7183
|
return { success: false, error: `CDP for ${managerKey || "unknown"} not connected` };
|
|
7107
7184
|
}
|
|
7108
7185
|
_log(`Targeting IDE: ${getCurrentManagerKey(h)}`);
|
|
7109
|
-
const sendScript = h.getProviderScript("sendMessage", {
|
|
7186
|
+
const sendScript = h.getProviderScript("sendMessage", { message: text });
|
|
7110
7187
|
if (sendScript) {
|
|
7111
7188
|
try {
|
|
7112
7189
|
const result = await targetCdp.evaluate(sendScript, 3e4);
|
|
@@ -8508,9 +8585,26 @@ var DaemonCommandHandler = class {
|
|
|
8508
8585
|
if (provider?.scripts) {
|
|
8509
8586
|
const fn = provider.scripts[scriptName];
|
|
8510
8587
|
if (typeof fn === "function") {
|
|
8511
|
-
|
|
8512
|
-
|
|
8513
|
-
|
|
8588
|
+
if (params && Object.keys(params).length > 0) {
|
|
8589
|
+
const firstVal = Object.values(params)[0];
|
|
8590
|
+
if (scriptName === "sendMessage" && typeof firstVal === "string") {
|
|
8591
|
+
const legacyScript = fn(firstVal);
|
|
8592
|
+
if (legacyScript) return legacyScript;
|
|
8593
|
+
}
|
|
8594
|
+
const script = fn(params);
|
|
8595
|
+
if (script) {
|
|
8596
|
+
const likelyLegacyObjectLeak = typeof script === "string" && script.includes("[object Object]") && typeof firstVal === "string";
|
|
8597
|
+
if (!likelyLegacyObjectLeak) return script;
|
|
8598
|
+
}
|
|
8599
|
+
if (firstVal !== void 0) {
|
|
8600
|
+
const legacyScript = fn(firstVal);
|
|
8601
|
+
if (legacyScript) return legacyScript;
|
|
8602
|
+
}
|
|
8603
|
+
if (script) return script;
|
|
8604
|
+
} else {
|
|
8605
|
+
const script = fn();
|
|
8606
|
+
if (script) return script;
|
|
8607
|
+
}
|
|
8514
8608
|
}
|
|
8515
8609
|
}
|
|
8516
8610
|
return null;
|
|
@@ -8566,17 +8660,27 @@ var DaemonCommandHandler = class {
|
|
|
8566
8660
|
return key.split("_")[0];
|
|
8567
8661
|
}
|
|
8568
8662
|
resolveRoute(args) {
|
|
8569
|
-
const
|
|
8570
|
-
|
|
8571
|
-
|
|
8572
|
-
|
|
8663
|
+
const targetSessionId = typeof args?.targetSessionId === "string" ? args.targetSessionId.trim() : "";
|
|
8664
|
+
let session = targetSessionId ? this._ctx.sessionRegistry?.get(targetSessionId) : void 0;
|
|
8665
|
+
if (targetSessionId && !session) {
|
|
8666
|
+
reconcileIdeRuntimeSessions(this._ctx.instanceManager, this._ctx.sessionRegistry);
|
|
8667
|
+
session = this._ctx.sessionRegistry?.get(targetSessionId);
|
|
8668
|
+
}
|
|
8669
|
+
const sessionLookupFailed = !!targetSessionId && !session;
|
|
8670
|
+
const managerKey = this.extractIdeType(args, sessionLookupFailed);
|
|
8671
|
+
let providerType;
|
|
8672
|
+
if (!sessionLookupFailed) {
|
|
8673
|
+
providerType = session?.providerType || args?.agentType || args?.providerType || this.inferProviderType(managerKey);
|
|
8674
|
+
}
|
|
8675
|
+
return { session, managerKey, providerType, sessionLookupFailed };
|
|
8573
8676
|
}
|
|
8574
8677
|
/** Extract CDP scope key from target session or explicit ideType */
|
|
8575
|
-
extractIdeType(args) {
|
|
8678
|
+
extractIdeType(args, sessionLookupFailed = false) {
|
|
8576
8679
|
if (args?.targetSessionId) {
|
|
8577
8680
|
const target = this._ctx.sessionRegistry?.get(args.targetSessionId);
|
|
8578
8681
|
if (target?.cdpManagerKey) return target.cdpManagerKey;
|
|
8579
8682
|
if (this._ctx.cdpManagers.has(args.targetSessionId)) return args.targetSessionId;
|
|
8683
|
+
if (sessionLookupFailed) return void 0;
|
|
8580
8684
|
}
|
|
8581
8685
|
if (args?.ideType) {
|
|
8582
8686
|
const target = this._ctx.sessionRegistry?.get(args.ideType);
|
|
@@ -8623,6 +8727,33 @@ var DaemonCommandHandler = class {
|
|
|
8623
8727
|
this._currentRoute = this.resolveRoute(args);
|
|
8624
8728
|
const startedAt = Date.now();
|
|
8625
8729
|
this.logCommandStart(cmd, args);
|
|
8730
|
+
const sessionScopedCommands = /* @__PURE__ */ new Set([
|
|
8731
|
+
"read_chat",
|
|
8732
|
+
"send_chat",
|
|
8733
|
+
"list_chats",
|
|
8734
|
+
"new_chat",
|
|
8735
|
+
"switch_chat",
|
|
8736
|
+
"set_mode",
|
|
8737
|
+
"change_model",
|
|
8738
|
+
"set_thought_level",
|
|
8739
|
+
"resolve_action",
|
|
8740
|
+
"focus_session",
|
|
8741
|
+
"pty_input",
|
|
8742
|
+
"pty_resize",
|
|
8743
|
+
"invoke_provider_script",
|
|
8744
|
+
"list_extension_models",
|
|
8745
|
+
"set_extension_model",
|
|
8746
|
+
"list_extension_modes",
|
|
8747
|
+
"set_extension_mode"
|
|
8748
|
+
]);
|
|
8749
|
+
if (this._currentRoute.sessionLookupFailed && sessionScopedCommands.has(cmd)) {
|
|
8750
|
+
const result2 = {
|
|
8751
|
+
success: false,
|
|
8752
|
+
error: `Live session not found for targetSessionId: ${String(args?.targetSessionId || "").trim() || "unknown"}`
|
|
8753
|
+
};
|
|
8754
|
+
this.logCommandEnd(cmd, result2, startedAt);
|
|
8755
|
+
return result2;
|
|
8756
|
+
}
|
|
8626
8757
|
let result;
|
|
8627
8758
|
if (!this._currentRoute.session && !this._currentRoute.managerKey && !this._currentRoute.providerType) {
|
|
8628
8759
|
const cdpCommands = ["send_chat", "read_chat", "list_chats", "new_chat", "switch_chat", "set_mode", "change_model", "set_thought_level", "resolve_action"];
|
|
@@ -8934,6 +9065,7 @@ var CliProviderInstance = class {
|
|
|
8934
9065
|
this.detectStatusTransition();
|
|
8935
9066
|
});
|
|
8936
9067
|
await this.adapter.spawn();
|
|
9068
|
+
this.maybeAppendRuntimeRecoveryMessage(this.adapter.getRuntimeMetadata());
|
|
8937
9069
|
if (this.providerSessionId) {
|
|
8938
9070
|
const restoredHistory = readChatHistory(this.type, 0, 200, this.providerSessionId);
|
|
8939
9071
|
if (restoredHistory.messages.length > 0) {
|
|
@@ -9028,6 +9160,7 @@ var CliProviderInstance = class {
|
|
|
9028
9160
|
this.promoteProviderSessionId(parsedProviderSessionId);
|
|
9029
9161
|
}
|
|
9030
9162
|
const runtime = this.adapter.getRuntimeMetadata();
|
|
9163
|
+
this.maybeAppendRuntimeRecoveryMessage(runtime);
|
|
9031
9164
|
const parsedMessages = Array.isArray(parsedStatus?.messages) ? parsedStatus.messages : [];
|
|
9032
9165
|
const controlValues = extractProviderControlValues(this.provider.controls, parsedStatus);
|
|
9033
9166
|
if (controlValues) {
|
|
@@ -9354,6 +9487,28 @@ ${effect.notification.body || ""}`.trim();
|
|
|
9354
9487
|
const pad = (value) => String(value).padStart(2, "0");
|
|
9355
9488
|
return `${date.getFullYear()}-${pad(date.getMonth() + 1)}-${pad(date.getDate())} ${pad(date.getHours())}:${pad(date.getMinutes())}:${pad(date.getSeconds())}`;
|
|
9356
9489
|
}
|
|
9490
|
+
maybeAppendRuntimeRecoveryMessage(runtime) {
|
|
9491
|
+
if (!runtime?.restoredFromStorage || !runtime.runtimeId) return;
|
|
9492
|
+
const recoveryState = String(runtime.recoveryState || "").trim();
|
|
9493
|
+
if (!recoveryState) return;
|
|
9494
|
+
let content = "";
|
|
9495
|
+
if (recoveryState === "auto_resumed") {
|
|
9496
|
+
content = "Session host restored this CLI after restart and reattached it from a saved snapshot.";
|
|
9497
|
+
} else if (recoveryState === "resume_failed") {
|
|
9498
|
+
const errorSuffix = runtime.recoveryError ? ` Resume failed: ${runtime.recoveryError}` : "";
|
|
9499
|
+
content = `Session host found this CLI after restart, but automatic resume failed.${errorSuffix}`;
|
|
9500
|
+
} else if (recoveryState === "host_restart_interrupted") {
|
|
9501
|
+
content = "Session host found this CLI in interrupted state after restart and is attempting to resume it.";
|
|
9502
|
+
} else if (recoveryState === "orphan_snapshot") {
|
|
9503
|
+
content = "Session host restored the last snapshot for this CLI, but the original runtime was not resumed automatically.";
|
|
9504
|
+
} else {
|
|
9505
|
+
content = `Session host restored this CLI after restart (${recoveryState}).`;
|
|
9506
|
+
}
|
|
9507
|
+
this.appendRuntimeSystemMessage(
|
|
9508
|
+
content,
|
|
9509
|
+
`runtime_recovery:${runtime.runtimeId}:${recoveryState}`
|
|
9510
|
+
);
|
|
9511
|
+
}
|
|
9357
9512
|
appendRuntimeSystemMessage(content, dedupKey, receivedAt = Date.now()) {
|
|
9358
9513
|
const normalizedContent = String(content || "").trim();
|
|
9359
9514
|
if (!normalizedContent) return;
|
|
@@ -12642,17 +12797,17 @@ function parseMessageTime(value) {
|
|
|
12642
12797
|
function getSessionMessageUpdatedAt(session) {
|
|
12643
12798
|
const lastMessage = session.activeChat?.messages?.at?.(-1);
|
|
12644
12799
|
if (!lastMessage) return 0;
|
|
12645
|
-
return parseMessageTime(lastMessage.
|
|
12800
|
+
return parseMessageTime(lastMessage.receivedAt) || 0;
|
|
12646
12801
|
}
|
|
12647
12802
|
function getSessionCompletionMarker(session) {
|
|
12648
12803
|
const lastMessage = session.activeChat?.messages?.at?.(-1);
|
|
12649
12804
|
if (!lastMessage) return "";
|
|
12650
12805
|
const role = typeof lastMessage.role === "string" ? lastMessage.role : "";
|
|
12651
|
-
if (role === "user" || role === "human") return "";
|
|
12806
|
+
if (role === "user" || role === "human" || role === "system") return "";
|
|
12652
12807
|
if (typeof lastMessage._turnKey === "string" && lastMessage._turnKey) return `turn:${lastMessage._turnKey}`;
|
|
12653
12808
|
if (typeof lastMessage.id === "string" && lastMessage.id) return `id:${lastMessage.id}`;
|
|
12654
12809
|
if (typeof lastMessage.index === "number" && Number.isFinite(lastMessage.index)) return `idx:${lastMessage.index}`;
|
|
12655
|
-
const timestamp = parseMessageTime(lastMessage.
|
|
12810
|
+
const timestamp = parseMessageTime(lastMessage.receivedAt);
|
|
12656
12811
|
return timestamp > 0 ? `ts:${timestamp}` : "";
|
|
12657
12812
|
}
|
|
12658
12813
|
function getSessionLastUsedAt(session) {
|
|
@@ -12669,7 +12824,7 @@ function getUnreadState(hasContentChange, status, lastUsedAt, lastSeenAt, lastRo
|
|
|
12669
12824
|
if (status === "generating" || status === "starting") {
|
|
12670
12825
|
return { unread: false, inboxBucket: "working" };
|
|
12671
12826
|
}
|
|
12672
|
-
const unread = completionMarker ? completionMarker !== seenCompletionMarker : hasContentChange && lastUsedAt > lastSeenAt && lastRole !== "user" && lastRole !== "human";
|
|
12827
|
+
const unread = completionMarker ? completionMarker !== seenCompletionMarker : hasContentChange && lastUsedAt > lastSeenAt && lastRole !== "user" && lastRole !== "human" && lastRole !== "system";
|
|
12673
12828
|
return { unread, inboxBucket: unread ? "task_complete" : "idle" };
|
|
12674
12829
|
}
|
|
12675
12830
|
function buildRecentLaunches(recentActivity) {
|
|
@@ -12954,6 +13109,25 @@ var CHAT_COMMANDS = [
|
|
|
12954
13109
|
"change_model"
|
|
12955
13110
|
];
|
|
12956
13111
|
var READ_DEBUG_ENABLED2 = process.argv.includes("--dev") || process.env.ADHDEV_READ_DEBUG === "1";
|
|
13112
|
+
function toHostedCliRuntimeDescriptor(record) {
|
|
13113
|
+
if (!record || typeof record !== "object") return null;
|
|
13114
|
+
const runtimeId = typeof record.sessionId === "string" ? record.sessionId : "";
|
|
13115
|
+
const cliType = typeof record.providerType === "string" ? record.providerType : "";
|
|
13116
|
+
const workspace = typeof record.workspace === "string" ? record.workspace : "";
|
|
13117
|
+
if (!runtimeId || !cliType || !workspace) return null;
|
|
13118
|
+
return {
|
|
13119
|
+
runtimeId,
|
|
13120
|
+
runtimeKey: typeof record.runtimeKey === "string" ? record.runtimeKey : void 0,
|
|
13121
|
+
displayName: typeof record.displayName === "string" ? record.displayName : void 0,
|
|
13122
|
+
workspaceLabel: typeof record.workspaceLabel === "string" ? record.workspaceLabel : void 0,
|
|
13123
|
+
lifecycle: typeof record.lifecycle === "string" ? record.lifecycle : void 0,
|
|
13124
|
+
recoveryState: typeof record.meta?.runtimeRecoveryState === "string" ? String(record.meta.runtimeRecoveryState) : null,
|
|
13125
|
+
cliType,
|
|
13126
|
+
workspace,
|
|
13127
|
+
cliArgs: Array.isArray(record.meta?.cliArgs) ? record.meta.cliArgs : [],
|
|
13128
|
+
providerSessionId: typeof record.meta?.providerSessionId === "string" ? String(record.meta.providerSessionId) : void 0
|
|
13129
|
+
};
|
|
13130
|
+
}
|
|
12957
13131
|
var DaemonCommandRouter = class {
|
|
12958
13132
|
deps;
|
|
12959
13133
|
constructor(deps) {
|
|
@@ -13027,6 +13201,90 @@ var DaemonCommandRouter = class {
|
|
|
13027
13201
|
return { success: false, error: e.message };
|
|
13028
13202
|
}
|
|
13029
13203
|
}
|
|
13204
|
+
case "session_host_get_diagnostics": {
|
|
13205
|
+
if (!this.deps.sessionHostControl) return { success: false, error: "Session host control unavailable" };
|
|
13206
|
+
const diagnostics = await this.deps.sessionHostControl.getDiagnostics({
|
|
13207
|
+
includeSessions: args?.includeSessions !== false,
|
|
13208
|
+
limit: Number(args?.limit) || void 0
|
|
13209
|
+
});
|
|
13210
|
+
return { success: true, diagnostics };
|
|
13211
|
+
}
|
|
13212
|
+
case "session_host_list_sessions": {
|
|
13213
|
+
if (!this.deps.sessionHostControl) return { success: false, error: "Session host control unavailable" };
|
|
13214
|
+
const sessions = await this.deps.sessionHostControl.listSessions();
|
|
13215
|
+
return { success: true, sessions };
|
|
13216
|
+
}
|
|
13217
|
+
case "session_host_stop_session": {
|
|
13218
|
+
if (!this.deps.sessionHostControl) return { success: false, error: "Session host control unavailable" };
|
|
13219
|
+
const sessionId = typeof args?.sessionId === "string" ? args.sessionId : "";
|
|
13220
|
+
if (!sessionId) return { success: false, error: "sessionId required" };
|
|
13221
|
+
const record = await this.deps.sessionHostControl.stopSession(sessionId);
|
|
13222
|
+
return { success: true, record };
|
|
13223
|
+
}
|
|
13224
|
+
case "session_host_resume_session": {
|
|
13225
|
+
if (!this.deps.sessionHostControl) return { success: false, error: "Session host control unavailable" };
|
|
13226
|
+
const sessionId = typeof args?.sessionId === "string" ? args.sessionId : "";
|
|
13227
|
+
if (!sessionId) return { success: false, error: "sessionId required" };
|
|
13228
|
+
const record = await this.deps.sessionHostControl.resumeSession(sessionId);
|
|
13229
|
+
const hosted = toHostedCliRuntimeDescriptor(record);
|
|
13230
|
+
if (hosted) {
|
|
13231
|
+
await this.deps.cliManager.restoreHostedSessions([hosted]);
|
|
13232
|
+
}
|
|
13233
|
+
return { success: true, record };
|
|
13234
|
+
}
|
|
13235
|
+
case "session_host_restart_session": {
|
|
13236
|
+
if (!this.deps.sessionHostControl) return { success: false, error: "Session host control unavailable" };
|
|
13237
|
+
const sessionId = typeof args?.sessionId === "string" ? args.sessionId : "";
|
|
13238
|
+
if (!sessionId) return { success: false, error: "sessionId required" };
|
|
13239
|
+
const record = await this.deps.sessionHostControl.restartSession(sessionId);
|
|
13240
|
+
const hosted = toHostedCliRuntimeDescriptor(record);
|
|
13241
|
+
if (hosted) {
|
|
13242
|
+
await this.deps.cliManager.restoreHostedSessions([hosted]);
|
|
13243
|
+
}
|
|
13244
|
+
return { success: true, record };
|
|
13245
|
+
}
|
|
13246
|
+
case "session_host_send_signal": {
|
|
13247
|
+
if (!this.deps.sessionHostControl) return { success: false, error: "Session host control unavailable" };
|
|
13248
|
+
const sessionId = typeof args?.sessionId === "string" ? args.sessionId : "";
|
|
13249
|
+
const signal = typeof args?.signal === "string" ? args.signal : "";
|
|
13250
|
+
if (!sessionId) return { success: false, error: "sessionId required" };
|
|
13251
|
+
if (!signal) return { success: false, error: "signal required" };
|
|
13252
|
+
const record = await this.deps.sessionHostControl.sendSignal(sessionId, signal);
|
|
13253
|
+
return { success: true, record };
|
|
13254
|
+
}
|
|
13255
|
+
case "session_host_force_detach_client": {
|
|
13256
|
+
if (!this.deps.sessionHostControl) return { success: false, error: "Session host control unavailable" };
|
|
13257
|
+
const sessionId = typeof args?.sessionId === "string" ? args.sessionId : "";
|
|
13258
|
+
const clientId = typeof args?.clientId === "string" ? args.clientId : "";
|
|
13259
|
+
if (!sessionId) return { success: false, error: "sessionId required" };
|
|
13260
|
+
if (!clientId) return { success: false, error: "clientId required" };
|
|
13261
|
+
const record = await this.deps.sessionHostControl.forceDetachClient(sessionId, clientId);
|
|
13262
|
+
return { success: true, record };
|
|
13263
|
+
}
|
|
13264
|
+
case "session_host_acquire_write": {
|
|
13265
|
+
if (!this.deps.sessionHostControl) return { success: false, error: "Session host control unavailable" };
|
|
13266
|
+
const sessionId = typeof args?.sessionId === "string" ? args.sessionId : "";
|
|
13267
|
+
const clientId = typeof args?.clientId === "string" ? args.clientId : "";
|
|
13268
|
+
const ownerType = args?.ownerType === "agent" ? "agent" : "user";
|
|
13269
|
+
if (!sessionId) return { success: false, error: "sessionId required" };
|
|
13270
|
+
if (!clientId) return { success: false, error: "clientId required" };
|
|
13271
|
+
const record = await this.deps.sessionHostControl.acquireWrite({
|
|
13272
|
+
sessionId,
|
|
13273
|
+
clientId,
|
|
13274
|
+
ownerType,
|
|
13275
|
+
force: args?.force !== false
|
|
13276
|
+
});
|
|
13277
|
+
return { success: true, record };
|
|
13278
|
+
}
|
|
13279
|
+
case "session_host_release_write": {
|
|
13280
|
+
if (!this.deps.sessionHostControl) return { success: false, error: "Session host control unavailable" };
|
|
13281
|
+
const sessionId = typeof args?.sessionId === "string" ? args.sessionId : "";
|
|
13282
|
+
const clientId = typeof args?.clientId === "string" ? args.clientId : "";
|
|
13283
|
+
if (!sessionId) return { success: false, error: "sessionId required" };
|
|
13284
|
+
if (!clientId) return { success: false, error: "clientId required" };
|
|
13285
|
+
const record = await this.deps.sessionHostControl.releaseWrite({ sessionId, clientId });
|
|
13286
|
+
return { success: true, record };
|
|
13287
|
+
}
|
|
13030
13288
|
case "list_saved_sessions": {
|
|
13031
13289
|
const providerType = typeof args?.providerType === "string" ? args.providerType.trim() : typeof args?.agentType === "string" ? args.agentType.trim() : "";
|
|
13032
13290
|
const kind = args?.kind === "acp" ? "acp" : "cli";
|
|
@@ -13555,6 +13813,14 @@ var ProviderStreamAdapter = class {
|
|
|
13555
13813
|
hasScript(name) {
|
|
13556
13814
|
return typeof this.provider.scripts?.[name] === "function";
|
|
13557
13815
|
}
|
|
13816
|
+
parseMaybeJson(raw) {
|
|
13817
|
+
if (typeof raw !== "string") return raw;
|
|
13818
|
+
try {
|
|
13819
|
+
return JSON.parse(raw);
|
|
13820
|
+
} catch {
|
|
13821
|
+
return raw;
|
|
13822
|
+
}
|
|
13823
|
+
}
|
|
13558
13824
|
summarizeRaw(raw) {
|
|
13559
13825
|
try {
|
|
13560
13826
|
if (typeof raw === "string") return raw.replace(/\s+/g, " ").trim().slice(0, 240);
|
|
@@ -13615,12 +13881,30 @@ var ProviderStreamAdapter = class {
|
|
|
13615
13881
|
}
|
|
13616
13882
|
}
|
|
13617
13883
|
async sendMessage(evaluate, text) {
|
|
13618
|
-
const
|
|
13884
|
+
const params = { message: text };
|
|
13885
|
+
const script = this.callScript("sendMessage", params) || this.callScript("sendMessage", text);
|
|
13619
13886
|
if (!script) throw new Error(`[${this.agentName}] sendMessage script not available`);
|
|
13620
13887
|
const result = await evaluate(script);
|
|
13621
13888
|
if (result && typeof result === "string" && result.startsWith("error:")) {
|
|
13622
13889
|
throw new Error(`[${this.agentName}] sendMessage failed: ${result}`);
|
|
13623
13890
|
}
|
|
13891
|
+
const parsed = this.parseMaybeJson(result);
|
|
13892
|
+
if (parsed === true) return;
|
|
13893
|
+
if (typeof parsed === "string") {
|
|
13894
|
+
const normalized = parsed.trim().toLowerCase();
|
|
13895
|
+
if (normalized === "ok" || normalized === "sent" || normalized === "success" || normalized === "true") {
|
|
13896
|
+
return;
|
|
13897
|
+
}
|
|
13898
|
+
}
|
|
13899
|
+
if (parsed && typeof parsed === "object") {
|
|
13900
|
+
if (parsed.sent === true || parsed.success === true || parsed.ok === true || parsed.submitted === true || parsed.dispatched === true) {
|
|
13901
|
+
return;
|
|
13902
|
+
}
|
|
13903
|
+
if (typeof parsed.error === "string" && parsed.error.trim()) {
|
|
13904
|
+
throw new Error(`[${this.agentName}] sendMessage failed: ${parsed.error}`);
|
|
13905
|
+
}
|
|
13906
|
+
}
|
|
13907
|
+
throw new Error(`[${this.agentName}] sendMessage was not confirmed`);
|
|
13624
13908
|
}
|
|
13625
13909
|
async resolveAction(evaluate, action, button) {
|
|
13626
13910
|
const script = this.callScript("resolveAction", { action, button });
|
|
@@ -14005,6 +14289,7 @@ var AgentStreamPoller = class {
|
|
|
14005
14289
|
sessionRegistry
|
|
14006
14290
|
} = this.deps;
|
|
14007
14291
|
if (!agentStreamManager || cdpManagers.size === 0) return;
|
|
14292
|
+
reconcileIdeRuntimeSessions(instanceManager, sessionRegistry);
|
|
14008
14293
|
for (const [ideType, cdp] of cdpManagers) {
|
|
14009
14294
|
registerExtensionProviders(providerLoader, cdp, ideType);
|
|
14010
14295
|
const ideInstance = instanceManager.getInstance(`ide:${ideType}`);
|
|
@@ -19870,6 +20155,7 @@ var SessionHostRuntimeTransport = class {
|
|
|
19870
20155
|
}
|
|
19871
20156
|
}
|
|
19872
20157
|
handleEvent(event) {
|
|
20158
|
+
if (!("sessionId" in event)) return;
|
|
19873
20159
|
if (event.sessionId !== this.options.runtimeId) return;
|
|
19874
20160
|
if ((event.type === "session_started" || event.type === "session_resumed") && typeof event.pid === "number") {
|
|
19875
20161
|
this.currentPid = event.pid;
|
|
@@ -19945,7 +20231,10 @@ var SessionHostRuntimeTransport = class {
|
|
|
19945
20231
|
clientId: client.clientId,
|
|
19946
20232
|
type: client.type,
|
|
19947
20233
|
readOnly: client.readOnly
|
|
19948
|
-
}))
|
|
20234
|
+
})),
|
|
20235
|
+
restoredFromStorage: record.meta?.restoredFromStorage === true,
|
|
20236
|
+
recoveryState: typeof record.meta?.runtimeRecoveryState === "string" ? String(record.meta.runtimeRecoveryState) : null,
|
|
20237
|
+
recoveryError: typeof record.meta?.runtimeRecoveryError === "string" ? String(record.meta.runtimeRecoveryError) : null
|
|
19949
20238
|
};
|
|
19950
20239
|
}
|
|
19951
20240
|
enqueue(action) {
|
|
@@ -20389,6 +20678,7 @@ async function initDaemonComponents(config) {
|
|
|
20389
20678
|
onIdeConnected: () => poller?.start(),
|
|
20390
20679
|
onStatusChange: config.onStatusChange,
|
|
20391
20680
|
onPostChatCommand: config.onPostChatCommand,
|
|
20681
|
+
sessionHostControl: config.sessionHostControl,
|
|
20392
20682
|
getCdpLogFn: config.getCdpLogFn || ((ideType) => LOG.forComponent(`CDP:${ideType}`).asLogFn())
|
|
20393
20683
|
});
|
|
20394
20684
|
poller = new AgentStreamPoller({
|