dsh-output-styles 0.4.3 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/lib/index.js CHANGED
@@ -38,7 +38,8 @@ const Config = z.object({
38
38
  style: z.string().min(1),
39
39
  priority: z.number().required(false)
40
40
  })).default([]),
41
- enableExport: z.boolean().default(true)
41
+ enableExport: z.boolean().default(true),
42
+ respectCoreOutputStyles: z.boolean().default(true)
42
43
  });
43
44
  /**
44
45
  * Resolve the same defaults for direct callers that bypass the Cordis Loader,
@@ -75,7 +76,59 @@ function resolveConfig(config, defaultStylesDir) {
75
76
  ...rule,
76
77
  priority: rule.priority ?? 0
77
78
  })),
78
- enableExport: config.enableExport ?? true
79
+ enableExport: config.enableExport ?? true,
80
+ respectCoreOutputStyles: config.respectCoreOutputStyles ?? true
81
+ };
82
+ }
83
+ //#endregion
84
+ //#region lib/types/coexist.js
85
+ /**
86
+ * Coexistence with a core `outputStyles` capability.
87
+ *
88
+ * If DeepSeek Harness ever ships a first-party `outputStyles` feature, this
89
+ * plugin's system-prompt injection would duplicate it. The coexistence policy
90
+ * detects the core capability through its reserved service seam and, when it
91
+ * is present, degrades to the incremental surface this plugin uniquely adds —
92
+ * runtime hot-switch, per-session/per-tool rules, and `/export` — while
93
+ * leaving prompt injection entirely to the core.
94
+ *
95
+ * Detection is a service probe, not a name guess: a core implementation
96
+ * publishes the `outputStyles` service; when that seam is absent the plugin
97
+ * runs standalone (injecting the style section itself).
98
+ * @module dsh-output-styles/coexist
99
+ */
100
+ /** The reserved service name a core `outputStyles` capability publishes. */
101
+ const CORE_OUTPUT_STYLES_SERVICE = "outputStyles";
102
+ /**
103
+ * Probe whether a core `outputStyles` capability is composed.
104
+ * @param ctx - the plugin-scoped context.
105
+ * @returns true when the core `outputStyles` service seam is present.
106
+ */
107
+ function detectCoreOutputStyles(ctx) {
108
+ return ctx.get(CORE_OUTPUT_STYLES_SERVICE) !== void 0;
109
+ }
110
+ /** The incremental surfaces that survive coexistence in either mode. */
111
+ const RETAINED = [
112
+ "hot-switch",
113
+ "rules",
114
+ "export"
115
+ ];
116
+ /**
117
+ * Compute the coexistence report for one composition. Respecting the core is
118
+ * the default (`respectCore` true); a deployment can force standalone mode by
119
+ * setting `respectCoreOutputStyles: false`.
120
+ * @param ctx - the plugin-scoped context.
121
+ * @param respectCore - whether to honor a detected core capability (default true).
122
+ * @returns the coexistence report.
123
+ */
124
+ function coexistenceReport(ctx, respectCore = true) {
125
+ const coreActive = respectCore && detectCoreOutputStyles(ctx);
126
+ return {
127
+ coreActive,
128
+ mode: coreActive ? "degraded" : "standalone",
129
+ promptInjection: coreActive ? "disabled" : "enabled",
130
+ retained: [...RETAINED],
131
+ disabled: coreActive ? ["system-prompt injection"] : []
79
132
  };
80
133
  }
81
134
  //#endregion
@@ -619,6 +672,8 @@ async function apply(ctx, config) {
619
672
  const storageDomain = ctx.get("storageDomain");
620
673
  if (storageDomain === void 0) throw new Error("dsh-output-styles: the \"storageDomain\" service declared by inject is unavailable — mount the storage facility (see README) or declare the inject");
621
674
  const resolved = resolveConfig(config, DEFAULT_STYLES_DIR);
675
+ const coreActive = resolved.respectCoreOutputStyles && detectCoreOutputStyles(ctx);
676
+ if (coreActive) ctx.logger.info("dsh-output-styles: core outputStyles detected — degrading to hot-switch + rules + export (no prompt injection)");
622
677
  const styles = loadStyleLibrary(resolved.stylesDirs, { compatJson: resolved.compatJson }, (message) => {
623
678
  ctx.logger.warn(`dsh-output-styles: ${message}`);
624
679
  });
@@ -673,31 +728,33 @@ async function apply(ctx, config) {
673
728
  if (value.style !== "" && !runtime.styles.has(value.style)) throw new Error(`dsh-output-styles: settings outputStyle "${value.style}" names no style (available: ${[...runtime.styles.keys()].join(", ") || "none"})`);
674
729
  }
675
730
  });
