@adhdev/daemon-standalone 0.8.64 → 0.8.66

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 CHANGED
@@ -28313,6 +28313,227 @@ var require_dist2 = __commonJS({
28313
28313
  LOG_PATH = path6.join(LOG_DIR, `daemon-${getDateStr()}.log`);
28314
28314
  }
28315
28315
  });
28316
+ function normalizeInputEnvelope(input) {
28317
+ const normalized = normalizeInputEnvelopePayload(input);
28318
+ const textFallback = normalized.textFallback ?? flattenInputParts(normalized.parts);
28319
+ return {
28320
+ parts: normalized.parts,
28321
+ textFallback,
28322
+ ...normalized.metadata ? { metadata: normalized.metadata } : {}
28323
+ };
28324
+ }
28325
+ function normalizeMessageParts(content) {
28326
+ if (typeof content === "string") return [{ type: "text", text: content }];
28327
+ if (!Array.isArray(content)) {
28328
+ if (content && typeof content === "object" && typeof content.text === "string") {
28329
+ return [{ type: "text", text: String(content.text) }];
28330
+ }
28331
+ return [];
28332
+ }
28333
+ const parts = [];
28334
+ for (const raw of content) {
28335
+ if (typeof raw === "string") {
28336
+ parts.push({ type: "text", text: raw });
28337
+ continue;
28338
+ }
28339
+ if (!raw || typeof raw !== "object") continue;
28340
+ const part = normalizeMessagePartObject(raw);
28341
+ if (part) parts.push(part);
28342
+ }
28343
+ return parts;
28344
+ }
28345
+ function flattenMessageParts(parts) {
28346
+ return parts.map((part) => {
28347
+ if (part.type === "text") return part.text;
28348
+ if (part.type === "resource") return part.resource.text || "";
28349
+ return "";
28350
+ }).filter((value) => value.length > 0).join("\n");
28351
+ }
28352
+ function normalizeInputEnvelopePayload(input) {
28353
+ if (typeof input === "string") {
28354
+ return { parts: [{ type: "text", text: input }], textFallback: input };
28355
+ }
28356
+ if (!input || typeof input !== "object") {
28357
+ return { parts: [], textFallback: "" };
28358
+ }
28359
+ const record2 = input;
28360
+ const nestedInput = record2.input;
28361
+ if (nestedInput && typeof nestedInput === "object") {
28362
+ const nested = nestedInput;
28363
+ return {
28364
+ parts: normalizeInputParts(nested.parts ?? nested.prompt),
28365
+ textFallback: typeof nested.textFallback === "string" ? nested.textFallback : void 0,
28366
+ metadata: normalizeInputMetadata(nested.metadata)
28367
+ };
28368
+ }
28369
+ const directText = typeof record2.text === "string" ? record2.text : typeof record2.message === "string" ? record2.message : void 0;
28370
+ if (directText !== void 0) {
28371
+ return { parts: [{ type: "text", text: directText }], textFallback: directText };
28372
+ }
28373
+ const directParts = normalizeInputParts(record2.parts ?? record2.prompt);
28374
+ return {
28375
+ parts: directParts,
28376
+ textFallback: typeof record2.textFallback === "string" ? record2.textFallback : void 0,
28377
+ metadata: normalizeInputMetadata(record2.metadata)
28378
+ };
28379
+ }
28380
+ function normalizeInputMetadata(value) {
28381
+ if (!value || typeof value !== "object") return void 0;
28382
+ const record2 = value;
28383
+ const metadata = {};
28384
+ if (record2.source === "dashboard" || record2.source === "shortcut_api" || record2.source === "provider_script" || record2.source === "session_replay") {
28385
+ metadata.source = record2.source;
28386
+ }
28387
+ if (typeof record2.clientTimestamp === "number" && Number.isFinite(record2.clientTimestamp)) {
28388
+ metadata.clientTimestamp = record2.clientTimestamp;
28389
+ }
28390
+ return Object.keys(metadata).length > 0 ? metadata : void 0;
28391
+ }
28392
+ function normalizeInputParts(value) {
28393
+ if (!Array.isArray(value)) return [];
28394
+ const parts = [];
28395
+ for (const raw of value) {
28396
+ if (typeof raw === "string") {
28397
+ parts.push({ type: "text", text: raw });
28398
+ continue;
28399
+ }
28400
+ if (!raw || typeof raw !== "object") continue;
28401
+ const part = normalizeInputPartObject(raw);
28402
+ if (part) parts.push(part);
28403
+ }
28404
+ return parts;
28405
+ }
28406
+ function normalizeInputPartObject(raw) {
28407
+ const type = raw.type;
28408
+ if (type === "text" && typeof raw.text === "string") {
28409
+ return { type, text: raw.text };
28410
+ }
28411
+ if (type === "image" && typeof raw.mimeType === "string") {
28412
+ return {
28413
+ type,
28414
+ mimeType: raw.mimeType,
28415
+ ...typeof raw.uri === "string" ? { uri: raw.uri } : {},
28416
+ ...typeof raw.data === "string" ? { data: raw.data } : {},
28417
+ ...typeof raw.alt === "string" ? { alt: raw.alt } : {}
28418
+ };
28419
+ }
28420
+ if (type === "audio" && typeof raw.mimeType === "string") {
28421
+ return {
28422
+ type,
28423
+ mimeType: raw.mimeType,
28424
+ ...typeof raw.uri === "string" ? { uri: raw.uri } : {},
28425
+ ...typeof raw.data === "string" ? { data: raw.data } : {},
28426
+ ...typeof raw.transcript === "string" ? { transcript: raw.transcript } : {}
28427
+ };
28428
+ }
28429
+ if (type === "video" && typeof raw.mimeType === "string") {
28430
+ return {
28431
+ type,
28432
+ mimeType: raw.mimeType,
28433
+ ...typeof raw.uri === "string" ? { uri: raw.uri } : {},
28434
+ ...typeof raw.data === "string" ? { data: raw.data } : {},
28435
+ ...typeof raw.posterUri === "string" ? { posterUri: raw.posterUri } : {}
28436
+ };
28437
+ }
28438
+ if (type === "resource" && typeof raw.uri === "string") {
28439
+ return {
28440
+ type,
28441
+ uri: raw.uri,
28442
+ ...typeof raw.mimeType === "string" ? { mimeType: raw.mimeType } : {},
28443
+ ...typeof raw.name === "string" ? { name: raw.name } : {},
28444
+ ...typeof raw.text === "string" ? { text: raw.text } : {},
28445
+ ...typeof raw.data === "string" ? { data: raw.data } : {}
28446
+ };
28447
+ }
28448
+ if (type === "resource_link" && typeof raw.uri === "string") {
28449
+ return {
28450
+ type: "resource",
28451
+ uri: raw.uri,
28452
+ ...typeof raw.mimeType === "string" ? { mimeType: raw.mimeType } : {},
28453
+ ...typeof raw.name === "string" ? { name: raw.name } : {}
28454
+ };
28455
+ }
28456
+ return null;
28457
+ }
28458
+ function normalizeMessagePartObject(raw) {
28459
+ const type = raw.type;
28460
+ if (type === "text" && typeof raw.text === "string") {
28461
+ return { type, text: raw.text };
28462
+ }
28463
+ if (type === "image" && typeof raw.mimeType === "string") {
28464
+ return {
28465
+ type,
28466
+ mimeType: raw.mimeType,
28467
+ ...typeof raw.uri === "string" ? { uri: raw.uri } : {},
28468
+ ...typeof raw.data === "string" ? { data: raw.data } : {}
28469
+ };
28470
+ }
28471
+ if (type === "audio" && typeof raw.mimeType === "string") {
28472
+ return {
28473
+ type,
28474
+ mimeType: raw.mimeType,
28475
+ ...typeof raw.uri === "string" ? { uri: raw.uri } : {},
28476
+ ...typeof raw.data === "string" ? { data: raw.data } : {},
28477
+ ...typeof raw.transcript === "string" ? { transcript: raw.transcript } : {}
28478
+ };
28479
+ }
28480
+ if (type === "video" && typeof raw.mimeType === "string") {
28481
+ return {
28482
+ type,
28483
+ mimeType: raw.mimeType,
28484
+ ...typeof raw.uri === "string" ? { uri: raw.uri } : {},
28485
+ ...typeof raw.data === "string" ? { data: raw.data } : {},
28486
+ ...typeof raw.posterUri === "string" ? { posterUri: raw.posterUri } : {}
28487
+ };
28488
+ }
28489
+ if (type === "resource_link" && typeof raw.uri === "string" && typeof raw.name === "string") {
28490
+ return {
28491
+ type,
28492
+ uri: raw.uri,
28493
+ name: raw.name,
28494
+ ...typeof raw.mimeType === "string" ? { mimeType: raw.mimeType } : {},
28495
+ ...typeof raw.size === "number" ? { size: raw.size } : {}
28496
+ };
28497
+ }
28498
+ if (type === "resource" && raw.resource && typeof raw.resource === "object") {
28499
+ const resource = raw.resource;
28500
+ if (typeof resource.uri !== "string") return null;
28501
+ return {
28502
+ type,
28503
+ resource: {
28504
+ uri: resource.uri,
28505
+ ...typeof resource.mimeType === "string" || resource.mimeType === null ? { mimeType: resource.mimeType } : {},
28506
+ ...typeof resource.text === "string" ? { text: resource.text } : {},
28507
+ ...typeof resource.blob === "string" ? { blob: resource.blob } : {}
28508
+ }
28509
+ };
28510
+ }
28511
+ return null;
28512
+ }
28513
+ function flattenInputParts(parts) {
28514
+ return parts.map((part) => {
28515
+ if (part.type === "text") return part.text;
28516
+ if (part.type === "audio") return part.transcript || "";
28517
+ if (part.type === "resource") return part.text || "";
28518
+ return "";
28519
+ }).filter((value) => value.length > 0).join("\n");
28520
+ }
28521
+ var init_io_contracts = __esm2({
28522
+ "src/providers/io-contracts.ts"() {
28523
+ "use strict";
28524
+ }
28525
+ });
28526
+ function flattenContent(content) {
28527
+ if (typeof content === "string") return content;
28528
+ return flattenMessageParts(normalizeMessageParts(content));
28529
+ }
28530
+ var init_contracts = __esm2({
28531
+ "src/providers/contracts.ts"() {
28532
+ "use strict";
28533
+ init_io_contracts();
28534
+ init_io_contracts();
28535
+ }
28536
+ });
28316
28537
  function canonicalizeKindHint(value) {
28317
28538
  return value.trim().toLowerCase().replace(/[\s-]+/g, "_");
28318
28539
  }
@@ -28463,6 +28684,127 @@ var require_dist2 = __commonJS({
28463
28684
  };
28464
28685
  }
28465
28686
  });
