@deepseek-harness-tui/dsh-tui 0.8.1 → 0.8.2

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.
Files changed (240) hide show
  1. package/README.md +22 -13
  2. package/dsh-ecosystem-spec/protocols/profile-definitions.d.ts +13 -0
  3. package/dsh-ecosystem-spec/protocols/profile-definitions.js +59 -0
  4. package/dsh-ecosystem-spec/protocols/tui-channel-http.d.ts +15 -0
  5. package/dsh-ecosystem-spec/protocols/tui-channel-http.js +32 -0
  6. package/dsh-ecosystem-spec/protocols/tui-channel.d.ts +59 -0
  7. package/dsh-ecosystem-spec/protocols/tui-channel.js +166 -0
  8. package/dsh-ecosystem-spec/protocols/tui-contributions.d.ts +42 -0
  9. package/dsh-ecosystem-spec/protocols/tui-contributions.js +143 -0
  10. package/dsh-ecosystem-spec/registry/README.md +14 -0
  11. package/dsh-ecosystem-spec/registry/contracts/scene-v1alpha1.json +13 -0
  12. package/dsh-ecosystem-spec/registry/contracts/settings-section-v1alpha1.json +13 -0
  13. package/dsh-ecosystem-spec/registry/contracts/tui-channel-v1alpha1.json +30 -0
  14. package/dsh-ecosystem-spec/registry/contracts/workspace-provider-v1alpha1.json +6 -0
  15. package/{ecosystem-spec → dsh-ecosystem-spec}/registry/permissions-0.1.json +1 -1
  16. package/dsh-ecosystem-spec/registry/registry-0.15.json +149 -0
  17. package/lib/types/command-trees.d.ts +2 -1
  18. package/lib/types/command-trees.d.ts.map +1 -1
  19. package/lib/types/command-trees.js +1 -1
  20. package/lib/types/components/LoadedContextPanel.d.ts +10 -5
  21. package/lib/types/components/LoadedContextPanel.d.ts.map +1 -1
  22. package/lib/types/components/LoadedContextPanel.js +18 -7
  23. package/lib/types/components/LogoV2.d.ts.map +1 -1
  24. package/lib/types/components/LogoV2.js +16 -7
  25. package/lib/types/components/Markdown.d.ts +13 -1
  26. package/lib/types/components/Markdown.d.ts.map +1 -1
  27. package/lib/types/components/Markdown.js +15 -1
  28. package/lib/types/components/MessageList.d.ts +3 -1
  29. package/lib/types/components/MessageList.d.ts.map +1 -1
  30. package/lib/types/components/MessageList.js +90 -9
  31. package/lib/types/components/PromptInput.d.ts.map +1 -1
  32. package/lib/types/components/PromptInput.js +26 -12
  33. package/lib/types/components/StreamingMarkdown.d.ts +2 -8
  34. package/lib/types/components/StreamingMarkdown.d.ts.map +1 -1
  35. package/lib/types/components/StreamingMarkdown.js +51 -9
  36. package/lib/types/components/activityFrames.d.ts +1 -1
  37. package/lib/types/components/activityFrames.d.ts.map +1 -1
  38. package/lib/types/components/activityFrames.js +4 -1
  39. package/lib/types/components/messages/AssistantThinkingMessage.d.ts +5 -1
  40. package/lib/types/components/messages/AssistantThinkingMessage.d.ts.map +1 -1
  41. package/lib/types/components/messages/AssistantThinkingMessage.js +26 -3
  42. package/lib/types/components/messages/AssistantToolUseMessage.d.ts.map +1 -1
  43. package/lib/types/components/messages/AssistantToolUseMessage.js +12 -1
  44. package/lib/types/dsh-adapter/channel.d.ts +31 -4
  45. package/lib/types/dsh-adapter/channel.d.ts.map +1 -1
  46. package/lib/types/dsh-adapter/channel.js +267 -102
  47. package/lib/types/dsh-adapter/childStderr.d.ts.map +1 -1
  48. package/lib/types/dsh-adapter/childStderr.js +10 -0
  49. package/lib/types/dsh-adapter/command-attribution.d.ts.map +1 -1
  50. package/lib/types/dsh-adapter/command-attribution.js +2 -2
  51. package/lib/types/dsh-adapter/command-trees.d.ts +7 -1
  52. package/lib/types/dsh-adapter/command-trees.d.ts.map +1 -1
  53. package/lib/types/dsh-adapter/command-trees.js +70 -20
  54. package/lib/types/dsh-adapter/component-identity.d.ts +5 -0
  55. package/lib/types/dsh-adapter/component-identity.d.ts.map +1 -1
  56. package/lib/types/dsh-adapter/component-identity.js +14 -14
  57. package/lib/types/dsh-adapter/decision-guard.d.ts.map +1 -1
  58. package/lib/types/dsh-adapter/decision-guard.js +10 -16
  59. package/lib/types/dsh-adapter/dialogs.d.ts +2 -3
  60. package/lib/types/dsh-adapter/dialogs.d.ts.map +1 -1
  61. package/lib/types/dsh-adapter/dialogs.js +114 -60
  62. package/lib/types/dsh-adapter/effect-ledger.d.ts +0 -8
  63. package/lib/types/dsh-adapter/effect-ledger.d.ts.map +1 -1
  64. package/lib/types/dsh-adapter/effect-ledger.js +73 -53
  65. package/lib/types/dsh-adapter/extension-events.d.ts.map +1 -1
  66. package/lib/types/dsh-adapter/extension-events.js +39 -1
  67. package/lib/types/dsh-adapter/grants.d.ts.map +1 -1
  68. package/lib/types/dsh-adapter/grants.js +43 -14
  69. package/lib/types/dsh-adapter/host-access.d.ts +37 -0
  70. package/lib/types/dsh-adapter/host-access.d.ts.map +1 -0
  71. package/lib/types/dsh-adapter/host-access.js +652 -0
  72. package/lib/types/dsh-adapter/host-descriptor.d.ts +2 -2
  73. package/lib/types/dsh-adapter/host-descriptor.d.ts.map +1 -1
  74. package/lib/types/dsh-adapter/host-descriptor.js +31 -8
  75. package/lib/types/dsh-adapter/index.d.ts +4 -0
  76. package/lib/types/dsh-adapter/index.d.ts.map +1 -1
  77. package/lib/types/dsh-adapter/index.js +1 -0
  78. package/lib/types/dsh-adapter/message-observer.d.ts +10 -15
  79. package/lib/types/dsh-adapter/message-observer.d.ts.map +1 -1
  80. package/lib/types/dsh-adapter/message-observer.js +114 -70
  81. package/lib/types/dsh-adapter/plugin-host.d.ts +39 -6
  82. package/lib/types/dsh-adapter/plugin-host.d.ts.map +1 -1
  83. package/lib/types/dsh-adapter/plugin-host.js +139 -44
  84. package/lib/types/dsh-adapter/plugin-storage.d.ts +0 -5
  85. package/lib/types/dsh-adapter/plugin-storage.d.ts.map +1 -1
  86. package/lib/types/dsh-adapter/plugin-storage.js +48 -36
  87. package/lib/types/dsh-adapter/plugin.d.ts.map +1 -1
  88. package/lib/types/dsh-adapter/plugin.js +80 -16
  89. package/lib/types/dsh-adapter/promptDebug.d.ts +0 -6
  90. package/lib/types/dsh-adapter/promptDebug.d.ts.map +1 -1
  91. package/lib/types/dsh-adapter/promptDebug.js +13 -4
  92. package/lib/types/dsh-adapter/renderers.d.ts +6 -14
  93. package/lib/types/dsh-adapter/renderers.d.ts.map +1 -1
  94. package/lib/types/dsh-adapter/renderers.js +81 -46
  95. package/lib/types/dsh-adapter/scenes.d.ts +8 -4
  96. package/lib/types/dsh-adapter/scenes.d.ts.map +1 -1
  97. package/lib/types/dsh-adapter/scenes.js +136 -37
  98. package/lib/types/dsh-adapter/settings-sections.d.ts +7 -3
  99. package/lib/types/dsh-adapter/settings-sections.d.ts.map +1 -1
  100. package/lib/types/dsh-adapter/settings-sections.js +106 -26
  101. package/lib/types/dsh-adapter/shortcuts.d.ts +8 -10
  102. package/lib/types/dsh-adapter/shortcuts.d.ts.map +1 -1
  103. package/lib/types/dsh-adapter/shortcuts.js +108 -36
  104. package/lib/types/dsh-adapter/status.d.ts +6 -7
  105. package/lib/types/dsh-adapter/status.d.ts.map +1 -1
  106. package/lib/types/dsh-adapter/status.js +88 -22
  107. package/lib/types/dsh-adapter/workspaces.d.ts +16 -5
  108. package/lib/types/dsh-adapter/workspaces.d.ts.map +1 -1
  109. package/lib/types/dsh-adapter/workspaces.js +287 -170
  110. package/lib/types/extensions.d.ts +10 -5
  111. package/lib/types/extensions.d.ts.map +1 -1
  112. package/lib/types/extensions.js +11 -5
  113. package/lib/types/force-production-react.d.ts +1 -0
  114. package/lib/types/force-production-react.d.ts.map +1 -0
  115. package/lib/types/force-production-react.js +12 -0
  116. package/lib/types/i18n.d.ts +14 -6
  117. package/lib/types/i18n.d.ts.map +1 -1
  118. package/lib/types/i18n.js +4 -2
  119. package/lib/types/index.d.ts +1 -0
  120. package/lib/types/index.d.ts.map +1 -1
  121. package/lib/types/index.js +1 -0
  122. package/lib/types/ink/components/App.d.ts +2 -0
  123. package/lib/types/ink/components/App.d.ts.map +1 -1
  124. package/lib/types/ink/components/App.js +4 -0
  125. package/lib/types/ink/ink.d.ts +20 -3
  126. package/lib/types/ink/ink.d.ts.map +1 -1
  127. package/lib/types/ink/ink.js +57 -5
  128. package/lib/types/ink/log-update.d.ts.map +1 -1
  129. package/lib/types/ink/log-update.js +128 -9
  130. package/lib/types/ink/output.d.ts +28 -2
  131. package/lib/types/ink/output.d.ts.map +1 -1
  132. package/lib/types/ink/output.js +158 -12
  133. package/lib/types/ink/screen.d.ts +39 -0
  134. package/lib/types/ink/screen.d.ts.map +1 -1
  135. package/lib/types/ink/screen.js +117 -0
  136. package/lib/types/ink/terminal.d.ts.map +1 -1
  137. package/lib/types/ink/terminal.js +10 -2
  138. package/lib/types/ink/wrapAnsi.d.ts +1 -1
  139. package/lib/types/ink/wrapAnsi.d.ts.map +1 -1
  140. package/lib/types/ink/wrapAnsi.js +29 -2
  141. package/lib/types/plugin-host.d.ts +13 -11
  142. package/lib/types/plugin-host.d.ts.map +1 -1
  143. package/lib/types/plugin-host.js +16 -16
  144. package/lib/types/plugin-spec/registry.d.ts +2 -2
  145. package/lib/types/plugin-spec/registry.d.ts.map +1 -1
  146. package/lib/types/plugin-spec/registry.js +113 -18
  147. package/lib/types/plugin-spec/tui-extension.d.ts.map +1 -1
  148. package/lib/types/plugin-spec/tui-extension.js +2 -0
  149. package/lib/types/plugin-spec/validate.d.ts.map +1 -1
  150. package/lib/types/plugin-spec/validate.js +20 -1
  151. package/lib/types/scenes.d.ts +2 -1
  152. package/lib/types/scenes.d.ts.map +1 -1
  153. package/lib/types/scenes.js +1 -1
  154. package/lib/types/screens/Chat.d.ts +3 -3
  155. package/lib/types/screens/Chat.d.ts.map +1 -1
  156. package/lib/types/screens/Chat.js +38 -17
  157. package/lib/types/screens/SessionBrowser.d.ts.map +1 -1
  158. package/lib/types/screens/SessionBrowser.js +10 -3
  159. package/lib/types/sessions/view.d.ts +3 -2
  160. package/lib/types/sessions/view.d.ts.map +1 -1
  161. package/lib/types/sessions/view.js +18 -0
  162. package/lib/types/settings-sections.d.ts +2 -1
  163. package/lib/types/settings-sections.d.ts.map +1 -1
  164. package/lib/types/settings-sections.js +1 -1
  165. package/lib/types/utils/execFileNoThrow.d.ts.map +1 -1
  166. package/lib/types/utils/execFileNoThrow.js +3 -0
  167. package/lib/types/workspaces.d.ts +2 -1
  168. package/lib/types/workspaces.d.ts.map +1 -1
  169. package/lib/types/workspaces.js +1 -1
  170. package/node_modules/@dsh-std/command/CHANGELOG.md +8 -0
  171. package/node_modules/@dsh-std/command/package.json +1 -1
  172. package/node_modules/@dsh-std/connection/CHANGELOG.md +8 -0
  173. package/node_modules/@dsh-std/connection/lib/memory.d.ts +1 -1
  174. package/node_modules/@dsh-std/connection/lib/memory.d.ts.map +1 -1
  175. package/node_modules/@dsh-std/connection/lib/memory.js +1 -0
  176. package/node_modules/@dsh-std/connection/lib/memory.js.map +1 -1
  177. package/node_modules/@dsh-std/connection/package.json +1 -1
  178. package/node_modules/@dsh-std/core/CHANGELOG.md +8 -0
  179. package/node_modules/@dsh-std/core/package.json +1 -1
  180. package/node_modules/@dsh-std/manifest/CHANGELOG.md +9 -0
  181. package/node_modules/@dsh-std/manifest/package.json +1 -1
  182. package/node_modules/@dsh-std/messages/CHANGELOG.md +8 -0
  183. package/node_modules/@dsh-std/messages/package.json +1 -1
  184. package/node_modules/@dsh-std/presentation/CHANGELOG.md +9 -0
  185. package/node_modules/@dsh-std/presentation/README.md +2 -1
  186. package/node_modules/@dsh-std/presentation/README.zh.md +2 -1
  187. package/node_modules/@dsh-std/presentation/lib/callback.d.ts +2 -0
  188. package/node_modules/@dsh-std/presentation/lib/callback.js +179 -0
  189. package/node_modules/@dsh-std/presentation/lib/callback.js.map +1 -0
  190. package/node_modules/@dsh-std/presentation/lib/index.d.ts +44 -1
  191. package/node_modules/@dsh-std/presentation/lib/index.d.ts.map +1 -1
  192. package/node_modules/@dsh-std/presentation/lib/index.js +6 -3
  193. package/node_modules/@dsh-std/presentation/lib/index.js.map +1 -1
  194. package/node_modules/@dsh-std/presentation/package.json +7 -3
  195. package/node_modules/@dsh-std/storage/CHANGELOG.md +8 -0
  196. package/node_modules/@dsh-std/storage/package.json +1 -1
  197. package/package.json +10 -5
  198. package/vendor/dsh-std/packages/command/node_modules/@dsh-std/connection/CHANGELOG.md +8 -0
  199. package/vendor/dsh-std/packages/command/node_modules/@dsh-std/connection/lib/memory.d.ts +1 -1
  200. package/vendor/dsh-std/packages/command/node_modules/@dsh-std/connection/lib/memory.d.ts.map +1 -1
  201. package/vendor/dsh-std/packages/command/node_modules/@dsh-std/connection/lib/memory.js +1 -0
  202. package/vendor/dsh-std/packages/command/node_modules/@dsh-std/connection/lib/memory.js.map +1 -1
  203. package/vendor/dsh-std/packages/command/node_modules/@dsh-std/connection/package.json +1 -1
  204. package/vendor/dsh-std/packages/command/node_modules/@dsh-std/core/CHANGELOG.md +8 -0
  205. package/vendor/dsh-std/packages/command/node_modules/@dsh-std/core/package.json +1 -1
  206. package/vendor/dsh-std/packages/command/node_modules/@dsh-std/manifest/CHANGELOG.md +9 -0
  207. package/vendor/dsh-std/packages/command/node_modules/@dsh-std/manifest/package.json +1 -1
  208. package/vendor/dsh-std/packages/command/node_modules/@dsh-std/presentation/CHANGELOG.md +9 -0
  209. package/vendor/dsh-std/packages/command/node_modules/@dsh-std/presentation/README.md +2 -1
  210. package/vendor/dsh-std/packages/command/node_modules/@dsh-std/presentation/README.zh.md +2 -1
  211. package/vendor/dsh-std/packages/command/node_modules/@dsh-std/presentation/lib/callback.d.ts +2 -0
  212. package/vendor/dsh-std/packages/command/node_modules/@dsh-std/presentation/lib/callback.js +179 -0
  213. package/vendor/dsh-std/packages/command/node_modules/@dsh-std/presentation/lib/callback.js.map +1 -0
  214. package/vendor/dsh-std/packages/command/node_modules/@dsh-std/presentation/lib/index.d.ts +44 -1
  215. package/vendor/dsh-std/packages/command/node_modules/@dsh-std/presentation/lib/index.d.ts.map +1 -1
  216. package/vendor/dsh-std/packages/command/node_modules/@dsh-std/presentation/lib/index.js +6 -3
  217. package/vendor/dsh-std/packages/command/node_modules/@dsh-std/presentation/lib/index.js.map +1 -1
  218. package/vendor/dsh-std/packages/command/node_modules/@dsh-std/presentation/package.json +7 -3
  219. package/vendor/dsh-std/packages/connection/node_modules/@dsh-std/core/CHANGELOG.md +8 -0
  220. package/vendor/dsh-std/packages/connection/node_modules/@dsh-std/core/package.json +1 -1
  221. package/vendor/dsh-std/packages/manifest/node_modules/@dsh-std/core/CHANGELOG.md +8 -0
  222. package/vendor/dsh-std/packages/manifest/node_modules/@dsh-std/core/package.json +1 -1
  223. package/vendor/dsh-std/packages/messages/node_modules/@dsh-std/core/CHANGELOG.md +8 -0
  224. package/vendor/dsh-std/packages/messages/node_modules/@dsh-std/core/package.json +1 -1
  225. package/vendor/dsh-std/packages/presentation/node_modules/@dsh-std/connection/CHANGELOG.md +8 -0
  226. package/vendor/dsh-std/packages/presentation/node_modules/@dsh-std/connection/lib/memory.d.ts +1 -1
  227. package/vendor/dsh-std/packages/presentation/node_modules/@dsh-std/connection/lib/memory.d.ts.map +1 -1
  228. package/vendor/dsh-std/packages/presentation/node_modules/@dsh-std/connection/lib/memory.js +1 -0
  229. package/vendor/dsh-std/packages/presentation/node_modules/@dsh-std/connection/lib/memory.js.map +1 -1
  230. package/vendor/dsh-std/packages/presentation/node_modules/@dsh-std/connection/package.json +1 -1
  231. package/vendor/dsh-std/packages/presentation/node_modules/@dsh-std/core/CHANGELOG.md +8 -0
  232. package/vendor/dsh-std/packages/presentation/node_modules/@dsh-std/core/package.json +1 -1
  233. package/vendor/dsh-std/packages/storage/node_modules/@dsh-std/core/CHANGELOG.md +8 -0
  234. package/vendor/dsh-std/packages/storage/node_modules/@dsh-std/core/package.json +1 -1
  235. package/ecosystem-spec/registry/registry-0.15.json +0 -57
  236. /package/{ecosystem-spec → dsh-ecosystem-spec}/registry/contracts/decision-events-v1alpha1.json +0 -0
  237. /package/{ecosystem-spec → dsh-ecosystem-spec}/registry/host-descriptor.tui.example.json +0 -0
  238. /package/{ecosystem-spec → dsh-ecosystem-spec}/schemas/conformance-claim.schema.json +0 -0
  239. /package/{ecosystem-spec → dsh-ecosystem-spec}/schemas/effect-ledger-record.schema.json +0 -0
  240. /package/{ecosystem-spec → dsh-ecosystem-spec}/schemas/host-descriptor.schema.json +0 -0