676
- ctx.systemPrompt.section({
677
- name: STYLE_SECTION_NAME,
678
- order: resolved.sectionOrder,
679
- text: (context) => {
731
+ if (!coreActive) {
732
+ ctx.systemPrompt.section({
733
+ name: STYLE_SECTION_NAME,
734
+ order: resolved.sectionOrder,
735
+ text: (context) => {
736
+ const agent = context.agent;
737
+ if (agent === void 0) return "";
738
+ const style = runtime.effectiveStyle(agent.session.id);
739
+ if (style === void 0 || !style.keepCodingInstructions) return "";
740
+ return runtime.promptText(agent.session.id);
741
+ }
742
+ });
743
+ ctx.on("system-prompt/assemble", async (_assembly, context, next) => {
744
+ const out = await next();
680
745
  const agent = context.agent;
681
- if (agent === void 0) return "";
746
+ if (agent === void 0) return out;
682
747
  const style = runtime.effectiveStyle(agent.session.id);
683
- if (style === void 0 || !style.keepCodingInstructions) return "";
684
- return runtime.promptText(agent.session.id);
685
- }
686
- });
687
- ctx.on("system-prompt/assemble", async (_assembly, context, next) => {
688
- const out = await next();
689
- const agent = context.agent;
690
- if (agent === void 0) return out;
691
- const style = runtime.effectiveStyle(agent.session.id);
692
- if (style === void 0 || style.keepCodingInstructions) return out;
693
- return {
694
- ...out,
695
- sections: [{
696
- name: STYLE_SECTION_NAME,
697
- text: runtime.promptText(agent.session.id)
698
- }]
699
- };
700
- });
748
+ if (style === void 0 || style.keepCodingInstructions) return out;
749
+ return {
750
+ ...out,
751
+ sections: [{
752
+ name: STYLE_SECTION_NAME,
753
+ text: runtime.promptText(agent.session.id)
754
+ }]
755
+ };
756
+ });
757
+ }
701
758
  ctx.inject(["commands"], (commandCtx) => {
702
759
  commandCtx.commands.register({
703
760
  name: STYLE_COMMAND,
@@ -794,12 +851,12 @@ async function apply(ctx, config) {
794
851
  commandCtx.commands.register({
795
852
  name: "export",
796
853
  description: "Export this session as Markdown or HTML (renderer-aware)",
797
- input: { hint: "[markdown|html] [--renderer=<id>]" },
798
- handler: async ({ agent, rawInput }) => {
854
+ input: { hint: "[markdown|html] [--renderer=<id>] [--save <path>]" },
855
+ handler: async ({ agent, rawInput, signal }) => {
799
856
  const input = parseExportInput(rawInput);
800
857
  if (input.kind === "error") return {
801
858
  kind: "error",
802
- text: "usage: /export [markdown|html] [--renderer=<id>]"
859
+ text: "usage: /export [markdown|html] [--renderer=<id>] [--save <path>]"
803
860
  };
804
861
  const lines = conversationLines(agent.session.events);
805
862
  const rules = input.renderer === void 0 ? [...effectiveRules] : [{
@@ -807,28 +864,57 @@ async function apply(ctx, config) {
807
864
  style: input.renderer,
808
865
  priority: 0
809
866
  }];
867
+ const document = renderExport(renderers, lines, input.format, rules);
868
+ if (input.save === void 0) return {
869
+ kind: "success",
870
+ text: document.text
871
+ };
872
+ const saved = await saveExportFile(ctx.get("fs"), ctx.get("approval"), agent, input.save, sanitizeText(document.text), signal);
873
+ if (saved.kind === "error") return {
874
+ kind: "error",
875
+ text: saved.text
876
+ };
810
877
  return {
811
878
  kind: "success",
812
- text: renderExport(renderers, lines, input.format, rules).text
879
+ text: `saved ${input.format} export to ${saved.path}`
813
880
  };
814
881
  }
815
882
  });
816
883
  });
817
884
  }
818
- /** Parse `/export [markdown|html] [--renderer=<id>]` from the raw command input. */
885
+ /** Parse `/export [markdown|html] [--renderer=<id>] [--save <path>]` from the raw command input. */
819
886
  function parseExportInput(rawInput) {
820
887
  const raw = String(rawInput ?? "").trim();
821
888
  const parts = raw === "" ? [] : raw.split(/\s+/);
822
889
  let format = "markdown";
823
890
  let renderer;
824
- for (const part of parts) {
825
- if (part === "markdown" || part === "html") {
826
- format = part;
891
+ let save;
892
+ for (let index = 0; index < parts.length; index += 1) {
893
+ const part = parts[index];
894
+ if (part === void 0) continue;
895
+ if (part === "markdown" || part === "md") {
896
+ format = "markdown";
897
+ continue;
898
+ }
899
+ if (part === "html") {
900
+ format = "html";
901
+ continue;
902
+ }
903
+ const rendererMatch = /^--renderer=([a-z0-9][a-z0-9-]*)$/.exec(part);
904
+ if (rendererMatch !== null) {
905
+ renderer = rendererMatch[1];
827
906
  continue;
828
907
  }
829
- const match = /^--renderer=([a-z0-9][a-z0-9-]*)$/.exec(part);
830
- if (match !== null) {
831
- renderer = match[1];
908
+ const saveInline = /^--save=(.+)$/.exec(part);
909
+ if (saveInline !== null) {
910
+ save = saveInline[1];
911
+ continue;
912
+ }
913
+ if (part === "--save") {
914
+ const next = parts[index + 1];
915
+ if (next === void 0 || next === "") return { kind: "error" };
916
+ save = next;
917
+ index += 1;
832
918
  continue;
833
919
  }
834
920
  return { kind: "error" };
@@ -836,7 +922,69 @@ function parseExportInput(rawInput) {
836
922
  return {
837
923
  kind: "ok",
838
924
  format,
839
- ...renderer === void 0 ? {} : { renderer }
925
+ ...renderer === void 0 ? {} : { renderer },
926
+ ...save === void 0 ? {} : { save }
927
+ };
928
+ }
929
+ /**
930
+ * Write one rendered export document to a workspace path after user approval.
931
+ * Fail-closed: a missing approval service, a rejected/cancelled/unavailable
932
+ * decision, or a throwing approval channel all deny the write; a missing fs
933
+ * service fails loudly with a structured error. The caller already sanitized
934
+ * `content` before this write.
935
+ * @param fs - the fs service (`ctx.get('fs')`), or undefined when none is composed.
936
+ * @param approval - the approval service (`ctx.get('approval')`), or undefined when none is composed.
937
+ * @param agent - the agent whose session the export belongs to (routes the approval).
938
+ * @param path - the workspace path to write.
939
+ * @param content - the sanitized document text.
940
+ * @param signal - the command's abort signal, forwarded to approval and resolve.
941
+ * @returns the written path, or a structured failure.
942
+ */
943
+ async function saveExportFile(fs, approval, agent, path, content, signal) {
944
+ if (approval === void 0) return {
945
+ kind: "error",
946
+ code: "approval-unavailable",
947
+ text: "dsh-output-styles: /export --save requires an approval service (compose @deepseek-ai/dsh-user-approval); nothing was written"
948
+ };
949
+ let outcome;
950
+ try {
951
+ outcome = await approval.request({
952
+ agent,
953
+ toolName: "export",
954
+ reason: `write the exported document to ${path}`,
955
+ ...signal === void 0 ? {} : { signal }
956
+ });
957
+ } catch {
958
+ outcome = "unavailable";
959
+ }
960
+ switch (outcome) {
961
+ case "allowed-once": break;
962
+ case "rejected": return {
963
+ kind: "error",
964
+ code: "approval-denied",
965
+ text: "dsh-output-styles: /export --save was rejected; nothing was written"
966
+ };
967
+ case "cancelled": return {
968
+ kind: "error",
969
+ code: "approval-cancelled",
970
+ text: "dsh-output-styles: /export --save was cancelled; nothing was written"
971
+ };
972
+ default: return {
973
+ kind: "error",
974
+ code: "approval-unavailable",
975
+ text: "dsh-output-styles: /export --save approval is unavailable; nothing was written"
976
+ };
977
+ }
978
+ if (fs === void 0) return {
979
+ kind: "error",
980
+ code: "fs-unavailable",
981
+ text: "dsh-output-styles: /export --save requires an fs service (compose @deepseek-ai/dsh-fs); nothing was written"
982
+ };
983
+ const target = await fs.resolve(path, signal === void 0 ? void 0 : { signal });
984
+ await fs.writeText(target, content);
985
+ return {
986
+ kind: "written",
987
+ path
840
988
  };
841
989
  }
842
990
  //#endregion
@@ -860,4 +1008,4 @@ const name = "dsh-output-styles";
860
1008
  */
861
1009
  const inject = ["systemPrompt", "storageDomain"];
862
1010
  //#endregion
863
- export { Config, DEFAULT_STYLES_DIR, EMPTY_STYLE_STATE, OFF, OUTPUT_STYLE_DOMAIN, OutputStyleRuntime, PACKAGE_NAME, STYLE_COMMAND, STYLE_NAME_RE, STYLE_SECTION_NAME, STYLE_SOURCE, apply, applyStyleEvent, inject, installInvariant, isValidStyleName, loadStyleLibrary, name, parseStyleInput, resolveConfig, styleFoldStateSchema, styleSelectionSchema, styleSelectionViewSchema, truncateStyle };
1011
+ export { CORE_OUTPUT_STYLES_SERVICE, Config, DEFAULT_STYLES_DIR, EMPTY_STYLE_STATE, OFF, OUTPUT_STYLE_DOMAIN, OutputStyleRuntime, PACKAGE_NAME, STYLE_COMMAND, STYLE_NAME_RE, STYLE_SECTION_NAME, STYLE_SOURCE, apply, applyStyleEvent, coexistenceReport, detectCoreOutputStyles, inject, installInvariant, isValidStyleName, loadStyleLibrary, name, parseStyleInput, resolveConfig, styleFoldStateSchema, styleSelectionSchema, styleSelectionViewSchema, truncateStyle };
@@ -0,0 +1,49 @@
1
+ /**
2
+ * Coexistence with a core `outputStyles` capability.
3
+ *
4
+ * If DeepSeek Harness ever ships a first-party `outputStyles` feature, this
5
+ * plugin's system-prompt injection would duplicate it. The coexistence policy
6
+ * detects the core capability through its reserved service seam and, when it
7
+ * is present, degrades to the incremental surface this plugin uniquely adds —
8
+ * runtime hot-switch, per-session/per-tool rules, and `/export` — while
9
+ * leaving prompt injection entirely to the core.
10
+ *
11
+ * Detection is a service probe, not a name guess: a core implementation
12
+ * publishes the `outputStyles` service; when that seam is absent the plugin
13
+ * runs standalone (injecting the style section itself).
14
+ * @module dsh-output-styles/coexist
15
+ */
16
+ import type { Context } from '@deepseek-ai/cordis';
17
+ /** The reserved service name a core `outputStyles` capability publishes. */
18
+ export declare const CORE_OUTPUT_STYLES_SERVICE = "outputStyles";
19
+ /**
20
+ * Probe whether a core `outputStyles` capability is composed.
21
+ * @param ctx - the plugin-scoped context.
22
+ * @returns true when the core `outputStyles` service seam is present.
23
+ */
24
+ export declare function detectCoreOutputStyles(ctx: Context): boolean;
25
+ /** The coexistence mode: standalone owns injection; degraded leaves it to core. */
26
+ export type CoexistenceMode = 'standalone' | 'degraded';
27
+ /** A runnable coexistence check: what this plugin contributes under the current composition. */
28
+ export interface CoexistenceReport {
29
+ /** Whether a core `outputStyles` capability was detected. */
30
+ readonly coreActive: boolean;
31
+ /** The effective coexistence mode. */
32
+ readonly mode: CoexistenceMode;
33
+ /** Whether this plugin injects the style system-prompt section. */
34
+ readonly promptInjection: 'enabled' | 'disabled';
35
+ /** The incremental surfaces this plugin always retains. */
36
+ readonly retained: readonly string[];
37
+ /** What this plugin disabled to avoid duplicating the core. */
38
+ readonly disabled: readonly string[];
39
+ }
40
+ /**
41
+ * Compute the coexistence report for one composition. Respecting the core is
42
+ * the default (`respectCore` true); a deployment can force standalone mode by
43
+ * setting `respectCoreOutputStyles: false`.
44
+ * @param ctx - the plugin-scoped context.
45
+ * @param respectCore - whether to honor a detected core capability (default true).
46
+ * @returns the coexistence report.
47
+ */
48
+ export declare function coexistenceReport(ctx: Context, respectCore?: boolean): CoexistenceReport;
49
+ //# sourceMappingURL=coexist.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"coexist.d.ts","sourceRoot":"","sources":["../../src/coexist.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAA;AAElD,4EAA4E;AAC5E,eAAO,MAAM,0BAA0B,iBAAiB,CAAA;AAExD;;;;GAIG;AACH,wBAAgB,sBAAsB,CAAC,GAAG,EAAE,OAAO,GAAG,OAAO,CAE5D;AAED,mFAAmF;AACnF,MAAM,MAAM,eAAe,GAAG,YAAY,GAAG,UAAU,CAAA;AAEvD,gGAAgG;AAChG,MAAM,WAAW,iBAAiB;IAChC,6DAA6D;IAC7D,QAAQ,CAAC,UAAU,EAAE,OAAO,CAAA;IAC5B,sCAAsC;IACtC,QAAQ,CAAC,IAAI,EAAE,eAAe,CAAA;IAC9B,mEAAmE;IACnE,QAAQ,CAAC,eAAe,EAAE,SAAS,GAAG,UAAU,CAAA;IAChD,2DAA2D;IAC3D,QAAQ,CAAC,QAAQ,EAAE,SAAS,MAAM,EAAE,CAAA;IACpC,+DAA+D;IAC/D,QAAQ,CAAC,QAAQ,EAAE,SAAS,MAAM,EAAE,CAAA;CACrC;AAKD;;;;;;;GAOG;AACH,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,OAAO,EAAE,WAAW,UAAO,GAAG,iBAAiB,CASrF"}
@@ -59,6 +59,13 @@ export interface Config {
59
59
  rules?: StyleRuleConfig[];
60
60
  /** Register the `/export` command (Markdown/HTML session-export, renderer-aware). */
61
61
  enableExport?: boolean;
62
+ /**
63
+ * Honor a detected core `outputStyles` capability: when true (default) and
64
+ * the core service is composed, this plugin skips its own system-prompt
65
+ * injection (hot-switch / rules / export stay active). Set false to always
66
+ * inject regardless.
67
+ */
68
+ respectCoreOutputStyles?: boolean;
62
69
  }
63
70
  /** Configuration after defaults have been resolved. */
64
71
  export interface ResolvedConfig {
@@ -90,6 +97,8 @@ export interface ResolvedConfig {
90
97
  }>;
91
98
  /** Whether the `/export` command registers. */
92
99
  enableExport: boolean;
100
+ /** Whether a detected core `outputStyles` capability disables prompt injection. */
101
+ respectCoreOutputStyles: boolean;
93
102
  }
94
103
  /** Loader-visible configuration schema and defaults. */
95
104
  export declare const Config: z<Config>;
@@ -1 +1 @@
1
- {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/config.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,CAAC,MAAM,0BAA0B,CAAA;AAGxC,gFAAgF;AAChF,MAAM,WAAW,eAAe;IAC9B,iEAAiE;IACjE,KAAK,EAAE;QACL,6CAA6C;QAC7C,IAAI,CAAC,EAAE,MAAM,CAAA;QACb,oCAAoC;QACpC,WAAW,CAAC,EAAE,MAAM,GAAG,UAAU,GAAG,MAAM,CAAA;QAC1C,uEAAuE;QACvE,OAAO,CAAC,EAAE,MAAM,CAAA;KACjB,CAAA;IACD,yFAAyF;IACzF,KAAK,EAAE,MAAM,CAAA;IACb,sEAAsE;IACtE,QAAQ,CAAC,EAAE,MAAM,CAAA;CAClB;AAED,gEAAgE;AAChE,MAAM,WAAW,MAAM;IACrB;;;;;;;;OAQG;IACH,SAAS,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAAA;IAC7B,gGAAgG;IAChG,aAAa,CAAC,EAAE,MAAM,CAAA;IACtB;;;;;OAKG;IACH,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,6GAA6G;IAC7G,UAAU,CAAC,EAAE,OAAO,CAAA;IACpB,4GAA4G;IAC5G,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,sGAAsG;IACtG,gBAAgB,CAAC,EAAE,MAAM,CAAA;IACzB,wEAAwE;IACxE,eAAe,CAAC,EAAE,OAAO,CAAA;IACzB,2EAA2E;IAC3E,WAAW,CAAC,EAAE,OAAO,CAAA;IACrB,0GAA0G;IAC1G,KAAK,CAAC,EAAE,eAAe,EAAE,CAAA;IACzB,qFAAqF;IACrF,YAAY,CAAC,EAAE,OAAO,CAAA;CACvB;AAED,uDAAuD;AACvD,MAAM,WAAW,cAAc;IAC7B,sGAAsG;IACtG,UAAU,EAAE,MAAM,EAAE,CAAA;IACpB,mDAAmD;IACnD,aAAa,EAAE,MAAM,CAAA;IACrB,6EAA6E;IAC7E,YAAY,EAAE,MAAM,CAAA;IACpB,kEAAkE;IAClE,UAAU,EAAE,OAAO,CAAA;IACnB,oEAAoE;IACpE,YAAY,EAAE,MAAM,CAAA;IACpB,+CAA+C;IAC/C,gBAAgB,EAAE,MAAM,CAAA;IACxB,4DAA4D;IAC5D,eAAe,EAAE,OAAO,CAAA;IACxB,yDAAyD;IACzD,WAAW,EAAE,OAAO,CAAA;IACpB,kEAAkE;IAClE,KAAK,EAAE,KAAK,CAAC;QAAE,KAAK,EAAE;YAAE,IAAI,CAAC,EAAE,MAAM,CAAC;YAAC,WAAW,CAAC,EAAE,MAAM,GAAG,UAAU,GAAG,MAAM,CAAC;YAAC,OAAO,CAAC,EAAE,MAAM,CAAA;SAAE,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,CAAC,CAAA;IACzI,+CAA+C;IAC/C,YAAY,EAAE,OAAO,CAAA;CACtB;AAED,wDAAwD;AACxD,eAAO,MAAM,MAAM,EAAE,CAAC,CAAC,MAAM,CAmB3B,CAAA;AAEF;;;;;;;GAOG;AACH,wBAAgB,aAAa,CAAC,MAAM,EAAE,MAAM,EAAE,gBAAgB,EAAE,MAAM,GAAG,cAAc,CAqCtF"}
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/config.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,CAAC,MAAM,0BAA0B,CAAA;AAGxC,gFAAgF;AAChF,MAAM,WAAW,eAAe;IAC9B,iEAAiE;IACjE,KAAK,EAAE;QACL,6CAA6C;QAC7C,IAAI,CAAC,EAAE,MAAM,CAAA;QACb,oCAAoC;QACpC,WAAW,CAAC,EAAE,MAAM,GAAG,UAAU,GAAG,MAAM,CAAA;QAC1C,uEAAuE;QACvE,OAAO,CAAC,EAAE,MAAM,CAAA;KACjB,CAAA;IACD,yFAAyF;IACzF,KAAK,EAAE,MAAM,CAAA;IACb,sEAAsE;IACtE,QAAQ,CAAC,EAAE,MAAM,CAAA;CAClB;AAED,gEAAgE;AAChE,MAAM,WAAW,MAAM;IACrB;;;;;;;;OAQG;IACH,SAAS,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAAA;IAC7B,gGAAgG;IAChG,aAAa,CAAC,EAAE,MAAM,CAAA;IACtB;;;;;OAKG;IACH,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,6GAA6G;IAC7G,UAAU,CAAC,EAAE,OAAO,CAAA;IACpB,4GAA4G;IAC5G,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,sGAAsG;IACtG,gBAAgB,CAAC,EAAE,MAAM,CAAA;IACzB,wEAAwE;IACxE,eAAe,CAAC,EAAE,OAAO,CAAA;IACzB,2EAA2E;IAC3E,WAAW,CAAC,EAAE,OAAO,CAAA;IACrB,0GAA0G;IAC1G,KAAK,CAAC,EAAE,eAAe,EAAE,CAAA;IACzB,qFAAqF;IACrF,YAAY,CAAC,EAAE,OAAO,CAAA;IACtB;;;;;OAKG;IACH,uBAAuB,CAAC,EAAE,OAAO,CAAA;CAClC;AAED,uDAAuD;AACvD,MAAM,WAAW,cAAc;IAC7B,sGAAsG;IACtG,UAAU,EAAE,MAAM,EAAE,CAAA;IACpB,mDAAmD;IACnD,aAAa,EAAE,MAAM,CAAA;IACrB,6EAA6E;IAC7E,YAAY,EAAE,MAAM,CAAA;IACpB,kEAAkE;IAClE,UAAU,EAAE,OAAO,CAAA;IACnB,oEAAoE;IACpE,YAAY,EAAE,MAAM,CAAA;IACpB,+CAA+C;IAC/C,gBAAgB,EAAE,MAAM,CAAA;IACxB,4DAA4D;IAC5D,eAAe,EAAE,OAAO,CAAA;IACxB,yDAAyD;IACzD,WAAW,EAAE,OAAO,CAAA;IACpB,kEAAkE;IAClE,KAAK,EAAE,KAAK,CAAC;QAAE,KAAK,EAAE;YAAE,IAAI,CAAC,EAAE,MAAM,CAAC;YAAC,WAAW,CAAC,EAAE,MAAM,GAAG,UAAU,GAAG,MAAM,CAAC;YAAC,OAAO,CAAC,EAAE,MAAM,CAAA;SAAE,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,CAAC,CAAA;IACzI,+CAA+C;IAC/C,YAAY,EAAE,OAAO,CAAA;IACrB,mFAAmF;IACnF,uBAAuB,EAAE,OAAO,CAAA;CACjC;AAED,wDAAwD;AACxD,eAAO,MAAM,MAAM,EAAE,CAAC,CAAC,MAAM,CAoB3B,CAAA;AAEF;;;;;;;GAOG;AACH,wBAAgB,aAAa,CAAC,MAAM,EAAE,MAAM,EAAE,gBAAgB,EAAE,MAAM,GAAG,cAAc,CAsCtF"}
@@ -18,6 +18,8 @@ export declare const name = "dsh-output-styles";
18
18
  export declare const inject: string[];
19
19
  export { Config, resolveConfig } from './config.js';
20
20
  export type { ResolvedConfig } from './config.js';
21
+ export { CORE_OUTPUT_STYLES_SERVICE, coexistenceReport, detectCoreOutputStyles } from './coexist.js';
22
+ export type { CoexistenceMode, CoexistenceReport } from './coexist.js';
21
23
  export { apply, DEFAULT_STYLES_DIR, OutputStyleRuntime, STYLE_SECTION_NAME } from './runtime.js';
22
24
  export { applyStyleEvent, EMPTY_STYLE_STATE, parseStyleInput, STYLE_COMMAND, } from './style-command.js';
23
25
  export type { StyleFoldState, StyleInput } from './style-command.js';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,6DAA6D;AAC7D,eAAO,MAAM,IAAI,sBAAsB,CAAA;AAEvC;;;;;;GAMG;AACH,eAAO,MAAM,MAAM,UAAoC,CAAA;AAEvD,OAAO,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,aAAa,CAAA;AACnD,YAAY,EAAE,cAAc,EAAE,MAAM,aAAa,CAAA;AACjD,OAAO,EAAE,KAAK,EAAE,kBAAkB,EAAE,kBAAkB,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAA;AAChG,OAAO,EACL,eAAe,EACf,iBAAiB,EACjB,eAAe,EACf,aAAa,GACd,MAAM,oBAAoB,CAAA;AAC3B,YAAY,EAAE,cAAc,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAA;AACpE,OAAO,EACL,gBAAgB,EAChB,gBAAgB,EAChB,aAAa,EACb,aAAa,GACd,MAAM,oBAAoB,CAAA;AAC3B,YAAY,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAA;AACrD,OAAO,EACL,GAAG,EACH,mBAAmB,EACnB,YAAY,EACZ,oBAAoB,EACpB,oBAAoB,EACpB,wBAAwB,GACzB,MAAM,YAAY,CAAA;AACnB,YAAY,EAAE,WAAW,EAAE,cAAc,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAA;AACjF,OAAO,EAAE,gBAAgB,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAA;AAC/D,YAAY,EAAE,cAAc,EAAE,kBAAkB,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAA;AAG3F,mBAAmB,YAAY,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,6DAA6D;AAC7D,eAAO,MAAM,IAAI,sBAAsB,CAAA;AAEvC;;;;;;GAMG;AACH,eAAO,MAAM,MAAM,UAAoC,CAAA;AAEvD,OAAO,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,aAAa,CAAA;AACnD,YAAY,EAAE,cAAc,EAAE,MAAM,aAAa,CAAA;AACjD,OAAO,EAAE,0BAA0B,EAAE,iBAAiB,EAAE,sBAAsB,EAAE,MAAM,cAAc,CAAA;AACpG,YAAY,EAAE,eAAe,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAA;AACtE,OAAO,EAAE,KAAK,EAAE,kBAAkB,EAAE,kBAAkB,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAA;AAChG,OAAO,EACL,eAAe,EACf,iBAAiB,EACjB,eAAe,EACf,aAAa,GACd,MAAM,oBAAoB,CAAA;AAC3B,YAAY,EAAE,cAAc,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAA;AACpE,OAAO,EACL,gBAAgB,EAChB,gBAAgB,EAChB,aAAa,EACb,aAAa,GACd,MAAM,oBAAoB,CAAA;AAC3B,YAAY,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAA;AACrD,OAAO,EACL,GAAG,EACH,mBAAmB,EACnB,YAAY,EACZ,oBAAoB,EACpB,oBAAoB,EACpB,wBAAwB,GACzB,MAAM,YAAY,CAAA;AACnB,YAAY,EAAE,WAAW,EAAE,cAAc,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAA;AACjF,OAAO,EAAE,gBAAgB,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAA;AAC/D,YAAY,EAAE,cAAc,EAAE,kBAAkB,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAA;AAG3F,mBAAmB,YAAY,CAAA"}
@@ -146,10 +146,56 @@ type ExportInput = {
146
146
  kind: 'ok';
147
147
  format: 'markdown' | 'html';
148
148
  renderer?: string;
149
+ save?: string;
149
150
  } | {
150
151
  kind: 'error';
151
152
  };
152
- /** Parse `/export [markdown|html] [--renderer=<id>]` from the raw command input. */
153
+ /** Parse `/export [markdown|html] [--renderer=<id>] [--save <path>]` from the raw command input. */
153
154
  export declare function parseExportInput(rawInput: unknown): ExportInput;
155
+ /** Approval outcome vocabulary, structural (mirrors the approval seam without importing it). */
156
+ type ApprovalOutcome = 'allowed-once' | 'rejected' | 'cancelled' | 'unavailable';
157
+ /** Structural slice of the DSH filesystem service the save path uses. */
158
+ export interface ExportFileSystem {
159
+ resolve(path: string, opts?: {
160
+ cwd?: string;
161
+ signal?: AbortSignal;
162
+ }): Promise<unknown>;
163
+ writeText(target: unknown, content: string): Promise<unknown>;
164
+ }
165
+ /** Structural slice of the DSH approval service the save path uses. */
166
+ export interface ExportApproval {
167
+ request(request: {
168
+ agent: unknown;
169
+ toolName: string;
170
+ reason: string;
171
+ signal?: AbortSignal;
172
+ }): Promise<ApprovalOutcome>;
173
+ }
174
+ /** Error codes of the `/export --save` path; each is a stable machine-readable label. */
175
+ export type ExportSaveErrorCode = 'fs-unavailable' | 'approval-unavailable' | 'approval-denied' | 'approval-cancelled';
176
+ /** Result of a `/export --save` attempt: the written path or a structured failure. */
177
+ export type ExportSaveResult = {
178
+ readonly kind: 'written';
179
+ readonly path: string;
180
+ } | {
181
+ readonly kind: 'error';
182
+ readonly code: ExportSaveErrorCode;
183
+ readonly text: string;
184
+ };
185
+ /**
186
+ * Write one rendered export document to a workspace path after user approval.
187
+ * Fail-closed: a missing approval service, a rejected/cancelled/unavailable
188
+ * decision, or a throwing approval channel all deny the write; a missing fs
189
+ * service fails loudly with a structured error. The caller already sanitized
190
+ * `content` before this write.
191
+ * @param fs - the fs service (`ctx.get('fs')`), or undefined when none is composed.
192
+ * @param approval - the approval service (`ctx.get('approval')`), or undefined when none is composed.
193
+ * @param agent - the agent whose session the export belongs to (routes the approval).
194
+ * @param path - the workspace path to write.
195
+ * @param content - the sanitized document text.
196
+ * @param signal - the command's abort signal, forwarded to approval and resolve.
197
+ * @returns the written path, or a structured failure.
198
+ */
199
+ export declare function saveExportFile(fs: ExportFileSystem | undefined, approval: ExportApproval | undefined, agent: unknown, path: string, content: string, signal?: AbortSignal): Promise<ExportSaveResult>;
154
200
  export {};
155
201
  //# sourceMappingURL=runtime.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"runtime.d.ts","sourceRoot":"","sources":["../../src/runtime.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"runtime.d.ts","sourceRoot":"","sources":["../../src/runtime.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAIH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAA;AAClD,OAAO,KAAK,EAAE,OAAO,EAAE,SAAS,EAAE,MAAM,0BAA0B,CAAA;AAKlE,OAAO,KAAK,EAAE,MAAM,EAA2B,MAAM,iCAAiC,CAAA;AAGtF,OAAO,EAAiB,KAAK,MAAM,EAAE,MAAM,aAAa,CAAA;AAGxD,OAAO,EAAmC,KAAK,WAAW,EAAE,MAAM,oBAAoB,CAAA;AAEtF,OAAO,EAAE,mBAAmB,EAAgE,KAAK,cAAc,EAA2B,MAAM,YAAY,CAAA;AAI5J,kGAAkG;AAClG,eAAO,MAAM,kBAAkB,QAAwD,CAAA;AAEvF,mFAAmF;AACnF,eAAO,MAAM,kBAAkB,2BAA2B,CAAA;AAQ1D;;;;;;GAMG;AACH,qBAAa,kBAAkB;IAqB3B,OAAO,CAAC,QAAQ,CAAC,MAAM;IApBzB,OAAO,CAAC,OAAO,CAAkC;IACjD,OAAO,CAAC,WAAW,CAAyB;IAE5C,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAoC;IAC9D,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAQ;IACrC,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAQ;IACtC,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAQ;IACzC,OAAO,CAAC,cAAc,CAAc;IAEpC,2DAA2D;IAC3D,IAAI,MAAM,IAAI,WAAW,CAAC,MAAM,EAAE,WAAW,CAAC,CAE7C;IAED;;;;OAIG;gBAEgB,MAAM,EAAE,MAAM,CAAC,OAAO,mBAAmB,CAAC,EAC3D,MAAM,EAAE,WAAW,CAAC,MAAM,EAAE,WAAW,CAAC,EACxC,OAAO,EAAE;QACP,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAA;QAC7B,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAA;QAC9B,QAAQ,CAAC,gBAAgB,EAAE,MAAM,CAAA;KAClC;IAWH,qDAAqD;IACrD,IAAI,KAAK,IAAI,SAAS,MAAM,EAAE,CAE7B;IAED,4EAA4E;IAC5E,IAAI,UAAU,IAAI,MAAM,GAAG,SAAS,CAEnC;IAED;;;;OAIG;IACH,MAAM,CAAC,MAAM,EAAE,WAAW,CAAC,MAAM,EAAE,WAAW,CAAC,GAAG,IAAI;IAKtD;;;;OAIG;IACH,iBAAiB,CAAC,GAAG,EAAE,MAAM,MAAM,GAAG,IAAI;IAI1C;;;;OAIG;IACH,GAAG,CAAC,IAAI,EAAE,MAAM,GAAG,WAAW,GAAG,SAAS;IAI1C;;;;OAIG;IACH,YAAY,CAAC,SAAS,EAAE,SAAS,GAAG,cAAc,GAAG,SAAS;IAI9D;;;;;;;OAOG;IACH,cAAc,CAAC,SAAS,EAAE,SAAS,GAAG,WAAW,GAAG,SAAS;IAO7D;;;;OAIG;IACH,WAAW,CAAC,SAAS,EAAE,SAAS,GAAG,MAAM;IAIzC;;;;;;OAMG;IACH,UAAU,CAAC,SAAS,EAAE,SAAS,GAAG,MAAM;IAOxC;;;;;OAKG;IACH,QAAQ,CAAC,SAAS,EAAE,SAAS,GAAG,MAAM;IAUtC;;;;;OAKG;IACH,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM;IAItC;;;;;;OAMG;IACG,MAAM,CAAC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAO3D;;;;OAIG;IACG,OAAO,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC;IAI9C;;;OAGG;IACG,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;CAG7B;AAmBD;;;;;;;;;GASG;AACH,wBAAsB,KAAK,CAAC,GAAG,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CA8SvE;AAED,mCAAmC;AACnC,KAAK,WAAW,GAAG;IACjB,IAAI,EAAE,IAAI,CAAA;IACV,MAAM,EAAE,UAAU,GAAG,MAAM,CAAA;IAC3B,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,IAAI,CAAC,EAAE,MAAM,CAAA;CACd,GAAG;IAAE,IAAI,EAAE,OAAO,CAAA;CAAE,CAAA;AAErB,oGAAoG;AACpG,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,OAAO,GAAG,WAAW,CA0C/D;AAED,gGAAgG;AAChG,KAAK,eAAe,GAAG,cAAc,GAAG,UAAU,GAAG,WAAW,GAAG,aAAa,CAAA;AAEhF,yEAAyE;AACzE,MAAM,WAAW,gBAAgB;IAC/B,OAAO,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE;QAAE,GAAG,CAAC,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,WAAW,CAAA;KAAE,GAAG,OAAO,CAAC,OAAO,CAAC,CAAA;IACtF,SAAS,CAAC,MAAM,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAA;CAC9D;AAED,uEAAuE;AACvE,MAAM,WAAW,cAAc;IAC7B,OAAO,CAAC,OAAO,EAAE;QAAE,KAAK,EAAE,OAAO,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,WAAW,CAAA;KAAE,GAAG,OAAO,CAAC,eAAe,CAAC,CAAA;CACvH;AAED,yFAAyF;AACzF,MAAM,MAAM,mBAAmB,GAAG,gBAAgB,GAAG,sBAAsB,GAAG,iBAAiB,GAAG,oBAAoB,CAAA;AAEtH,sFAAsF;AACtF,MAAM,MAAM,gBAAgB,GACxB;IAAE,QAAQ,CAAC,IAAI,EAAE,SAAS,CAAC;IAAC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GACnD;IAAE,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC;IAAC,QAAQ,CAAC,IAAI,EAAE,mBAAmB,CAAC;IAAC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAAA;AAEzF;;;;;;;;;;;;;GAaG;AACH,wBAAsB,cAAc,CAClC,EAAE,EAAE,gBAAgB,GAAG,SAAS,EAChC,QAAQ,EAAE,cAAc,GAAG,SAAS,EACpC,KAAK,EAAE,OAAO,EACd,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,MAAM,EACf,MAAM,CAAC,EAAE,WAAW,GACnB,OAAO,CAAC,gBAAgB,CAAC,CAoD3B"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-output-styles",
3
- "version": "0.4.3",
3
+ "version": "0.6.0",
4
4
  "description": "Claude Code outputStyles-equivalent runtime output-style switching for DeepSeek Harness",
5
5
  "type": "module",
6
6
  "sideEffects": false,
@@ -17,6 +17,7 @@
17
17
  "publishConfig": {
18
18
  "access": "public"
19
19
  },
20
+ "packageManager": "pnpm@11.7.0",
20
21
  "engines": {
21
22
  "node": "^22.19.0 || >=24.0.0"
22
23
  },
@@ -99,6 +100,7 @@
99
100
  },
100
101
  "permissions": [
101
102
  "fs:read",
103
+ "fs:write",
102
104
  "fs:watch",
103
105
  "storage:read",
104
106
  "storage:write",
@@ -131,13 +133,21 @@
131
133
  },
132
134
  "peerDependencies": {
133
135
  "@deepseek-ai/cordis": "^4.0.1",
136
+ "@deepseek-ai/dsh-fs": ">=0.1.0-rc.8 <0.2.0",
134
137
  "@deepseek-ai/dsh-session": ">=0.1.0-rc.8 <0.2.0",
135
138
  "@deepseek-ai/dsh-session-projection": ">=0.1.1-rc.2 <0.2.0",
136
139
  "@deepseek-ai/dsh-settings": ">=0.1.0-rc.8 <0.2.0",
137
140
  "@deepseek-ai/dsh-storage-domain": ">=0.1.0-rc.8 <0.2.0",
141
+ "@deepseek-ai/dsh-user-approval": ">=0.1.0-rc.8 <0.2.0",
138
142
  "@deepseek-ai/schemastery": "^3.18.1"
139
143
  },
140
144
  "peerDependenciesMeta": {
145
+ "@deepseek-ai/dsh-fs": {
146
+ "optional": true
147
+ },
148
+ "@deepseek-ai/dsh-user-approval": {
149
+ "optional": true
150
+ },
141
151
  "@deepseek-ai/dsh-settings": {
142
152
  "optional": true
143
153
  },
package/src/coexist.ts ADDED
@@ -0,0 +1,68 @@
1
+ /**
2
+ * Coexistence with a core `outputStyles` capability.
3
+ *
4
+ * If DeepSeek Harness ever ships a first-party `outputStyles` feature, this
5
+ * plugin's system-prompt injection would duplicate it. The coexistence policy
6
+ * detects the core capability through its reserved service seam and, when it
7
+ * is present, degrades to the incremental surface this plugin uniquely adds —
8
+ * runtime hot-switch, per-session/per-tool rules, and `/export` — while
9
+ * leaving prompt injection entirely to the core.
10
+ *
11
+ * Detection is a service probe, not a name guess: a core implementation
12
+ * publishes the `outputStyles` service; when that seam is absent the plugin
13
+ * runs standalone (injecting the style section itself).
14
+ * @module dsh-output-styles/coexist
15
+ */
16
+
17
+ import type { Context } from '@deepseek-ai/cordis'
18
+
19
+ /** The reserved service name a core `outputStyles` capability publishes. */
20
+ export const CORE_OUTPUT_STYLES_SERVICE = 'outputStyles'
21
+
22
+ /**
23
+ * Probe whether a core `outputStyles` capability is composed.
24
+ * @param ctx - the plugin-scoped context.
25
+ * @returns true when the core `outputStyles` service seam is present.
26
+ */
27
+ export function detectCoreOutputStyles(ctx: Context): boolean {
28
+ return ctx.get(CORE_OUTPUT_STYLES_SERVICE) !== undefined
29
+ }
30
+
31
+ /** The coexistence mode: standalone owns injection; degraded leaves it to core. */
32
+ export type CoexistenceMode = 'standalone' | 'degraded'
33
+
34
+ /** A runnable coexistence check: what this plugin contributes under the current composition. */
35
+ export interface CoexistenceReport {
36
+ /** Whether a core `outputStyles` capability was detected. */
37
+ readonly coreActive: boolean
38
+ /** The effective coexistence mode. */
39
+ readonly mode: CoexistenceMode
40
+ /** Whether this plugin injects the style system-prompt section. */
41
+ readonly promptInjection: 'enabled' | 'disabled'
42
+ /** The incremental surfaces this plugin always retains. */
43
+ readonly retained: readonly string[]
44
+ /** What this plugin disabled to avoid duplicating the core. */
45
+ readonly disabled: readonly string[]
46
+ }
47
+
48
+ /** The incremental surfaces that survive coexistence in either mode. */
49
+ const RETAINED = ['hot-switch', 'rules', 'export'] as const
50
+
51
+ /**
52
+ * Compute the coexistence report for one composition. Respecting the core is
53
+ * the default (`respectCore` true); a deployment can force standalone mode by
54
+ * setting `respectCoreOutputStyles: false`.
55
+ * @param ctx - the plugin-scoped context.
56
+ * @param respectCore - whether to honor a detected core capability (default true).
57
+ * @returns the coexistence report.
58
+ */
59
+ export function coexistenceReport(ctx: Context, respectCore = true): CoexistenceReport {
60
+ const coreActive = respectCore && detectCoreOutputStyles(ctx)
61
+ return {
62
+ coreActive,
63
+ mode: coreActive ? 'degraded' : 'standalone',
64
+ promptInjection: coreActive ? 'disabled' : 'enabled',
65
+ retained: [...RETAINED],
66
+ disabled: coreActive ? ['system-prompt injection'] : [],
67
+ }
68
+ }
package/src/config.ts CHANGED
@@ -63,6 +63,13 @@ export interface Config {
63
63
  rules?: StyleRuleConfig[]
64
64
  /** Register the `/export` command (Markdown/HTML session-export, renderer-aware). */
65
65
  enableExport?: boolean
66
+ /**
67
+ * Honor a detected core `outputStyles` capability: when true (default) and
68
+ * the core service is composed, this plugin skips its own system-prompt
69
+ * injection (hot-switch / rules / export stay active). Set false to always
70
+ * inject regardless.
71
+ */
72
+ respectCoreOutputStyles?: boolean
66
73
  }
67
74
 
68
75
  /** Configuration after defaults have been resolved. */
@@ -87,6 +94,8 @@ export interface ResolvedConfig {
87
94
  rules: Array<{ match: { tool?: string; contentType?: 'text' | 'markdown' | 'html'; session?: string }; style: string; priority: number }>
88
95
  /** Whether the `/export` command registers. */
89
96
  enableExport: boolean
97
+ /** Whether a detected core `outputStyles` capability disables prompt injection. */
98
+ respectCoreOutputStyles: boolean
90
99
  }
91
100
 
92
101
  /** Loader-visible configuration schema and defaults. */
@@ -109,6 +118,7 @@ export const Config: z<Config> = z.object({
109
118
  priority: z.number().required(false),
110
119
  })).default([]),
111
120
  enableExport: z.boolean().default(true),
121
+ respectCoreOutputStyles: z.boolean().default(true),
112
122
  })
113
123
 
114
124
  /**
@@ -155,5 +165,6 @@ export function resolveConfig(config: Config, defaultStylesDir: string): Resolve
155
165
  watchStyles: config.watchStyles ?? true,
156
166
  rules: (config.rules ?? []).map(rule => ({ ...rule, priority: rule.priority ?? 0 })),
157
167
  enableExport: config.enableExport ?? true,
168
+ respectCoreOutputStyles: config.respectCoreOutputStyles ?? true,
158
169
  }
159
170
  }
package/src/index.ts CHANGED
@@ -21,6 +21,8 @@ export const inject = ['systemPrompt', 'storageDomain']
21
21
 
22
22
  export { Config, resolveConfig } from './config.ts'
23
23
  export type { ResolvedConfig } from './config.ts'
24
+ export { CORE_OUTPUT_STYLES_SERVICE, coexistenceReport, detectCoreOutputStyles } from './coexist.ts'
25
+ export type { CoexistenceMode, CoexistenceReport } from './coexist.ts'
24
26
  export { apply, DEFAULT_STYLES_DIR, OutputStyleRuntime, STYLE_SECTION_NAME } from './runtime.ts'
25
27
  export {
26
28
  applyStyleEvent,