@adhdev/daemon-core 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.mjs CHANGED
@@ -453,6 +453,231 @@ var init_logger = __esm({
453
453
  }
454
454
  });
455
455
 
456
+ // src/providers/io-contracts.ts
457
+ function normalizeInputEnvelope(input) {
458
+ const normalized = normalizeInputEnvelopePayload(input);
459
+ const textFallback = normalized.textFallback ?? flattenInputParts(normalized.parts);
460
+ return {
461
+ parts: normalized.parts,
462
+ textFallback,
463
+ ...normalized.metadata ? { metadata: normalized.metadata } : {}
464
+ };
465
+ }
466
+ function normalizeMessageParts(content) {
467
+ if (typeof content === "string") return [{ type: "text", text: content }];
468
+ if (!Array.isArray(content)) {
469
+ if (content && typeof content === "object" && typeof content.text === "string") {
470
+ return [{ type: "text", text: String(content.text) }];
471
+ }
472
+ return [];
473
+ }
474
+ const parts = [];
475
+ for (const raw of content) {
476
+ if (typeof raw === "string") {
477
+ parts.push({ type: "text", text: raw });
478
+ continue;
479
+ }
480
+ if (!raw || typeof raw !== "object") continue;
481
+ const part = normalizeMessagePartObject(raw);
482
+ if (part) parts.push(part);
483
+ }
484
+ return parts;
485
+ }
486
+ function flattenMessageParts(parts) {
487
+ return parts.map((part) => {
488
+ if (part.type === "text") return part.text;
489
+ if (part.type === "resource") return part.resource.text || "";
490
+ return "";
491
+ }).filter((value) => value.length > 0).join("\n");
492
+ }
493
+ function normalizeInputEnvelopePayload(input) {
494
+ if (typeof input === "string") {
495
+ return { parts: [{ type: "text", text: input }], textFallback: input };
496
+ }
497
+ if (!input || typeof input !== "object") {
498
+ return { parts: [], textFallback: "" };
499
+ }
500
+ const record = input;
501
+ const nestedInput = record.input;
502
+ if (nestedInput && typeof nestedInput === "object") {
503
+ const nested = nestedInput;
504
+ return {
505
+ parts: normalizeInputParts(nested.parts ?? nested.prompt),
506
+ textFallback: typeof nested.textFallback === "string" ? nested.textFallback : void 0,
507
+ metadata: normalizeInputMetadata(nested.metadata)
508
+ };
509
+ }
510
+ const directText = typeof record.text === "string" ? record.text : typeof record.message === "string" ? record.message : void 0;
511
+ if (directText !== void 0) {
512
+ return { parts: [{ type: "text", text: directText }], textFallback: directText };
513
+ }
514
+ const directParts = normalizeInputParts(record.parts ?? record.prompt);
515
+ return {
516
+ parts: directParts,
517
+ textFallback: typeof record.textFallback === "string" ? record.textFallback : void 0,
518
+ metadata: normalizeInputMetadata(record.metadata)
519
+ };
520
+ }
521
+ function normalizeInputMetadata(value) {
522
+ if (!value || typeof value !== "object") return void 0;
523
+ const record = value;
524
+ const metadata = {};
525
+ if (record.source === "dashboard" || record.source === "shortcut_api" || record.source === "provider_script" || record.source === "session_replay") {
526
+ metadata.source = record.source;
527
+ }
528
+ if (typeof record.clientTimestamp === "number" && Number.isFinite(record.clientTimestamp)) {
529
+ metadata.clientTimestamp = record.clientTimestamp;
530
+ }
531
+ return Object.keys(metadata).length > 0 ? metadata : void 0;
532
+ }
533
+ function normalizeInputParts(value) {
534
+ if (!Array.isArray(value)) return [];
535
+ const parts = [];
536
+ for (const raw of value) {
537
+ if (typeof raw === "string") {
538
+ parts.push({ type: "text", text: raw });
539
+ continue;
540
+ }
541
+ if (!raw || typeof raw !== "object") continue;
542
+ const part = normalizeInputPartObject(raw);
543
+ if (part) parts.push(part);
544
+ }
545
+ return parts;
546
+ }
547
+ function normalizeInputPartObject(raw) {
548
+ const type = raw.type;
549
+ if (type === "text" && typeof raw.text === "string") {
550
+ return { type, text: raw.text };
551
+ }
552
+ if (type === "image" && typeof raw.mimeType === "string") {
553
+ return {
554
+ type,
555
+ mimeType: raw.mimeType,
556
+ ...typeof raw.uri === "string" ? { uri: raw.uri } : {},
557
+ ...typeof raw.data === "string" ? { data: raw.data } : {},
558
+ ...typeof raw.alt === "string" ? { alt: raw.alt } : {}
559
+ };
560
+ }
561
+ if (type === "audio" && typeof raw.mimeType === "string") {
562
+ return {
563
+ type,
564
+ mimeType: raw.mimeType,
565
+ ...typeof raw.uri === "string" ? { uri: raw.uri } : {},
566
+ ...typeof raw.data === "string" ? { data: raw.data } : {},
567
+ ...typeof raw.transcript === "string" ? { transcript: raw.transcript } : {}
568
+ };
569
+ }
570
+ if (type === "video" && typeof raw.mimeType === "string") {
571
+ return {
572
+ type,
573
+ mimeType: raw.mimeType,
574
+ ...typeof raw.uri === "string" ? { uri: raw.uri } : {},
575
+ ...typeof raw.data === "string" ? { data: raw.data } : {},
576
+ ...typeof raw.posterUri === "string" ? { posterUri: raw.posterUri } : {}
577
+ };
578
+ }
579
+ if (type === "resource" && typeof raw.uri === "string") {
580
+ return {
581
+ type,
582
+ uri: raw.uri,
583
+ ...typeof raw.mimeType === "string" ? { mimeType: raw.mimeType } : {},
584
+ ...typeof raw.name === "string" ? { name: raw.name } : {},
585
+ ...typeof raw.text === "string" ? { text: raw.text } : {},
586
+ ...typeof raw.data === "string" ? { data: raw.data } : {}
587
+ };
588
+ }
589
+ if (type === "resource_link" && typeof raw.uri === "string") {
590
+ return {
591
+ type: "resource",
592
+ uri: raw.uri,
593
+ ...typeof raw.mimeType === "string" ? { mimeType: raw.mimeType } : {},
594
+ ...typeof raw.name === "string" ? { name: raw.name } : {}
595
+ };
596
+ }
597
+ return null;
598
+ }
599
+ function normalizeMessagePartObject(raw) {
600
+ const type = raw.type;
601
+ if (type === "text" && typeof raw.text === "string") {
602
+ return { type, text: raw.text };
603
+ }
604
+ if (type === "image" && typeof raw.mimeType === "string") {
605
+ return {
606
+ type,
607
+ mimeType: raw.mimeType,
608
+ ...typeof raw.uri === "string" ? { uri: raw.uri } : {},
609
+ ...typeof raw.data === "string" ? { data: raw.data } : {}
610
+ };
611
+ }
612
+ if (type === "audio" && typeof raw.mimeType === "string") {
613
+ return {
614
+ type,
615
+ mimeType: raw.mimeType,
616
+ ...typeof raw.uri === "string" ? { uri: raw.uri } : {},
617
+ ...typeof raw.data === "string" ? { data: raw.data } : {},
618
+ ...typeof raw.transcript === "string" ? { transcript: raw.transcript } : {}
619
+ };
620
+ }
621
+ if (type === "video" && typeof raw.mimeType === "string") {
622
+ return {
623
+ type,
624
+ mimeType: raw.mimeType,
625
+ ...typeof raw.uri === "string" ? { uri: raw.uri } : {},
626
+ ...typeof raw.data === "string" ? { data: raw.data } : {},
627
+ ...typeof raw.posterUri === "string" ? { posterUri: raw.posterUri } : {}
628
+ };
629
+ }
630
+ if (type === "resource_link" && typeof raw.uri === "string" && typeof raw.name === "string") {
631
+ return {
632
+ type,
633
+ uri: raw.uri,
634
+ name: raw.name,
635
+ ...typeof raw.mimeType === "string" ? { mimeType: raw.mimeType } : {},
636
+ ...typeof raw.size === "number" ? { size: raw.size } : {}
637
+ };
638
+ }
639
+ if (type === "resource" && raw.resource && typeof raw.resource === "object") {
640
+ const resource = raw.resource;
641
+ if (typeof resource.uri !== "string") return null;
642
+ return {
643
+ type,
644
+ resource: {
645
+ uri: resource.uri,
646
+ ...typeof resource.mimeType === "string" || resource.mimeType === null ? { mimeType: resource.mimeType } : {},
647
+ ...typeof resource.text === "string" ? { text: resource.text } : {},
648
+ ...typeof resource.blob === "string" ? { blob: resource.blob } : {}
649
+ }
650
+ };
651
+ }
652
+ return null;
653
+ }
654
+ function flattenInputParts(parts) {
655
+ return parts.map((part) => {
656
+ if (part.type === "text") return part.text;
657
+ if (part.type === "audio") return part.transcript || "";
658
+ if (part.type === "resource") return part.text || "";
659
+ return "";
660
+ }).filter((value) => value.length > 0).join("\n");
661
+ }
662
+ var init_io_contracts = __esm({
663
+ "src/providers/io-contracts.ts"() {
664
+ "use strict";
665
+ }
666
+ });
667
+
668
+ // src/providers/contracts.ts
669
+ function flattenContent(content) {
670
+ if (typeof content === "string") return content;
671
+ return flattenMessageParts(normalizeMessageParts(content));
672
+ }
673
+ var init_contracts = __esm({
674
+ "src/providers/contracts.ts"() {
675
+ "use strict";
676
+ init_io_contracts();
677
+ init_io_contracts();
678
+ }
679
+ });
680
+
456
681
  // src/providers/chat-message-normalization.ts
457
682
  function canonicalizeKindHint(value) {
458
683
  return value.trim().toLowerCase().replace(/[\s-]+/g, "_");
@@ -603,39 +828,161 @@ var init_chat_message_normalization = __esm({
603
828
  }
604
829
  });
605
830
 