28687
+ function isPlainObject3(value) {
28688
+ return !!value && typeof value === "object" && !Array.isArray(value);
28689
+ }
28690
+ function isFiniteNumber(value) {
28691
+ return typeof value === "number" && Number.isFinite(value);
28692
+ }
28693
+ function validateStatus(status, source) {
28694
+ if (typeof status !== "string" || !VALID_STATUSES.includes(status)) {
28695
+ throw new Error(`${source}: status must be one of ${VALID_STATUSES.join(", ")}`);
28696
+ }
28697
+ return status;
28698
+ }
28699
+ function validateRole(role, source, index) {
28700
+ if (typeof role !== "string" || !VALID_ROLES.includes(role)) {
28701
+ throw new Error(`${source}: messages[${index}].role must be one of ${VALID_ROLES.join(", ")}`);
28702
+ }
28703
+ return role;
28704
+ }
28705
+ function validateMessageContent(content, source, index) {
28706
+ if (typeof content === "string") return content;
28707
+ if (Array.isArray(content)) return normalizeMessageParts(content);
28708
+ throw new Error(`${source}: messages[${index}].content must be a string or structured content array`);
28709
+ }
28710
+ function validateMessage(message, source, index) {
28711
+ if (!isPlainObject3(message)) {
28712
+ throw new Error(`${source}: messages[${index}] must be an object`);
28713
+ }
28714
+ const normalized = {
28715
+ role: validateRole(message.role, source, index),
28716
+ content: validateMessageContent(message.content, source, index)
28717
+ };
28718
+ if (typeof message.kind === "string") normalized.kind = message.kind;
28719
+ if (typeof message.id === "string") normalized.id = message.id;
28720
+ if (isFiniteNumber(message.index)) normalized.index = message.index;
28721
+ if (isFiniteNumber(message.timestamp)) normalized.timestamp = message.timestamp;
28722
+ if (isFiniteNumber(message.receivedAt)) normalized.receivedAt = message.receivedAt;
28723
+ if (Array.isArray(message.toolCalls)) normalized.toolCalls = message.toolCalls;
28724
+ if (isPlainObject3(message.meta)) normalized.meta = message.meta;
28725
+ if (typeof message.senderName === "string") normalized.senderName = message.senderName;
28726
+ if (typeof message._type === "string") normalized._type = message._type;
28727
+ if (typeof message._sub === "string") normalized._sub = message._sub;
28728
+ return normalized;
28729
+ }
28730
+ function validateModal(activeModal, status, source) {
28731
+ if (activeModal == null) {
28732
+ if (status === "waiting_approval") {
28733
+ throw new Error(`${source}: waiting_approval status requires activeModal with buttons`);
28734
+ }
28735
+ return activeModal === null ? null : void 0;
28736
+ }
28737
+ if (!isPlainObject3(activeModal)) {
28738
+ throw new Error(`${source}: activeModal must be an object when provided`);
28739
+ }
28740
+ if (typeof activeModal.message !== "string") {
28741
+ throw new Error(`${source}: activeModal.message must be a string`);
28742
+ }
28743
+ if (!Array.isArray(activeModal.buttons) || activeModal.buttons.some((button) => typeof button !== "string" || !button.trim())) {
28744
+ throw new Error(`${source}: activeModal.buttons must be a non-empty string array`);
28745
+ }
28746
+ const normalized = {
28747
+ message: activeModal.message,
28748
+ buttons: activeModal.buttons.map((button) => button.trim())
28749
+ };
28750
+ if (isFiniteNumber(activeModal.width)) normalized.width = activeModal.width;
28751
+ if (isFiniteNumber(activeModal.height)) normalized.height = activeModal.height;
28752
+ return normalized;
28753
+ }
28754
+ function validateControlValues(controlValues, source) {
28755
+ if (controlValues === void 0) return void 0;
28756
+ if (!isPlainObject3(controlValues)) {
28757
+ throw new Error(`${source}: controlValues must be an object when provided`);
28758
+ }
28759
+ const normalized = {};
28760
+ for (const [key, value] of Object.entries(controlValues)) {
28761
+ if (typeof value !== "string" && typeof value !== "number" && typeof value !== "boolean") {
28762
+ throw new Error(`${source}: controlValues.${key} must be string, number, or boolean`);
28763
+ }
28764
+ normalized[key] = value;
28765
+ }
28766
+ return normalized;
28767
+ }
28768
+ function validateReadChatResultPayload(raw, source = "read_chat") {
28769
+ if (!isPlainObject3(raw)) {
28770
+ throw new Error(`${source}: payload must be an object`);
28771
+ }
28772
+ const status = validateStatus(raw.status, source);
28773
+ if (!Array.isArray(raw.messages)) {
28774
+ throw new Error(`${source}: messages must be an array`);
28775
+ }
28776
+ const messages = raw.messages.map((message, index) => validateMessage(message, source, index));
28777
+ const activeModal = validateModal(raw.activeModal, status, source);
28778
+ const controlValues = validateControlValues(raw.controlValues, source);
28779
+ const normalized = {
28780
+ status,
28781
+ messages
28782
+ };
28783
+ if (activeModal !== void 0) normalized.activeModal = activeModal;
28784
+ if (typeof raw.id === "string") normalized.id = raw.id;
28785
+ if (typeof raw.title === "string") normalized.title = raw.title;
28786
+ if (typeof raw.agentType === "string") normalized.agentType = raw.agentType;
28787
+ if (typeof raw.agentName === "string") normalized.agentName = raw.agentName;
28788
+ if (typeof raw.extensionId === "string") normalized.extensionId = raw.extensionId;
28789
+ if (typeof raw.inputContent === "string") normalized.inputContent = raw.inputContent;
28790
+ if (typeof raw.isVisible === "boolean") normalized.isVisible = raw.isVisible;
28791
+ if (typeof raw.isWelcomeScreen === "boolean") normalized.isWelcomeScreen = raw.isWelcomeScreen;
28792
+ if (controlValues) normalized.controlValues = controlValues;
28793
+ if (raw.summaryMetadata !== void 0) normalized.summaryMetadata = raw.summaryMetadata;
28794
+ if (Array.isArray(raw.effects)) normalized.effects = raw.effects;
28795
+ if (typeof raw.providerSessionId === "string") normalized.providerSessionId = raw.providerSessionId;
28796
+ return normalized;
28797
+ }
28798
+ var VALID_STATUSES;
28799
+ var VALID_ROLES;
28800
+ var init_read_chat_contract = __esm2({
28801
+ "src/providers/read-chat-contract.ts"() {
28802
+ "use strict";
28803
+ init_contracts();
28804
+ VALID_STATUSES = ["idle", "generating", "waiting_approval", "error", "panel_hidden", "streaming", "long_generating"];
28805
+ VALID_ROLES = ["user", "assistant", "system", "human"];
28806
+ }
28807
+ });
28466
28808
  function isModuleNotFoundError(error48, ref) {
28467
28809
  if (!(error48 instanceof Error)) return false;
28468
28810
  const message = error48.message || "";
@@ -29313,6 +29655,7 @@ var require_dist2 = __commonJS({
29313
29655
  init_pty_transport();
29314
29656
  init_provider_cli_shared();
29315
29657
  init_chat_message_normalization();
29658
+ init_read_chat_contract();
29316
29659
  init_provider_cli_parse();
29317
29660
  init_provider_cli_config();
29318
29661
  init_provider_cli_runtime();
@@ -30458,6 +30801,9 @@ var require_dist2 = __commonJS({
30458
30801
  runtimeSettings: this.runtimeSettings
30459
30802
  });
30460
30803
  const parsed = this.cliScripts.parseOutput(input);
30804
+ if (parsed && typeof parsed === "object") {
30805
+ Object.assign(parsed, validateReadChatResultPayload(parsed, `${this.cliType} parseOutput`));
30806
+ }
30461
30807
  const refinedStatus = this.refineDetectedStatus(typeof parsed?.status === "string" ? parsed.status : null, input.recentBuffer, input.screenText);
30462
30808
  if (parsed && refinedStatus && parsed.status !== refinedStatus) {
30463
30809
  parsed.status = refinedStatus;
@@ -33016,352 +33362,145 @@ ${data.message || ""}`.trim();
33016
33362
  return { success: false, error: e.message };
33017
33363
  }
33018
33364
  }
33019
- /**
33020
- * CDP DOM Debug — IDE AI panel specialized analysis
33021
- * Collect all essential info at once when supporting new IDE
33022
- *
33023
- * args:
33024
- * ideType?: string — IDE type hint
33025
- * sessionId?: string — agent webview session ID
33026
- */
33027
- async handleDomDebug(args) {
33028
- if (!this.getCdp()?.isConnected) return { success: false, error: "CDP not connected" };
33029
- const sessionId = args?.sessionId;
33030
- const expression = `(() => {
33031
- const result = {
33032
- url: location.href,
33033
- title: document.title,
33034
- viewport: { w: window.innerWidth, h: window.innerHeight },
33035
-
33036
- // Input field info
33037
- inputs: [],
33038
- // Textarea info
33039
- textareas: [],
33040
- // Contenteditable info
33041
- editables: [],
33042
- // Buttons (send, submit etc)
33043
- buttons: [],
33044
- // iframes (agent webviews)
33045
- iframes: [],
33046
- // role="textbox" info
33047
- textboxes: [],
33048
- };
33049
-
33050
- // Input fields
33051
- document.querySelectorAll('input[type="text"], input:not([type])').forEach((el, i) => {
33052
- if (i >= 10) return;
33053
- result.inputs.push({
33054
- tag: 'input',
33055
- id: el.id || null,
33056
- class: (el.className || '').toString().slice(0, 150),
33057
- placeholder: el.getAttribute('placeholder') || null,
33058
- name: el.name || null,
33059
- value: el.value?.slice(0, 100) || null,
33060
- visible: el.offsetParent !== null,
33061
- rect: (() => { try { const r = el.getBoundingClientRect(); return { x: Math.round(r.x), y: Math.round(r.y), w: Math.round(r.width), h: Math.round(r.height) }; } catch { return null; } })(),
33062
- });
33063
- });
33064
-
33065
- // textarea
33066
- document.querySelectorAll('textarea').forEach((el, i) => {
33067
- if (i >= 10) return;
33068
- result.textareas.push({
33069
- id: el.id || null,
33070
- class: (el.className || '').toString().slice(0, 150),
33071
- placeholder: el.getAttribute('placeholder') || null,
33072
- rows: el.rows,
33073
- value: el.value?.slice(0, 100) || null,
33074
- visible: el.offsetParent !== null,
33075
- rect: (() => { try { const r = el.getBoundingClientRect(); return { x: Math.round(r.x), y: Math.round(r.y), w: Math.round(r.width), h: Math.round(r.height) }; } catch { return null; } })(),
33076
- });
33077
- });
33078
-
33079
- // contenteditable
33080
- document.querySelectorAll('[contenteditable="true"]').forEach((el, i) => {
33081
- if (i >= 10) return;
33082
- result.editables.push({
33083
- tag: el.tagName?.toLowerCase(),
33084
- id: el.id || null,
33085
- class: (el.className || '').toString().slice(0, 150),
33086
- role: el.getAttribute('role') || null,
33087
- text: (el.textContent || '').trim().slice(0, 100),
33088
- visible: el.offsetParent !== null,
33089
- rect: (() => { try { const r = el.getBoundingClientRect(); return { x: Math.round(r.x), y: Math.round(r.y), w: Math.round(r.width), h: Math.round(r.height) }; } catch { return null; } })(),
33090
- });
33091
- });
33092
-
33093
- // role="textbox"
33094
- document.querySelectorAll('[role="textbox"]').forEach((el, i) => {
33095
- if (i >= 10) return;
33096
- result.textboxes.push({
33097
- tag: el.tagName?.toLowerCase(),
33098
- id: el.id || null,
33099
- class: (el.className || '').toString().slice(0, 150),
33100
- 'aria-label': el.getAttribute('aria-label') || null,
33101
- text: (el.textContent || '').trim().slice(0, 100),
33102
- visible: el.offsetParent !== null,
33103
- rect: (() => { try { const r = el.getBoundingClientRect(); return { x: Math.round(r.x), y: Math.round(r.y), w: Math.round(r.width), h: Math.round(r.height) }; } catch { return null; } })(),
33104
- });
33105
- });
33106
-
33107
- // Buttons (send, submit, accept, reject, approve etc)
33108
- const btnKeywords = /send|submit|accept|reject|approve|deny|cancel|confirm|run|execute|apply/i;
33109
- document.querySelectorAll('button, [role="button"], input[type="submit"]').forEach((el, i) => {
33110
- const text = (el.textContent || el.getAttribute('aria-label') || '').trim();
33111
- if (i < 30 && (text.length < 30 || btnKeywords.test(text))) {
33112
- result.buttons.push({
33113
- tag: el.tagName?.toLowerCase(),
33114
- id: el.id || null,
33115
- class: (el.className || '').toString().slice(0, 150),
33116
- text: text.slice(0, 80),
33117
- 'aria-label': el.getAttribute('aria-label') || null,
33118
- disabled: el.disabled || el.getAttribute('disabled') !== null,
33119
- visible: el.offsetParent !== null,
33120
- rect: (() => { try { const r = el.getBoundingClientRect(); return { x: Math.round(r.x), y: Math.round(r.y), w: Math.round(r.width), h: Math.round(r.height) }; } catch { return null; } })(),
33121
- });
33122
- }
33123
- });
33124
-
33125
- // iframes
33126
- document.querySelectorAll('iframe, webview').forEach((el, i) => {
33127
- if (i >= 20) return;
33128
- result.iframes.push({
33129
- tag: el.tagName?.toLowerCase(),
33130
- id: el.id || null,
33131
- class: (el.className || '').toString().slice(0, 150),
33132
- src: el.getAttribute('src')?.slice(0, 200) || null,
33133
- title: el.getAttribute('title') || null,
33134
- visible: el.offsetParent !== null,
33135
- rect: (() => { try { const r = el.getBoundingClientRect(); return { x: Math.round(r.x), y: Math.round(r.y), w: Math.round(r.width), h: Math.round(r.height) }; } catch { return null; } })(),
33136
- });
33137
- });
33138
-
33139
- return JSON.stringify(result);
33140
- })()`;
33141
- try {
33142
- let raw;
33143
- if (sessionId) {
33144
- raw = await this.getCdp().evaluateInSessionFrame(sessionId, expression);
33145
- } else {
33146
- raw = await this.getCdp().evaluate(expression, 3e4);
33147
- }
33148
- const parsed = typeof raw === "string" ? JSON.parse(raw) : raw;
33149
- return { success: true, ...parsed };
33150
- } catch (e) {
33151
- return { success: false, error: e.message };
33152
- }
33153
- }
33154
- };
33155
- var crypto2 = __toESM2(require("crypto"));
33156
- function normalizeInputEnvelope(input) {
33157
- const normalized = normalizeInputEnvelopePayload(input);
33158
- const textFallback = normalized.textFallback ?? flattenInputParts(normalized.parts);
33159
- return {
33160
- parts: normalized.parts,
33161
- textFallback,
33162
- ...normalized.metadata ? { metadata: normalized.metadata } : {}
33163
- };
33164
- }
33165
- function normalizeMessageParts(content) {
33166
- if (typeof content === "string") return [{ type: "text", text: content }];
33167
- if (!Array.isArray(content)) {
33168
- if (content && typeof content === "object" && typeof content.text === "string") {
33169
- return [{ type: "text", text: String(content.text) }];
33170
- }
33171
- return [];
33172
- }
33173
- const parts = [];
33174
- for (const raw of content) {
33175
- if (typeof raw === "string") {
33176
- parts.push({ type: "text", text: raw });
33177
- continue;
33178
- }
33179
- if (!raw || typeof raw !== "object") continue;
33180
- const part = normalizeMessagePartObject(raw);
33181
- if (part) parts.push(part);
33182
- }
33183
- return parts;
33184
- }
33185
- function flattenMessageParts(parts) {
33186
- return parts.map((part) => {
33187
- if (part.type === "text") return part.text;
33188
- if (part.type === "resource") return part.resource.text || "";
33189
- return "";
33190
- }).filter((value) => value.length > 0).join("\n");
33191
- }
33192
- function normalizeInputEnvelopePayload(input) {
33193
- if (typeof input === "string") {
33194
- return { parts: [{ type: "text", text: input }], textFallback: input };
33195
- }
33196
- if (!input || typeof input !== "object") {
33197
- return { parts: [], textFallback: "" };
33198
- }
33199
- const record2 = input;
33200
- const nestedInput = record2.input;
33201
- if (nestedInput && typeof nestedInput === "object") {
33202
- const nested = nestedInput;
33203
- return {
33204
- parts: normalizeInputParts(nested.parts ?? nested.prompt),
33205
- textFallback: typeof nested.textFallback === "string" ? nested.textFallback : void 0,
33206
- metadata: normalizeInputMetadata(nested.metadata)
33207
- };
33208
- }
33209
- const directText = typeof record2.text === "string" ? record2.text : typeof record2.message === "string" ? record2.message : void 0;
33210
- if (directText !== void 0) {
33211
- return { parts: [{ type: "text", text: directText }], textFallback: directText };
33212
- }
33213
- const directParts = normalizeInputParts(record2.parts ?? record2.prompt);
33214
- return {
33215
- parts: directParts,
33216
- textFallback: typeof record2.textFallback === "string" ? record2.textFallback : void 0,
33217
- metadata: normalizeInputMetadata(record2.metadata)
33218
- };
33219
- }
33220
- function normalizeInputMetadata(value) {
33221
- if (!value || typeof value !== "object") return void 0;
33222
- const record2 = value;
33223
- const metadata = {};
33224
- if (record2.source === "dashboard" || record2.source === "shortcut_api" || record2.source === "provider_script" || record2.source === "session_replay") {
33225
- metadata.source = record2.source;
33226
- }
33227
- if (typeof record2.clientTimestamp === "number" && Number.isFinite(record2.clientTimestamp)) {
33228
- metadata.clientTimestamp = record2.clientTimestamp;
33229
- }
33230
- return Object.keys(metadata).length > 0 ? metadata : void 0;
33231
- }
33232
- function normalizeInputParts(value) {
33233
- if (!Array.isArray(value)) return [];
33234
- const parts = [];
33235
- for (const raw of value) {
33236
- if (typeof raw === "string") {
33237
- parts.push({ type: "text", text: raw });
33238
- continue;
33239
- }
33240
- if (!raw || typeof raw !== "object") continue;
33241
- const part = normalizeInputPartObject(raw);
33242
- if (part) parts.push(part);
33243
- }
33244
- return parts;
33245
- }
33246
- function normalizeInputPartObject(raw) {
33247
- const type = raw.type;
33248
- if (type === "text" && typeof raw.text === "string") {
33249
- return { type, text: raw.text };
33250
- }
33251
- if (type === "image" && typeof raw.mimeType === "string") {
33252
- return {
33253
- type,
33254
- mimeType: raw.mimeType,
33255
- ...typeof raw.uri === "string" ? { uri: raw.uri } : {},
33256
- ...typeof raw.data === "string" ? { data: raw.data } : {},
33257
- ...typeof raw.alt === "string" ? { alt: raw.alt } : {}
33258
- };
33259
- }
33260
- if (type === "audio" && typeof raw.mimeType === "string") {
33261
- return {
33262
- type,
33263
- mimeType: raw.mimeType,
33264
- ...typeof raw.uri === "string" ? { uri: raw.uri } : {},
33265
- ...typeof raw.data === "string" ? { data: raw.data } : {},
33266
- ...typeof raw.transcript === "string" ? { transcript: raw.transcript } : {}
33267
- };
33268
- }
33269
- if (type === "video" && typeof raw.mimeType === "string") {
33270
- return {
33271
- type,
33272
- mimeType: raw.mimeType,
33273
- ...typeof raw.uri === "string" ? { uri: raw.uri } : {},
33274
- ...typeof raw.data === "string" ? { data: raw.data } : {},
33275
- ...typeof raw.posterUri === "string" ? { posterUri: raw.posterUri } : {}
33276
- };
33277
- }
33278
- if (type === "resource" && typeof raw.uri === "string") {
33279
- return {
33280
- type,
33281
- uri: raw.uri,
33282
- ...typeof raw.mimeType === "string" ? { mimeType: raw.mimeType } : {},
33283
- ...typeof raw.name === "string" ? { name: raw.name } : {},
33284
- ...typeof raw.text === "string" ? { text: raw.text } : {},
33285
- ...typeof raw.data === "string" ? { data: raw.data } : {}
33286
- };
33287
- }
33288
- if (type === "resource_link" && typeof raw.uri === "string") {
33289
- return {
33290
- type: "resource",
33291
- uri: raw.uri,
33292
- ...typeof raw.mimeType === "string" ? { mimeType: raw.mimeType } : {},
33293
- ...typeof raw.name === "string" ? { name: raw.name } : {}
33294
- };
33295
- }
33296
- return null;
33297
- }
33298
- function normalizeMessagePartObject(raw) {
33299
- const type = raw.type;
33300
- if (type === "text" && typeof raw.text === "string") {
33301
- return { type, text: raw.text };
33302
- }
33303
- if (type === "image" && typeof raw.mimeType === "string") {
33304
- return {
33305
- type,
33306
- mimeType: raw.mimeType,
33307
- ...typeof raw.uri === "string" ? { uri: raw.uri } : {},
33308
- ...typeof raw.data === "string" ? { data: raw.data } : {}
33309
- };
33310
- }
33311
- if (type === "audio" && typeof raw.mimeType === "string") {
33312
- return {
33313
- type,
33314
- mimeType: raw.mimeType,
33315
- ...typeof raw.uri === "string" ? { uri: raw.uri } : {},
33316
- ...typeof raw.data === "string" ? { data: raw.data } : {},
33317
- ...typeof raw.transcript === "string" ? { transcript: raw.transcript } : {}
33318
- };
33319
- }
33320
- if (type === "video" && typeof raw.mimeType === "string") {
33321
- return {
33322
- type,
33323
- mimeType: raw.mimeType,
33324
- ...typeof raw.uri === "string" ? { uri: raw.uri } : {},
33325
- ...typeof raw.data === "string" ? { data: raw.data } : {},
33326
- ...typeof raw.posterUri === "string" ? { posterUri: raw.posterUri } : {}
33327
- };
33328
- }
33329
- if (type === "resource_link" && typeof raw.uri === "string" && typeof raw.name === "string") {
33330
- return {
33331
- type,
33332
- uri: raw.uri,
33333
- name: raw.name,
33334
- ...typeof raw.mimeType === "string" ? { mimeType: raw.mimeType } : {},
33335
- ...typeof raw.size === "number" ? { size: raw.size } : {}
33336
- };
33337
- }
33338
- if (type === "resource" && raw.resource && typeof raw.resource === "object") {
33339
- const resource = raw.resource;
33340
- if (typeof resource.uri !== "string") return null;
33341
- return {
33342
- type,
33343
- resource: {
33344
- uri: resource.uri,
33345
- ...typeof resource.mimeType === "string" || resource.mimeType === null ? { mimeType: resource.mimeType } : {},
33346
- ...typeof resource.text === "string" ? { text: resource.text } : {},
33347
- ...typeof resource.blob === "string" ? { blob: resource.blob } : {}
33365
+ /**
33366
+ * CDP DOM Debug — IDE AI panel specialized analysis
33367
+ * Collect all essential info at once when supporting new IDE
33368
+ *
33369
+ * args:
33370
+ * ideType?: string — IDE type hint
33371
+ * sessionId?: string — agent webview session ID
33372
+ */
33373
+ async handleDomDebug(args) {
33374
+ if (!this.getCdp()?.isConnected) return { success: false, error: "CDP not connected" };
33375
+ const sessionId = args?.sessionId;
33376
+ const expression = `(() => {
33377
+ const result = {
33378
+ url: location.href,
33379
+ title: document.title,
33380
+ viewport: { w: window.innerWidth, h: window.innerHeight },
33381
+
33382
+ // Input field info
33383
+ inputs: [],
33384
+ // Textarea info
33385
+ textareas: [],
33386
+ // Contenteditable info
33387
+ editables: [],
33388
+ // Buttons (send, submit etc)
33389
+ buttons: [],
33390
+ // iframes (agent webviews)
33391
+ iframes: [],
33392
+ // role="textbox" info
33393
+ textboxes: [],
33394
+ };
33395
+
33396
+ // Input fields
33397
+ document.querySelectorAll('input[type="text"], input:not([type])').forEach((el, i) => {
33398
+ if (i >= 10) return;
33399
+ result.inputs.push({
33400
+ tag: 'input',
33401
+ id: el.id || null,
33402
+ class: (el.className || '').toString().slice(0, 150),
33403
+ placeholder: el.getAttribute('placeholder') || null,
33404
+ name: el.name || null,
33405
+ value: el.value?.slice(0, 100) || null,
33406
+ visible: el.offsetParent !== null,
33407
+ rect: (() => { try { const r = el.getBoundingClientRect(); return { x: Math.round(r.x), y: Math.round(r.y), w: Math.round(r.width), h: Math.round(r.height) }; } catch { return null; } })(),
33408
+ });
33409
+ });
33410
+
33411
+ // textarea
33412
+ document.querySelectorAll('textarea').forEach((el, i) => {
33413
+ if (i >= 10) return;
33414
+ result.textareas.push({
33415
+ id: el.id || null,
33416
+ class: (el.className || '').toString().slice(0, 150),
33417
+ placeholder: el.getAttribute('placeholder') || null,
33418
+ rows: el.rows,
33419
+ value: el.value?.slice(0, 100) || null,
33420
+ visible: el.offsetParent !== null,
33421
+ rect: (() => { try { const r = el.getBoundingClientRect(); return { x: Math.round(r.x), y: Math.round(r.y), w: Math.round(r.width), h: Math.round(r.height) }; } catch { return null; } })(),
33422
+ });
33423
+ });
33424
+
33425
+ // contenteditable
33426
+ document.querySelectorAll('[contenteditable="true"]').forEach((el, i) => {
33427
+ if (i >= 10) return;
33428
+ result.editables.push({
33429
+ tag: el.tagName?.toLowerCase(),
33430
+ id: el.id || null,
33431
+ class: (el.className || '').toString().slice(0, 150),
33432
+ role: el.getAttribute('role') || null,
33433
+ text: (el.textContent || '').trim().slice(0, 100),
33434
+ visible: el.offsetParent !== null,
33435
+ rect: (() => { try { const r = el.getBoundingClientRect(); return { x: Math.round(r.x), y: Math.round(r.y), w: Math.round(r.width), h: Math.round(r.height) }; } catch { return null; } })(),
33436
+ });
33437
+ });
33438
+
33439
+ // role="textbox"
33440
+ document.querySelectorAll('[role="textbox"]').forEach((el, i) => {
33441
+ if (i >= 10) return;
33442
+ result.textboxes.push({
33443
+ tag: el.tagName?.toLowerCase(),
33444
+ id: el.id || null,
33445
+ class: (el.className || '').toString().slice(0, 150),
33446
+ 'aria-label': el.getAttribute('aria-label') || null,
33447
+ text: (el.textContent || '').trim().slice(0, 100),
33448
+ visible: el.offsetParent !== null,
33449
+ rect: (() => { try { const r = el.getBoundingClientRect(); return { x: Math.round(r.x), y: Math.round(r.y), w: Math.round(r.width), h: Math.round(r.height) }; } catch { return null; } })(),
33450
+ });
33451
+ });
33452
+
33453
+ // Buttons (send, submit, accept, reject, approve etc)
33454
+ const btnKeywords = /send|submit|accept|reject|approve|deny|cancel|confirm|run|execute|apply/i;
33455
+ document.querySelectorAll('button, [role="button"], input[type="submit"]').forEach((el, i) => {
33456
+ const text = (el.textContent || el.getAttribute('aria-label') || '').trim();
33457
+ if (i < 30 && (text.length < 30 || btnKeywords.test(text))) {
33458
+ result.buttons.push({
33459
+ tag: el.tagName?.toLowerCase(),
33460
+ id: el.id || null,
33461
+ class: (el.className || '').toString().slice(0, 150),
33462
+ text: text.slice(0, 80),
33463
+ 'aria-label': el.getAttribute('aria-label') || null,
33464
+ disabled: el.disabled || el.getAttribute('disabled') !== null,
33465
+ visible: el.offsetParent !== null,
33466
+ rect: (() => { try { const r = el.getBoundingClientRect(); return { x: Math.round(r.x), y: Math.round(r.y), w: Math.round(r.width), h: Math.round(r.height) }; } catch { return null; } })(),
33467
+ });
33468
+ }
33469
+ });
33470
+
33471
+ // iframes
33472
+ document.querySelectorAll('iframe, webview').forEach((el, i) => {
33473
+ if (i >= 20) return;
33474
+ result.iframes.push({
33475
+ tag: el.tagName?.toLowerCase(),
33476
+ id: el.id || null,
33477
+ class: (el.className || '').toString().slice(0, 150),
33478
+ src: el.getAttribute('src')?.slice(0, 200) || null,
33479
+ title: el.getAttribute('title') || null,
33480
+ visible: el.offsetParent !== null,
33481
+ rect: (() => { try { const r = el.getBoundingClientRect(); return { x: Math.round(r.x), y: Math.round(r.y), w: Math.round(r.width), h: Math.round(r.height) }; } catch { return null; } })(),
33482
+ });
33483
+ });
33484
+
33485
+ return JSON.stringify(result);
33486
+ })()`;
33487
+ try {
33488
+ let raw;
33489
+ if (sessionId) {
33490
+ raw = await this.getCdp().evaluateInSessionFrame(sessionId, expression);
33491
+ } else {
33492
+ raw = await this.getCdp().evaluate(expression, 3e4);
33348
33493
  }
33349
- };
33494
+ const parsed = typeof raw === "string" ? JSON.parse(raw) : raw;
33495
+ return { success: true, ...parsed };
33496
+ } catch (e) {
33497
+ return { success: false, error: e.message };
33498
+ }
33350
33499
  }
33351
- return null;
33352
- }
33353
- function flattenInputParts(parts) {
33354
- return parts.map((part) => {
33355
- if (part.type === "text") return part.text;
33356
- if (part.type === "audio") return part.transcript || "";
33357
- if (part.type === "resource") return part.text || "";
33358
- return "";
33359
- }).filter((value) => value.length > 0).join("\n");
33360
- }
33361
- function flattenContent(content) {
33362
- if (typeof content === "string") return content;
33363
- return flattenMessageParts(normalizeMessageParts(content));
33364
- }
33500
+ };
33501
+ var crypto2 = __toESM2(require("crypto"));
33502
+ init_contracts();
33503
+ init_contracts();
33365
33504
  var DEFAULT_MONITOR_CONFIG = {
33366
33505
  approvalAlert: true,
33367
33506
  longGeneratingAlert: true,
@@ -33472,6 +33611,7 @@ ${data.message || ""}`.trim();
33472
33611
  }
33473
33612
  }
33474
33613
  };
33614
+ init_contracts();
33475
33615
  init_chat_message_normalization();
33476
33616
  function extractProviderControlValues(controls, data) {
33477
33617
  if (!data || typeof data !== "object") return void 0;
@@ -33593,38 +33733,37 @@ ${data.message || ""}`.trim();
33593
33733
  return null;
33594
33734
  }
33595
33735
  function normalizeControlListResult(data) {
33596
- if (data && typeof data === "object" && Array.isArray(data.options)) {
33597
- return {
33598
- options: normalizeControlOptions(data.options),
33599
- ...isScalarControlValue(data.currentValue) ? { currentValue: data.currentValue } : {},
33600
- ...typeof data.error === "string" ? { error: data.error } : {}
33601
- };
33736
+ if (!data || typeof data !== "object" || !Array.isArray(data.options)) {
33737
+ throw new Error("Provider control list results must use the typed shape { options, currentValue?, error? }");
33602
33738
  }
33603
- const rawOptions = Array.isArray(data?.models) ? data.models : Array.isArray(data?.modes) ? data.modes : Array.isArray(data?.options) ? data.options : [];
33604
- const options = normalizeControlOptions(rawOptions);
33605
33739
  return {
33606
- options,
33607
- ...isScalarControlValue(data?.current) ? { currentValue: data.current } : {},
33608
- ...isScalarControlValue(data?.currentValue) ? { currentValue: data.currentValue } : {},
33609
- ...typeof data?.error === "string" ? { error: data.error } : {}
33740
+ options: normalizeControlOptions(data.options),
33741
+ ...isScalarControlValue(data.currentValue) ? { currentValue: data.currentValue } : {},
33742
+ ...typeof data.error === "string" ? { error: data.error } : {}
33610
33743
  };
33611
33744
  }
33612
33745
  function normalizeControlSetResult(data) {
33613
- const currentValue = isScalarControlValue(data?.currentValue) ? data.currentValue : isScalarControlValue(data?.value) ? data.value : void 0;
33746
+ if (!data || typeof data !== "object" || typeof data.ok !== "boolean") {
33747
+ throw new Error("Provider control set results must use the typed shape { ok, currentValue?, effects?, error? }");
33748
+ }
33749
+ const currentValue = isScalarControlValue(data.currentValue) ? data.currentValue : isScalarControlValue(data.value) ? data.value : void 0;
33614
33750
  return {
33615
- ok: data?.ok === true || data?.success === true,
33751
+ ok: data.ok,
33616
33752
  ...currentValue !== void 0 ? { currentValue } : {},
33617
- ...Array.isArray(data?.effects) ? { effects: normalizeProviderEffects(data) } : {},
33618
- ...typeof data?.error === "string" ? { error: data.error } : {}
33753
+ ...Array.isArray(data.effects) ? { effects: normalizeProviderEffects(data) } : {},
33754
+ ...typeof data.error === "string" ? { error: data.error } : {}
33619
33755
  };
33620
33756
  }
33621
33757
  function normalizeControlInvokeResult(data) {
33622
- const currentValue = isScalarControlValue(data?.currentValue) ? data.currentValue : isScalarControlValue(data?.value) ? data.value : void 0;
33758
+ if (!data || typeof data !== "object" || typeof data.ok !== "boolean") {
33759
+ throw new Error("Provider control invoke results must use the typed shape { ok, currentValue?, effects?, error? }");
33760
+ }
33761
+ const currentValue = isScalarControlValue(data.currentValue) ? data.currentValue : isScalarControlValue(data.value) ? data.value : void 0;
33623
33762
  return {
33624
- ok: data?.ok === true || data?.success === true,
33763
+ ok: data.ok,
33625
33764
  ...currentValue !== void 0 ? { currentValue } : {},
33626
- ...Array.isArray(data?.effects) ? { effects: normalizeProviderEffects(data) } : {},
33627
- ...typeof data?.error === "string" ? { error: data.error } : {}
33765
+ ...Array.isArray(data.effects) ? { effects: normalizeProviderEffects(data) } : {},
33766
+ ...typeof data.error === "string" ? { error: data.error } : {}
33628
33767
  };
33629
33768
  }
33630
33769
  function normalizeControlOptions(options) {
@@ -34509,19 +34648,37 @@ ${cleanBody}`;
34509
34648
  );
34510
34649
  }