@@ -41,6 +41,7 @@ import { DATA_DIR } from '../utils/paths.js';
41
41
  import { loadSpecData } from '../plugin-spec/registry.js';
42
42
  import { check } from '../plugin-spec/schema-check.js';
43
43
  import { componentIdentityOf } from './component-identity.js';
44
+ import { compositionRoot, concreteService } from './host-access.js';
44
45
  /** Default ledger file (JSONL, one record per line). */
45
46
  export const EFFECT_LEDGER_FILE = join(DATA_DIR, 'effect-ledger.jsonl');
46
47
  /** Resource kinds recorded by the host's wired emitters. */
@@ -65,26 +66,29 @@ function cleanField(value, max, fallback) {
65
66
  }
66
67
  /** `ctx.tuiEffectLedger` — append-only effect journal (C-060). */
67
68
  export class TuiEffectLedgerRuntime extends Service {
68
- file;
69
- optionsGenerationId;
70
- generationId;
71
- ledgerSchema;
72
- activations = new WeakMap();
73
- nextActivation = 1;
74
- sequence;
75
- schemaWarned = false;
76
69
  constructor(ctx, options = {}) {
77
70
  super(ctx, 'tuiEffectLedger');
78
- this.file = options.file ?? EFFECT_LEDGER_FILE;
71
+ const file = options.file ?? EFFECT_LEDGER_FILE;
79
72
  // The generation is resolved LAZILY (per first record): cordis does not
80
73
  // make sibling services visible to constructors of plugins mounted later
81
74
  // by the same apply(), so a constructor-time probe would always miss the
82
75
  // plugin-host service and fall back to 'unknown-generation'.
83
- this.optionsGenerationId = options.generationId;
76
+ const state = {
77
+ hostContext: compositionRoot(ctx),
78
+ file,
79
+ optionsGenerationId: options.generationId,
80
+ generationId: undefined,
81
+ activations: new WeakMap(),
82
+ nextActivation: 1,
83
+ sequence: 0,
84
+ schemaWarned: false,
85
+ ledgerSchema: undefined,
86
+ };
84
87
  // `'ledgerSchema' in options` lets a caller force-undefined (fail-closed
85
88
  // test seam), same contract as the message observer's envelopeSchema.
86
- this.ledgerSchema = 'ledgerSchema' in options ? options.ledgerSchema : loadSpecData()?.schemas.ledger;
87
- this.sequence = this.resumeSequence();
89
+ state.ledgerSchema = 'ledgerSchema' in options ? options.ledgerSchema : loadSpecData()?.schemas.ledger;
90
+ state.sequence = resumeSequence(file);
91
+ ledgerStates.set(this, state);
88
92
  }
89
93
  /**
90
94
  * Append one record. `identity` is the caller's own context (plugins pass
@@ -92,11 +96,13 @@ export class TuiEffectLedgerRuntime extends Service {
92
96
  * parameter); omitting it records `undeclared`, never a guess.
93
97
  */
94
98
  record(entry, identity) {
99
+ let state;
95
100
  try {
96
- if (this.ledgerSchema === undefined) {
97
- if (!this.schemaWarned) {
98
- this.schemaWarned = true;
99
- this.ctx.logger.warn('dsh-tui: effect ledger schema unavailable — suppressing all ledger writes (fail-closed)');
101
+ state = ledgerStateFor(this);
102
+ if (state.ledgerSchema === undefined) {
103
+ if (!state.schemaWarned) {
104
+ state.schemaWarned = true;
105
+ state.hostContext.logger.warn('dsh-tui: effect ledger schema unavailable — suppressing all ledger writes (fail-closed)');
100
106
  }
101
107
  return;
102
108
  }
@@ -105,7 +111,7 @@ export class TuiEffectLedgerRuntime extends Service {
105
111
  const pluginId = this.pluginIdOf(identity, fiber, verified?.componentId);
106
112
  const record = {
107
113
  ledgerVersion: '0.15',
108
- sequence: this.sequence,
114
+ sequence: state.sequence,
109
115
  timestamp: new Date().toISOString(),
110
116
  pluginId,
111
117
  activationInstance: verified?.activationId ?? this.activationOf(fiber, pluginId),
@@ -135,54 +141,34 @@ export class TuiEffectLedgerRuntime extends Service {
135
141
  // schema is DROPPED, not written (the schema's additionalProperties:
136
142
  // false is the structural secret ban).
137
143
  try {
138
- check(record, this.ledgerSchema, this.ledgerSchema);
144
+ check(record, state.ledgerSchema, state.ledgerSchema);
139
145
  }
140
146
  catch (error) {
141
- this.ctx.logger.warn(`dsh-tui: effect ledger record dropped (schema: ${error instanceof Error ? error.message : String(error)})`);
147
+ state.hostContext.logger.warn(`dsh-tui: effect ledger record dropped (schema: ${error instanceof Error ? error.message : String(error)})`);
142
148
  return;
143
149
  }
144
- mkdirSync(dirname(this.file), { recursive: true, mode: 0o700 });
145
- appendFileSync(this.file, `${JSON.stringify(record)}\n`, { mode: 0o600 });
146
- this.sequence += 1;
150
+ mkdirSync(dirname(state.file), { recursive: true, mode: 0o700 });
151
+ appendFileSync(state.file, `${JSON.stringify(record)}\n`, { mode: 0o600 });
152
+ state.sequence += 1;
147
153
  }
148
154
  catch (error) {
149
- this.ctx.logger.warn(`dsh-tui: effect ledger write failed: %o`, error);
155
+ ;
156
+ (state?.hostContext ?? this.ctx).logger.warn('dsh-tui: effect ledger write failed');
150
157
  }
151
158
  }
152
159
  /** Runtime generation (C-050): options override, else the plugin-host
153
160
  * service's id — resolved on first record and cached (the host row mounts
154
161
  * before any caller can record). */
155
162
  generation() {
156
- if (this.optionsGenerationId !== undefined)
157
- return this.optionsGenerationId;
158
- this.generationId ??= this.ctx.get('tuiPluginHost')?.generationId ?? 'unknown-generation';
159
- return this.generationId;
163
+ const state = ledgerStateFor(this);
164
+ if (state.optionsGenerationId !== undefined)
165
+ return state.optionsGenerationId;
166
+ state.generationId ??= state.hostContext.get('tuiPluginHost')?.generationId ?? 'unknown-generation';
167
+ return state.generationId;
160
168
  }
161
169
  /** Continue numbering after the existing file's max sequence (restart-safe). */
162
170
  resumeSequence() {
163
- let text;
164
- try {
165
- text = readFileSync(this.file, 'utf8');
166
- }
167
- catch {
168
- return 0; // missing file = fresh ledger, not corruption
169
- }
170
- let max = -1;
171
- for (const line of text.split('\n')) {
172
- if (line.trim() === '')
173
- continue;
174
- try {
175
- const parsed = JSON.parse(line);
176
- const sequence = parsed.sequence;
177
- if (typeof sequence === 'number' && Number.isInteger(sequence) && sequence > max)
178
- max = sequence;
179
- }
180
- catch {
181
- // Corrupt line: skip it (never rewritten — the bytes stay for manual
182
- // recovery, same posture as plugin-storage).
183
- }
184
- }
185
- return max + 1;
171
+ return resumeSequence(ledgerStateFor(this).file);
186
172
  }
187
173
  fiberOf(identity) {
188
174
  if (identity === undefined)
@@ -220,12 +206,46 @@ export class TuiEffectLedgerRuntime extends Service {
220
206
  // is exact; per-fiber ids only matter for plugin fibers.
221
207
  if (fiber === undefined || pluginId === 'host' || pluginId === 'undeclared')
222
208
  return pluginId;
223
- let activation = this.activations.get(fiber);
209
+ const state = ledgerStateFor(this);
210
+ let activation = state.activations.get(fiber);
224
211
  if (activation === undefined) {
225
- activation = `activation-${this.nextActivation++}`;
226
- this.activations.set(fiber, activation);
212
+ activation = `activation-${state.nextActivation++}`;
213
+ state.activations.set(fiber, activation);
227
214
  }
228
215
  return activation;
229
216
  }
230
217
  }
218
+ const ledgerStates = new WeakMap();
219
+ function ledgerStateFor(runtime) {
220
+ const state = ledgerStates.get(concreteService(runtime));
221
+ if (state === undefined)
222
+ throw new Error('tuiEffectLedger host state is unavailable');
223
+ return state;
224
+ }
225
+ /** Continue numbering after the existing file's max sequence (restart-safe). */
226
+ function resumeSequence(file) {
227
+ let text;
228
+ try {
229
+ text = readFileSync(file, 'utf8');
230
+ }
231
+ catch {
232
+ return 0; // missing file = fresh ledger, not corruption
233
+ }
234
+ let max = -1;
235
+ for (const line of text.split('\n')) {
236
+ if (line.trim() === '')
237
+ continue;
238
+ try {
239
+ const parsed = JSON.parse(line);
240
+ const sequence = parsed.sequence;
241
+ if (typeof sequence === 'number' && Number.isInteger(sequence) && sequence > max)
242
+ max = sequence;
243
+ }
244
+ catch {
245
+ // Corrupt line: skip it (never rewritten — the bytes stay for manual
246
+ // recovery, same posture as plugin-storage).
247
+ }
248
+ }
249
+ return max + 1;
250
+ }
231
251
  export default TuiEffectLedgerRuntime;
@@ -1 +1 @@
1
- {"version":3,"file":"extension-events.d.ts","sourceRoot":"","sources":["../../../src/dsh-adapter/extension-events.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"extension-events.d.ts","sourceRoot":"","sources":["../../../src/dsh-adapter/extension-events.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAA;AAYlD;;wEAEwE;AACxE,eAAO,MAAM,2BAA2B,OAAO,CAAA;AAC/C,eAAO,MAAM,yBAAyB,OAAO,CAAA;AA4C7C;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAsB,mBAAmB,CAAC,CAAC,EACzC,GAAG,EAAE,OAAO,EACZ,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAChC,SAAS,EAAE,CAAC,MAAM,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,KAAK,CAAC,GAAG,SAAS,GAC1E,OAAO,CAAC,CAAC,GAAG,SAAS,CAAC,CA4DxB;AAED;uEACuE;AACvE,wBAAsB,uBAAuB,CAC3C,GAAG,EAAE,OAAO,EACZ,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC/B,OAAO,CAAC,IAAI,CAAC,CAwCf;AAED;;;kDAGkD;AAClD,wBAAgB,uBAAuB,CACrC,MAAM,EAAE,OAAO,EACf,IAAI,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,GAC3B;IAAE,MAAM,EAAE,IAAI,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,SAAS,CAQ/C;AAED,mDAAmD;AACnD,MAAM,WAAW,kBAAkB;IACjC,uDAAuD;IACvD,SAAS,EAAE,MAAM,CAAA;IACjB,wCAAwC;IACxC,GAAG,EAAE,MAAM,CAAA;CACZ;AAED;;;;;GAKG;AACH,MAAM,WAAW,aAAc,SAAQ,kBAAkB;IACvD,iCAAiC;IACjC,IAAI,EAAE,MAAM,CAAA;IACZ,2EAA2E;IAC3E,QAAQ,EAAE,UAAU,GAAG,OAAO,CAAA;CAC/B;AAED,MAAM,MAAM,gBAAgB;AAC1B,6DAA6D;AAC3D;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE;AAClB;+EAC+E;GAC7E;IAAE,OAAO,EAAE,IAAI,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAA;CAAE;AACpC,kEAAkE;GAChE;IAAE,MAAM,EAAE,IAAI,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAA;CAAE;AACnC,gDAAgD;GAC9C,SAAS,CAAA;AAEb;;;;GAIG;AACH,MAAM,WAAW,oBAAqB,SAAQ,kBAAkB;IAC9D,kCAAkC;IAClC,IAAI,EAAE,MAAM,CAAA;IACZ,+CAA+C;IAC/C,GAAG,EAAE,MAAM,CAAA;CACZ;AAED,6EAA6E;AAC7E,MAAM,WAAW,aAAa;IAC5B,4DAA4D;IAC5D,EAAE,EAAE,MAAM,CAAA;IACV,gDAAgD;IAChD,KAAK,EAAE,MAAM,CAAA;IACb,mDAAmD;IACnD,WAAW,CAAC,EAAE,MAAM,CAAA;CACrB;AAED,MAAM,MAAM,uBAAuB;AACjC,2EAA2E;AACzE;IAAE,MAAM,EAAE,IAAI,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAA;CAAE;AACnC;0DAC0D;GACxD;IAAE,KAAK,EAAE,SAAS,aAAa,EAAE,CAAA;CAAE;AACrC,oEAAoE;GAClE,SAAS,CAAA;AAEb;;;;GAIG;AACH,MAAM,WAAW,kBAAmB,SAAQ,kBAAkB;IAC5D,mEAAmE;IACnE,IAAI,EAAE,MAAM,CAAA;IACZ;oCACgC;IAChC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAA;IACnB,mEAAmE;IACnE,WAAW,EAAE,MAAM,CAAA;IACnB,6CAA6C;IAC7C,eAAe,EAAE,MAAM,CAAA;IACvB,2DAA2D;IAC3D,cAAc,EAAE,MAAM,CAAA;CACvB;AAED;;;GAGG;AACH,MAAM,WAAW,qBAAsB,SAAQ,kBAAkB;IAC/D,IAAI,EAAE,KAAK,GAAG,QAAQ,CAAA;IACtB,eAAe,CAAC,EAAE,MAAM,CAAA;CACzB;AAED,MAAM,MAAM,wBAAwB;AAClC;4BAC4B;AAC1B;IAAE,MAAM,EAAE,IAAI,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAA;CAAE,GACjC,SAAS,CAAA;AAEb;;;;;GAKG;AACH,MAAM,WAAW,uBAAwB,SAAQ,kBAAkB;IACjE,IAAI,EAAE,KAAK,GAAG,QAAQ,GAAG,QAAQ,CAAA;IACjC,uCAAuC;IACvC,SAAS,EAAE,MAAM,CAAA;IACjB,iBAAiB,CAAC,EAAE,MAAM,CAAA;CAC3B;AAED,2CAA2C;AAC3C,MAAM,WAAW,eAAgB,SAAQ,kBAAkB;CAAG;AAE9D,MAAM,MAAM,kBAAkB;AAC5B,qEAAqE;AACnE;IAAE,MAAM,EAAE,IAAI,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAA;CAAE,GACjC,SAAS,CAAA;AAEb,OAAO,QAAQ,qBAAqB,CAAC;IACnC,UAAU,MAAM;QACd,WAAW,CAAC,KAAK,EAAE,aAAa,GAAG,gBAAgB,GAAG,OAAO,CAAC,gBAAgB,CAAC,CAAA;QAC/E,mBAAmB,CAAC,KAAK,EAAE,oBAAoB,GAAG,uBAAuB,GAAG,OAAO,CAAC,uBAAuB,CAAC,CAAA;QAC5G,iBAAiB,CAAC,KAAK,EAAE,kBAAkB,GAAG,MAAM,GAAG,SAAS,GAAG,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAAA;QAC9F,oBAAoB,CAAC,KAAK,EAAE,qBAAqB,GAAG,wBAAwB,GAAG,OAAO,CAAC,wBAAwB,CAAC,CAAA;QAChH,sBAAsB,CAAC,KAAK,EAAE,uBAAuB,GAAG,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;QAC5E,aAAa,CAAC,KAAK,EAAE,eAAe,GAAG,kBAAkB,GAAG,OAAO,CAAC,kBAAkB,CAAC,CAAA;KACxF;CACF"}
@@ -151,7 +151,45 @@ export async function dispatchTuiDecision(ctx, name, payload, normalize) {
151
151
  /** Fire a non-veto DecisionEvents point with the same bounded, isolated
152
152
  * semantics. Notifications deliberately ignore every return value. */
153
153
  export async function dispatchTuiNotification(ctx, name, payload) {
154
- await dispatchTuiDecision(ctx, name, payload, () => undefined);
154
+ const sessionId = typeof payload.sessionId === 'string' ? payload.sessionId : undefined;
155
+ const scope = sessionId === undefined ? undefined : `session:${sessionId}`;
156
+ const listeners = decisionHandlersOf(ctx, name, scope);
157
+ const log = (message, error) => {
158
+ try {
159
+ if (error === undefined)
160
+ ctx.logger.warn(message);
161
+ else
162
+ ctx.logger.warn(message, error);
163
+ }
164
+ catch {
165
+ // Degraded ctx without a logger: warnings are best-effort.
166
+ }
167
+ };
168
+ const started = Date.now();
169
+ // Notifications have no return-value or ordering semantics. Start every
170
+ // listener from the same snapshot so one slow plugin cannot delay another
171
+ // plugin's session rebind (or make a fire-and-forget dispatch look stuck).
172
+ await Promise.all(listeners.map(async (handler) => {
173
+ const permission = DECISION_EVENT_PERMISSIONS[name];
174
+ const principal = { componentId: handler.componentId, activationId: handler.activationId };
175
+ if (permission !== undefined
176
+ && !decisionRegistryOf(ctx).grants.allows(principal, permission, scope ?? handler.scope)) {
177
+ log(`dsh-tui: ${name} handler from Component "${handler.componentId}" skipped after grant revocation`);
178
+ return;
179
+ }
180
+ const remaining = DECISION_TOTAL_TIMEOUT_MS - (Date.now() - started);
181
+ if (remaining <= 0) {
182
+ log(`dsh-tui: ${name} total decision budget exceeded; handler skipped`);
183
+ return;
184
+ }
185
+ const bounded = await runBounded(() => handler.listener(freezeClone(payload)), Math.min(DECISION_HANDLER_TIMEOUT_MS, remaining));
186
+ if (bounded.kind === 'timeout') {
187
+ log(`dsh-tui: ${name} handler from Component "${handler.componentId}" exceeded ${DECISION_HANDLER_TIMEOUT_MS}ms; continuing`);
188
+ }
189
+ else if (bounded.kind === 'error') {
190
+ log(`dsh-tui: ${name} listener failed; continuing with the other listeners: %o`, bounded.error);
191
+ }
192
+ }));
155
193
  }
156
194
  /** Shared normalizer for the veto-only decisions (session-switch, compact):
157
195
  * `{ cancel: true, reason? }` or no opinion; everything else is ignored.
@@ -1 +1 @@
1
- {"version":3,"file":"grants.d.ts","sourceRoot":"","sources":["../../../src/dsh-adapter/grants.ts"],"names":[],"mappings":"AAAA,4CAA4C;AAI5C,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,yBAAyB,CAAA;AAKjE,eAAO,MAAM,qBAAqB,0BAA0B,CAAA;AAE5D,MAAM,WAAW,cAAc;IAC7B,WAAW,EAAE,MAAM,CAAA;IACnB,YAAY,CAAC,EAAE,MAAM,CAAA;CACtB;AAED,MAAM,WAAW,UAAU;IACzB,8EAA8E;IAC9E,MAAM,CAAC,SAAS,EAAE,cAAc,GAAG,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAA;IACtF,SAAS,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,GAAG,MAAM,CAAA;IAC/C,gBAAgB,IAAI,SAAS,MAAM,EAAE,CAAA;IACrC,+EAA+E;IAC/E,QAAQ,CAAC,CAAC,QAAQ,EAAE,MAAM,IAAI,GAAG,MAAM,IAAI,CAAA;IAC3C,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAA;CAC1B;AAkID,iEAAiE;AACjE,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,kBAAkB,GAAG,UAAU,CAKvF;AAED;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,GAAG,GAAE,MAAiB,EAAE,QAAQ,CAAC,EAAE,kBAAkB,GAAG,UAAU,CAgDhG"}
1
+ {"version":3,"file":"grants.d.ts","sourceRoot":"","sources":["../../../src/dsh-adapter/grants.ts"],"names":[],"mappings":"AAAA,4CAA4C;AAI5C,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,yBAAyB,CAAA;AAKjE,eAAO,MAAM,qBAAqB,0BAA0B,CAAA;AAE5D,MAAM,WAAW,cAAc;IAC7B,WAAW,EAAE,MAAM,CAAA;IACnB,YAAY,CAAC,EAAE,MAAM,CAAA;CACtB;AAED,MAAM,WAAW,UAAU;IACzB,8EAA8E;IAC9E,MAAM,CAAC,SAAS,EAAE,cAAc,GAAG,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAA;IACtF,SAAS,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,GAAG,MAAM,CAAA;IAC/C,gBAAgB,IAAI,SAAS,MAAM,EAAE,CAAA;IACrC,+EAA+E;IAC/E,QAAQ,CAAC,CAAC,QAAQ,EAAE,MAAM,IAAI,GAAG,MAAM,IAAI,CAAA;IAC3C,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAA;CAC1B;AAkJD,iEAAiE;AACjE,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,kBAAkB,GAAG,UAAU,CAKvF;AAED;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,GAAG,GAAE,MAAiB,EAAE,QAAQ,CAAC,EAAE,kBAAkB,GAAG,UAAU,CA0DhG"}
@@ -1,5 +1,5 @@
1
1
  /** Live, scoped plugin grant evaluation. */
2
- import { readFileSync, unwatchFile, watchFile } from 'node:fs';
2
+ import { readFileSync } from 'node:fs';
3
3
  import { join } from 'node:path';
4
4
  import { normalizePermissionScope, permissionScopeCovers } from '../plugin-spec/permission-scope.js';
5
5
  import { loadSpecData } from '../plugin-spec/registry.js';
@@ -34,18 +34,32 @@ function parseTable(text) {
34
34
  catch {
35
35
  return { grants, denies, corrupt: true };
36
36
  }
37
+ if (parsed === null || typeof parsed !== 'object' || Array.isArray(parsed)) {
38
+ return { grants, denies, corrupt: true };
39
+ }
40
+ const root = parsed;
41
+ if (Object.keys(root).some(key => key !== 'grants' && key !== 'denies')) {
42
+ return { grants, denies, corrupt: true };
43
+ }
37
44
  const readSection = (key, target) => {
38
- const section = parsed?.[key];
45
+ const section = root[key];
46
+ if (section === undefined)
47
+ return true;
39
48
  if (section === null || typeof section !== 'object' || Array.isArray(section))
40
- return;
49
+ return false;
41
50
  for (const [componentId, values] of Object.entries(section)) {
42
- if (!Array.isArray(values))
43
- continue;
44
- target.set(componentId, values.map(parseRule).filter((rule) => rule !== undefined));
51
+ if (componentId === '' || !Array.isArray(values))
52
+ return false;
53
+ const rules = values.map(parseRule);
54
+ if (rules.some(rule => rule === undefined))
55
+ return false;
56
+ target.set(componentId, rules);
45
57
  }
58
+ return true;
46
59
  };
47
- readSection('grants', grants);
48
- readSection('denies', denies);
60
+ if (!readSection('grants', grants) || !readSection('denies', denies)) {
61
+ return { grants: new Map(), denies: new Map(), corrupt: true };
62
+ }
49
63
  return { grants, denies, corrupt: false };
50
64
  }
51
65
  function resolveRegistry(registry) {
@@ -91,7 +105,11 @@ function hasUnknownActivationRule(rules, principal, permission, scope) {
91
105
  }
92
106
  function storeFrom(table, registry, onChange) {
93
107
  const known = new Map((registry?.permissions ?? []).map(entry => [entry.name, entry.default]));
94
- return {
108
+ // The store is a capability, not a mutable configuration object. Keep the
109
+ // live table in the closure and freeze the facade so a traceable Cordis
110
+ // proxy (or an accidentally retained reference) cannot replace `allows`
111
+ // and turn an authorization check into an unconditional allow.
112
+ return Object.freeze({
95
113
  get corrupt() {
96
114
  return table().corrupt;
97
115
  },
@@ -116,7 +134,7 @@ function storeFrom(table, registry, onChange) {
116
134
  defaultOf: permission => known.get(permission) ?? 'deny',
117
135
  knownPermissions: () => [...known.keys()],
118
136
  onChange,
119
- };
137
+ });
120
138
  }
121
139
  /** Parse a fixed snapshot, primarily for deterministic tests. */
122
140
  export function parseGrantStore(text, registry) {
@@ -134,15 +152,18 @@ export function readGrantStore(dir = DATA_DIR, registry) {
134
152
  const file = join(dir, EXTENSION_GRANTS_FILE);
135
153
  const readCurrent = () => {
136
154
  let text;
155
+ let missing = false;
137
156
  try {
138
157
  text = readFileSync(file, 'utf8');
139
158
  }
140
159
  catch (error) {
141
- const missing = error.code === 'ENOENT';
160
+ missing = error.code === 'ENOENT';
142
161
  text = missing ? '' : '{unreadable';
143
162
  }
144
163
  return {
145
- table: text === ''
164
+ // An existing zero-byte file is corruption, not the same state as an
165
+ // absent grants file. Only ENOENT receives the registry defaults.
166
+ table: missing
146
167
  ? { grants: new Map(), denies: new Map(), corrupt: false }
147
168
  : parseTable(text),
148
169
  signature: text,
@@ -150,6 +171,7 @@ export function readGrantStore(dir = DATA_DIR, registry) {
150
171
  };
151
172
  const listeners = new Set();
152
173
  let watching = false;
174
+ let watchTimer;
153
175
  let signature = readCurrent().signature;
154
176
  const changed = () => {
155
177
  const next = readCurrent().signature;
@@ -170,13 +192,20 @@ export function readGrantStore(dir = DATA_DIR, registry) {
170
192
  listeners.add(listener);
171
193
  if (!watching) {
172
194
  watching = true;
173
- watchFile(file, { interval: 100, persistent: false }, changed);
195
+ // A small unref'ed poll is deterministic across filesystems and
196
+ // timestamp granularities. The synchronous read on every operation
197
+ // remains the authorization source of truth; this loop only releases
198
+ // grant-owned subscriptions promptly after a revocation.
199
+ watchTimer = setInterval(changed, 50);
200
+ watchTimer.unref?.();
174
201
  }
175
202
  return () => {
176
203
  listeners.delete(listener);
177
204
  if (watching && listeners.size === 0) {
178
205
  watching = false;
179
- unwatchFile(file, changed);
206
+ if (watchTimer !== undefined)
207
+ clearInterval(watchTimer);
208
+ watchTimer = undefined;
180
209
  }
181
210
  };
182
211
  };
@@ -0,0 +1,37 @@
1
+ import { Context } from '@deepseek-ai/cordis';
2
+ /** Host-only escape hatch for process teardown paths that intentionally act on
3
+ * the composition root while running from a host plugin callback. This module
4
+ * is internal and is not part of the public plugin package surface. */
5
+ export declare function withHostRootCapability<T>(callback: () => T): T;
6
+ export declare function concreteService<T extends object>(service: T): T;
7
+ /** Bind a host-side disposer to the caller's authenticated Cordis activation.
8
+ * A failed registration immediately rolls back the owned contribution. */
9
+ export declare function bindCallerEffect(ctx: Context, disposer: () => unknown, onRegistered?: (cleanup: () => unknown) => void): boolean;
10
+ /** Return the stable, authenticated Fiber identity for an activation. */
11
+ export declare function activationFiber(ctx: Context): object | undefined;
12
+ /** Return Cordis's canonical activation context, never a writable context
13
+ * shadow. Explicit owner arguments use this before resolving host services. */
14
+ export declare function activationContext(ctx: Context): Context | undefined;
15
+ /** Resolve a service's composition root once. Service methods are invoked
16
+ * through caller-bound Cordis proxies; keeping this root in host state avoids
17
+ * resolving sibling services through an attacker-provided caller shadow. */
18
+ export declare function compositionRoot(ctx: Context): Context;
19
+ /** Recover the composition root that owns a traceable service. Cordis stores
20
+ * the provider context on the concrete Service instance; callers may reach
21
+ * that instance through a proxy from another composition, so this must be
22
+ * resolved from the unwrapped service rather than from the call context. */
23
+ export declare function serviceCompositionRoot(service: object): Context | undefined;
24
+ /** Explicit context arguments on mediated APIs may be supplied by host code
25
+ * running from the composition root, or may be the activation making the
26
+ * service call. A non-root plugin cannot nominate a different activation's
27
+ * context and thereby borrow its verified identity/lifecycle. */
28
+ export declare function assertCallerContext(caller: Context, target: Context, capability: string, service?: object): void;
29
+ /**
30
+ * Resolve the caller for a plugin-owned effect. A plugin can read
31
+ * `ctx.root`, but using its root-bound service proxy would attach cleanup to
32
+ * the host fiber and leave the effect alive after the plugin unloads. Host
33
+ * code must use the module-local host accessor for controls it owns; the
34
+ * public service surface only accepts a live non-root activation.
35
+ */
36
+ export declare function requirePluginCaller(caller: Context, capability: string, service?: object): Context;
37
+ //# sourceMappingURL=host-access.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"host-access.d.ts","sourceRoot":"","sources":["../../../src/dsh-adapter/host-access.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,OAAO,EAAS,MAAM,qBAAqB,CAAA;AAoSpD;;uEAEuE;AACvE,wBAAgB,sBAAsB,CAAC,CAAC,EAAE,QAAQ,EAAE,MAAM,CAAC,GAAG,CAAC,CAE9D;AA8FD,wBAAgB,eAAe,CAAC,CAAC,SAAS,MAAM,EAAE,OAAO,EAAE,CAAC,GAAG,CAAC,CAe/D;AAED;0EAC0E;AAC1E,wBAAgB,gBAAgB,CAC9B,GAAG,EAAE,OAAO,EACZ,QAAQ,EAAE,MAAM,OAAO,EACvB,YAAY,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,OAAO,KAAK,IAAI,GAC9C,OAAO,CA2BT;AAED,yEAAyE;AACzE,wBAAgB,eAAe,CAAC,GAAG,EAAE,OAAO,GAAG,MAAM,GAAG,SAAS,CAMhE;AAED;+EAC+E;AAC/E,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,OAAO,GAAG,OAAO,GAAG,SAAS,CAInE;AAED;;4EAE4E;AAC5E,wBAAgB,eAAe,CAAC,GAAG,EAAE,OAAO,GAAG,OAAO,CAwDrD;AAuDD;;;4EAG4E;AAC5E,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,GAAG,SAAS,CAO3E;AAED;;;iEAGiE;AACjE,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,UAAU,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAehH;AAED;;;;;;GAMG;AACH,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,OAAO,EAAE,UAAU,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,GAAG,OAAO,CAmBlG"}