606
- // src/cli-adapters/terminal-backends/ghostty-vt-backend.ts
607
- function isModuleNotFoundError(error, ref) {
608
- if (!(error instanceof Error)) return false;
609
- const message = error.message || "";
610
- const code = "code" in error ? error.code : void 0;
611
- return code === "MODULE_NOT_FOUND" && message.includes(ref);
831
+ // src/providers/read-chat-contract.ts
832
+ function isPlainObject3(value) {
833
+ return !!value && typeof value === "object" && !Array.isArray(value);
612
834
  }
613
- function normalizeBinding(mod, ref) {
614
- const binding = mod?.default?.createTerminal ? mod.default : mod?.createTerminal ? mod : null;
615
- if (!binding) {
616
- throw new Error(`Ghostty VT binding "${ref}" does not export createTerminal()`);
835
+ function isFiniteNumber(value) {
836
+ return typeof value === "number" && Number.isFinite(value);
837
+ }
838
+ function validateStatus(status, source) {
839
+ if (typeof status !== "string" || !VALID_STATUSES.includes(status)) {
840
+ throw new Error(`${source}: status must be one of ${VALID_STATUSES.join(", ")}`);
617
841
  }
618
- return binding;
842
+ return status;
619
843
  }
620
- function getBindingCandidates() {
621
- const explicit = process.env.ADHDEV_GHOSTTY_VT_BINDING?.trim();
622
- return explicit ? [explicit] : DEFAULT_BINDING_CANDIDATES;
844
+ function validateRole(role, source, index) {
845
+ if (typeof role !== "string" || !VALID_ROLES.includes(role)) {
846
+ throw new Error(`${source}: messages[${index}].role must be one of ${VALID_ROLES.join(", ")}`);
847
+ }
848
+ return role;
623
849
  }
624
- function loadGhosttyVtBinding(required) {
625
- if (cachedBinding !== void 0) {
626
- if (!cachedBinding && required && cachedBindingError) {
627
- throw cachedBindingError;
850
+ function validateMessageContent(content, source, index) {
851
+ if (typeof content === "string") return content;
852
+ if (Array.isArray(content)) return normalizeMessageParts(content);
853
+ throw new Error(`${source}: messages[${index}].content must be a string or structured content array`);
854
+ }
855
+ function validateMessage(message, source, index) {
856
+ if (!isPlainObject3(message)) {
857
+ throw new Error(`${source}: messages[${index}] must be an object`);
858
+ }
859
+ const normalized = {
860
+ role: validateRole(message.role, source, index),
861
+ content: validateMessageContent(message.content, source, index)
862
+ };
863
+ if (typeof message.kind === "string") normalized.kind = message.kind;
864
+ if (typeof message.id === "string") normalized.id = message.id;
865
+ if (isFiniteNumber(message.index)) normalized.index = message.index;
866
+ if (isFiniteNumber(message.timestamp)) normalized.timestamp = message.timestamp;
867
+ if (isFiniteNumber(message.receivedAt)) normalized.receivedAt = message.receivedAt;
868
+ if (Array.isArray(message.toolCalls)) normalized.toolCalls = message.toolCalls;
869
+ if (isPlainObject3(message.meta)) normalized.meta = message.meta;
870
+ if (typeof message.senderName === "string") normalized.senderName = message.senderName;
871
+ if (typeof message._type === "string") normalized._type = message._type;
872
+ if (typeof message._sub === "string") normalized._sub = message._sub;
873
+ return normalized;
874
+ }
875
+ function validateModal(activeModal, status, source) {
876
+ if (activeModal == null) {
877
+ if (status === "waiting_approval") {
878
+ throw new Error(`${source}: waiting_approval status requires activeModal with buttons`);
628
879
  }
629
- return cachedBinding;
880
+ return activeModal === null ? null : void 0;
630
881
  }
631
- const errors = [];
632
- for (const ref of getBindingCandidates()) {
633
- try {
634
- const mod = __require(ref);
635
- cachedBinding = normalizeBinding(mod, ref);
636
- cachedBindingError = null;
637
- return cachedBinding;
638
- } catch (error) {
882
+ if (!isPlainObject3(activeModal)) {
883
+ throw new Error(`${source}: activeModal must be an object when provided`);
884
+ }
885
+ if (typeof activeModal.message !== "string") {
886
+ throw new Error(`${source}: activeModal.message must be a string`);
887
+ }
888
+ if (!Array.isArray(activeModal.buttons) || activeModal.buttons.some((button) => typeof button !== "string" || !button.trim())) {
889
+ throw new Error(`${source}: activeModal.buttons must be a non-empty string array`);
890
+ }
891
+ const normalized = {
892
+ message: activeModal.message,
893
+ buttons: activeModal.buttons.map((button) => button.trim())
894
+ };
895
+ if (isFiniteNumber(activeModal.width)) normalized.width = activeModal.width;
896
+ if (isFiniteNumber(activeModal.height)) normalized.height = activeModal.height;
897
+ return normalized;
898
+ }
899
+ function validateControlValues(controlValues, source) {
900
+ if (controlValues === void 0) return void 0;
901
+ if (!isPlainObject3(controlValues)) {
902
+ throw new Error(`${source}: controlValues must be an object when provided`);
903
+ }
904
+ const normalized = {};
905
+ for (const [key, value] of Object.entries(controlValues)) {
906
+ if (typeof value !== "string" && typeof value !== "number" && typeof value !== "boolean") {
907
+ throw new Error(`${source}: controlValues.${key} must be string, number, or boolean`);
908
+ }
909
+ normalized[key] = value;
910
+ }
911
+ return normalized;
912
+ }
913
+ function validateReadChatResultPayload(raw, source = "read_chat") {
914
+ if (!isPlainObject3(raw)) {
915
+ throw new Error(`${source}: payload must be an object`);
916
+ }
917
+ const status = validateStatus(raw.status, source);
918
+ if (!Array.isArray(raw.messages)) {
919
+ throw new Error(`${source}: messages must be an array`);
920
+ }
921
+ const messages = raw.messages.map((message, index) => validateMessage(message, source, index));
922
+ const activeModal = validateModal(raw.activeModal, status, source);
923
+ const controlValues = validateControlValues(raw.controlValues, source);
924
+ const normalized = {
925
+ status,
926
+ messages
927
+ };
928
+ if (activeModal !== void 0) normalized.activeModal = activeModal;
929
+ if (typeof raw.id === "string") normalized.id = raw.id;
930
+ if (typeof raw.title === "string") normalized.title = raw.title;
931
+ if (typeof raw.agentType === "string") normalized.agentType = raw.agentType;
932
+ if (typeof raw.agentName === "string") normalized.agentName = raw.agentName;
933
+ if (typeof raw.extensionId === "string") normalized.extensionId = raw.extensionId;
934
+ if (typeof raw.inputContent === "string") normalized.inputContent = raw.inputContent;
935
+ if (typeof raw.isVisible === "boolean") normalized.isVisible = raw.isVisible;
936
+ if (typeof raw.isWelcomeScreen === "boolean") normalized.isWelcomeScreen = raw.isWelcomeScreen;
937
+ if (controlValues) normalized.controlValues = controlValues;
938
+ if (raw.summaryMetadata !== void 0) normalized.summaryMetadata = raw.summaryMetadata;
939
+ if (Array.isArray(raw.effects)) normalized.effects = raw.effects;
940
+ if (typeof raw.providerSessionId === "string") normalized.providerSessionId = raw.providerSessionId;
941
+ return normalized;
942
+ }
943
+ var VALID_STATUSES, VALID_ROLES;
944
+ var init_read_chat_contract = __esm({
945
+ "src/providers/read-chat-contract.ts"() {
946
+ "use strict";
947
+ init_contracts();
948
+ VALID_STATUSES = ["idle", "generating", "waiting_approval", "error", "panel_hidden", "streaming", "long_generating"];
949
+ VALID_ROLES = ["user", "assistant", "system", "human"];
950
+ }
951
+ });
952
+
953
+ // src/cli-adapters/terminal-backends/ghostty-vt-backend.ts
954
+ function isModuleNotFoundError(error, ref) {
955
+ if (!(error instanceof Error)) return false;
956
+ const message = error.message || "";
957
+ const code = "code" in error ? error.code : void 0;
958
+ return code === "MODULE_NOT_FOUND" && message.includes(ref);
959
+ }
960
+ function normalizeBinding(mod, ref) {
961
+ const binding = mod?.default?.createTerminal ? mod.default : mod?.createTerminal ? mod : null;
962
+ if (!binding) {
963
+ throw new Error(`Ghostty VT binding "${ref}" does not export createTerminal()`);
964
+ }
965
+ return binding;
966
+ }
967
+ function getBindingCandidates() {
968
+ const explicit = process.env.ADHDEV_GHOSTTY_VT_BINDING?.trim();
969
+ return explicit ? [explicit] : DEFAULT_BINDING_CANDIDATES;
970
+ }
971
+ function loadGhosttyVtBinding(required) {
972
+ if (cachedBinding !== void 0) {
973
+ if (!cachedBinding && required && cachedBindingError) {
974
+ throw cachedBindingError;
975
+ }
976
+ return cachedBinding;
977
+ }
978
+ const errors = [];
979
+ for (const ref of getBindingCandidates()) {
980
+ try {
981
+ const mod = __require(ref);
982
+ cachedBinding = normalizeBinding(mod, ref);
983
+ cachedBindingError = null;
984
+ return cachedBinding;
985
+ } catch (error) {
639
986
  if (isModuleNotFoundError(error, ref)) {
640
987
  errors.push(`${ref}: module not found`);
641
988
  continue;
@@ -1460,6 +1807,7 @@ var init_provider_cli_adapter = __esm({
1460
1807
  init_pty_transport();
1461
1808
  init_provider_cli_shared();
1462
1809
  init_chat_message_normalization();
1810
+ init_read_chat_contract();
1463
1811
  init_provider_cli_parse();
1464
1812
  init_provider_cli_config();
1465
1813
  init_provider_cli_runtime();
@@ -2605,6 +2953,9 @@ var init_provider_cli_adapter = __esm({
2605
2953
  runtimeSettings: this.runtimeSettings
2606
2954
  });
2607
2955
  const parsed = this.cliScripts.parseOutput(input);
2956
+ if (parsed && typeof parsed === "object") {
2957
+ Object.assign(parsed, validateReadChatResultPayload(parsed, `${this.cliType} parseOutput`));
2958
+ }
2608
2959
  const refinedStatus = this.refineDetectedStatus(typeof parsed?.status === "string" ? parsed.status : null, input.recentBuffer, input.screenText);
2609
2960
  if (parsed && refinedStatus && parsed.status !== refinedStatus) {
2610
2961
  parsed.status = refinedStatus;
@@ -5070,351 +5421,142 @@ var CdpDomHandlers = class {
5070
5421
  * args:
5071
5422
  * ideType?: string — IDE type hint
5072
5423
  * sessionId?: string — agent webview session ID
5073
- */
5074
- async handleDomDebug(args) {
5075
- if (!this.getCdp()?.isConnected) return { success: false, error: "CDP not connected" };
5076
- const sessionId = args?.sessionId;
5077
- const expression = `(() => {
5078
- const result = {
5079
- url: location.href,
5080
- title: document.title,
5081
- viewport: { w: window.innerWidth, h: window.innerHeight },
5082
-
5083
- // Input field info
5084
- inputs: [],
5085
- // Textarea info
5086
- textareas: [],
5087
- // Contenteditable info
5088
- editables: [],
5089
- // Buttons (send, submit etc)
5090
- buttons: [],
5091
- // iframes (agent webviews)
5092
- iframes: [],
5093
- // role="textbox" info
5094
- textboxes: [],
5095
- };
5096
-
5097
- // Input fields
5098
- document.querySelectorAll('input[type="text"], input:not([type])').forEach((el, i) => {
5099
- if (i >= 10) return;
5100
- result.inputs.push({
5101
- tag: 'input',
5102
- id: el.id || null,
5103
- class: (el.className || '').toString().slice(0, 150),
5104
- placeholder: el.getAttribute('placeholder') || null,
5105
- name: el.name || null,
5106
- value: el.value?.slice(0, 100) || null,
5107
- visible: el.offsetParent !== null,
5108
- 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; } })(),
5109
- });
5110
- });
5111
-
5112
- // textarea
5113
- document.querySelectorAll('textarea').forEach((el, i) => {
5114
- if (i >= 10) return;
5115
- result.textareas.push({
5116
- id: el.id || null,
5117
- class: (el.className || '').toString().slice(0, 150),
5118
- placeholder: el.getAttribute('placeholder') || null,
5119
- rows: el.rows,
5120
- value: el.value?.slice(0, 100) || null,
5121
- visible: el.offsetParent !== null,
5122
- 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; } })(),
5123
- });
5124
- });
5125
-
5126
- // contenteditable
5127
- document.querySelectorAll('[contenteditable="true"]').forEach((el, i) => {
5128
- if (i >= 10) return;
5129
- result.editables.push({
5130
- tag: el.tagName?.toLowerCase(),
5131
- id: el.id || null,
5132
- class: (el.className || '').toString().slice(0, 150),
5133
- role: el.getAttribute('role') || null,
5134
- text: (el.textContent || '').trim().slice(0, 100),
5135
- visible: el.offsetParent !== null,
5136
- 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; } })(),
5137
- });
5138
- });
5139
-
5140
- // role="textbox"
5141
- document.querySelectorAll('[role="textbox"]').forEach((el, i) => {
5142
- if (i >= 10) return;
5143
- result.textboxes.push({
5144
- tag: el.tagName?.toLowerCase(),
5145
- id: el.id || null,
5146
- class: (el.className || '').toString().slice(0, 150),
5147
- 'aria-label': el.getAttribute('aria-label') || null,
5148
- text: (el.textContent || '').trim().slice(0, 100),
5149
- visible: el.offsetParent !== null,
5150
- 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; } })(),
5151
- });
5152
- });
5153
-
5154
- // Buttons (send, submit, accept, reject, approve etc)
5155
- const btnKeywords = /send|submit|accept|reject|approve|deny|cancel|confirm|run|execute|apply/i;
5156
- document.querySelectorAll('button, [role="button"], input[type="submit"]').forEach((el, i) => {
5157
- const text = (el.textContent || el.getAttribute('aria-label') || '').trim();
5158
- if (i < 30 && (text.length < 30 || btnKeywords.test(text))) {
5159
- result.buttons.push({
5160
- tag: el.tagName?.toLowerCase(),
5161
- id: el.id || null,
5162
- class: (el.className || '').toString().slice(0, 150),
5163
- text: text.slice(0, 80),
5164
- 'aria-label': el.getAttribute('aria-label') || null,
5165
- disabled: el.disabled || el.getAttribute('disabled') !== null,
5166
- visible: el.offsetParent !== null,
5167
- 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; } })(),
5168
- });
5169
- }
5170
- });
5171
-
5172
- // iframes
5173
- document.querySelectorAll('iframe, webview').forEach((el, i) => {
5174
- if (i >= 20) return;
5175
- result.iframes.push({
5176
- tag: el.tagName?.toLowerCase(),
5177
- id: el.id || null,
5178
- class: (el.className || '').toString().slice(0, 150),
5179
- src: el.getAttribute('src')?.slice(0, 200) || null,
5180
- title: el.getAttribute('title') || null,
5181
- visible: el.offsetParent !== null,
5182
- 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; } })(),
5183
- });
5184
- });
5185
-
5186
- return JSON.stringify(result);
5187
- })()`;
5188
- try {
5189
- let raw;
5190
- if (sessionId) {
5191
- raw = await this.getCdp().evaluateInSessionFrame(sessionId, expression);
5192
- } else {
5193
- raw = await this.getCdp().evaluate(expression, 3e4);
5194
- }
5195
- const parsed = typeof raw === "string" ? JSON.parse(raw) : raw;
5196
- return { success: true, ...parsed };
5197
- } catch (e) {
5198
- return { success: false, error: e.message };
5199
- }
5200
- }
5201
- };
5202
-
5203
- // src/providers/ide-provider-instance.ts
5204
- import * as crypto2 from "crypto";
5205
-
5206
- // src/providers/io-contracts.ts
5207
- function normalizeInputEnvelope(input) {
5208
- const normalized = normalizeInputEnvelopePayload(input);
5209
- const textFallback = normalized.textFallback ?? flattenInputParts(normalized.parts);
5210
- return {
5211
- parts: normalized.parts,
5212
- textFallback,
5213
- ...normalized.metadata ? { metadata: normalized.metadata } : {}
5214
- };
5215
- }
5216
- function normalizeMessageParts(content) {
5217
- if (typeof content === "string") return [{ type: "text", text: content }];
5218
- if (!Array.isArray(content)) {
5219
- if (content && typeof content === "object" && typeof content.text === "string") {
5220
- return [{ type: "text", text: String(content.text) }];
5221
- }
5222
- return [];
5223
- }
5224
- const parts = [];
5225
- for (const raw of content) {
5226
- if (typeof raw === "string") {
5227
- parts.push({ type: "text", text: raw });
5228
- continue;
5229
- }
5230
- if (!raw || typeof raw !== "object") continue;
5231
- const part = normalizeMessagePartObject(raw);
5232
- if (part) parts.push(part);
5233
- }
5234
- return parts;
5235
- }
5236
- function flattenMessageParts(parts) {
5237
- return parts.map((part) => {
5238
- if (part.type === "text") return part.text;
5239
- if (part.type === "resource") return part.resource.text || "";
5240
- return "";
5241
- }).filter((value) => value.length > 0).join("\n");
5242
- }
5243
- function normalizeInputEnvelopePayload(input) {
5244
- if (typeof input === "string") {
5245
- return { parts: [{ type: "text", text: input }], textFallback: input };
5246
- }
5247
- if (!input || typeof input !== "object") {
5248
- return { parts: [], textFallback: "" };
5249
- }
5250
- const record = input;
5251
- const nestedInput = record.input;
5252
- if (nestedInput && typeof nestedInput === "object") {
5253
- const nested = nestedInput;
5254
- return {
5255
- parts: normalizeInputParts(nested.parts ?? nested.prompt),
5256
- textFallback: typeof nested.textFallback === "string" ? nested.textFallback : void 0,
5257
- metadata: normalizeInputMetadata(nested.metadata)
5258
- };
5259
- }
5260
- const directText = typeof record.text === "string" ? record.text : typeof record.message === "string" ? record.message : void 0;
5261
- if (directText !== void 0) {
5262
- return { parts: [{ type: "text", text: directText }], textFallback: directText };
5263
- }
5264
- const directParts = normalizeInputParts(record.parts ?? record.prompt);
5265
- return {
5266
- parts: directParts,
5267
- textFallback: typeof record.textFallback === "string" ? record.textFallback : void 0,
5268
- metadata: normalizeInputMetadata(record.metadata)
5269
- };
5270
- }
5271
- function normalizeInputMetadata(value) {
5272
- if (!value || typeof value !== "object") return void 0;
5273
- const record = value;
5274
- const metadata = {};
5275
- if (record.source === "dashboard" || record.source === "shortcut_api" || record.source === "provider_script" || record.source === "session_replay") {
5276
- metadata.source = record.source;
5277
- }
5278
- if (typeof record.clientTimestamp === "number" && Number.isFinite(record.clientTimestamp)) {
5279
- metadata.clientTimestamp = record.clientTimestamp;
5280
- }
5281
- return Object.keys(metadata).length > 0 ? metadata : void 0;
5282
- }
5283
- function normalizeInputParts(value) {
5284
- if (!Array.isArray(value)) return [];
5285
- const parts = [];
5286
- for (const raw of value) {
5287
- if (typeof raw === "string") {
5288
- parts.push({ type: "text", text: raw });
5289
- continue;
5290
- }
5291
- if (!raw || typeof raw !== "object") continue;
5292
- const part = normalizeInputPartObject(raw);
5293
- if (part) parts.push(part);
5294
- }
5295
- return parts;
5296
- }
5297
- function normalizeInputPartObject(raw) {
5298
- const type = raw.type;
5299
- if (type === "text" && typeof raw.text === "string") {
5300
- return { type, text: raw.text };
5301
- }
5302
- if (type === "image" && typeof raw.mimeType === "string") {
5303
- return {
5304
- type,
5305
- mimeType: raw.mimeType,
5306
- ...typeof raw.uri === "string" ? { uri: raw.uri } : {},
5307
- ...typeof raw.data === "string" ? { data: raw.data } : {},
5308
- ...typeof raw.alt === "string" ? { alt: raw.alt } : {}
5309
- };
5310
- }
5311
- if (type === "audio" && typeof raw.mimeType === "string") {
5312
- return {
5313
- type,
5314
- mimeType: raw.mimeType,
5315
- ...typeof raw.uri === "string" ? { uri: raw.uri } : {},
5316
- ...typeof raw.data === "string" ? { data: raw.data } : {},
5317
- ...typeof raw.transcript === "string" ? { transcript: raw.transcript } : {}
5318
- };
5319
- }
5320
- if (type === "video" && typeof raw.mimeType === "string") {
5321
- return {
5322
- type,
5323
- mimeType: raw.mimeType,
5324
- ...typeof raw.uri === "string" ? { uri: raw.uri } : {},
5325
- ...typeof raw.data === "string" ? { data: raw.data } : {},
5326
- ...typeof raw.posterUri === "string" ? { posterUri: raw.posterUri } : {}
5327
- };
5328
- }
5329
- if (type === "resource" && typeof raw.uri === "string") {
5330
- return {
5331
- type,
5332
- uri: raw.uri,
5333
- ...typeof raw.mimeType === "string" ? { mimeType: raw.mimeType } : {},
5334
- ...typeof raw.name === "string" ? { name: raw.name } : {},
5335
- ...typeof raw.text === "string" ? { text: raw.text } : {},
5336
- ...typeof raw.data === "string" ? { data: raw.data } : {}
5337
- };
5338
- }
5339
- if (type === "resource_link" && typeof raw.uri === "string") {
5340
- return {
5341
- type: "resource",
5342
- uri: raw.uri,
5343
- ...typeof raw.mimeType === "string" ? { mimeType: raw.mimeType } : {},
5344
- ...typeof raw.name === "string" ? { name: raw.name } : {}
5345
- };
5346
- }
5347
- return null;
5348
- }
5349
- function normalizeMessagePartObject(raw) {
5350
- const type = raw.type;
5351
- if (type === "text" && typeof raw.text === "string") {
5352
- return { type, text: raw.text };
5353
- }
5354
- if (type === "image" && typeof raw.mimeType === "string") {
5355
- return {
5356
- type,
5357
- mimeType: raw.mimeType,
5358
- ...typeof raw.uri === "string" ? { uri: raw.uri } : {},
5359
- ...typeof raw.data === "string" ? { data: raw.data } : {}
5360
- };
5361
- }
5362
- if (type === "audio" && typeof raw.mimeType === "string") {
5363
- return {
5364
- type,
5365
- mimeType: raw.mimeType,
5366
- ...typeof raw.uri === "string" ? { uri: raw.uri } : {},
5367
- ...typeof raw.data === "string" ? { data: raw.data } : {},
5368
- ...typeof raw.transcript === "string" ? { transcript: raw.transcript } : {}
5369
- };
5370
- }
5371
- if (type === "video" && typeof raw.mimeType === "string") {
5372
- return {
5373
- type,
5374
- mimeType: raw.mimeType,
5375
- ...typeof raw.uri === "string" ? { uri: raw.uri } : {},
5376
- ...typeof raw.data === "string" ? { data: raw.data } : {},
5377
- ...typeof raw.posterUri === "string" ? { posterUri: raw.posterUri } : {}
5378
- };
5379
- }
5380
- if (type === "resource_link" && typeof raw.uri === "string" && typeof raw.name === "string") {
5381
- return {
5382
- type,
5383
- uri: raw.uri,
5384
- name: raw.name,
5385
- ...typeof raw.mimeType === "string" ? { mimeType: raw.mimeType } : {},
5386
- ...typeof raw.size === "number" ? { size: raw.size } : {}
5387
- };
5388
- }
5389
- if (type === "resource" && raw.resource && typeof raw.resource === "object") {
5390
- const resource = raw.resource;
5391
- if (typeof resource.uri !== "string") return null;
5392
- return {
5393
- type,
5394
- resource: {
5395
- uri: resource.uri,
5396
- ...typeof resource.mimeType === "string" || resource.mimeType === null ? { mimeType: resource.mimeType } : {},
5397
- ...typeof resource.text === "string" ? { text: resource.text } : {},
5398
- ...typeof resource.blob === "string" ? { blob: resource.blob } : {}
5424
+ */
5425
+ async handleDomDebug(args) {
5426
+ if (!this.getCdp()?.isConnected) return { success: false, error: "CDP not connected" };
5427
+ const sessionId = args?.sessionId;
5428
+ const expression = `(() => {
5429
+ const result = {
5430
+ url: location.href,
5431
+ title: document.title,
5432
+ viewport: { w: window.innerWidth, h: window.innerHeight },
5433
+
5434
+ // Input field info
5435
+ inputs: [],
5436
+ // Textarea info
5437
+ textareas: [],
5438
+ // Contenteditable info
5439
+ editables: [],
5440
+ // Buttons (send, submit etc)
5441
+ buttons: [],
5442
+ // iframes (agent webviews)
5443
+ iframes: [],
5444
+ // role="textbox" info
5445
+ textboxes: [],
5446
+ };
5447
+
5448
+ // Input fields
5449
+ document.querySelectorAll('input[type="text"], input:not([type])').forEach((el, i) => {
5450
+ if (i >= 10) return;
5451
+ result.inputs.push({
5452
+ tag: 'input',
5453
+ id: el.id || null,
5454
+ class: (el.className || '').toString().slice(0, 150),
5455
+ placeholder: el.getAttribute('placeholder') || null,
5456
+ name: el.name || null,
5457
+ value: el.value?.slice(0, 100) || null,
5458
+ visible: el.offsetParent !== null,
5459
+ 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; } })(),
5460
+ });
5461
+ });
5462
+
5463
+ // textarea
5464
+ document.querySelectorAll('textarea').forEach((el, i) => {
5465
+ if (i >= 10) return;
5466
+ result.textareas.push({
5467
+ id: el.id || null,
5468
+ class: (el.className || '').toString().slice(0, 150),
5469
+ placeholder: el.getAttribute('placeholder') || null,
5470
+ rows: el.rows,
5471
+ value: el.value?.slice(0, 100) || null,
5472
+ visible: el.offsetParent !== null,
5473
+ 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; } })(),
5474
+ });
5475
+ });
5476
+
5477
+ // contenteditable
5478
+ document.querySelectorAll('[contenteditable="true"]').forEach((el, i) => {
5479
+ if (i >= 10) return;
5480
+ result.editables.push({
5481
+ tag: el.tagName?.toLowerCase(),
5482
+ id: el.id || null,
5483
+ class: (el.className || '').toString().slice(0, 150),
5484
+ role: el.getAttribute('role') || null,
5485
+ text: (el.textContent || '').trim().slice(0, 100),
5486
+ visible: el.offsetParent !== null,
5487
+ 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; } })(),
5488
+ });
5489
+ });
5490
+
5491
+ // role="textbox"
5492
+ document.querySelectorAll('[role="textbox"]').forEach((el, i) => {
5493
+ if (i >= 10) return;
5494
+ result.textboxes.push({
5495
+ tag: el.tagName?.toLowerCase(),
5496
+ id: el.id || null,
5497
+ class: (el.className || '').toString().slice(0, 150),
5498
+ 'aria-label': el.getAttribute('aria-label') || null,
5499
+ text: (el.textContent || '').trim().slice(0, 100),
5500
+ visible: el.offsetParent !== null,
5501
+ 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; } })(),
5502
+ });
5503
+ });
5504
+
5505
+ // Buttons (send, submit, accept, reject, approve etc)
5506
+ const btnKeywords = /send|submit|accept|reject|approve|deny|cancel|confirm|run|execute|apply/i;
5507
+ document.querySelectorAll('button, [role="button"], input[type="submit"]').forEach((el, i) => {
5508
+ const text = (el.textContent || el.getAttribute('aria-label') || '').trim();
5509
+ if (i < 30 && (text.length < 30 || btnKeywords.test(text))) {
5510
+ result.buttons.push({
5511
+ tag: el.tagName?.toLowerCase(),
5512
+ id: el.id || null,
5513
+ class: (el.className || '').toString().slice(0, 150),
5514
+ text: text.slice(0, 80),
5515
+ 'aria-label': el.getAttribute('aria-label') || null,
5516
+ disabled: el.disabled || el.getAttribute('disabled') !== null,
5517
+ visible: el.offsetParent !== null,
5518
+ 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; } })(),
5519
+ });
5520
+ }
5521
+ });
5522
+
5523
+ // iframes
5524
+ document.querySelectorAll('iframe, webview').forEach((el, i) => {
5525
+ if (i >= 20) return;
5526
+ result.iframes.push({
5527
+ tag: el.tagName?.toLowerCase(),
5528
+ id: el.id || null,
5529
+ class: (el.className || '').toString().slice(0, 150),
5530
+ src: el.getAttribute('src')?.slice(0, 200) || null,
5531
+ title: el.getAttribute('title') || null,
5532
+ visible: el.offsetParent !== null,
5533
+ 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; } })(),
5534
+ });
5535
+ });
5536
+
5537
+ return JSON.stringify(result);
5538
+ })()`;
5539
+ try {
5540
+ let raw;
5541
+ if (sessionId) {
5542
+ raw = await this.getCdp().evaluateInSessionFrame(sessionId, expression);
5543
+ } else {
5544
+ raw = await this.getCdp().evaluate(expression, 3e4);
5399
5545
  }
5400
- };
5546
+ const parsed = typeof raw === "string" ? JSON.parse(raw) : raw;
5547
+ return { success: true, ...parsed };
5548
+ } catch (e) {
5549
+ return { success: false, error: e.message };
5550
+ }
5401
5551
  }
5402
- return null;
5403
- }
5404
- function flattenInputParts(parts) {
5405
- return parts.map((part) => {
5406
- if (part.type === "text") return part.text;
5407
- if (part.type === "audio") return part.transcript || "";
5408
- if (part.type === "resource") return part.text || "";
5409
- return "";
5410
- }).filter((value) => value.length > 0).join("\n");
5411
- }
5552
+ };
5412
5553
 
5413
- // src/providers/contracts.ts
5414
- function flattenContent(content) {
5415
- if (typeof content === "string") return content;
5416
- return flattenMessageParts(normalizeMessageParts(content));
5417
- }
5554
+ // src/providers/ide-provider-instance.ts
5555
+ init_contracts();
5556
+ import * as crypto2 from "crypto";
5557
+
5558
+ // src/providers/extension-provider-instance.ts
5559
+ init_contracts();
5418
5560
 
5419
5561
  // src/providers/status-monitor.ts
5420
5562
  var DEFAULT_MONITOR_CONFIG = {
@@ -5529,6 +5671,7 @@ var StatusMonitor = class {
5529
5671
  };
5530
5672
 
5531
5673
  // src/providers/control-effects.ts
5674
+ init_contracts();
5532
5675
  init_chat_message_normalization();
5533
5676
  function extractProviderControlValues(controls, data) {
5534
5677
  if (!data || typeof data !== "object") return void 0;
@@ -5650,38 +5793,37 @@ function buildPersistedProviderEffectMessage(effect) {
5650
5793
  return null;
5651
5794
  }
5652
5795
  function normalizeControlListResult(data) {
5653
- if (data && typeof data === "object" && Array.isArray(data.options)) {
5654
- return {
5655
- options: normalizeControlOptions(data.options),
5656
- ...isScalarControlValue(data.currentValue) ? { currentValue: data.currentValue } : {},
5657
- ...typeof data.error === "string" ? { error: data.error } : {}
5658
- };
5796
+ if (!data || typeof data !== "object" || !Array.isArray(data.options)) {
5797
+ throw new Error("Provider control list results must use the typed shape { options, currentValue?, error? }");
5659
5798
  }
5660
- const rawOptions = Array.isArray(data?.models) ? data.models : Array.isArray(data?.modes) ? data.modes : Array.isArray(data?.options) ? data.options : [];
5661
- const options = normalizeControlOptions(rawOptions);
5662
5799
  return {
5663
- options,
5664
- ...isScalarControlValue(data?.current) ? { currentValue: data.current } : {},
5665
- ...isScalarControlValue(data?.currentValue) ? { currentValue: data.currentValue } : {},
5666
- ...typeof data?.error === "string" ? { error: data.error } : {}
5800
+ options: normalizeControlOptions(data.options),
5801
+ ...isScalarControlValue(data.currentValue) ? { currentValue: data.currentValue } : {},
5802
+ ...typeof data.error === "string" ? { error: data.error } : {}
5667
5803
  };
5668
5804
  }
5669
5805
  function normalizeControlSetResult(data) {
5670
- const currentValue = isScalarControlValue(data?.currentValue) ? data.currentValue : isScalarControlValue(data?.value) ? data.value : void 0;
5806
+ if (!data || typeof data !== "object" || typeof data.ok !== "boolean") {
5807
+ throw new Error("Provider control set results must use the typed shape { ok, currentValue?, effects?, error? }");
5808
+ }
5809
+ const currentValue = isScalarControlValue(data.currentValue) ? data.currentValue : isScalarControlValue(data.value) ? data.value : void 0;
5671
5810
  return {
5672
- ok: data?.ok === true || data?.success === true,
5811
+ ok: data.ok,
5673
5812
  ...currentValue !== void 0 ? { currentValue } : {},
5674
- ...Array.isArray(data?.effects) ? { effects: normalizeProviderEffects(data) } : {},
5675
- ...typeof data?.error === "string" ? { error: data.error } : {}
5813
+ ...Array.isArray(data.effects) ? { effects: normalizeProviderEffects(data) } : {},
5814
+ ...typeof data.error === "string" ? { error: data.error } : {}
5676
5815
  };
5677
5816
  }
5678
5817
  function normalizeControlInvokeResult(data) {
5679
- const currentValue = isScalarControlValue(data?.currentValue) ? data.currentValue : isScalarControlValue(data?.value) ? data.value : void 0;
5818
+ if (!data || typeof data !== "object" || typeof data.ok !== "boolean") {
5819
+ throw new Error("Provider control invoke results must use the typed shape { ok, currentValue?, effects?, error? }");
5820
+ }
5821
+ const currentValue = isScalarControlValue(data.currentValue) ? data.currentValue : isScalarControlValue(data.value) ? data.value : void 0;
5680
5822
  return {
5681
- ok: data?.ok === true || data?.success === true,
5823
+ ok: data.ok,
5682
5824
  ...currentValue !== void 0 ? { currentValue } : {},
5683
- ...Array.isArray(data?.effects) ? { effects: normalizeProviderEffects(data) } : {},
5684
- ...typeof data?.error === "string" ? { error: data.error } : {}
5825
+ ...Array.isArray(data.effects) ? { effects: normalizeProviderEffects(data) } : {},
5826
+ ...typeof data.error === "string" ? { error: data.error } : {}
5685
5827
  };
5686
5828
  }
5687
5829
  function normalizeControlOptions(options) {
@@ -6572,19 +6714,37 @@ var ExtensionProviderInstance = class {
6572
6714
  );
6573
6715
  }
6574
6716
  }
6717
+ buildSyntheticTurnKey(message, occurrence) {
6718
+ const role = typeof message?.role === "string" ? message.role : "";
6719
+ const kind = typeof message?.kind === "string" ? message.kind : "";
6720
+ const senderName = typeof message?.senderName === "string" ? message.senderName : "";
6721
+ const content = flattenContent(message?.content).replace(/\s+/g, " ").trim().slice(0, 500);
6722
+ return `${role}|${kind}|${senderName}|${content}|${occurrence}`;
6723
+ }
6575
6724
  /**
6576
- * Assign stable receivedAt to extension messages.
6577
- * Same pattern as IdeProviderInstance.readChat() prevByHash
6578
- * preserves first-seen timestamp across polling cycles.
6725
+ * Assign stable receivedAt / synthetic _turnKey to extension messages.
6726
+ * Same transcript should keep the same identity across polling cycles and
6727
+ * stream resets, while repeated identical text later in the transcript still
6728
+ * produces a distinct completion marker via the occurrence suffix.
6579
6729
  */
6580
6730
  assignReceivedAt(messages) {
6581
6731
  const now = Date.now();
6582
6732
  const nextHashes = /* @__PURE__ */ new Map();
6733
+ const occurrenceByBaseKey = /* @__PURE__ */ new Map();
6583
6734
  for (const msg of messages) {
6584
- const hash = `${msg.role}:${(msg.content || "").slice(0, 100)}`;
6585
- const prevTime = this.prevMessageHashes.get(hash);
6735
+ const explicitTurnKey = typeof msg?._turnKey === "string" && msg._turnKey.trim() ? msg._turnKey.trim() : "";
6736
+ const explicitId = typeof msg?.id === "string" && msg.id.trim() ? `id:${msg.id.trim()}` : "";
6737
+ const explicitIndex = typeof msg?.index === "number" && Number.isFinite(msg.index) ? `idx:${msg.index}` : "";
6738
+ const baseKey = explicitTurnKey || explicitId || explicitIndex || `${msg?.role || ""}:${flattenContent(msg?.content || "").slice(0, 500)}`;
6739
+ const occurrence = (occurrenceByBaseKey.get(baseKey) || 0) + 1;
6740
+ occurrenceByBaseKey.set(baseKey, occurrence);
6741
+ const syntheticTurnKey = explicitTurnKey || explicitId || explicitIndex || this.buildSyntheticTurnKey(msg, occurrence);
6742
+ if (!explicitTurnKey && !explicitId && !explicitIndex) {
6743
+ msg._turnKey = syntheticTurnKey;
6744
+ }
6745
+ const prevTime = this.prevMessageHashes.get(syntheticTurnKey);
6586
6746
  msg.receivedAt = prevTime || now;
6587
- nextHashes.set(hash, msg.receivedAt);
6747
+ nextHashes.set(syntheticTurnKey, msg.receivedAt);
6588
6748
  }
6589
6749
  this.prevMessageHashes = nextHashes;
6590
6750
  return normalizeChatMessages(messages);
@@ -6661,6 +6821,7 @@ ${effect.notification.body || ""}`.trim();
6661
6821
 
6662
6822
  // src/providers/ide-provider-instance.ts
6663
6823
  init_logger();
6824
+ init_read_chat_contract();
6664
6825
 
6665
6826
  // src/providers/approval-utils.ts
6666
6827
  var DEFAULT_APPROVAL_POSITIVE_HINTS = [
@@ -6942,7 +7103,7 @@ var IdeProviderInstance = class {
6942
7103
  }
6943
7104
  }
6944
7105
  if (!raw || typeof raw !== "object") return;
6945
- const chat = raw;
7106
+ const chat = validateReadChatResultPayload(raw, `${this.type} readChat`);
6946
7107
  let { activeModal } = chat;
6947
7108
  if (activeModal) {
6948
7109
  const w = activeModal.width ?? Infinity;
@@ -8087,6 +8248,65 @@ function reconcileIdeRuntimeSessions(instanceManager, sessionRegistry) {
8087
8248
  init_logger();
8088
8249
 
8089
8250
  // src/commands/chat-commands.ts
8251
+ init_contracts();
8252
+
8253
+ // src/providers/provider-input-support.ts
8254
+ var VALID_INPUT_MEDIA_TYPES = /* @__PURE__ */ new Set(["text", "image", "audio", "video", "resource"]);
8255
+ function getProviderLabel(provider) {
8256
+ return provider?.name || provider?.type || "This provider";
8257
+ }
8258
+ function hasNonEmptyFallbackText(input) {
8259
+ return typeof input.textFallback === "string" && input.textFallback.trim().length > 0;
8260
+ }
8261
+ function getRequestedInputMediaTypes(input) {
8262
+ const types = /* @__PURE__ */ new Set();
8263
+ if (hasNonEmptyFallbackText(input) && !input.parts.some((part) => part.type === "text")) {
8264
+ types.add("text");
8265
+ }
8266
+ for (const part of input.parts) {
8267
+ if (VALID_INPUT_MEDIA_TYPES.has(part.type)) {
8268
+ types.add(part.type);
8269
+ }
8270
+ }
8271
+ return Array.from(types);
8272
+ }
8273
+ function getEffectiveSemanticPartCount(input) {
8274
+ let count = input.parts.length;
8275
+ if (hasNonEmptyFallbackText(input) && !input.parts.some((part) => part.type === "text")) {
8276
+ count += 1;
8277
+ }
8278
+ return count;
8279
+ }
8280
+ function assertTextOnlyInput(provider, input) {
8281
+ const unsupported = getRequestedInputMediaTypes(input).filter((type) => type !== "text");
8282
+ if (unsupported.length === 0) return;
8283
+ const label = getProviderLabel(provider);
8284
+ const suffix = unsupported.length === 1 ? "" : "s";
8285
+ throw new Error(`${label} only supports text input; unsupported input type${suffix}: ${unsupported.join(", ")}`);
8286
+ }
8287
+ function getDeclaredProviderInputSupport(provider) {
8288
+ const rawMediaTypes = Array.isArray(provider?.capabilities?.input?.mediaTypes) ? provider?.capabilities?.input?.mediaTypes.filter((type) => VALID_INPUT_MEDIA_TYPES.has(type)) : [];
8289
+ return {
8290
+ multipart: provider?.capabilities?.input?.multipart === true,
8291
+ mediaTypes: new Set(rawMediaTypes.length > 0 ? rawMediaTypes : ["text"])
8292
+ };
8293
+ }
8294
+ function assertProviderSupportsDeclaredInput(provider, input) {
8295
+ const label = getProviderLabel(provider);
8296
+ const support = getDeclaredProviderInputSupport(provider);
8297
+ const requestedTypes = getRequestedInputMediaTypes(input);
8298
+ const unsupported = requestedTypes.filter((type) => !support.mediaTypes.has(type));
8299
+ if (unsupported.length > 0) {
8300
+ const suffix = unsupported.length === 1 ? "" : "s";
8301
+ throw new Error(`${label} does not support input type${suffix}: ${unsupported.join(", ")}`);
8302
+ }
8303
+ if (getEffectiveSemanticPartCount(input) > 1 && !support.multipart) {
8304
+ throw new Error(`${label} does not support multipart input`);
8305
+ }
8306
+ }
8307
+
8308
+ // src/commands/chat-commands.ts
8309
+ init_read_chat_contract();
8090
8310
  init_logger();
8091
8311
 
8092
8312
  // src/logging/debug-config.ts
@@ -8268,10 +8488,15 @@ function isCliLikeTransport(transport) {
8268
8488
  function isExtensionTransport(transport) {
8269
8489
  return transport === "cdp-webview";
8270
8490
  }
8271
- function buildRecentSendKey(h, args, provider, text) {
8491
+ function buildRecentSendKey(h, args, provider, signature) {
8272
8492
  const transport = getTargetTransport(h, provider) || "unknown";
8273
8493
  const target = args?.targetSessionId || args?.agentType || h.currentSession?.providerType || h.currentProviderType || h.currentManagerKey || "unknown";
8274
- return `${transport}:${target}:${text.trim()}`;
8494
+ return `${transport}:${target}:${signature.trim()}`;
8495
+ }
8496
+ function buildSendInputSignature(input) {
8497
+ const text = typeof input.textFallback === "string" ? input.textFallback.trim() : "";
8498
+ if (text) return text;
8499
+ return JSON.stringify(input.parts || []);
8275
8500
  }
8276
8501
  function getSendChatInputEnvelope(args) {
8277
8502
  return normalizeInputEnvelope(args?.input ? { input: args.input } : args);
@@ -8424,14 +8649,20 @@ function computeReadChatSync(messages, cursor) {
8424
8649
  };
8425
8650
  }
8426
8651
  function buildReadChatCommandResult(payload, args) {
8427
- const messages = normalizeReadChatMessages(payload);
8652
+ let validatedPayload;
8653
+ try {
8654
+ validatedPayload = validateReadChatResultPayload(payload, "read_chat command result");
8655
+ } catch (error) {
8656
+ return { success: false, error: error?.message || String(error) };
8657
+ }
8658
+ const messages = normalizeReadChatMessages(validatedPayload);
8428
8659
  const cursor = normalizeReadChatCursor(args);
8429
8660
  if (!cursor.knownMessageCount && !cursor.lastMessageSignature && cursor.tailLimit > 0 && messages.length > cursor.tailLimit) {
8430
8661
  const tailMessages = messages.slice(-cursor.tailLimit);
8431
8662
  const lastMessageSignature = getChatMessageSignature(tailMessages[tailMessages.length - 1]);
8432
8663
  return {
8433
8664
  success: true,
8434
- ...payload,
8665
+ ...validatedPayload,
8435
8666
  messages: tailMessages,
8436
8667
  syncMode: "full",
8437
8668
  replaceFrom: 0,
@@ -8442,7 +8673,7 @@ function buildReadChatCommandResult(payload, args) {
8442
8673
  const sync = computeReadChatSync(messages, cursor);
8443
8674
  return {
8444
8675
  success: true,
8445
- ...payload,
8676
+ ...validatedPayload,
8446
8677
  messages: sync.messages,
8447
8678
  syncMode: sync.syncMode,
8448
8679
  replaceFrom: sync.replaceFrom,
@@ -8521,12 +8752,18 @@ async function handleReadChat(h, args) {
8521
8752
  const adapter = getTargetedCliAdapter(h, args, provider?.type);
8522
8753
  if (adapter) {
8523
8754
  _log(`${transport} adapter: ${adapter.cliType}`);
8524
- const status = adapter.getStatus();
8755
+ const parsedStatus = typeof adapter.getScriptParsedStatus === "function" ? parseMaybeJson(adapter.getScriptParsedStatus()) : null;
8756
+ const parsedRecord = parsedStatus && typeof parsedStatus === "object" ? parsedStatus : null;
8757
+ const status = parsedRecord || adapter.getStatus();
8758
+ const title = typeof parsedRecord?.title === "string" ? parsedRecord.title : void 0;
8759
+ const providerSessionId = typeof parsedRecord?.providerSessionId === "string" ? parsedRecord.providerSessionId : void 0;
8525
8760
  if (status) {
8526
8761
  return buildReadChatCommandResult({
8527
8762
  messages: status.messages || [],
8528
8763
  status: status.status,
8529
- activeModal: status.activeModal
8764
+ activeModal: status.activeModal,
8765
+ ...title ? { title } : {},
8766
+ ...providerSessionId ? { providerSessionId } : {}
8530
8767
  }, args);
8531
8768
  }
8532
8769
  }
@@ -8544,25 +8781,26 @@ async function handleReadChat(h, args) {
8544
8781
  }
8545
8782
  }
8546
8783
  if (parsed && typeof parsed === "object") {
8547
- _log(`Extension OK: ${parsed.messages?.length || 0} msgs`);
8784
+ const validated = validateReadChatResultPayload(parsed, "extension read_chat");
8785
+ _log(`Extension OK: ${validated.messages?.length || 0} msgs`);
8548
8786
  traceProviderEvent(args, "provider", "extension.read_chat.success", {
8549
8787
  h,
8550
8788
  provider,
8551
8789
  payload: {
8552
8790
  method: "evaluateProviderScript",
8553
8791
  result: evalResult.result,
8554
- parsed,
8555
- messageCount: Array.isArray(parsed.messages) ? parsed.messages.length : 0
8792
+ parsed: validated,
8793
+ messageCount: Array.isArray(validated.messages) ? validated.messages.length : 0
8556
8794
  }
8557
8795
  });
8558
8796
  h.historyWriter.appendNewMessages(
8559
8797
  provider?.type || "unknown_extension",
8560
- toHistoryPersistedMessages(normalizeReadChatMessages(parsed)),
8561
- parsed.title,
8798
+ toHistoryPersistedMessages(normalizeReadChatMessages(validated)),
8799
+ validated.title,
8562
8800
  args?.targetSessionId,
8563
8801
  historySessionId
8564
8802
  );
8565
- return buildReadChatCommandResult(parsed, args);
8803
+ return buildReadChatCommandResult(validated, args);
8566
8804
  }
8567
8805
  }
8568
8806
  } catch (e) {
@@ -8617,15 +8855,16 @@ async function handleReadChat(h, args) {
8617
8855
  }
8618
8856
  }
8619
8857
  if (parsed && typeof parsed === "object") {
8620
- _log(`Webview OK: ${parsed.messages?.length || 0} msgs`);
8858
+ const validated = validateReadChatResultPayload(parsed, "webview read_chat");
8859
+ _log(`Webview OK: ${validated.messages?.length || 0} msgs`);
8621
8860
  h.historyWriter.appendNewMessages(
8622
8861
  provider?.type || getCurrentProviderType(h, "unknown_webview"),
8623
- toHistoryPersistedMessages(normalizeReadChatMessages(parsed)),
8624
- parsed.title,
8862
+ toHistoryPersistedMessages(normalizeReadChatMessages(validated)),
8863
+ validated.title,
8625
8864
  args?.targetSessionId,
8626
8865
  historySessionId
8627
8866
  );
8628
- return buildReadChatCommandResult(parsed, args);
8867
+ return buildReadChatCommandResult(validated, args);
8629
8868
  }
8630
8869
  }
8631
8870
  } catch (e) {
@@ -8646,25 +8885,26 @@ async function handleReadChat(h, args) {
8646
8885
  }
8647
8886
  }
8648
8887
  if (parsed && typeof parsed === "object" && parsed.messages?.length > 0) {
8649
- _log(`OK: ${parsed.messages?.length} msgs`);
8888
+ const validated = validateReadChatResultPayload(parsed, "ide read_chat");
8889
+ _log(`OK: ${validated.messages?.length} msgs`);
8650
8890
  traceProviderEvent(args, "provider", "ide.read_chat.success", {
8651
8891
  h,
8652
8892
  provider,
8653
8893
  payload: {
8654
8894
  method: "evaluate",
8655
8895
  result: evalResult.result,
8656
- parsed,
8657
- messageCount: Array.isArray(parsed.messages) ? parsed.messages.length : 0
8896
+ parsed: validated,
8897
+ messageCount: Array.isArray(validated.messages) ? validated.messages.length : 0
8658
8898
  }
8659
8899
  });
8660
8900
  h.historyWriter.appendNewMessages(
8661
8901
  provider?.type || getCurrentProviderType(h, "unknown_ide"),
8662
- toHistoryPersistedMessages(normalizeReadChatMessages(parsed)),
8663
- parsed.title,
8902
+ toHistoryPersistedMessages(normalizeReadChatMessages(validated)),
8903
+ validated.title,
8664
8904
  args?.targetSessionId,
8665
8905
  historySessionId
8666
8906
  );
8667
- return buildReadChatCommandResult(parsed, args);
8907
+ return buildReadChatCommandResult(validated, args);
8668
8908
  }
8669
8909
  }
8670
8910
  } catch (e) {
@@ -8682,11 +8922,12 @@ async function handleReadChat(h, args) {
8682
8922
  async function handleSendChat(h, args) {
8683
8923
  const input = getSendChatInputEnvelope(args);
8684
8924
  const text = input.textFallback;
8685
- if (!text) return { success: false, error: "text required" };
8925
+ const hasInput = input.parts.length > 0 || typeof text === "string" && text.trim().length > 0;
8926
+ if (!hasInput) return { success: false, error: "input required" };
8686
8927
  const _log = (msg) => LOG.debug("Command", `[send_chat] ${msg}`);
8687
8928
  const provider = h.getProvider(args?.agentType);
8688
8929
  const transport = getTargetTransport(h, provider);
8689
- const dedupeKey = buildRecentSendKey(h, args, provider, text);
8930
+ const dedupeKey = buildRecentSendKey(h, args, provider, buildSendInputSignature(input));
8690
8931
  const _logSendSuccess = (method, targetAgent) => {
8691
8932
  return { success: true, sent: true, method, targetAgent };
8692
8933
  };
@@ -8694,11 +8935,26 @@ async function handleSendChat(h, args) {
8694
8935
  _log(`Suppressed duplicate send for ${dedupeKey}`);
8695
8936
  return { success: true, sent: false, deduplicated: true };
8696
8937
  }
8697
- if (isCliLikeTransport(transport)) {
8938
+ if (transport === "acp") {
8939
+ const target = getTargetInstance(h, args);
8940
+ if (!target || target.category !== "acp") {
8941
+ return { success: false, error: `ACP instance not found for ${provider?.type || args?.agentType || "unknown"}` };
8942
+ }
8943
+ try {
8944
+ assertProviderSupportsDeclaredInput(provider, input);
8945
+ target.onEvent("send_message", { input });
8946
+ return _logSendSuccess("acp-instance", target.type);
8947
+ } catch (e) {
8948
+ return { success: false, error: `acp send failed: ${e.message}` };
8949
+ }
8950
+ }
8951
+ if (transport === "pty") {
8698
8952
  const adapter = getTargetedCliAdapter(h, args, provider?.type);
8699
8953
  if (adapter) {
8700
8954
  _log(`${transport} adapter: ${adapter.cliType}`);
8701
8955
  try {
8956
+ assertTextOnlyInput(provider, input);
8957
+ if (!text) return { success: false, error: "text required for PTY send" };
8702
8958
  await adapter.sendMessage(text);
8703
8959
  return _logSendSuccess(`${transport}-adapter`, adapter.cliType);
8704
8960
  } catch (e) {
@@ -8706,6 +8962,8 @@ async function handleSendChat(h, args) {
8706
8962
  }
8707
8963
  }
8708
8964
  }
8965
+ assertTextOnlyInput(provider, input);
8966
+ if (!text) return { success: false, error: "text required" };
8709
8967
  if (isExtensionTransport(transport)) {
8710
8968
  _log(`Extension: ${provider?.type || "unknown_extension"}`);
8711
8969
  try {
@@ -9870,14 +10128,14 @@ function normalizeProviderScriptArgs(args, scriptName) {
9870
10128
  }
9871
10129
  function buildControlScriptResult(scriptName, payload) {
9872
10130
  if (!payload || typeof payload !== "object") return {};
9873
- if (Array.isArray(payload.options) || Array.isArray(payload.models) || Array.isArray(payload.modes)) {
10131
+ if (Array.isArray(payload.options)) {
9874
10132
  return { controlResult: normalizeControlListResult(payload) };
9875
10133
  }
9876
10134
  const looksLikeValueMutation = /^set|^change/i.test(scriptName) || payload.currentValue !== void 0 || payload.value !== void 0;
9877
10135
  if (looksLikeValueMutation) {
9878
10136
  return { controlResult: normalizeControlSetResult(payload) };
9879
10137
  }
9880
- if (payload.ok !== void 0 || payload.success !== void 0 || Array.isArray(payload.effects)) {
10138
+ if (payload.ok !== void 0 || Array.isArray(payload.effects) || typeof payload.error === "string") {
9881
10139
  return { controlResult: normalizeControlInvokeResult(payload) };
9882
10140
  }
9883
10141
  return {};
@@ -10681,12 +10939,13 @@ var DaemonCommandHandler = class {
10681
10939
  // src/commands/cli-manager.ts
10682
10940
  init_provider_cli_adapter();
10683
10941
  import * as os12 from "os";
10684
- import * as path13 from "path";
10942
+ import * as path12 from "path";
10685
10943
  import * as crypto4 from "crypto";
10686
10944
  import chalk from "chalk";
10687
10945
  init_config();
10688
10946
 
10689
10947
  // src/providers/cli-provider-instance.ts
10948
+ init_contracts();
10690
10949
  import * as os11 from "os";
10691
10950
  import * as path11 from "path";
10692
10951
  import * as crypto3 from "crypto";
@@ -10990,6 +11249,7 @@ var CliProviderInstance = class {
10990
11249
  onEvent(event, data) {
10991
11250
  if (event === "send_message") {
10992
11251
  const input = normalizeInputEnvelope(data);
11252
+ assertTextOnlyInput(this.provider, input);
10993
11253
  if (input.textFallback) {
10994
11254
  void this.adapter.sendMessage(input.textFallback).catch((e) => {
10995
11255
  LOG.warn("CLI", `[${this.type}] send_message failed: ${e?.message || e}`);
@@ -11427,7 +11687,7 @@ ${effect.notification.body || ""}`.trim();
11427
11687
  };
11428
11688
 
11429
11689
  // src/providers/acp-provider-instance.ts
11430
- import * as path12 from "path";
11690
+ init_contracts();
11431
11691
  import { Readable, Writable } from "stream";
11432
11692
  import { spawn } from "child_process";
11433
11693
  import {
@@ -11446,25 +11706,6 @@ function getPromptCapabilityFlags(agentCapabilities) {
11446
11706
  embeddedContext: prompt.embeddedContext === true
11447
11707
  };
11448
11708
  }
11449
- function getResourceNameFromUri(uri, fallback) {
11450
- try {
11451
- if (uri.startsWith("file://")) {
11452
- return path12.basename(new URL(uri).pathname) || fallback;
11453
- }
11454
- return path12.basename(uri) || fallback;
11455
- } catch {
11456
- return fallback;
11457
- }
11458
- }
11459
- function inputPartToResourceLink(part, fallbackName) {
11460
- if (!part.uri) return null;
11461
- return {
11462
- type: "resource_link",
11463
- uri: part.uri,
11464
- name: getResourceNameFromUri(part.uri, fallbackName),
11465
- ...part.mimeType ? { mimeType: part.mimeType } : {}
11466
- };
11467
- }
11468
11709
  function appendPromptText(promptParts, text) {
11469
11710
  const normalized = typeof text === "string" ? text.trim() : "";
11470
11711
  if (!normalized) return;
@@ -11481,55 +11722,60 @@ function buildAcpPromptParts(input, agentCapabilities) {
11481
11722
  continue;
11482
11723
  }
11483
11724
  if (part.type === "image") {
11484
- if (caps.image && part.data) {
11485
- promptParts.push({
11486
- type: "image",
11487
- data: part.data,
11488
- mimeType: part.mimeType,
11489
- ...part.uri ? { uri: part.uri } : {}
11490
- });
11491
- continue;
11725
+ if (!caps.image) {
11726
+ throw new Error("ACP agent does not support input type: image");
11727
+ }
11728
+ if (!part.data) {
11729
+ throw new Error("ACP image input requires inline image data");
11492
11730
  }
11493
- const fallback = inputPartToResourceLink(part, "image");
11494
- if (fallback) promptParts.push(fallback);
11495
- appendPromptText(promptParts, part.alt || (!part.uri ? `Attached image (${part.mimeType})` : void 0));
11731
+ promptParts.push({
11732
+ type: "image",
11733
+ data: part.data,
11734
+ mimeType: part.mimeType,
11735
+ ...part.uri ? { uri: part.uri } : {}
11736
+ });
11496
11737
  continue;
11497
11738
  }
11498
11739
  if (part.type === "audio") {
11499
- if (caps.audio && part.data) {
11500
- promptParts.push({
11501
- type: "audio",
11502
- data: part.data,
11503
- mimeType: part.mimeType
11504
- });
11505
- continue;
11740
+ if (!caps.audio) {
11741
+ throw new Error("ACP agent does not support input type: audio");
11742
+ }
11743
+ if (!part.data) {
11744
+ throw new Error("ACP audio input requires inline audio data");
11506
11745
  }
11507
- const fallback = inputPartToResourceLink(part, "audio");
11508
- if (fallback) promptParts.push(fallback);
11509
- appendPromptText(promptParts, part.transcript || (!part.uri ? `Attached audio (${part.mimeType})` : void 0));
11746
+ promptParts.push({
11747
+ type: "audio",
11748
+ data: part.data,
11749
+ mimeType: part.mimeType
11750
+ });
11510
11751
  continue;
11511
11752
  }
11512
11753
  if (part.type === "resource") {
11513
- if (caps.embeddedContext && (part.text || part.data)) {
11754
+ if (!caps.embeddedContext) {
11755
+ throw new Error("ACP agent does not support input type: resource");
11756
+ }
11757
+ if (part.text) {
11514
11758
  promptParts.push({
11515
11759
  type: "resource",
11516
- resource: part.text ? { uri: part.uri, text: part.text, mimeType: part.mimeType ?? null } : { uri: part.uri, blob: part.data || "", mimeType: part.mimeType ?? null }
11760
+ resource: { uri: part.uri, text: part.text, mimeType: part.mimeType ?? null }
11517
11761
  });
11518
11762
  continue;
11519
11763
  }
11520
- const fallback = inputPartToResourceLink(part, part.name || "resource");
11521
- if (fallback) promptParts.push(fallback);
11522
- appendPromptText(promptParts, part.text || (!part.uri && part.name ? part.name : void 0));
11523
- continue;
11764
+ if (part.data) {
11765
+ promptParts.push({
11766
+ type: "resource",
11767
+ resource: { uri: part.uri, blob: part.data, mimeType: part.mimeType ?? null }
11768
+ });
11769
+ continue;
11770
+ }
11771
+ throw new Error("ACP resource input requires embedded text or binary data");
11524
11772
  }
11525
11773
  if (part.type === "video") {
11526
- const fallback = inputPartToResourceLink(part, "video");
11527
- if (fallback) promptParts.push(fallback);
11528
- appendPromptText(promptParts, !part.uri ? `Attached video (${part.mimeType})` : void 0);
11774
+ throw new Error("ACP agent does not support input type: video");
11529
11775
  }
11530
11776
  }
11531
11777
  if (!promptParts.some((part) => part.type === "text") && input.textFallback) {
11532
- promptParts.unshift({ type: "text", text: input.textFallback });
11778
+ appendPromptText(promptParts, input.textFallback);
11533
11779
  }
11534
11780
  return promptParts;
11535
11781
  }
@@ -11660,6 +11906,7 @@ var AcpProviderInstance = class {
11660
11906
  onEvent(event, data) {
11661
11907
  if (event === "send_message") {
11662
11908
  const input = normalizeInputEnvelope(data);
11909
+ assertProviderSupportsDeclaredInput(this.provider, input);
11663
11910
  const promptParts = buildAcpPromptParts(input, this.agentCapabilities);
11664
11911
  this.sendPrompt(input.textFallback, promptParts.length > 0 ? promptParts : void 0).catch(
11665
11912
  (e) => this.log.warn(`[${this.type}] sendPrompt error: ${e?.message}`)
@@ -12593,6 +12840,7 @@ ${rawInput}` : rawInput;
12593
12840
  };
12594
12841
 
12595
12842
  // src/commands/cli-manager.ts
12843
+ init_contracts();
12596
12844
  init_logger();
12597
12845
 
12598
12846
  // src/commands/hosted-runtime-restore.ts
@@ -12859,7 +13107,7 @@ var DaemonCliManager = class {
12859
13107
  async startSession(cliType, workingDir, cliArgs, initialModel, options) {
12860
13108
  const trimmed = (workingDir || "").trim();
12861
13109
  if (!trimmed) throw new Error("working directory required");
12862
- const resolvedDir = trimmed.startsWith("~") ? trimmed.replace(/^~/, os12.homedir()) : path13.resolve(trimmed);
13110
+ const resolvedDir = trimmed.startsWith("~") ? trimmed.replace(/^~/, os12.homedir()) : path12.resolve(trimmed);
12863
13111
  const normalizedType = this.providerLoader.resolveAlias(cliType);
12864
13112
  const provider = this.providerLoader.getByAlias(cliType);
12865
13113
  const key = crypto4.randomUUID();
@@ -13318,6 +13566,12 @@ Run 'adhdev doctor' for detailed diagnostics.`
13318
13566
  const { adapter, key } = found;
13319
13567
  if (action === "send_chat") {
13320
13568
  const input = normalizeInputEnvelope(args?.input ? { input: args.input } : args);
13569
+ const provider = this.providerLoader.resolve(agentType) || this.providerLoader.getMeta(agentType);
13570
+ if (provider?.category === "acp") {
13571
+ assertProviderSupportsDeclaredInput(provider, input);
13572
+ } else {
13573
+ assertTextOnlyInput(provider, input);
13574
+ }
13321
13575
  const message = input.textFallback;
13322
13576
  if (!message) throw new Error("message required for send_chat");
13323
13577
  await adapter.sendMessage(message);
@@ -13340,16 +13594,17 @@ Run 'adhdev doctor' for detailed diagnostics.`
13340
13594
  import { execSync as execSync4, spawn as spawn2 } from "child_process";
13341
13595
  import * as net from "net";
13342
13596
  import * as os14 from "os";
13343
- import * as path15 from "path";
13597
+ import * as path14 from "path";
13344
13598
 
13345
13599
  // src/providers/provider-loader.ts
13346
13600
  import * as fs6 from "fs";
13347
- import * as path14 from "path";
13601
+ import * as path13 from "path";
13348
13602
  import * as os13 from "os";
13349
13603
  import * as chokidar from "chokidar";
13350
13604
  init_logger();
13351
13605
 
13352
13606
  // src/providers/provider-schema.ts
13607
+ var VALID_CAPABILITY_MEDIA_TYPES = /* @__PURE__ */ new Set(["text", "image", "audio", "video", "resource"]);
13353
13608
  var KNOWN_PROVIDER_FIELDS = /* @__PURE__ */ new Set([
13354
13609
  "type",
13355
13610
  "name",
@@ -13400,6 +13655,7 @@ var KNOWN_PROVIDER_FIELDS = /* @__PURE__ */ new Set([
13400
13655
  "sendDelayMs",
13401
13656
  "sendKey",
13402
13657
  "submitStrategy",
13658
+ "timeouts",
13403
13659
  "disableUpstream"
13404
13660
  ]);
13405
13661
  var VALUE_CONTROL_TYPES = /* @__PURE__ */ new Set(["select", "toggle", "cycle", "slider"]);
@@ -13426,6 +13682,7 @@ function validateProviderDefinition(raw) {
13426
13682
  warnings.push("disableUpstream is deprecated in provider definitions; use machine-level provider source policy instead");
13427
13683
  }
13428
13684
  const category = provider.category;
13685
+ const controls = Array.isArray(provider.controls) ? provider.controls : [];
13429
13686
  if (category === "cli" || category === "acp") {
13430
13687
  const spawn4 = provider.spawn;
13431
13688
  const command = spawn4 && typeof spawn4 === "object" ? spawn4.command : void 0;
@@ -13443,11 +13700,61 @@ function validateProviderDefinition(raw) {
13443
13700
  if (category === "extension" && !provider.extensionId) {
13444
13701
  warnings.push("Extension providers should have extensionId");
13445
13702
  }
13446
- for (const control of Array.isArray(provider.controls) ? provider.controls : []) {
13703
+ validateCapabilities(provider, controls, errors);
13704
+ for (const control of controls) {
13447
13705
  validateControl(control, errors);
13448
13706
  }
13449
13707
  return { errors, warnings };
13450
13708
  }
13709
+ function validateCapabilities(provider, controls, errors) {
13710
+ const capabilities = provider.capabilities;
13711
+ if (provider.contractVersion === 2) {
13712
+ if (!capabilities || typeof capabilities !== "object") {
13713
+ errors.push("contractVersion 2 providers must declare capabilities");
13714
+ return;
13715
+ }
13716
+ }
13717
+ if (!capabilities || typeof capabilities !== "object") {
13718
+ return;
13719
+ }
13720
+ const input = capabilities.input;
13721
+ if (!input || typeof input !== "object") {
13722
+ errors.push("capabilities.input is required");
13723
+ } else {
13724
+ if (typeof input.multipart !== "boolean") {
13725
+ errors.push("capabilities.input.multipart must be boolean");
13726
+ }
13727
+ if (!Array.isArray(input.mediaTypes) || input.mediaTypes.length === 0) {
13728
+ errors.push("capabilities.input.mediaTypes must be a non-empty array");
13729
+ } else if (input.mediaTypes.some((type) => typeof type !== "string" || !VALID_CAPABILITY_MEDIA_TYPES.has(type))) {
13730
+ errors.push(`capabilities.input.mediaTypes must only include: ${Array.from(VALID_CAPABILITY_MEDIA_TYPES).join(", ")}`);
13731
+ }
13732
+ }
13733
+ const output = capabilities.output;
13734
+ if (!output || typeof output !== "object") {
13735
+ errors.push("capabilities.output is required");
13736
+ } else {
13737
+ if (typeof output.richContent !== "boolean") {
13738
+ errors.push("capabilities.output.richContent must be boolean");
13739
+ }
13740
+ if (!Array.isArray(output.mediaTypes) || output.mediaTypes.length === 0) {
13741
+ errors.push("capabilities.output.mediaTypes must be a non-empty array");
13742
+ } else if (output.mediaTypes.some((type) => typeof type !== "string" || !VALID_CAPABILITY_MEDIA_TYPES.has(type))) {
13743
+ errors.push(`capabilities.output.mediaTypes must only include: ${Array.from(VALID_CAPABILITY_MEDIA_TYPES).join(", ")}`);
13744
+ }
13745
+ }
13746
+ const controlCapabilities = capabilities.controls;
13747
+ if (!controlCapabilities || typeof controlCapabilities !== "object") {
13748
+ errors.push("capabilities.controls is required");
13749
+ return;
13750
+ }
13751
+ if (typeof controlCapabilities.typedResults !== "boolean") {
13752
+ errors.push("capabilities.controls.typedResults must be boolean");
13753
+ }
13754
+ if (controls.length > 0 && controlCapabilities.typedResults !== true) {
13755
+ errors.push("providers declaring controls must set capabilities.controls.typedResults=true");
13756
+ }
13757
+ }
13451
13758
  function validateControl(control, errors) {
13452
13759
  if (!control || typeof control !== "object") {
13453
13760
  errors.push("controls: each control must be an object");
@@ -13502,9 +13809,9 @@ var ProviderLoader = class _ProviderLoader {
13502
13809
  static META_FILE = ".meta.json";
13503
13810
  constructor(options) {
13504
13811
  this.logFn = options?.logFn || LOG.forComponent("Provider").asLogFn();
13505
- this.defaultProvidersDir = path14.join(os13.homedir(), ".adhdev", "providers");
13812
+ this.defaultProvidersDir = path13.join(os13.homedir(), ".adhdev", "providers");
13506
13813
  this.userDir = this.defaultProvidersDir;
13507
- this.upstreamDir = path14.join(this.defaultProvidersDir, ".upstream");
13814
+ this.upstreamDir = path13.join(this.defaultProvidersDir, ".upstream");
13508
13815
  this.disableUpstream = false;
13509
13816
  this.applySourceConfig({
13510
13817
  userDir: options?.userDir,
@@ -13552,7 +13859,7 @@ var ProviderLoader = class _ProviderLoader {
13552
13859
  }
13553
13860
  this.sourceMode = nextSourceMode;
13554
13861
  this.userDir = this.explicitProviderDir || this.defaultProvidersDir;
13555
- this.upstreamDir = path14.join(this.defaultProvidersDir, ".upstream");
13862
+ this.upstreamDir = path13.join(this.defaultProvidersDir, ".upstream");
13556
13863
  this.disableUpstream = this.sourceMode === "no-upstream";
13557
13864
  if (this.explicitProviderDir) {
13558
13865
  this.log(`Config 'providerDir' applied: ${this.userDir}`);
@@ -13566,7 +13873,7 @@ var ProviderLoader = class _ProviderLoader {
13566
13873
  * Canonical provider directory shape for a given root.
13567
13874
  */
13568
13875
  getProviderDir(root, category, type) {
13569
- return path14.join(root, category, type);
13876
+ return path13.join(root, category, type);
13570
13877
  }
13571
13878
  /**
13572
13879
  * Canonical user override directory for a provider.
@@ -13593,7 +13900,7 @@ var ProviderLoader = class _ProviderLoader {
13593
13900
  resolveProviderFile(type, ...segments) {
13594
13901
  const dir = this.findProviderDirInternal(type);
13595
13902
  if (!dir) return null;
13596
- return path14.join(dir, ...segments);
13903
+ return path13.join(dir, ...segments);
13597
13904
  }
13598
13905
  /**
13599
13906
  * Load all providers (3-tier priority)
@@ -13632,7 +13939,7 @@ var ProviderLoader = class _ProviderLoader {
13632
13939
  if (!fs6.existsSync(this.upstreamDir)) return false;
13633
13940
  try {
13634
13941
  return fs6.readdirSync(this.upstreamDir).some(
13635
- (d) => fs6.statSync(path14.join(this.upstreamDir, d)).isDirectory()
13942
+ (d) => fs6.statSync(path13.join(this.upstreamDir, d)).isDirectory()
13636
13943
  );
13637
13944
  } catch {
13638
13945
  return false;
@@ -13947,8 +14254,8 @@ var ProviderLoader = class _ProviderLoader {
13947
14254
  resolved._resolvedScriptDir = entry.scriptDir;
13948
14255
  resolved._resolvedScriptsSource = `compatibility:${entry.ideVersion}`;
13949
14256
  if (providerDir) {
13950
- const fullDir = path14.join(providerDir, entry.scriptDir);
13951
- resolved._resolvedScriptsPath = fs6.existsSync(path14.join(fullDir, "scripts.js")) ? path14.join(fullDir, "scripts.js") : fullDir;
14257
+ const fullDir = path13.join(providerDir, entry.scriptDir);
14258
+ resolved._resolvedScriptsPath = fs6.existsSync(path13.join(fullDir, "scripts.js")) ? path13.join(fullDir, "scripts.js") : fullDir;
13952
14259
  }
13953
14260
  matched = true;
13954
14261
  }
@@ -13963,8 +14270,8 @@ var ProviderLoader = class _ProviderLoader {
13963
14270
  resolved._resolvedScriptDir = base.defaultScriptDir;
13964
14271
  resolved._resolvedScriptsSource = "defaultScriptDir:version_miss";
13965
14272
  if (providerDir) {
13966
- const fullDir = path14.join(providerDir, base.defaultScriptDir);
13967
- resolved._resolvedScriptsPath = fs6.existsSync(path14.join(fullDir, "scripts.js")) ? path14.join(fullDir, "scripts.js") : fullDir;
14273
+ const fullDir = path13.join(providerDir, base.defaultScriptDir);
14274
+ resolved._resolvedScriptsPath = fs6.existsSync(path13.join(fullDir, "scripts.js")) ? path13.join(fullDir, "scripts.js") : fullDir;
13968
14275
  }
13969
14276
  }
13970
14277
  resolved._versionWarning = `Version ${currentVersion} not in compatibility matrix. Using default scripts.`;
@@ -13981,8 +14288,8 @@ var ProviderLoader = class _ProviderLoader {
13981
14288
  resolved._resolvedScriptDir = dirOverride;
13982
14289
  resolved._resolvedScriptsSource = `versions:${range}`;
13983
14290
  if (providerDir) {
13984
- const fullDir = path14.join(providerDir, dirOverride);
13985
- resolved._resolvedScriptsPath = fs6.existsSync(path14.join(fullDir, "scripts.js")) ? path14.join(fullDir, "scripts.js") : fullDir;
14291
+ const fullDir = path13.join(providerDir, dirOverride);
14292
+ resolved._resolvedScriptsPath = fs6.existsSync(path13.join(fullDir, "scripts.js")) ? path13.join(fullDir, "scripts.js") : fullDir;
13986
14293
  }
13987
14294
  }
13988
14295
  } else if (override.scripts) {
@@ -13998,8 +14305,8 @@ var ProviderLoader = class _ProviderLoader {
13998
14305
  resolved._resolvedScriptDir = base.defaultScriptDir;
13999
14306
  resolved._resolvedScriptsSource = "defaultScriptDir:no_version";
14000
14307
  if (providerDir) {
14001
- const fullDir = path14.join(providerDir, base.defaultScriptDir);
14002
- resolved._resolvedScriptsPath = fs6.existsSync(path14.join(fullDir, "scripts.js")) ? path14.join(fullDir, "scripts.js") : fullDir;
14308
+ const fullDir = path13.join(providerDir, base.defaultScriptDir);
14309
+ resolved._resolvedScriptsPath = fs6.existsSync(path13.join(fullDir, "scripts.js")) ? path13.join(fullDir, "scripts.js") : fullDir;
14003
14310
  }
14004
14311
  }
14005
14312
  }
@@ -14024,14 +14331,14 @@ var ProviderLoader = class _ProviderLoader {
14024
14331
  this.log(` [loadScriptsFromDir] ${type}: providerDir not found`);
14025
14332
  return null;
14026
14333
  }
14027
- const dir = path14.join(providerDir, scriptDir);
14334
+ const dir = path13.join(providerDir, scriptDir);
14028
14335
  if (!fs6.existsSync(dir)) {
14029
14336
  this.log(` [loadScriptsFromDir] ${type}: dir not found: ${dir}`);
14030
14337
  return null;
14031
14338
  }
14032
14339
  const cached = this.scriptsCache.get(dir);
14033
14340
  if (cached) return cached;
14034
- const scriptsJs = path14.join(dir, "scripts.js");
14341
+ const scriptsJs = path13.join(dir, "scripts.js");
14035
14342
  if (fs6.existsSync(scriptsJs)) {
14036
14343
  try {
14037
14344
  delete __require.cache[__require.resolve(scriptsJs)];
@@ -14073,7 +14380,7 @@ var ProviderLoader = class _ProviderLoader {
14073
14380
  return;
14074
14381
  }
14075
14382
  if (filePath.endsWith(".js") || filePath.endsWith(".json")) {
14076
- this.log(`File changed: ${path14.basename(filePath)}, reloading...`);
14383
+ this.log(`File changed: ${path13.basename(filePath)}, reloading...`);
14077
14384
  this.reload();
14078
14385
  }
14079
14386
  };
@@ -14128,7 +14435,7 @@ var ProviderLoader = class _ProviderLoader {
14128
14435
  }
14129
14436
  const https = __require("https");
14130
14437
  const { execSync: execSync7 } = __require("child_process");
14131
- const metaPath = path14.join(this.upstreamDir, _ProviderLoader.META_FILE);
14438
+ const metaPath = path13.join(this.upstreamDir, _ProviderLoader.META_FILE);
14132
14439
  let prevEtag = "";
14133
14440
  let prevTimestamp = 0;
14134
14441
  try {
@@ -14188,17 +14495,17 @@ var ProviderLoader = class _ProviderLoader {
14188
14495
  return { updated: false };
14189
14496
  }
14190
14497
  this.log("Downloading latest providers from GitHub...");
14191
- const tmpTar = path14.join(os13.tmpdir(), `adhdev-providers-${Date.now()}.tar.gz`);
14192
- const tmpExtract = path14.join(os13.tmpdir(), `adhdev-providers-extract-${Date.now()}`);
14498
+ const tmpTar = path13.join(os13.tmpdir(), `adhdev-providers-${Date.now()}.tar.gz`);
14499
+ const tmpExtract = path13.join(os13.tmpdir(), `adhdev-providers-extract-${Date.now()}`);
14193
14500
  await this.downloadFile(_ProviderLoader.GITHUB_TARBALL_URL, tmpTar);
14194
14501
  fs6.mkdirSync(tmpExtract, { recursive: true });
14195
14502
  execSync7(`tar -xzf "${tmpTar}" -C "${tmpExtract}"`, { timeout: 3e4 });
14196
14503
  const extracted = fs6.readdirSync(tmpExtract);
14197
14504
  const rootDir = extracted.find(
14198
- (d) => fs6.statSync(path14.join(tmpExtract, d)).isDirectory() && d.startsWith("adhdev-providers")
14505
+ (d) => fs6.statSync(path13.join(tmpExtract, d)).isDirectory() && d.startsWith("adhdev-providers")
14199
14506
  );
14200
14507
  if (!rootDir) throw new Error("Unexpected tarball structure");
14201
- const sourceDir = path14.join(tmpExtract, rootDir);
14508
+ const sourceDir = path13.join(tmpExtract, rootDir);
14202
14509
  const backupDir = this.upstreamDir + ".bak";
14203
14510
  if (fs6.existsSync(this.upstreamDir)) {
14204
14511
  if (fs6.existsSync(backupDir)) fs6.rmSync(backupDir, { recursive: true, force: true });
@@ -14273,8 +14580,8 @@ var ProviderLoader = class _ProviderLoader {
14273
14580
  copyDirRecursive(src, dest) {
14274
14581
  fs6.mkdirSync(dest, { recursive: true });
14275
14582
  for (const entry of fs6.readdirSync(src, { withFileTypes: true })) {
14276
- const srcPath = path14.join(src, entry.name);
14277
- const destPath = path14.join(dest, entry.name);
14583
+ const srcPath = path13.join(src, entry.name);
14584
+ const destPath = path13.join(dest, entry.name);
14278
14585
  if (entry.isDirectory()) {
14279
14586
  this.copyDirRecursive(srcPath, destPath);
14280
14587
  } else {
@@ -14285,7 +14592,7 @@ var ProviderLoader = class _ProviderLoader {
14285
14592
  /** .meta.json save */
14286
14593
  writeMeta(metaPath, etag, timestamp) {
14287
14594
  try {
14288
- fs6.mkdirSync(path14.dirname(metaPath), { recursive: true });
14595
+ fs6.mkdirSync(path13.dirname(metaPath), { recursive: true });
14289
14596
  fs6.writeFileSync(metaPath, JSON.stringify({
14290
14597
  etag,
14291
14598
  timestamp,
@@ -14302,7 +14609,7 @@ var ProviderLoader = class _ProviderLoader {
14302
14609
  const scan = (d) => {
14303
14610
  try {
14304
14611
  for (const entry of fs6.readdirSync(d, { withFileTypes: true })) {
14305
- if (entry.isDirectory()) scan(path14.join(d, entry.name));
14612
+ if (entry.isDirectory()) scan(path13.join(d, entry.name));
14306
14613
  else if (entry.name === "provider.json") count++;
14307
14614
  }
14308
14615
  } catch {
@@ -14487,17 +14794,17 @@ var ProviderLoader = class _ProviderLoader {
14487
14794
  for (const root of searchRoots) {
14488
14795
  if (!fs6.existsSync(root)) continue;
14489
14796
  const candidate = this.getProviderDir(root, cat, type);
14490
- if (fs6.existsSync(path14.join(candidate, "provider.json"))) return candidate;
14491
- const catDir = path14.join(root, cat);
14797
+ if (fs6.existsSync(path13.join(candidate, "provider.json"))) return candidate;
14798
+ const catDir = path13.join(root, cat);
14492
14799
  if (fs6.existsSync(catDir)) {
14493
14800
  try {
14494
14801
  for (const entry of fs6.readdirSync(catDir, { withFileTypes: true })) {
14495
14802
  if (!entry.isDirectory()) continue;
14496
- const jsonPath = path14.join(catDir, entry.name, "provider.json");
14803
+ const jsonPath = path13.join(catDir, entry.name, "provider.json");
14497
14804
  if (fs6.existsSync(jsonPath)) {
14498
14805
  try {
14499
14806
  const data = JSON.parse(fs6.readFileSync(jsonPath, "utf-8"));
14500
- if (data.type === type) return path14.join(catDir, entry.name);
14807
+ if (data.type === type) return path13.join(catDir, entry.name);
14501
14808
  } catch {
14502
14809
  }
14503
14810
  }
@@ -14514,7 +14821,7 @@ var ProviderLoader = class _ProviderLoader {
14514
14821
  * (template substitution is NOT applied here — scripts.js handles that)
14515
14822
  */
14516
14823
  buildScriptWrappersFromDir(dir) {
14517
- const scriptsJs = path14.join(dir, "scripts.js");
14824
+ const scriptsJs = path13.join(dir, "scripts.js");
14518
14825
  if (fs6.existsSync(scriptsJs)) {
14519
14826
  try {
14520
14827
  delete __require.cache[__require.resolve(scriptsJs)];
@@ -14528,7 +14835,7 @@ var ProviderLoader = class _ProviderLoader {
14528
14835
  for (const file of fs6.readdirSync(dir)) {
14529
14836
  if (!file.endsWith(".js")) continue;
14530
14837
  const scriptName = toCamel(file.replace(".js", ""));
14531
- const filePath = path14.join(dir, file);
14838
+ const filePath = path13.join(dir, file);
14532
14839
  result[scriptName] = (...args) => {
14533
14840
  try {
14534
14841
  let content = fs6.readFileSync(filePath, "utf-8");
@@ -14588,7 +14895,7 @@ var ProviderLoader = class _ProviderLoader {
14588
14895
  }
14589
14896
  const hasJson = entries.some((e) => e.name === "provider.json");
14590
14897
  if (hasJson) {
14591
- const jsonPath = path14.join(d, "provider.json");
14898
+ const jsonPath = path13.join(d, "provider.json");
14592
14899
  try {
14593
14900
  const raw = fs6.readFileSync(jsonPath, "utf-8");
14594
14901
  const mod = JSON.parse(raw);
@@ -14609,7 +14916,7 @@ var ProviderLoader = class _ProviderLoader {
14609
14916
  this.log(`\u26A0 Invalid provider at ${jsonPath}: ${validation.errors.join("; ")}`);
14610
14917
  } else {
14611
14918
  const hasCompatibility = Array.isArray(normalizedProvider.compatibility);
14612
- const scriptsPath = path14.join(d, "scripts.js");
14919
+ const scriptsPath = path13.join(d, "scripts.js");
14613
14920
  if (!hasCompatibility && fs6.existsSync(scriptsPath)) {
14614
14921
  try {
14615
14922
  delete __require.cache[__require.resolve(scriptsPath)];
@@ -14635,7 +14942,7 @@ var ProviderLoader = class _ProviderLoader {
14635
14942
  if (!entry.isDirectory()) continue;
14636
14943
  if (entry.name.startsWith("_") || entry.name.startsWith(".")) continue;
14637
14944
  if (excludeDirs && d === dir && excludeDirs.includes(entry.name)) continue;
14638
- scan(path14.join(d, entry.name));
14945
+ scan(path13.join(d, entry.name));
14639
14946
  }
14640
14947
  }
14641
14948
  };
@@ -14893,8 +15200,8 @@ function detectCurrentWorkspace(ideId) {
14893
15200
  const appNameMap = getMacAppIdentifiers();
14894
15201
  const appName = appNameMap[ideId];
14895
15202
  if (appName) {
14896
- const storagePath = path15.join(
14897
- process.env.APPDATA || path15.join(os14.homedir(), "AppData", "Roaming"),
15203
+ const storagePath = path14.join(
15204
+ process.env.APPDATA || path14.join(os14.homedir(), "AppData", "Roaming"),
14898
15205
  appName,
14899
15206
  "storage.json"
14900
15207
  );
@@ -15072,9 +15379,9 @@ init_logger();
15072
15379
 
15073
15380
  // src/logging/command-log.ts
15074
15381
  import * as fs7 from "fs";
15075
- import * as path16 from "path";
15382
+ import * as path15 from "path";
15076
15383
  import * as os15 from "os";
15077
- 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");
15384
+ 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");
15078
15385
  var MAX_FILE_SIZE = 5 * 1024 * 1024;
15079
15386
  var MAX_DAYS = 7;
15080
15387
  try {
@@ -15112,13 +15419,13 @@ function getDateStr2() {
15112
15419
  return (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
15113
15420
  }
15114
15421
  var currentDate2 = getDateStr2();
15115
- var currentFile = path16.join(LOG_DIR2, `commands-${currentDate2}.jsonl`);
15422
+ var currentFile = path15.join(LOG_DIR2, `commands-${currentDate2}.jsonl`);
15116
15423
  var writeCount2 = 0;
15117
15424
  function checkRotation() {
15118
15425
  const today = getDateStr2();
15119
15426
  if (today !== currentDate2) {
15120
15427
  currentDate2 = today;
15121
- currentFile = path16.join(LOG_DIR2, `commands-${currentDate2}.jsonl`);
15428
+ currentFile = path15.join(LOG_DIR2, `commands-${currentDate2}.jsonl`);
15122
15429
  cleanOldFiles();
15123
15430
  }
15124
15431
  }
@@ -15132,7 +15439,7 @@ function cleanOldFiles() {
15132
15439
  const dateMatch = file.match(/commands-(\d{4}-\d{2}-\d{2})/);
15133
15440
  if (dateMatch && dateMatch[1] < cutoffStr) {
15134
15441
  try {
15135
- fs7.unlinkSync(path16.join(LOG_DIR2, file));
15442
+ fs7.unlinkSync(path15.join(LOG_DIR2, file));
15136
15443
  } catch {
15137
15444
  }
15138
15445
  }
@@ -15545,13 +15852,13 @@ import { execFileSync } from "child_process";
15545
15852
  import { spawn as spawn3 } from "child_process";
15546
15853
  import * as fs8 from "fs";
15547
15854
  import * as os17 from "os";
15548
- import * as path17 from "path";
15855
+ import * as path16 from "path";
15549
15856
  var UPGRADE_HELPER_ENV = "ADHDEV_DAEMON_UPGRADE_HELPER";
15550
15857
  function getUpgradeLogPath() {
15551
15858
  const home = os17.homedir();
15552
- const dir = path17.join(home, ".adhdev");
15859
+ const dir = path16.join(home, ".adhdev");
15553
15860
  fs8.mkdirSync(dir, { recursive: true });
15554
- return path17.join(dir, "daemon-upgrade.log");
15861
+ return path16.join(dir, "daemon-upgrade.log");
15555
15862
  }
15556
15863
  function appendUpgradeLog(message) {
15557
15864
  const line = `[${(/* @__PURE__ */ new Date()).toISOString()}] ${message}
@@ -15591,7 +15898,7 @@ async function waitForPidExit(pid, timeoutMs) {
15591
15898
  }
15592
15899
  }
15593
15900
  function stopSessionHostProcesses(appName) {
15594
- const pidFile = path17.join(os17.homedir(), ".adhdev", `${appName}-session-host.pid`);
15901
+ const pidFile = path16.join(os17.homedir(), ".adhdev", `${appName}-session-host.pid`);
15595
15902
  try {
15596
15903
  if (fs8.existsSync(pidFile)) {
15597
15904
  const pid = Number.parseInt(fs8.readFileSync(pidFile, "utf8").trim(), 10);
@@ -15620,7 +15927,7 @@ function stopSessionHostProcesses(appName) {
15620
15927
  }
15621
15928
  }
15622
15929
  function removeDaemonPidFile() {
15623
- const pidFile = path17.join(os17.homedir(), ".adhdev", "daemon.pid");
15930
+ const pidFile = path16.join(os17.homedir(), ".adhdev", "daemon.pid");
15624
15931
  try {
15625
15932
  fs8.unlinkSync(pidFile);
15626
15933
  } catch {
@@ -15631,7 +15938,7 @@ function cleanupStaleGlobalInstallDirs(pkgName) {
15631
15938
  const npmRoot = execFileSync(getNpmExecutable(), ["root", "-g"], { encoding: "utf8", ...npmExecOpts }).trim();
15632
15939
  if (!npmRoot) return;
15633
15940
  const npmPrefix = execFileSync(getNpmExecutable(), ["prefix", "-g"], { encoding: "utf8", ...npmExecOpts }).trim();
15634
- const binDir = process.platform === "win32" ? npmPrefix : path17.join(npmPrefix, "bin");
15941
+ const binDir = process.platform === "win32" ? npmPrefix : path16.join(npmPrefix, "bin");
15635
15942
  const packageBaseName = pkgName.startsWith("@") ? pkgName.split("/")[1] : pkgName;
15636
15943
  const binNames = /* @__PURE__ */ new Set([packageBaseName]);
15637
15944
  if (pkgName === "@adhdev/daemon-standalone") {
@@ -15639,25 +15946,25 @@ function cleanupStaleGlobalInstallDirs(pkgName) {
15639
15946
  }
15640
15947
  if (pkgName.startsWith("@")) {
15641
15948
  const [scope, name] = pkgName.split("/");
15642
- const scopeDir = path17.join(npmRoot, scope);
15949
+ const scopeDir = path16.join(npmRoot, scope);
15643
15950
  if (!fs8.existsSync(scopeDir)) return;
15644
15951
  for (const entry of fs8.readdirSync(scopeDir)) {
15645
15952
  if (!entry.startsWith(`.${name}-`)) continue;
15646
- fs8.rmSync(path17.join(scopeDir, entry), { recursive: true, force: true });
15647
- appendUpgradeLog(`Removed stale scoped staging dir: ${path17.join(scopeDir, entry)}`);
15953
+ fs8.rmSync(path16.join(scopeDir, entry), { recursive: true, force: true });
15954
+ appendUpgradeLog(`Removed stale scoped staging dir: ${path16.join(scopeDir, entry)}`);
15648
15955
  }
15649
15956
  } else {
15650
15957
  for (const entry of fs8.readdirSync(npmRoot)) {
15651
15958
  if (!entry.startsWith(`.${pkgName}-`)) continue;
15652
- fs8.rmSync(path17.join(npmRoot, entry), { recursive: true, force: true });
15653
- appendUpgradeLog(`Removed stale staging dir: ${path17.join(npmRoot, entry)}`);
15959
+ fs8.rmSync(path16.join(npmRoot, entry), { recursive: true, force: true });
15960
+ appendUpgradeLog(`Removed stale staging dir: ${path16.join(npmRoot, entry)}`);
15654
15961
  }
15655
15962
  }
15656
15963
  if (fs8.existsSync(binDir)) {
15657
15964
  for (const entry of fs8.readdirSync(binDir)) {
15658
15965
  if (![...binNames].some((name) => entry.startsWith(`.${name}-`))) continue;
15659
- fs8.rmSync(path17.join(binDir, entry), { recursive: true, force: true });
15660
- appendUpgradeLog(`Removed stale bin staging entry: ${path17.join(binDir, entry)}`);
15966
+ fs8.rmSync(path16.join(binDir, entry), { recursive: true, force: true });
15967
+ appendUpgradeLog(`Removed stale bin staging entry: ${path16.join(binDir, entry)}`);
15661
15968
  }
15662
15969
  }
15663
15970
  }
@@ -16687,6 +16994,7 @@ var DEFAULT_DAEMON_PORT = 19222;
16687
16994
  var DAEMON_WS_PATH = "/ipc";
16688
16995
 
16689
16996
  // src/agent-stream/provider-adapter.ts
16997
+ init_read_chat_contract();
16690
16998
  init_chat_message_normalization();
16691
16999
  var ProviderStreamAdapter = class {
16692
17000
  agentType;
@@ -16792,26 +17100,29 @@ var ProviderStreamAdapter = class {
16792
17100
  }
16793
17101
  return state2;
16794
17102
  }
17103
+ const validated = validateReadChatResultPayload(data, `${this.agentType} readChat`);
17104
+ const validatedStatus = validated.status;
17105
+ const streamStatus = validatedStatus === "generating" || validatedStatus === "long_generating" ? "streaming" : validatedStatus;
16795
17106
  const state = {
16796
17107
  agentType: this.agentType,
16797
17108
  agentName: this.agentName,
16798
17109
  extensionId: this.extensionId,
16799
- status: data.status || "idle",
16800
- messages: normalizeChatMessages(Array.isArray(data.messages) ? data.messages : []),
16801
- inputContent: data.inputContent || "",
16802
- activeModal: data.activeModal
17110
+ status: streamStatus,
17111
+ messages: normalizeChatMessages(validated.messages),
17112
+ inputContent: typeof validated.inputContent === "string" ? validated.inputContent : "",
17113
+ ...validated.activeModal ? { activeModal: validated.activeModal } : {}
16803
17114
  };
16804
- if (typeof data.title === "string" && data.title.trim()) {
16805
- state.title = data.title.trim();
17115
+ if (typeof validated.title === "string" && validated.title.trim()) {
17116
+ state.title = validated.title.trim();
16806
17117
  }
16807
- const controlValues = extractProviderControlValues(this.provider.controls, data);
17118
+ const controlValues = extractProviderControlValues(this.provider.controls, validated);
16808
17119
  const surface = resolveProviderStateSurface({
16809
17120
  controlValues,
16810
- summaryMetadata: data.summaryMetadata
17121
+ summaryMetadata: validated.summaryMetadata
16811
17122
  });
16812
17123
  if (surface.controlValues) state.controlValues = surface.controlValues;
16813
17124
  if (surface.summaryMetadata) state.summaryMetadata = surface.summaryMetadata;
16814
- const effects = normalizeProviderEffects(data);
17125
+ const effects = normalizeProviderEffects(validated);
16815
17126
  if (effects.length > 0) state.effects = effects;
16816
17127
  if (state.messages.length > 0) {
16817
17128
  this.lastSuccessState = state;
@@ -17650,15 +17961,16 @@ var ProviderInstanceManager = class {
17650
17961
  };
17651
17962
 
17652
17963
  // src/index.ts
17964
+ init_io_contracts();
17653
17965
  init_chat_message_normalization();
17654
17966
 
17655
17967
  // src/providers/version-archive.ts
17656
17968
  import * as fs10 from "fs";
17657
- import * as path18 from "path";
17969
+ import * as path17 from "path";
17658
17970
  import * as os18 from "os";
17659
17971
  import { execSync as execSync5 } from "child_process";
17660
17972
  import { platform as platform8 } from "os";
17661
- var ARCHIVE_PATH = path18.join(os18.homedir(), ".adhdev", "version-history.json");
17973
+ var ARCHIVE_PATH = path17.join(os18.homedir(), ".adhdev", "version-history.json");
17662
17974
  var MAX_ENTRIES_PER_PROVIDER = 20;
17663
17975
  var VersionArchive = class {
17664
17976
  history = {};
@@ -17705,7 +18017,7 @@ var VersionArchive = class {
17705
18017
  }
17706
18018
  save() {
17707
18019
  try {
17708
- fs10.mkdirSync(path18.dirname(ARCHIVE_PATH), { recursive: true });
18020
+ fs10.mkdirSync(path17.dirname(ARCHIVE_PATH), { recursive: true });
17709
18021
  fs10.writeFileSync(ARCHIVE_PATH, JSON.stringify(this.history, null, 2));
17710
18022
  } catch {
17711
18023
  }
@@ -17762,7 +18074,7 @@ function checkPathExists2(paths) {
17762
18074
  for (const p of paths) {
17763
18075
  if (p.includes("*")) {
17764
18076
  const home = os18.homedir();
17765
- const resolved = p.replace(/\*/g, home.split(path18.sep).pop() || "");
18077
+ const resolved = p.replace(/\*/g, home.split(path17.sep).pop() || "");
17766
18078
  if (fs10.existsSync(resolved)) return resolved;
17767
18079
  } else {
17768
18080
  if (fs10.existsSync(p)) return p;
@@ -17772,7 +18084,7 @@ function checkPathExists2(paths) {
17772
18084
  }
17773
18085
  function getMacAppVersion(appPath) {
17774
18086
  if (platform8() !== "darwin" || !appPath.endsWith(".app")) return null;
17775
- const plistPath = path18.join(appPath, "Contents", "Info.plist");
18087
+ const plistPath = path17.join(appPath, "Contents", "Info.plist");
17776
18088
  if (!fs10.existsSync(plistPath)) return null;
17777
18089
  const raw = runCommand(`/usr/libexec/PlistBuddy -c "Print CFBundleShortVersionString" "${plistPath}"`);
17778
18090
  return raw || null;
@@ -17798,7 +18110,7 @@ async function detectAllVersions(loader, archive) {
17798
18110
  const cliBin = provider.cli ? findBinary2(provider.cli) : null;
17799
18111
  let resolvedBin = cliBin;
17800
18112
  if (!resolvedBin && appPath && currentOs === "darwin") {
17801
- const bundled = path18.join(appPath, "Contents", "Resources", "app", "bin", provider.cli || "");
18113
+ const bundled = path17.join(appPath, "Contents", "Resources", "app", "bin", provider.cli || "");
17802
18114
  if (provider.cli && fs10.existsSync(bundled)) resolvedBin = bundled;
17803
18115
  }
17804
18116
  info.installed = !!(appPath || resolvedBin);
@@ -17839,7 +18151,7 @@ async function detectAllVersions(loader, archive) {
17839
18151
  // src/daemon/dev-server.ts
17840
18152
  import * as http2 from "http";
17841
18153
  import * as fs14 from "fs";
17842
- import * as path22 from "path";
18154
+ import * as path21 from "path";
17843
18155
  init_config();
17844
18156
 
17845
18157
  // src/daemon/scaffold-template.ts
@@ -18184,7 +18496,7 @@ init_logger();
18184
18496
  // src/daemon/dev-cdp-handlers.ts
18185
18497
  init_logger();
18186
18498
  import * as fs11 from "fs";
18187
- import * as path19 from "path";
18499
+ import * as path18 from "path";
18188
18500
  async function handleCdpEvaluate(ctx, req, res) {
18189
18501
  const body = await ctx.readBody(req);
18190
18502
  const { expression, timeout, ideType } = body;
@@ -18362,17 +18674,17 @@ async function handleScriptHints(ctx, type, _req, res) {
18362
18674
  return;
18363
18675
  }
18364
18676
  let scriptsPath = "";
18365
- const directScripts = path19.join(dir, "scripts.js");
18677
+ const directScripts = path18.join(dir, "scripts.js");
18366
18678
  if (fs11.existsSync(directScripts)) {
18367
18679
  scriptsPath = directScripts;
18368
18680
  } else {
18369
- const scriptsDir = path19.join(dir, "scripts");
18681
+ const scriptsDir = path18.join(dir, "scripts");
18370
18682
  if (fs11.existsSync(scriptsDir)) {
18371
18683
  const versions = fs11.readdirSync(scriptsDir).filter((d) => {
18372
- return fs11.statSync(path19.join(scriptsDir, d)).isDirectory();
18684
+ return fs11.statSync(path18.join(scriptsDir, d)).isDirectory();
18373
18685
  }).sort().reverse();
18374
18686
  for (const ver of versions) {
18375
- const p = path19.join(scriptsDir, ver, "scripts.js");
18687
+ const p = path18.join(scriptsDir, ver, "scripts.js");
18376
18688
  if (fs11.existsSync(p)) {
18377
18689
  scriptsPath = p;
18378
18690
  break;
@@ -19201,7 +19513,7 @@ async function handleDomContext(ctx, type, req, res) {
19201
19513
 
19202
19514
  // src/daemon/dev-cli-debug.ts
19203
19515
  import * as fs12 from "fs";
19204
- import * as path20 from "path";
19516
+ import * as path19 from "path";
19205
19517
  function slugifyFixtureName(value) {
19206
19518
  const normalized = String(value || "").trim().toLowerCase().replace(/[^a-z0-9._-]+/g, "-").replace(/^-+|-+$/g, "");
19207
19519
  return normalized || `fixture-${Date.now()}`;
@@ -19211,11 +19523,11 @@ function getCliFixtureDir(ctx, type) {
19211
19523
  if (!providerDir) {
19212
19524
  throw new Error(`Provider directory not found for '${type}'`);
19213
19525
  }
19214
- return path20.join(providerDir, "fixtures");
19526
+ return path19.join(providerDir, "fixtures");
19215
19527
  }
19216
19528
  function readCliFixture(ctx, type, name) {
19217
19529
  const fixtureDir = getCliFixtureDir(ctx, type);
19218
- const filePath = path20.join(fixtureDir, `${name}.json`);
19530
+ const filePath = path19.join(fixtureDir, `${name}.json`);
19219
19531
  if (!fs12.existsSync(filePath)) {
19220
19532
  throw new Error(`Fixture not found: ${filePath}`);
19221
19533
  }
@@ -19982,7 +20294,7 @@ async function handleCliFixtureCapture(ctx, req, res) {
19982
20294
  },
19983
20295
  notes: typeof body?.notes === "string" ? body.notes : void 0
19984
20296
  };
19985
- const filePath = path20.join(fixtureDir, `${name}.json`);
20297
+ const filePath = path19.join(fixtureDir, `${name}.json`);
19986
20298
  fs12.writeFileSync(filePath, JSON.stringify(fixture, null, 2));
19987
20299
  ctx.json(res, 200, {
19988
20300
  saved: true,
@@ -20006,7 +20318,7 @@ async function handleCliFixtureList(ctx, type, _req, res) {
20006
20318
  return;
20007
20319
  }
20008
20320
  const fixtures = fs12.readdirSync(fixtureDir).filter((file) => file.endsWith(".json")).sort((a, b) => b.localeCompare(a, void 0, { numeric: true, sensitivity: "base" })).map((file) => {
20009
- const fullPath = path20.join(fixtureDir, file);
20321
+ const fullPath = path19.join(fixtureDir, file);
20010
20322
  try {
20011
20323
  const raw = JSON.parse(fs12.readFileSync(fullPath, "utf-8"));
20012
20324
  return {
@@ -20142,7 +20454,7 @@ async function handleCliRaw(ctx, req, res) {
20142
20454
 
20143
20455
  // src/daemon/dev-auto-implement.ts
20144
20456
  import * as fs13 from "fs";
20145
- import * as path21 from "path";
20457
+ import * as path20 from "path";
20146
20458
  import * as os19 from "os";
20147
20459
  function getAutoImplPid(ctx) {
20148
20460
  const pid = ctx.autoImplProcess?.pid;
@@ -20199,22 +20511,22 @@ function getLatestScriptVersionDir(scriptsDir) {
20199
20511
  if (!fs13.existsSync(scriptsDir)) return null;
20200
20512
  const versions = fs13.readdirSync(scriptsDir).filter((d) => {
20201
20513
  try {
20202
- return fs13.statSync(path21.join(scriptsDir, d)).isDirectory();
20514
+ return fs13.statSync(path20.join(scriptsDir, d)).isDirectory();
20203
20515
  } catch {
20204
20516
  return false;
20205
20517
  }
20206
20518
  }).sort((a, b) => b.localeCompare(a, void 0, { numeric: true, sensitivity: "base" }));
20207
20519
  if (versions.length === 0) return null;
20208
- return path21.join(scriptsDir, versions[0]);
20520
+ return path20.join(scriptsDir, versions[0]);
20209
20521
  }
20210
20522
  function resolveAutoImplWritableProviderDir(ctx, category, type, requestedDir) {
20211
- const canonicalUserDir = path21.resolve(ctx.providerLoader.getUserProviderDir(category, type));
20212
- const desiredDir = requestedDir ? path21.resolve(requestedDir) : canonicalUserDir;
20213
- const upstreamRoot = path21.resolve(ctx.providerLoader.getUpstreamDir());
20214
- if (desiredDir === upstreamRoot || desiredDir.startsWith(`${upstreamRoot}${path21.sep}`)) {
20523
+ const canonicalUserDir = path20.resolve(ctx.providerLoader.getUserProviderDir(category, type));
20524
+ const desiredDir = requestedDir ? path20.resolve(requestedDir) : canonicalUserDir;
20525
+ const upstreamRoot = path20.resolve(ctx.providerLoader.getUpstreamDir());
20526
+ if (desiredDir === upstreamRoot || desiredDir.startsWith(`${upstreamRoot}${path20.sep}`)) {
20215
20527
  return { dir: null, reason: `Refusing to write into upstream provider directory: ${desiredDir}` };
20216
20528
  }
20217
- if (path21.basename(desiredDir) !== type) {
20529
+ if (path20.basename(desiredDir) !== type) {
20218
20530
  return { dir: null, reason: `Requested writable provider directory must end with '${type}': ${desiredDir}` };
20219
20531
  }
20220
20532
  const sourceDir = ctx.findProviderDir(type);
@@ -20222,11 +20534,11 @@ function resolveAutoImplWritableProviderDir(ctx, category, type, requestedDir) {
20222
20534
  return { dir: null, reason: `Provider source directory not found for '${type}'` };
20223
20535
  }
20224
20536
  if (!fs13.existsSync(desiredDir)) {
20225
- fs13.mkdirSync(path21.dirname(desiredDir), { recursive: true });
20537
+ fs13.mkdirSync(path20.dirname(desiredDir), { recursive: true });
20226
20538
  fs13.cpSync(sourceDir, desiredDir, { recursive: true });
20227
20539
  ctx.log(`Auto-implement writable copy created: ${desiredDir}`);
20228
20540
  }
20229
- const providerJson = path21.join(desiredDir, "provider.json");
20541
+ const providerJson = path20.join(desiredDir, "provider.json");
20230
20542
  if (!fs13.existsSync(providerJson)) {
20231
20543
  return { dir: null, reason: `provider.json not found in writable provider directory: ${desiredDir}` };
20232
20544
  }
@@ -20237,13 +20549,13 @@ function loadAutoImplReferenceScripts(ctx, referenceType) {
20237
20549
  const refDir = ctx.findProviderDir(referenceType);
20238
20550
  if (!refDir || !fs13.existsSync(refDir)) return {};
20239
20551
  const referenceScripts = {};
20240
- const scriptsDir = path21.join(refDir, "scripts");
20552
+ const scriptsDir = path20.join(refDir, "scripts");
20241
20553
  const latestDir = getLatestScriptVersionDir(scriptsDir);
20242
20554
  if (!latestDir) return referenceScripts;
20243
20555
  for (const file of fs13.readdirSync(latestDir)) {
20244
20556
  if (!file.endsWith(".js")) continue;
20245
20557
  try {
20246
- referenceScripts[file] = fs13.readFileSync(path21.join(latestDir, file), "utf-8");
20558
+ referenceScripts[file] = fs13.readFileSync(path20.join(latestDir, file), "utf-8");
20247
20559
  } catch {
20248
20560
  }
20249
20561
  }
@@ -20351,9 +20663,9 @@ async function handleAutoImplement(ctx, type, req, res) {
20351
20663
  });
20352
20664
  const referenceScripts = loadAutoImplReferenceScripts(ctx, resolvedReference);
20353
20665
  const prompt = buildAutoImplPrompt(ctx, type, provider, providerDir, functions, domContext, referenceScripts, comment, resolvedReference, verification);
20354
- const tmpDir = path21.join(os19.tmpdir(), "adhdev-autoimpl");
20666
+ const tmpDir = path20.join(os19.tmpdir(), "adhdev-autoimpl");
20355
20667
  if (!fs13.existsSync(tmpDir)) fs13.mkdirSync(tmpDir, { recursive: true });
20356
- const promptFile = path21.join(tmpDir, `prompt-${type}-${Date.now()}.md`);
20668
+ const promptFile = path20.join(tmpDir, `prompt-${type}-${Date.now()}.md`);
20357
20669
  fs13.writeFileSync(promptFile, prompt, "utf-8");
20358
20670
  ctx.log(`Auto-implement prompt written to ${promptFile} (${prompt.length} chars)`);
20359
20671
  const agentProvider = ctx.providerLoader.resolve(agent) || ctx.providerLoader.getMeta(agent);
@@ -20790,7 +21102,7 @@ function buildAutoImplPrompt(ctx, type, provider, providerDir, functions, domCon
20790
21102
  setMode: "set_mode.js"
20791
21103
  };
20792
21104
  const targetFileNames = new Set(functions.map((fn) => funcToFile[fn]).filter(Boolean));
20793
- const scriptsDir = path21.join(providerDir, "scripts");
21105
+ const scriptsDir = path20.join(providerDir, "scripts");
20794
21106
  const latestScriptsDir = getLatestScriptVersionDir(scriptsDir);
20795
21107
  if (latestScriptsDir) {
20796
21108
  lines.push(`Scripts version directory: \`${latestScriptsDir}\``);
@@ -20801,7 +21113,7 @@ function buildAutoImplPrompt(ctx, type, provider, providerDir, functions, domCon
20801
21113
  for (const file of fs13.readdirSync(latestScriptsDir)) {
20802
21114
  if (file.endsWith(".js") && targetFileNames.has(file)) {
20803
21115
  try {
20804
- const content = fs13.readFileSync(path21.join(latestScriptsDir, file), "utf-8");
21116
+ const content = fs13.readFileSync(path20.join(latestScriptsDir, file), "utf-8");
20805
21117
  lines.push(`### \`${file}\` \u270F\uFE0F EDIT`);
20806
21118
  lines.push("```javascript");
20807
21119
  lines.push(content);
@@ -20818,7 +21130,7 @@ function buildAutoImplPrompt(ctx, type, provider, providerDir, functions, domCon
20818
21130
  lines.push("");
20819
21131
  for (const file of refFiles) {
20820
21132
  try {
20821
- const content = fs13.readFileSync(path21.join(latestScriptsDir, file), "utf-8");
21133
+ const content = fs13.readFileSync(path20.join(latestScriptsDir, file), "utf-8");
20822
21134
  lines.push(`### \`${file}\` \u{1F512}`);
20823
21135
  lines.push("```javascript");
20824
21136
  lines.push(content);
@@ -20859,10 +21171,10 @@ function buildAutoImplPrompt(ctx, type, provider, providerDir, functions, domCon
20859
21171
  lines.push("");
20860
21172
  }
20861
21173
  }
20862
- const docsDir = path21.join(providerDir, "../../docs");
21174
+ const docsDir = path20.join(providerDir, "../../docs");
20863
21175
  const loadGuide = (name) => {
20864
21176
  try {
20865
- const p = path21.join(docsDir, name);
21177
+ const p = path20.join(docsDir, name);
20866
21178
  if (fs13.existsSync(p)) return fs13.readFileSync(p, "utf-8");
20867
21179
  } catch {
20868
21180
  }
@@ -21099,7 +21411,7 @@ function buildCliAutoImplPrompt(ctx, type, provider, providerDir, functions, ref
21099
21411
  parseApproval: "parse_approval.js"
21100
21412
  };
21101
21413
  const targetFileNames = new Set(functions.map((fn) => funcToFile[fn]).filter(Boolean));
21102
- const scriptsDir = path21.join(providerDir, "scripts");
21414
+ const scriptsDir = path20.join(providerDir, "scripts");
21103
21415
  const latestScriptsDir = getLatestScriptVersionDir(scriptsDir);
21104
21416
  if (latestScriptsDir) {
21105
21417
  lines.push(`Scripts version directory: \`${latestScriptsDir}\``);
@@ -21111,7 +21423,7 @@ function buildCliAutoImplPrompt(ctx, type, provider, providerDir, functions, ref
21111
21423
  if (!file.endsWith(".js")) continue;
21112
21424
  if (!targetFileNames.has(file)) continue;
21113
21425
  try {
21114
- const content = fs13.readFileSync(path21.join(latestScriptsDir, file), "utf-8");
21426
+ const content = fs13.readFileSync(path20.join(latestScriptsDir, file), "utf-8");
21115
21427
  lines.push(`### \`${file}\` \u270F\uFE0F EDIT`);
21116
21428
  lines.push("```javascript");
21117
21429
  lines.push(content);
@@ -21127,7 +21439,7 @@ function buildCliAutoImplPrompt(ctx, type, provider, providerDir, functions, ref
21127
21439
  lines.push("");
21128
21440
  for (const file of refFiles) {
21129
21441
  try {
21130
- const content = fs13.readFileSync(path21.join(latestScriptsDir, file), "utf-8");
21442
+ const content = fs13.readFileSync(path20.join(latestScriptsDir, file), "utf-8");
21131
21443
  lines.push(`### \`${file}\` \u{1F512}`);
21132
21444
  lines.push("```javascript");
21133
21445
  lines.push(content);
@@ -21160,10 +21472,10 @@ function buildCliAutoImplPrompt(ctx, type, provider, providerDir, functions, ref
21160
21472
  lines.push("");
21161
21473
  }
21162
21474
  }
21163
- const docsDir = path21.join(providerDir, "../../docs");
21475
+ const docsDir = path20.join(providerDir, "../../docs");
21164
21476
  const loadGuide = (name) => {
21165
21477
  try {
21166
- const p = path21.join(docsDir, name);
21478
+ const p = path20.join(docsDir, name);
21167
21479
  if (fs13.existsSync(p)) return fs13.readFileSync(p, "utf-8");
21168
21480
  } catch {
21169
21481
  }
@@ -21610,8 +21922,8 @@ var DevServer = class _DevServer {
21610
21922
  }
21611
21923
  getEndpointList() {
21612
21924
  return this.routes.map((r) => {
21613
- const path23 = typeof r.pattern === "string" ? r.pattern : r.pattern.source.replace(/\\\//g, "/").replace(/\(\[.*?\]\+\)/g, ":type").replace(/[\^$]/g, "");
21614
- return `${r.method.padEnd(5)} ${path23}`;
21925
+ const path22 = typeof r.pattern === "string" ? r.pattern : r.pattern.source.replace(/\\\//g, "/").replace(/\(\[.*?\]\+\)/g, ":type").replace(/[\^$]/g, "");
21926
+ return `${r.method.padEnd(5)} ${path22}`;
21615
21927
  });
21616
21928
  }
21617
21929
  async start(port = DEV_SERVER_PORT) {
@@ -21892,12 +22204,12 @@ var DevServer = class _DevServer {
21892
22204
  // ─── DevConsole SPA ───
21893
22205
  getConsoleDistDir() {
21894
22206
  const candidates = [
21895
- path22.resolve(__dirname, "../../web-devconsole/dist"),
21896
- path22.resolve(__dirname, "../../../web-devconsole/dist"),
21897
- path22.join(process.cwd(), "packages/web-devconsole/dist")
22207
+ path21.resolve(__dirname, "../../web-devconsole/dist"),
22208
+ path21.resolve(__dirname, "../../../web-devconsole/dist"),
22209
+ path21.join(process.cwd(), "packages/web-devconsole/dist")
21898
22210
  ];
21899
22211
  for (const dir of candidates) {
21900
- if (fs14.existsSync(path22.join(dir, "index.html"))) return dir;
22212
+ if (fs14.existsSync(path21.join(dir, "index.html"))) return dir;
21901
22213
  }
21902
22214
  return null;
21903
22215
  }
@@ -21907,7 +22219,7 @@ var DevServer = class _DevServer {
21907
22219
  this.json(res, 500, { error: "DevConsole not found. Run: npm run build -w packages/web-devconsole" });
21908
22220
  return;
21909
22221
  }
21910
- const htmlPath = path22.join(distDir, "index.html");
22222
+ const htmlPath = path21.join(distDir, "index.html");
21911
22223
  try {
21912
22224
  const html = fs14.readFileSync(htmlPath, "utf-8");
21913
22225
  res.writeHead(200, { "Content-Type": "text/html; charset=utf-8" });
@@ -21932,15 +22244,15 @@ var DevServer = class _DevServer {
21932
22244
  this.json(res, 404, { error: "Not found" });
21933
22245
  return;
21934
22246
  }
21935
- const safePath = path22.normalize(pathname).replace(/^\.\.\//, "");
21936
- const filePath = path22.join(distDir, safePath);
22247
+ const safePath = path21.normalize(pathname).replace(/^\.\.\//, "");
22248
+ const filePath = path21.join(distDir, safePath);
21937
22249
  if (!filePath.startsWith(distDir)) {
21938
22250
  this.json(res, 403, { error: "Forbidden" });
21939
22251
  return;
21940
22252
  }
21941
22253
  try {
21942
22254
  const content = fs14.readFileSync(filePath);
21943
- const ext = path22.extname(filePath);
22255
+ const ext = path21.extname(filePath);
21944
22256
  const contentType = _DevServer.MIME_MAP[ext] || "application/octet-stream";
21945
22257
  res.writeHead(200, { "Content-Type": contentType, "Cache-Control": "public, max-age=31536000, immutable" });
21946
22258
  res.end(content);
@@ -22053,9 +22365,9 @@ var DevServer = class _DevServer {
22053
22365
  const rel = prefix ? `${prefix}/${entry.name}` : entry.name;
22054
22366
  if (entry.isDirectory()) {
22055
22367
  files.push({ path: rel, size: 0, type: "dir" });
22056
- scan(path22.join(d, entry.name), rel);
22368
+ scan(path21.join(d, entry.name), rel);
22057
22369
  } else {
22058
- const stat = fs14.statSync(path22.join(d, entry.name));
22370
+ const stat = fs14.statSync(path21.join(d, entry.name));
22059
22371
  files.push({ path: rel, size: stat.size, type: "file" });
22060
22372
  }
22061
22373
  }
@@ -22078,7 +22390,7 @@ var DevServer = class _DevServer {
22078
22390
  this.json(res, 404, { error: `Provider directory not found: ${type}` });
22079
22391
  return;
22080
22392
  }
22081
- const fullPath = path22.resolve(dir, path22.normalize(filePath));
22393
+ const fullPath = path21.resolve(dir, path21.normalize(filePath));
22082
22394
  if (!fullPath.startsWith(dir)) {
22083
22395
  this.json(res, 403, { error: "Forbidden" });
22084
22396
  return;
@@ -22103,14 +22415,14 @@ var DevServer = class _DevServer {
22103
22415
  this.json(res, 404, { error: `Provider directory not found: ${type}` });
22104
22416
  return;
22105
22417
  }
22106
- const fullPath = path22.resolve(dir, path22.normalize(filePath));
22418
+ const fullPath = path21.resolve(dir, path21.normalize(filePath));
22107
22419
  if (!fullPath.startsWith(dir)) {
22108
22420
  this.json(res, 403, { error: "Forbidden" });
22109
22421
  return;
22110
22422
  }
22111
22423
  try {
22112
22424
  if (fs14.existsSync(fullPath)) fs14.copyFileSync(fullPath, fullPath + ".bak");
22113
- fs14.mkdirSync(path22.dirname(fullPath), { recursive: true });
22425
+ fs14.mkdirSync(path21.dirname(fullPath), { recursive: true });
22114
22426
  fs14.writeFileSync(fullPath, content, "utf-8");
22115
22427
  this.log(`File saved: ${fullPath} (${content.length} chars)`);
22116
22428
  this.providerLoader.reload();
@@ -22127,7 +22439,7 @@ var DevServer = class _DevServer {
22127
22439
  return;
22128
22440
  }
22129
22441
  for (const name of ["scripts.js", "provider.json"]) {
22130
- const p = path22.join(dir, name);
22442
+ const p = path21.join(dir, name);
22131
22443
  if (fs14.existsSync(p)) {
22132
22444
  const source = fs14.readFileSync(p, "utf-8");
22133
22445
  this.json(res, 200, { type, path: p, source, lines: source.split("\n").length });
@@ -22148,8 +22460,8 @@ var DevServer = class _DevServer {
22148
22460
  this.json(res, 404, { error: `Provider not found: ${type}` });
22149
22461
  return;
22150
22462
  }
22151
- const target = fs14.existsSync(path22.join(dir, "scripts.js")) ? "scripts.js" : "provider.json";
22152
- const targetPath = path22.join(dir, target);
22463
+ const target = fs14.existsSync(path21.join(dir, "scripts.js")) ? "scripts.js" : "provider.json";
22464
+ const targetPath = path21.join(dir, target);
22153
22465
  try {
22154
22466
  if (fs14.existsSync(targetPath)) fs14.copyFileSync(targetPath, targetPath + ".bak");
22155
22467
  fs14.writeFileSync(targetPath, source, "utf-8");
@@ -22296,7 +22608,7 @@ var DevServer = class _DevServer {
22296
22608
  }
22297
22609
  let targetDir;
22298
22610
  targetDir = this.providerLoader.getUserProviderDir(category, type);
22299
- const jsonPath = path22.join(targetDir, "provider.json");
22611
+ const jsonPath = path21.join(targetDir, "provider.json");
22300
22612
  if (fs14.existsSync(jsonPath)) {
22301
22613
  this.json(res, 409, { error: `Provider already exists at ${targetDir}`, path: targetDir });
22302
22614
  return;
@@ -22308,8 +22620,8 @@ var DevServer = class _DevServer {
22308
22620
  const createdFiles = ["provider.json"];
22309
22621
  if (result.files) {
22310
22622
  for (const [relPath, content] of Object.entries(result.files)) {
22311
- const fullPath = path22.join(targetDir, relPath);
22312
- fs14.mkdirSync(path22.dirname(fullPath), { recursive: true });
22623
+ const fullPath = path21.join(targetDir, relPath);
22624
+ fs14.mkdirSync(path21.dirname(fullPath), { recursive: true });
22313
22625
  fs14.writeFileSync(fullPath, content, "utf-8");
22314
22626
  createdFiles.push(relPath);
22315
22627
  }
@@ -22362,22 +22674,22 @@ var DevServer = class _DevServer {
22362
22674
  if (!fs14.existsSync(scriptsDir)) return null;
22363
22675
  const versions = fs14.readdirSync(scriptsDir).filter((d) => {
22364
22676
  try {
22365
- return fs14.statSync(path22.join(scriptsDir, d)).isDirectory();
22677
+ return fs14.statSync(path21.join(scriptsDir, d)).isDirectory();
22366
22678
  } catch {
22367
22679
  return false;
22368
22680
  }
22369
22681
  }).sort((a, b) => b.localeCompare(a, void 0, { numeric: true, sensitivity: "base" }));
22370
22682
  if (versions.length === 0) return null;
22371
- return path22.join(scriptsDir, versions[0]);
22683
+ return path21.join(scriptsDir, versions[0]);
22372
22684
  }
22373
22685
  resolveAutoImplWritableProviderDir(category, type, requestedDir) {
22374
- const canonicalUserDir = path22.resolve(this.providerLoader.getUserProviderDir(category, type));
22375
- const desiredDir = requestedDir ? path22.resolve(requestedDir) : canonicalUserDir;
22376
- const upstreamRoot = path22.resolve(this.providerLoader.getUpstreamDir());
22377
- if (desiredDir === upstreamRoot || desiredDir.startsWith(`${upstreamRoot}${path22.sep}`)) {
22686
+ const canonicalUserDir = path21.resolve(this.providerLoader.getUserProviderDir(category, type));
22687
+ const desiredDir = requestedDir ? path21.resolve(requestedDir) : canonicalUserDir;
22688
+ const upstreamRoot = path21.resolve(this.providerLoader.getUpstreamDir());
22689
+ if (desiredDir === upstreamRoot || desiredDir.startsWith(`${upstreamRoot}${path21.sep}`)) {
22378
22690
  return { dir: null, reason: `Refusing to write into upstream provider directory: ${desiredDir}` };
22379
22691
  }
22380
- if (path22.basename(desiredDir) !== type) {
22692
+ if (path21.basename(desiredDir) !== type) {
22381
22693
  return { dir: null, reason: `Requested writable provider directory must end with '${type}': ${desiredDir}` };
22382
22694
  }
22383
22695
  const sourceDir = this.findProviderDir(type);
@@ -22385,11 +22697,11 @@ var DevServer = class _DevServer {
22385
22697
  return { dir: null, reason: `Provider source directory not found for '${type}'` };
22386
22698
  }
22387
22699
  if (!fs14.existsSync(desiredDir)) {
22388
- fs14.mkdirSync(path22.dirname(desiredDir), { recursive: true });
22700
+ fs14.mkdirSync(path21.dirname(desiredDir), { recursive: true });
22389
22701
  fs14.cpSync(sourceDir, desiredDir, { recursive: true });
22390
22702
  this.log(`Auto-implement writable copy created: ${desiredDir}`);
22391
22703
  }
22392
- const providerJson = path22.join(desiredDir, "provider.json");
22704
+ const providerJson = path21.join(desiredDir, "provider.json");
22393
22705
  if (!fs14.existsSync(providerJson)) {
22394
22706
  return { dir: null, reason: `provider.json not found in writable provider directory: ${desiredDir}` };
22395
22707
  }
@@ -22425,7 +22737,7 @@ var DevServer = class _DevServer {
22425
22737
  setMode: "set_mode.js"
22426
22738
  };
22427
22739
  const targetFileNames = new Set(functions.map((fn) => funcToFile[fn]).filter(Boolean));
22428
- const scriptsDir = path22.join(providerDir, "scripts");
22740
+ const scriptsDir = path21.join(providerDir, "scripts");
22429
22741
  const latestScriptsDir = this.getLatestScriptVersionDir(scriptsDir);
22430
22742
  if (latestScriptsDir) {
22431
22743
  lines.push(`Scripts version directory: \`${latestScriptsDir}\``);
@@ -22436,7 +22748,7 @@ var DevServer = class _DevServer {
22436
22748
  for (const file of fs14.readdirSync(latestScriptsDir)) {
22437
22749
  if (file.endsWith(".js") && targetFileNames.has(file)) {
22438
22750
  try {
22439
- const content = fs14.readFileSync(path22.join(latestScriptsDir, file), "utf-8");
22751
+ const content = fs14.readFileSync(path21.join(latestScriptsDir, file), "utf-8");
22440
22752
  lines.push(`### \`${file}\` \u270F\uFE0F EDIT`);
22441
22753
  lines.push("```javascript");
22442
22754
  lines.push(content);
@@ -22453,7 +22765,7 @@ var DevServer = class _DevServer {
22453
22765
  lines.push("");
22454
22766
  for (const file of refFiles) {
22455
22767
  try {
22456
- const content = fs14.readFileSync(path22.join(latestScriptsDir, file), "utf-8");
22768
+ const content = fs14.readFileSync(path21.join(latestScriptsDir, file), "utf-8");
22457
22769
  lines.push(`### \`${file}\` \u{1F512}`);
22458
22770
  lines.push("```javascript");
22459
22771
  lines.push(content);
@@ -22494,10 +22806,10 @@ var DevServer = class _DevServer {
22494
22806
  lines.push("");
22495
22807
  }
22496
22808
  }
22497
- const docsDir = path22.join(providerDir, "../../docs");
22809
+ const docsDir = path21.join(providerDir, "../../docs");
22498
22810
  const loadGuide = (name) => {
22499
22811
  try {
22500
- const p = path22.join(docsDir, name);
22812
+ const p = path21.join(docsDir, name);
22501
22813
  if (fs14.existsSync(p)) return fs14.readFileSync(p, "utf-8");
22502
22814
  } catch {
22503
22815
  }
@@ -22671,7 +22983,7 @@ var DevServer = class _DevServer {
22671
22983
  parseApproval: "parse_approval.js"
22672
22984
  };
22673
22985
  const targetFileNames = new Set(functions.map((fn) => funcToFile[fn]).filter(Boolean));
22674
- const scriptsDir = path22.join(providerDir, "scripts");
22986
+ const scriptsDir = path21.join(providerDir, "scripts");
22675
22987
  const latestScriptsDir = this.getLatestScriptVersionDir(scriptsDir);
22676
22988
  if (latestScriptsDir) {
22677
22989
  lines.push(`Scripts version directory: \`${latestScriptsDir}\``);
@@ -22683,7 +22995,7 @@ var DevServer = class _DevServer {
22683
22995
  if (!file.endsWith(".js")) continue;
22684
22996
  if (!targetFileNames.has(file)) continue;
22685
22997
  try {
22686
- const content = fs14.readFileSync(path22.join(latestScriptsDir, file), "utf-8");
22998
+ const content = fs14.readFileSync(path21.join(latestScriptsDir, file), "utf-8");
22687
22999
  lines.push(`### \`${file}\` \u270F\uFE0F EDIT`);
22688
23000
  lines.push("```javascript");
22689
23001
  lines.push(content);
@@ -22699,7 +23011,7 @@ var DevServer = class _DevServer {
22699
23011
  lines.push("");
22700
23012
  for (const file of refFiles) {
22701
23013
  try {
22702
- const content = fs14.readFileSync(path22.join(latestScriptsDir, file), "utf-8");
23014
+ const content = fs14.readFileSync(path21.join(latestScriptsDir, file), "utf-8");
22703
23015
  lines.push(`### \`${file}\` \u{1F512}`);
22704
23016
  lines.push("```javascript");
22705
23017
  lines.push(content);
@@ -22732,10 +23044,10 @@ var DevServer = class _DevServer {
22732
23044
  lines.push("");
22733
23045
  }
22734
23046
  }
22735
- const docsDir = path22.join(providerDir, "../../docs");
23047
+ const docsDir = path21.join(providerDir, "../../docs");
22736
23048
  const loadGuide = (name) => {
22737
23049
  try {
22738
- const p = path22.join(docsDir, name);
23050
+ const p = path21.join(docsDir, name);
22739
23051
  if (fs14.existsSync(p)) return fs14.readFileSync(p, "utf-8");
22740
23052
  } catch {
22741
23053
  }