34511
34650
  }
34651
+ buildSyntheticTurnKey(message, occurrence) {
34652
+ const role = typeof message?.role === "string" ? message.role : "";
34653
+ const kind = typeof message?.kind === "string" ? message.kind : "";
34654
+ const senderName = typeof message?.senderName === "string" ? message.senderName : "";
34655
+ const content = flattenContent(message?.content).replace(/\s+/g, " ").trim().slice(0, 500);
34656
+ return `${role}|${kind}|${senderName}|${content}|${occurrence}`;
34657
+ }
34512
34658
  /**
34513
- * Assign stable receivedAt to extension messages.
34514
- * Same pattern as IdeProviderInstance.readChat() prevByHash
34515
- * preserves first-seen timestamp across polling cycles.
34659
+ * Assign stable receivedAt / synthetic _turnKey to extension messages.
34660
+ * Same transcript should keep the same identity across polling cycles and
34661
+ * stream resets, while repeated identical text later in the transcript still
34662
+ * produces a distinct completion marker via the occurrence suffix.
34516
34663
  */
34517
34664
  assignReceivedAt(messages) {
34518
34665
  const now = Date.now();
34519
34666
  const nextHashes = /* @__PURE__ */ new Map();
34667
+ const occurrenceByBaseKey = /* @__PURE__ */ new Map();
34520
34668
  for (const msg of messages) {
34521
- const hash2 = `${msg.role}:${(msg.content || "").slice(0, 100)}`;
34522
- const prevTime = this.prevMessageHashes.get(hash2);
34669
+ const explicitTurnKey = typeof msg?._turnKey === "string" && msg._turnKey.trim() ? msg._turnKey.trim() : "";
34670
+ const explicitId = typeof msg?.id === "string" && msg.id.trim() ? `id:${msg.id.trim()}` : "";
34671
+ const explicitIndex = typeof msg?.index === "number" && Number.isFinite(msg.index) ? `idx:${msg.index}` : "";
34672
+ const baseKey = explicitTurnKey || explicitId || explicitIndex || `${msg?.role || ""}:${flattenContent(msg?.content || "").slice(0, 500)}`;
34673
+ const occurrence = (occurrenceByBaseKey.get(baseKey) || 0) + 1;
34674
+ occurrenceByBaseKey.set(baseKey, occurrence);
34675
+ const syntheticTurnKey = explicitTurnKey || explicitId || explicitIndex || this.buildSyntheticTurnKey(msg, occurrence);
34676
+ if (!explicitTurnKey && !explicitId && !explicitIndex) {
34677
+ msg._turnKey = syntheticTurnKey;
34678
+ }
34679
+ const prevTime = this.prevMessageHashes.get(syntheticTurnKey);
34523
34680
  msg.receivedAt = prevTime || now;
34524
- nextHashes.set(hash2, msg.receivedAt);
34681
+ nextHashes.set(syntheticTurnKey, msg.receivedAt);
34525
34682
  }
34526
34683
  this.prevMessageHashes = nextHashes;
34527
34684
  return normalizeChatMessages(messages);
@@ -34596,6 +34753,7 @@ ${effect.notification.body || ""}`.trim();
34596
34753
  }
34597
34754
  };
34598
34755
  init_logger();
34756
+ init_read_chat_contract();
34599
34757
  var DEFAULT_APPROVAL_POSITIVE_HINTS = [
34600
34758
  "run",
34601
34759
  "approve",
@@ -34873,7 +35031,7 @@ ${effect.notification.body || ""}`.trim();
34873
35031
  }
34874
35032
  }
34875
35033
  if (!raw || typeof raw !== "object") return;
34876
- const chat = raw;
35034
+ const chat = validateReadChatResultPayload(raw, `${this.type} readChat`);
34877
35035
  let { activeModal } = chat;
