@deepseek-harness-tui/dsh-tui 0.15.0 → 0.16.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/README.md +92 -5
- package/guide/dsh-tui-guide/codex-backend.en.md +13 -3
- package/guide/dsh-tui-guide/codex-backend.md +9 -2
- package/guide/dsh-tui-guide/configuration.en.md +39 -2
- package/guide/dsh-tui-guide/configuration.md +34 -2
- package/guide/dsh-tui-guide/getting-started.en.md +15 -0
- package/guide/dsh-tui-guide/getting-started.md +15 -0
- package/guide/dsh-tui-guide/interaction.en.md +8 -1
- package/guide/dsh-tui-guide/interaction.md +8 -1
- package/guide/dsh-tui-guide/plugins.en.md +66 -5
- package/guide/dsh-tui-guide/plugins.md +37 -3
- package/guide/dsh-tui-guide/user-guide.en.md +3 -2
- package/guide/dsh-tui-guide/user-guide.md +3 -2
- package/lib/settings.json +1 -1
- package/lib/types/adapter/channel/projection.d.ts.map +1 -1
- package/lib/types/adapter/channel/projection.js +1 -0
- package/lib/types/adapter/channel/ui-policy.d.ts +1 -1
- package/lib/types/adapter/channel/ui-policy.d.ts.map +1 -1
- package/lib/types/adapter/channel/ui-policy.js +1 -0
- package/lib/types/adapter/ports/channel-catalog.d.ts +5 -1
- package/lib/types/adapter/ports/channel-catalog.d.ts.map +1 -1
- package/lib/types/adapter/ports/channel-settings.d.ts +5 -3
- package/lib/types/adapter/ports/channel-settings.d.ts.map +1 -1
- package/lib/types/adapter/ports/channel-ui.d.ts +7 -1
- package/lib/types/adapter/ports/channel-ui.d.ts.map +1 -1
- package/lib/types/adapter/ports/channel-view.d.ts +47 -0
- package/lib/types/adapter/ports/channel-view.d.ts.map +1 -1
- package/lib/types/adapter/ports/channel.d.ts +2 -0
- package/lib/types/adapter/ports/channel.d.ts.map +1 -1
- package/lib/types/agent/events.d.ts +3 -2
- package/lib/types/agent/events.d.ts.map +1 -1
- package/lib/types/backends/codex/contract.d.ts +3 -0
- package/lib/types/backends/codex/contract.d.ts.map +1 -1
- package/lib/types/backends/codex/contract.js +3 -0
- package/lib/types/backends/codex/session/async-questions.d.ts +17 -0
- package/lib/types/backends/codex/session/async-questions.d.ts.map +1 -0
- package/lib/types/backends/codex/session/async-questions.js +89 -0
- package/lib/types/backends/codex/session/session.d.ts.map +1 -1
- package/lib/types/backends/codex/session/session.js +35 -9
- package/lib/types/backends/codex/text.d.ts +3 -3
- package/lib/types/backends/codex/text.js +1 -1
- package/lib/types/backends/codex/translate/items.d.ts.map +1 -1
- package/lib/types/backends/codex/translate/items.js +3 -1
- package/lib/types/backends/codex/translate/question-reply.d.ts +11 -0
- package/lib/types/backends/codex/translate/question-reply.d.ts.map +1 -0
- package/lib/types/backends/codex/translate/question-reply.js +53 -0
- package/lib/types/channel/projection.d.ts.map +1 -1
- package/lib/types/channel/projection.js +10 -0
- package/lib/types/commands.d.ts +10 -0
- package/lib/types/commands.d.ts.map +1 -1
- package/lib/types/commands.js +11 -2
- package/lib/types/components/CommandSuggestions.d.ts.map +1 -1
- package/lib/types/components/CommandSuggestions.js +4 -1
- package/lib/types/components/LogoV2.d.ts +4 -1
- package/lib/types/components/LogoV2.d.ts.map +1 -1
- package/lib/types/components/LogoV2.js +5 -3
- package/lib/types/components/MessageList.d.ts.map +1 -1
- package/lib/types/components/MessageList.js +95 -14
- package/lib/types/components/PermissionsPicker.d.ts +10 -1
- package/lib/types/components/PermissionsPicker.d.ts.map +1 -1
- package/lib/types/components/PermissionsPicker.js +24 -16
- package/lib/types/components/PluginInstallPane.d.ts +21 -0
- package/lib/types/components/PluginInstallPane.d.ts.map +1 -0
- package/lib/types/components/PluginInstallPane.js +84 -0
- package/lib/types/components/PluginManagePane.d.ts +21 -0
- package/lib/types/components/PluginManagePane.d.ts.map +1 -0
- package/lib/types/components/PluginManagePane.js +69 -0
- package/lib/types/components/PromptInput.d.ts.map +1 -1
- package/lib/types/components/PromptInput.js +7 -3
- package/lib/types/components/SubagentCard.d.ts +13 -1
- package/lib/types/components/SubagentCard.d.ts.map +1 -1
- package/lib/types/components/SubagentCard.js +27 -12
- package/lib/types/components/SubagentDashboard.d.ts +8 -1
- package/lib/types/components/SubagentDashboard.d.ts.map +1 -1
- package/lib/types/components/SubagentDashboard.js +58 -16
- package/lib/types/components/launchpadLayout.d.ts +14 -1
- package/lib/types/components/launchpadLayout.d.ts.map +1 -1
- package/lib/types/components/launchpadLayout.js +36 -17
- package/lib/types/components/messages/TeamMessageBubble.d.ts +20 -0
- package/lib/types/components/messages/TeamMessageBubble.d.ts.map +1 -0
- package/lib/types/components/messages/TeamMessageBubble.js +15 -0
- package/lib/types/components/sidePanel/PanelHost.d.ts.map +1 -1
- package/lib/types/components/sidePanel/PanelHost.js +5 -1
- package/lib/types/components/sidePanel/PanelStore.d.ts +8 -0
- package/lib/types/components/sidePanel/PanelStore.d.ts.map +1 -1
- package/lib/types/components/sidePanel/PanelStore.js +11 -0
- package/lib/types/components/sidePanel/SidePanelColumn.d.ts.map +1 -1
- package/lib/types/components/sidePanel/SidePanelColumn.js +13 -1
- package/lib/types/components/sidePanel/SidePanelRuntimeContext.d.ts +7 -0
- package/lib/types/components/sidePanel/SidePanelRuntimeContext.d.ts.map +1 -1
- package/lib/types/components/sidePanel/SidePanelRuntimeContext.js +4 -0
- package/lib/types/components/sidePanel/btw/BtwComposer.d.ts +3 -0
- package/lib/types/components/sidePanel/btw/BtwComposer.d.ts.map +1 -1
- package/lib/types/components/sidePanel/btw/BtwComposer.js +20 -8
- package/lib/types/components/sidePanel/btw/BtwPanelAdapter.d.ts +2 -1
- package/lib/types/components/sidePanel/btw/BtwPanelAdapter.d.ts.map +1 -1
- package/lib/types/components/sidePanel/btw/BtwPanelAdapter.js +104 -13
- package/lib/types/components/sidePanel/btw/BtwThreadScene.d.ts +6 -2
- package/lib/types/components/sidePanel/btw/BtwThreadScene.d.ts.map +1 -1
- package/lib/types/components/sidePanel/btw/BtwThreadScene.js +61 -15
- package/lib/types/components/sidePanel/btw/BtwThreadView.d.ts +14 -4
- package/lib/types/components/sidePanel/btw/BtwThreadView.d.ts.map +1 -1
- package/lib/types/components/sidePanel/btw/BtwThreadView.js +90 -5
- package/lib/types/components/sidePanel/btw/threads.d.ts +18 -0
- package/lib/types/components/sidePanel/btw/threads.d.ts.map +1 -1
- package/lib/types/components/sidePanel/btw/threads.js +51 -0
- package/lib/types/components/sidePanel/builtinPanels.d.ts.map +1 -1
- package/lib/types/components/sidePanel/builtinPanels.js +5 -1
- package/lib/types/components/sidePanel/companion/SplashMascot.d.ts +5 -1
- package/lib/types/components/sidePanel/companion/SplashMascot.d.ts.map +1 -1
- package/lib/types/components/sidePanel/companion/SplashMascot.js +19 -4
- package/lib/types/components/sidePanel/companion/skins.d.ts +8 -0
- package/lib/types/components/sidePanel/companion/skins.d.ts.map +1 -1
- package/lib/types/components/sidePanel/companion/skins.js +57 -12
- package/lib/types/components/sidePanel/types.d.ts +6 -0
- package/lib/types/components/sidePanel/types.d.ts.map +1 -1
- package/lib/types/components/splashArtRows.d.ts +29 -0
- package/lib/types/components/splashArtRows.d.ts.map +1 -0
- package/lib/types/components/splashArtRows.js +31 -0
- package/lib/types/components/teamRoster.d.ts +70 -0
- package/lib/types/components/teamRoster.d.ts.map +1 -0
- package/lib/types/components/teamRoster.js +78 -0
- package/lib/types/dsh-adapter/admission-loader.d.ts +82 -0
- package/lib/types/dsh-adapter/admission-loader.d.ts.map +1 -1
- package/lib/types/dsh-adapter/admission-loader.js +73 -28
- package/lib/types/dsh-adapter/agent-view.d.ts.map +1 -1
- package/lib/types/dsh-adapter/agent-view.js +3 -1
- package/lib/types/dsh-adapter/approvals.d.ts +7 -0
- package/lib/types/dsh-adapter/approvals.d.ts.map +1 -1
- package/lib/types/dsh-adapter/approvals.js +45 -1
- package/lib/types/dsh-adapter/backend/translate.d.ts.map +1 -1
- package/lib/types/dsh-adapter/backend/translate.js +68 -8
- package/lib/types/dsh-adapter/backend-assembly.d.ts +31 -0
- package/lib/types/dsh-adapter/backend-assembly.d.ts.map +1 -0
- package/lib/types/dsh-adapter/backend-assembly.js +225 -0
- package/lib/types/dsh-adapter/backend-contribution.d.ts +36 -0
- package/lib/types/dsh-adapter/backend-contribution.d.ts.map +1 -1
- package/lib/types/dsh-adapter/backend-contribution.js +41 -0
- package/lib/types/dsh-adapter/backend-registry.d.ts.map +1 -1
- package/lib/types/dsh-adapter/backend-registry.js +22 -3
- package/lib/types/dsh-adapter/backends.d.ts.map +1 -1
- package/lib/types/dsh-adapter/backends.js +4 -5
- package/lib/types/dsh-adapter/bundled-presets.d.ts.map +1 -1
- package/lib/types/dsh-adapter/bundled-presets.js +60 -3
- package/lib/types/dsh-adapter/channel/binding-events.d.ts +1 -0
- package/lib/types/dsh-adapter/channel/binding-events.d.ts.map +1 -1
- package/lib/types/dsh-adapter/channel/binding-events.js +1 -0
- package/lib/types/dsh-adapter/channel/command-completions.d.ts.map +1 -1
- package/lib/types/dsh-adapter/channel/command-completions.js +64 -7
- package/lib/types/dsh-adapter/channel/compaction.d.ts +7 -2
- package/lib/types/dsh-adapter/channel/compaction.d.ts.map +1 -1
- package/lib/types/dsh-adapter/channel/compaction.js +100 -26
- package/lib/types/dsh-adapter/channel/core/binding-feed.d.ts +3 -0
- package/lib/types/dsh-adapter/channel/core/binding-feed.d.ts.map +1 -1
- package/lib/types/dsh-adapter/channel/core/binding-feed.js +2 -1
- package/lib/types/dsh-adapter/channel/core/compose.d.ts.map +1 -1
- package/lib/types/dsh-adapter/channel/core/compose.js +12 -0
- package/lib/types/dsh-adapter/channel/extensions.d.ts.map +1 -1
- package/lib/types/dsh-adapter/channel/extensions.js +68 -8
- package/lib/types/dsh-adapter/channel/mode-permission-actions.d.ts +6 -0
- package/lib/types/dsh-adapter/channel/mode-permission-actions.d.ts.map +1 -1
- package/lib/types/dsh-adapter/channel/mode-permission-actions.js +104 -12
- package/lib/types/dsh-adapter/channel/model-actions.d.ts.map +1 -1
- package/lib/types/dsh-adapter/channel/model-actions.js +8 -1
- package/lib/types/dsh-adapter/channel/permissions.d.ts.map +1 -1
- package/lib/types/dsh-adapter/channel/permissions.js +4 -1
- package/lib/types/dsh-adapter/channel/projection-helpers.d.ts.map +1 -1
- package/lib/types/dsh-adapter/channel/projection-helpers.js +2 -0
- package/lib/types/dsh-adapter/channel/session-metadata.d.ts.map +1 -1
- package/lib/types/dsh-adapter/channel/session-metadata.js +4 -3
- package/lib/types/dsh-adapter/channel/session-reset.js +2 -2
- package/lib/types/dsh-adapter/channel/session-resume.d.ts.map +1 -1
- package/lib/types/dsh-adapter/channel/session-resume.js +7 -0
- package/lib/types/dsh-adapter/channel/settings-host.d.ts +3 -0
- package/lib/types/dsh-adapter/channel/settings-host.d.ts.map +1 -1
- package/lib/types/dsh-adapter/channel/settings-host.js +135 -5
- package/lib/types/dsh-adapter/channel/state.d.ts +6 -0
- package/lib/types/dsh-adapter/channel/state.d.ts.map +1 -1
- package/lib/types/dsh-adapter/channel/subagent-projection.d.ts.map +1 -1
- package/lib/types/dsh-adapter/channel/subagent-projection.js +18 -6
- package/lib/types/dsh-adapter/channel/transcript.d.ts +7 -0
- package/lib/types/dsh-adapter/channel/transcript.d.ts.map +1 -1
- package/lib/types/dsh-adapter/channel/transcript.js +19 -2
- package/lib/types/dsh-adapter/compat/agent-team-0.2.0.d.ts +55 -0
- package/lib/types/dsh-adapter/compat/agent-team-0.2.0.d.ts.map +1 -0
- package/lib/types/dsh-adapter/compat/agent-team-0.2.0.js +96 -0
- package/lib/types/dsh-adapter/contract.d.ts +4 -4
- package/lib/types/dsh-adapter/contract.d.ts.map +1 -1
- package/lib/types/dsh-adapter/contract.js +5 -5
- package/lib/types/dsh-adapter/effect-ledger.d.ts +7 -0
- package/lib/types/dsh-adapter/effect-ledger.d.ts.map +1 -1
- package/lib/types/dsh-adapter/effect-ledger.js +8 -1
- package/lib/types/dsh-adapter/entry-slot.d.ts +3 -1
- package/lib/types/dsh-adapter/entry-slot.d.ts.map +1 -1
- package/lib/types/dsh-adapter/entry-slot.js +2 -2
- package/lib/types/dsh-adapter/fresh-agent.d.ts.map +1 -1
- package/lib/types/dsh-adapter/fresh-agent.js +6 -1
- package/lib/types/dsh-adapter/host-access.d.ts.map +1 -1
- package/lib/types/dsh-adapter/host-access.js +95 -0
- package/lib/types/dsh-adapter/host-contract.d.ts +1 -1
- package/lib/types/dsh-adapter/host-contract.d.ts.map +1 -1
- package/lib/types/dsh-adapter/host-contract.js +3 -2
- package/lib/types/dsh-adapter/host-dsh.d.ts +8 -2
- package/lib/types/dsh-adapter/host-dsh.d.ts.map +1 -1
- package/lib/types/dsh-adapter/host-dsh.js +45 -30
- package/lib/types/dsh-adapter/host-entry.js +35 -10
- package/lib/types/dsh-adapter/index.d.ts +9 -10
- package/lib/types/dsh-adapter/index.d.ts.map +1 -1
- package/lib/types/dsh-adapter/index.js +8 -6
- package/lib/types/dsh-adapter/invariant.d.ts +7 -2
- package/lib/types/dsh-adapter/invariant.d.ts.map +1 -1
- package/lib/types/dsh-adapter/invariant.js +6 -3
- package/lib/types/dsh-adapter/migrate/index.js +2 -2
- package/lib/types/dsh-adapter/panels.d.ts.map +1 -1
- package/lib/types/dsh-adapter/panels.js +6 -0
- package/lib/types/dsh-adapter/plugin-declaration.d.ts +99 -1
- package/lib/types/dsh-adapter/plugin-declaration.d.ts.map +1 -1
- package/lib/types/dsh-adapter/plugin-declaration.js +132 -2
- package/lib/types/dsh-adapter/plugin-feed.d.ts +94 -0
- package/lib/types/dsh-adapter/plugin-feed.d.ts.map +1 -0
- package/lib/types/dsh-adapter/plugin-feed.js +135 -0
- package/lib/types/dsh-adapter/plugin-guard.d.ts +115 -0
- package/lib/types/dsh-adapter/plugin-guard.d.ts.map +1 -0
- package/lib/types/dsh-adapter/plugin-guard.js +108 -0
- package/lib/types/dsh-adapter/plugin-install.d.ts +160 -0
- package/lib/types/dsh-adapter/plugin-install.d.ts.map +1 -0
- package/lib/types/dsh-adapter/plugin-install.js +545 -0
- package/lib/types/dsh-adapter/plugin-manage.d.ts +178 -0
- package/lib/types/dsh-adapter/plugin-manage.d.ts.map +1 -0
- package/lib/types/dsh-adapter/plugin-manage.js +468 -0
- package/lib/types/dsh-adapter/plugin-manager.d.ts +78 -2
- package/lib/types/dsh-adapter/plugin-manager.d.ts.map +1 -1
- package/lib/types/dsh-adapter/plugin-manager.js +32 -0
- package/lib/types/dsh-adapter/plugin.d.ts.map +1 -1
- package/lib/types/dsh-adapter/plugin.js +49 -2
- package/lib/types/dsh-adapter/plugins-info.d.ts +87 -1
- package/lib/types/dsh-adapter/plugins-info.d.ts.map +1 -1
- package/lib/types/dsh-adapter/plugins-info.js +131 -26
- package/lib/types/dsh-adapter/process-exit.d.ts +1 -1
- package/lib/types/dsh-adapter/process-exit.d.ts.map +1 -1
- package/lib/types/dsh-adapter/process-exit.js +1 -1
- package/lib/types/dsh-adapter/session-projection-store.d.ts +66 -0
- package/lib/types/dsh-adapter/session-projection-store.d.ts.map +1 -0
- package/lib/types/dsh-adapter/session-projection-store.js +149 -0
- package/lib/types/dsh-adapter/settingsEditor.js +2 -2
- package/lib/types/dsh-adapter/sideQuestion.d.ts +21 -0
- package/lib/types/dsh-adapter/sideQuestion.d.ts.map +1 -1
- package/lib/types/dsh-adapter/sideQuestion.js +53 -0
- package/lib/types/dsh-adapter/subagents.d.ts.map +1 -1
- package/lib/types/dsh-adapter/subagents.js +2 -1
- package/lib/types/dsh-adapter/team-mode.d.ts +74 -0
- package/lib/types/dsh-adapter/team-mode.d.ts.map +1 -0
- package/lib/types/dsh-adapter/team-mode.js +188 -0
- package/lib/types/dsh-adapter/team-roster.d.ts +67 -0
- package/lib/types/dsh-adapter/team-roster.d.ts.map +1 -0
- package/lib/types/dsh-adapter/team-roster.js +136 -0
- package/lib/types/dsh-adapter/team-tools.d.ts +58 -0
- package/lib/types/dsh-adapter/team-tools.d.ts.map +1 -0
- package/lib/types/dsh-adapter/team-tools.js +208 -0
- package/lib/types/dsh-adapter/types.d.ts +1 -1
- package/lib/types/dsh-adapter/types.d.ts.map +1 -1
- package/lib/types/i18n.d.ts +607 -16
- package/lib/types/i18n.d.ts.map +1 -1
- package/lib/types/i18n.js +295 -9
- package/lib/types/ink/components/ScrollBox.d.ts +9 -0
- package/lib/types/ink/components/ScrollBox.d.ts.map +1 -1
- package/lib/types/ink/components/ScrollBox.js +23 -0
- package/lib/types/ink/dom.d.ts.map +1 -1
- package/lib/types/kernelPrefs.d.ts +1 -1
- package/lib/types/kernelPrefs.d.ts.map +1 -1
- package/lib/types/native-ts/yoga-layout/index.d.ts.map +1 -1
- package/lib/types/native-ts/yoga-layout/index.js +8 -1
- package/lib/types/permissionPresetLabels.d.ts +25 -0
- package/lib/types/permissionPresetLabels.d.ts.map +1 -0
- package/lib/types/permissionPresetLabels.js +33 -0
- package/lib/types/preset-patch.d.ts +22 -0
- package/lib/types/preset-patch.d.ts.map +1 -0
- package/lib/types/preset-patch.js +14 -0
- package/lib/types/screens/Chat.d.ts +33 -1
- package/lib/types/screens/Chat.d.ts.map +1 -1
- package/lib/types/screens/Chat.js +301 -9
- package/lib/types/screens/Launchpad.d.ts +7 -1
- package/lib/types/screens/Launchpad.d.ts.map +1 -1
- package/lib/types/screens/Launchpad.js +19 -4
- package/lib/types/screens/Settings.d.ts.map +1 -1
- package/lib/types/screens/Settings.js +1 -0
- package/lib/types/screens/chat/usePluginWriteFlow.d.ts +68 -0
- package/lib/types/screens/chat/usePluginWriteFlow.d.ts.map +1 -0
- package/lib/types/screens/chat/usePluginWriteFlow.js +92 -0
- package/lib/types/screens/chatOverlay.d.ts +19 -1
- package/lib/types/screens/chatOverlay.d.ts.map +1 -1
- package/lib/types/sessionModes.d.ts +5 -3
- package/lib/types/sessionModes.d.ts.map +1 -1
- package/lib/types/sessionModes.js +9 -3
- package/lib/types/tips.js +2 -2
- package/lib/types/utils/paths.d.ts +11 -0
- package/lib/types/utils/paths.d.ts.map +1 -1
- package/lib/types/utils/paths.js +13 -0
- package/package.json +126 -120
- package/tui-profile/feed/README.md +63 -0
- package/tui-profile/feed/first-party.json +10 -0
|
@@ -39,10 +39,10 @@ import { t } from '../i18n.js';
|
|
|
39
39
|
import { loadSpecData } from '../adapter/standard/registry.js';
|
|
40
40
|
import { createContractIndex, validatePlugin, negotiate } from '../adapter/standard/admission.js';
|
|
41
41
|
import { cleanScalarText } from './sanitize.js';
|
|
42
|
-
import { isCompositionOwned } from './plugin-declaration.js';
|
|
42
|
+
import { isCompositionOwned, pluginDeclarationIn } from './plugin-declaration.js';
|
|
43
43
|
import { extraBundles, localizedPluginText } from './plugin-manager.js';
|
|
44
44
|
import { PLUGIN_STORAGE_DIR } from './plugin-storage.js';
|
|
45
|
-
import {
|
|
45
|
+
import { EFFECT_LEDGER_NAME } from './effect-ledger.js';
|
|
46
46
|
/** How many plugins the matrix shows before an overflow note. */
|
|
47
47
|
export const PLUGINS_MATRIX_MAX_ROWS = 20;
|
|
48
48
|
/** How many ledger records the tail section shows. */
|
|
@@ -100,9 +100,15 @@ function redactText(value) {
|
|
|
100
100
|
return redacted === match ? match.replace(USERINFO_AT_START, '$1') : redacted;
|
|
101
101
|
});
|
|
102
102
|
}
|
|
103
|
-
/**
|
|
104
|
-
*
|
|
105
|
-
|
|
103
|
+
/**
|
|
104
|
+
* One report cell: credentials redacted first, then sanitized, then capped —
|
|
105
|
+
* truncation comes last, or a cut URL keeps the head of its own secret.
|
|
106
|
+
*
|
|
107
|
+
* Exported for the install flow (C-4): pnpm's output and the service's error
|
|
108
|
+
* text are foreign input reaching the same transcript, and one renderer means
|
|
109
|
+
* the two surfaces cannot disagree about what a cell is.
|
|
110
|
+
*/
|
|
111
|
+
export const cell = (value) => cleanScalarText(redactText(String(value ?? '')), LINE_CELLS);
|
|
106
112
|
/** One URL field as a report cell: credentials stripped, then capped. */
|
|
107
113
|
const urlCell = (value) => cell(redactUrl(value));
|
|
108
114
|
/** Parse the ledger file tolerantly (skip corrupt lines, never throw). */
|
|
@@ -132,10 +138,10 @@ function readLedgerRecords(file) {
|
|
|
132
138
|
/** The plugin set for the matrix: grants-file keys ∪ ledger pluginIds ∪
|
|
133
139
|
* storage namespaces ('host'/'undeclared' ledger identities are not
|
|
134
140
|
* plugins and are excluded). */
|
|
135
|
-
function
|
|
141
|
+
export function pluginFootprints(paths) {
|
|
136
142
|
const found = new Set();
|
|
137
143
|
try {
|
|
138
|
-
const raw = JSON.parse(readFileSync(join(dataDir, 'extension-grants.json'), 'utf8'));
|
|
144
|
+
const raw = JSON.parse(readFileSync(join(paths.dataDir, 'extension-grants.json'), 'utf8'));
|
|
139
145
|
if (raw !== null && typeof raw === 'object') {
|
|
140
146
|
const table = raw;
|
|
141
147
|
for (const section of [table.grants, table.denies]) {
|
|
@@ -150,13 +156,13 @@ function footprintPlugins(deps, dataDir, ledgerFile, storageDir) {
|
|
|
150
156
|
// Missing/corrupt grants file: the GrantStore already answers deny;
|
|
151
157
|
// the matrix simply has no rows from this source.
|
|
152
158
|
}
|
|
153
|
-
for (const record of readLedgerRecords(ledgerFile)) {
|
|
159
|
+
for (const record of readLedgerRecords(paths.ledgerFile)) {
|
|
154
160
|
if (typeof record.pluginId === 'string' && record.pluginId !== '' && record.pluginId !== 'host' && record.pluginId !== 'undeclared') {
|
|
155
161
|
found.add(record.pluginId);
|
|
156
162
|
}
|
|
157
163
|
}
|
|
158
164
|
try {
|
|
159
|
-
for (const entry of readdirSync(storageDir)) {
|
|
165
|
+
for (const entry of readdirSync(paths.storageDir)) {
|
|
160
166
|
if (!entry.endsWith('.json'))
|
|
161
167
|
continue;
|
|
162
168
|
const namespace = decodeURIComponent(entry.slice(0, -'.json'.length));
|
|
@@ -167,9 +173,29 @@ function footprintPlugins(deps, dataDir, ledgerFile, storageDir) {
|
|
|
167
173
|
catch {
|
|
168
174
|
// No storage directory yet: no rows from this source.
|
|
169
175
|
}
|
|
170
|
-
void deps;
|
|
171
176
|
return [...found].sort();
|
|
172
177
|
}
|
|
178
|
+
/**
|
|
179
|
+
* The three footprint sources, resolved the way the diagnostics resolve them
|
|
180
|
+
* (test overrides first, else the packaged locations under `~/.dsh-tui`).
|
|
181
|
+
*/
|
|
182
|
+
export function pluginFootprintPaths(overrides = {}) {
|
|
183
|
+
const dataDir = overrides.dataDir ?? DATA_DIR;
|
|
184
|
+
return {
|
|
185
|
+
dataDir,
|
|
186
|
+
ledgerFile: overrides.ledgerFile ?? join(dataDir, EFFECT_LEDGER_NAME),
|
|
187
|
+
storageDir: overrides.storageDir ?? join(dataDir, PLUGIN_STORAGE_DIR),
|
|
188
|
+
};
|
|
189
|
+
}
|
|
190
|
+
/**
|
|
191
|
+
* Whether one plugin id still has a footprint in this profile: a grants-file
|
|
192
|
+
* key, an effect-ledger record, or a storage namespace. The same three sources
|
|
193
|
+
* the matrix unions — a removal that left something behind is read here rather
|
|
194
|
+
* than by a second reader that could disagree with the report (C-5).
|
|
195
|
+
*/
|
|
196
|
+
export function pluginFootprintContains(pluginId, overrides = {}) {
|
|
197
|
+
return pluginFootprints(pluginFootprintPaths(overrides)).includes(pluginId);
|
|
198
|
+
}
|
|
173
199
|
/** `/plugin check <path>`: official parse/projection + profile validation + negotiation. */
|
|
174
200
|
function checkManifestLines(pathArg, deps) {
|
|
175
201
|
const lines = [];
|
|
@@ -283,6 +309,14 @@ export function parsePluginArgs(args) {
|
|
|
283
309
|
return { kind: 'info', spec: rest.join(' ') };
|
|
284
310
|
if (sub === 'registries')
|
|
285
311
|
return { kind: 'registries' };
|
|
312
|
+
if (sub === 'install')
|
|
313
|
+
return { kind: 'install', spec: rest.join(' ') };
|
|
314
|
+
if (sub === 'remove')
|
|
315
|
+
return { kind: 'remove', target: rest.join(' ') };
|
|
316
|
+
if (sub === 'enable')
|
|
317
|
+
return { kind: 'enable', target: rest.join(' ') };
|
|
318
|
+
if (sub === 'disable')
|
|
319
|
+
return { kind: 'disable', target: rest.join(' ') };
|
|
286
320
|
return { kind: 'unknown', sub: sub ?? '' };
|
|
287
321
|
}
|
|
288
322
|
/** The `/plugin` overview lines (banner always first). */
|
|
@@ -314,9 +348,20 @@ export function pluginsInfoLines(args, deps) {
|
|
|
314
348
|
}
|
|
315
349
|
if (parsed.kind === 'registries')
|
|
316
350
|
return lines;
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
351
|
+
// `install <spec>` is the write path (C-4): a bare one is a usage error, and
|
|
352
|
+
// the UI drives the rest — this half stays the diagnostics it always was.
|
|
353
|
+
if (parsed.kind === 'install') {
|
|
354
|
+
if (parsed.spec === '')
|
|
355
|
+
lines.push(t('plugins-install-usage'));
|
|
356
|
+
return lines;
|
|
357
|
+
}
|
|
358
|
+
// The three lifecycle forms (C-5) report themselves as they run, like an
|
|
359
|
+
// install: a bare one is a usage error, and this half stays the diagnostics.
|
|
360
|
+
if (parsed.kind === 'remove' || parsed.kind === 'enable' || parsed.kind === 'disable') {
|
|
361
|
+
if (parsed.target === '')
|
|
362
|
+
lines.push(t('plugins-manage-usage', { sub: parsed.kind }));
|
|
363
|
+
return lines;
|
|
364
|
+
}
|
|
320
365
|
// ── Host Descriptor ──
|
|
321
366
|
if (deps.host === undefined) {
|
|
322
367
|
lines.push(t('plugins-host-unavailable'));
|
|
@@ -336,7 +381,7 @@ export function pluginsInfoLines(args, deps) {
|
|
|
336
381
|
}
|
|
337
382
|
}
|
|
338
383
|
// ── 授权矩阵 ──
|
|
339
|
-
const plugins =
|
|
384
|
+
const plugins = pluginFootprints(pluginFootprintPaths(deps));
|
|
340
385
|
lines.push(t('plugins-matrix-note'));
|
|
341
386
|
const permissions = deps.grants.knownPermissions();
|
|
342
387
|
if (permissions.length === 0) {
|
|
@@ -368,6 +413,7 @@ export function pluginsInfoLines(args, deps) {
|
|
|
368
413
|
lines.push(` ${t('plugins-grant-hint')}`);
|
|
369
414
|
}
|
|
370
415
|
// ── 台账尾 ──
|
|
416
|
+
const ledgerFile = pluginFootprintPaths(deps).ledgerFile;
|
|
371
417
|
const records = readLedgerRecords(ledgerFile);
|
|
372
418
|
if (records.length === 0) {
|
|
373
419
|
lines.push(t('plugins-ledger-empty'));
|
|
@@ -382,38 +428,89 @@ export function pluginsInfoLines(args, deps) {
|
|
|
382
428
|
}
|
|
383
429
|
return lines;
|
|
384
430
|
}
|
|
385
|
-
/**
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
431
|
+
/**
|
|
432
|
+
* A declaration this profile can use: parsed, projected and validated against
|
|
433
|
+
* the admission catalog — the half `check <path>` and the list tags share. One
|
|
434
|
+
* implementation, so every surface that judges a declaration is judging the
|
|
435
|
+
* same thing; the caller decides what to do with the answer (the tag negotiates
|
|
436
|
+
* it further, the pre-install notice does not).
|
|
437
|
+
*/
|
|
438
|
+
function admitDeclaration(declaration) {
|
|
390
439
|
const data = loadSpecData();
|
|
391
440
|
if (data === undefined)
|
|
392
|
-
return {
|
|
441
|
+
return { error: t('plugins-check-spec-unavailable') };
|
|
393
442
|
let manifest;
|
|
394
443
|
try {
|
|
395
444
|
manifest = parseManifest(declaration.source, { source: declaration.path });
|
|
396
445
|
projectManifest(manifest);
|
|
397
446
|
}
|
|
398
447
|
catch (error) {
|
|
399
|
-
return {
|
|
448
|
+
return { error: cell(error instanceof Error ? error.message : String(error)) };
|
|
400
449
|
}
|
|
401
450
|
const index = createContractIndex(data.registry, data.permissions);
|
|
402
451
|
try {
|
|
403
452
|
validatePlugin(index, manifest);
|
|
404
453
|
}
|
|
405
454
|
catch (error) {
|
|
406
|
-
return {
|
|
455
|
+
return { error: cell(error instanceof Error ? error.message : String(error)) };
|
|
407
456
|
}
|
|
457
|
+
return { manifest, index };
|
|
458
|
+
}
|
|
459
|
+
/** Evaluate one declaration exactly as `check <path>` does. */
|
|
460
|
+
function verdictOfDeclaration(deps, declaration) {
|
|
461
|
+
const admitted = admitDeclaration(declaration);
|
|
462
|
+
if ('error' in admitted)
|
|
463
|
+
return { kind: 'invalid', error: admitted.error };
|
|
408
464
|
const host = deps.host;
|
|
409
465
|
if (host === undefined)
|
|
410
466
|
return { kind: 'invalid', error: t('plugins-check-host-unavailable') };
|
|
411
|
-
const granted = manifest.permissions.map(request => ({
|
|
467
|
+
const granted = admitted.manifest.permissions.map(request => ({
|
|
412
468
|
name: request.name,
|
|
413
469
|
scope: request.scope,
|
|
414
|
-
granted: deps.grants.allows({ componentId: manifest.id }, request.name, request.scope),
|
|
470
|
+
granted: deps.grants.allows({ componentId: admitted.manifest.id }, request.name, request.scope),
|
|
415
471
|
}));
|
|
416
|
-
return { kind: 'decided', decision: negotiate(index, manifest, host.descriptor, granted) };
|
|
472
|
+
return { kind: 'decided', decision: negotiate(admitted.index, admitted.manifest, host.descriptor, granted) };
|
|
473
|
+
}
|
|
474
|
+
/** Evaluate a package's declaration exactly as `check <path>` does. */
|
|
475
|
+
function declarationVerdict(deps, packageName) {
|
|
476
|
+
const declaration = deps.declaration?.(packageName);
|
|
477
|
+
if (declaration === undefined)
|
|
478
|
+
return { kind: 'undeclared' };
|
|
479
|
+
return verdictOfDeclaration(deps, declaration);
|
|
480
|
+
}
|
|
481
|
+
/**
|
|
482
|
+
* Gate 1's pre-install statement about an install target (C-4), as data.
|
|
483
|
+
*
|
|
484
|
+
* It runs the same parse/project/validate pipeline `check <path>` and the list
|
|
485
|
+
* tags run — one implementation, so the surfaces cannot disagree — over the
|
|
486
|
+
* declaration of the target directory the caller resolved. It deliberately stops
|
|
487
|
+
* before `negotiate`: admission's five states are a statement about a *running*
|
|
488
|
+
* host, and the standing tag in `/plugin` is where they are reported after the
|
|
489
|
+
* install. What a person needs before installing is narrower and decidable: is
|
|
490
|
+
* there a declaration at all, and does this profile's catalog accept it.
|
|
491
|
+
*
|
|
492
|
+
* The evidence is the target directory and nothing else. A declaration resolved
|
|
493
|
+
* by package name would be the profile's already-installed copy of that name —
|
|
494
|
+
* a different artifact from the one being installed (another version, or a local
|
|
495
|
+
* directory), which is how a stale same-name package used to answer for a new
|
|
496
|
+
* target.
|
|
497
|
+
*
|
|
498
|
+
* The two empty answers are not the same thing, and that is the point:
|
|
499
|
+
*
|
|
500
|
+
* - a directory the caller resolved (`install <path>` specs) either carries a
|
|
501
|
+
* package-root `dsh-plugin.json` or does not, so no file is `undeclared`;
|
|
502
|
+
* - a target with no directory to read (a registry name, a tarball, a git spec)
|
|
503
|
+
* ships no file this machine can read, so an empty answer there is `unjudged`
|
|
504
|
+
* — the flow cannot honestly claim a package it has never seen fails gate 1.
|
|
505
|
+
*/
|
|
506
|
+
export function declarationNoticeFor(target) {
|
|
507
|
+
if (target.directory === undefined)
|
|
508
|
+
return { kind: 'unjudged' };
|
|
509
|
+
const declaration = pluginDeclarationIn(target.directory);
|
|
510
|
+
if (declaration === undefined)
|
|
511
|
+
return { kind: 'undeclared' };
|
|
512
|
+
const admitted = admitDeclaration(declaration);
|
|
513
|
+
return 'error' in admitted ? { kind: 'invalid', error: admitted.error } : { kind: 'declared' };
|
|
417
514
|
}
|
|
418
515
|
/**
|
|
419
516
|
* The gate-1 tag for one listed package, or `undefined` when it needs none.
|
|
@@ -586,8 +683,16 @@ export async function pluginServiceLines(args, deps, manager) {
|
|
|
586
683
|
return [];
|
|
587
684
|
if (parsed.kind === 'info' && parsed.spec === '')
|
|
588
685
|
return [];
|
|
686
|
+
// With no service the report is the diagnostics plus the reason and the CLI
|
|
687
|
+
// route, whatever the form asked for — the write path included: the flow that
|
|
688
|
+
// would run it lives in the entry, and without a service the entry hands back
|
|
689
|
+
// no flow, so this is the sentence the person reads.
|
|
589
690
|
if (manager === undefined)
|
|
590
|
-
return [t('plugins-service-unavailable')];
|
|
691
|
+
return [t('plugins-service-unavailable'), t('plugins-service-unavailable-route')];
|
|
692
|
+
// The write paths report themselves line by line as they run (C-4 / C-5): with
|
|
693
|
+
// a service present, this half has nothing to add to them.
|
|
694
|
+
if (parsed.kind === 'install' || parsed.kind === 'remove' || parsed.kind === 'enable' || parsed.kind === 'disable')
|
|
695
|
+
return [];
|
|
591
696
|
// The service belongs to the host: a refusal or a crash there must not take
|
|
592
697
|
// the diagnostics half down with it. Every call funnels into one line, so a
|
|
593
698
|
// rejected promise still leaves a readable report instead of an unhandled
|
|
@@ -23,7 +23,7 @@ export type ExitRequestAnswer = 'exiting' | 'pending' | 'supervising' | 'refused
|
|
|
23
23
|
export interface ProcessExitSeam {
|
|
24
24
|
request?: (request: ExitRequest) => ExitRequestAnswer;
|
|
25
25
|
}
|
|
26
|
-
/** dsh `
|
|
26
|
+
/** dsh `process-shutdown` PROCESS_SHUTDOWN_TIMEOUT_MS (0.2.1-rc.2): the dispose bound. */
|
|
27
27
|
export declare const PROCESS_SHUTDOWN_TIMEOUT_MS = 5000;
|
|
28
28
|
/**
|
|
29
29
|
* End the process by `signal`. Only the entry's own listeners come off, so the
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"process-exit.d.ts","sourceRoot":"","sources":["../../../src/dsh-adapter/process-exit.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,MAAM,MAAM,iBAAiB,GAAG,SAAS,GAAG,QAAQ,GAAG,QAAQ,CAAA;AAC/D,eAAO,MAAM,mBAAmB,EAAE,SAAS,iBAAiB,EAAoC,CAAA;AAEhG,MAAM,MAAM,WAAW,GACnB;IAAE,QAAQ,CAAC,IAAI,EAAE,QAAQ,CAAC;IAAC,QAAQ,CAAC,MAAM,EAAE,iBAAiB,CAAA;CAAE;AACjE,yEAAyE;GACvE;IAAE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAAA;AAEpD;;+CAE+C;AAC/C,MAAM,MAAM,iBAAiB,GAAG,SAAS,GAAG,SAAS,GAAG,aAAa,GAAG,SAAS,CAAA;AAEjF,MAAM,WAAW,eAAe;IAC9B,OAAO,CAAC,EAAE,CAAC,OAAO,EAAE,WAAW,KAAK,iBAAiB,CAAA;CACtD;AAED,
|
|
1
|
+
{"version":3,"file":"process-exit.d.ts","sourceRoot":"","sources":["../../../src/dsh-adapter/process-exit.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,MAAM,MAAM,iBAAiB,GAAG,SAAS,GAAG,QAAQ,GAAG,QAAQ,CAAA;AAC/D,eAAO,MAAM,mBAAmB,EAAE,SAAS,iBAAiB,EAAoC,CAAA;AAEhG,MAAM,MAAM,WAAW,GACnB;IAAE,QAAQ,CAAC,IAAI,EAAE,QAAQ,CAAC;IAAC,QAAQ,CAAC,MAAM,EAAE,iBAAiB,CAAA;CAAE;AACjE,yEAAyE;GACvE;IAAE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAAA;AAEpD;;+CAE+C;AAC/C,MAAM,MAAM,iBAAiB,GAAG,SAAS,GAAG,SAAS,GAAG,aAAa,GAAG,SAAS,CAAA;AAEjF,MAAM,WAAW,eAAe;IAC9B,OAAO,CAAC,EAAE,CAAC,OAAO,EAAE,WAAW,KAAK,iBAAiB,CAAA;CACtD;AAED,0FAA0F;AAC1F,eAAO,MAAM,2BAA2B,OAAO,CAAA;AAQ/C;;;;GAIG;AACH,wBAAgB,WAAW,CAAC,MAAM,EAAE,iBAAiB,GAAG,IAAI,CAQ3D;AAED,MAAM,WAAW,kBAAkB;IACjC,QAAQ,CAAC,IAAI,EAAE,eAAe,CAAA;IAC9B,qEAAqE;IACrE,QAAQ,CAAC,WAAW,EAAE,MAAM,OAAO,CAAC,OAAO,CAAC,CAAA;IAC5C,sEAAsE;IACtE,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,IAAI,CAAA;CACvE;AAED,wBAAwB;AACxB,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,kBAAkB,GAAG,IAAI,CA4BrE"}
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
* tmux and service managers); a second signal forces the exit at once.
|
|
7
7
|
*/
|
|
8
8
|
export const TERMINATION_SIGNALS = ['SIGTERM', 'SIGHUP', 'SIGINT'];
|
|
9
|
-
/** dsh `
|
|
9
|
+
/** dsh `process-shutdown` PROCESS_SHUTDOWN_TIMEOUT_MS (0.2.1-rc.2): the dispose bound. */
|
|
10
10
|
export const PROCESS_SHUTDOWN_TIMEOUT_MS = 5000;
|
|
11
11
|
/** Last-resort bound for a funnel exit: outlasts its terminal cleanup plus the dispose bound. */
|
|
12
12
|
const BACKSTOP_MS = PROCESS_SHUTDOWN_TIMEOUT_MS + 2000;
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* One host session-projection key, folded for the session on screen.
|
|
3
|
+
*
|
|
4
|
+
* The host projection feed is process-wide and carries whole values, so a
|
|
5
|
+
* read side needs the same three pieces every time: drop other keys, narrow
|
|
6
|
+
* the wire value with a local guard (the producer package is never imported),
|
|
7
|
+
* and read one baseline snapshot when a session binds — a value is only
|
|
8
|
+
* published on change, so a resumed session would otherwise wait for its next
|
|
9
|
+
* event. The store keeps only the watched session's value: a background
|
|
10
|
+
* session must not overwrite what the screen shows.
|
|
11
|
+
*
|
|
12
|
+
* @module
|
|
13
|
+
*/
|
|
14
|
+
import type { Context } from '@deepseek-ai/cordis';
|
|
15
|
+
import type { ProjectionRegistryLike } from './activity-store.js';
|
|
16
|
+
/** The session whose projection value the session on screen shows. */
|
|
17
|
+
export interface WatchedSession {
|
|
18
|
+
readonly id: string;
|
|
19
|
+
/** The live session object, when there is one to read a baseline from. */
|
|
20
|
+
readonly session?: unknown;
|
|
21
|
+
}
|
|
22
|
+
export interface SessionProjectionStoreOptions<T> {
|
|
23
|
+
/** Projection key the value is published under. */
|
|
24
|
+
readonly key: string;
|
|
25
|
+
/** Narrow one wire value of a session; anything else is dropped, never half-rendered. */
|
|
26
|
+
narrow(value: unknown, sessionId: string): T | undefined;
|
|
27
|
+
/** Map the bound session to the session it reads (default: itself). */
|
|
28
|
+
watch?(session: unknown): WatchedSession;
|
|
29
|
+
/** Read-failure sink, reported once per attached registry (the composition root's logger). */
|
|
30
|
+
warn?(message: string): void;
|
|
31
|
+
}
|
|
32
|
+
export interface SessionProjectionStore<T> {
|
|
33
|
+
/** Remember the host registry; a late registry seeds the bound session. */
|
|
34
|
+
attachRegistry(registry: ProjectionRegistryLike): void;
|
|
35
|
+
/** Forget a registry that left; nothing is read from it again and the value clears. */
|
|
36
|
+
detachRegistry(registry: ProjectionRegistryLike): void;
|
|
37
|
+
/** Bind the session on screen and read its watched value once. */
|
|
38
|
+
seed(session: unknown): void;
|
|
39
|
+
/** Record one value from the change feed. */
|
|
40
|
+
update(sessionId: string, value: unknown): void;
|
|
41
|
+
/**
|
|
42
|
+
* Read the bound session's baseline again. For a change the feed does not
|
|
43
|
+
* carry: a projection definition that left (its key reads as absent) or
|
|
44
|
+
* came back.
|
|
45
|
+
*/
|
|
46
|
+
reseed(): void;
|
|
47
|
+
/** The bound session's id, once a session bound. */
|
|
48
|
+
readonly boundId: string | undefined;
|
|
49
|
+
/** The watched session's id, once a session bound. */
|
|
50
|
+
readonly watchedId: string | undefined;
|
|
51
|
+
/** The watched session's current value, stable between updates. */
|
|
52
|
+
read(): T | undefined;
|
|
53
|
+
subscribe(listener: () => void): () => void;
|
|
54
|
+
noteReadFailure(error: unknown): void;
|
|
55
|
+
}
|
|
56
|
+
export declare function createSessionProjectionStore<T>(options: SessionProjectionStoreOptions<T>): SessionProjectionStore<T>;
|
|
57
|
+
/**
|
|
58
|
+
* Wire one store to the host's projection registry once it exists. The
|
|
59
|
+
* registry and the unit that publishes the key are mounted by other plugins,
|
|
60
|
+
* so both may arrive later — or never, which leaves the store empty.
|
|
61
|
+
* @param ctx - Host context of the composition root.
|
|
62
|
+
* @param key - Projection key the store folds.
|
|
63
|
+
* @param store - Store to fill.
|
|
64
|
+
*/
|
|
65
|
+
export declare function attachSessionProjection(ctx: Context, key: string, store: SessionProjectionStore<unknown>): void;
|
|
66
|
+
//# sourceMappingURL=session-projection-store.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"session-projection-store.d.ts","sourceRoot":"","sources":["../../../src/dsh-adapter/session-projection-store.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAA;AAClD,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,qBAAqB,CAAA;AAEjE,sEAAsE;AACtE,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAA;IACnB,0EAA0E;IAC1E,QAAQ,CAAC,OAAO,CAAC,EAAE,OAAO,CAAA;CAC3B;AAED,MAAM,WAAW,6BAA6B,CAAC,CAAC;IAC9C,mDAAmD;IACnD,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAA;IACpB,yFAAyF;IACzF,MAAM,CAAC,KAAK,EAAE,OAAO,EAAE,SAAS,EAAE,MAAM,GAAG,CAAC,GAAG,SAAS,CAAA;IACxD,uEAAuE;IACvE,KAAK,CAAC,CAAC,OAAO,EAAE,OAAO,GAAG,cAAc,CAAA;IACxC,8FAA8F;IAC9F,IAAI,CAAC,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAA;CAC7B;AAED,MAAM,WAAW,sBAAsB,CAAC,CAAC;IACvC,2EAA2E;IAC3E,cAAc,CAAC,QAAQ,EAAE,sBAAsB,GAAG,IAAI,CAAA;IACtD,uFAAuF;IACvF,cAAc,CAAC,QAAQ,EAAE,sBAAsB,GAAG,IAAI,CAAA;IACtD,kEAAkE;IAClE,IAAI,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAAA;IAC5B,6CAA6C;IAC7C,MAAM,CAAC,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,GAAG,IAAI,CAAA;IAC/C;;;;OAIG;IACH,MAAM,IAAI,IAAI,CAAA;IACd,oDAAoD;IACpD,QAAQ,CAAC,OAAO,EAAE,MAAM,GAAG,SAAS,CAAA;IACpC,sDAAsD;IACtD,QAAQ,CAAC,SAAS,EAAE,MAAM,GAAG,SAAS,CAAA;IACtC,mEAAmE;IACnE,IAAI,IAAI,CAAC,GAAG,SAAS,CAAA;IACrB,SAAS,CAAC,QAAQ,EAAE,MAAM,IAAI,GAAG,MAAM,IAAI,CAAA;IAC3C,eAAe,CAAC,KAAK,EAAE,OAAO,GAAG,IAAI,CAAA;CACtC;AASD,wBAAgB,4BAA4B,CAAC,CAAC,EAAE,OAAO,EAAE,6BAA6B,CAAC,CAAC,CAAC,GAAG,sBAAsB,CAAC,CAAC,CAAC,CAsFpH;AAED;;;;;;;GAOG;AACH,wBAAgB,uBAAuB,CAAC,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,sBAAsB,CAAC,OAAO,CAAC,GAAG,IAAI,CAmB/G"}
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* One host session-projection key, folded for the session on screen.
|
|
3
|
+
*
|
|
4
|
+
* The host projection feed is process-wide and carries whole values, so a
|
|
5
|
+
* read side needs the same three pieces every time: drop other keys, narrow
|
|
6
|
+
* the wire value with a local guard (the producer package is never imported),
|
|
7
|
+
* and read one baseline snapshot when a session binds — a value is only
|
|
8
|
+
* published on change, so a resumed session would otherwise wait for its next
|
|
9
|
+
* event. The store keeps only the watched session's value: a background
|
|
10
|
+
* session must not overwrite what the screen shows.
|
|
11
|
+
*
|
|
12
|
+
* @module
|
|
13
|
+
*/
|
|
14
|
+
/** A raw value no snapshot can return: the next update always narrows. */
|
|
15
|
+
const UNREAD = Symbol('unread');
|
|
16
|
+
function sessionIdOf(session) {
|
|
17
|
+
return String(session.id);
|
|
18
|
+
}
|
|
19
|
+
export function createSessionProjectionStore(options) {
|
|
20
|
+
const listeners = new Set();
|
|
21
|
+
let registry;
|
|
22
|
+
let bound;
|
|
23
|
+
let watched;
|
|
24
|
+
let raw;
|
|
25
|
+
let value;
|
|
26
|
+
let warned = false;
|
|
27
|
+
const emit = () => {
|
|
28
|
+
for (const listener of [...listeners])
|
|
29
|
+
listener();
|
|
30
|
+
};
|
|
31
|
+
const store = {
|
|
32
|
+
attachRegistry(next) {
|
|
33
|
+
registry = next;
|
|
34
|
+
warned = false;
|
|
35
|
+
if (bound !== undefined)
|
|
36
|
+
store.seed(bound);
|
|
37
|
+
},
|
|
38
|
+
detachRegistry(left) {
|
|
39
|
+
if (registry !== left)
|
|
40
|
+
return;
|
|
41
|
+
registry = undefined;
|
|
42
|
+
raw = undefined;
|
|
43
|
+
if (value === undefined)
|
|
44
|
+
return;
|
|
45
|
+
value = undefined;
|
|
46
|
+
emit();
|
|
47
|
+
},
|
|
48
|
+
seed(session) {
|
|
49
|
+
if (session === null || session === undefined)
|
|
50
|
+
return;
|
|
51
|
+
bound = session;
|
|
52
|
+
const next = options.watch?.(session) ?? { id: sessionIdOf(session), session };
|
|
53
|
+
if (next.id !== watched?.id) {
|
|
54
|
+
raw = undefined;
|
|
55
|
+
if (value !== undefined) {
|
|
56
|
+
value = undefined;
|
|
57
|
+
emit();
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
watched = next;
|
|
61
|
+
if (registry === undefined || next.session === undefined)
|
|
62
|
+
return;
|
|
63
|
+
try {
|
|
64
|
+
store.update(next.id, registry.snapshot(next.session, [options.key]).values[options.key]);
|
|
65
|
+
}
|
|
66
|
+
catch (error) {
|
|
67
|
+
store.noteReadFailure(error);
|
|
68
|
+
}
|
|
69
|
+
},
|
|
70
|
+
update(sessionId, next) {
|
|
71
|
+
if (sessionId !== watched?.id)
|
|
72
|
+
return;
|
|
73
|
+
// The host reuses the value reference while its state is unchanged, so
|
|
74
|
+
// identity is the whole change test.
|
|
75
|
+
if (next === raw)
|
|
76
|
+
return;
|
|
77
|
+
raw = next;
|
|
78
|
+
const narrowed = options.narrow(next, sessionId);
|
|
79
|
+
if (narrowed === undefined && value === undefined)
|
|
80
|
+
return;
|
|
81
|
+
value = narrowed;
|
|
82
|
+
emit();
|
|
83
|
+
},
|
|
84
|
+
reseed() {
|
|
85
|
+
if (bound === undefined)
|
|
86
|
+
return;
|
|
87
|
+
// The narrow may read host state the value reference does not track
|
|
88
|
+
// (the team's service presence), so an unchanged value, an absent one
|
|
89
|
+
// included, is re-narrowed.
|
|
90
|
+
const held = raw;
|
|
91
|
+
raw = UNREAD;
|
|
92
|
+
store.seed(bound);
|
|
93
|
+
// No snapshot was read (no registry, the watched session not live, or a
|
|
94
|
+
// failed read): narrow the value already held instead.
|
|
95
|
+
if (raw === UNREAD && held !== UNREAD && watched !== undefined)
|
|
96
|
+
store.update(watched.id, held);
|
|
97
|
+
},
|
|
98
|
+
get boundId() {
|
|
99
|
+
return bound === undefined ? undefined : sessionIdOf(bound);
|
|
100
|
+
},
|
|
101
|
+
get watchedId() {
|
|
102
|
+
return watched?.id;
|
|
103
|
+
},
|
|
104
|
+
read: () => value,
|
|
105
|
+
subscribe(listener) {
|
|
106
|
+
listeners.add(listener);
|
|
107
|
+
return () => { listeners.delete(listener); };
|
|
108
|
+
},
|
|
109
|
+
noteReadFailure(error) {
|
|
110
|
+
if (warned)
|
|
111
|
+
return;
|
|
112
|
+
warned = true;
|
|
113
|
+
const detail = error instanceof Error ? error.message : String(error);
|
|
114
|
+
options.warn?.(`dsh-tui: ${options.key} projection read failed: ${detail}`);
|
|
115
|
+
},
|
|
116
|
+
};
|
|
117
|
+
return store;
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* Wire one store to the host's projection registry once it exists. The
|
|
121
|
+
* registry and the unit that publishes the key are mounted by other plugins,
|
|
122
|
+
* so both may arrive later — or never, which leaves the store empty.
|
|
123
|
+
* @param ctx - Host context of the composition root.
|
|
124
|
+
* @param key - Projection key the store folds.
|
|
125
|
+
* @param store - Store to fill.
|
|
126
|
+
*/
|
|
127
|
+
export function attachSessionProjection(ctx, key, store) {
|
|
128
|
+
ctx.inject(['sessionProjections'], ((projectionCtx) => {
|
|
129
|
+
const registry = projectionCtx.sessionProjections;
|
|
130
|
+
if (registry === undefined)
|
|
131
|
+
return;
|
|
132
|
+
store.attachRegistry(registry);
|
|
133
|
+
let offFeed;
|
|
134
|
+
try {
|
|
135
|
+
offFeed = registry.onChanged((session, changed, value) => {
|
|
136
|
+
if (changed === key)
|
|
137
|
+
store.update(sessionIdOf(session), value);
|
|
138
|
+
});
|
|
139
|
+
}
|
|
140
|
+
catch (error) {
|
|
141
|
+
store.noteReadFailure(error);
|
|
142
|
+
}
|
|
143
|
+
// On the injected fiber: a re-provided registry re-runs this callback.
|
|
144
|
+
projectionCtx.effect(() => () => {
|
|
145
|
+
offFeed?.();
|
|
146
|
+
store.detachRegistry(registry);
|
|
147
|
+
}, `dsh-tui ${key} projection feed`);
|
|
148
|
+
}));
|
|
149
|
+
}
|
|
@@ -183,7 +183,7 @@ export class SettingsForm {
|
|
|
183
183
|
if (field.secret !== undefined) {
|
|
184
184
|
// Blank drafts write nothing; typed drafts replace the credential.
|
|
185
185
|
if (staged.text !== '')
|
|
186
|
-
secrets.push({ ref: field.secret.ref, text: staged.text });
|
|
186
|
+
secrets.push({ ref: field.secret.ref, text: staged.text, field });
|
|
187
187
|
continue;
|
|
188
188
|
}
|
|
189
189
|
const write = staged.clear ? { kind: 'clear' } : defaultParse(field, staged.text);
|
|
@@ -210,7 +210,7 @@ export class SettingsForm {
|
|
|
210
210
|
}
|
|
211
211
|
}
|
|
212
212
|
for (const secret of secrets) {
|
|
213
|
-
await this.host.writeCredential(secret.ref, secret.text);
|
|
213
|
+
await this.host.writeCredential(secret.ref, secret.text, secret.field.secret?.authorization);
|
|
214
214
|
}
|
|
215
215
|
for (const [key, edit] of snapshot) {
|
|
216
216
|
if (this.edits.get(key) === edit)
|
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
*
|
|
7
7
|
* @module
|
|
8
8
|
*/
|
|
9
|
+
import type { Agent } from '@deepseek-ai/dsh-agent';
|
|
9
10
|
import { type ContentBlock, type Message, type StreamChunk } from '@deepseek-ai/dsh-llm';
|
|
10
11
|
import type { SessionEvent } from '@deepseek-ai/dsh-session';
|
|
11
12
|
type ToolCall = Extract<ContentBlock, {
|
|
@@ -71,5 +72,25 @@ export declare function runSideQuestion(params: {
|
|
|
71
72
|
/** Cancellation: aborting yields `{answer: null}` with no error text. */
|
|
72
73
|
signal?: AbortSignal;
|
|
73
74
|
}): Promise<SideQuestionOutcome>;
|
|
75
|
+
/** The `agents` service as {@link runAsSideBranch} needs it. */
|
|
76
|
+
export interface InitiatorScope {
|
|
77
|
+
withInitiator<T>(agent: Agent, operation: () => T): T;
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Run a side question with a seeded fork of `agent` as its initiator. The
|
|
81
|
+
* call replays the session's history without entering it, which DSH models as
|
|
82
|
+
* a fork (`parentSession` + `isSeeded` on the child header). Initiator-keyed
|
|
83
|
+
* consumers then see a branch of the conversation rather than an agentless
|
|
84
|
+
* call: a compressing proxy (billion-context) adopts the parent's state for
|
|
85
|
+
* the branch instead of sending the raw replay, which overflows long sessions.
|
|
86
|
+
* Without the service, or once its scope is closing, the call runs as before.
|
|
87
|
+
*
|
|
88
|
+
* The view is no stored Session: `session.id` carries the branch identity
|
|
89
|
+
* while `header.id` deliberately keeps the parent's (a stored fork's header id
|
|
90
|
+
* mirrors its own session). The verified consumer path reads `session.id`;
|
|
91
|
+
* aligning `header.id` changes what a parent-resolving consumer sees and needs
|
|
92
|
+
* the same real-host recheck. scripts/verify-preset-patch.ts pins this shape.
|
|
93
|
+
*/
|
|
94
|
+
export declare function runAsSideBranch<T>(agents: Partial<InitiatorScope> | undefined, agent: Agent, operation: () => Promise<T>): Promise<T>;
|
|
74
95
|
export {};
|
|
75
96
|
//# sourceMappingURL=sideQuestion.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sideQuestion.d.ts","sourceRoot":"","sources":["../../../src/dsh-adapter/sideQuestion.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAkB,KAAK,YAAY,EAAE,KAAK,OAAO,EAAE,KAAK,WAAW,EAAE,MAAM,sBAAsB,CAAA;AACxG,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,0BAA0B,CAAA;
|
|
1
|
+
{"version":3,"file":"sideQuestion.d.ts","sourceRoot":"","sources":["../../../src/dsh-adapter/sideQuestion.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,wBAAwB,CAAA;AACnD,OAAO,EAAkB,KAAK,YAAY,EAAE,KAAK,OAAO,EAAE,KAAK,WAAW,EAAE,MAAM,sBAAsB,CAAA;AACxG,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,0BAA0B,CAAA;AAK5D,KAAK,QAAQ,GAAG,OAAO,CAAC,YAAY,EAAE;IAAE,IAAI,EAAE,WAAW,CAAA;CAAE,CAAC,CAAA;AAK5D;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,GAAE,SAAS,QAAQ,EAAO,GAAG,MAAM,CAG5F;AAeD;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,wBAAwB,CAAC,QAAQ,EAAE,SAAS,OAAO,EAAE,EAAE,OAAO,EAAE,WAAW,CAAC,MAAM,CAAC,GAAG;IACpG,QAAQ,EAAE,SAAS,OAAO,EAAE,CAAA;IAC5B,OAAO,EAAE,QAAQ,EAAE,CAAA;CACpB,CA2BA;AAED;;;;;;;GAOG;AACH,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,SAAS,YAAY,EAAE,GAAG,GAAG,CAAC,MAAM,CAAC,CAUhF;AAED,0EAA0E;AAC1E,MAAM,WAAW,mBAAmB;IAClC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAA;IACrB,KAAK,CAAC,EAAE,MAAM,CAAA;CACf;AAED;;;;;GAKG;AACH,wBAAsB,eAAe,CAAC,MAAM,EAAE;IAC5C,yEAAyE;IACzE,MAAM,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,aAAa,CAAC,WAAW,CAAC,CAAA;IACvD,0DAA0D;IAC1D,OAAO,EAAE,MAAM,CAAA;IACf,iDAAiD;IACjD,MAAM,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAA;IAChC,yEAAyE;IACzE,MAAM,CAAC,EAAE,WAAW,CAAA;CACrB,GAAG,OAAO,CAAC,mBAAmB,CAAC,CAuB/B;AAED,gEAAgE;AAChE,MAAM,WAAW,cAAc;IAC7B,aAAa,CAAC,CAAC,EAAE,KAAK,EAAE,KAAK,EAAE,SAAS,EAAE,MAAM,CAAC,GAAG,CAAC,CAAA;CACtD;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,eAAe,CAAC,CAAC,EAAE,MAAM,EAAE,OAAO,CAAC,cAAc,CAAC,GAAG,SAAS,EAAE,KAAK,EAAE,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAOrI"}
|
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
* @module
|
|
8
8
|
*/
|
|
9
9
|
import { BlockAssembler } from '@deepseek-ai/dsh-llm';
|
|
10
|
+
import { randomUUID } from 'node:crypto';
|
|
10
11
|
import { sideQuestionPrompt } from '../channel/side-prompts.js';
|
|
11
12
|
import { answeredToolCallIds } from './compat/messages.js';
|
|
12
13
|
/** Per-call argument budget in the pending-calls note; a file write can carry megabytes. */
|
|
@@ -143,3 +144,55 @@ export async function runSideQuestion(params) {
|
|
|
143
144
|
return { answer: null, error: 'No response received' };
|
|
144
145
|
return { answer };
|
|
145
146
|
}
|
|
147
|
+
/**
|
|
148
|
+
* Run a side question with a seeded fork of `agent` as its initiator. The
|
|
149
|
+
* call replays the session's history without entering it, which DSH models as
|
|
150
|
+
* a fork (`parentSession` + `isSeeded` on the child header). Initiator-keyed
|
|
151
|
+
* consumers then see a branch of the conversation rather than an agentless
|
|
152
|
+
* call: a compressing proxy (billion-context) adopts the parent's state for
|
|
153
|
+
* the branch instead of sending the raw replay, which overflows long sessions.
|
|
154
|
+
* Without the service, or once its scope is closing, the call runs as before.
|
|
155
|
+
*
|
|
156
|
+
* The view is no stored Session: `session.id` carries the branch identity
|
|
157
|
+
* while `header.id` deliberately keeps the parent's (a stored fork's header id
|
|
158
|
+
* mirrors its own session). The verified consumer path reads `session.id`;
|
|
159
|
+
* aligning `header.id` changes what a parent-resolving consumer sees and needs
|
|
160
|
+
* the same real-host recheck. scripts/verify-preset-patch.ts pins this shape.
|
|
161
|
+
*/
|
|
162
|
+
export function runAsSideBranch(agents, agent, operation) {
|
|
163
|
+
if (typeof agents?.withInitiator !== 'function')
|
|
164
|
+
return operation();
|
|
165
|
+
try {
|
|
166
|
+
return agents.withInitiator(sideBranchOf(agent), operation);
|
|
167
|
+
}
|
|
168
|
+
catch {
|
|
169
|
+
return operation();
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
function sideBranchOf(agent) {
|
|
173
|
+
const session = agent.session;
|
|
174
|
+
const surface = session.surface;
|
|
175
|
+
const last = surface?.nodes?.at(-1);
|
|
176
|
+
const generation = surface?.replaceGeneration;
|
|
177
|
+
const id = last === undefined || generation === undefined
|
|
178
|
+
? `${session.id}-btw-${randomUUID()}`
|
|
179
|
+
: `${session.id}-btw-${generation}-${last}`;
|
|
180
|
+
// header.id stays the parent's on purpose; see runAsSideBranch.
|
|
181
|
+
const header = { ...session.header, parentSession: session.id, isSeeded: true };
|
|
182
|
+
return readThrough(agent, { session: readThrough(session, { id, header }) });
|
|
183
|
+
}
|
|
184
|
+
/**
|
|
185
|
+
* A view of `target` with some properties replaced. Everything else, methods
|
|
186
|
+
* included, still acts on `target` (a mutating call reaches the real
|
|
187
|
+
* session); the view lives only for one side call's stream.
|
|
188
|
+
*/
|
|
189
|
+
function readThrough(target, overrides) {
|
|
190
|
+
return new Proxy(target, {
|
|
191
|
+
get(object, key) {
|
|
192
|
+
if (Object.hasOwn(overrides, key))
|
|
193
|
+
return overrides[key];
|
|
194
|
+
const value = Reflect.get(object, key, object);
|
|
195
|
+
return typeof value === 'function' ? value.bind(object) : value;
|
|
196
|
+
},
|
|
197
|
+
});
|
|
198
|
+
}
|