remote-codex 0.11.0 → 0.11.1

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.
@@ -263,9 +263,19 @@ function pricingModelKeyForModel(model) {
263
263
  if (models[modelKey2]) {
264
264
  return modelKey2;
265
265
  }
266
- const claudeDateSuffixMatch = modelKey2.match(/^(claude-[a-z]+-\d+(?:-\d+)?)-20\d{6}$/);
267
- if (claudeDateSuffixMatch?.[1] && models[claudeDateSuffixMatch[1]]) {
268
- return claudeDateSuffixMatch[1];
266
+ const providerSeparatorIndex = modelKey2.indexOf("/");
267
+ const unqualifiedModelKey = providerSeparatorIndex >= 0 ? modelKey2.slice(providerSeparatorIndex + 1) : null;
268
+ if (unqualifiedModelKey && models[unqualifiedModelKey]) {
269
+ return unqualifiedModelKey;
270
+ }
271
+ for (const candidate of [modelKey2, unqualifiedModelKey]) {
272
+ if (!candidate) {
273
+ continue;
274
+ }
275
+ const claudeDateSuffixMatch = candidate.match(/^(claude-[a-z]+-\d+(?:-\d+)?)-20\d{6}$/);
276
+ if (claudeDateSuffixMatch?.[1] && models[claudeDateSuffixMatch[1]]) {
277
+ return claudeDateSuffixMatch[1];
278
+ }
269
279
  }
270
280
  return modelKey2;
271
281
  }
@@ -487,6 +497,354 @@ function isUnsupportedHooksListError(error) {
487
497
  // ../../packages/agent-runtime/src/unavailable-runtime.ts
488
498
  import { EventEmitter } from "events";
489
499
 
500
+ // ../../packages/plugin-runtime/src/registry.ts
501
+ var PluginRegistry = class {
502
+ plugins = /* @__PURE__ */ new Map();
503
+ enabled = /* @__PURE__ */ new Map();
504
+ constructor(plugins = []) {
505
+ for (const plugin of plugins) {
506
+ this.register(plugin);
507
+ }
508
+ }
509
+ register(plugin) {
510
+ if (this.plugins.has(plugin.manifest.id)) {
511
+ throw new Error(`Plugin is already registered: ${plugin.manifest.id}`);
512
+ }
513
+ this.plugins.set(plugin.manifest.id, plugin);
514
+ this.enabled.set(plugin.manifest.id, plugin.enabledByDefault ?? true);
515
+ }
516
+ updateImported(plugin) {
517
+ const existing = this.plugins.get(plugin.manifest.id);
518
+ if (existing && existing.source !== "imported") {
519
+ throw new Error(`Built-in plugin cannot be replaced: ${plugin.manifest.id}`);
520
+ }
521
+ this.plugins.set(plugin.manifest.id, {
522
+ ...plugin,
523
+ source: "imported"
524
+ });
525
+ this.enabled.set(plugin.manifest.id, plugin.enabledByDefault ?? true);
526
+ }
527
+ list() {
528
+ return [...this.plugins.values()].map(
529
+ (plugin) => this.toDto(plugin.manifest)
530
+ );
531
+ }
532
+ get(pluginId) {
533
+ const plugin = this.plugins.get(pluginId);
534
+ return plugin ? this.toDto(plugin.manifest) : null;
535
+ }
536
+ getManifest(pluginId) {
537
+ return this.plugins.get(pluginId)?.manifest ?? null;
538
+ }
539
+ getRegistered(pluginId) {
540
+ return this.plugins.get(pluginId) ?? null;
541
+ }
542
+ isEnabled(pluginId) {
543
+ return this.enabled.get(pluginId) ?? false;
544
+ }
545
+ setEnabled(pluginId, enabled) {
546
+ const plugin = this.plugins.get(pluginId);
547
+ if (!plugin) {
548
+ throw new Error(`Plugin is not registered: ${pluginId}`);
549
+ }
550
+ this.enabled.set(pluginId, enabled);
551
+ return this.toDto(plugin.manifest);
552
+ }
553
+ enabledManifests() {
554
+ return [...this.plugins.values()].filter((plugin) => this.isEnabled(plugin.manifest.id)).map((plugin) => plugin.manifest);
555
+ }
556
+ toDto(manifest) {
557
+ return {
558
+ ...manifest,
559
+ enabled: this.isEnabled(manifest.id),
560
+ source: this.plugins.get(manifest.id)?.source ?? "builtin"
561
+ };
562
+ }
563
+ };
564
+
565
+ // ../../packages/plugin-runtime/src/artifacts.ts
566
+ var artifactFenceLanguages = /* @__PURE__ */ new Set(["artifact", "remote-codex-artifact"]);
567
+ function stableArtifactId(input) {
568
+ return [
569
+ "artifact",
570
+ input.pluginId.replace(/[^a-zA-Z0-9_-]+/g, "-"),
571
+ input.artifactType.replace(/[^a-zA-Z0-9_-]+/g, "-"),
572
+ input.turnId.replace(/[^a-zA-Z0-9_-]+/g, "-"),
573
+ input.itemId.replace(/[^a-zA-Z0-9_-]+/g, "-"),
574
+ input.index
575
+ ].join(":");
576
+ }
577
+ function artifactItemFromArtifact(artifact, sourceItem, sequenceOffset) {
578
+ return {
579
+ id: artifact.id,
580
+ kind: "artifact",
581
+ text: artifact.title,
582
+ previewText: artifact.summaryText ?? artifact.title,
583
+ sequence: sourceItem.sequence === null || sourceItem.sequence === void 0 ? null : sourceItem.sequence + sequenceOffset,
584
+ sourceTurnId: artifact.sourceTurnId ?? null,
585
+ artifact
586
+ };
587
+ }
588
+ function maybeParseArtifactPayload(value) {
589
+ if (!value || typeof value !== "object") {
590
+ return null;
591
+ }
592
+ const record = value;
593
+ const type = record.type === "remote-codex.artifact" ? record.artifactType : record.artifactType ?? record.type;
594
+ if (typeof type !== "string" || !type.trim()) {
595
+ return null;
596
+ }
597
+ return {
598
+ artifactType: type,
599
+ title: typeof record.title === "string" ? record.title : null,
600
+ summaryText: typeof record.summaryText === "string" ? record.summaryText : null,
601
+ payload: record.payload ?? record
602
+ };
603
+ }
604
+ function findFencedBlocks(text2, languages) {
605
+ const blocks = [];
606
+ const lines = text2.split(/\r?\n/);
607
+ for (let index = 0; index < lines.length; index += 1) {
608
+ const line = lines[index] ?? "";
609
+ const opener = line.match(/^ {0,3}(`{3,}|~{3,})(.*)$/);
610
+ if (!opener) {
611
+ continue;
612
+ }
613
+ const marker = opener[1] ?? "";
614
+ const markerChar = marker[0] ?? "`";
615
+ const markerLength = marker.length;
616
+ const language = (opener[2] ?? "").trim().split(/\s+/)[0]?.toLowerCase() ?? "";
617
+ const contentLines = [];
618
+ index += 1;
619
+ while (index < lines.length) {
620
+ const closeLine = lines[index] ?? "";
621
+ const closePattern = new RegExp(`^ {0,3}\\${markerChar}{${markerLength},}\\s*$`);
622
+ if (closePattern.test(closeLine)) {
623
+ break;
624
+ }
625
+ contentLines.push(closeLine);
626
+ index += 1;
627
+ }
628
+ if (languages.has(language)) {
629
+ blocks.push({
630
+ language,
631
+ content: contentLines.join("\n").trim()
632
+ });
633
+ }
634
+ }
635
+ return blocks;
636
+ }
637
+ var ManifestArtifactExtractor = class {
638
+ constructor(manifests) {
639
+ this.manifests = manifests;
640
+ }
641
+ manifests;
642
+ extractFromTurn(turn, context) {
643
+ const results = [];
644
+ for (const item of turn.items) {
645
+ const artifacts = this.extractFromItem(turn, item, context);
646
+ if (artifacts.length > 0) {
647
+ results.push({ sourceItem: item, artifacts });
648
+ }
649
+ }
650
+ return results;
651
+ }
652
+ extractFromItem(turn, item, context) {
653
+ if (item.kind === "artifact" || !item.text.trim()) {
654
+ return [];
655
+ }
656
+ const artifacts = [];
657
+ artifacts.push(...this.extractJsonArtifacts(turn, item, context));
658
+ return artifacts;
659
+ }
660
+ extractJsonArtifacts(turn, item, context) {
661
+ const artifacts = [];
662
+ for (const block of findFencedBlocks(item.text, artifactFenceLanguages)) {
663
+ if (!block.content) {
664
+ continue;
665
+ }
666
+ let parsed;
667
+ try {
668
+ parsed = JSON.parse(block.content);
669
+ } catch {
670
+ continue;
671
+ }
672
+ const payload = maybeParseArtifactPayload(parsed);
673
+ if (!payload || !this.hasArtifactType(payload.artifactType)) {
674
+ continue;
675
+ }
676
+ artifacts.push({
677
+ id: stableArtifactId({
678
+ turnId: turn.id,
679
+ itemId: item.id,
680
+ pluginId: this.pluginIdForArtifactType(payload.artifactType) ?? "unknown",
681
+ artifactType: payload.artifactType,
682
+ index: artifacts.length
683
+ }),
684
+ pluginId: this.pluginIdForArtifactType(payload.artifactType) ?? "unknown",
685
+ type: payload.artifactType,
686
+ title: payload.title ?? "Plugin artifact",
687
+ summaryText: payload.summaryText ?? null,
688
+ payload: payload.payload,
689
+ sourceTurnId: turn.id,
690
+ sourceItemId: item.id,
691
+ createdAt: context.now
692
+ });
693
+ }
694
+ return artifacts;
695
+ }
696
+ hasArtifactType(artifactType) {
697
+ return this.pluginIdForArtifactType(artifactType) !== null;
698
+ }
699
+ pluginIdForArtifactType(artifactType) {
700
+ for (const manifest of this.manifests) {
701
+ if (manifest.capabilities.artifactTypes.some(
702
+ (entry) => entry.type === artifactType
703
+ )) {
704
+ return manifest.id;
705
+ }
706
+ }
707
+ return null;
708
+ }
709
+ };
710
+ function appendArtifactItemsToTurns(turns, extractor, context) {
711
+ return turns.map((turn) => {
712
+ const extractionResults = extractor.extractFromTurn(turn, context);
713
+ if (extractionResults.length === 0) {
714
+ return turn;
715
+ }
716
+ const artifactItemsBySourceItemId = /* @__PURE__ */ new Map();
717
+ for (const result of extractionResults) {
718
+ artifactItemsBySourceItemId.set(
719
+ result.sourceItem.id,
720
+ result.artifacts.map(
721
+ (artifact, index) => artifactItemFromArtifact(artifact, result.sourceItem, (index + 1) / 100)
722
+ )
723
+ );
724
+ }
725
+ const items = [];
726
+ const existingIds = new Set(turn.items.map((item) => item.id));
727
+ for (const item of turn.items) {
728
+ items.push(item);
729
+ const artifactItems = artifactItemsBySourceItemId.get(item.id) ?? [];
730
+ for (const artifactItem of artifactItems) {
731
+ if (!existingIds.has(artifactItem.id)) {
732
+ items.push(artifactItem);
733
+ existingIds.add(artifactItem.id);
734
+ }
735
+ }
736
+ }
737
+ return {
738
+ ...turn,
739
+ items
740
+ };
741
+ });
742
+ }
743
+
744
+ // ../../packages/plugin-runtime/src/manifest.ts
745
+ function isRecord2(value) {
746
+ return Boolean(value) && typeof value === "object" && !Array.isArray(value);
747
+ }
748
+ function assertString(value, field) {
749
+ if (typeof value !== "string" || !value.trim()) {
750
+ throw new Error(`Plugin manifest field "${field}" must be a non-empty string.`);
751
+ }
752
+ return value.trim();
753
+ }
754
+ function optionalStringArray(value, field) {
755
+ if (value === void 0) {
756
+ return void 0;
757
+ }
758
+ if (!Array.isArray(value) || value.some((entry) => typeof entry !== "string")) {
759
+ throw new Error(`Plugin manifest field "${field}" must be an array of strings.`);
760
+ }
761
+ return value;
762
+ }
763
+ function parsePluginManifest(value) {
764
+ if (!isRecord2(value)) {
765
+ throw new Error("Plugin manifest must be an object.");
766
+ }
767
+ const capabilities = value.capabilities;
768
+ if (!isRecord2(capabilities)) {
769
+ throw new Error('Plugin manifest field "capabilities" must be an object.');
770
+ }
771
+ const artifactTypes = capabilities.artifactTypes;
772
+ if (!Array.isArray(artifactTypes)) {
773
+ throw new Error('Plugin manifest field "capabilities.artifactTypes" must be an array.');
774
+ }
775
+ const timelineRenderers = optionalStringArray(
776
+ capabilities.timelineRenderers,
777
+ "capabilities.timelineRenderers"
778
+ ) ?? [];
779
+ const threadPanels = capabilities.threadPanels;
780
+ if (threadPanels !== void 0 && !Array.isArray(threadPanels)) {
781
+ throw new Error('Plugin manifest field "capabilities.threadPanels" must be an array.');
782
+ }
783
+ const frontend = capabilities.frontend;
784
+ if (frontend !== void 0 && !isRecord2(frontend)) {
785
+ throw new Error('Plugin manifest field "capabilities.frontend" must be an object.');
786
+ }
787
+ const backend = capabilities.backend;
788
+ if (backend !== void 0 && !isRecord2(backend)) {
789
+ throw new Error('Plugin manifest field "capabilities.backend" must be an object.');
790
+ }
791
+ return {
792
+ id: assertString(value.id, "id"),
793
+ name: assertString(value.name, "name"),
794
+ version: assertString(value.version, "version"),
795
+ description: assertString(value.description, "description"),
796
+ remoteCodex: assertString(value.remoteCodex, "remoteCodex"),
797
+ capabilities: {
798
+ artifactTypes: artifactTypes.map((entry, index) => {
799
+ if (!isRecord2(entry)) {
800
+ throw new Error(
801
+ `Plugin manifest field "capabilities.artifactTypes[${index}]" must be an object.`
802
+ );
803
+ }
804
+ const parsed = {
805
+ type: assertString(entry.type, `capabilities.artifactTypes[${index}].type`),
806
+ title: assertString(entry.title, `capabilities.artifactTypes[${index}].title`)
807
+ };
808
+ const fileExtensions = optionalStringArray(
809
+ entry.fileExtensions,
810
+ `capabilities.artifactTypes[${index}].fileExtensions`
811
+ );
812
+ return fileExtensions ? {
813
+ ...parsed,
814
+ fileExtensions
815
+ } : parsed;
816
+ }),
817
+ timelineRenderers,
818
+ threadPanels: (threadPanels ?? []).map((entry, index) => {
819
+ if (!isRecord2(entry)) {
820
+ throw new Error(
821
+ `Plugin manifest field "capabilities.threadPanels[${index}]" must be an object.`
822
+ );
823
+ }
824
+ return {
825
+ id: assertString(entry.id, `capabilities.threadPanels[${index}].id`),
826
+ label: assertString(entry.label, `capabilities.threadPanels[${index}].label`),
827
+ artifactTypes: optionalStringArray(
828
+ entry.artifactTypes,
829
+ `capabilities.threadPanels[${index}].artifactTypes`
830
+ ) ?? []
831
+ };
832
+ }),
833
+ ...frontend ? {
834
+ frontend: {
835
+ ...typeof frontend.entry === "string" ? { entry: frontend.entry } : {},
836
+ ...typeof frontend.style === "string" ? { style: frontend.style } : {}
837
+ }
838
+ } : {},
839
+ ...backend ? {
840
+ backend: {
841
+ ...typeof backend.entry === "string" ? { entry: backend.entry } : {}
842
+ }
843
+ } : {}
844
+ }
845
+ };
846
+ }
847
+
490
848
  // ../../packages/db/src/client.ts
491
849
  import fs2 from "fs";
492
850
  import os2 from "os";
@@ -7326,7 +7684,7 @@ var CodexAppServerManager = class extends EventEmitter3 {
7326
7684
  };
7327
7685
 
7328
7686
  // ../../packages/codex/src/hookHistory.ts
7329
- function isRecord2(value) {
7687
+ function isRecord3(value) {
7330
7688
  return Boolean(value && typeof value === "object" && !Array.isArray(value));
7331
7689
  }
7332
7690
  function decodeXmlEntities(value) {
@@ -7381,7 +7739,7 @@ function codexHookStatusLabel(value) {
7381
7739
  }
7382
7740
  }
7383
7741
  function hookRunOutputEntryText(entry) {
7384
- if (!isRecord2(entry)) {
7742
+ if (!isRecord3(entry)) {
7385
7743
  return textFromUnknown(entry);
7386
7744
  }
7387
7745
  return textFromUnknown(entry.text) ?? textFromUnknown(entry.message) ?? textFromUnknown(entry.systemMessage) ?? textFromUnknown(entry.stopReason) ?? textFromUnknown(entry.reason) ?? textFromUnknown(entry.output) ?? textFromUnknown(entry.stdout) ?? textFromUnknown(entry.stderr);
@@ -7475,7 +7833,7 @@ ${entryPreview}` : null
7475
7833
  var DEFERRED_COMMAND_DETAIL_TITLE = "Command Output";
7476
7834
  var DEFERRED_TOOL_DETAIL_TITLE = "Tool Call Details";
7477
7835
  var DEFERRED_AGENT_TOOL_DETAIL_TITLE = "Agent Details";
7478
- function isRecord3(value) {
7836
+ function isRecord4(value) {
7479
7837
  return typeof value === "object" && value !== null && !Array.isArray(value);
7480
7838
  }
7481
7839
  function stringOrNull(value) {
@@ -7504,6 +7862,36 @@ function stringArray(value) {
7504
7862
  function uniqueStrings(values) {
7505
7863
  return [...new Set(values.filter((value) => Boolean(value?.trim())))];
7506
7864
  }
7865
+ function projectRelativePathLabel(value) {
7866
+ const normalized = value?.trim();
7867
+ if (!normalized) {
7868
+ return null;
7869
+ }
7870
+ const slashNormalized = normalized.replace(/\\/g, "/");
7871
+ if (!slashNormalized.startsWith("/")) {
7872
+ return slashNormalized.replace(/^\.\//, "");
7873
+ }
7874
+ const markers = [
7875
+ "/apps/",
7876
+ "/packages/",
7877
+ "/src/",
7878
+ "/test/",
7879
+ "/tests/",
7880
+ "/docs/",
7881
+ "/config/",
7882
+ "/scripts/",
7883
+ "/e2e/",
7884
+ "/.agents/",
7885
+ "/.codex/"
7886
+ ];
7887
+ for (const marker of markers) {
7888
+ const markerIndex = slashNormalized.indexOf(marker);
7889
+ if (markerIndex >= 0) {
7890
+ return slashNormalized.slice(markerIndex + 1);
7891
+ }
7892
+ }
7893
+ return slashNormalized;
7894
+ }
7507
7895
  function normalizeTextLines(text2) {
7508
7896
  const lines = text2.replace(/\r\n/g, "\n").split("\n");
7509
7897
  while (lines.length > 1 && lines.at(-1)?.trim() === "") {
@@ -7572,8 +7960,8 @@ function deferCommandHistoryItem(item, deferredDetails) {
7572
7960
  function formatCommandHistoryItem(item, deferredDetails) {
7573
7961
  const nestedRecords = [
7574
7962
  item,
7575
- isRecord3(item.action) ? item.action : null,
7576
- isRecord3(item.result) ? item.result : null
7963
+ isRecord4(item.action) ? item.action : null,
7964
+ isRecord4(item.result) ? item.result : null
7577
7965
  ].filter((candidate) => Boolean(candidate));
7578
7966
  const commandText = textFromUnknown2(valueFromNestedRecords(nestedRecords, ["command", "cmd", "argv"])) ?? stringOrNull(item.text) ?? "Command output";
7579
7967
  const outputText = textFromUnknown2(
@@ -7611,13 +7999,13 @@ function deferToolCallHistoryItem(item, deferredDetails) {
7611
7999
  };
7612
8000
  }
7613
8001
  function extractToolCallRecords(item) {
7614
- const action = isRecord3(item.action) ? item.action : null;
7615
- const result = isRecord3(item.result) ? item.result : null;
8002
+ const action = isRecord4(item.action) ? item.action : null;
8003
+ const result = isRecord4(item.result) ? item.result : null;
7616
8004
  return {
7617
8005
  action,
7618
8006
  result,
7619
- input: action && isRecord3(action.input) ? action.input : null,
7620
- output: result && isRecord3(result.output) ? result.output : null
8007
+ input: action && isRecord4(action.input) ? action.input : null,
8008
+ output: result && isRecord4(result.output) ? result.output : null
7621
8009
  };
7622
8010
  }
7623
8011
  function valueFromNestedRecords(records, keys) {
@@ -7696,20 +8084,20 @@ function formatToolCallHistoryItem(item, deferredDetails, kind = "toolCall") {
7696
8084
  return historyItem;
7697
8085
  }
7698
8086
  function extractWebSearchQueries(item) {
7699
- const action = isRecord3(item.action) ? item.action : null;
7700
- const result = isRecord3(item.result) ? item.result : null;
8087
+ const action = isRecord4(item.action) ? item.action : null;
8088
+ const result = isRecord4(item.result) ? item.result : null;
7701
8089
  return uniqueStrings([
7702
8090
  stringOrNull(item.query),
7703
8091
  ...stringArray(item.queries),
7704
8092
  action ? stringOrNull(action.query) : null,
7705
8093
  ...action ? stringArray(action.queries) : [],
7706
- action && isRecord3(action.input) ? stringOrNull(action.input.query) : null,
8094
+ action && isRecord4(action.input) ? stringOrNull(action.input.query) : null,
7707
8095
  result ? stringOrNull(result.query) : null,
7708
8096
  ...result ? stringArray(result.queries) : []
7709
8097
  ]);
7710
8098
  }
7711
8099
  function normalizeWebSearchSource(value) {
7712
- if (!isRecord3(value)) {
8100
+ if (!isRecord4(value)) {
7713
8101
  return null;
7714
8102
  }
7715
8103
  const title = stringOrNull(value.title) ?? stringOrNull(value.name);
@@ -7721,8 +8109,8 @@ function normalizeWebSearchSource(value) {
7721
8109
  return { title, url, snippet };
7722
8110
  }
7723
8111
  function extractWebSearchSources(item) {
7724
- const action = isRecord3(item.action) ? item.action : null;
7725
- const result = isRecord3(item.result) ? item.result : null;
8112
+ const action = isRecord4(item.action) ? item.action : null;
8113
+ const result = isRecord4(item.result) ? item.result : null;
7726
8114
  const candidates = [
7727
8115
  item.sources,
7728
8116
  action?.sources,
@@ -7763,10 +8151,10 @@ function extractImageAssetPath(item) {
7763
8151
  item.path,
7764
8152
  item.imagePath,
7765
8153
  item.filePath,
7766
- isRecord3(item.action) ? item.action.path : null,
7767
- isRecord3(item.action) ? item.action.imagePath : null,
7768
- isRecord3(item.result) ? item.result.path : null,
7769
- isRecord3(item.result) ? item.result.imagePath : null
8154
+ isRecord4(item.action) ? item.action.path : null,
8155
+ isRecord4(item.action) ? item.action.imagePath : null,
8156
+ isRecord4(item.result) ? item.result.path : null,
8157
+ isRecord4(item.result) ? item.result.imagePath : null
7770
8158
  ];
7771
8159
  for (const candidate of candidates) {
7772
8160
  const normalized = stringOrNull(candidate);
@@ -7887,10 +8275,10 @@ function extractFileChangeEntries(item) {
7887
8275
  const candidateArrays = [
7888
8276
  item.changes,
7889
8277
  item.files,
7890
- isRecord3(item.result) ? item.result.changes : null,
7891
- isRecord3(item.result) ? item.result.files : null,
7892
- isRecord3(item.action) ? item.action.changes : null,
7893
- isRecord3(item.action) ? item.action.files : null
8278
+ isRecord4(item.result) ? item.result.changes : null,
8279
+ isRecord4(item.result) ? item.result.files : null,
8280
+ isRecord4(item.action) ? item.action.changes : null,
8281
+ isRecord4(item.action) ? item.action.files : null
7894
8282
  ];
7895
8283
  const normalizedEntries = /* @__PURE__ */ new Map();
7896
8284
  function valueFromRecords(records, keys) {
@@ -7912,16 +8300,16 @@ function extractFileChangeEntries(item) {
7912
8300
  deletions: 0
7913
8301
  };
7914
8302
  }
7915
- if (!isRecord3(entry)) {
8303
+ if (!isRecord4(entry)) {
7916
8304
  return null;
7917
8305
  }
7918
8306
  const nestedRecords = [
7919
8307
  entry,
7920
- isRecord3(entry.result) ? entry.result : null,
7921
- isRecord3(entry.action) ? entry.action : null,
7922
- isRecord3(entry.stats) ? entry.stats : null,
7923
- isRecord3(entry.summary) ? entry.summary : null,
7924
- isRecord3(entry.diff) ? entry.diff : null
8308
+ isRecord4(entry.result) ? entry.result : null,
8309
+ isRecord4(entry.action) ? entry.action : null,
8310
+ isRecord4(entry.stats) ? entry.stats : null,
8311
+ isRecord4(entry.summary) ? entry.summary : null,
8312
+ isRecord4(entry.diff) ? entry.diff : null
7925
8313
  ].filter((candidate) => Boolean(candidate));
7926
8314
  const path22 = uniqueStrings([
7927
8315
  stringOrNull(valueFromRecords(nestedRecords, ["path", "filePath", "targetPath"])),
@@ -7937,7 +8325,7 @@ function extractFileChangeEntries(item) {
7937
8325
  "old_path"
7938
8326
  ])
7939
8327
  )
7940
- ])[0] ?? null;
8328
+ ]).map(projectRelativePathLabel).find((entry2) => Boolean(entry2)) ?? null;
7941
8329
  const explicitAdditions = numberOrNull(
7942
8330
  valueFromRecords(nestedRecords, [
7943
8331
  "additions",
@@ -7970,7 +8358,7 @@ function extractFileChangeEntries(item) {
7970
8358
  const diffStats = explicitAdditions === 0 && explicitDeletions === 0 && diffText ? countUnifiedDiffStats(diffText) : null;
7971
8359
  const additions = explicitAdditions || diffStats?.additions || 0;
7972
8360
  const deletions = explicitDeletions || diffStats?.deletions || 0;
7973
- const normalizedPath = path22 ?? (diffText ? extractPathFromDiffText(diffText) : null);
8361
+ const normalizedPath = path22 ?? (diffText ? projectRelativePathLabel(extractPathFromDiffText(diffText)) : null);
7974
8362
  if (!normalizedPath && additions === 0 && deletions === 0) {
7975
8363
  return null;
7976
8364
  }
@@ -8608,11 +8996,11 @@ var HOOK_EVENT_JSON_KEYS = {
8608
8996
  var HOOK_EVENT_DTO_KEYS = Object.fromEntries(
8609
8997
  Object.entries(HOOK_EVENT_JSON_KEYS).map(([dtoKey, jsonKey]) => [jsonKey, dtoKey])
8610
8998
  );
8611
- function isRecord4(value) {
8999
+ function isRecord5(value) {
8612
9000
  return typeof value === "object" && value !== null && !Array.isArray(value);
8613
9001
  }
8614
9002
  function normalizeHooksJson(value) {
8615
- if (!isRecord4(value) || !isRecord4(value.hooks)) {
9003
+ if (!isRecord5(value) || !isRecord5(value.hooks)) {
8616
9004
  return { hooks: {} };
8617
9005
  }
8618
9006
  const hooks = {};
@@ -8652,7 +9040,7 @@ function hooksPathForInput(codexHome, workspacePath, input) {
8652
9040
  return input.scope === "global" ? path8.join(codexHome, "hooks.json") : path8.join(workspacePath, ".codex", "hooks.json");
8653
9041
  }
8654
9042
  function hookInputMatches(group, handler, input) {
8655
- if (!isRecord4(group) || !isRecord4(handler)) {
9043
+ if (!isRecord5(group) || !isRecord5(handler)) {
8656
9044
  return false;
8657
9045
  }
8658
9046
  const matcher = typeof group.matcher === "string" ? group.matcher : null;
@@ -8803,7 +9191,7 @@ var CodexManagementService = class {
8803
9191
  const currentGroups = Array.isArray(config.hooks[targetEventKey]) ? config.hooks[targetEventKey] : [];
8804
9192
  let replacementGroup = null;
8805
9193
  config.hooks[targetEventKey] = currentGroups.map((group) => {
8806
- if (replacementGroup || !isRecord4(group) || !Array.isArray(group.hooks)) {
9194
+ if (replacementGroup || !isRecord5(group) || !Array.isArray(group.hooks)) {
8807
9195
  return group;
8808
9196
  }
8809
9197
  const hookIndex = group.hooks.findIndex(
@@ -8843,7 +9231,7 @@ var CodexManagementService = class {
8843
9231
  )
8844
9232
  };
8845
9233
  }).filter((group) => {
8846
- if (!isRecord4(group) || !Array.isArray(group.hooks)) {
9234
+ if (!isRecord5(group) || !Array.isArray(group.hooks)) {
8847
9235
  return true;
8848
9236
  }
8849
9237
  return group.hooks.length > 0;
@@ -8875,12 +9263,12 @@ var CodexManagementService = class {
8875
9263
  continue;
8876
9264
  }
8877
9265
  groups.forEach((group, groupIndex) => {
8878
- if (!isRecord4(group) || !Array.isArray(group.hooks)) {
9266
+ if (!isRecord5(group) || !Array.isArray(group.hooks)) {
8879
9267
  return;
8880
9268
  }
8881
9269
  const matcher = typeof group.matcher === "string" ? group.matcher : null;
8882
9270
  group.hooks.forEach((handler, handlerIndex) => {
8883
- if (!isRecord4(handler) || handler.type !== "command") {
9271
+ if (!isRecord5(handler) || handler.type !== "command") {
8884
9272
  return;
8885
9273
  }
8886
9274
  const command = typeof handler.command === "string" ? handler.command : null;
@@ -8924,7 +9312,7 @@ var CodexManagementService = class {
8924
9312
  import { EventEmitter as EventEmitter4 } from "events";
8925
9313
 
8926
9314
  // ../../packages/codex/src/requestMapper.ts
8927
- function isRecord5(value) {
9315
+ function isRecord6(value) {
8928
9316
  return typeof value === "object" && value !== null && !Array.isArray(value);
8929
9317
  }
8930
9318
  function stringOrNull2(value) {
@@ -9051,7 +9439,7 @@ function yoloApprovalResultForServerRequest(request) {
9051
9439
  case "item/permissions/requestApproval": {
9052
9440
  const params = request.params;
9053
9441
  return {
9054
- permissions: isRecord5(params.permissions) ? params.permissions : {},
9442
+ permissions: isRecord6(params.permissions) ? params.permissions : {},
9055
9443
  scope: "turn"
9056
9444
  };
9057
9445
  }
@@ -9080,7 +9468,7 @@ function responseKindForApprovalRequest(request) {
9080
9468
  }
9081
9469
  }
9082
9470
  function buildThreadRequestFromMcpElicitation(request) {
9083
- const meta = isRecord5(request.params._meta) ? request.params._meta : null;
9471
+ const meta = isRecord6(request.params._meta) ? request.params._meta : null;
9084
9472
  const toolTitle = stringOrNull2(meta?.tool_title);
9085
9473
  const toolDescription = stringOrNull2(meta?.tool_description);
9086
9474
  const serverName = stringOrNull2(request.params.serverName) ?? "MCP";
@@ -9129,7 +9517,7 @@ function buildCodexProviderRequestResponse(pending, input) {
9129
9517
  return { decision: allowed ? "accept" : "decline" };
9130
9518
  case "permissionsApproval":
9131
9519
  return allowed ? {
9132
- permissions: isRecord5(pending.responsePayload?.permissions) ? pending.responsePayload.permissions : {},
9520
+ permissions: isRecord6(pending.responsePayload?.permissions) ? pending.responsePayload.permissions : {},
9133
9521
  scope: "turn"
9134
9522
  } : {
9135
9523
  permissions: {},
@@ -9149,7 +9537,7 @@ function mapCodexProviderRequest(providerRequest, approvalMode) {
9149
9537
  const request = {
9150
9538
  id: Number(providerRequest.id),
9151
9539
  method: providerRequest.method,
9152
- params: isRecord5(providerRequest.params) ? providerRequest.params : {}
9540
+ params: isRecord6(providerRequest.params) ? providerRequest.params : {}
9153
9541
  };
9154
9542
  const providerRequestId = providerRequest.id;
9155
9543
  const approvalResponseKind = responseKindForApprovalRequest(request);
@@ -9178,7 +9566,7 @@ function mapCodexProviderRequest(providerRequest, approvalMode) {
9178
9566
  responseKind: approvalResponseKind,
9179
9567
  request: threadRequest2
9180
9568
  };
9181
- if (approvalResponseKind === "permissionsApproval" && isRecord5(params2.permissions)) {
9569
+ if (approvalResponseKind === "permissionsApproval" && isRecord6(params2.permissions)) {
9182
9570
  pendingRequest.responsePayload = { permissions: params2.permissions };
9183
9571
  }
9184
9572
  return {
@@ -9908,7 +10296,7 @@ function isAgentToolName(toolName) {
9908
10296
  function isSkillToolName(toolName) {
9909
10297
  return normalizedToolName(toolName) === "skill";
9910
10298
  }
9911
- function isRecord6(value) {
10299
+ function isRecord7(value) {
9912
10300
  return Boolean(value) && typeof value === "object" && !Array.isArray(value);
9913
10301
  }
9914
10302
  function stringValue(value) {
@@ -9933,20 +10321,52 @@ function readableToolName(toolName) {
9933
10321
  }
9934
10322
  return toolName;
9935
10323
  }
10324
+ function projectRelativePathLabel2(value) {
10325
+ const normalized = value?.trim();
10326
+ if (!normalized) {
10327
+ return null;
10328
+ }
10329
+ const slashNormalized = normalized.replace(/\\/g, "/");
10330
+ if (!slashNormalized.startsWith("/")) {
10331
+ return slashNormalized.replace(/^\.\//, "");
10332
+ }
10333
+ const markers = [
10334
+ "/apps/",
10335
+ "/packages/",
10336
+ "/src/",
10337
+ "/test/",
10338
+ "/tests/",
10339
+ "/docs/",
10340
+ "/config/",
10341
+ "/scripts/",
10342
+ "/e2e/",
10343
+ "/.agents/",
10344
+ "/.codex/"
10345
+ ];
10346
+ for (const marker of markers) {
10347
+ const markerIndex = slashNormalized.indexOf(marker);
10348
+ if (markerIndex >= 0) {
10349
+ return slashNormalized.slice(markerIndex + 1);
10350
+ }
10351
+ }
10352
+ return slashNormalized;
10353
+ }
9936
10354
  function commandFromInput(input) {
9937
- if (!isRecord6(input)) {
10355
+ if (!isRecord7(input)) {
9938
10356
  return null;
9939
10357
  }
9940
10358
  return stringValue(input.command) ?? stringValue(input.cmd) ?? stringValue(input.description);
9941
10359
  }
9942
10360
  function pathFromInput(input) {
9943
- if (!isRecord6(input)) {
10361
+ if (!isRecord7(input)) {
9944
10362
  return null;
9945
10363
  }
9946
- return stringValue(input.file_path) ?? stringValue(input.filePath) ?? stringValue(input.path) ?? stringValue(input.notebook_path);
10364
+ return projectRelativePathLabel2(
10365
+ stringValue(input.relative_path) ?? stringValue(input.relativePath) ?? stringValue(input.file_path) ?? stringValue(input.filePath) ?? stringValue(input.path) ?? stringValue(input.notebook_path)
10366
+ );
9947
10367
  }
9948
10368
  function summarizeToolInput(input) {
9949
- if (!isRecord6(input)) {
10369
+ if (!isRecord7(input)) {
9950
10370
  return compactJson(input);
9951
10371
  }
9952
10372
  const description = stringValue(input.description);
@@ -9980,7 +10400,7 @@ function summarizeToolInput(input) {
9980
10400
  return compactJson(input);
9981
10401
  }
9982
10402
  function messageContentText(message) {
9983
- if (!isRecord6(message)) {
10403
+ if (!isRecord7(message)) {
9984
10404
  return "";
9985
10405
  }
9986
10406
  const content = message.content;
@@ -9991,7 +10411,7 @@ function messageContentText(message) {
9991
10411
  return "";
9992
10412
  }
9993
10413
  return content.map((block) => {
9994
- if (!isRecord6(block)) {
10414
+ if (!isRecord7(block)) {
9995
10415
  return "";
9996
10416
  }
9997
10417
  if (typeof block.text === "string") {
@@ -10057,7 +10477,7 @@ function toolUseToHistoryItem(input) {
10057
10477
  };
10058
10478
  }
10059
10479
  if (isExitPlanModeToolName(input.name)) {
10060
- const plan = isRecord6(input.toolInput) ? stringValue(input.toolInput.plan) : null;
10480
+ const plan = isRecord7(input.toolInput) ? stringValue(input.toolInput.plan) : null;
10061
10481
  return {
10062
10482
  id: input.id,
10063
10483
  kind: "plan",
@@ -10113,7 +10533,7 @@ function toolUseToHistoryItem(input) {
10113
10533
  };
10114
10534
  }
10115
10535
  if (isSkillToolName(input.name)) {
10116
- const skill = isRecord6(input.toolInput) ? stringValue(input.toolInput.skill) : null;
10536
+ const skill = isRecord7(input.toolInput) ? stringValue(input.toolInput.skill) : null;
10117
10537
  const summaryText = skill ? `Skill: ${skill}` : summary ? `${displayName}: ${summary}` : displayName;
10118
10538
  return {
10119
10539
  id: input.id,
@@ -10134,14 +10554,14 @@ function toolUseToHistoryItem(input) {
10134
10554
  };
10135
10555
  }
10136
10556
  function contentBlocks(message) {
10137
- if (!isRecord6(message) || !Array.isArray(message.content)) {
10557
+ if (!isRecord7(message) || !Array.isArray(message.content)) {
10138
10558
  return [];
10139
10559
  }
10140
10560
  return message.content;
10141
10561
  }
10142
10562
  function toolResultBlocks(message) {
10143
10563
  return contentBlocks(message).map((block) => {
10144
- if (!isRecord6(block) || block.type !== "tool_result") {
10564
+ if (!isRecord7(block) || block.type !== "tool_result") {
10145
10565
  return null;
10146
10566
  }
10147
10567
  const toolUseId = stringValue(block.tool_use_id);
@@ -10157,7 +10577,7 @@ function toolResultBlocks(message) {
10157
10577
  function suppressedClaudeToolUseIds(message) {
10158
10578
  const ids = /* @__PURE__ */ new Set();
10159
10579
  for (const block of contentBlocks(message)) {
10160
- if (!isRecord6(block) || block.type !== "tool_use") {
10580
+ if (!isRecord7(block) || block.type !== "tool_use") {
10161
10581
  continue;
10162
10582
  }
10163
10583
  const id = stringValue(block.id);
@@ -10171,7 +10591,7 @@ function suppressedClaudeToolUseIds(message) {
10171
10591
  function assistantMessageToHistoryItems(input) {
10172
10592
  const items = [];
10173
10593
  for (const [index, block] of contentBlocks(input.message).entries()) {
10174
- if (!isRecord6(block)) {
10594
+ if (!isRecord7(block)) {
10175
10595
  continue;
10176
10596
  }
10177
10597
  const type = block.type;
@@ -10253,13 +10673,13 @@ function assistantMessageToHistoryItems(input) {
10253
10673
  return items;
10254
10674
  }
10255
10675
  function partialTextDelta(input) {
10256
- if (!isRecord6(input.event)) {
10676
+ if (!isRecord7(input.event)) {
10257
10677
  return null;
10258
10678
  }
10259
10679
  if (input.event.type === "content_block_start") {
10260
10680
  const index2 = typeof input.event.index === "number" ? input.event.index : 0;
10261
10681
  const block = input.event.content_block;
10262
- if (!isRecord6(block) || block.type !== "text" || typeof block.text !== "string" || !block.text) {
10682
+ if (!isRecord7(block) || block.type !== "text" || typeof block.text !== "string" || !block.text) {
10263
10683
  return null;
10264
10684
  }
10265
10685
  return {
@@ -10272,7 +10692,7 @@ function partialTextDelta(input) {
10272
10692
  }
10273
10693
  const index = typeof input.event.index === "number" ? input.event.index : 0;
10274
10694
  const delta = input.event.delta;
10275
- if (!isRecord6(delta) || delta.type !== "text_delta" || typeof delta.text !== "string" || !delta.text) {
10695
+ if (!isRecord7(delta) || delta.type !== "text_delta" || typeof delta.text !== "string" || !delta.text) {
10276
10696
  return null;
10277
10697
  }
10278
10698
  return {
@@ -10281,14 +10701,14 @@ function partialTextDelta(input) {
10281
10701
  };
10282
10702
  }
10283
10703
  function partialReasoningDelta(input) {
10284
- if (!isRecord6(input.event)) {
10704
+ if (!isRecord7(input.event)) {
10285
10705
  return null;
10286
10706
  }
10287
10707
  const index = typeof input.event.index === "number" ? input.event.index : 0;
10288
10708
  const itemId = `${input.messageId}:content:${index}`;
10289
10709
  if (input.event.type === "content_block_start") {
10290
10710
  const block = input.event.content_block;
10291
- if (!isRecord6(block) || block.type !== "thinking") {
10711
+ if (!isRecord7(block) || block.type !== "thinking") {
10292
10712
  return null;
10293
10713
  }
10294
10714
  const text3 = thinkingTextFromBlock(block) ?? "";
@@ -10303,7 +10723,7 @@ function partialReasoningDelta(input) {
10303
10723
  return null;
10304
10724
  }
10305
10725
  const delta = input.event.delta;
10306
- if (!isRecord6(delta) || delta.type !== "thinking_delta") {
10726
+ if (!isRecord7(delta) || delta.type !== "thinking_delta") {
10307
10727
  return null;
10308
10728
  }
10309
10729
  const text2 = stringValue(delta.thinking) ?? stringValue(delta.text);
@@ -10318,12 +10738,12 @@ function partialReasoningDelta(input) {
10318
10738
  };
10319
10739
  }
10320
10740
  function toolUseFromPartialStart(input) {
10321
- if (!isRecord6(input.event) || input.event.type !== "content_block_start") {
10741
+ if (!isRecord7(input.event) || input.event.type !== "content_block_start") {
10322
10742
  return null;
10323
10743
  }
10324
10744
  const index = typeof input.event.index === "number" ? input.event.index : 0;
10325
10745
  const block = input.event.content_block;
10326
- if (!isRecord6(block)) {
10746
+ if (!isRecord7(block)) {
10327
10747
  return null;
10328
10748
  }
10329
10749
  if (block.type === "tool_use") {
@@ -10360,7 +10780,7 @@ function toolUseFromPartialStart(input) {
10360
10780
  function resultForToolUse(input) {
10361
10781
  if (input.previous) {
10362
10782
  if (input.previous.kind === "plan") {
10363
- const plan = isRecord6(input.result) ? stringValue(input.result.plan) : null;
10783
+ const plan = isRecord7(input.result) ? stringValue(input.result.plan) : null;
10364
10784
  const nextItem = {
10365
10785
  ...input.previous,
10366
10786
  text: plan ?? input.previous.text,
@@ -10630,7 +11050,7 @@ var DEFAULT_CLAUDE_MODELS = [
10630
11050
  defaultReasoningEffort: null
10631
11051
  }
10632
11052
  ];
10633
- function isRecord7(value) {
11053
+ function isRecord8(value) {
10634
11054
  return Boolean(value) && typeof value === "object" && !Array.isArray(value);
10635
11055
  }
10636
11056
  function toIsoFromMs(value) {
@@ -10775,7 +11195,7 @@ function assistantMessagePayload(message) {
10775
11195
  return message.type === "assistant" ? message.message : null;
10776
11196
  }
10777
11197
  function messageIdFromPayload(message) {
10778
- return isRecord7(message) && typeof message.id === "string" && message.id ? message.id : null;
11198
+ return isRecord8(message) && typeof message.id === "string" && message.id ? message.id : null;
10779
11199
  }
10780
11200
  function messageUuid(message, fallback) {
10781
11201
  return typeof message.uuid === "string" && message.uuid ? message.uuid : fallback;
@@ -10796,7 +11216,7 @@ function addClaudeUsage(left, right) {
10796
11216
  };
10797
11217
  }
10798
11218
  function normalizeClaudeUsage(value) {
10799
- if (!isRecord7(value)) {
11219
+ if (!isRecord8(value)) {
10800
11220
  return null;
10801
11221
  }
10802
11222
  const baseInputTokens = finiteNumber(value.input_tokens ?? value.inputTokens);
@@ -10821,7 +11241,7 @@ function normalizeClaudeUsage(value) {
10821
11241
  };
10822
11242
  }
10823
11243
  function usageFromAssistantMessage(message) {
10824
- if (message.type !== "assistant" || !isRecord7(message.message)) {
11244
+ if (message.type !== "assistant" || !isRecord8(message.message)) {
10825
11245
  return null;
10826
11246
  }
10827
11247
  return normalizeClaudeUsage(message.message.usage);
@@ -10830,11 +11250,11 @@ function usageFromResultMessage(message) {
10830
11250
  return message.type === "result" ? normalizeClaudeUsage(message.usage) : null;
10831
11251
  }
10832
11252
  function contextWindowFromResultMessage(message) {
10833
- if (message.type !== "result" || !isRecord7(message.modelUsage)) {
11253
+ if (message.type !== "result" || !isRecord8(message.modelUsage)) {
10834
11254
  return null;
10835
11255
  }
10836
11256
  for (const usage of Object.values(message.modelUsage)) {
10837
- if (!isRecord7(usage)) {
11257
+ if (!isRecord8(usage)) {
10838
11258
  continue;
10839
11259
  }
10840
11260
  const contextWindow = nullableFiniteNumber(usage.contextWindow);
@@ -10853,7 +11273,7 @@ function claudeUsagePayload(usage, modelContextWindow) {
10853
11273
  };
10854
11274
  }
10855
11275
  function streamMessageId(event) {
10856
- if (!isRecord7(event) || event.type !== "message_start") {
11276
+ if (!isRecord8(event) || event.type !== "message_start") {
10857
11277
  return null;
10858
11278
  }
10859
11279
  const message = event.message;
@@ -10877,7 +11297,7 @@ function normalizeClaudeQuestionOptions(value) {
10877
11297
  return null;
10878
11298
  }
10879
11299
  const options = value.map((entry) => {
10880
- if (!isRecord7(entry)) {
11300
+ if (!isRecord8(entry)) {
10881
11301
  return null;
10882
11302
  }
10883
11303
  const label = stringFromRecord(entry, "label");
@@ -10894,11 +11314,11 @@ function normalizeClaudeQuestionOptions(value) {
10894
11314
  return options.length > 0 ? options : null;
10895
11315
  }
10896
11316
  function normalizeClaudeAskUserQuestions(input) {
10897
- if (!isRecord7(input) || !Array.isArray(input.questions)) {
11317
+ if (!isRecord8(input) || !Array.isArray(input.questions)) {
10898
11318
  return [];
10899
11319
  }
10900
11320
  return input.questions.map((entry, index) => {
10901
- if (!isRecord7(entry)) {
11321
+ if (!isRecord8(entry)) {
10902
11322
  return null;
10903
11323
  }
10904
11324
  const question = stringFromRecord(entry, "question");
@@ -10918,11 +11338,11 @@ function normalizeClaudeAskUserQuestions(input) {
10918
11338
  }).filter((entry) => Boolean(entry));
10919
11339
  }
10920
11340
  function claudeAskUserToolUseFromAssistantMessage(message) {
10921
- if (!isRecord7(message) || !Array.isArray(message.content)) {
11341
+ if (!isRecord8(message) || !Array.isArray(message.content)) {
10922
11342
  return null;
10923
11343
  }
10924
11344
  for (const [index, block] of message.content.entries()) {
10925
- if (!isRecord7(block) || block.type !== "tool_use") {
11345
+ if (!isRecord8(block) || block.type !== "tool_use") {
10926
11346
  continue;
10927
11347
  }
10928
11348
  const name = stringFromRecord(block, "name");
@@ -10941,7 +11361,7 @@ function mapClaudeAskUserQuestionRequest(request) {
10941
11361
  if (request.method !== "tool/AskUserQuestion") {
10942
11362
  return null;
10943
11363
  }
10944
- if (!isRecord7(request.params)) {
11364
+ if (!isRecord8(request.params)) {
10945
11365
  return null;
10946
11366
  }
10947
11367
  const providerSessionId = stringFromRecord(request.params, "providerSessionId");
@@ -11817,13 +12237,13 @@ var ClaudeRuntimeAdapter = class extends EventEmitter5 {
11817
12237
  };
11818
12238
  }
11819
12239
  async userMessageToHistoryItem(id, message, context) {
11820
- if (!isRecord7(message) || !Array.isArray(message.content)) {
12240
+ if (!isRecord8(message) || !Array.isArray(message.content)) {
11821
12241
  return userMessageToHistoryItem(id, message);
11822
12242
  }
11823
12243
  const parts = [];
11824
12244
  for (let index = 0; index < message.content.length; index += 1) {
11825
12245
  const block = message.content[index];
11826
- if (!isRecord7(block)) {
12246
+ if (!isRecord8(block)) {
11827
12247
  continue;
11828
12248
  }
11829
12249
  if (block.type === "image") {
@@ -11852,7 +12272,7 @@ var ClaudeRuntimeAdapter = class extends EventEmitter5 {
11852
12272
  if (!input.localThreadId || !input.workspacePath) {
11853
12273
  return null;
11854
12274
  }
11855
- const source = isRecord7(input.block.source) ? input.block.source : null;
12275
+ const source = isRecord8(input.block.source) ? input.block.source : null;
11856
12276
  if (!source || source.type !== "base64") {
11857
12277
  return null;
11858
12278
  }
@@ -11978,7 +12398,7 @@ var ClaudeRuntimeAdapter = class extends EventEmitter5 {
11978
12398
  const previous = current?.itemsById.get(message.parent_tool_use_id) ?? null;
11979
12399
  upsertCurrentItem(resultForToolUse({
11980
12400
  toolUseId: message.parent_tool_use_id,
11981
- result: isRecord7(message.message) && "content" in message.message ? message.message.content : message.message,
12401
+ result: isRecord8(message.message) && "content" in message.message ? message.message.content : message.message,
11982
12402
  previous
11983
12403
  }));
11984
12404
  }
@@ -12053,7 +12473,7 @@ async function npmGlobalRoot() {
12053
12473
 
12054
12474
  // ../../packages/opencode/src/historyItems.ts
12055
12475
  import path10 from "path";
12056
- function isRecord8(value) {
12476
+ function isRecord9(value) {
12057
12477
  return Boolean(value) && typeof value === "object" && !Array.isArray(value);
12058
12478
  }
12059
12479
  function stringValue2(value) {
@@ -12084,7 +12504,7 @@ function toolContentText(content) {
12084
12504
  return "";
12085
12505
  }
12086
12506
  return content.map((entry) => {
12087
- if (!isRecord8(entry)) {
12507
+ if (!isRecord9(entry)) {
12088
12508
  return "";
12089
12509
  }
12090
12510
  if (typeof entry.text === "string") {
@@ -12102,7 +12522,7 @@ function toolStateDetail(state) {
12102
12522
  if (output) {
12103
12523
  parts.push("", "Output:", output);
12104
12524
  }
12105
- const error = isRecord8(state.error) ? stringValue2(state.error.message) ?? compactJson2(state.error) : stringValue2(state.error);
12525
+ const error = isRecord9(state.error) ? stringValue2(state.error.message) ?? compactJson2(state.error) : stringValue2(state.error);
12106
12526
  if (error) {
12107
12527
  parts.push("", "Error:", error);
12108
12528
  }
@@ -12119,13 +12539,13 @@ function toolStateStatus(state) {
12119
12539
  return "completed";
12120
12540
  }
12121
12541
  function toolSummary(input) {
12122
- if (!isRecord8(input)) {
12542
+ if (!isRecord9(input)) {
12123
12543
  return stringValue2(input) ?? compactJson2(input);
12124
12544
  }
12125
12545
  return stringValue2(input.description) ?? stringValue2(input.command) ?? stringValue2(input.cmd) ?? stringValue2(input.filePath) ?? stringValue2(input.file_path) ?? stringValue2(input.path) ?? stringValue2(input.file) ?? stringValue2(input.relativePath) ?? stringValue2(input.relative_path) ?? stringValue2(input.pattern) ?? stringValue2(input.query) ?? stringValue2(input.url) ?? compactJson2(input);
12126
12546
  }
12127
12547
  function commandSummary(input, state) {
12128
- const command = isRecord8(input) ? stringValue2(input.command) ?? stringValue2(input.cmd) : null;
12548
+ const command = isRecord9(input) ? stringValue2(input.command) ?? stringValue2(input.cmd) : null;
12129
12549
  if (command) {
12130
12550
  return command;
12131
12551
  }
@@ -12145,7 +12565,7 @@ function firstStringValue(record, keys) {
12145
12565
  return null;
12146
12566
  }
12147
12567
  function filePathFromInput(input) {
12148
- if (!isRecord8(input)) {
12568
+ if (!isRecord9(input)) {
12149
12569
  return null;
12150
12570
  }
12151
12571
  return firstStringValue(input, [
@@ -12183,7 +12603,7 @@ function toolIsLowInformationPatch(normalized, state, input, patchText, path22,
12183
12603
  if (path22 || patchText || metadataStats || stringValue2(state.output)) {
12184
12604
  return false;
12185
12605
  }
12186
- return !isRecord8(input) || Object.keys(input).length === 0;
12606
+ return !isRecord9(input) || Object.keys(input).length === 0;
12187
12607
  }
12188
12608
  function countUnifiedDiffStats2(diffText) {
12189
12609
  if (!diffText) {
@@ -12216,10 +12636,10 @@ function extractPathFromPatchText(patchText) {
12216
12636
  return null;
12217
12637
  }
12218
12638
  function fileChangeStatsFromMetadata(metadata) {
12219
- if (!isRecord8(metadata) || !Array.isArray(metadata.files)) {
12639
+ if (!isRecord9(metadata) || !Array.isArray(metadata.files)) {
12220
12640
  return null;
12221
12641
  }
12222
- const files = metadata.files.filter(isRecord8);
12642
+ const files = metadata.files.filter(isRecord9);
12223
12643
  if (files.length === 0) {
12224
12644
  return null;
12225
12645
  }
@@ -12238,7 +12658,7 @@ function todoRecordsFromValue(value) {
12238
12658
  if (Array.isArray(value)) {
12239
12659
  return value;
12240
12660
  }
12241
- if (isRecord8(value) && Array.isArray(value.todos)) {
12661
+ if (isRecord9(value) && Array.isArray(value.todos)) {
12242
12662
  return value.todos;
12243
12663
  }
12244
12664
  if (typeof value === "string" && value.trim()) {
@@ -12257,13 +12677,13 @@ function planUpdateFromTodoTool(tool) {
12257
12677
  if (normalized !== "todowrite" && normalized !== "todo" && normalized !== "todos") {
12258
12678
  return null;
12259
12679
  }
12260
- const state = isRecord8(tool.state) ? tool.state : {};
12261
- const todos = todoRecordsFromValue(state.input) ?? (isRecord8(state.metadata) ? todoRecordsFromValue(state.metadata) : null) ?? todoRecordsFromValue(state.output);
12680
+ const state = isRecord9(tool.state) ? tool.state : {};
12681
+ const todos = todoRecordsFromValue(state.input) ?? (isRecord9(state.metadata) ? todoRecordsFromValue(state.metadata) : null) ?? todoRecordsFromValue(state.output);
12262
12682
  if (!todos || todos.length === 0) {
12263
12683
  return null;
12264
12684
  }
12265
12685
  const plan = todos.map((todo) => {
12266
- if (!isRecord8(todo)) {
12686
+ if (!isRecord9(todo)) {
12267
12687
  return null;
12268
12688
  }
12269
12689
  const step = stringValue2(todo.content) ?? stringValue2(todo.step) ?? stringValue2(todo.title);
@@ -12281,7 +12701,7 @@ function planUpdateFromTodoTool(tool) {
12281
12701
  } : null;
12282
12702
  }
12283
12703
  function normalizeLegacyPart(part) {
12284
- if (!isRecord8(part)) {
12704
+ if (!isRecord9(part)) {
12285
12705
  return null;
12286
12706
  }
12287
12707
  const type = stringValue2(part.type);
@@ -12301,15 +12721,15 @@ function normalizeLegacyParts(parts, fallbackText) {
12301
12721
  return [{ type: "text", text: fallbackText }];
12302
12722
  }
12303
12723
  function normalizeLegacyMessage(message) {
12304
- if (!isRecord8(message) || !isRecord8(message.info) || !Array.isArray(message.parts)) {
12724
+ if (!isRecord9(message) || !isRecord9(message.info) || !Array.isArray(message.parts)) {
12305
12725
  return message;
12306
12726
  }
12307
12727
  const info = message.info;
12308
12728
  const role = stringValue2(info.role) ?? stringValue2(info.type);
12309
12729
  const id = stringValue2(info.id) ?? crypto.randomUUID();
12310
- const time = isRecord8(info.time) ? info.time : void 0;
12730
+ const time = isRecord9(info.time) ? info.time : void 0;
12311
12731
  if (role === "user") {
12312
- const text2 = message.parts.map((part) => isRecord8(part) && stringValue2(part.text) ? part.text : null).filter((text3) => Boolean(text3)).join("\n");
12732
+ const text2 = message.parts.map((part) => isRecord9(part) && stringValue2(part.text) ? part.text : null).filter((text3) => Boolean(text3)).join("\n");
12313
12733
  return {
12314
12734
  id,
12315
12735
  type: "user",
@@ -12320,7 +12740,7 @@ function normalizeLegacyMessage(message) {
12320
12740
  if (role !== "assistant") {
12321
12741
  return message;
12322
12742
  }
12323
- const model = isRecord8(info.model) ? {
12743
+ const model = isRecord9(info.model) ? {
12324
12744
  id: stringValue2(info.model.id) ?? stringValue2(info.model.modelID) ?? stringValue2(info.modelID) ?? "unknown",
12325
12745
  providerID: stringValue2(info.model.providerID) ?? stringValue2(info.providerID) ?? "unknown",
12326
12746
  variant: stringValue2(info.model.variant) ?? stringValue2(info.variant) ?? "default"
@@ -12338,14 +12758,14 @@ function normalizeLegacyMessage(message) {
12338
12758
  content: normalizeLegacyParts(message.parts, stringValue2(info.text)),
12339
12759
  error: info.error,
12340
12760
  cost: numberValue(info.cost) ?? void 0,
12341
- tokens: isRecord8(info.tokens) ? info.tokens : void 0,
12761
+ tokens: isRecord9(info.tokens) ? info.tokens : void 0,
12342
12762
  finish: stringValue2(info.finish) ?? void 0
12343
12763
  };
12344
12764
  }
12345
12765
  function mapAssistantTool(messageId2, tool, options) {
12346
12766
  const id = stringValue2(tool.id) ?? `${messageId2}:tool`;
12347
12767
  const name = stringValue2(tool.name) ?? stringValue2(tool.tool) ?? "Tool";
12348
- const state = isRecord8(tool.state) ? tool.state : {};
12768
+ const state = isRecord9(tool.state) ? tool.state : {};
12349
12769
  const input = state.input;
12350
12770
  const summary = toolSummary(input);
12351
12771
  const detailText = [`Tool: ${name}`, "", "Input:", compactJson2(input), "", toolStateDetail(state)].filter(Boolean).join("\n");
@@ -12370,7 +12790,7 @@ function mapAssistantTool(messageId2, tool, options) {
12370
12790
  "patch"
12371
12791
  ].includes(normalized)) {
12372
12792
  const metadataStats = fileChangeStatsFromMetadata(state.metadata);
12373
- const patchText = isRecord8(input) ? stringValue2(input.patchText) ?? stringValue2(input.patch) ?? stringValue2(input.diff) : null;
12793
+ const patchText = isRecord9(input) ? stringValue2(input.patchText) ?? stringValue2(input.patch) ?? stringValue2(input.diff) : null;
12374
12794
  const path22 = metadataStats?.path ?? filePathFromInput(input) ?? extractPathFromPatchText(patchText);
12375
12795
  if (toolIsLowInformationPatch(normalized, state, input, patchText, path22, metadataStats)) {
12376
12796
  return null;
@@ -12426,7 +12846,7 @@ function mapAssistantTool(messageId2, tool, options) {
12426
12846
  };
12427
12847
  }
12428
12848
  if (normalized === "skill") {
12429
- const skill = isRecord8(input) ? stringValue2(input.skill) ?? stringValue2(input.name) : null;
12849
+ const skill = isRecord9(input) ? stringValue2(input.skill) ?? stringValue2(input.name) : null;
12430
12850
  return {
12431
12851
  id,
12432
12852
  kind: "skillToolCall",
@@ -12447,12 +12867,12 @@ function mapAssistantTool(messageId2, tool, options) {
12447
12867
  }
12448
12868
  function openCodeMessageToPlanUpdate(message) {
12449
12869
  const normalizedMessage = normalizeLegacyMessage(message);
12450
- if (!isRecord8(normalizedMessage)) {
12870
+ if (!isRecord9(normalizedMessage)) {
12451
12871
  return null;
12452
12872
  }
12453
12873
  const content = Array.isArray(normalizedMessage.content) ? normalizedMessage.content : [];
12454
12874
  for (const part of content) {
12455
- if (!isRecord8(part) || stringValue2(part.type) !== "tool") {
12875
+ if (!isRecord9(part) || stringValue2(part.type) !== "tool") {
12456
12876
  continue;
12457
12877
  }
12458
12878
  const planUpdate = planUpdateFromTodoTool(part);
@@ -12494,9 +12914,9 @@ function mapAssistantPart(messageId2, part, index, options) {
12494
12914
  return mapAssistantTool(messageId2, part, options);
12495
12915
  }
12496
12916
  if (partType === "file") {
12497
- const sourcePath = isRecord8(part.source) ? stringValue2(part.source.path) : null;
12917
+ const sourcePath = isRecord9(part.source) ? stringValue2(part.source.path) : null;
12498
12918
  const filename = displayPath(sourcePath, options) ?? stringValue2(part.filename) ?? stringValue2(part.url) ?? "Attached file";
12499
- const sourceText = isRecord8(part.source) && isRecord8(part.source.text) ? stringValue2(part.source.text.value) : null;
12919
+ const sourceText = isRecord9(part.source) && isRecord9(part.source.text) ? stringValue2(part.source.text.value) : null;
12500
12920
  return {
12501
12921
  id: partId,
12502
12922
  kind: "fileRead",
@@ -12538,7 +12958,7 @@ function mapAssistantPart(messageId2, part, index, options) {
12538
12958
  }
12539
12959
  if (partType === "agent") {
12540
12960
  const name = stringValue2(part.name) ?? "Agent";
12541
- const source = isRecord8(part.source) ? stringValue2(part.source.value) : null;
12961
+ const source = isRecord9(part.source) ? stringValue2(part.source.value) : null;
12542
12962
  return {
12543
12963
  id: partId,
12544
12964
  kind: "agentToolCall",
@@ -12561,7 +12981,7 @@ function mapAssistantPart(messageId2, part, index, options) {
12561
12981
  };
12562
12982
  }
12563
12983
  if (partType === "retry") {
12564
- const error = isRecord8(part.error) ? stringValue2(part.error.message) ?? compactJson2(part.error) : stringValue2(part.error);
12984
+ const error = isRecord9(part.error) ? stringValue2(part.error.message) ?? compactJson2(part.error) : stringValue2(part.error);
12565
12985
  return {
12566
12986
  id: partId,
12567
12987
  kind: "other",
@@ -12588,7 +13008,7 @@ function mapAssistantPart(messageId2, part, index, options) {
12588
13008
  }
12589
13009
  function openCodeMessageToHistoryItems(message, options = {}) {
12590
13010
  message = normalizeLegacyMessage(message);
12591
- if (!isRecord8(message)) {
13011
+ if (!isRecord9(message)) {
12592
13012
  return [];
12593
13013
  }
12594
13014
  const id = stringValue2(message.id) ?? crypto.randomUUID();
@@ -12616,7 +13036,7 @@ function openCodeMessageToHistoryItems(message, options = {}) {
12616
13036
  text: command,
12617
13037
  previewText: command,
12618
13038
  detailText: stringValue2(message.output) ?? null,
12619
- status: isRecord8(message.time) && typeof message.time.completed === "number" ? "completed" : "running"
13039
+ status: isRecord9(message.time) && typeof message.time.completed === "number" ? "completed" : "running"
12620
13040
  }];
12621
13041
  }
12622
13042
  if (type === "agent-switched") {
@@ -12628,7 +13048,7 @@ function openCodeMessageToHistoryItems(message, options = {}) {
12628
13048
  }];
12629
13049
  }
12630
13050
  if (type === "model-switched") {
12631
- const model = isRecord8(message.model) ? [stringValue2(message.model.providerID), stringValue2(message.model.id)].filter(Boolean).join("/") : "unknown";
13051
+ const model = isRecord9(message.model) ? [stringValue2(message.model.providerID), stringValue2(message.model.id)].filter(Boolean).join("/") : "unknown";
12632
13052
  return [{
12633
13053
  id,
12634
13054
  kind: "other",
@@ -12655,7 +13075,7 @@ function openCodeMessageToHistoryItems(message, options = {}) {
12655
13075
  const items = [];
12656
13076
  const content = Array.isArray(message.content) ? message.content : [];
12657
13077
  for (const [index, part] of content.entries()) {
12658
- if (!isRecord8(part)) {
13078
+ if (!isRecord9(part)) {
12659
13079
  continue;
12660
13080
  }
12661
13081
  const item = mapAssistantPart(id, part, index, options);
@@ -12663,7 +13083,7 @@ function openCodeMessageToHistoryItems(message, options = {}) {
12663
13083
  items.push(item);
12664
13084
  }
12665
13085
  }
12666
- const error = isRecord8(message.error) ? stringValue2(message.error.message) ?? compactJson2(message.error) : null;
13086
+ const error = isRecord9(message.error) ? stringValue2(message.error.message) ?? compactJson2(message.error) : null;
12667
13087
  if (error) {
12668
13088
  items.push({
12669
13089
  id: `${id}:error`,
@@ -12693,12 +13113,12 @@ function openCodeMessagesToTurns(messages, options = {}) {
12693
13113
  };
12694
13114
  for (const message of messages) {
12695
13115
  const normalizedMessage = normalizeLegacyMessage(message);
12696
- if (!isRecord8(normalizedMessage)) {
13116
+ if (!isRecord9(normalizedMessage)) {
12697
13117
  continue;
12698
13118
  }
12699
13119
  const id = stringValue2(normalizedMessage.id) ?? crypto.randomUUID();
12700
13120
  const type = stringValue2(normalizedMessage.type);
12701
- const createdAt = isRecord8(normalizedMessage.time) ? isoFromMs(normalizedMessage.time.created) : null;
13121
+ const createdAt = isRecord9(normalizedMessage.time) ? isoFromMs(normalizedMessage.time.created) : null;
12702
13122
  if (type === "user") {
12703
13123
  flush();
12704
13124
  current = {
@@ -12724,7 +13144,7 @@ function openCodeMessagesToTurns(messages, options = {}) {
12724
13144
  if (items.some((item) => item.kind !== "userMessage" && item.kind !== "other")) {
12725
13145
  current.hasAssistantResult = true;
12726
13146
  }
12727
- if (isRecord8(normalizedMessage.error)) {
13147
+ if (isRecord9(normalizedMessage.error)) {
12728
13148
  current.error = stringValue2(normalizedMessage.error.message) ?? compactJson2(normalizedMessage.error);
12729
13149
  }
12730
13150
  }
@@ -12787,7 +13207,7 @@ var opencodeCapabilities = {
12787
13207
  costUsd: true
12788
13208
  }
12789
13209
  };
12790
- function isRecord9(value) {
13210
+ function isRecord10(value) {
12791
13211
  return Boolean(value) && typeof value === "object" && !Array.isArray(value);
12792
13212
  }
12793
13213
  function stringValue3(value) {
@@ -12805,12 +13225,12 @@ function isoFromMs2(value) {
12805
13225
  return time === null ? null : new Date(time).toISOString();
12806
13226
  }
12807
13227
  function unwrapResult(result) {
12808
- if (isRecord9(result) && "error" in result && result.error !== void 0) {
13228
+ if (isRecord10(result) && "error" in result && result.error !== void 0) {
12809
13229
  const error = result.error;
12810
- const message = isRecord9(error) && typeof error.message === "string" ? error.message : String(error);
13230
+ const message = isRecord10(error) && typeof error.message === "string" ? error.message : String(error);
12811
13231
  throw new Error(message);
12812
13232
  }
12813
- if (isRecord9(result) && "data" in result) {
13233
+ if (isRecord10(result) && "data" in result) {
12814
13234
  return result.data;
12815
13235
  }
12816
13236
  return result;
@@ -12837,7 +13257,7 @@ function parseModelKey(value) {
12837
13257
  };
12838
13258
  }
12839
13259
  function mapModel3(record, index) {
12840
- if (!isRecord9(record)) {
13260
+ if (!isRecord10(record)) {
12841
13261
  return null;
12842
13262
  }
12843
13263
  const providerID = stringValue3(record.providerID);
@@ -12847,8 +13267,8 @@ function mapModel3(record, index) {
12847
13267
  }
12848
13268
  const name = stringValue3(record.name) ?? id;
12849
13269
  const variants = Array.isArray(record.variants) ? record.variants : [];
12850
- const defaultVariant = isRecord9(record.options) ? stringValue3(record.options.variant) : null;
12851
- const variantIds = variants.map((variant) => isRecord9(variant) ? stringValue3(variant.id) : null).filter((variant) => Boolean(variant));
13270
+ const defaultVariant = isRecord10(record.options) ? stringValue3(record.options.variant) : null;
13271
+ const variantIds = variants.map((variant) => isRecord10(variant) ? stringValue3(variant.id) : null).filter((variant) => Boolean(variant));
12852
13272
  const firstVariant = defaultVariant ?? variantIds[0] ?? null;
12853
13273
  const model = modelKey(providerID, id, firstVariant);
12854
13274
  const enabled = record.enabled !== false;
@@ -12864,24 +13284,24 @@ function mapModel3(record, index) {
12864
13284
  };
12865
13285
  }
12866
13286
  function configuredProviderModelRecords(config) {
12867
- const data = isRecord9(config) && isRecord9(config.data) ? config.data : config;
12868
- const providerConfig = isRecord9(data) && isRecord9(data.provider) ? data.provider : isRecord9(data) && isRecord9(data.providers) ? data.providers : null;
13287
+ const data = isRecord10(config) && isRecord10(config.data) ? config.data : config;
13288
+ const providerConfig = isRecord10(data) && isRecord10(data.provider) ? data.provider : isRecord10(data) && isRecord10(data.providers) ? data.providers : null;
12869
13289
  const configured = /* @__PURE__ */ new Map();
12870
13290
  if (!providerConfig) {
12871
13291
  return configured;
12872
13292
  }
12873
13293
  Object.entries(providerConfig).forEach(([providerID, provider]) => {
12874
- if (!isRecord9(provider) || !isRecord9(provider.models)) {
13294
+ if (!isRecord10(provider) || !isRecord10(provider.models)) {
12875
13295
  return;
12876
13296
  }
12877
13297
  Object.entries(provider.models).forEach(([modelID, model]) => {
12878
- configured.set(providerModelKey(providerID, modelID), isRecord9(model) ? model : {});
13298
+ configured.set(providerModelKey(providerID, modelID), isRecord10(model) ? model : {});
12879
13299
  });
12880
13300
  });
12881
13301
  return configured;
12882
13302
  }
12883
13303
  function mapProviderModel(provider, record, index, configuredRecord) {
12884
- if (!isRecord9(provider) || !isRecord9(record)) {
13304
+ if (!isRecord10(provider) || !isRecord10(record)) {
12885
13305
  return null;
12886
13306
  }
12887
13307
  const providerID = stringValue3(record.providerID) ?? stringValue3(provider.id);
@@ -12890,7 +13310,7 @@ function mapProviderModel(provider, record, index, configuredRecord) {
12890
13310
  return null;
12891
13311
  }
12892
13312
  const name = stringValue3(configuredRecord?.name) ?? stringValue3(record.name) ?? id;
12893
- const variants = configuredRecord ? isRecord9(configuredRecord.variants) ? Object.keys(configuredRecord.variants) : [] : isRecord9(record.variants) ? Object.keys(record.variants) : [];
13313
+ const variants = configuredRecord ? isRecord10(configuredRecord.variants) ? Object.keys(configuredRecord.variants) : [] : isRecord10(record.variants) ? Object.keys(record.variants) : [];
12894
13314
  const model = providerModelKey(providerID, id);
12895
13315
  const providerName = stringValue3(provider.name) ?? providerID;
12896
13316
  const disabled = configuredRecord?.status === "disabled" || record.status === "disabled" || provider.disabled === true;
@@ -12910,15 +13330,15 @@ function mapProviderModel(provider, record, index, configuredRecord) {
12910
13330
  };
12911
13331
  }
12912
13332
  function providerModels(result, configuredModels) {
12913
- const data = isRecord9(result) && Array.isArray(result.providers) ? result : isRecord9(result) && isRecord9(result.data) && Array.isArray(result.data.providers) ? result.data : null;
13333
+ const data = isRecord10(result) && Array.isArray(result.providers) ? result : isRecord10(result) && isRecord10(result.data) && Array.isArray(result.data.providers) ? result.data : null;
12914
13334
  const providers = Array.isArray(data?.providers) ? data.providers : [];
12915
13335
  const models = [];
12916
13336
  providers.forEach((provider) => {
12917
- if (!isRecord9(provider) || !isRecord9(provider.models)) {
13337
+ if (!isRecord10(provider) || !isRecord10(provider.models)) {
12918
13338
  return;
12919
13339
  }
12920
13340
  Object.values(provider.models).forEach((model) => {
12921
- if (!isRecord9(model)) {
13341
+ if (!isRecord10(model)) {
12922
13342
  return;
12923
13343
  }
12924
13344
  const providerID = stringValue3(model.providerID) ?? stringValue3(provider.id);
@@ -12973,7 +13393,7 @@ function modelContextWindowFromTokens(tokens) {
12973
13393
  );
12974
13394
  }
12975
13395
  function openCodeUsageFromTokens(tokens) {
12976
- if (!isRecord9(tokens)) {
13396
+ if (!isRecord10(tokens)) {
12977
13397
  return null;
12978
13398
  }
12979
13399
  const inputTokens = nonNegativeNumberValue(tokens.input ?? tokens.inputTokens ?? tokens.input_tokens) ?? 0;
@@ -12981,7 +13401,7 @@ function openCodeUsageFromTokens(tokens) {
12981
13401
  const reasoningOutputTokens = nonNegativeNumberValue(
12982
13402
  tokens.reasoning ?? tokens.reasoningOutputTokens ?? tokens.reasoning_output_tokens
12983
13403
  ) ?? 0;
12984
- const cache = isRecord9(tokens.cache) ? tokens.cache : null;
13404
+ const cache = isRecord10(tokens.cache) ? tokens.cache : null;
12985
13405
  const cachedInputTokens = nonNegativeNumberValue(
12986
13406
  tokens.cachedInputTokens ?? tokens.cached_input_tokens ?? cache?.read
12987
13407
  ) ?? 0;
@@ -13001,10 +13421,10 @@ function openCodeUsageFromTokens(tokens) {
13001
13421
  };
13002
13422
  }
13003
13423
  function messageTokenUsage(message) {
13004
- if (!isRecord9(message)) {
13424
+ if (!isRecord10(message)) {
13005
13425
  return null;
13006
13426
  }
13007
- const legacyMessage = isRecord9(message.info) && Array.isArray(message.parts) ? {
13427
+ const legacyMessage = isRecord10(message.info) && Array.isArray(message.parts) ? {
13008
13428
  type: stringValue3(message.info.role) ?? stringValue3(message.info.type),
13009
13429
  content: message.parts,
13010
13430
  tokens: message.info.tokens
@@ -13018,7 +13438,7 @@ function messageTokenUsage(message) {
13018
13438
  }
13019
13439
  const parts = Array.isArray(message.parts) ? message.parts : Array.isArray(message.content) ? message.content : [];
13020
13440
  for (const part of parts) {
13021
- if (!isRecord9(part)) {
13441
+ if (!isRecord10(part)) {
13022
13442
  continue;
13023
13443
  }
13024
13444
  const partType = stringValue3(part.type);
@@ -13063,10 +13483,7 @@ function turnTokenUsage(messages, model) {
13063
13483
  };
13064
13484
  }
13065
13485
  function openCodeStatusType(value) {
13066
- if (!isRecord9(value)) {
13067
- return null;
13068
- }
13069
- const type = stringValue3(value.type);
13486
+ const type = typeof value === "string" ? value : isRecord10(value) ? stringValue3(value.type) ?? stringValue3(value.status) ?? (isRecord10(value.status) ? stringValue3(value.status.type) : null) : null;
13070
13487
  return type === "idle" || type === "busy" || type === "retry" ? type : null;
13071
13488
  }
13072
13489
  function permissionRule(permission, action, pattern = "*") {
@@ -13171,21 +13588,21 @@ function turnWithPlanItemForCollaborationMode(turn, collaborationMode) {
13171
13588
  };
13172
13589
  }
13173
13590
  function messageId(message) {
13174
- if (!isRecord9(message)) {
13591
+ if (!isRecord10(message)) {
13175
13592
  return null;
13176
13593
  }
13177
- return stringValue3(message.id) ?? (isRecord9(message.info) ? stringValue3(message.info.id) : null);
13594
+ return stringValue3(message.id) ?? (isRecord10(message.info) ? stringValue3(message.info.id) : null);
13178
13595
  }
13179
13596
  function sessionSummary(record) {
13180
- if (!isRecord9(record)) {
13597
+ if (!isRecord10(record)) {
13181
13598
  return null;
13182
13599
  }
13183
13600
  const id = stringValue3(record.id);
13184
13601
  if (!id) {
13185
13602
  return null;
13186
13603
  }
13187
- const time = isRecord9(record.time) ? record.time : {};
13188
- const model = isRecord9(record.model) ? modelKey(
13604
+ const time = isRecord10(record.time) ? record.time : {};
13605
+ const model = isRecord10(record.model) ? modelKey(
13189
13606
  stringValue3(record.model.providerID) ?? "unknown",
13190
13607
  stringValue3(record.model.id) ?? "unknown",
13191
13608
  stringValue3(record.model.variant)
@@ -13648,7 +14065,7 @@ var OpenCodeRuntimeAdapter = class extends EventEmitter6 {
13648
14065
  ...directory ? { directory } : {}
13649
14066
  }
13650
14067
  }));
13651
- if (!isRecord9(statusMap)) {
14068
+ if (!isRecord10(statusMap)) {
13652
14069
  return null;
13653
14070
  }
13654
14071
  return openCodeStatusType(statusMap[providerSessionId]);
@@ -14460,6 +14877,23 @@ function hasHistoryItemSequence(item) {
14460
14877
  function historyItemSequence(item) {
14461
14878
  return hasHistoryItemSequence(item) ? item.sequence : Number.POSITIVE_INFINITY;
14462
14879
  }
14880
+ function historyItemTranscriptOrder(item) {
14881
+ return typeof item.transcriptOrder === "number" && Number.isFinite(item.transcriptOrder) ? item.transcriptOrder : null;
14882
+ }
14883
+ function copyPersistedOrderingHints(item, persistedItem) {
14884
+ let nextItem = item;
14885
+ if (hasHistoryItemSequence(persistedItem)) {
14886
+ const sequence = historyItemSequence(persistedItem);
14887
+ if (nextItem.sequence !== sequence) {
14888
+ nextItem = { ...nextItem, sequence };
14889
+ }
14890
+ }
14891
+ const transcriptOrder = historyItemTranscriptOrder(persistedItem);
14892
+ if (transcriptOrder !== null && nextItem.transcriptOrder !== transcriptOrder) {
14893
+ nextItem = { ...nextItem, transcriptOrder };
14894
+ }
14895
+ return nextItem;
14896
+ }
14463
14897
  function sortHistoryItemsBySequence(items) {
14464
14898
  if (!items.some(hasHistoryItemSequence)) {
14465
14899
  return items;
@@ -14480,6 +14914,9 @@ function sortTurnItemsByRecordedSequence(items) {
14480
14914
  if (!trailingItems.some(hasHistoryItemSequence)) {
14481
14915
  return items;
14482
14916
  }
14917
+ if (trailingItems.some((item) => historyItemTranscriptOrder(item) !== null)) {
14918
+ return items;
14919
+ }
14483
14920
  const sequenceValues = trailingItems.map((item) => historyItemSequence(item)).filter(Number.isFinite);
14484
14921
  const maxSequence = sequenceValues.length > 0 ? Math.max(...sequenceValues) : 0;
14485
14922
  const orderedItems2 = [];
@@ -14555,13 +14992,6 @@ function mergeHistoryItemsBySequence(items, missingItems) {
14555
14992
  }
14556
14993
  return sortTurnItemsByRecordedSequence(mergedItems);
14557
14994
  }
14558
- function copyPersistedSequence(item, persistedItem) {
14559
- if (!hasHistoryItemSequence(persistedItem)) {
14560
- return item;
14561
- }
14562
- const sequence = historyItemSequence(persistedItem);
14563
- return item.sequence === sequence ? item : { ...item, sequence };
14564
- }
14565
14995
  function shouldAppendPersistedMissingItem(turn, item) {
14566
14996
  if (item.kind !== "agentMessage") {
14567
14997
  return true;
@@ -14580,16 +15010,26 @@ function mergePersistedHistoryItemsIntoTurns(turns, persistedItemsByTurnId, defe
14580
15010
  }
14581
15011
  let changed = false;
14582
15012
  const persistedItemsById = new Map(persistedItems.map((item) => [item.id, item]));
14583
- const nextItems = turn.items.map((item) => {
15013
+ const nextItems = turn.items.map((item, transcriptIndex) => {
14584
15014
  const persistedItem = persistedItemsById.get(item.id);
15015
+ const itemWithTranscriptOrder = item.transcriptOrder === transcriptIndex ? item : { ...item, transcriptOrder: transcriptIndex };
14585
15016
  if (!persistedItem) {
14586
- return item;
15017
+ changed = true;
15018
+ return itemWithTranscriptOrder;
14587
15019
  }
14588
15020
  persistedItemsById.delete(item.id);
14589
15021
  if (item.kind !== persistedItem.kind) {
14590
- return item;
15022
+ changed = itemWithTranscriptOrder !== item || changed;
15023
+ return itemWithTranscriptOrder;
14591
15024
  }
14592
- const sequencedItem = copyPersistedSequence(item, persistedItem);
15025
+ const persistedItemWithTranscriptOrder = {
15026
+ ...persistedItem,
15027
+ transcriptOrder: transcriptIndex
15028
+ };
15029
+ const sequencedItem = copyPersistedOrderingHints(
15030
+ itemWithTranscriptOrder,
15031
+ persistedItemWithTranscriptOrder
15032
+ );
14593
15033
  if (sequencedItem !== item) {
14594
15034
  changed = true;
14595
15035
  }
@@ -14598,11 +15038,11 @@ function mergePersistedHistoryItemsIntoTurns(turns, persistedItemsByTurnId, defe
14598
15038
  const persistedText = persistedItem.detailText?.trim() || persistedItem.text.trim();
14599
15039
  if (persistedText.length > existingText.length) {
14600
15040
  changed = true;
14601
- return persistedItem.kind === "commandExecution" ? deferCommandHistoryItem2(
14602
- persistedItem,
15041
+ return persistedItemWithTranscriptOrder.kind === "commandExecution" ? deferCommandHistoryItem2(
15042
+ persistedItemWithTranscriptOrder,
14603
15043
  deferredDetails
14604
15044
  ) : deferToolCallHistoryItem2(
14605
- persistedItem,
15045
+ persistedItemWithTranscriptOrder,
14606
15046
  deferredDetails
14607
15047
  );
14608
15048
  }
@@ -14875,7 +15315,7 @@ var ThreadLiveStateStore = class {
14875
15315
 
14876
15316
  // src/thread-usage-accounting.ts
14877
15317
  var CONTEXT_BASELINE_TOKENS = 12e3;
14878
- function isRecord10(value) {
15318
+ function isRecord11(value) {
14879
15319
  return typeof value === "object" && value !== null && !Array.isArray(value);
14880
15320
  }
14881
15321
  function numberOrNull2(value) {
@@ -14923,11 +15363,11 @@ function computeContextRemainingPercent(tokensInContextWindow, contextWindow) {
14923
15363
  return clampPercentage(Math.round(remaining / effectiveWindow * 100));
14924
15364
  }
14925
15365
  function buildThreadContextUsageFromPayload(payload, model = null, timestamp = (/* @__PURE__ */ new Date()).toISOString()) {
14926
- const tokenUsage = isRecord10(payload) ? payload : null;
15366
+ const tokenUsage = isRecord11(payload) ? payload : null;
14927
15367
  const modelContextWindow = numberOrNull2(
14928
15368
  tokenUsage?.modelContextWindow ?? tokenUsage?.model_context_window
14929
15369
  ) ?? contextWindowForModel(model);
14930
- const lastUsage = isRecord10(tokenUsage?.last) ? tokenUsage.last : null;
15370
+ const lastUsage = isRecord11(tokenUsage?.last) ? tokenUsage.last : null;
14931
15371
  const tokensInContextWindow = numberOrNull2(
14932
15372
  lastUsage?.totalTokens ?? lastUsage?.total_tokens
14933
15373
  );
@@ -14946,7 +15386,7 @@ function buildThreadContextUsageFromPayload(payload, model = null, timestamp = (
14946
15386
  };
14947
15387
  }
14948
15388
  function buildTurnTokenBreakdown(payload) {
14949
- const usage = isRecord10(payload) ? payload : null;
15389
+ const usage = isRecord11(payload) ? payload : null;
14950
15390
  const totalTokens2 = numberOrNull2(usage?.totalTokens ?? usage?.total_tokens);
14951
15391
  const inputTokens = numberOrNull2(usage?.inputTokens ?? usage?.input_tokens);
14952
15392
  const cachedInputTokens = numberOrNull2(
@@ -14992,12 +15432,12 @@ function subtractTurnTokenBreakdowns(current, previous) {
14992
15432
  };
14993
15433
  }
14994
15434
  function parseThreadTurnTokenUsage(payload) {
14995
- const tokenUsage = isRecord10(payload) ? payload : null;
15435
+ const tokenUsage = isRecord11(payload) ? payload : null;
14996
15436
  const total = buildTurnTokenBreakdown(
14997
- isRecord10(tokenUsage?.total) ? tokenUsage.total : null
15437
+ isRecord11(tokenUsage?.total) ? tokenUsage.total : null
14998
15438
  );
14999
15439
  const last = buildTurnTokenBreakdown(
15000
- isRecord10(tokenUsage?.last) ? tokenUsage.last : null
15440
+ isRecord11(tokenUsage?.last) ? tokenUsage.last : null
15001
15441
  );
15002
15442
  const modelContextWindow = numberOrNull2(
15003
15443
  tokenUsage?.modelContextWindow ?? tokenUsage?.model_context_window
@@ -15021,7 +15461,7 @@ function parseStoredThreadTurnTokenUsageState(value) {
15021
15461
  try {
15022
15462
  const parsed = JSON.parse(value);
15023
15463
  const baselineTotal = buildTurnTokenBreakdown(
15024
- isRecord10(parsed?.baselineTotal) ? parsed.baselineTotal : null
15464
+ isRecord11(parsed?.baselineTotal) ? parsed.baselineTotal : null
15025
15465
  );
15026
15466
  return {
15027
15467
  baselineTotal,
@@ -15061,12 +15501,12 @@ function stringifyStoredThreadTurnTokenUsageState(state) {
15061
15501
  });
15062
15502
  }
15063
15503
  function buildThreadTurnTokenUsage(payload, baselineTotal, previous = null) {
15064
- const tokenUsage = isRecord10(payload) ? payload : null;
15504
+ const tokenUsage = isRecord11(payload) ? payload : null;
15065
15505
  const cumulativeTotal = buildTurnTokenBreakdown(
15066
- isRecord10(tokenUsage?.total) ? tokenUsage.total : null
15506
+ isRecord11(tokenUsage?.total) ? tokenUsage.total : null
15067
15507
  );
15068
15508
  const last = buildTurnTokenBreakdown(
15069
- isRecord10(tokenUsage?.last) ? tokenUsage.last : null
15509
+ isRecord11(tokenUsage?.last) ? tokenUsage.last : null
15070
15510
  );
15071
15511
  const modelContextWindow = numberOrNull2(
15072
15512
  tokenUsage?.modelContextWindow ?? tokenUsage?.model_context_window
@@ -15138,7 +15578,7 @@ var ThreadUsageAccounting = class {
15138
15578
  input.localThreadId
15139
15579
  );
15140
15580
  const currentCumulativeTotal = buildTurnTokenBreakdown(
15141
- isRecord10(input.tokenUsage?.total) ? input.tokenUsage.total : null
15581
+ isRecord11(input.tokenUsage?.total) ? input.tokenUsage.total : null
15142
15582
  );
15143
15583
  if (currentCumulativeTotal) {
15144
15584
  this.threadCumulativeTokenUsage.set(input.localThreadId, currentCumulativeTotal);
@@ -15645,7 +16085,7 @@ var ProviderRequestCoordinator = class {
15645
16085
  const defaultMappedRequest = runtime.mapProviderRequest?.(request, {
15646
16086
  approvalMode: "guarded"
15647
16087
  });
15648
- const providerSessionIdFromParams = isRecord11(request.params) ? request.params.providerSessionId ?? request.params.threadId ?? request.params.conversationId ?? request.params.sessionId : null;
16088
+ const providerSessionIdFromParams = isRecord12(request.params) ? request.params.providerSessionId ?? request.params.threadId ?? request.params.conversationId ?? request.params.sessionId : null;
15649
16089
  const providerSessionId = defaultMappedRequest?.providerSessionId ?? (typeof providerSessionIdFromParams === "string" ? providerSessionIdFromParams : null);
15650
16090
  const record = providerSessionId ? this.callbacks.findRecordByProviderSessionId(request.provider, providerSessionId) : null;
15651
16091
  if (!record) {
@@ -15858,7 +16298,7 @@ function buildRequestAnswerLines(request, input) {
15858
16298
  return `- ${question.question}: ${answers.join(", ")}`;
15859
16299
  }).filter((line) => Boolean(line));
15860
16300
  }
15861
- function isRecord11(value) {
16301
+ function isRecord12(value) {
15862
16302
  return typeof value === "object" && value !== null && !Array.isArray(value);
15863
16303
  }
15864
16304
 
@@ -15977,9 +16417,16 @@ var ThreadDetailAssembler = class {
15977
16417
  input.record,
15978
16418
  latestThreadTurnMetadata(input.turnMetadataById)
15979
16419
  );
16420
+ const localTurns = localSession?.turns ?? [...persistedItemsByTurnId.entries()].map(([turnId, items]) => ({
16421
+ id: turnId,
16422
+ startedAt: null,
16423
+ status: "completed",
16424
+ error: null,
16425
+ items: []
16426
+ }));
15980
16427
  const turns = mergePersistedHistoryItemsIntoTurns(
15981
16428
  applyRecordedTurnItemOrders(
15982
- localSession?.turns ?? [],
16429
+ localTurns,
15983
16430
  this.input.liveState.turnItemOrderSnapshot(input.localThreadId)
15984
16431
  ),
15985
16432
  persistedItemsByTurnId,
@@ -16154,7 +16601,7 @@ var ThreadProviderRuntimeCoordinator = class {
16154
16601
  }
16155
16602
  runtimeForProvider(provider) {
16156
16603
  const normalizedProvider = this.normalizeProvider(provider);
16157
- const runtime = this.agentRuntimes.getOptional(normalizedProvider);
16604
+ const runtime = this.optionalRuntimeForProvider(normalizedProvider);
16158
16605
  if (!runtime) {
16159
16606
  throw new HttpError(501, {
16160
16607
  code: "service_unavailable",
@@ -16163,6 +16610,9 @@ var ThreadProviderRuntimeCoordinator = class {
16163
16610
  }
16164
16611
  return runtime;
16165
16612
  }
16613
+ optionalRuntimeForProvider(provider) {
16614
+ return this.agentRuntimes.getOptional(this.normalizeProvider(provider)) ?? null;
16615
+ }
16166
16616
  allRuntimes() {
16167
16617
  return this.agentRuntimes.all();
16168
16618
  }
@@ -16173,7 +16623,7 @@ var ThreadProviderRuntimeCoordinator = class {
16173
16623
  return this.providerForRecord({ provider }) === "codex";
16174
16624
  }
16175
16625
  runtimeSupportsFastMode(provider) {
16176
- return this.runtimeForProvider(provider).capabilities.controls.performanceMode;
16626
+ return this.optionalRuntimeForProvider(provider)?.capabilities.controls.performanceMode ?? false;
16177
16627
  }
16178
16628
  fastModeForProvider(provider, fastMode) {
16179
16629
  return this.runtimeSupportsFastMode(provider) ? normalizeFastMode(fastMode) : false;
@@ -16184,8 +16634,12 @@ var ThreadProviderRuntimeCoordinator = class {
16184
16634
  );
16185
16635
  }
16186
16636
  async listLoadedProviderSessionIds(provider = "codex") {
16637
+ const runtime = this.optionalRuntimeForProvider(provider);
16638
+ if (!runtime) {
16639
+ return /* @__PURE__ */ new Set();
16640
+ }
16187
16641
  return new Set(
16188
- await this.runtimeForProvider(provider).listLoadedSessions().catch(() => [])
16642
+ await runtime.listLoadedSessions().catch(() => [])
16189
16643
  );
16190
16644
  }
16191
16645
  async listProviderModels(provider = "codex") {
@@ -18703,6 +19157,7 @@ var ProviderFeatureCoordinator = class {
18703
19157
  var DEFAULT_THREAD_TITLE2 = "Untitled thread";
18704
19158
  var GENERIC_REMOTE_THREAD_TITLE = "Thread";
18705
19159
  var IMPLEMENT_APPROVED_PLAN_PROMPT = "Implement the approved plan.";
19160
+ var LOCAL_PLAN_DECISION_PREFIX2 = "plan-decision:";
18706
19161
  var FAST_MODE_NOTE_ON = "Fast mode on";
18707
19162
  var FAST_MODE_NOTE_OFF = "Fast mode off";
18708
19163
  async function pathExists3(absPath) {
@@ -18714,10 +19169,11 @@ async function pathExists3(absPath) {
18714
19169
  }
18715
19170
  }
18716
19171
  var ThreadService = class {
18717
- constructor(db, agentRuntimes, eventBus, localSessionStore, workspaceRoot, providerManagement) {
19172
+ constructor(db, agentRuntimes, eventBus, localSessionStore, workspaceRoot, providerManagement, pluginService) {
18718
19173
  this.db = db;
18719
19174
  this.eventBus = eventBus;
18720
19175
  this.workspaceRoot = workspaceRoot;
19176
+ this.pluginService = pluginService;
18721
19177
  this.providerRuntime = new ThreadProviderRuntimeCoordinator(agentRuntimes);
18722
19178
  this.historyPersistence = new ThreadHistoryPersistenceCoordinator(db, this.liveState);
18723
19179
  this.attachmentCoordinator = new ThreadAttachmentCoordinator(db);
@@ -18764,6 +19220,9 @@ var ThreadService = class {
18764
19220
  listPersistedHistoryItemsByTurnId: (localThreadId) => this.historyPersistence.listPersistedHistoryItemsByTurnId(localThreadId),
18765
19221
  materializeHiddenRuntimeTurns: (localThreadId, turns) => this.materializeHiddenRuntimeTurns(localThreadId, turns),
18766
19222
  readRemoteSession: async (record, options) => {
19223
+ if (!this.optionalRuntimeForProvider(record.provider)) {
19224
+ return null;
19225
+ }
18767
19226
  const workspace = getWorkspaceRecordById(this.db, record.workspaceId);
18768
19227
  return this.sessionCoordinator.readRemoteSession({
18769
19228
  provider: record.provider,
@@ -18909,6 +19368,7 @@ var ThreadService = class {
18909
19368
  db;
18910
19369
  eventBus;
18911
19370
  workspaceRoot;
19371
+ pluginService;
18912
19372
  liveState = new ThreadLiveStateStore();
18913
19373
  detailAssembler;
18914
19374
  usageAccounting;
@@ -18933,6 +19393,9 @@ var ThreadService = class {
18933
19393
  runtimeForProvider(provider) {
18934
19394
  return this.providerRuntime.runtimeForProvider(provider);
18935
19395
  }
19396
+ optionalRuntimeForProvider(provider) {
19397
+ return this.providerRuntime.optionalRuntimeForProvider(provider);
19398
+ }
18936
19399
  providerForRecord(record) {
18937
19400
  return this.providerRuntime.providerForRecord(record);
18938
19401
  }
@@ -19117,14 +19580,25 @@ var ThreadService = class {
19117
19580
  options
19118
19581
  });
19119
19582
  const updated = getThreadRecordById(this.db, record.id);
19120
- const pagedTurns = this.detailAssembler.sliceTurns(cachedDetail.turns, options);
19583
+ const enrichedTurns = this.pluginService?.enrichTurnsWithArtifacts({
19584
+ threadId: updated.id,
19585
+ workspacePath: workspace.absPath,
19586
+ turns: cachedDetail.turns
19587
+ }) ?? cachedDetail.turns;
19588
+ const pagedTurns = this.detailAssembler.sliceTurns(enrichedTurns, options);
19589
+ this.syncPendingPlanDecisionRequestFromTurns(
19590
+ updated.id,
19591
+ updated.collaborationMode,
19592
+ enrichedTurns
19593
+ );
19121
19594
  const liveItems = this.liveState.getLiveItems(
19122
19595
  updated.id,
19123
- cachedDetail.turns,
19596
+ enrichedTurns,
19124
19597
  pagedTurns.turns
19125
19598
  );
19126
19599
  const goalHistory = this.goalCoordinator.listThreadGoalHistory(updated.id);
19127
- const goal = await this.goalCoordinator.getThreadGoalForRecord(updated).catch(() => null) ?? this.goalCoordinator.localGoalSnapshotForFallback(goalHistory);
19600
+ const remoteGoal = updated.isConnected === false || !this.optionalRuntimeForProvider(updated.provider) ? null : await this.goalCoordinator.getThreadGoalForRecord(updated).catch(() => null);
19601
+ const goal = remoteGoal ?? this.goalCoordinator.localGoalSnapshotForFallback(goalHistory);
19128
19602
  const pendingRequests = this.listPendingRequests(updated.id);
19129
19603
  return {
19130
19604
  thread: this.toThreadDto(updated, loadedIds),
@@ -19631,16 +20105,25 @@ var ThreadService = class {
19631
20105
  requestId,
19632
20106
  input
19633
20107
  );
19634
- if (!requestResponse) {
20108
+ let resolvedRequestResponse = requestResponse;
20109
+ if (!resolvedRequestResponse && requestId.startsWith(LOCAL_PLAN_DECISION_PREFIX2)) {
20110
+ await this.syncPendingPlanDecisionRequestFromCurrentDetail(localThreadId);
20111
+ resolvedRequestResponse = this.requestCoordinator.respondToRequest(
20112
+ localThreadId,
20113
+ requestId,
20114
+ input
20115
+ );
20116
+ }
20117
+ if (!resolvedRequestResponse) {
19635
20118
  throw new HttpError(404, {
19636
20119
  code: "not_found",
19637
20120
  message: "Request was not found for this thread."
19638
20121
  });
19639
20122
  }
19640
- if (requestResponse.source === "server") {
19641
- const pending = requestResponse.pending;
20123
+ if (resolvedRequestResponse.source === "server") {
20124
+ const pending = resolvedRequestResponse.pending;
19642
20125
  this.requestCoordinator.respondToProviderRequest(record.provider, pending, input);
19643
- if (requestResponse.continuationPrompt) {
20126
+ if (resolvedRequestResponse.continuationPrompt) {
19644
20127
  const providerSessionId = this.requireProviderSessionId(record);
19645
20128
  const connectedRecord = {
19646
20129
  ...record,
@@ -19663,7 +20146,7 @@ var ThreadService = class {
19663
20146
  approvalMode: record.approvalMode ?? "yolo"
19664
20147
  });
19665
20148
  await this.promptTurnCoordinator.startPromptTurn(localThreadId, connectedRecord, {
19666
- prompt: requestResponse.continuationPrompt,
20149
+ prompt: resolvedRequestResponse.continuationPrompt,
19667
20150
  effectiveModel: turnConfig.effectiveModel,
19668
20151
  normalizedReasoning: turnConfig.normalizedReasoning,
19669
20152
  collaborationMode: turnConfig.collaborationMode,
@@ -19674,7 +20157,7 @@ var ThreadService = class {
19674
20157
  displayTurnId: pending.request.turnId
19675
20158
  });
19676
20159
  }
19677
- } else if (requestResponse.selectedAction === "implement") {
20160
+ } else if (resolvedRequestResponse.selectedAction === "implement") {
19678
20161
  await this.importCoordinator.ensureImportedThreadConnectedForImplementation({
19679
20162
  source: record.source,
19680
20163
  provider: record.provider,
@@ -19693,7 +20176,7 @@ var ThreadService = class {
19693
20176
  }
19694
20177
  this.auxiliaryState.appendAnsweredRequestNote(
19695
20178
  localThreadId,
19696
- requestResponse.answeredNote
20179
+ resolvedRequestResponse.answeredNote
19697
20180
  );
19698
20181
  this.requestCoordinator.emitRequestResolved(localThreadId, requestId);
19699
20182
  return this.getThreadDetail(localThreadId);
@@ -19753,6 +20236,49 @@ var ThreadService = class {
19753
20236
  latestTurn: remoteSession.turns.at(-1) ?? null
19754
20237
  });
19755
20238
  }
20239
+ syncPendingPlanDecisionRequestFromTurns(localThreadId, collaborationMode, turns) {
20240
+ const latestTurn = turns.at(-1) ?? null;
20241
+ this.requestCoordinator.syncPendingPlanDecisionRequest({
20242
+ localThreadId,
20243
+ collaborationMode,
20244
+ latestTurn: latestTurn ? {
20245
+ providerTurnId: latestTurn.id,
20246
+ status: latestTurn.status,
20247
+ items: latestTurn.items.map((item) => ({ kind: item.kind }))
20248
+ } : null
20249
+ });
20250
+ }
20251
+ async syncPendingPlanDecisionRequestFromCurrentDetail(localThreadId) {
20252
+ const record = getThreadRecordById(this.db, localThreadId);
20253
+ if (!record) {
20254
+ return;
20255
+ }
20256
+ const turnMetadataById = new Map(
20257
+ listThreadTurnMetadataByThreadId(this.db, localThreadId).map((entry) => [
20258
+ entry.turnId,
20259
+ {
20260
+ model: entry.model ?? null,
20261
+ reasoningEffort: entry.reasoningEffort ?? null,
20262
+ reasoningEffortAvailable: entry.reasoningEffortAvailable ?? null,
20263
+ pricingModelKey: entry.pricingModelKey ?? null,
20264
+ pricingTierKey: normalizePricingTier(entry.pricingTierKey),
20265
+ tokenUsageJson: entry.tokenUsageJson ?? null,
20266
+ createdAt: entry.createdAt ?? null
20267
+ }
20268
+ ])
20269
+ );
20270
+ const cachedDetail = await this.detailAssembler.buildCacheEntry({
20271
+ localThreadId,
20272
+ record,
20273
+ turnMetadataById
20274
+ });
20275
+ const updated = getThreadRecordById(this.db, localThreadId) ?? record;
20276
+ this.syncPendingPlanDecisionRequestFromTurns(
20277
+ updated.id,
20278
+ updated.collaborationMode,
20279
+ cachedDetail.turns
20280
+ );
20281
+ }
19756
20282
  };
19757
20283
 
19758
20284
  // src/routes/agent-runtimes.ts
@@ -21135,6 +21661,72 @@ async function registerWorkspaceRoutes(app2) {
21135
21661
  });
21136
21662
  }
21137
21663
 
21664
+ // src/routes/plugins.ts
21665
+ import { z as z8 } from "zod";
21666
+ var pluginParamsSchema = z8.object({
21667
+ pluginId: z8.string().min(1)
21668
+ });
21669
+ var updatePluginSchema = z8.object({
21670
+ enabled: z8.boolean()
21671
+ });
21672
+ var importPluginSchema = z8.object({
21673
+ enabled: z8.boolean().optional(),
21674
+ manifestJson: z8.string().optional(),
21675
+ manifest: z8.unknown().optional()
21676
+ }).refine((value) => value.manifest !== void 0 || value.manifestJson !== void 0, {
21677
+ message: "Plugin import requires manifest or manifestJson."
21678
+ });
21679
+ async function registerPluginRoutes(app2) {
21680
+ app2.get("/api/plugins", async () => {
21681
+ return app2.services.pluginService.listPlugins();
21682
+ });
21683
+ app2.post("/api/plugins/import", async (request) => {
21684
+ const parsed = importPluginSchema.parse(request.body);
21685
+ const body = {
21686
+ ...parsed.enabled === void 0 ? {} : { enabled: parsed.enabled },
21687
+ ...parsed.manifest === void 0 ? {} : { manifest: parsed.manifest },
21688
+ ...parsed.manifestJson === void 0 ? {} : { manifestJson: parsed.manifestJson }
21689
+ };
21690
+ try {
21691
+ return app2.services.pluginService.importPlugin(body);
21692
+ } catch (error) {
21693
+ if (error instanceof SyntaxError) {
21694
+ throw new HttpError(400, {
21695
+ code: "bad_request",
21696
+ message: "Plugin manifest JSON is invalid."
21697
+ });
21698
+ }
21699
+ throw new HttpError(400, {
21700
+ code: "bad_request",
21701
+ message: error instanceof Error ? error.message : "Plugin import failed."
21702
+ });
21703
+ }
21704
+ });
21705
+ app2.get("/api/plugins/:pluginId", async (request) => {
21706
+ const { pluginId } = pluginParamsSchema.parse(request.params);
21707
+ const plugin = app2.services.pluginService.getPlugin(pluginId);
21708
+ if (!plugin) {
21709
+ throw new HttpError(404, {
21710
+ code: "not_found",
21711
+ message: "Plugin was not found."
21712
+ });
21713
+ }
21714
+ return plugin;
21715
+ });
21716
+ app2.patch("/api/plugins/:pluginId", async (request) => {
21717
+ const { pluginId } = pluginParamsSchema.parse(request.params);
21718
+ const body = updatePluginSchema.parse(request.body);
21719
+ try {
21720
+ return app2.services.pluginService.setPluginEnabled(pluginId, body.enabled);
21721
+ } catch {
21722
+ throw new HttpError(404, {
21723
+ code: "not_found",
21724
+ message: "Plugin was not found."
21725
+ });
21726
+ }
21727
+ });
21728
+ }
21729
+
21138
21730
  // src/provider-host-config-service.ts
21139
21731
  import fs18 from "fs/promises";
21140
21732
  import path18 from "path";
@@ -22495,6 +23087,212 @@ function tokenizeTmuxInput(data) {
22495
23087
  return tokens;
22496
23088
  }
22497
23089
 
23090
+ // ../../packages/plugin-xyz-viewer/src/manifest.ts
23091
+ var XYZ_MOLECULE_ARTIFACT_TYPE = "chemistry.molecule3d";
23092
+ var xyzViewerPluginManifest = {
23093
+ id: "remote-codex.xyz-viewer",
23094
+ name: "XYZ Molecule Viewer",
23095
+ version: "0.1.0",
23096
+ description: "A draft built-in plugin for previewing xyz, extxyz, cif, and pdb molecular structures with 3Dmol.js.",
23097
+ remoteCodex: "^0.11.0",
23098
+ capabilities: {
23099
+ artifactTypes: [
23100
+ {
23101
+ type: XYZ_MOLECULE_ARTIFACT_TYPE,
23102
+ title: "3D Molecule",
23103
+ fileExtensions: ["xyz", "extxyz", "cif", "pdb"]
23104
+ }
23105
+ ],
23106
+ timelineRenderers: [XYZ_MOLECULE_ARTIFACT_TYPE],
23107
+ threadPanels: [
23108
+ {
23109
+ id: "xyz-viewer",
23110
+ label: "Molecules",
23111
+ artifactTypes: [XYZ_MOLECULE_ARTIFACT_TYPE]
23112
+ }
23113
+ ],
23114
+ frontend: {
23115
+ entry: "./dist/index.js",
23116
+ style: "./src/styles.css"
23117
+ }
23118
+ }
23119
+ };
23120
+
23121
+ // src/plugins/builtin-plugins.ts
23122
+ var builtinPlugins = [
23123
+ {
23124
+ manifest: xyzViewerPluginManifest,
23125
+ enabledByDefault: true
23126
+ }
23127
+ ];
23128
+
23129
+ // src/plugins/plugin-service.ts
23130
+ var PluginService = class {
23131
+ constructor(registry, settingsStore) {
23132
+ this.registry = registry;
23133
+ this.settingsStore = settingsStore;
23134
+ this.loadPersistedSettings();
23135
+ }
23136
+ registry;
23137
+ settingsStore;
23138
+ settings = {
23139
+ enabled: {},
23140
+ imported: []
23141
+ };
23142
+ listPlugins() {
23143
+ return this.registry.list();
23144
+ }
23145
+ getPlugin(pluginId) {
23146
+ return this.registry.get(pluginId);
23147
+ }
23148
+ setPluginEnabled(pluginId, enabled) {
23149
+ const plugin = this.registry.setEnabled(pluginId, enabled);
23150
+ this.settings.enabled[pluginId] = enabled;
23151
+ this.persistSettings();
23152
+ return plugin;
23153
+ }
23154
+ importPlugin(input) {
23155
+ const manifestInput = input.manifest ?? this.parseManifestJson(input.manifestJson);
23156
+ const manifest = parsePluginManifest(manifestInput);
23157
+ const enabled = input.enabled ?? true;
23158
+ const existing = this.registry.getRegistered(manifest.id);
23159
+ if (existing && existing.source !== "imported") {
23160
+ throw new Error(`Built-in plugin cannot be replaced: ${manifest.id}`);
23161
+ }
23162
+ this.registerImportedManifest(manifest, enabled);
23163
+ const existingIndex = this.settings.imported.findIndex(
23164
+ (entry) => entry.id === manifest.id
23165
+ );
23166
+ if (existingIndex >= 0) {
23167
+ this.settings.imported[existingIndex] = manifest;
23168
+ } else {
23169
+ this.settings.imported.push(manifest);
23170
+ }
23171
+ this.settings.enabled[manifest.id] = enabled;
23172
+ this.persistSettings();
23173
+ const plugin = this.registry.get(manifest.id);
23174
+ if (!plugin) {
23175
+ throw new Error(`Plugin import failed: ${manifest.id}`);
23176
+ }
23177
+ return plugin;
23178
+ }
23179
+ enrichTurnsWithArtifacts(input) {
23180
+ const manifests = this.registry.enabledManifests();
23181
+ if (manifests.length === 0) {
23182
+ return input.turns;
23183
+ }
23184
+ return appendArtifactItemsToTurns(
23185
+ input.turns,
23186
+ new ManifestArtifactExtractor(manifests),
23187
+ {
23188
+ threadId: input.threadId,
23189
+ workspacePath: input.workspacePath,
23190
+ now: (/* @__PURE__ */ new Date()).toISOString()
23191
+ }
23192
+ );
23193
+ }
23194
+ loadPersistedSettings() {
23195
+ if (!this.settingsStore) {
23196
+ return;
23197
+ }
23198
+ this.settings = this.settingsStore.load();
23199
+ for (const manifest of this.settings.imported) {
23200
+ this.registerImportedManifest(
23201
+ manifest,
23202
+ this.settings.enabled[manifest.id] ?? true
23203
+ );
23204
+ }
23205
+ for (const [pluginId, enabled] of Object.entries(this.settings.enabled)) {
23206
+ if (this.registry.get(pluginId)) {
23207
+ this.registry.setEnabled(pluginId, enabled);
23208
+ }
23209
+ }
23210
+ }
23211
+ registerImportedManifest(manifest, enabled) {
23212
+ if (this.registry.get(manifest.id)) {
23213
+ const existing = this.registry.getRegistered(manifest.id);
23214
+ if (existing?.source === "imported") {
23215
+ this.registry.updateImported({
23216
+ manifest,
23217
+ enabledByDefault: enabled,
23218
+ source: "imported"
23219
+ });
23220
+ } else {
23221
+ this.registry.setEnabled(manifest.id, enabled);
23222
+ }
23223
+ return;
23224
+ }
23225
+ this.registry.register({
23226
+ manifest,
23227
+ enabledByDefault: enabled,
23228
+ source: "imported"
23229
+ });
23230
+ }
23231
+ persistSettings() {
23232
+ this.settingsStore?.save(this.settings);
23233
+ }
23234
+ parseManifestJson(manifestJson) {
23235
+ if (!manifestJson?.trim()) {
23236
+ throw new Error("Plugin import requires a manifest object or manifestJson string.");
23237
+ }
23238
+ return JSON.parse(manifestJson);
23239
+ }
23240
+ };
23241
+
23242
+ // src/plugins/plugin-settings-store.ts
23243
+ var PLUGIN_SETTINGS_POLICY_KEY = "plugins";
23244
+ function emptySettings() {
23245
+ return {
23246
+ enabled: {},
23247
+ imported: []
23248
+ };
23249
+ }
23250
+ function parseEnabled(value) {
23251
+ if (!value || typeof value !== "object" || Array.isArray(value)) {
23252
+ return {};
23253
+ }
23254
+ const output = {};
23255
+ for (const [key, enabled] of Object.entries(value)) {
23256
+ if (typeof enabled === "boolean") {
23257
+ output[key] = enabled;
23258
+ } else if (enabled && typeof enabled === "object" && !Array.isArray(enabled) && typeof enabled.enabled === "boolean") {
23259
+ output[key] = enabled.enabled;
23260
+ }
23261
+ }
23262
+ return output;
23263
+ }
23264
+ var PluginSettingsStore = class {
23265
+ constructor(db) {
23266
+ this.db = db;
23267
+ }
23268
+ db;
23269
+ load() {
23270
+ const record = getPolicyRecordByKey(this.db, PLUGIN_SETTINGS_POLICY_KEY);
23271
+ if (!record?.valueJson) {
23272
+ return emptySettings();
23273
+ }
23274
+ try {
23275
+ const parsed = JSON.parse(record.valueJson);
23276
+ return {
23277
+ enabled: parseEnabled(parsed.enabled),
23278
+ imported: Array.isArray(parsed.imported) ? parsed.imported.map((entry) => parsePluginManifest(entry)) : []
23279
+ };
23280
+ } catch {
23281
+ return emptySettings();
23282
+ }
23283
+ }
23284
+ save(settings) {
23285
+ upsertPolicyRecord(
23286
+ this.db,
23287
+ PLUGIN_SETTINGS_POLICY_KEY,
23288
+ JSON.stringify({
23289
+ enabled: settings.enabled,
23290
+ imported: settings.imported
23291
+ })
23292
+ );
23293
+ }
23294
+ };
23295
+
22498
23296
  // src/app.ts
22499
23297
  var MAX_PROMPT_ATTACHMENTS2 = 10;
22500
23298
  var MAX_PROMPT_ATTACHMENT_BYTES2 = 25 * 1024 * 1024;
@@ -22554,6 +23352,9 @@ function buildApp(options = {}) {
22554
23352
  const database = createDatabase(config.databaseUrl);
22555
23353
  seedDefaults(database.db);
22556
23354
  const eventBus = new SupervisorEventBus();
23355
+ const pluginRegistry = new PluginRegistry(builtinPlugins);
23356
+ const pluginSettingsStore = new PluginSettingsStore(database.db);
23357
+ const pluginService = new PluginService(pluginRegistry, pluginSettingsStore);
22557
23358
  const runtimeBootstrap = options.runtimeBootstrap ?? createAgentRuntimeBootstrap(config);
22558
23359
  const agentRuntimes = options.agentRuntimes ?? runtimeBootstrap.agentRuntimes;
22559
23360
  const threadService = new ThreadService(
@@ -22562,7 +23363,8 @@ function buildApp(options = {}) {
22562
23363
  eventBus,
22563
23364
  runtimeBootstrap.localCodexSessionStore,
22564
23365
  config.workspaceRoot,
22565
- runtimeBootstrap.codexManagement
23366
+ runtimeBootstrap.codexManagement,
23367
+ pluginService
22566
23368
  );
22567
23369
  const shellService = options.shellService ?? new ShellSessionService(database.db, eventBus, new TmuxManager());
22568
23370
  const providerHostConfigService = new ProviderHostConfigService(
@@ -22589,7 +23391,9 @@ function buildApp(options = {}) {
22589
23391
  eventBus,
22590
23392
  threadService,
22591
23393
  shellService,
22592
- providerHostConfigService
23394
+ providerHostConfigService,
23395
+ pluginRegistry,
23396
+ pluginService
22593
23397
  });
22594
23398
  app2.register(async (realtimeApp) => {
22595
23399
  await realtimeApp.register(websocket);
@@ -22731,6 +23535,7 @@ function buildApp(options = {}) {
22731
23535
  app2.register(registerSystemRoutes);
22732
23536
  app2.register(registerAgentRuntimeRoutes);
22733
23537
  app2.register(registerShellRoutes);
23538
+ app2.register(registerPluginRoutes);
22734
23539
  app2.register(registerThreadRoutes);
22735
23540
  app2.register(registerWorkspaceRoutes);
22736
23541
  app2.setNotFoundHandler((_request, reply) => {