34878
35036
  if (activeModal) {
34879
35037
  const w = activeModal.width ?? Infinity;
@@ -36002,6 +36160,61 @@ ${effect.notification.body || ""}`.trim();
36002
36160
  }
36003
36161
  }
36004
36162
  init_logger();
36163
+ init_contracts();
36164
+ var VALID_INPUT_MEDIA_TYPES = /* @__PURE__ */ new Set(["text", "image", "audio", "video", "resource"]);
36165
+ function getProviderLabel(provider) {
36166
+ return provider?.name || provider?.type || "This provider";
36167
+ }
36168
+ function hasNonEmptyFallbackText(input) {
36169
+ return typeof input.textFallback === "string" && input.textFallback.trim().length > 0;
36170
+ }
36171
+ function getRequestedInputMediaTypes(input) {
36172
+ const types = /* @__PURE__ */ new Set();
36173
+ if (hasNonEmptyFallbackText(input) && !input.parts.some((part) => part.type === "text")) {
36174
+ types.add("text");
36175
+ }
36176
+ for (const part of input.parts) {
36177
+ if (VALID_INPUT_MEDIA_TYPES.has(part.type)) {
36178
+ types.add(part.type);
36179
+ }
36180
+ }
36181
+ return Array.from(types);
36182
+ }
36183
+ function getEffectiveSemanticPartCount(input) {
36184
+ let count = input.parts.length;
36185
+ if (hasNonEmptyFallbackText(input) && !input.parts.some((part) => part.type === "text")) {
36186
+ count += 1;
36187
+ }
36188
+ return count;
36189
+ }
36190
+ function assertTextOnlyInput(provider, input) {
36191
+ const unsupported = getRequestedInputMediaTypes(input).filter((type) => type !== "text");
36192
+ if (unsupported.length === 0) return;
36193
+ const label = getProviderLabel(provider);
36194
+ const suffix = unsupported.length === 1 ? "" : "s";
36195
+ throw new Error(`${label} only supports text input; unsupported input type${suffix}: ${unsupported.join(", ")}`);
36196
+ }
36197
+ function getDeclaredProviderInputSupport(provider) {
36198
+ const rawMediaTypes = Array.isArray(provider?.capabilities?.input?.mediaTypes) ? provider?.capabilities?.input?.mediaTypes.filter((type) => VALID_INPUT_MEDIA_TYPES.has(type)) : [];
36199
+ return {
36200
+ multipart: provider?.capabilities?.input?.multipart === true,
36201
+ mediaTypes: new Set(rawMediaTypes.length > 0 ? rawMediaTypes : ["text"])
36202
+ };
36203
+ }
36204
+ function assertProviderSupportsDeclaredInput(provider, input) {
36205
+ const label = getProviderLabel(provider);
36206
+ const support = getDeclaredProviderInputSupport(provider);
36207
+ const requestedTypes = getRequestedInputMediaTypes(input);
36208
+ const unsupported = requestedTypes.filter((type) => !support.mediaTypes.has(type));
36209
+ if (unsupported.length > 0) {
36210
+ const suffix = unsupported.length === 1 ? "" : "s";
36211
+ throw new Error(`${label} does not support input type${suffix}: ${unsupported.join(", ")}`);
36212
+ }
36213
+ if (getEffectiveSemanticPartCount(input) > 1 && !support.multipart) {
36214
+ throw new Error(`${label} does not support multipart input`);
36215
+ }
36216
+ }
36217
+ init_read_chat_contract();
36005
36218
  init_logger();
36006
36219
  var NORMAL_TRACE_BUFFER_SIZE = 200;
36007
36220
  var DEV_TRACE_BUFFER_SIZE = 1e3;
@@ -36177,10 +36390,15 @@ ${effect.notification.body || ""}`.trim();
36177
36390
  function isExtensionTransport(transport) {
36178
36391
  return transport === "cdp-webview";
36179
36392
  }
36180
- function buildRecentSendKey(h, args, provider, text) {
36393
+ function buildRecentSendKey(h, args, provider, signature) {
36181
36394
  const transport = getTargetTransport(h, provider) || "unknown";
36182
36395
  const target = args?.targetSessionId || args?.agentType || h.currentSession?.providerType || h.currentProviderType || h.currentManagerKey || "unknown";
36183
- return `${transport}:${target}:${text.trim()}`;
36396
+ return `${transport}:${target}:${signature.trim()}`;
36397
+ }
36398
+ function buildSendInputSignature(input) {
36399
+ const text = typeof input.textFallback === "string" ? input.textFallback.trim() : "";
36400
+ if (text) return text;
36401
+ return JSON.stringify(input.parts || []);
36184
36402
  }
36185
36403
  function getSendChatInputEnvelope(args) {
36186
36404
  return normalizeInputEnvelope(args?.input ? { input: args.input } : args);
@@ -36333,14 +36551,20 @@ ${effect.notification.body || ""}`.trim();
36333
36551
  };
36334
36552
  }
36335
36553
  function buildReadChatCommandResult(payload, args) {
36336
- const messages = normalizeReadChatMessages(payload);
36554
+ let validatedPayload;
36555
+ try {
36556
+ validatedPayload = validateReadChatResultPayload(payload, "read_chat command result");
36557
+ } catch (error48) {
36558
+ return { success: false, error: error48?.message || String(error48) };
36559
+ }
36560
+ const messages = normalizeReadChatMessages(validatedPayload);
36337
36561
  const cursor = normalizeReadChatCursor(args);
36338
36562
  if (!cursor.knownMessageCount && !cursor.lastMessageSignature && cursor.tailLimit > 0 && messages.length > cursor.tailLimit) {
36339
36563
  const tailMessages = messages.slice(-cursor.tailLimit);
36340
36564
  const lastMessageSignature = getChatMessageSignature(tailMessages[tailMessages.length - 1]);
36341
36565
  return {
36342
36566
  success: true,
36343
- ...payload,
36567
+ ...validatedPayload,
36344
36568
  messages: tailMessages,
36345
36569
  syncMode: "full",
36346
36570
  replaceFrom: 0,
@@ -36351,7 +36575,7 @@ ${effect.notification.body || ""}`.trim();
36351
36575
  const sync = computeReadChatSync(messages, cursor);
36352
36576
  return {
36353
36577
  success: true,
36354
- ...payload,
36578
+ ...validatedPayload,
36355
36579
  messages: sync.messages,
36356
36580
  syncMode: sync.syncMode,
36357
36581
  replaceFrom: sync.replaceFrom,
@@ -36430,12 +36654,18 @@ ${effect.notification.body || ""}`.trim();
36430
36654
  const adapter = getTargetedCliAdapter(h, args, provider?.type);
36431
36655
  if (adapter) {
36432
36656
  _log(`${transport} adapter: ${adapter.cliType}`);
36433
- const status = adapter.getStatus();
36657
+ const parsedStatus = typeof adapter.getScriptParsedStatus === "function" ? parseMaybeJson(adapter.getScriptParsedStatus()) : null;
36658
+ const parsedRecord = parsedStatus && typeof parsedStatus === "object" ? parsedStatus : null;
36659
+ const status = parsedRecord || adapter.getStatus();
36660
+ const title = typeof parsedRecord?.title === "string" ? parsedRecord.title : void 0;
36661
+ const providerSessionId = typeof parsedRecord?.providerSessionId === "string" ? parsedRecord.providerSessionId : void 0;
36434
36662
  if (status) {
36435
36663
  return buildReadChatCommandResult({
36436
36664
  messages: status.messages || [],
36437
36665
  status: status.status,
36438
- activeModal: status.activeModal
36666
+ activeModal: status.activeModal,
36667
+ ...title ? { title } : {},
36668
+ ...providerSessionId ? { providerSessionId } : {}
36439
36669
  }, args);
36440
36670
  }
36441
36671
  }
@@ -36453,25 +36683,26 @@ ${effect.notification.body || ""}`.trim();
36453
36683
  }
36454
36684
  }
36455
36685
  if (parsed && typeof parsed === "object") {
36456
- _log(`Extension OK: ${parsed.messages?.length || 0} msgs`);
36686
+ const validated = validateReadChatResultPayload(parsed, "extension read_chat");
36687
+ _log(`Extension OK: ${validated.messages?.length || 0} msgs`);
36457
36688
  traceProviderEvent(args, "provider", "extension.read_chat.success", {
36458
36689
  h,
36459
36690
  provider,
36460
36691
  payload: {
36461
36692
  method: "evaluateProviderScript",
36462
36693
  result: evalResult.result,
36463
- parsed,
36464
- messageCount: Array.isArray(parsed.messages) ? parsed.messages.length : 0
36694
+ parsed: validated,
36695
+ messageCount: Array.isArray(validated.messages) ? validated.messages.length : 0
36465
36696
  }
36466
36697
  });
36467
36698
  h.historyWriter.appendNewMessages(
36468
36699
  provider?.type || "unknown_extension",
36469
- toHistoryPersistedMessages(normalizeReadChatMessages(parsed)),
36470
- parsed.title,
36700
+ toHistoryPersistedMessages(normalizeReadChatMessages(validated)),
36701
+ validated.title,
36471
36702
  args?.targetSessionId,
36472
36703
  historySessionId
36473
36704
  );
36474
- return buildReadChatCommandResult(parsed, args);
36705
+ return buildReadChatCommandResult(validated, args);
36475
36706
  }
36476
36707
  }
36477
36708
  } catch (e) {
@@ -36526,15 +36757,16 @@ ${effect.notification.body || ""}`.trim();
36526
36757
  }
36527
36758
  }
36528
36759
  if (parsed && typeof parsed === "object") {
36529
- _log(`Webview OK: ${parsed.messages?.length || 0} msgs`);
36760
+ const validated = validateReadChatResultPayload(parsed, "webview read_chat");
36761
+ _log(`Webview OK: ${validated.messages?.length || 0} msgs`);
36530
36762
  h.historyWriter.appendNewMessages(
36531
36763
  provider?.type || getCurrentProviderType(h, "unknown_webview"),
36532
- toHistoryPersistedMessages(normalizeReadChatMessages(parsed)),
36533
- parsed.title,
36764
+ toHistoryPersistedMessages(normalizeReadChatMessages(validated)),
36765
+ validated.title,
36534
36766
  args?.targetSessionId,
36535
36767
  historySessionId
36536
36768
  );
36537
- return buildReadChatCommandResult(parsed, args);
36769
+ return buildReadChatCommandResult(validated, args);
36538
36770
  }
36539
36771
  }
36540
36772
  } catch (e) {
@@ -36555,25 +36787,26 @@ ${effect.notification.body || ""}`.trim();
36555
36787
  }
36556
36788
  }
36557
36789
  if (parsed && typeof parsed === "object" && parsed.messages?.length > 0) {
36558
- _log(`OK: ${parsed.messages?.length} msgs`);
36790
+ const validated = validateReadChatResultPayload(parsed, "ide read_chat");
36791
+ _log(`OK: ${validated.messages?.length} msgs`);
36559
36792
  traceProviderEvent(args, "provider", "ide.read_chat.success", {
36560
36793
  h,
36561
36794
  provider,
36562
36795
  payload: {
36563
36796
  method: "evaluate",
36564
36797
  result: evalResult.result,
36565
- parsed,
36566
- messageCount: Array.isArray(parsed.messages) ? parsed.messages.length : 0
36798
+ parsed: validated,
36799
+ messageCount: Array.isArray(validated.messages) ? validated.messages.length : 0
36567
36800
  }
36568
36801
  });
36569
36802
  h.historyWriter.appendNewMessages(
36570
36803
  provider?.type || getCurrentProviderType(h, "unknown_ide"),
36571
- toHistoryPersistedMessages(normalizeReadChatMessages(parsed)),
36572
- parsed.title,
36804
+ toHistoryPersistedMessages(normalizeReadChatMessages(validated)),
36805
+ validated.title,
36573
36806
  args?.targetSessionId,
36574
36807
  historySessionId
36575
36808
  );
36576
- return buildReadChatCommandResult(parsed, args);
36809
+ return buildReadChatCommandResult(validated, args);
36577
36810
  }
36578
36811
  }
36579
36812
  } catch (e) {
@@ -36591,11 +36824,12 @@ ${effect.notification.body || ""}`.trim();
36591
36824
  async function handleSendChat(h, args) {
36592
36825
  const input = getSendChatInputEnvelope(args);
36593
36826
  const text = input.textFallback;
36594
- if (!text) return { success: false, error: "text required" };
36827
+ const hasInput = input.parts.length > 0 || typeof text === "string" && text.trim().length > 0;
36828
+ if (!hasInput) return { success: false, error: "input required" };
36595
36829
  const _log = (msg) => LOG2.debug("Command", `[send_chat] ${msg}`);
36596
36830
  const provider = h.getProvider(args?.agentType);
36597
36831
  const transport = getTargetTransport(h, provider);
36598
- const dedupeKey = buildRecentSendKey(h, args, provider, text);
36832
+ const dedupeKey = buildRecentSendKey(h, args, provider, buildSendInputSignature(input));
36599
36833
  const _logSendSuccess = (method, targetAgent) => {
36600
36834
  return { success: true, sent: true, method, targetAgent };
36601
36835
  };
@@ -36603,11 +36837,26 @@ ${effect.notification.body || ""}`.trim();
36603
36837
  _log(`Suppressed duplicate send for ${dedupeKey}`);
36604
36838
  return { success: true, sent: false, deduplicated: true };
36605
36839
  }
