@gotgenes/pi-subagents 18.0.1 → 18.0.3
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/CHANGELOG.md +15 -0
- package/docs/architecture/architecture.md +165 -203
- package/docs/architecture/history/phase-19-implement-ui-decisions.md +282 -0
- package/package.json +6 -7
- package/src/config/default-agents.ts +3 -0
- package/src/lifecycle/subagent.ts +25 -14
- package/src/observation/notification.ts +23 -10
- package/src/observation/subagent-events-observer.ts +1 -3
- package/src/runtime.ts +2 -1
- package/src/service/service-adapter.ts +20 -17
- package/src/session/model-resolver.ts +31 -30
- package/src/tools/agent-tool.ts +17 -13
- package/src/tools/get-result-report.ts +60 -0
- package/src/tools/get-result-tool.ts +25 -40
- package/src/tools/helpers.ts +21 -5
- package/src/tools/spawn-config.ts +5 -5
- package/src/tools/steer-tool.ts +37 -27
- package/src/types.ts +4 -1
- package/src/ui/session-navigator.ts +0 -2
- package/.prettierignore +0 -5
- package/AGENTS.md +0 -7
- package/docs/plans/0048-implement-subagents-api.md +0 -304
- package/docs/plans/0049-remove-group-join-output-file-rpc.md +0 -180
- package/docs/plans/0051-update-adr-0001-hard-fork.md +0 -77
- package/docs/plans/0052-remove-scheduled-subagents.md +0 -133
- package/docs/plans/0053-extract-model-resolution-from-execute.md +0 -181
- package/docs/plans/0054-decompose-index-into-modules.md +0 -302
- package/docs/plans/0057-structured-debug-logging.md +0 -154
- package/docs/plans/0061-session-format-transcript.md +0 -284
- package/docs/plans/0066-replace-as-any-with-sdk-types.md +0 -215
- package/docs/plans/0069-create-subagent-runtime.md +0 -345
- package/docs/plans/0070-extract-event-handlers.md +0 -306
- package/docs/plans/0071-extract-session-config-assembler.md +0 -362
- package/docs/plans/0072-inject-agent-manager-collaborators.md +0 -329
- package/docs/plans/0076-inject-cwd-into-agent-manager.md +0 -102
- package/docs/plans/0077-inject-project-agents-dir.md +0 -115
- package/docs/plans/0080-consolidate-agent-config-lookup.md +0 -247
- package/docs/plans/0084-extract-git-worktree-manager.md +0 -142
- package/docs/plans/0087-evolve-subagent-runtime-methods.md +0 -240
- package/docs/plans/0098-extract-agent-record-state-machine.md +0 -435
- package/docs/plans/0099-replace-ctx-with-parent-snapshot.md +0 -488
- package/docs/plans/0100-replace-callback-threading-with-session-subscription.md +0 -454
- package/docs/plans/0102-consolidate-test-record-factory.md +0 -176
- package/docs/plans/0108-extract-agent-type-registry.md +0 -322
- package/docs/plans/0109-extract-settings-manager.md +0 -276
- package/docs/plans/0110-agent-activity-tracker.md +0 -297
- package/docs/plans/0111-split-agent-record-lifecycle.md +0 -582
- package/docs/plans/0112-replace-agent-manager-callbacks.md +0 -241
- package/docs/plans/0113-disambiguate-spawn-options.md +0 -155
- package/docs/plans/0114-narrow-agent-tool-menu-deps.md +0 -279
- package/docs/plans/0115-decompose-agent-tool.md +0 -337
- package/docs/plans/0116-type-housekeeping.md +0 -351
- package/docs/plans/0118-settings-manager-apply-methods.md +0 -271
- package/docs/plans/0123-remove-vi-fn-cast-smell.md +0 -179
- package/docs/plans/0131-consolidate-shared-test-fixtures.md +0 -207
- package/docs/plans/0132-inject-io-into-session-config.md +0 -219
- package/docs/plans/0133-inject-sdk-boundary-into-agent-runner.md +0 -373
- package/docs/plans/0134-reduce-as-any-casts.md +0 -366
- package/docs/plans/0135-extract-display-helpers.md +0 -182
- package/docs/plans/0136-decompose-agent-menu.md +0 -300
- package/docs/plans/0144-consolidate-observation-model.md +0 -263
- package/docs/plans/0145-decompose-execute-push-ctx-to-boundary.md +0 -290
- package/docs/plans/0146-narrow-ui-context.md +0 -319
- package/docs/plans/0147-inject-wrap-text-into-conversation-viewer.md +0 -166
- package/docs/plans/0148-split-agent-widget-rendering.md +0 -255
- package/docs/plans/0152-add-prompt-snippet.md +0 -91
- package/docs/plans/0164-reorganize-into-domain-directories.md +0 -409
- package/docs/plans/0165-decompose-resolved-spawn-config.md +0 -157
- package/docs/plans/0166-extract-parent-session-info.md +0 -231
- package/docs/plans/0167-narrow-runner-io.md +0 -150
- package/docs/plans/0168-extract-tool-filter-config.md +0 -173
- package/docs/plans/0169-extract-run-context-from-run-options.md +0 -194
- package/docs/plans/0170-reduce-build-content-lines-complexity.md +0 -225
- package/docs/plans/0171-reduce-render-result-complexity.md +0 -200
- package/docs/plans/0172-extract-turn-formatting.md +0 -206
- package/docs/plans/0180-reorder-append-prompt-for-kv-cache.md +0 -100
- package/docs/plans/0185-remove-persistent-agent-memory.md +0 -161
- package/docs/plans/0188-replace-any-casts-with-sdk-types.md +0 -162
- package/docs/plans/0192-define-session-context-interface.md +0 -107
- package/docs/plans/0193-runtime-owns-context-queries.md +0 -245
- package/docs/plans/0194-align-tool-interfaces-for-structural-typing.md +0 -139
- package/docs/plans/0195-convert-tool-factories-to-classes.md +0 -264
- package/docs/plans/0196-convert-runner-menu-to-classes.md +0 -268
- package/docs/plans/0205-decompose-render-widget-lines.md +0 -140
- package/docs/plans/0206-decompose-show-agent-detail.md +0 -211
- package/docs/plans/0207-decompose-agent-widget-update.md +0 -242
- package/docs/plans/0208-extract-shared-test-fixtures.md +0 -298
- package/docs/plans/0214-convert-remaining-closure-factories-to-classes.md +0 -261
- package/docs/plans/0215-decompose-build-parent-context.md +0 -166
- package/docs/plans/0216-decompose-start-agent.md +0 -255
- package/docs/plans/0217-extract-overwrite-guard.md +0 -176
- package/docs/plans/0218-push-sdk-boundary-in-settings.md +0 -172
- package/docs/plans/0219-reduce-test-duplication-top-3-clone-families.md +0 -162
- package/docs/plans/0227-evolve-agent-record-into-agent.md +0 -322
- package/docs/plans/0228-async-start-agent-dissolve-run-handle.md +0 -288
- package/docs/plans/0229-agent-born-complete.md +0 -564
- package/docs/plans/0230-extract-concurrency-queue.md +0 -265
- package/docs/plans/0231-push-exec-registry-to-runner.md +0 -245
- package/docs/plans/0232-agent-resume-internal-observer-lifecycle.md +0 -180
- package/docs/plans/0237-remove-disallowed-tools.md +0 -180
- package/docs/plans/0238-remove-extensions-filtering.md +0 -191
- package/docs/plans/0239-collapse-filter-active-tools.md +0 -217
- package/docs/plans/0242-rename-agent-tool-to-subagent.md +0 -165
- package/docs/plans/0256-extract-worktree-isolation.md +0 -256
- package/docs/plans/0257-extract-child-session-factory.md +0 -285
- package/docs/plans/0262-add-workspace-provider-seam.md +0 -264
- package/docs/plans/0264-remove-extension-lifecycle-control.md +0 -277
- package/docs/plans/0265-born-complete-subagent-session.md +0 -333
- package/docs/plans/0270-type-consumable-public-surface.md +0 -204
- package/docs/plans/0272-export-workspace-collaborator-types.md +0 -147
- package/docs/plans/0277-encapsulate-agent-session.md +0 -304
- package/docs/plans/0280-rename-agent-to-subagent.md +0 -197
- package/docs/plans/0360-custom-agents-default-append.md +0 -132
- package/docs/plans/0373-extract-subagent-state.md +0 -250
- package/docs/plans/0374-encapsulate-subagent-start-notification.md +0 -268
- package/docs/plans/0375-extract-run-listener-workspace-bracket.md +0 -300
- package/docs/plans/0376-extract-manager-observer.md +0 -232
- package/docs/plans/0377-split-widget-delegation-from-runtime.md +0 -254
- package/docs/plans/0378-consolidate-lifecycle-test-fixtures.md +0 -243
- package/docs/plans/0379-consolidate-ui-tools-test-fixtures.md +0 -335
- package/docs/plans/0380-extract-layered-settings-loader.md +0 -223
- package/docs/plans/0381-replace-concurrency-queue-with-limiter.md +0 -267
- package/docs/plans/0400-include-parent-prompt-in-replace-mode.md +0 -199
- package/docs/plans/0403-abort-subagents-on-interrupt.md +0 -180
- package/docs/plans/0412-unify-session-mock-builders.md +0 -146
- package/docs/plans/0420-fold-run-metrics-live-activity-onto-record.md +0 -228
- package/docs/plans/0421-migrate-activity-readers-to-record-getters.md +0 -309
- package/docs/plans/0422-delete-activity-tracker-ui-observer.md +0 -204
- package/docs/plans/0423-widget-self-drive-from-lifecycle.md +0 -264
- package/docs/plans/0424-drop-widget-dep-from-subagent-tool.md +0 -166
- package/docs/plans/0425-reconcile-subagent-events-contract.md +0 -144
- package/docs/plans/0426-consolidate-residual-test-clone-families.md +0 -235
- package/docs/plans/0427-reconsider-ui-direction.md +0 -212
- package/docs/plans/0441-remove-agent-definition-management-subtree.md +0 -180
- package/docs/plans/0442-dissolve-agents-conversation-viewer.md +0 -195
- package/docs/plans/0443-consolidate-remaining-test-clone-families.md +0 -268
- package/docs/plans/0444-shrink-widget-to-background-agents.md +0 -156
- package/docs/plans/0445-native-session-navigation.md +0 -365
- package/docs/plans/0446-spike-session-navigation-entry-criteria.md +0 -171
- package/docs/plans/0447-extract-subagents-settings-command.md +0 -220
- package/docs/plans/0448-block-disabled-agent-spawn.md +0 -207
- package/docs/plans/0462-navigation-renderer-tui-components.md +0 -338
- package/docs/plans/0463-file-snapshot-source-evicted-agents.md +0 -306
- package/docs/plans/0470-refresh-readme-command-surface.md +0 -159
- package/docs/retro/0048-implement-subagents-api.md +0 -44
- package/docs/retro/0049-remove-group-join-output-file-rpc.md +0 -49
- package/docs/retro/0051-update-adr-0001-hard-fork.md +0 -39
- package/docs/retro/0053-extract-model-resolution-from-execute.md +0 -40
- package/docs/retro/0054-decompose-index-into-modules.md +0 -53
- package/docs/retro/0057-structured-debug-logging.md +0 -77
- package/docs/retro/0061-session-format-transcript.md +0 -41
- package/docs/retro/0066-replace-as-any-with-sdk-types.md +0 -44
- package/docs/retro/0069-create-subagent-runtime.md +0 -43
- package/docs/retro/0070-extract-event-handlers.md +0 -49
- package/docs/retro/0071-extract-session-config-assembler.md +0 -60
- package/docs/retro/0072-inject-agent-manager-collaborators.md +0 -46
- package/docs/retro/0076-inject-cwd-into-agent-manager.md +0 -35
- package/docs/retro/0077-inject-project-agents-dir.md +0 -33
- package/docs/retro/0080-consolidate-agent-config-lookup.md +0 -35
- package/docs/retro/0084-extract-git-worktree-manager.md +0 -37
- package/docs/retro/0087-evolve-subagent-runtime-methods.md +0 -42
- package/docs/retro/0098-extract-agent-record-state-machine.md +0 -46
- package/docs/retro/0099-replace-ctx-with-parent-snapshot.md +0 -37
- package/docs/retro/0100-replace-callback-threading-with-session-subscription.md +0 -36
- package/docs/retro/0102-consolidate-test-record-factory.md +0 -30
- package/docs/retro/0108-extract-agent-type-registry.md +0 -41
- package/docs/retro/0109-extract-settings-manager.md +0 -55
- package/docs/retro/0110-agent-activity-tracker.md +0 -44
- package/docs/retro/0111-split-agent-record-lifecycle.md +0 -61
- package/docs/retro/0112-replace-agent-manager-callbacks.md +0 -35
- package/docs/retro/0113-disambiguate-spawn-options.md +0 -29
- package/docs/retro/0114-narrow-agent-tool-menu-deps.md +0 -38
- package/docs/retro/0115-decompose-agent-tool.md +0 -51
- package/docs/retro/0116-type-housekeeping.md +0 -42
- package/docs/retro/0118-settings-manager-apply-methods.md +0 -40
- package/docs/retro/0123-remove-vi-fn-cast-smell.md +0 -49
- package/docs/retro/0131-consolidate-shared-test-fixtures.md +0 -46
- package/docs/retro/0132-inject-io-into-session-config.md +0 -33
- package/docs/retro/0133-inject-sdk-boundary-into-agent-runner.md +0 -45
- package/docs/retro/0134-reduce-as-any-casts.md +0 -56
- package/docs/retro/0135-extract-display-helpers.md +0 -38
- package/docs/retro/0136-decompose-agent-menu.md +0 -43
- package/docs/retro/0144-consolidate-observation-model.md +0 -39
- package/docs/retro/0145-decompose-execute-push-ctx-to-boundary.md +0 -56
- package/docs/retro/0146-narrow-ui-context.md +0 -70
- package/docs/retro/0147-inject-wrap-text-into-conversation-viewer.md +0 -90
- package/docs/retro/0148-split-agent-widget-rendering.md +0 -39
- package/docs/retro/0152-add-prompt-snippet.md +0 -34
- package/docs/retro/0164-reorganize-into-domain-directories.md +0 -82
- package/docs/retro/0165-decompose-resolved-spawn-config.md +0 -78
- package/docs/retro/0166-extract-parent-session-info.md +0 -66
- package/docs/retro/0167-narrow-runner-io.md +0 -63
- package/docs/retro/0168-extract-tool-filter-config.md +0 -76
- package/docs/retro/0169-extract-run-context-from-run-options.md +0 -68
- package/docs/retro/0170-reduce-build-content-lines-complexity.md +0 -72
- package/docs/retro/0171-reduce-render-result-complexity.md +0 -77
- package/docs/retro/0172-extract-turn-formatting.md +0 -80
- package/docs/retro/0180-reorder-append-prompt-for-kv-cache.md +0 -62
- package/docs/retro/0185-remove-persistent-agent-memory.md +0 -73
- package/docs/retro/0188-replace-any-casts-with-sdk-types.md +0 -69
- package/docs/retro/0192-define-session-context-interface.md +0 -59
- package/docs/retro/0193-runtime-owns-context-queries.md +0 -75
- package/docs/retro/0194-align-tool-interfaces-for-structural-typing.md +0 -62
- package/docs/retro/0195-convert-tool-factories-to-classes.md +0 -84
- package/docs/retro/0196-convert-runner-menu-to-classes.md +0 -73
- package/docs/retro/0205-decompose-render-widget-lines.md +0 -64
- package/docs/retro/0206-decompose-show-agent-detail.md +0 -67
- package/docs/retro/0207-decompose-agent-widget-update.md +0 -92
- package/docs/retro/0208-extract-shared-test-fixtures.md +0 -102
- package/docs/retro/0214-convert-remaining-closure-factories-to-classes.md +0 -66
- package/docs/retro/0215-decompose-build-parent-context.md +0 -61
- package/docs/retro/0216-decompose-start-agent.md +0 -80
- package/docs/retro/0217-extract-overwrite-guard.md +0 -63
- package/docs/retro/0218-push-sdk-boundary-in-settings.md +0 -72
- package/docs/retro/0219-reduce-test-duplication-top-3-clone-families.md +0 -64
- package/docs/retro/0227-evolve-agent-record-into-agent.md +0 -80
- package/docs/retro/0228-async-start-agent-dissolve-run-handle.md +0 -80
- package/docs/retro/0229-agent-born-complete.md +0 -89
- package/docs/retro/0230-extract-concurrency-queue.md +0 -38
- package/docs/retro/0231-push-exec-registry-to-runner.md +0 -71
- package/docs/retro/0232-agent-resume-internal-observer-lifecycle.md +0 -109
- package/docs/retro/0237-remove-disallowed-tools.md +0 -78
- package/docs/retro/0238-remove-extensions-filtering.md +0 -72
- package/docs/retro/0239-collapse-filter-active-tools.md +0 -70
- package/docs/retro/0242-rename-agent-tool-to-subagent.md +0 -82
- package/docs/retro/0256-extract-worktree-isolation.md +0 -89
- package/docs/retro/0257-extract-child-session-factory.md +0 -33
- package/docs/retro/0262-add-workspace-provider-seam.md +0 -89
- package/docs/retro/0264-remove-extension-lifecycle-control.md +0 -91
- package/docs/retro/0265-born-complete-subagent-session.md +0 -97
- package/docs/retro/0270-type-consumable-public-surface.md +0 -108
- package/docs/retro/0272-export-workspace-collaborator-types.md +0 -38
- package/docs/retro/0277-encapsulate-agent-session.md +0 -80
- package/docs/retro/0280-rename-agent-to-subagent.md +0 -96
- package/docs/retro/0360-custom-agents-default-append.md +0 -81
- package/docs/retro/0373-extract-subagent-state.md +0 -94
- package/docs/retro/0374-encapsulate-subagent-start-notification.md +0 -171
- package/docs/retro/0375-extract-run-listener-workspace-bracket.md +0 -104
- package/docs/retro/0376-extract-manager-observer.md +0 -81
- package/docs/retro/0377-split-widget-delegation-from-runtime.md +0 -105
- package/docs/retro/0378-consolidate-lifecycle-test-fixtures.md +0 -100
- package/docs/retro/0379-consolidate-ui-tools-test-fixtures.md +0 -52
- package/docs/retro/0380-extract-layered-settings-loader.md +0 -97
- package/docs/retro/0381-replace-concurrency-queue-with-limiter.md +0 -95
- package/docs/retro/0400-include-parent-prompt-in-replace-mode.md +0 -84
- package/docs/retro/0403-abort-subagents-on-interrupt.md +0 -90
- package/docs/retro/0412-unify-session-mock-builders.md +0 -80
- package/docs/retro/0420-fold-run-metrics-live-activity-onto-record.md +0 -90
- package/docs/retro/0421-migrate-activity-readers-to-record-getters.md +0 -105
- package/docs/retro/0422-delete-activity-tracker-ui-observer.md +0 -97
- package/docs/retro/0423-widget-self-drive-from-lifecycle.md +0 -112
- package/docs/retro/0424-drop-widget-dep-from-subagent-tool.md +0 -89
- package/docs/retro/0425-reconcile-subagent-events-contract.md +0 -101
- package/docs/retro/0426-consolidate-residual-test-clone-families.md +0 -100
- package/docs/retro/0427-reconsider-ui-direction.md +0 -102
- package/docs/retro/0441-remove-agent-definition-management-subtree.md +0 -97
- package/docs/retro/0442-dissolve-agents-conversation-viewer.md +0 -108
- package/docs/retro/0443-consolidate-remaining-test-clone-families.md +0 -28
- package/docs/retro/0444-shrink-widget-to-background-agents.md +0 -81
- package/docs/retro/0445-native-session-navigation.md +0 -122
- package/docs/retro/0446-spike-session-navigation-entry-criteria.md +0 -99
- package/docs/retro/0447-extract-subagents-settings-command.md +0 -91
- package/docs/retro/0448-block-disabled-agent-spawn.md +0 -75
- package/docs/retro/0462-navigation-renderer-tui-components.md +0 -105
- package/docs/retro/0463-file-snapshot-source-evicted-agents.md +0 -103
- package/docs/retro/0470-refresh-readme-command-surface.md +0 -40
- package/src/observation/notification-state.ts +0 -27
- package/vitest.config.ts +0 -14
|
@@ -1,351 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
issue: 116
|
|
3
|
-
issue_title: "refactor(pi-subagents): type housekeeping and small structural cleanups"
|
|
4
|
-
---
|
|
5
|
-
|
|
6
|
-
# Type housekeeping and small structural cleanups
|
|
7
|
-
|
|
8
|
-
## Problem Statement
|
|
9
|
-
|
|
10
|
-
`types.ts` is a type dumping ground — it collects types that don't have a natural home because the module that should own them didn't exist yet.
|
|
11
|
-
With foundation extractions (#108 registry, `parent-snapshot.ts`, `env.ts`, `notification.ts`) now in place, most of these types have a natural home.
|
|
12
|
-
Several other small housekeeping items share the same "polish" character: a closure-bag factory that is a class in disguise, a positional constructor that creates test friction, and a 22-field `AgentConfig` interface consumed by modules that touch 2–4 of its fields.
|
|
13
|
-
|
|
14
|
-
## Goals
|
|
15
|
-
|
|
16
|
-
- Move `NotificationDetails` to `notification.ts`, `ParentSnapshot` to `parent-snapshot.ts`, and `EnvInfo` to `env.ts`.
|
|
17
|
-
- Convert `createNotificationSystem` closure to a `NotificationManager` class.
|
|
18
|
-
- Convert `ConversationViewer` constructor from 7 positional parameters to an options bag.
|
|
19
|
-
- Define narrow `AgentConfig` subset interfaces for consumers that use only a few fields.
|
|
20
|
-
- Leave `types.ts` containing only genuinely cross-cutting types: `SubagentType`, `MemoryScope`, `IsolationMode`, `AgentConfig`, `AgentInvocation`, `ShellExec`, and re-exports (`AgentRecord`, `ThinkingLevel`).
|
|
21
|
-
|
|
22
|
-
## Non-Goals
|
|
23
|
-
|
|
24
|
-
- Refactoring `AgentConfig` itself (field additions, removals, renames).
|
|
25
|
-
- Changing the `NotificationDeps` interface shape.
|
|
26
|
-
- Modifying `agent-menu.ts` to use narrow subsets — it legitimately reads most `AgentConfig` fields as a config editor/viewer.
|
|
27
|
-
- Touching `invocation-config.ts` — it already receives `AgentConfig | undefined` and only reads invocation-default fields; narrowing its parameter is a separate concern.
|
|
28
|
-
|
|
29
|
-
## Background
|
|
30
|
-
|
|
31
|
-
### Prerequisite status
|
|
32
|
-
|
|
33
|
-
| Issue | Title | Status |
|
|
34
|
-
| ----- | --------------------------------- | ------- |
|
|
35
|
-
| #108 | Extract `AgentTypeRegistry` class | ✅ Done |
|
|
36
|
-
|
|
37
|
-
`DEFAULT_AGENT_NAMES` was already moved to `AgentTypeRegistry.DEFAULT_AGENT_NAMES` as part of #108.
|
|
38
|
-
The remaining work from the issue's checklist is type relocations, two structural conversions, and narrow subset interfaces.
|
|
39
|
-
|
|
40
|
-
### Current `types.ts` exports
|
|
41
|
-
|
|
42
|
-
| Export | Kind | Should stay? |
|
|
43
|
-
| -------------------------------------------------- | --------- | ------------------------------- |
|
|
44
|
-
| `AgentRecord` (re-export) | class | ✅ Cross-cutting |
|
|
45
|
-
| `AgentRecordInit`, `AgentRecordStatus` (re-export) | type | ✅ Cross-cutting |
|
|
46
|
-
| `ThinkingLevel` (re-export) | type | ✅ Cross-cutting |
|
|
47
|
-
| `SubagentType` | type | ✅ Cross-cutting |
|
|
48
|
-
| `MemoryScope` | type | ✅ Cross-cutting |
|
|
49
|
-
| `IsolationMode` | type | ✅ Cross-cutting |
|
|
50
|
-
| `AgentConfig` | interface | ✅ Cross-cutting |
|
|
51
|
-
| `AgentInvocation` | interface | ✅ Cross-cutting |
|
|
52
|
-
| `ShellExec` | type | ✅ Cross-cutting |
|
|
53
|
-
| `NotificationDetails` | interface | ❌ Move to `notification.ts` |
|
|
54
|
-
| `ParentSnapshot` | interface | ❌ Move to `parent-snapshot.ts` |
|
|
55
|
-
| `EnvInfo` | interface | ❌ Move to `env.ts` |
|
|
56
|
-
|
|
57
|
-
### `createNotificationSystem` closure analysis
|
|
58
|
-
|
|
59
|
-
The factory in `notification.ts` shares `pendingNudges` (a `Map`) and timer state across 4 inner functions (`cancelNudge`, `scheduleNudge`, `sendCompletion`, `cleanupCompleted`, `dispose`).
|
|
60
|
-
This is a class in disguise — mutable state + methods that read/write it.
|
|
61
|
-
Converting to a `NotificationManager` class makes the state explicit and lets tests use instance methods directly.
|
|
62
|
-
|
|
63
|
-
### `ConversationViewer` constructor
|
|
64
|
-
|
|
65
|
-
The constructor takes 7 positional parameters:
|
|
66
|
-
|
|
67
|
-
```typescript
|
|
68
|
-
constructor(tui, session, record, activity, theme, done, registry)
|
|
69
|
-
```
|
|
70
|
-
|
|
71
|
-
Every test must reconstruct all 7 in order.
|
|
72
|
-
An options bag reduces friction and is resilient to new parameters.
|
|
73
|
-
|
|
74
|
-
### `AgentConfig` field usage by consumer
|
|
75
|
-
|
|
76
|
-
| Consumer | Fields used |
|
|
77
|
-
| ---------------------------------------------------------- | -------------------------------------------------------------------------------------------- |
|
|
78
|
-
| `agent-widget.ts` (`getDisplayName`, `getPromptModeLabel`) | `name`, `displayName`, `promptMode` |
|
|
79
|
-
| `tools/helpers.ts` (`formatAgentList`) | `name`, `description`, `model` |
|
|
80
|
-
| `prompts.ts` (`buildAgentPrompt`) | `name`, `promptMode`, `systemPrompt` |
|
|
81
|
-
| `session-config.ts` (`assembleSessionConfig`) | `name`, `model`, `thinking`, `maxTurns`, `extensions`, `skills`, `memory`, `disallowedTools` |
|
|
82
|
-
| `agent-menu.ts` | Nearly all fields (config viewer/editor) |
|
|
83
|
-
|
|
84
|
-
Natural clusters for narrow interfaces:
|
|
85
|
-
|
|
86
|
-
- **`AgentIdentity`**: `name`, `displayName`, `description`, `promptMode` — UI display and agent listing.
|
|
87
|
-
- **`AgentPromptConfig`**: `name`, `promptMode`, `systemPrompt` — prompt assembly.
|
|
88
|
-
|
|
89
|
-
`session-config.ts` uses 8 fields; narrowing it yields limited value vs. complexity.
|
|
90
|
-
`invocation-config.ts` receives `AgentConfig | undefined` and reads invocation-default fields; this is a separate concern.
|
|
91
|
-
|
|
92
|
-
## Design Overview
|
|
93
|
-
|
|
94
|
-
### Type relocations
|
|
95
|
-
|
|
96
|
-
Move each type to its natural home module and re-export from `types.ts` during an interim step.
|
|
97
|
-
After updating all importers to point at the new home, remove the re-export.
|
|
98
|
-
This two-phase approach (move + re-export, then update importers + remove re-export) avoids a big-bang commit.
|
|
99
|
-
|
|
100
|
-
However, since importers are few (2–4 each) and the types are only used internally, a single step per type is simpler: move the type, update all importers in the same commit.
|
|
101
|
-
|
|
102
|
-
### `NotificationManager` class
|
|
103
|
-
|
|
104
|
-
Replace the `createNotificationSystem` factory with a class that owns its state:
|
|
105
|
-
|
|
106
|
-
```typescript
|
|
107
|
-
export class NotificationManager {
|
|
108
|
-
private pendingNudges = new Map<string, ReturnType<typeof setTimeout>>();
|
|
109
|
-
|
|
110
|
-
constructor(private deps: NotificationDeps) {}
|
|
111
|
-
|
|
112
|
-
cancelNudge(key: string): void { /* ... */ }
|
|
113
|
-
sendCompletion(record: AgentRecord): void { /* ... */ }
|
|
114
|
-
cleanupCompleted(id: string): void { /* ... */ }
|
|
115
|
-
dispose(): void { /* ... */ }
|
|
116
|
-
|
|
117
|
-
// private helpers
|
|
118
|
-
private scheduleNudge(key: string, send: () => void, delay?: number): void { /* ... */ }
|
|
119
|
-
private emitIndividualNudge(record: AgentRecord): void { /* ... */ }
|
|
120
|
-
}
|
|
121
|
-
```
|
|
122
|
-
|
|
123
|
-
The `NotificationSystem` interface stays as-is — `NotificationManager implements NotificationSystem`.
|
|
124
|
-
The `NotificationDeps` interface is unchanged.
|
|
125
|
-
Callers in `index.ts` switch from `createNotificationSystem(deps)` to `new NotificationManager(deps)`.
|
|
126
|
-
|
|
127
|
-
Consumer call site (index.ts):
|
|
128
|
-
|
|
129
|
-
```typescript
|
|
130
|
-
const notifications = new NotificationManager({
|
|
131
|
-
sendMessage: (msg, opts) => pi.sendMessage(msg, opts),
|
|
132
|
-
agentActivity: runtime.agentActivity,
|
|
133
|
-
markFinished: (id) => runtime.markFinished(id),
|
|
134
|
-
updateWidget: () => runtime.updateWidget(),
|
|
135
|
-
});
|
|
136
|
-
```
|
|
137
|
-
|
|
138
|
-
### `ConversationViewer` options bag
|
|
139
|
-
|
|
140
|
-
Replace positional parameters with a named options interface:
|
|
141
|
-
|
|
142
|
-
```typescript
|
|
143
|
-
export interface ConversationViewerOptions {
|
|
144
|
-
tui: TUI;
|
|
145
|
-
session: AgentSession;
|
|
146
|
-
record: AgentRecord;
|
|
147
|
-
activity: AgentActivityTracker | undefined;
|
|
148
|
-
theme: Theme;
|
|
149
|
-
done: (result: undefined) => void;
|
|
150
|
-
registry: AgentConfigLookup;
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
export class ConversationViewer {
|
|
154
|
-
constructor(private options: ConversationViewerOptions) {
|
|
155
|
-
// destructure into private fields or use options.field access
|
|
156
|
-
}
|
|
157
|
-
}
|
|
158
|
-
```
|
|
159
|
-
|
|
160
|
-
The existing private fields (`tui`, `session`, `record`, etc.) become reads from `this.options.*` or are destructured in the constructor to named private fields.
|
|
161
|
-
The constructor body (session subscription) is unchanged.
|
|
162
|
-
|
|
163
|
-
### Narrow `AgentConfig` subset interfaces
|
|
164
|
-
|
|
165
|
-
Define two narrow interfaces in `types.ts` and make `AgentConfig` extend them:
|
|
166
|
-
|
|
167
|
-
```typescript
|
|
168
|
-
/** UI display and agent listing — name, display name, description, prompt mode. */
|
|
169
|
-
export interface AgentIdentity {
|
|
170
|
-
name: string;
|
|
171
|
-
displayName?: string;
|
|
172
|
-
description: string;
|
|
173
|
-
promptMode: "replace" | "append";
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
/** Prompt assembly — name, prompt mode, system prompt. */
|
|
177
|
-
export interface AgentPromptConfig {
|
|
178
|
-
name: string;
|
|
179
|
-
promptMode: "replace" | "append";
|
|
180
|
-
systemPrompt: string;
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
export interface AgentConfig extends AgentIdentity, AgentPromptConfig {
|
|
184
|
-
// remaining fields unchanged
|
|
185
|
-
}
|
|
186
|
-
```
|
|
187
|
-
|
|
188
|
-
Then update consumers to accept the narrow interface:
|
|
189
|
-
|
|
190
|
-
- `agent-widget.ts`: `getDisplayName(type, registry)` and `getPromptModeLabel(type, registry)` — these go through `AgentConfigLookup.resolveAgentConfig()`, which returns `AgentConfig`.
|
|
191
|
-
The narrowing applies to the *return type usage*, not the function signature.
|
|
192
|
-
No change needed here — the function already destructures only `displayName`, `name`, `promptMode`.
|
|
193
|
-
- `tools/helpers.ts`: `formatAgentList` reads `description`, `model` — these are not in `AgentIdentity`.
|
|
194
|
-
`model` is session-config territory.
|
|
195
|
-
Leave as-is.
|
|
196
|
-
- `prompts.ts`: `buildAgentPrompt(config: AgentPromptConfig, ...)` — narrow the parameter type.
|
|
197
|
-
- `AgentConfigLookup.resolveAgentConfig()` return type stays `AgentConfig` — the registry always has the full config.
|
|
198
|
-
|
|
199
|
-
Since `AgentConfig extends AgentIdentity, AgentPromptConfig`, any code passing an `AgentConfig` to a function expecting the narrow type works without casts.
|
|
200
|
-
|
|
201
|
-
## Module-Level Changes
|
|
202
|
-
|
|
203
|
-
### Modified files
|
|
204
|
-
|
|
205
|
-
1. **`src/types.ts`**
|
|
206
|
-
- Remove `NotificationDetails`, `ParentSnapshot`, `EnvInfo` interface definitions.
|
|
207
|
-
- Add `AgentIdentity` and `AgentPromptConfig` interfaces.
|
|
208
|
-
- Make `AgentConfig` extend `AgentIdentity` and `AgentPromptConfig`.
|
|
209
|
-
- Remove fields from `AgentConfig` body that are now inherited (`name`, `displayName`, `description`, `promptMode`, `systemPrompt`).
|
|
210
|
-
|
|
211
|
-
2. **`src/notification.ts`**
|
|
212
|
-
- Add `NotificationDetails` interface (moved from `types.ts`).
|
|
213
|
-
- Replace `createNotificationSystem` factory with `NotificationManager` class implementing `NotificationSystem`.
|
|
214
|
-
- Export `NotificationManager` (named export) and keep `NotificationSystem` interface export.
|
|
215
|
-
- Remove `createNotificationSystem` export.
|
|
216
|
-
|
|
217
|
-
3. **`src/parent-snapshot.ts`**
|
|
218
|
-
- Add `ParentSnapshot` interface (moved from `types.ts`).
|
|
219
|
-
|
|
220
|
-
4. **`src/env.ts`**
|
|
221
|
-
- Add `EnvInfo` interface (moved from `types.ts`).
|
|
222
|
-
|
|
223
|
-
5. **`src/prompts.ts`**
|
|
224
|
-
- Change `config` parameter type from `AgentConfig` to `AgentPromptConfig`.
|
|
225
|
-
- Update import to use `AgentPromptConfig` from `./types.js`.
|
|
226
|
-
|
|
227
|
-
6. **`src/index.ts`**
|
|
228
|
-
- Update `NotificationDetails` import from `./types.js` to `./notification.js`.
|
|
229
|
-
- Replace `createNotificationSystem(deps)` with `new NotificationManager(deps)`.
|
|
230
|
-
- Update import: `NotificationManager` from `./notification.js`.
|
|
231
|
-
|
|
232
|
-
7. **`src/renderer.ts`**
|
|
233
|
-
- Update `NotificationDetails` import from `./types.js` to `./notification.js`.
|
|
234
|
-
|
|
235
|
-
8. **`src/agent-runner.ts`**
|
|
236
|
-
- Update `ParentSnapshot` import from `./types.js` to `./parent-snapshot.js`.
|
|
237
|
-
|
|
238
|
-
9. **`src/agent-manager.ts`**
|
|
239
|
-
- Update `ParentSnapshot` import from `./types.js` to `./parent-snapshot.js`.
|
|
240
|
-
|
|
241
|
-
10. **`src/session-config.ts`**
|
|
242
|
-
- Update `EnvInfo` import from `./types.js` to `./env.js`.
|
|
243
|
-
|
|
244
|
-
11. **`src/ui/conversation-viewer.ts`**
|
|
245
|
-
- Define `ConversationViewerOptions` interface.
|
|
246
|
-
- Replace 7 positional constructor parameters with single `options: ConversationViewerOptions` parameter.
|
|
247
|
-
- Assign private fields from `options` in constructor body.
|
|
248
|
-
|
|
249
|
-
### Unchanged files
|
|
250
|
-
|
|
251
|
-
- `src/agent-types.ts` — `DEFAULT_AGENT_NAMES` already moved in #108.
|
|
252
|
-
- `src/invocation-config.ts` — narrowing is a separate concern.
|
|
253
|
-
- `src/ui/agent-menu.ts` — legitimately uses most `AgentConfig` fields.
|
|
254
|
-
|
|
255
|
-
### Test files
|
|
256
|
-
|
|
257
|
-
12. **`test/notification.test.ts`**
|
|
258
|
-
- Replace `createNotificationSystem(deps)` calls with `new NotificationManager(deps)`.
|
|
259
|
-
- Update import.
|
|
260
|
-
- All existing assertions stay — the public API is identical.
|
|
261
|
-
|
|
262
|
-
13. **`test/conversation-viewer.test.ts`**
|
|
263
|
-
- Replace positional `new ConversationViewer(tui, session, record, activity, theme, done, registry)` calls with `new ConversationViewer({ tui, session, record, activity, theme, done, registry })`.
|
|
264
|
-
- All existing assertions stay.
|
|
265
|
-
|
|
266
|
-
14. **`test/prompts.test.ts`**
|
|
267
|
-
- Verify existing test fixtures satisfy `AgentPromptConfig` (they should — tests already pass `name`, `promptMode`, `systemPrompt`).
|
|
268
|
-
- May need to narrow mock type annotations.
|
|
269
|
-
|
|
270
|
-
15. **Other test files importing relocated types** (`test/parent-snapshot.test.ts`, `test/renderer.test.ts`)
|
|
271
|
-
- Update import paths if they import directly from `types.ts` (most import from the source module already).
|
|
272
|
-
|
|
273
|
-
## Test Impact Analysis
|
|
274
|
-
|
|
275
|
-
1. **New unit tests enabled:**
|
|
276
|
-
- `NotificationManager` as a class can be tested with standard `new` + method calls — no factory indirection.
|
|
277
|
-
However, the existing factory tests are already clean and simply switch to `new NotificationManager(deps)`.
|
|
278
|
-
No fundamentally new test capabilities.
|
|
279
|
-
|
|
280
|
-
2. **Existing tests that simplify:**
|
|
281
|
-
- `conversation-viewer.test.ts` — the options bag makes test construction more readable and resilient to parameter additions.
|
|
282
|
-
Each test only needs to specify the fields it cares about (with a helper providing defaults).
|
|
283
|
-
- `prompts.test.ts` — narrower `AgentPromptConfig` parameter means test fixtures can omit 19 irrelevant `AgentConfig` fields.
|
|
284
|
-
|
|
285
|
-
3. **Existing tests that must stay as-is:**
|
|
286
|
-
- `notification.test.ts` — all factory tests stay, just switch constructor syntax.
|
|
287
|
-
- `conversation-viewer.test.ts` — all render and input tests stay, just switch constructor syntax.
|
|
288
|
-
- `parent-snapshot.test.ts`, `env.test.ts`, `renderer.test.ts` — behavior unchanged.
|
|
289
|
-
|
|
290
|
-
## TDD Order
|
|
291
|
-
|
|
292
|
-
1. **Move `NotificationDetails` from `types.ts` to `notification.ts`.**
|
|
293
|
-
Cut the interface from `types.ts`, paste into `notification.ts`.
|
|
294
|
-
Update importers: `index.ts`, `renderer.ts` (change import from `./types.js` to `./notification.js`).
|
|
295
|
-
`notification.ts` already imports from `./types.js` for `AgentRecord` — no circular dependency.
|
|
296
|
-
Run `pnpm run check`.
|
|
297
|
-
Commit: `refactor: move NotificationDetails to notification.ts`
|
|
298
|
-
|
|
299
|
-
2. **Move `ParentSnapshot` from `types.ts` to `parent-snapshot.ts`.**
|
|
300
|
-
Cut the interface from `types.ts`, paste into `parent-snapshot.ts`.
|
|
301
|
-
Update importers: `agent-manager.ts`, `agent-runner.ts` (change import from `./types.js` to `./parent-snapshot.js`).
|
|
302
|
-
Run `pnpm run check`.
|
|
303
|
-
Commit: `refactor: move ParentSnapshot to parent-snapshot.ts`
|
|
304
|
-
|
|
305
|
-
3. **Move `EnvInfo` from `types.ts` to `env.ts`.**
|
|
306
|
-
Cut the interface from `types.ts`, paste into `env.ts`.
|
|
307
|
-
Update importers: `session-config.ts`, `prompts.ts` (change import from `./types.js` to `./env.js`).
|
|
308
|
-
`env.ts` already imports `ShellExec` from `./types.js` — no circular dependency.
|
|
309
|
-
Run `pnpm run check`.
|
|
310
|
-
Commit: `refactor: move EnvInfo to env.ts`
|
|
311
|
-
|
|
312
|
-
4. **Convert `createNotificationSystem` to `NotificationManager` class.**
|
|
313
|
-
Replace the factory with a class implementing `NotificationSystem`.
|
|
314
|
-
Move `pendingNudges` and timer logic to private instance state.
|
|
315
|
-
Convert inner functions to methods.
|
|
316
|
-
Keep `NotificationSystem` interface, `NotificationDeps` interface, and `NUDGE_HOLD_MS` constant unchanged.
|
|
317
|
-
Remove `createNotificationSystem` export, add `NotificationManager` export.
|
|
318
|
-
Update `index.ts`: `new NotificationManager(deps)` instead of `createNotificationSystem(deps)`.
|
|
319
|
-
Update `test/notification.test.ts`: `new NotificationManager(deps)` instead of `createNotificationSystem(deps)`, update import.
|
|
320
|
-
Run `pnpm vitest run test/notification.test.ts`.
|
|
321
|
-
Commit: `refactor: convert createNotificationSystem to NotificationManager class`
|
|
322
|
-
|
|
323
|
-
5. **Convert `ConversationViewer` constructor to options bag.**
|
|
324
|
-
Define `ConversationViewerOptions` interface.
|
|
325
|
-
Replace 7 positional parameters with `options: ConversationViewerOptions`.
|
|
326
|
-
Assign private fields from options in constructor body.
|
|
327
|
-
Update all call sites in `test/conversation-viewer.test.ts` and `src/index.ts` (or wherever `new ConversationViewer(...)` is called).
|
|
328
|
-
Run `pnpm vitest run test/conversation-viewer.test.ts`.
|
|
329
|
-
Commit: `refactor: convert ConversationViewer to options bag constructor`
|
|
330
|
-
|
|
331
|
-
6. **Define narrow `AgentIdentity` and `AgentPromptConfig` interfaces.**
|
|
332
|
-
Add interfaces to `types.ts`.
|
|
333
|
-
Make `AgentConfig` extend both; remove inherited fields from `AgentConfig` body.
|
|
334
|
-
Narrow `prompts.ts` `buildAgentPrompt` parameter from `AgentConfig` to `AgentPromptConfig`.
|
|
335
|
-
Update `test/prompts.test.ts` type annotations if needed.
|
|
336
|
-
Run `pnpm run check` and `pnpm vitest run`.
|
|
337
|
-
Commit: `refactor: define AgentIdentity and AgentPromptConfig subset interfaces`
|
|
338
|
-
|
|
339
|
-
## Risks and Mitigations
|
|
340
|
-
|
|
341
|
-
| Risk | Mitigation |
|
|
342
|
-
| --------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
343
|
-
| Moving types breaks import paths in files not caught by grep | Run `pnpm run check` (full `tsc --noEmit`) after each type relocation step. |
|
|
344
|
-
| `NotificationManager` class changes test mock patterns (e.g., spreading instances) | Tests already use the factory return as an opaque object — switching to `new` is mechanical. No spread patterns in notification tests. |
|
|
345
|
-
| `ConversationViewer` options bag breaks all test call sites at once | All 314 lines of tests use the same 7-arg pattern. A search-and-replace handles it. The options bag is a single-step change. |
|
|
346
|
-
| `AgentConfig extends AgentIdentity, AgentPromptConfig` changes structural compatibility | `extends` is purely additive — existing code passing `AgentConfig` anywhere still works. Narrowed consumers gain type safety, nothing breaks. |
|
|
347
|
-
| `prompts.ts` parameter narrowing breaks callers passing `AgentConfig` | `AgentConfig extends AgentPromptConfig`, so all existing call sites are compatible without changes. |
|
|
348
|
-
|
|
349
|
-
## Open Questions
|
|
350
|
-
|
|
351
|
-
- Whether to define an `AgentSessionConfig` subset for `session-config.ts` (8 fields) — deferred because the narrowing ratio (8 of 22) yields less clarity benefit than `AgentIdentity` (4 of 22) or `AgentPromptConfig` (3 of 22).
|
|
@@ -1,271 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
issue: 118
|
|
3
|
-
issue_title: "refactor(pi-subagents): SettingsManager apply methods — eliminate cross-collaborator orchestration"
|
|
4
|
-
---
|
|
5
|
-
|
|
6
|
-
# SettingsManager apply methods — eliminate cross-collaborator orchestration
|
|
7
|
-
|
|
8
|
-
## Problem Statement
|
|
9
|
-
|
|
10
|
-
`showSettings` in `agent-menu.ts` orchestrates across two collaborators when changing a setting — it mutates `settings` properties directly (output arguments), separately pokes `manager.notifyConcurrencyChanged()`, then calls `settings.saveAndNotify()`.
|
|
11
|
-
The menu knows too much about the consequence chain of a settings change.
|
|
12
|
-
|
|
13
|
-
This is a Law of Demeter / Tell-Don't-Ask violation: the menu should *tell* settings what the user wants, not coordinate the mechanics of persistence and queue drain.
|
|
14
|
-
|
|
15
|
-
## Goals
|
|
16
|
-
|
|
17
|
-
- Add `applyMaxConcurrent(n)`, `applyDefaultMaxTurns(n)`, `applyGraceTurns(n)` methods to `SettingsManager` that own the full consequence chain: normalize → set in memory → notify interested parties → persist → emit lifecycle event → return toast.
|
|
18
|
-
- Accept an optional `onMaxConcurrentChanged` callback in `SettingsManager` constructor deps, wired to `manager.notifyConcurrencyChanged()` at init.
|
|
19
|
-
- Narrow `AgentMenuSettings` — replace writable property setters and `saveAndNotify` with 3 read-only getters and 3 apply methods.
|
|
20
|
-
- Remove `notifyConcurrencyChanged` from `AgentMenuManager` — the menu no longer needs to know about the manager for settings changes.
|
|
21
|
-
- This is a non-breaking refactoring — no public API surface changes.
|
|
22
|
-
|
|
23
|
-
## Non-Goals
|
|
24
|
-
|
|
25
|
-
- Narrowing `AgentToolDeps` or `AgentMenuDeps` further (#114) — that is a separate issue.
|
|
26
|
-
- Changing the persistence format or the global-vs-project merge strategy.
|
|
27
|
-
- Removing `saveAndNotify` from `SettingsManager` — it remains as a public method; the apply methods delegate to it internally.
|
|
28
|
-
- Removing the property setters from `SettingsManager` — they remain for `load()` and direct test use; only the `AgentMenuSettings` interface narrows.
|
|
29
|
-
|
|
30
|
-
## Background
|
|
31
|
-
|
|
32
|
-
### Current flow (max concurrency example)
|
|
33
|
-
|
|
34
|
-
```text
|
|
35
|
-
showSettings (agent-menu.ts)
|
|
36
|
-
├── deps.settings.maxConcurrent = n ← output argument
|
|
37
|
-
├── deps.manager.notifyConcurrencyChanged()← cross-collaborator orchestration
|
|
38
|
-
└── notifyApplied(ctx, message)
|
|
39
|
-
└── deps.settings.saveAndNotify(msg) ← separate persist + emit call
|
|
40
|
-
```
|
|
41
|
-
|
|
42
|
-
### Target flow
|
|
43
|
-
|
|
44
|
-
```text
|
|
45
|
-
showSettings (agent-menu.ts)
|
|
46
|
-
└── deps.settings.applyMaxConcurrent(n) ← one call, toast returned
|
|
47
|
-
├── this.maxConcurrent = n ← internal set
|
|
48
|
-
├── this.onMaxConcurrentChanged?.() ← callback to manager
|
|
49
|
-
└── this.saveAndNotify(message) ← internal persist + emit
|
|
50
|
-
```
|
|
51
|
-
|
|
52
|
-
### Module map (affected files only)
|
|
53
|
-
|
|
54
|
-
| Module | Current role | Change |
|
|
55
|
-
| ---------------------- | ------------------------------------------------------ | ------------------------------------------------------------- |
|
|
56
|
-
| `src/settings.ts` | `SettingsManager` class with setters, `saveAndNotify` | Add `onMaxConcurrentChanged` callback; add 3 `apply*` methods |
|
|
57
|
-
| `src/ui/agent-menu.ts` | `showSettings` orchestrates across manager + settings | Simplify to single apply call per setting |
|
|
58
|
-
| `src/agent-manager.ts` | `notifyConcurrencyChanged()` public method | No change — still called via callback |
|
|
59
|
-
| `src/index.ts` | Wires `notifyConcurrencyChanged` on `AgentMenuManager` | Wire `onMaxConcurrentChanged` on settings constructor instead |
|
|
60
|
-
|
|
61
|
-
### Architecture reference
|
|
62
|
-
|
|
63
|
-
Follow-up to Phase 7, Step A2 (#109, closed/implemented).
|
|
64
|
-
Sequenced before D2 (#114, open).
|
|
65
|
-
|
|
66
|
-
### Applicable constraints
|
|
67
|
-
|
|
68
|
-
- Law of Demeter — eliminate cross-collaborator reach-through (code-design skill).
|
|
69
|
-
- No output arguments — the menu should not write into received deps (code-design skill).
|
|
70
|
-
- Dependency inversion — consumers accept narrow interfaces (code-design skill).
|
|
71
|
-
- One concern per file — `SettingsManager` already owns the concern; this deepens encapsulation.
|
|
72
|
-
|
|
73
|
-
## Design Overview
|
|
74
|
-
|
|
75
|
-
### SettingsManager constructor deps change
|
|
76
|
-
|
|
77
|
-
```typescript
|
|
78
|
-
constructor(deps: {
|
|
79
|
-
emit: SettingsEmit;
|
|
80
|
-
cwd: string;
|
|
81
|
-
onMaxConcurrentChanged?: () => void; // ← new
|
|
82
|
-
})
|
|
83
|
-
```
|
|
84
|
-
|
|
85
|
-
The callback is stored as a private field and called from `applyMaxConcurrent` only.
|
|
86
|
-
|
|
87
|
-
### Apply methods
|
|
88
|
-
|
|
89
|
-
Each method normalizes the input, sets the in-memory value, calls any interested-party callback, and delegates to `saveAndNotify` for persist + emit + toast:
|
|
90
|
-
|
|
91
|
-
```typescript
|
|
92
|
-
applyMaxConcurrent(n: number): { message: string; level: "info" | "warning" } {
|
|
93
|
-
this.maxConcurrent = n; // setter normalizes (max(1, n))
|
|
94
|
-
this.onMaxConcurrentChanged?.();
|
|
95
|
-
return this.saveAndNotify(`Max concurrency set to ${this.maxConcurrent}`);
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
applyDefaultMaxTurns(n: number): { message: string; level: "info" | "warning" } {
|
|
99
|
-
this.defaultMaxTurns = n === 0 ? undefined : n; // setter normalizes
|
|
100
|
-
const label = this.defaultMaxTurns == null ? "unlimited" : String(this.defaultMaxTurns);
|
|
101
|
-
return this.saveAndNotify(`Default max turns set to ${label}`);
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
applyGraceTurns(n: number): { message: string; level: "info" | "warning" } {
|
|
105
|
-
this.graceTurns = n; // setter normalizes (max(1, n))
|
|
106
|
-
return this.saveAndNotify(`Grace turns set to ${this.graceTurns}`);
|
|
107
|
-
}
|
|
108
|
-
```
|
|
109
|
-
|
|
110
|
-
The toast message uses the *post-normalization* value (e.g., `max(1, n)`) so the user sees what was actually applied.
|
|
111
|
-
|
|
112
|
-
### Consumer call-site sketch (agent-menu.ts)
|
|
113
|
-
|
|
114
|
-
```typescript
|
|
115
|
-
// Max concurrency — before:
|
|
116
|
-
deps.settings.maxConcurrent = n;
|
|
117
|
-
deps.manager.notifyConcurrencyChanged();
|
|
118
|
-
notifyApplied(ctx, `Max concurrency set to ${n}`);
|
|
119
|
-
|
|
120
|
-
// Max concurrency — after:
|
|
121
|
-
const toast = deps.settings.applyMaxConcurrent(n);
|
|
122
|
-
ctx.ui.notify(toast.message, toast.level);
|
|
123
|
-
```
|
|
124
|
-
|
|
125
|
-
Three property writes + one cross-collaborator call + one persist call → one method call.
|
|
126
|
-
The menu never touches the manager for settings changes.
|
|
127
|
-
|
|
128
|
-
### Narrowed AgentMenuSettings interface
|
|
129
|
-
|
|
130
|
-
```typescript
|
|
131
|
-
export interface AgentMenuSettings {
|
|
132
|
-
readonly maxConcurrent: number;
|
|
133
|
-
readonly defaultMaxTurns: number | undefined;
|
|
134
|
-
readonly graceTurns: number;
|
|
135
|
-
applyMaxConcurrent(n: number): { message: string; level: "info" | "warning" };
|
|
136
|
-
applyDefaultMaxTurns(n: number): { message: string; level: "info" | "warning" };
|
|
137
|
-
applyGraceTurns(n: number): { message: string; level: "info" | "warning" };
|
|
138
|
-
}
|
|
139
|
-
```
|
|
140
|
-
|
|
141
|
-
### Narrowed AgentMenuManager interface
|
|
142
|
-
|
|
143
|
-
```typescript
|
|
144
|
-
export interface AgentMenuManager {
|
|
145
|
-
listAgents: () => AgentRecord[];
|
|
146
|
-
getRecord: (id: string) => AgentRecord | undefined;
|
|
147
|
-
spawnAndWait: (...) => Promise<AgentRecord>;
|
|
148
|
-
// notifyConcurrencyChanged removed — settings owns the callback
|
|
149
|
-
}
|
|
150
|
-
```
|
|
151
|
-
|
|
152
|
-
### index.ts wiring change
|
|
153
|
-
|
|
154
|
-
```typescript
|
|
155
|
-
// Before:
|
|
156
|
-
const settings = new SettingsManager({ emit, cwd });
|
|
157
|
-
// ... later in menu deps:
|
|
158
|
-
manager: { ..., notifyConcurrencyChanged: () => manager.notifyConcurrencyChanged() },
|
|
159
|
-
|
|
160
|
-
// After:
|
|
161
|
-
const settings = new SettingsManager({
|
|
162
|
-
emit,
|
|
163
|
-
cwd,
|
|
164
|
-
onMaxConcurrentChanged: () => manager.notifyConcurrencyChanged(),
|
|
165
|
-
});
|
|
166
|
-
// ... menu deps no longer includes notifyConcurrencyChanged
|
|
167
|
-
```
|
|
168
|
-
|
|
169
|
-
The closure captures `manager` by reference — safe because the callback is never invoked before `manager` is constructed.
|
|
170
|
-
|
|
171
|
-
## Module-Level Changes
|
|
172
|
-
|
|
173
|
-
### `src/settings.ts`
|
|
174
|
-
|
|
175
|
-
- **Add** `onMaxConcurrentChanged?: () => void` to constructor deps.
|
|
176
|
-
- **Add** `applyMaxConcurrent(n)`, `applyDefaultMaxTurns(n)`, `applyGraceTurns(n)` methods.
|
|
177
|
-
- **Keep** property setters, `saveAndNotify`, `load`, `snapshot` — apply methods delegate to them.
|
|
178
|
-
|
|
179
|
-
### `src/ui/agent-menu.ts`
|
|
180
|
-
|
|
181
|
-
- **Change** `AgentMenuSettings`: replace writable properties + `saveAndNotify` with readonly properties + 3 apply methods.
|
|
182
|
-
- **Change** `AgentMenuManager`: remove `notifyConcurrencyChanged`.
|
|
183
|
-
- **Change** `showSettings`: replace 3-step orchestration with single apply call per setting.
|
|
184
|
-
- **Remove** `notifyApplied` helper — no longer needed; each branch calls `ctx.ui.notify(toast.message, toast.level)` directly.
|
|
185
|
-
|
|
186
|
-
### `src/index.ts`
|
|
187
|
-
|
|
188
|
-
- **Add** `onMaxConcurrentChanged` to `SettingsManager` constructor call.
|
|
189
|
-
- **Remove** `notifyConcurrencyChanged` from the menu's manager dep.
|
|
190
|
-
|
|
191
|
-
### `src/agent-manager.ts`
|
|
192
|
-
|
|
193
|
-
- **No change** — `notifyConcurrencyChanged()` remains as a public method, now called via callback instead of menu.
|
|
194
|
-
|
|
195
|
-
## Test Impact Analysis
|
|
196
|
-
|
|
197
|
-
### New unit tests enabled
|
|
198
|
-
|
|
199
|
-
- **Apply method integration**: construct `SettingsManager` with an `onMaxConcurrentChanged` spy → call `applyMaxConcurrent(n)` → verify the spy was called, value was set, `saveAndNotify` persisted, and the returned toast is correct.
|
|
200
|
-
Previously impossible because the consequence chain was spread across the menu and two collaborators.
|
|
201
|
-
- **Toast message accuracy**: verify that apply methods use post-normalization values in the toast (e.g., `applyMaxConcurrent(0)` sets to 1 and reports "set to 1", not "set to 0").
|
|
202
|
-
- **Callback not invoked for non-concurrency settings**: verify `onMaxConcurrentChanged` is *not* called when `applyDefaultMaxTurns` or `applyGraceTurns` is used.
|
|
203
|
-
|
|
204
|
-
### Existing tests that become simpler
|
|
205
|
-
|
|
206
|
-
- `agent-menu.test.ts` settings tests: currently assert 3 side effects per setting (property mutation, `notifyConcurrencyChanged` call, `saveAndNotify` call).
|
|
207
|
-
After: assert a single `apply*` call with the correct argument.
|
|
208
|
-
The mock object shrinks (no writable setters, no `saveAndNotify`).
|
|
209
|
-
|
|
210
|
-
### Existing tests that must stay
|
|
211
|
-
|
|
212
|
-
- `settings.test.ts` — `saveAndNotify()` tests stay; the apply methods delegate to it.
|
|
213
|
-
- `settings.test.ts` — property setter normalization tests stay; apply methods delegate to setters.
|
|
214
|
-
- `agent-menu.test.ts` — settings navigation tests stay; only the assertions change.
|
|
215
|
-
- `agent-manager.test.ts` — `notifyConcurrencyChanged` / drain-queue tests stay unchanged.
|
|
216
|
-
|
|
217
|
-
## TDD Order
|
|
218
|
-
|
|
219
|
-
### Cycle 1: Add `onMaxConcurrentChanged` callback to SettingsManager constructor
|
|
220
|
-
|
|
221
|
-
1. Red: test that constructing with `onMaxConcurrentChanged` stores the callback (verified indirectly in cycle 2).
|
|
222
|
-
Test that constructing without it does not throw.
|
|
223
|
-
2. Green: add optional `onMaxConcurrentChanged` to constructor deps, store as private field.
|
|
224
|
-
3. Commit: `feat: accept onMaxConcurrentChanged callback in SettingsManager constructor`
|
|
225
|
-
|
|
226
|
-
### Cycle 2: Add `applyMaxConcurrent` method
|
|
227
|
-
|
|
228
|
-
1. Red: test `applyMaxConcurrent(8)` — sets `maxConcurrent` to 8, calls `onMaxConcurrentChanged` spy, persists, emits event, returns info toast with "Max concurrency set to 8".
|
|
229
|
-
Test `applyMaxConcurrent(0)` — normalizes to 1, toast says "set to 1".
|
|
230
|
-
Test without callback — no throw, still persists and returns toast.
|
|
231
|
-
2. Green: implement `applyMaxConcurrent`.
|
|
232
|
-
3. Commit: `feat: add SettingsManager.applyMaxConcurrent method`
|
|
233
|
-
|
|
234
|
-
### Cycle 3: Add `applyDefaultMaxTurns` and `applyGraceTurns` methods
|
|
235
|
-
|
|
236
|
-
1. Red: test `applyDefaultMaxTurns(0)` — sets to unlimited, toast says "unlimited".
|
|
237
|
-
Test `applyDefaultMaxTurns(10)` — sets to 10, toast says "set to 10".
|
|
238
|
-
Test `applyGraceTurns(3)` — sets to 3, toast says "set to 3".
|
|
239
|
-
Test that neither calls `onMaxConcurrentChanged`.
|
|
240
|
-
2. Green: implement both methods.
|
|
241
|
-
3. Commit: `feat: add SettingsManager.applyDefaultMaxTurns and applyGraceTurns methods`
|
|
242
|
-
|
|
243
|
-
### Cycle 4: Narrow `AgentMenuSettings` and `AgentMenuManager`, simplify `showSettings`
|
|
244
|
-
|
|
245
|
-
1. Red: update `makeDeps` in `agent-menu.test.ts` — replace writable settings properties + `saveAndNotify` with readonly properties + 3 `apply*` mocks; remove `notifyConcurrencyChanged` from manager mock.
|
|
246
|
-
Update assertions: `expect(deps.settings.applyMaxConcurrent).toHaveBeenCalledWith(8)` instead of checking property + `saveAndNotify` + `notifyConcurrencyChanged`.
|
|
247
|
-
2. Green: update `AgentMenuSettings` interface (readonly getters + apply methods), update `AgentMenuManager` (remove `notifyConcurrencyChanged`), rewrite `showSettings` to use apply methods, remove `notifyApplied` helper.
|
|
248
|
-
3. Run `pnpm run check`.
|
|
249
|
-
4. Commit: `refactor: simplify showSettings to use SettingsManager apply methods`
|
|
250
|
-
|
|
251
|
-
### Cycle 5: Wire `onMaxConcurrentChanged` in index.ts
|
|
252
|
-
|
|
253
|
-
1. Update `SettingsManager` constructor in `index.ts` — add `onMaxConcurrentChanged: () => manager.notifyConcurrencyChanged()`.
|
|
254
|
-
2. Remove `notifyConcurrencyChanged` from the menu's manager dep object.
|
|
255
|
-
3. Run full test suite.
|
|
256
|
-
4. Commit: `refactor: wire onMaxConcurrentChanged callback in extension init (#118)`
|
|
257
|
-
|
|
258
|
-
## Risks and Mitigations
|
|
259
|
-
|
|
260
|
-
| Risk | Mitigation |
|
|
261
|
-
| ---------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
262
|
-
| Closure ordering: `onMaxConcurrentChanged` references `manager` before it's constructed | The closure captures by reference; `manager` is assigned before any runtime invocation of the callback. Verified by the `index.ts` construction order (`settings` → `manager` → menu wiring). |
|
|
263
|
-
| Toast message drift: apply methods generate messages internally, so changes require updating `SettingsManager` instead of the menu | Each method has exactly one message template; the coupling is intentional — the owner of the setting knows how to describe the change. |
|
|
264
|
-
| Apply methods duplicate the 3-step pattern (set → callback → save) | The duplication is incidental across 3 settings — extracting a shared helper would need a discriminator for the callback, adding complexity for 3 call sites. |
|
|
265
|
-
| `saveAndNotify` remains public but is no longer in `AgentMenuSettings` | It's still useful for programmatic callers and tests; keeping it public is intentional. |
|
|
266
|
-
|
|
267
|
-
## Open Questions
|
|
268
|
-
|
|
269
|
-
- Should the property setters on `SettingsManager` be demoted to `private` (with only apply methods for external mutation)?
|
|
270
|
-
Defer — `load()` uses them internally, and narrowing is done via the `AgentMenuSettings` interface.
|
|
271
|
-
If a future consumer needs direct set access, the setters are there.
|