36606
- if (isCliLikeTransport(transport)) {
36840
+ if (transport === "acp") {
36841
+ const target = getTargetInstance(h, args);
36842
+ if (!target || target.category !== "acp") {
36843
+ return { success: false, error: `ACP instance not found for ${provider?.type || args?.agentType || "unknown"}` };
36844
+ }
36845
+ try {
36846
+ assertProviderSupportsDeclaredInput(provider, input);
36847
+ target.onEvent("send_message", { input });
36848
+ return _logSendSuccess("acp-instance", target.type);
36849
+ } catch (e) {
36850
+ return { success: false, error: `acp send failed: ${e.message}` };
36851
+ }
36852
+ }
36853
+ if (transport === "pty") {
36607
36854
  const adapter = getTargetedCliAdapter(h, args, provider?.type);
36608
36855
  if (adapter) {
36609
36856
  _log(`${transport} adapter: ${adapter.cliType}`);
36610
36857
  try {
36858
+ assertTextOnlyInput(provider, input);
36859
+ if (!text) return { success: false, error: "text required for PTY send" };
36611
36860
  await adapter.sendMessage(text);
36612
36861
  return _logSendSuccess(`${transport}-adapter`, adapter.cliType);
36613
36862
  } catch (e) {
@@ -36615,6 +36864,8 @@ ${effect.notification.body || ""}`.trim();
36615
36864
  }
36616
36865
  }
36617
36866
  }
36867
+ assertTextOnlyInput(provider, input);
36868
+ if (!text) return { success: false, error: "text required" };
36618
36869
  if (isExtensionTransport(transport)) {
36619
36870
  _log(`Extension: ${provider?.type || "unknown_extension"}`);
36620
36871
  try {
@@ -37769,14 +38020,14 @@ ${effect.notification.body || ""}`.trim();
37769
38020
  }
37770
38021
  function buildControlScriptResult(scriptName, payload) {
37771
38022
  if (!payload || typeof payload !== "object") return {};
37772
- if (Array.isArray(payload.options) || Array.isArray(payload.models) || Array.isArray(payload.modes)) {
38023
+ if (Array.isArray(payload.options)) {
37773
38024
  return { controlResult: normalizeControlListResult(payload) };
37774
38025
  }
37775
38026
  const looksLikeValueMutation = /^set|^change/i.test(scriptName) || payload.currentValue !== void 0 || payload.value !== void 0;
37776
38027
  if (looksLikeValueMutation) {
37777
38028
  return { controlResult: normalizeControlSetResult(payload) };
37778
38029
  }
37779
- if (payload.ok !== void 0 || payload.success !== void 0 || Array.isArray(payload.effects)) {
38030
+ if (payload.ok !== void 0 || Array.isArray(payload.effects) || typeof payload.error === "string") {
37780
38031
  return { controlResult: normalizeControlInvokeResult(payload) };
37781
38032
  }
37782
38033
  return {};
@@ -38573,7 +38824,7 @@ ${effect.notification.body || ""}`.trim();
38573
38824
  }
38574
38825
  };
38575
38826
  var os12 = __toESM2(require("os"));
38576
- var path13 = __toESM2(require("path"));
38827
+ var path12 = __toESM2(require("path"));
38577
38828
  var crypto4 = __toESM2(require("crypto"));
38578
38829
  var import_chalk = __toESM2(require("chalk"));
38579
38830
  init_provider_cli_adapter();
@@ -38583,6 +38834,7 @@ ${effect.notification.body || ""}`.trim();
38583
38834
  var crypto3 = __toESM2(require("crypto"));
38584
38835
  var fs5 = __toESM2(require("fs"));
38585
38836
  var import_node_module = require("module");
38837
+ init_contracts();
38586
38838
  init_provider_cli_adapter();
38587
38839
  init_logger();
38588
38840
  init_chat_message_normalization();
@@ -38881,6 +39133,7 @@ ${effect.notification.body || ""}`.trim();
38881
39133
  onEvent(event, data) {
38882
39134
  if (event === "send_message") {
38883
39135
  const input = normalizeInputEnvelope(data);
39136
+ assertTextOnlyInput(this.provider, input);
38884
39137
  if (input.textFallback) {
38885
39138
  void this.adapter.sendMessage(input.textFallback).catch((e) => {
38886
39139
  LOG2.warn("CLI", `[${this.type}] send_message failed: ${e?.message || e}`);
@@ -39316,10 +39569,10 @@ ${effect.notification.body || ""}`.trim();
39316
39569
  }
39317
39570
  }
39318
39571
  };
39319
- var path12 = __toESM2(require("path"));
39320
39572
  var import_stream = require("stream");
39321
39573
  var import_child_process5 = require("child_process");
39322
39574
  var import_sdk = (init_acp(), __toCommonJS(acp_exports));
39575
+ init_contracts();
39323
39576
  init_chat_message_normalization();
39324
39577
  init_logger();
39325
39578
  function getPromptCapabilityFlags(agentCapabilities) {
@@ -39330,25 +39583,6 @@ ${effect.notification.body || ""}`.trim();
39330
39583
  embeddedContext: prompt.embeddedContext === true
39331
39584
  };
39332
39585
  }
39333
- function getResourceNameFromUri(uri, fallback) {
39334
- try {
39335
- if (uri.startsWith("file://")) {
39336
- return path12.basename(new URL(uri).pathname) || fallback;
39337
- }
39338
- return path12.basename(uri) || fallback;
39339
- } catch {
39340
- return fallback;
39341
- }
39342
- }
39343
- function inputPartToResourceLink(part, fallbackName) {
39344
- if (!part.uri) return null;
39345
- return {
39346
- type: "resource_link",
39347
- uri: part.uri,
39348
- name: getResourceNameFromUri(part.uri, fallbackName),
39349
- ...part.mimeType ? { mimeType: part.mimeType } : {}
39350
- };
39351
- }
39352
39586
  function appendPromptText(promptParts, text) {
39353
39587
  const normalized = typeof text === "string" ? text.trim() : "";
39354
39588
  if (!normalized) return;
@@ -39365,55 +39599,60 @@ ${effect.notification.body || ""}`.trim();
39365
39599
  continue;
39366
39600
  }
39367
39601
  if (part.type === "image") {
39368
- if (caps.image && part.data) {
39369
- promptParts.push({
39370
- type: "image",
39371
- data: part.data,
39372
- mimeType: part.mimeType,
39373
- ...part.uri ? { uri: part.uri } : {}
39374
- });
39375
- continue;
39602
+ if (!caps.image) {
39603
+ throw new Error("ACP agent does not support input type: image");
39604
+ }
39605
+ if (!part.data) {
39606
+ throw new Error("ACP image input requires inline image data");
39376
39607
  }
39377
- const fallback = inputPartToResourceLink(part, "image");
39378
- if (fallback) promptParts.push(fallback);
39379
- appendPromptText(promptParts, part.alt || (!part.uri ? `Attached image (${part.mimeType})` : void 0));
39608
+ promptParts.push({
39609
+ type: "image",
39610
+ data: part.data,
39611
+ mimeType: part.mimeType,
39612
+ ...part.uri ? { uri: part.uri } : {}
39613
+ });
39380
39614
  continue;
39381
39615
  }
39382
39616
  if (part.type === "audio") {
39383
- if (caps.audio && part.data) {
39384
- promptParts.push({
39385
- type: "audio",
39386
- data: part.data,
39387
- mimeType: part.mimeType
39388
- });
39389
- continue;
39617
+ if (!caps.audio) {
39618
+ throw new Error("ACP agent does not support input type: audio");
39619
+ }
39620
+ if (!part.data) {
39621
+ throw new Error("ACP audio input requires inline audio data");
39390
39622
  }
39391
- const fallback = inputPartToResourceLink(part, "audio");
39392
- if (fallback) promptParts.push(fallback);
39393
- appendPromptText(promptParts, part.transcript || (!part.uri ? `Attached audio (${part.mimeType})` : void 0));
39623
+ promptParts.push({
39624
+ type: "audio",
39625
+ data: part.data,
39626
+ mimeType: part.mimeType
39627
+ });
39394
39628
  continue;
39395
39629
  }
39396
39630
  if (part.type === "resource") {
39397
- if (caps.embeddedContext && (part.text || part.data)) {
39631
+ if (!caps.embeddedContext) {
39632
+ throw new Error("ACP agent does not support input type: resource");
39633
+ }
39634
+ if (part.text) {
39398
39635
  promptParts.push({
39399
39636
  type: "resource",
39400
- resource: part.text ? { uri: part.uri, text: part.text, mimeType: part.mimeType ?? null } : { uri: part.uri, blob: part.data || "", mimeType: part.mimeType ?? null }
39637
+ resource: { uri: part.uri, text: part.text, mimeType: part.mimeType ?? null }
39401
39638
  });
39402
39639
  continue;
39403
39640
  }
39404
- const fallback = inputPartToResourceLink(part, part.name || "resource");
39405
- if (fallback) promptParts.push(fallback);
39406
- appendPromptText(promptParts, part.text || (!part.uri && part.name ? part.name : void 0));
39407
- continue;
39641
+ if (part.data) {
39642
+ promptParts.push({
39643
+ type: "resource",
39644
+ resource: { uri: part.uri, blob: part.data, mimeType: part.mimeType ?? null }
39645
+ });
39646
+ continue;
39647
+ }
39648
+ throw new Error("ACP resource input requires embedded text or binary data");
39408
39649
  }
39409
39650
  if (part.type === "video") {
39410
- const fallback = inputPartToResourceLink(part, "video");
39411
- if (fallback) promptParts.push(fallback);
39412
- appendPromptText(promptParts, !part.uri ? `Attached video (${part.mimeType})` : void 0);
39651
+ throw new Error("ACP agent does not support input type: video");
39413
39652
  }
39414
39653
  }
39415
39654
  if (!promptParts.some((part) => part.type === "text") && input.textFallback) {
39416
- promptParts.unshift({ type: "text", text: input.textFallback });
39655
+ appendPromptText(promptParts, input.textFallback);
39417
39656
  }
39418
39657
  return promptParts;
39419
39658
  }
@@ -39544,6 +39783,7 @@ ${effect.notification.body || ""}`.trim();
39544
39783
  onEvent(event, data) {
39545
39784
  if (event === "send_message") {
39546
39785
  const input = normalizeInputEnvelope(data);
39786
+ assertProviderSupportsDeclaredInput(this.provider, input);
39547
39787
  const promptParts = buildAcpPromptParts(input, this.agentCapabilities);
39548
39788
  this.sendPrompt(input.textFallback, promptParts.length > 0 ? promptParts : void 0).catch(
39549
39789
  (e) => this.log.warn(`[${this.type}] sendPrompt error: ${e?.message}`)
@@ -40475,6 +40715,7 @@ ${rawInput}` : rawInput;
40475
40715
  return this.agentCapabilities;
40476
40716
  }
40477
40717
  };
40718
+ init_contracts();
40478
40719
  init_logger();
40479
40720
  function shouldRestoreHostedRuntime(record2, managerTag) {
40480
40721
  if (!managerTag) return true;
@@ -40737,7 +40978,7 @@ ${rawInput}` : rawInput;
40737
40978
  async startSession(cliType, workingDir, cliArgs, initialModel, options) {
40738
40979
  const trimmed = (workingDir || "").trim();
40739
40980
  if (!trimmed) throw new Error("working directory required");
40740
- const resolvedDir = trimmed.startsWith("~") ? trimmed.replace(/^~/, os12.homedir()) : path13.resolve(trimmed);
40981
+ const resolvedDir = trimmed.startsWith("~") ? trimmed.replace(/^~/, os12.homedir()) : path12.resolve(trimmed);
40741
40982
  const normalizedType = this.providerLoader.resolveAlias(cliType);
40742
40983
  const provider = this.providerLoader.getByAlias(cliType);
40743
40984
  const key = crypto4.randomUUID();
@@ -41196,6 +41437,12 @@ Run 'adhdev doctor' for detailed diagnostics.`
41196
41437
  const { adapter, key } = found;
41197
41438
  if (action === "send_chat") {
41198
41439
  const input = normalizeInputEnvelope(args?.input ? { input: args.input } : args);
41440
+ const provider = this.providerLoader.resolve(agentType) || this.providerLoader.getMeta(agentType);
41441
+ if (provider?.category === "acp") {
41442
+ assertProviderSupportsDeclaredInput(provider, input);
41443
+ } else {
41444
+ assertTextOnlyInput(provider, input);
41445
+ }
41199
41446
  const message = input.textFallback;
41200
41447
  if (!message) throw new Error("message required for send_chat");
41201
41448
  await adapter.sendMessage(message);
@@ -41216,12 +41463,13 @@ Run 'adhdev doctor' for detailed diagnostics.`
41216
41463
  var import_child_process6 = require("child_process");
41217
41464
  var net3 = __toESM2(require("net"));
41218
41465
  var os14 = __toESM2(require("os"));
41219
- var path15 = __toESM2(require("path"));
41220
- var fs6 = __toESM2(require("fs"));
41221
41466
  var path14 = __toESM2(require("path"));
41467
+ var fs6 = __toESM2(require("fs"));
41468
+ var path13 = __toESM2(require("path"));
41222
41469
  var os13 = __toESM2(require("os"));
41223
41470
  var chokidar = __toESM2((init_chokidar(), __toCommonJS(chokidar_exports)));
41224
41471
  init_logger();
41472
+ var VALID_CAPABILITY_MEDIA_TYPES = /* @__PURE__ */ new Set(["text", "image", "audio", "video", "resource"]);
41225
41473
  var KNOWN_PROVIDER_FIELDS = /* @__PURE__ */ new Set([
41226
41474
  "type",
41227
41475
  "name",
@@ -41272,6 +41520,7 @@ Run 'adhdev doctor' for detailed diagnostics.`
41272
41520
  "sendDelayMs",
41273
41521
  "sendKey",
41274
41522
  "submitStrategy",
41523
+ "timeouts",
41275
41524
  "disableUpstream"
41276
41525
  ]);
41277
41526
  var VALUE_CONTROL_TYPES = /* @__PURE__ */ new Set(["select", "toggle", "cycle", "slider"]);
@@ -41298,6 +41547,7 @@ Run 'adhdev doctor' for detailed diagnostics.`
41298
41547
  warnings.push("disableUpstream is deprecated in provider definitions; use machine-level provider source policy instead");
41299
41548
  }
41300
41549
  const category = provider.category;
41550
+ const controls = Array.isArray(provider.controls) ? provider.controls : [];
41301
41551
  if (category === "cli" || category === "acp") {
41302
41552
  const spawn4 = provider.spawn;
41303
41553
  const command = spawn4 && typeof spawn4 === "object" ? spawn4.command : void 0;
@@ -41315,11 +41565,61 @@ Run 'adhdev doctor' for detailed diagnostics.`
41315
41565
  if (category === "extension" && !provider.extensionId) {
41316
41566
  warnings.push("Extension providers should have extensionId");
41317
41567
  }
41318
- for (const control of Array.isArray(provider.controls) ? provider.controls : []) {
41568
+ validateCapabilities(provider, controls, errors);
41569
+ for (const control of controls) {
41319
41570
  validateControl(control, errors);
41320
41571
  }
41321
41572
  return { errors, warnings };
41322
41573
  }
41574
+ function validateCapabilities(provider, controls, errors) {
41575
+ const capabilities = provider.capabilities;
41576
+ if (provider.contractVersion === 2) {
41577
+ if (!capabilities || typeof capabilities !== "object") {
41578
+ errors.push("contractVersion 2 providers must declare capabilities");
41579
+ return;
41580
+ }
41581
+ }
41582
+ if (!capabilities || typeof capabilities !== "object") {
41583
+ return;
41584
+ }
41585
+ const input = capabilities.input;
41586
+ if (!input || typeof input !== "object") {
41587
+ errors.push("capabilities.input is required");
41588
+ } else {
41589
+ if (typeof input.multipart !== "boolean") {
41590
+ errors.push("capabilities.input.multipart must be boolean");
41591
+ }
41592
+ if (!Array.isArray(input.mediaTypes) || input.mediaTypes.length === 0) {
41593
+ errors.push("capabilities.input.mediaTypes must be a non-empty array");
41594
+ } else if (input.mediaTypes.some((type) => typeof type !== "string" || !VALID_CAPABILITY_MEDIA_TYPES.has(type))) {
41595
+ errors.push(`capabilities.input.mediaTypes must only include: ${Array.from(VALID_CAPABILITY_MEDIA_TYPES).join(", ")}`);
41596
+ }
41597
+ }
41598
+ const output = capabilities.output;
41599
+ if (!output || typeof output !== "object") {
41600
+ errors.push("capabilities.output is required");
41601
+ } else {
41602
+ if (typeof output.richContent !== "boolean") {
41603
+ errors.push("capabilities.output.richContent must be boolean");
41604
+ }
41605
+ if (!Array.isArray(output.mediaTypes) || output.mediaTypes.length === 0) {
41606
+ errors.push("capabilities.output.mediaTypes must be a non-empty array");
41607
+ } else if (output.mediaTypes.some((type) => typeof type !== "string" || !VALID_CAPABILITY_MEDIA_TYPES.has(type))) {
41608
+ errors.push(`capabilities.output.mediaTypes must only include: ${Array.from(VALID_CAPABILITY_MEDIA_TYPES).join(", ")}`);
41609
+ }
41610
+ }
41611
+ const controlCapabilities = capabilities.controls;
41612
+ if (!controlCapabilities || typeof controlCapabilities !== "object") {
41613
+ errors.push("capabilities.controls is required");
41614
+ return;
41615
+ }
41616
+ if (typeof controlCapabilities.typedResults !== "boolean") {
41617
+ errors.push("capabilities.controls.typedResults must be boolean");
41618
+ }
41619
+ if (controls.length > 0 && controlCapabilities.typedResults !== true) {
41620
+ errors.push("providers declaring controls must set capabilities.controls.typedResults=true");
41621
+ }
41622
+ }
41323
41623
  function validateControl(control, errors) {
41324
41624
  if (!control || typeof control !== "object") {
41325
41625
  errors.push("controls: each control must be an object");
@@ -41372,9 +41672,9 @@ Run 'adhdev doctor' for detailed diagnostics.`
41372
41672
  static META_FILE = ".meta.json";
41373
41673
  constructor(options) {
41374
41674
  this.logFn = options?.logFn || LOG2.forComponent("Provider").asLogFn();
41375
- this.defaultProvidersDir = path14.join(os13.homedir(), ".adhdev", "providers");
41675
+ this.defaultProvidersDir = path13.join(os13.homedir(), ".adhdev", "providers");
41376
41676
  this.userDir = this.defaultProvidersDir;
41377
- this.upstreamDir = path14.join(this.defaultProvidersDir, ".upstream");
41677
+ this.upstreamDir = path13.join(this.defaultProvidersDir, ".upstream");
41378
41678
  this.disableUpstream = false;
41379
41679
  this.applySourceConfig({
41380
41680
  userDir: options?.userDir,
@@ -41422,7 +41722,7 @@ Run 'adhdev doctor' for detailed diagnostics.`
41422
41722
  }
41423
41723
  this.sourceMode = nextSourceMode;
41424
41724
  this.userDir = this.explicitProviderDir || this.defaultProvidersDir;
41425
- this.upstreamDir = path14.join(this.defaultProvidersDir, ".upstream");
41725
+ this.upstreamDir = path13.join(this.defaultProvidersDir, ".upstream");
41426
41726
  this.disableUpstream = this.sourceMode === "no-upstream";
41427
41727
  if (this.explicitProviderDir) {
41428
41728
  this.log(`Config 'providerDir' applied: ${this.userDir}`);
@@ -41436,7 +41736,7 @@ Run 'adhdev doctor' for detailed diagnostics.`
41436
41736
  * Canonical provider directory shape for a given root.
41437
41737
  */
41438
41738
  getProviderDir(root, category, type) {
41439
- return path14.join(root, category, type);
41739
+ return path13.join(root, category, type);
41440
41740
  }
41441
41741
  /**
41442
41742
  * Canonical user override directory for a provider.
@@ -41463,7 +41763,7 @@ Run 'adhdev doctor' for detailed diagnostics.`
41463
41763
  resolveProviderFile(type, ...segments) {
41464
41764
  const dir = this.findProviderDirInternal(type);
41465
41765
  if (!dir) return null;
41466
- return path14.join(dir, ...segments);
41766
+ return path13.join(dir, ...segments);
41467
41767
  }
41468
41768
  /**
41469
41769
  * Load all providers (3-tier priority)
@@ -41502,7 +41802,7 @@ Run 'adhdev doctor' for detailed diagnostics.`
41502
41802
  if (!fs6.existsSync(this.upstreamDir)) return false;
41503
41803
  try {
41504
41804
  return fs6.readdirSync(this.upstreamDir).some(
41505
- (d) => fs6.statSync(path14.join(this.upstreamDir, d)).isDirectory()
41805
+ (d) => fs6.statSync(path13.join(this.upstreamDir, d)).isDirectory()
41506
41806
  );
41507
41807
  } catch {
41508
41808
  return false;
@@ -41817,8 +42117,8 @@ Run 'adhdev doctor' for detailed diagnostics.`
41817
42117
  resolved._resolvedScriptDir = entry.scriptDir;
41818
42118
  resolved._resolvedScriptsSource = `compatibility:${entry.ideVersion}`;
41819
42119
  if (providerDir) {
41820
- const fullDir = path14.join(providerDir, entry.scriptDir);
41821
- resolved._resolvedScriptsPath = fs6.existsSync(path14.join(fullDir, "scripts.js")) ? path14.join(fullDir, "scripts.js") : fullDir;
42120
+ const fullDir = path13.join(providerDir, entry.scriptDir);
42121
+ resolved._resolvedScriptsPath = fs6.existsSync(path13.join(fullDir, "scripts.js")) ? path13.join(fullDir, "scripts.js") : fullDir;
41822
42122
  }
41823
42123
  matched = true;
41824
42124
  }
@@ -41833,8 +42133,8 @@ Run 'adhdev doctor' for detailed diagnostics.`
41833
42133
  resolved._resolvedScriptDir = base.defaultScriptDir;
41834
42134
  resolved._resolvedScriptsSource = "defaultScriptDir:version_miss";
41835
42135
  if (providerDir) {
41836
- const fullDir = path14.join(providerDir, base.defaultScriptDir);
41837
- resolved._resolvedScriptsPath = fs6.existsSync(path14.join(fullDir, "scripts.js")) ? path14.join(fullDir, "scripts.js") : fullDir;
42136
+ const fullDir = path13.join(providerDir, base.defaultScriptDir);
42137
+ resolved._resolvedScriptsPath = fs6.existsSync(path13.join(fullDir, "scripts.js")) ? path13.join(fullDir, "scripts.js") : fullDir;
41838
42138
  }
41839
42139
  }
41840
42140
  resolved._versionWarning = `Version ${currentVersion} not in compatibility matrix. Using default scripts.`;
@@ -41851,8 +42151,8 @@ Run 'adhdev doctor' for detailed diagnostics.`
41851
42151
  resolved._resolvedScriptDir = dirOverride;
41852
42152
  resolved._resolvedScriptsSource = `versions:${range}`;
41853
42153
  if (providerDir) {
41854
- const fullDir = path14.join(providerDir, dirOverride);
41855
- resolved._resolvedScriptsPath = fs6.existsSync(path14.join(fullDir, "scripts.js")) ? path14.join(fullDir, "scripts.js") : fullDir;
42154
+ const fullDir = path13.join(providerDir, dirOverride);
42155
+ resolved._resolvedScriptsPath = fs6.existsSync(path13.join(fullDir, "scripts.js")) ? path13.join(fullDir, "scripts.js") : fullDir;
41856
42156
  }
41857
42157
  }
41858
42158
  } else if (override.scripts) {
@@ -41868,8 +42168,8 @@ Run 'adhdev doctor' for detailed diagnostics.`
41868
42168
  resolved._resolvedScriptDir = base.defaultScriptDir;
41869
42169
  resolved._resolvedScriptsSource = "defaultScriptDir:no_version";
41870
42170
  if (providerDir) {
41871
- const fullDir = path14.join(providerDir, base.defaultScriptDir);
41872
- resolved._resolvedScriptsPath = fs6.existsSync(path14.join(fullDir, "scripts.js")) ? path14.join(fullDir, "scripts.js") : fullDir;
42171
+ const fullDir = path13.join(providerDir, base.defaultScriptDir);
42172
+ resolved._resolvedScriptsPath = fs6.existsSync(path13.join(fullDir, "scripts.js")) ? path13.join(fullDir, "scripts.js") : fullDir;
41873
42173
  }
41874
42174
  }
41875
42175
  }
@@ -41894,14 +42194,14 @@ Run 'adhdev doctor' for detailed diagnostics.`
41894
42194
  this.log(` [loadScriptsFromDir] ${type}: providerDir not found`);
41895
42195
  return null;
41896
42196
  }
41897
- const dir = path14.join(providerDir, scriptDir);
42197
+ const dir = path13.join(providerDir, scriptDir);
41898
42198
  if (!fs6.existsSync(dir)) {
41899
42199
  this.log(` [loadScriptsFromDir] ${type}: dir not found: ${dir}`);
41900
42200
  return null;
41901
42201
  }
41902
42202
  const cached2 = this.scriptsCache.get(dir);
41903
42203
  if (cached2) return cached2;
41904
- const scriptsJs = path14.join(dir, "scripts.js");
42204
+ const scriptsJs = path13.join(dir, "scripts.js");
41905
42205
  if (fs6.existsSync(scriptsJs)) {
41906
42206
  try {
41907
42207
  delete require.cache[require.resolve(scriptsJs)];
@@ -41943,7 +42243,7 @@ Run 'adhdev doctor' for detailed diagnostics.`
41943
42243
  return;
41944
42244
  }
41945
42245
  if (filePath.endsWith(".js") || filePath.endsWith(".json")) {
41946
- this.log(`File changed: ${path14.basename(filePath)}, reloading...`);
42246
+ this.log(`File changed: ${path13.basename(filePath)}, reloading...`);
41947
42247
  this.reload();
41948
42248
  }
41949
42249
  };
@@ -41998,7 +42298,7 @@ Run 'adhdev doctor' for detailed diagnostics.`
41998
42298
  }
41999
42299
  const https = require("https");
42000
42300
  const { execSync: execSync7 } = require("child_process");
42001
- const metaPath = path14.join(this.upstreamDir, _ProviderLoader.META_FILE);
42301
+ const metaPath = path13.join(this.upstreamDir, _ProviderLoader.META_FILE);
42002
42302
  let prevEtag = "";
42003
42303
  let prevTimestamp = 0;
42004
42304
  try {
@@ -42058,17 +42358,17 @@ Run 'adhdev doctor' for detailed diagnostics.`
42058
42358
  return { updated: false };
42059
42359
  }
42060
42360
  this.log("Downloading latest providers from GitHub...");
42061
- const tmpTar = path14.join(os13.tmpdir(), `adhdev-providers-${Date.now()}.tar.gz`);
42062
- const tmpExtract = path14.join(os13.tmpdir(), `adhdev-providers-extract-${Date.now()}`);
42361
+ const tmpTar = path13.join(os13.tmpdir(), `adhdev-providers-${Date.now()}.tar.gz`);
42362
+ const tmpExtract = path13.join(os13.tmpdir(), `adhdev-providers-extract-${Date.now()}`);
42063
42363
  await this.downloadFile(_ProviderLoader.GITHUB_TARBALL_URL, tmpTar);
42064
42364
  fs6.mkdirSync(tmpExtract, { recursive: true });
42065
42365
  execSync7(`tar -xzf "${tmpTar}" -C "${tmpExtract}"`, { timeout: 3e4 });
42066
42366
  const extracted = fs6.readdirSync(tmpExtract);
42067
42367
  const rootDir = extracted.find(
42068
- (d) => fs6.statSync(path14.join(tmpExtract, d)).isDirectory() && d.startsWith("adhdev-providers")
42368
+ (d) => fs6.statSync(path13.join(tmpExtract, d)).isDirectory() && d.startsWith("adhdev-providers")
42069
42369
  );
42070
42370
  if (!rootDir) throw new Error("Unexpected tarball structure");
42071
- const sourceDir = path14.join(tmpExtract, rootDir);
42371
+ const sourceDir = path13.join(tmpExtract, rootDir);
42072
42372
  const backupDir = this.upstreamDir + ".bak";
42073
42373
  if (fs6.existsSync(this.upstreamDir)) {
42074
42374
  if (fs6.existsSync(backupDir)) fs6.rmSync(backupDir, { recursive: true, force: true });
@@ -42143,8 +42443,8 @@ Run 'adhdev doctor' for detailed diagnostics.`
42143
42443
  copyDirRecursive(src, dest) {
42144
42444
  fs6.mkdirSync(dest, { recursive: true });
42145
42445
  for (const entry of fs6.readdirSync(src, { withFileTypes: true })) {
42146
- const srcPath = path14.join(src, entry.name);
42147
- const destPath = path14.join(dest, entry.name);
42446
+ const srcPath = path13.join(src, entry.name);
42447
+ const destPath = path13.join(dest, entry.name);
42148
42448
  if (entry.isDirectory()) {
42149
42449
  this.copyDirRecursive(srcPath, destPath);
42150
42450
  } else {
@@ -42155,7 +42455,7 @@ Run 'adhdev doctor' for detailed diagnostics.`
42155
42455
  /** .meta.json save */
42156
42456
  writeMeta(metaPath, etag, timestamp) {
42157
42457
  try {
42158
- fs6.mkdirSync(path14.dirname(metaPath), { recursive: true });
42458
+ fs6.mkdirSync(path13.dirname(metaPath), { recursive: true });
42159
42459
  fs6.writeFileSync(metaPath, JSON.stringify({
42160
42460
  etag,
42161
42461
  timestamp,
@@ -42172,7 +42472,7 @@ Run 'adhdev doctor' for detailed diagnostics.`
42172
42472
  const scan = (d) => {
42173
42473
  try {
42174
42474
  for (const entry of fs6.readdirSync(d, { withFileTypes: true })) {
42175
- if (entry.isDirectory()) scan(path14.join(d, entry.name));
42475
+ if (entry.isDirectory()) scan(path13.join(d, entry.name));
42176
42476
  else if (entry.name === "provider.json") count++;
42177
42477
  }
42178
42478
  } catch {
@@ -42357,17 +42657,17 @@ Run 'adhdev doctor' for detailed diagnostics.`
42357
42657
  for (const root of searchRoots) {
42358
42658
  if (!fs6.existsSync(root)) continue;
42359
42659
  const candidate = this.getProviderDir(root, cat, type);
42360
- if (fs6.existsSync(path14.join(candidate, "provider.json"))) return candidate;
42361
- const catDir = path14.join(root, cat);
42660
+ if (fs6.existsSync(path13.join(candidate, "provider.json"))) return candidate;
42661
+ const catDir = path13.join(root, cat);
42362
42662
  if (fs6.existsSync(catDir)) {
42363
42663
  try {
42364
42664
  for (const entry of fs6.readdirSync(catDir, { withFileTypes: true })) {
42365
42665
  if (!entry.isDirectory()) continue;
42366
- const jsonPath = path14.join(catDir, entry.name, "provider.json");
42666
+ const jsonPath = path13.join(catDir, entry.name, "provider.json");
42367
42667
  if (fs6.existsSync(jsonPath)) {
42368
42668
  try {
42369
42669
  const data = JSON.parse(fs6.readFileSync(jsonPath, "utf-8"));
42370
- if (data.type === type) return path14.join(catDir, entry.name);
42670
+ if (data.type === type) return path13.join(catDir, entry.name);
42371
42671
  } catch {
42372
42672
  }
42373
42673
  }
@@ -42384,7 +42684,7 @@ Run 'adhdev doctor' for detailed diagnostics.`
42384
42684
  * (template substitution is NOT applied here — scripts.js handles that)
42385
42685
  */
42386
42686
  buildScriptWrappersFromDir(dir) {
42387
- const scriptsJs = path14.join(dir, "scripts.js");
42687
+ const scriptsJs = path13.join(dir, "scripts.js");
42388
42688
  if (fs6.existsSync(scriptsJs)) {
42389
42689
  try {
42390
42690
  delete require.cache[require.resolve(scriptsJs)];
@@ -42398,7 +42698,7 @@ Run 'adhdev doctor' for detailed diagnostics.`
42398
42698
  for (const file2 of fs6.readdirSync(dir)) {
42399
42699
  if (!file2.endsWith(".js")) continue;
42400
42700
  const scriptName = toCamel(file2.replace(".js", ""));
42401
- const filePath = path14.join(dir, file2);
42701
+ const filePath = path13.join(dir, file2);
42402
42702
  result[scriptName] = (...args) => {
42403
42703
  try {
42404
42704
  let content = fs6.readFileSync(filePath, "utf-8");
@@ -42458,7 +42758,7 @@ Run 'adhdev doctor' for detailed diagnostics.`
42458
42758
  }
42459
42759
  const hasJson = entries.some((e) => e.name === "provider.json");
42460
42760
  if (hasJson) {
42461
- const jsonPath = path14.join(d, "provider.json");
42761
+ const jsonPath = path13.join(d, "provider.json");
42462
42762
  try {
42463
42763
  const raw = fs6.readFileSync(jsonPath, "utf-8");
42464
42764
  const mod = JSON.parse(raw);
@@ -42479,7 +42779,7 @@ Run 'adhdev doctor' for detailed diagnostics.`
42479
42779
  this.log(`\u26A0 Invalid provider at ${jsonPath}: ${validation.errors.join("; ")}`);
42480
42780
  } else {
42481
42781
  const hasCompatibility = Array.isArray(normalizedProvider.compatibility);
42482
- const scriptsPath = path14.join(d, "scripts.js");
42782
+ const scriptsPath = path13.join(d, "scripts.js");
42483
42783
  if (!hasCompatibility && fs6.existsSync(scriptsPath)) {
42484
42784
  try {
42485
42785
  delete require.cache[require.resolve(scriptsPath)];
@@ -42505,7 +42805,7 @@ Run 'adhdev doctor' for detailed diagnostics.`
42505
42805
  if (!entry.isDirectory()) continue;
42506
42806
  if (entry.name.startsWith("_") || entry.name.startsWith(".")) continue;
42507
42807
  if (excludeDirs && d === dir && excludeDirs.includes(entry.name)) continue;
42508
- scan(path14.join(d, entry.name));
42808
+ scan(path13.join(d, entry.name));
42509
42809
  }
42510
42810
  }
42511
42811
  };
@@ -42761,8 +43061,8 @@ Run 'adhdev doctor' for detailed diagnostics.`
42761
43061
  const appNameMap = getMacAppIdentifiers();
42762
43062
  const appName = appNameMap[ideId];
42763
43063
  if (appName) {
42764
- const storagePath = path15.join(
42765
- process.env.APPDATA || path15.join(os14.homedir(), "AppData", "Roaming"),
43064
+ const storagePath = path14.join(
43065
+ process.env.APPDATA || path14.join(os14.homedir(), "AppData", "Roaming"),
42766
43066
  appName,
42767
43067
  "storage.json"
42768
43068
  );
@@ -42936,9 +43236,9 @@ Run 'adhdev doctor' for detailed diagnostics.`
42936
43236
  init_config();
42937
43237
  init_logger();
42938
43238
  var fs7 = __toESM2(require("fs"));
42939
- var path16 = __toESM2(require("path"));
43239
+ var path15 = __toESM2(require("path"));
42940
43240
  var os15 = __toESM2(require("os"));
42941
- var LOG_DIR2 = process.platform === "win32" ? path16.join(process.env.LOCALAPPDATA || process.env.APPDATA || path16.join(os15.homedir(), "AppData", "Local"), "adhdev", "logs") : process.platform === "darwin" ? path16.join(os15.homedir(), "Library", "Logs", "adhdev") : path16.join(os15.homedir(), ".local", "share", "adhdev", "logs");
43241
+ var LOG_DIR2 = process.platform === "win32" ? path15.join(process.env.LOCALAPPDATA || process.env.APPDATA || path15.join(os15.homedir(), "AppData", "Local"), "adhdev", "logs") : process.platform === "darwin" ? path15.join(os15.homedir(), "Library", "Logs", "adhdev") : path15.join(os15.homedir(), ".local", "share", "adhdev", "logs");
42942
43242
  var MAX_FILE_SIZE = 5 * 1024 * 1024;
42943
43243
  var MAX_DAYS = 7;
42944
43244
  try {
@@ -42976,13 +43276,13 @@ Run 'adhdev doctor' for detailed diagnostics.`
42976
43276
  return (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
42977
43277
  }
42978
43278
  var currentDate2 = getDateStr2();
42979
- var currentFile = path16.join(LOG_DIR2, `commands-${currentDate2}.jsonl`);
43279
+ var currentFile = path15.join(LOG_DIR2, `commands-${currentDate2}.jsonl`);
42980
43280
  var writeCount2 = 0;
42981
43281
  function checkRotation() {
42982
43282
  const today = getDateStr2();
42983
43283
  if (today !== currentDate2) {
42984
43284
  currentDate2 = today;
42985
- currentFile = path16.join(LOG_DIR2, `commands-${currentDate2}.jsonl`);
43285
+ currentFile = path15.join(LOG_DIR2, `commands-${currentDate2}.jsonl`);
42986
43286
  cleanOldFiles();
42987
43287
  }
42988
43288
  }
@@ -42996,7 +43296,7 @@ Run 'adhdev doctor' for detailed diagnostics.`
42996
43296
  const dateMatch = file2.match(/commands-(\d{4}-\d{2}-\d{2})/);
42997
43297
  if (dateMatch && dateMatch[1] < cutoffStr) {
42998
43298
  try {
42999
- fs7.unlinkSync(path16.join(LOG_DIR2, file2));
43299
+ fs7.unlinkSync(path15.join(LOG_DIR2, file2));
43000
43300
  } catch {
43001
43301
  }
43002
43302
  }
@@ -43401,13 +43701,13 @@ Run 'adhdev doctor' for detailed diagnostics.`
43401
43701
  var import_child_process8 = require("child_process");
43402
43702
  var fs8 = __toESM2(require("fs"));
43403
43703
  var os17 = __toESM2(require("os"));
43404
- var path17 = __toESM2(require("path"));
43704
+ var path16 = __toESM2(require("path"));
43405
43705
  var UPGRADE_HELPER_ENV = "ADHDEV_DAEMON_UPGRADE_HELPER";
43406
43706
  function getUpgradeLogPath() {
43407
43707
  const home = os17.homedir();
43408
- const dir = path17.join(home, ".adhdev");
43708
+ const dir = path16.join(home, ".adhdev");
43409
43709
  fs8.mkdirSync(dir, { recursive: true });
43410
- return path17.join(dir, "daemon-upgrade.log");
43710
+ return path16.join(dir, "daemon-upgrade.log");
43411
43711
  }
43412
43712
  function appendUpgradeLog(message) {
43413
43713
  const line = `[${(/* @__PURE__ */ new Date()).toISOString()}] ${message}
@@ -43447,7 +43747,7 @@ Run 'adhdev doctor' for detailed diagnostics.`
43447
43747
  }
43448
43748
  }
43449
43749
  function stopSessionHostProcesses(appName) {
43450
- const pidFile = path17.join(os17.homedir(), ".adhdev", `${appName}-session-host.pid`);
43750
+ const pidFile = path16.join(os17.homedir(), ".adhdev", `${appName}-session-host.pid`);
43451
43751
  try {
43452
43752
  if (fs8.existsSync(pidFile)) {
43453
43753
  const pid = Number.parseInt(fs8.readFileSync(pidFile, "utf8").trim(), 10);
@@ -43476,7 +43776,7 @@ Run 'adhdev doctor' for detailed diagnostics.`
43476
43776
  }
43477
43777
  }
43478
43778
  function removeDaemonPidFile() {
43479
- const pidFile = path17.join(os17.homedir(), ".adhdev", "daemon.pid");
43779
+ const pidFile = path16.join(os17.homedir(), ".adhdev", "daemon.pid");
43480
43780
  try {
43481
43781
  fs8.unlinkSync(pidFile);
43482
43782
  } catch {
@@ -43487,7 +43787,7 @@ Run 'adhdev doctor' for detailed diagnostics.`
43487
43787
  const npmRoot = (0, import_child_process7.execFileSync)(getNpmExecutable(), ["root", "-g"], { encoding: "utf8", ...npmExecOpts }).trim();
43488
43788
  if (!npmRoot) return;
43489
43789
  const npmPrefix = (0, import_child_process7.execFileSync)(getNpmExecutable(), ["prefix", "-g"], { encoding: "utf8", ...npmExecOpts }).trim();
43490
- const binDir = process.platform === "win32" ? npmPrefix : path17.join(npmPrefix, "bin");
43790
+ const binDir = process.platform === "win32" ? npmPrefix : path16.join(npmPrefix, "bin");
43491
43791
  const packageBaseName = pkgName.startsWith("@") ? pkgName.split("/")[1] : pkgName;
43492
43792
  const binNames = /* @__PURE__ */ new Set([packageBaseName]);
43493
43793
  if (pkgName === "@adhdev/daemon-standalone") {
@@ -43495,25 +43795,25 @@ Run 'adhdev doctor' for detailed diagnostics.`
43495
43795
  }
43496
43796
  if (pkgName.startsWith("@")) {
43497
43797
  const [scope, name] = pkgName.split("/");
43498
- const scopeDir = path17.join(npmRoot, scope);
43798
+ const scopeDir = path16.join(npmRoot, scope);
43499
43799
  if (!fs8.existsSync(scopeDir)) return;
43500
43800
  for (const entry of fs8.readdirSync(scopeDir)) {
43501
43801
  if (!entry.startsWith(`.${name}-`)) continue;
43502
- fs8.rmSync(path17.join(scopeDir, entry), { recursive: true, force: true });
43503
- appendUpgradeLog(`Removed stale scoped staging dir: ${path17.join(scopeDir, entry)}`);
43802
+ fs8.rmSync(path16.join(scopeDir, entry), { recursive: true, force: true });
43803
+ appendUpgradeLog(`Removed stale scoped staging dir: ${path16.join(scopeDir, entry)}`);
43504
43804
  }
43505
43805
  } else {
43506
43806
  for (const entry of fs8.readdirSync(npmRoot)) {
43507
43807
  if (!entry.startsWith(`.${pkgName}-`)) continue;
43508
- fs8.rmSync(path17.join(npmRoot, entry), { recursive: true, force: true });
43509
- appendUpgradeLog(`Removed stale staging dir: ${path17.join(npmRoot, entry)}`);
43808
+ fs8.rmSync(path16.join(npmRoot, entry), { recursive: true, force: true });
43809
+ appendUpgradeLog(`Removed stale staging dir: ${path16.join(npmRoot, entry)}`);
43510
43810
  }
43511
43811
  }
43512
43812
  if (fs8.existsSync(binDir)) {
43513
43813
  for (const entry of fs8.readdirSync(binDir)) {
43514
43814
  if (![...binNames].some((name) => entry.startsWith(`.${name}-`))) continue;
43515
- fs8.rmSync(path17.join(binDir, entry), { recursive: true, force: true });
43516
- appendUpgradeLog(`Removed stale bin staging entry: ${path17.join(binDir, entry)}`);
43815
+ fs8.rmSync(path16.join(binDir, entry), { recursive: true, force: true });
43816
+ appendUpgradeLog(`Removed stale bin staging entry: ${path16.join(binDir, entry)}`);
43517
43817
  }
43518
43818
  }
43519
43819
  }
@@ -44533,6 +44833,7 @@ Run 'adhdev doctor' for detailed diagnostics.`
44533
44833
  init_logger();
44534
44834
  var DEFAULT_DAEMON_PORT = 19222;
44535
44835
  var DAEMON_WS_PATH = "/ipc";
44836
+ init_read_chat_contract();
44536
44837
  init_chat_message_normalization();
44537
44838
  var ProviderStreamAdapter = class {
44538
44839
  agentType;
@@ -44638,26 +44939,29 @@ Run 'adhdev doctor' for detailed diagnostics.`
44638
44939
  }
44639
44940
  return state2;
44640
44941
  }
44942
+ const validated = validateReadChatResultPayload(data, `${this.agentType} readChat`);
44943
+ const validatedStatus = validated.status;
44944
+ const streamStatus = validatedStatus === "generating" || validatedStatus === "long_generating" ? "streaming" : validatedStatus;
44641
44945
  const state = {
44642
44946
  agentType: this.agentType,
44643
44947
  agentName: this.agentName,
44644
44948
  extensionId: this.extensionId,
44645
- status: data.status || "idle",
44646
- messages: normalizeChatMessages(Array.isArray(data.messages) ? data.messages : []),
44647
- inputContent: data.inputContent || "",
44648
- activeModal: data.activeModal
44949
+ status: streamStatus,
44950
+ messages: normalizeChatMessages(validated.messages),
44951
+ inputContent: typeof validated.inputContent === "string" ? validated.inputContent : "",
44952
+ ...validated.activeModal ? { activeModal: validated.activeModal } : {}
44649
44953
  };
44650
- if (typeof data.title === "string" && data.title.trim()) {
44651
- state.title = data.title.trim();
44954
+ if (typeof validated.title === "string" && validated.title.trim()) {
44955
+ state.title = validated.title.trim();
44652
44956
  }
44653
- const controlValues = extractProviderControlValues(this.provider.controls, data);
44957
+ const controlValues = extractProviderControlValues(this.provider.controls, validated);
44654
44958
  const surface = resolveProviderStateSurface({
44655
44959
  controlValues,
44656
- summaryMetadata: data.summaryMetadata
44960
+ summaryMetadata: validated.summaryMetadata
44657
44961
  });
44658
44962
  if (surface.controlValues) state.controlValues = surface.controlValues;
44659
44963
  if (surface.summaryMetadata) state.summaryMetadata = surface.summaryMetadata;
44660
- const effects = normalizeProviderEffects(data);
44964
+ const effects = normalizeProviderEffects(validated);
44661
44965
  if (effects.length > 0) state.effects = effects;
44662
44966
  if (state.messages.length > 0) {
44663
44967
  this.lastSuccessState = state;
@@ -45486,13 +45790,14 @@ Run 'adhdev doctor' for detailed diagnostics.`
45486
45790
  this.eventListeners = [];
45487
45791
  }
45488
45792
  };
45793
+ init_io_contracts();
45489
45794
  init_chat_message_normalization();
45490
45795
  var fs10 = __toESM2(require("fs"));
45491
- var path18 = __toESM2(require("path"));
45796
+ var path17 = __toESM2(require("path"));
45492
45797
  var os18 = __toESM2(require("os"));
45493
45798
  var import_child_process9 = require("child_process");
45494
45799
  var import_os3 = require("os");
45495
- var ARCHIVE_PATH = path18.join(os18.homedir(), ".adhdev", "version-history.json");
45800
+ var ARCHIVE_PATH = path17.join(os18.homedir(), ".adhdev", "version-history.json");
45496
45801
  var MAX_ENTRIES_PER_PROVIDER = 20;
45497
45802
  var VersionArchive = class {
45498
45803
  history = {};
@@ -45539,7 +45844,7 @@ Run 'adhdev doctor' for detailed diagnostics.`
45539
45844
  }
45540
45845
  save() {
45541
45846
  try {
45542
- fs10.mkdirSync(path18.dirname(ARCHIVE_PATH), { recursive: true });
45847
+ fs10.mkdirSync(path17.dirname(ARCHIVE_PATH), { recursive: true });
45543
45848
  fs10.writeFileSync(ARCHIVE_PATH, JSON.stringify(this.history, null, 2));
45544
45849
  } catch {
45545
45850
  }
@@ -45596,7 +45901,7 @@ Run 'adhdev doctor' for detailed diagnostics.`
45596
45901
  for (const p of paths) {
45597
45902
  if (p.includes("*")) {
45598
45903
  const home = os18.homedir();
45599
- const resolved = p.replace(/\*/g, home.split(path18.sep).pop() || "");
45904
+ const resolved = p.replace(/\*/g, home.split(path17.sep).pop() || "");
45600
45905
  if (fs10.existsSync(resolved)) return resolved;
45601
45906
  } else {
45602
45907
  if (fs10.existsSync(p)) return p;
@@ -45606,7 +45911,7 @@ Run 'adhdev doctor' for detailed diagnostics.`
45606
45911
  }
45607
45912
  function getMacAppVersion(appPath) {
45608
45913
  if ((0, import_os3.platform)() !== "darwin" || !appPath.endsWith(".app")) return null;
45609
- const plistPath = path18.join(appPath, "Contents", "Info.plist");
45914
+ const plistPath = path17.join(appPath, "Contents", "Info.plist");
45610
45915
  if (!fs10.existsSync(plistPath)) return null;
45611
45916
  const raw = runCommand(`/usr/libexec/PlistBuddy -c "Print CFBundleShortVersionString" "${plistPath}"`);
45612
45917
  return raw || null;
@@ -45632,7 +45937,7 @@ Run 'adhdev doctor' for detailed diagnostics.`
45632
45937
  const cliBin = provider.cli ? findBinary2(provider.cli) : null;
45633
45938
  let resolvedBin = cliBin;
45634
45939
  if (!resolvedBin && appPath && currentOs === "darwin") {
45635
- const bundled = path18.join(appPath, "Contents", "Resources", "app", "bin", provider.cli || "");
45940
+ const bundled = path17.join(appPath, "Contents", "Resources", "app", "bin", provider.cli || "");
45636
45941
  if (provider.cli && fs10.existsSync(bundled)) resolvedBin = bundled;
45637
45942
  }
45638
45943
  info.installed = !!(appPath || resolvedBin);
@@ -45671,7 +45976,7 @@ Run 'adhdev doctor' for detailed diagnostics.`
45671
45976
  }
45672
45977
  var http2 = __toESM2(require("http"));
45673
45978
  var fs14 = __toESM2(require("fs"));
45674
- var path222 = __toESM2(require("path"));
45979
+ var path21 = __toESM2(require("path"));
45675
45980
  init_config();
45676
45981
  function generateFiles(type, name, category, opts = {}) {
45677
45982
  const { cdpPorts, cli, processName, installPath, binary, extensionId, version: version2 = "0.1" } = opts;
@@ -46009,7 +46314,7 @@ async (params) => {
46009
46314
  }
46010
46315
  init_logger();
46011
46316
  var fs11 = __toESM2(require("fs"));
46012
- var path19 = __toESM2(require("path"));
46317
+ var path18 = __toESM2(require("path"));
46013
46318
  init_logger();
46014
46319
  async function handleCdpEvaluate(ctx, req, res) {
46015
46320
  const body = await ctx.readBody(req);
@@ -46188,17 +46493,17 @@ async (params) => {
46188
46493
  return;
46189
46494
  }
46190
46495
  let scriptsPath = "";
46191
- const directScripts = path19.join(dir, "scripts.js");
46496
+ const directScripts = path18.join(dir, "scripts.js");
46192
46497
  if (fs11.existsSync(directScripts)) {
46193
46498
  scriptsPath = directScripts;
46194
46499
  } else {
46195
- const scriptsDir = path19.join(dir, "scripts");
46500
+ const scriptsDir = path18.join(dir, "scripts");
46196
46501
  if (fs11.existsSync(scriptsDir)) {
46197
46502
  const versions = fs11.readdirSync(scriptsDir).filter((d) => {
46198
- return fs11.statSync(path19.join(scriptsDir, d)).isDirectory();
46503
+ return fs11.statSync(path18.join(scriptsDir, d)).isDirectory();
46199
46504
  }).sort().reverse();
46200
46505
  for (const ver of versions) {
46201
- const p = path19.join(scriptsDir, ver, "scripts.js");
46506
+ const p = path18.join(scriptsDir, ver, "scripts.js");
46202
46507
  if (fs11.existsSync(p)) {
46203
46508
  scriptsPath = p;
46204
46509
  break;
@@ -47025,7 +47330,7 @@ async (params) => {
47025
47330
  }
47026
47331
  }
47027
47332
  var fs12 = __toESM2(require("fs"));
47028
- var path20 = __toESM2(require("path"));
47333
+ var path19 = __toESM2(require("path"));
47029
47334
  function slugifyFixtureName(value) {
47030
47335
  const normalized = String(value || "").trim().toLowerCase().replace(/[^a-z0-9._-]+/g, "-").replace(/^-+|-+$/g, "");
47031
47336
  return normalized || `fixture-${Date.now()}`;
@@ -47035,11 +47340,11 @@ async (params) => {
47035
47340
  if (!providerDir) {
47036
47341
  throw new Error(`Provider directory not found for '${type}'`);
47037
47342
  }
47038
- return path20.join(providerDir, "fixtures");
47343
+ return path19.join(providerDir, "fixtures");
47039
47344
  }
47040
47345
  function readCliFixture(ctx, type, name) {
47041
47346
  const fixtureDir = getCliFixtureDir(ctx, type);
47042
- const filePath = path20.join(fixtureDir, `${name}.json`);
47347
+ const filePath = path19.join(fixtureDir, `${name}.json`);
47043
47348
  if (!fs12.existsSync(filePath)) {
47044
47349
  throw new Error(`Fixture not found: ${filePath}`);
47045
47350
  }
@@ -47806,7 +48111,7 @@ async (params) => {
47806
48111
  },
47807
48112
  notes: typeof body?.notes === "string" ? body.notes : void 0
47808
48113
  };
47809
- const filePath = path20.join(fixtureDir, `${name}.json`);
48114
+ const filePath = path19.join(fixtureDir, `${name}.json`);
47810
48115
  fs12.writeFileSync(filePath, JSON.stringify(fixture, null, 2));
47811
48116
  ctx.json(res, 200, {
47812
48117
  saved: true,
@@ -47830,7 +48135,7 @@ async (params) => {
47830
48135
  return;
47831
48136
  }
47832
48137
  const fixtures = fs12.readdirSync(fixtureDir).filter((file2) => file2.endsWith(".json")).sort((a, b2) => b2.localeCompare(a, void 0, { numeric: true, sensitivity: "base" })).map((file2) => {
47833
- const fullPath = path20.join(fixtureDir, file2);
48138
+ const fullPath = path19.join(fixtureDir, file2);
47834
48139
  try {
47835
48140
  const raw = JSON.parse(fs12.readFileSync(fullPath, "utf-8"));
47836
48141
  return {
@@ -47964,7 +48269,7 @@ async (params) => {
47964
48269
  }
47965
48270
  }
47966
48271
  var fs13 = __toESM2(require("fs"));
47967
- var path21 = __toESM2(require("path"));
48272
+ var path20 = __toESM2(require("path"));
47968
48273
  var os19 = __toESM2(require("os"));
47969
48274
  function getAutoImplPid(ctx) {
47970
48275
  const pid = ctx.autoImplProcess?.pid;
@@ -48021,22 +48326,22 @@ async (params) => {
48021
48326
  if (!fs13.existsSync(scriptsDir)) return null;
48022
48327
  const versions = fs13.readdirSync(scriptsDir).filter((d) => {
48023
48328
  try {
48024
- return fs13.statSync(path21.join(scriptsDir, d)).isDirectory();
48329
+ return fs13.statSync(path20.join(scriptsDir, d)).isDirectory();
48025
48330
  } catch {
48026
48331
  return false;
48027
48332
  }
48028
48333
  }).sort((a, b2) => b2.localeCompare(a, void 0, { numeric: true, sensitivity: "base" }));
48029
48334
  if (versions.length === 0) return null;
48030
- return path21.join(scriptsDir, versions[0]);
48335
+ return path20.join(scriptsDir, versions[0]);
48031
48336
  }
48032
48337
  function resolveAutoImplWritableProviderDir(ctx, category, type, requestedDir) {
48033
- const canonicalUserDir = path21.resolve(ctx.providerLoader.getUserProviderDir(category, type));
48034
- const desiredDir = requestedDir ? path21.resolve(requestedDir) : canonicalUserDir;
48035
- const upstreamRoot = path21.resolve(ctx.providerLoader.getUpstreamDir());
48036
- if (desiredDir === upstreamRoot || desiredDir.startsWith(`${upstreamRoot}${path21.sep}`)) {
48338
+ const canonicalUserDir = path20.resolve(ctx.providerLoader.getUserProviderDir(category, type));
48339
+ const desiredDir = requestedDir ? path20.resolve(requestedDir) : canonicalUserDir;
48340
+ const upstreamRoot = path20.resolve(ctx.providerLoader.getUpstreamDir());
48341
+ if (desiredDir === upstreamRoot || desiredDir.startsWith(`${upstreamRoot}${path20.sep}`)) {
48037
48342
  return { dir: null, reason: `Refusing to write into upstream provider directory: ${desiredDir}` };
48038
48343
  }
48039
- if (path21.basename(desiredDir) !== type) {
48344
+ if (path20.basename(desiredDir) !== type) {
48040
48345
  return { dir: null, reason: `Requested writable provider directory must end with '${type}': ${desiredDir}` };
48041
48346
  }
48042
48347
  const sourceDir = ctx.findProviderDir(type);
@@ -48044,11 +48349,11 @@ async (params) => {
48044
48349
  return { dir: null, reason: `Provider source directory not found for '${type}'` };
48045
48350
  }
48046
48351
  if (!fs13.existsSync(desiredDir)) {
48047
- fs13.mkdirSync(path21.dirname(desiredDir), { recursive: true });
48352
+ fs13.mkdirSync(path20.dirname(desiredDir), { recursive: true });
48048
48353
  fs13.cpSync(sourceDir, desiredDir, { recursive: true });
48049
48354
  ctx.log(`Auto-implement writable copy created: ${desiredDir}`);
48050
48355
  }
48051
- const providerJson = path21.join(desiredDir, "provider.json");
48356
+ const providerJson = path20.join(desiredDir, "provider.json");
48052
48357
  if (!fs13.existsSync(providerJson)) {
48053
48358
  return { dir: null, reason: `provider.json not found in writable provider directory: ${desiredDir}` };
48054
48359
  }
@@ -48059,13 +48364,13 @@ async (params) => {
48059
48364
  const refDir = ctx.findProviderDir(referenceType);
48060
48365
  if (!refDir || !fs13.existsSync(refDir)) return {};
48061
48366
  const referenceScripts = {};
48062
- const scriptsDir = path21.join(refDir, "scripts");
48367
+ const scriptsDir = path20.join(refDir, "scripts");
48063
48368
  const latestDir = getLatestScriptVersionDir(scriptsDir);
48064
48369
  if (!latestDir) return referenceScripts;
48065
48370
  for (const file2 of fs13.readdirSync(latestDir)) {
48066
48371
  if (!file2.endsWith(".js")) continue;
48067
48372
  try {
48068
- referenceScripts[file2] = fs13.readFileSync(path21.join(latestDir, file2), "utf-8");
48373
+ referenceScripts[file2] = fs13.readFileSync(path20.join(latestDir, file2), "utf-8");
48069
48374
  } catch {
48070
48375
  }
48071
48376
  }
@@ -48173,9 +48478,9 @@ async (params) => {
48173
48478
  });
48174
48479
  const referenceScripts = loadAutoImplReferenceScripts(ctx, resolvedReference);
48175
48480
  const prompt = buildAutoImplPrompt(ctx, type, provider, providerDir, functions, domContext, referenceScripts, comment, resolvedReference, verification);
48176
- const tmpDir = path21.join(os19.tmpdir(), "adhdev-autoimpl");
48481
+ const tmpDir = path20.join(os19.tmpdir(), "adhdev-autoimpl");
48177
48482
  if (!fs13.existsSync(tmpDir)) fs13.mkdirSync(tmpDir, { recursive: true });
48178
- const promptFile = path21.join(tmpDir, `prompt-${type}-${Date.now()}.md`);
48483
+ const promptFile = path20.join(tmpDir, `prompt-${type}-${Date.now()}.md`);
48179
48484
  fs13.writeFileSync(promptFile, prompt, "utf-8");
48180
48485
  ctx.log(`Auto-implement prompt written to ${promptFile} (${prompt.length} chars)`);
48181
48486
  const agentProvider = ctx.providerLoader.resolve(agent) || ctx.providerLoader.getMeta(agent);
@@ -48612,7 +48917,7 @@ async (params) => {
48612
48917
  setMode: "set_mode.js"
48613
48918
  };
48614
48919
  const targetFileNames = new Set(functions.map((fn2) => funcToFile[fn2]).filter(Boolean));
48615
- const scriptsDir = path21.join(providerDir, "scripts");
48920
+ const scriptsDir = path20.join(providerDir, "scripts");
48616
48921
  const latestScriptsDir = getLatestScriptVersionDir(scriptsDir);
48617
48922
  if (latestScriptsDir) {
48618
48923
  lines.push(`Scripts version directory: \`${latestScriptsDir}\``);
@@ -48623,7 +48928,7 @@ async (params) => {
48623
48928
  for (const file2 of fs13.readdirSync(latestScriptsDir)) {
48624
48929
  if (file2.endsWith(".js") && targetFileNames.has(file2)) {
48625
48930
  try {
48626
- const content = fs13.readFileSync(path21.join(latestScriptsDir, file2), "utf-8");
48931
+ const content = fs13.readFileSync(path20.join(latestScriptsDir, file2), "utf-8");
48627
48932
  lines.push(`### \`${file2}\` \u270F\uFE0F EDIT`);
48628
48933
  lines.push("```javascript");
48629
48934
  lines.push(content);
@@ -48640,7 +48945,7 @@ async (params) => {
48640
48945
  lines.push("");
48641
48946
  for (const file2 of refFiles) {
48642
48947
  try {
48643
- const content = fs13.readFileSync(path21.join(latestScriptsDir, file2), "utf-8");
48948
+ const content = fs13.readFileSync(path20.join(latestScriptsDir, file2), "utf-8");
48644
48949
  lines.push(`### \`${file2}\` \u{1F512}`);
48645
48950
  lines.push("```javascript");
48646
48951
  lines.push(content);
@@ -48681,10 +48986,10 @@ async (params) => {
48681
48986
  lines.push("");
48682
48987
  }
48683
48988
  }
48684
- const docsDir = path21.join(providerDir, "../../docs");
48989
+ const docsDir = path20.join(providerDir, "../../docs");
48685
48990
  const loadGuide = (name) => {
48686
48991
  try {
48687
- const p = path21.join(docsDir, name);
48992
+ const p = path20.join(docsDir, name);
48688
48993
  if (fs13.existsSync(p)) return fs13.readFileSync(p, "utf-8");
48689
48994
  } catch {
48690
48995
  }
@@ -48921,7 +49226,7 @@ async (params) => {
48921
49226
  parseApproval: "parse_approval.js"
48922
49227
  };
48923
49228
  const targetFileNames = new Set(functions.map((fn2) => funcToFile[fn2]).filter(Boolean));
48924
- const scriptsDir = path21.join(providerDir, "scripts");
49229
+ const scriptsDir = path20.join(providerDir, "scripts");
48925
49230
  const latestScriptsDir = getLatestScriptVersionDir(scriptsDir);
48926
49231
  if (latestScriptsDir) {
48927
49232
  lines.push(`Scripts version directory: \`${latestScriptsDir}\``);
@@ -48933,7 +49238,7 @@ async (params) => {
48933
49238
  if (!file2.endsWith(".js")) continue;
48934
49239
  if (!targetFileNames.has(file2)) continue;
48935
49240
  try {
48936
- const content = fs13.readFileSync(path21.join(latestScriptsDir, file2), "utf-8");
49241
+ const content = fs13.readFileSync(path20.join(latestScriptsDir, file2), "utf-8");
48937
49242
  lines.push(`### \`${file2}\` \u270F\uFE0F EDIT`);
48938
49243
  lines.push("```javascript");
48939
49244
  lines.push(content);
@@ -48949,7 +49254,7 @@ async (params) => {
48949
49254
  lines.push("");
48950
49255
  for (const file2 of refFiles) {
48951
49256
  try {
48952
- const content = fs13.readFileSync(path21.join(latestScriptsDir, file2), "utf-8");
49257
+ const content = fs13.readFileSync(path20.join(latestScriptsDir, file2), "utf-8");
48953
49258
  lines.push(`### \`${file2}\` \u{1F512}`);
48954
49259
  lines.push("```javascript");
48955
49260
  lines.push(content);
@@ -48982,10 +49287,10 @@ async (params) => {
48982
49287
  lines.push("");
48983
49288
  }
48984
49289
  }
48985
- const docsDir = path21.join(providerDir, "../../docs");
49290
+ const docsDir = path20.join(providerDir, "../../docs");
48986
49291
  const loadGuide = (name) => {
48987
49292
  try {
48988
- const p = path21.join(docsDir, name);
49293
+ const p = path20.join(docsDir, name);
48989
49294
  if (fs13.existsSync(p)) return fs13.readFileSync(p, "utf-8");
48990
49295
  } catch {
48991
49296
  }
@@ -49430,8 +49735,8 @@ data: ${JSON.stringify(msg.data)}
49430
49735
  }
49431
49736
  getEndpointList() {
49432
49737
  return this.routes.map((r) => {
49433
- const path23 = typeof r.pattern === "string" ? r.pattern : r.pattern.source.replace(/\\\//g, "/").replace(/\(\[.*?\]\+\)/g, ":type").replace(/[\^$]/g, "");
49434
- return `${r.method.padEnd(5)} ${path23}`;
49738
+ const path222 = typeof r.pattern === "string" ? r.pattern : r.pattern.source.replace(/\\\//g, "/").replace(/\(\[.*?\]\+\)/g, ":type").replace(/[\^$]/g, "");
49739
+ return `${r.method.padEnd(5)} ${path222}`;
49435
49740
  });
49436
49741
  }
49437
49742
  async start(port = DEV_SERVER_PORT) {
@@ -49712,12 +50017,12 @@ data: ${JSON.stringify(msg.data)}
49712
50017
  // ─── DevConsole SPA ───
49713
50018
  getConsoleDistDir() {
49714
50019
  const candidates = [
49715
- path222.resolve(__dirname, "../../web-devconsole/dist"),
49716
- path222.resolve(__dirname, "../../../web-devconsole/dist"),
49717
- path222.join(process.cwd(), "packages/web-devconsole/dist")
50020
+ path21.resolve(__dirname, "../../web-devconsole/dist"),
50021
+ path21.resolve(__dirname, "../../../web-devconsole/dist"),
50022
+ path21.join(process.cwd(), "packages/web-devconsole/dist")
49718
50023
  ];
49719
50024
  for (const dir of candidates) {
49720
- if (fs14.existsSync(path222.join(dir, "index.html"))) return dir;
50025
+ if (fs14.existsSync(path21.join(dir, "index.html"))) return dir;
49721
50026
  }
49722
50027
  return null;
49723
50028
  }
@@ -49727,7 +50032,7 @@ data: ${JSON.stringify(msg.data)}
49727
50032
  this.json(res, 500, { error: "DevConsole not found. Run: npm run build -w packages/web-devconsole" });
49728
50033
  return;
49729
50034
  }
49730
- const htmlPath = path222.join(distDir, "index.html");
50035
+ const htmlPath = path21.join(distDir, "index.html");
49731
50036
  try {
49732
50037
  const html = fs14.readFileSync(htmlPath, "utf-8");
49733
50038
  res.writeHead(200, { "Content-Type": "text/html; charset=utf-8" });
@@ -49752,15 +50057,15 @@ data: ${JSON.stringify(msg.data)}
49752
50057
  this.json(res, 404, { error: "Not found" });
49753
50058
  return;
49754
50059
  }
49755
- const safePath = path222.normalize(pathname).replace(/^\.\.\//, "");
49756
- const filePath = path222.join(distDir, safePath);
50060
+ const safePath = path21.normalize(pathname).replace(/^\.\.\//, "");
50061
+ const filePath = path21.join(distDir, safePath);
49757
50062
  if (!filePath.startsWith(distDir)) {
49758
50063
  this.json(res, 403, { error: "Forbidden" });
49759
50064
  return;
49760
50065
  }
49761
50066
  try {
49762
50067
  const content = fs14.readFileSync(filePath);
49763
- const ext = path222.extname(filePath);
50068
+ const ext = path21.extname(filePath);
49764
50069
  const contentType = _DevServer.MIME_MAP[ext] || "application/octet-stream";
49765
50070
  res.writeHead(200, { "Content-Type": contentType, "Cache-Control": "public, max-age=31536000, immutable" });
49766
50071
  res.end(content);
@@ -49873,9 +50178,9 @@ data: ${JSON.stringify(msg.data)}
49873
50178
  const rel = prefix ? `${prefix}/${entry.name}` : entry.name;
49874
50179
  if (entry.isDirectory()) {
49875
50180
  files.push({ path: rel, size: 0, type: "dir" });
49876
- scan(path222.join(d, entry.name), rel);
50181
+ scan(path21.join(d, entry.name), rel);
49877
50182
  } else {
49878
- const stat4 = fs14.statSync(path222.join(d, entry.name));
50183
+ const stat4 = fs14.statSync(path21.join(d, entry.name));
49879
50184
  files.push({ path: rel, size: stat4.size, type: "file" });
49880
50185
  }
49881
50186
  }
@@ -49898,7 +50203,7 @@ data: ${JSON.stringify(msg.data)}
49898
50203
  this.json(res, 404, { error: `Provider directory not found: ${type}` });
49899
50204
  return;
49900
50205
  }
49901
- const fullPath = path222.resolve(dir, path222.normalize(filePath));
50206
+ const fullPath = path21.resolve(dir, path21.normalize(filePath));
49902
50207
  if (!fullPath.startsWith(dir)) {
49903
50208
  this.json(res, 403, { error: "Forbidden" });
49904
50209
  return;
@@ -49923,14 +50228,14 @@ data: ${JSON.stringify(msg.data)}
49923
50228
  this.json(res, 404, { error: `Provider directory not found: ${type}` });
49924
50229
  return;
49925
50230
  }
49926
- const fullPath = path222.resolve(dir, path222.normalize(filePath));
50231
+ const fullPath = path21.resolve(dir, path21.normalize(filePath));
49927
50232
  if (!fullPath.startsWith(dir)) {
49928
50233
  this.json(res, 403, { error: "Forbidden" });
49929
50234
  return;
49930
50235
  }
49931
50236
  try {
49932
50237
  if (fs14.existsSync(fullPath)) fs14.copyFileSync(fullPath, fullPath + ".bak");
49933
- fs14.mkdirSync(path222.dirname(fullPath), { recursive: true });
50238
+ fs14.mkdirSync(path21.dirname(fullPath), { recursive: true });
49934
50239
  fs14.writeFileSync(fullPath, content, "utf-8");
49935
50240
  this.log(`File saved: ${fullPath} (${content.length} chars)`);
49936
50241
  this.providerLoader.reload();
@@ -49947,7 +50252,7 @@ data: ${JSON.stringify(msg.data)}
49947
50252
  return;
49948
50253
  }
49949
50254
  for (const name of ["scripts.js", "provider.json"]) {
49950
- const p = path222.join(dir, name);
50255
+ const p = path21.join(dir, name);
49951
50256
  if (fs14.existsSync(p)) {
49952
50257
  const source = fs14.readFileSync(p, "utf-8");
49953
50258
  this.json(res, 200, { type, path: p, source, lines: source.split("\n").length });
@@ -49968,8 +50273,8 @@ data: ${JSON.stringify(msg.data)}
49968
50273
  this.json(res, 404, { error: `Provider not found: ${type}` });
49969
50274
  return;
49970
50275
  }
49971
- const target = fs14.existsSync(path222.join(dir, "scripts.js")) ? "scripts.js" : "provider.json";
49972
- const targetPath = path222.join(dir, target);
50276
+ const target = fs14.existsSync(path21.join(dir, "scripts.js")) ? "scripts.js" : "provider.json";
50277
+ const targetPath = path21.join(dir, target);
49973
50278
  try {
49974
50279
  if (fs14.existsSync(targetPath)) fs14.copyFileSync(targetPath, targetPath + ".bak");
49975
50280
  fs14.writeFileSync(targetPath, source, "utf-8");
@@ -50116,7 +50421,7 @@ data: ${JSON.stringify(msg.data)}
50116
50421
  }
50117
50422
  let targetDir;
50118
50423
  targetDir = this.providerLoader.getUserProviderDir(category, type);
50119
- const jsonPath = path222.join(targetDir, "provider.json");
50424
+ const jsonPath = path21.join(targetDir, "provider.json");
50120
50425
  if (fs14.existsSync(jsonPath)) {
50121
50426
  this.json(res, 409, { error: `Provider already exists at ${targetDir}`, path: targetDir });
50122
50427
  return;
@@ -50128,8 +50433,8 @@ data: ${JSON.stringify(msg.data)}
50128
50433
  const createdFiles = ["provider.json"];
50129
50434
  if (result.files) {
50130
50435
  for (const [relPath, content] of Object.entries(result.files)) {
50131
- const fullPath = path222.join(targetDir, relPath);
50132
- fs14.mkdirSync(path222.dirname(fullPath), { recursive: true });
50436
+ const fullPath = path21.join(targetDir, relPath);
50437
+ fs14.mkdirSync(path21.dirname(fullPath), { recursive: true });
50133
50438
  fs14.writeFileSync(fullPath, content, "utf-8");
50134
50439
  createdFiles.push(relPath);
50135
50440
  }
@@ -50182,22 +50487,22 @@ data: ${JSON.stringify(msg.data)}
50182
50487
  if (!fs14.existsSync(scriptsDir)) return null;
50183
50488
  const versions = fs14.readdirSync(scriptsDir).filter((d) => {
50184
50489
  try {
50185
- return fs14.statSync(path222.join(scriptsDir, d)).isDirectory();
50490
+ return fs14.statSync(path21.join(scriptsDir, d)).isDirectory();
50186
50491
  } catch {
50187
50492
  return false;
50188
50493
  }
50189
50494
  }).sort((a, b2) => b2.localeCompare(a, void 0, { numeric: true, sensitivity: "base" }));
50190
50495
  if (versions.length === 0) return null;
50191
- return path222.join(scriptsDir, versions[0]);
50496
+ return path21.join(scriptsDir, versions[0]);
50192
50497
  }
50193
50498
  resolveAutoImplWritableProviderDir(category, type, requestedDir) {
50194
- const canonicalUserDir = path222.resolve(this.providerLoader.getUserProviderDir(category, type));
50195
- const desiredDir = requestedDir ? path222.resolve(requestedDir) : canonicalUserDir;
50196
- const upstreamRoot = path222.resolve(this.providerLoader.getUpstreamDir());
50197
- if (desiredDir === upstreamRoot || desiredDir.startsWith(`${upstreamRoot}${path222.sep}`)) {
50499
+ const canonicalUserDir = path21.resolve(this.providerLoader.getUserProviderDir(category, type));
50500
+ const desiredDir = requestedDir ? path21.resolve(requestedDir) : canonicalUserDir;
50501
+ const upstreamRoot = path21.resolve(this.providerLoader.getUpstreamDir());
50502
+ if (desiredDir === upstreamRoot || desiredDir.startsWith(`${upstreamRoot}${path21.sep}`)) {
50198
50503
  return { dir: null, reason: `Refusing to write into upstream provider directory: ${desiredDir}` };
50199
50504
  }
50200
- if (path222.basename(desiredDir) !== type) {
50505
+ if (path21.basename(desiredDir) !== type) {
50201
50506
  return { dir: null, reason: `Requested writable provider directory must end with '${type}': ${desiredDir}` };
50202
50507
  }
50203
50508
  const sourceDir = this.findProviderDir(type);
@@ -50205,11 +50510,11 @@ data: ${JSON.stringify(msg.data)}
50205
50510
  return { dir: null, reason: `Provider source directory not found for '${type}'` };
50206
50511
  }
50207
50512
  if (!fs14.existsSync(desiredDir)) {
50208
- fs14.mkdirSync(path222.dirname(desiredDir), { recursive: true });
50513
+ fs14.mkdirSync(path21.dirname(desiredDir), { recursive: true });
50209
50514
  fs14.cpSync(sourceDir, desiredDir, { recursive: true });
50210
50515
  this.log(`Auto-implement writable copy created: ${desiredDir}`);
50211
50516
  }
50212
- const providerJson = path222.join(desiredDir, "provider.json");
50517
+ const providerJson = path21.join(desiredDir, "provider.json");
50213
50518
  if (!fs14.existsSync(providerJson)) {
50214
50519
  return { dir: null, reason: `provider.json not found in writable provider directory: ${desiredDir}` };
50215
50520
  }
@@ -50245,7 +50550,7 @@ data: ${JSON.stringify(msg.data)}
50245
50550
  setMode: "set_mode.js"
50246
50551
  };
50247
50552
  const targetFileNames = new Set(functions.map((fn2) => funcToFile[fn2]).filter(Boolean));
50248
- const scriptsDir = path222.join(providerDir, "scripts");
50553
+ const scriptsDir = path21.join(providerDir, "scripts");
50249
50554
  const latestScriptsDir = this.getLatestScriptVersionDir(scriptsDir);
50250
50555
  if (latestScriptsDir) {
50251
50556
  lines.push(`Scripts version directory: \`${latestScriptsDir}\``);
@@ -50256,7 +50561,7 @@ data: ${JSON.stringify(msg.data)}
50256
50561
  for (const file2 of fs14.readdirSync(latestScriptsDir)) {
50257
50562
  if (file2.endsWith(".js") && targetFileNames.has(file2)) {
50258
50563
  try {
50259
- const content = fs14.readFileSync(path222.join(latestScriptsDir, file2), "utf-8");
50564
+ const content = fs14.readFileSync(path21.join(latestScriptsDir, file2), "utf-8");
50260
50565
  lines.push(`### \`${file2}\` \u270F\uFE0F EDIT`);
50261
50566
  lines.push("```javascript");
50262
50567
  lines.push(content);
@@ -50273,7 +50578,7 @@ data: ${JSON.stringify(msg.data)}
50273
50578
  lines.push("");
50274
50579
  for (const file2 of refFiles) {
50275
50580
  try {
50276
- const content = fs14.readFileSync(path222.join(latestScriptsDir, file2), "utf-8");
50581
+ const content = fs14.readFileSync(path21.join(latestScriptsDir, file2), "utf-8");
50277
50582
  lines.push(`### \`${file2}\` \u{1F512}`);
50278
50583
  lines.push("```javascript");
50279
50584
  lines.push(content);
@@ -50314,10 +50619,10 @@ data: ${JSON.stringify(msg.data)}
50314
50619
  lines.push("");
50315
50620
  }
50316
50621
  }
50317
- const docsDir = path222.join(providerDir, "../../docs");
50622
+ const docsDir = path21.join(providerDir, "../../docs");
50318
50623
  const loadGuide = (name) => {
50319
50624
  try {
50320
- const p = path222.join(docsDir, name);
50625
+ const p = path21.join(docsDir, name);
50321
50626
  if (fs14.existsSync(p)) return fs14.readFileSync(p, "utf-8");
50322
50627
  } catch {
50323
50628
  }
@@ -50491,7 +50796,7 @@ data: ${JSON.stringify(msg.data)}
50491
50796
  parseApproval: "parse_approval.js"
50492
50797
  };
50493
50798
  const targetFileNames = new Set(functions.map((fn2) => funcToFile[fn2]).filter(Boolean));
50494
- const scriptsDir = path222.join(providerDir, "scripts");
50799
+ const scriptsDir = path21.join(providerDir, "scripts");
50495
50800
  const latestScriptsDir = this.getLatestScriptVersionDir(scriptsDir);
50496
50801
  if (latestScriptsDir) {
50497
50802
  lines.push(`Scripts version directory: \`${latestScriptsDir}\``);
@@ -50503,7 +50808,7 @@ data: ${JSON.stringify(msg.data)}
50503
50808
  if (!file2.endsWith(".js")) continue;
50504
50809
  if (!targetFileNames.has(file2)) continue;
50505
50810
  try {
50506
- const content = fs14.readFileSync(path222.join(latestScriptsDir, file2), "utf-8");
50811
+ const content = fs14.readFileSync(path21.join(latestScriptsDir, file2), "utf-8");
50507
50812
  lines.push(`### \`${file2}\` \u270F\uFE0F EDIT`);
50508
50813
  lines.push("```javascript");
50509
50814
  lines.push(content);
@@ -50519,7 +50824,7 @@ data: ${JSON.stringify(msg.data)}
50519
50824
  lines.push("");
50520
50825
  for (const file2 of refFiles) {
50521
50826
  try {
50522
- const content = fs14.readFileSync(path222.join(latestScriptsDir, file2), "utf-8");
50827
+ const content = fs14.readFileSync(path21.join(latestScriptsDir, file2), "utf-8");
50523
50828
  lines.push(`### \`${file2}\` \u{1F512}`);
50524
50829
  lines.push("```javascript");
50525
50830
  lines.push(content);
@@ -50552,10 +50857,10 @@ data: ${JSON.stringify(msg.data)}
50552
50857
  lines.push("");
50553
50858
  }
50554
50859
  }
50555
- const docsDir = path222.join(providerDir, "../../docs");
50860
+ const docsDir = path21.join(providerDir, "../../docs");
50556
50861
  const loadGuide = (name) => {
50557
50862
  try {
50558
- const p = path222.join(docsDir, name);
50863
+ const p = path21.join(docsDir, name);
50559
50864
  if (fs14.existsSync(p)) return fs14.readFileSync(p, "utf-8");
50560
50865
  } catch {
50561
50866
  }