@gajae-code/coding-agent 0.18.12 → 0.19.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/CHANGELOG.md +467 -0
- package/dist/types/capability/rule.d.ts +7 -2
- package/dist/types/cli/append-system-prompt.d.ts +6 -0
- package/dist/types/cli/args.d.ts +7 -2
- package/dist/types/cli/customize-doctor-precedence.d.ts +16 -0
- package/dist/types/cli/customize-doctor.d.ts +4 -3
- package/dist/types/cli/public-command-errors.d.ts +1 -0
- package/dist/types/cli/root-flags.d.ts +0 -12
- package/dist/types/cli/update-cli.d.ts +7 -2
- package/dist/types/cli-main.d.ts +0 -6
- package/dist/types/commands/launch.d.ts +0 -6
- package/dist/types/commands/migrate.d.ts +1 -1
- package/dist/types/commands/sdk.d.ts +8 -0
- package/dist/types/commit/map-reduce/index.d.ts +4 -4
- package/dist/types/config/autorouting-tier-map.d.ts +23 -1
- package/dist/types/config/config-hot-reload.d.ts +5 -0
- package/dist/types/config/file-lock.d.ts +44 -2
- package/dist/types/config/model-profile-activation.d.ts +19 -5
- package/dist/types/config/model-registry.d.ts +16 -3
- package/dist/types/config/model-resolver.d.ts +5 -4
- package/dist/types/config/settings-schema.d.ts +11 -1
- package/dist/types/coordinator-mcp/server.d.ts +4 -0
- package/dist/types/customization/import.d.ts +2 -0
- package/dist/types/customization/types.d.ts +1 -1
- package/dist/types/dap/session.d.ts +11 -2
- package/dist/types/discovery/helpers.d.ts +7 -2
- package/dist/types/edit/index.d.ts +5 -0
- package/dist/types/edit/modes/patch.d.ts +9 -0
- package/dist/types/eval/py/runtime.d.ts +9 -0
- package/dist/types/export/ttsr.d.ts +19 -0
- package/dist/types/extensibility/extensions/types.d.ts +5 -1
- package/dist/types/extensibility/slash-commands.d.ts +1 -0
- package/dist/types/gjc-runtime/ralplan-current-final.d.ts +54 -0
- package/dist/types/gjc-runtime/ralplan-runtime.d.ts +5 -0
- package/dist/types/gjc-runtime/tmux-sessions.d.ts +24 -0
- package/dist/types/gjc-runtime/ultragoal-change-set.d.ts +6 -0
- package/dist/types/gjc-runtime/ultragoal-runtime.d.ts +1 -0
- package/dist/types/harness-control-plane/receipt-spool.d.ts +4 -0
- package/dist/types/hindsight/content.d.ts +5 -6
- package/dist/types/internal-urls/types.d.ts +2 -0
- package/dist/types/lsp/clients/swiftlint-client.d.ts +0 -4
- package/dist/types/main.d.ts +23 -18
- package/dist/types/migrate/action-planner.d.ts +10 -1
- package/dist/types/migrate/adapters/codex.d.ts +1 -1
- package/dist/types/migrate/adapters/index.d.ts +35 -3
- package/dist/types/migrate/hook-module.d.ts +23 -0
- package/dist/types/migrate/report.d.ts +2 -1
- package/dist/types/migrate/types.d.ts +69 -2
- package/dist/types/modes/components/agent-dashboard.d.ts +1 -1
- package/dist/types/modes/components/model-selector.d.ts +43 -0
- package/dist/types/modes/prompt-suggestion-controller.d.ts +1 -1
- package/dist/types/modes/utils/host-status.d.ts +15 -11
- package/dist/types/runtime-mcp/project-host-values.d.ts +11 -5
- package/dist/types/sdk/broker/broker-exit.d.ts +1 -1
- package/dist/types/sdk/broker/broker.d.ts +22 -1
- package/dist/types/sdk/broker/ensure.d.ts +5 -2
- package/dist/types/sdk/broker/hop.d.ts +9 -0
- package/dist/types/sdk/broker/lifecycle.d.ts +22 -0
- package/dist/types/sdk/broker/session-index.d.ts +2 -0
- package/dist/types/sdk/broker/transport.d.ts +2 -0
- package/dist/types/sdk/bus/index.d.ts +21 -0
- package/dist/types/sdk/bus/telegram-daemon-contract.d.ts +7 -1
- package/dist/types/sdk/bus/telegram-daemon.d.ts +2 -0
- package/dist/types/sdk/bus/threaded-render.d.ts +2 -1
- package/dist/types/sdk/host/control/operations.d.ts +5 -1
- package/dist/types/sdk/host/events.d.ts +4 -0
- package/dist/types/sdk/host/host.d.ts +2 -1
- package/dist/types/sdk/host/prompt-image-upload.d.ts +43 -0
- package/dist/types/sdk/host/session-runtime.d.ts +8 -8
- package/dist/types/sdk/host/surface-policy.d.ts +2 -1
- package/dist/types/sdk/lifecycle/service.d.ts +12 -0
- package/dist/types/sdk/prompt-status.d.ts +11 -4
- package/dist/types/session/agent-session.d.ts +50 -8
- package/dist/types/session/session-manager.d.ts +16 -0
- package/dist/types/skill-state/workflow-mutation-guard.d.ts +13 -1
- package/dist/types/slash-commands/helpers/mcp.d.ts +5 -0
- package/dist/types/ssh/ssh-executor.d.ts +8 -1
- package/dist/types/system-prompt.d.ts +3 -2
- package/dist/types/task/role-body.d.ts +11 -0
- package/dist/types/tools/awaiting-input.d.ts +28 -0
- package/dist/types/tools/bash-allowed-prefixes.d.ts +6 -0
- package/dist/types/tools/bash-skill-urls.d.ts +4 -0
- package/dist/types/tools/bash.d.ts +7 -0
- package/dist/types/tools/descriptors.d.ts +13 -2
- package/dist/types/tools/fetch.d.ts +6 -0
- package/dist/types/tools/irc.d.ts +5 -3
- package/dist/types/tools/tmux-self-injection-guard.d.ts +2 -0
- package/dist/types/tools/ultragoal-ask-guard.d.ts +1 -1
- package/dist/types/tools/write.d.ts +2 -0
- package/dist/types/utils/file-mentions.d.ts +5 -0
- package/dist/types/utils/image-limits.d.ts +7 -0
- package/dist/types/utils/image-loading.d.ts +0 -1
- package/dist/types/utils/open.d.ts +0 -1
- package/dist/types/utils/pasted-image-loading.d.ts +1 -5
- package/dist/types/utils/prompt-suggestion.d.ts +2 -2
- package/dist/types/utils/shell-snapshot.d.ts +2 -0
- package/dist/types/utils/title-generator.d.ts +13 -3
- package/dist/types/web/search/providers/codex.d.ts +6 -7
- package/package.json +9 -8
- package/scripts/acp-conformance-agent.ts +12 -2
- package/scripts/generate-sdk-operation-inventory.ts +16 -0
- package/src/capability/fs.ts +31 -3
- package/src/capability/rule.ts +11 -2
- package/src/cli/append-system-prompt.ts +12 -0
- package/src/cli/args.ts +11 -7
- package/src/cli/customize-doctor-precedence.ts +31 -0
- package/src/cli/customize-doctor.ts +49 -39
- package/src/cli/fast-help.ts +0 -2
- package/src/cli/migrate-cli.ts +14 -3
- package/src/cli/public-command-errors.ts +2 -0
- package/src/cli/root-flags.ts +0 -2
- package/src/cli/update-cli.ts +247 -6
- package/src/commands/harness.ts +25 -15
- package/src/commands/migrate.ts +3 -3
- package/src/commands/sdk.ts +81 -11
- package/src/commit/agentic/agent.ts +6 -6
- package/src/commit/analysis/conventional.ts +14 -5
- package/src/commit/map-reduce/index.ts +7 -7
- package/src/commit/pipeline.ts +12 -12
- package/src/config/autorouting-tier-map.ts +20 -1
- package/src/config/config-file.ts +106 -3
- package/src/config/config-hot-reload.ts +8 -0
- package/src/config/file-lock.ts +1431 -304
- package/src/config/keybindings.ts +154 -7
- package/src/config/model-profile-activation.ts +177 -39
- package/src/config/model-registry.ts +380 -55
- package/src/config/model-resolver.ts +49 -21
- package/src/config/settings-schema.ts +13 -1
- package/src/config/settings.ts +2 -0
- package/src/coordinator-mcp/server.ts +37 -2
- package/src/crash/sanitize.ts +3 -1
- package/src/customization/import.ts +276 -7
- package/src/customization/inventory.ts +24 -8
- package/src/customization/types.ts +1 -0
- package/src/dap/client.ts +3 -2
- package/src/dap/session.ts +161 -36
- package/src/debug/crash-diagnostics.ts +87 -5
- package/src/debug/report-bundle.ts +71 -2
- package/src/discovery/builtin.ts +4 -3
- package/src/discovery/helpers.ts +24 -14
- package/src/edit/index.ts +191 -5
- package/src/edit/modes/patch.ts +27 -0
- package/src/eval/py/runtime.ts +44 -4
- package/src/exec/non-interactive-env.ts +0 -1
- package/src/export/html/index.ts +60 -20
- package/src/export/ttsr.ts +176 -1
- package/src/extensibility/extensions/loader.ts +2 -0
- package/src/extensibility/extensions/ouroboros-ooo-bridge.ts +63 -1
- package/src/extensibility/extensions/types.ts +5 -1
- package/src/extensibility/hooks/loader.ts +10 -2
- package/src/extensibility/plugins/marketplace/manager.ts +36 -10
- package/src/extensibility/slash-commands.ts +5 -0
- package/src/gjc-runtime/gc-runtime.ts +4 -1
- package/src/gjc-runtime/launch-tmux.ts +23 -13
- package/src/gjc-runtime/ralplan-current-final.ts +294 -0
- package/src/gjc-runtime/ralplan-runtime.ts +95 -1
- package/src/gjc-runtime/state-writer.ts +109 -5
- package/src/gjc-runtime/tmux-sessions.ts +93 -13
- package/src/gjc-runtime/ultragoal-change-set.ts +90 -11
- package/src/gjc-runtime/ultragoal-guard.ts +9 -1
- package/src/gjc-runtime/ultragoal-runtime.ts +15 -5
- package/src/harness-control-plane/finalize.ts +11 -0
- package/src/harness-control-plane/receipt-spool.ts +105 -28
- package/src/harness-control-plane/storage.ts +124 -6
- package/src/hindsight/content.ts +116 -16
- package/src/hindsight/mental-models.ts +22 -10
- package/src/hindsight/state.ts +7 -4
- package/src/hooks/native-skill-hook.ts +3 -1
- package/src/internal-urls/artifact-protocol.ts +3 -1
- package/src/internal-urls/docs-index.generated.ts +14 -14
- package/src/internal-urls/local-protocol.ts +28 -3
- package/src/internal-urls/types.ts +2 -0
- package/src/lsp/client.ts +2 -1
- package/src/lsp/clients/biome-client.ts +2 -0
- package/src/lsp/clients/swiftlint-client.ts +2 -0
- package/src/main.ts +330 -92
- package/src/memories/index.ts +25 -2
- package/src/migrate/action-planner.ts +517 -2
- package/src/migrate/adapters/claude-code.ts +141 -3
- package/src/migrate/adapters/codex.ts +57 -5
- package/src/migrate/adapters/index.ts +240 -5
- package/src/migrate/adapters/opencode.ts +4 -0
- package/src/migrate/executor.ts +141 -3
- package/src/migrate/hook-module.ts +193 -0
- package/src/migrate/report.ts +45 -1
- package/src/migrate/types.ts +73 -3
- package/src/modes/acp/acp-agent.ts +10 -1
- package/src/modes/components/agent-dashboard.ts +20 -4
- package/src/modes/components/customization/customization-dashboard.ts +7 -0
- package/src/modes/components/customization/import-wizard.ts +29 -3
- package/src/modes/components/model-selector.ts +157 -103
- package/src/modes/components/welcome.ts +3 -1
- package/src/modes/controllers/command-controller.ts +271 -16
- package/src/modes/controllers/event-controller.ts +1 -1
- package/src/modes/controllers/extension-ui-controller.ts +31 -1
- package/src/modes/controllers/goal-mode-controller.ts +79 -10
- package/src/modes/controllers/input-controller.ts +2 -0
- package/src/modes/controllers/plan-mode-controller.ts +49 -4
- package/src/modes/controllers/runtime-mcp-command-controller.ts +7 -6
- package/src/modes/controllers/selector-controller.ts +14 -1
- package/src/modes/interactive-mode.ts +55 -4
- package/src/modes/prompt-suggestion-controller.ts +1 -1
- package/src/modes/runtime-init.ts +5 -1
- package/src/modes/utils/host-status.ts +21 -10
- package/src/prompts/agent-fragments/browser-aside.md +2 -2
- package/src/prompts/system/irc-incoming.md +3 -1
- package/src/prompts/tools/irc.md +1 -1
- package/src/runtime-mcp/manager.ts +8 -3
- package/src/runtime-mcp/project-host-values.ts +12 -8
- package/src/runtime-mcp/tool-bridge.ts +68 -4
- package/src/runtime-mcp/transports/stdio.ts +2 -1
- package/src/sdk/broker/broker-exit.ts +2 -0
- package/src/sdk/broker/broker.ts +143 -4
- package/src/sdk/broker/ensure.ts +74 -6
- package/src/sdk/broker/hop.ts +13 -1
- package/src/sdk/broker/lifecycle-ledger.ts +3 -0
- package/src/sdk/broker/lifecycle.ts +102 -6
- package/src/sdk/broker/session-index.ts +29 -4
- package/src/sdk/broker/transport.ts +17 -1
- package/src/sdk/bus/chat-daemon-control.ts +2 -2
- package/src/sdk/bus/index.ts +408 -14
- package/src/sdk/bus/session-control.ts +2 -0
- package/src/sdk/bus/slack-daemon.ts +105 -9
- package/src/sdk/bus/telegram-daemon-contract.ts +7 -1
- package/src/sdk/bus/telegram-daemon.ts +10 -2
- package/src/sdk/bus/threaded-render.ts +20 -1
- package/src/sdk/cli/session-cli.ts +2 -0
- package/src/sdk/host/control/dispatch.ts +13 -1
- package/src/sdk/host/control/operations.ts +10 -1
- package/src/sdk/host/events.ts +67 -4
- package/src/sdk/host/host.ts +8 -2
- package/src/sdk/host/prompt-image-upload.ts +438 -0
- package/src/sdk/host/sdk-surface-parity.test.ts +15 -0
- package/src/sdk/host/session-runtime.test.ts +1537 -24
- package/src/sdk/host/session-runtime.ts +332 -36
- package/src/sdk/host/surface-policy.ts +4 -0
- package/src/sdk/host/websocket-transport.lifecycle.test.ts +90 -2
- package/src/sdk/host/websocket-transport.ts +19 -7
- package/src/sdk/lifecycle/service.ts +14 -1
- package/src/sdk/prompt-status.ts +6 -4
- package/src/sdk/protocol/adapter-validation.ts +28 -1
- package/src/sdk/protocol/operation-inventory.generated.json +160 -0
- package/src/sdk/protocol/operation-registry.ts +33 -3
- package/src/sdk/session.ts +123 -36
- package/src/session/agent-session.ts +1614 -329
- package/src/session/history-storage.ts +182 -12
- package/src/session/internal/managed-session-scope.ts +47 -18
- package/src/session/internal/managed-session-storage.ts +71 -1
- package/src/session/session-manager.ts +90 -5
- package/src/session-import/redact.ts +53 -2
- package/src/skill-state/workflow-mutation-guard.ts +1498 -18
- package/src/slash-commands/builtin-registry.ts +6 -3
- package/src/slash-commands/helpers/mcp.ts +11 -3
- package/src/ssh/ssh-executor.ts +9 -2
- package/src/stt/recorder.ts +26 -9
- package/src/system-prompt.ts +61 -16
- package/src/task/executor.ts +12 -8
- package/src/task/role-body.ts +84 -0
- package/src/task/token-log-mode.test.ts +162 -0
- package/src/task/token-log.ts +91 -2
- package/src/tools/archive-reader.ts +650 -37
- package/src/tools/ask.ts +1 -0
- package/src/tools/ast-grep.ts +7 -2
- package/src/tools/auto-generated-guard.ts +12 -3
- package/src/tools/awaiting-input.ts +114 -0
- package/src/tools/bash-allowed-prefixes.ts +61 -1
- package/src/tools/bash-skill-urls.ts +58 -5
- package/src/tools/bash.ts +99 -26
- package/src/tools/browser/tab-worker.ts +39 -13
- package/src/tools/browser.ts +17 -7
- package/src/tools/cron.ts +10 -1
- package/src/tools/debug.ts +9 -2
- package/src/tools/descriptors.test.ts +4 -4
- package/src/tools/descriptors.ts +16 -4
- package/src/tools/eval.ts +15 -0
- package/src/tools/fetch.ts +90 -2
- package/src/tools/gh.ts +27 -12
- package/src/tools/image-gen.ts +20 -6
- package/src/tools/irc.ts +5 -3
- package/src/tools/monitor.ts +9 -1
- package/src/tools/output-meta.ts +51 -3
- package/src/tools/read.ts +9 -2
- package/src/tools/recipe/runner.ts +23 -12
- package/src/tools/render-utils.ts +2 -2
- package/src/tools/search.ts +3 -2
- package/src/tools/ssh.ts +274 -8
- package/src/tools/subagent.ts +10 -4
- package/src/tools/tmux-self-injection-guard.ts +3301 -122
- package/src/tools/tool-catalog.generated.ts +1 -1
- package/src/tools/ultragoal-ask-guard.ts +13 -1
- package/src/tools/write.ts +7 -0
- package/src/tui/tree-list.ts +15 -1
- package/src/utils/commit-message-generator.ts +3 -2
- package/src/utils/external-editor.ts +21 -7
- package/src/utils/file-mentions.ts +46 -6
- package/src/utils/git.ts +38 -7
- package/src/utils/image-limits.ts +8 -0
- package/src/utils/image-loading.ts +1 -1
- package/src/utils/open.ts +73 -4
- package/src/utils/pasted-image-loading.ts +9 -8
- package/src/utils/prompt-suggestion.ts +5 -4
- package/src/utils/shell-snapshot.ts +9 -1
- package/src/utils/title-generator.ts +65 -20
- package/src/web/search/index.ts +2 -1
- package/src/web/search/providers/codex.ts +29 -32
package/src/migrate/executor.ts
CHANGED
|
@@ -76,7 +76,11 @@ async function writeSkillFileNoFollow(destination: string, content: string): Pro
|
|
|
76
76
|
}
|
|
77
77
|
}
|
|
78
78
|
|
|
79
|
-
async function ensureInstructionDirectory(
|
|
79
|
+
async function ensureInstructionDirectory(
|
|
80
|
+
directory: string,
|
|
81
|
+
project: boolean,
|
|
82
|
+
label = "instruction destination",
|
|
83
|
+
): Promise<void> {
|
|
80
84
|
let trustedBase = path.dirname(directory);
|
|
81
85
|
if (!project) {
|
|
82
86
|
const home = path.resolve(getTrustedHomeDir());
|
|
@@ -92,7 +96,7 @@ async function ensureInstructionDirectory(directory: string, project: boolean):
|
|
|
92
96
|
try {
|
|
93
97
|
const stat = await fs.lstat(current);
|
|
94
98
|
if (!stat.isDirectory() || stat.isSymbolicLink()) {
|
|
95
|
-
throw new Error(`user
|
|
99
|
+
throw new Error(`user ${label} ancestor is not a real directory: ${current}`);
|
|
96
100
|
}
|
|
97
101
|
trustedBase = current;
|
|
98
102
|
break;
|
|
@@ -105,7 +109,7 @@ async function ensureInstructionDirectory(directory: string, project: boolean):
|
|
|
105
109
|
}
|
|
106
110
|
}
|
|
107
111
|
}
|
|
108
|
-
await ensureRealDirectoryPathNoFollow(directory, trustedBase,
|
|
112
|
+
await ensureRealDirectoryPathNoFollow(directory, trustedBase, label, true, 0o700);
|
|
109
113
|
}
|
|
110
114
|
|
|
111
115
|
async function writeInstructionFileNoFollow(destination: string, content: string, project: boolean): Promise<void> {
|
|
@@ -122,6 +126,107 @@ async function writeInstructionFileNoFollow(destination: string, content: string
|
|
|
122
126
|
}
|
|
123
127
|
}
|
|
124
128
|
|
|
129
|
+
async function writeAgentFileNoFollow(
|
|
130
|
+
destination: string,
|
|
131
|
+
content: string,
|
|
132
|
+
project: boolean,
|
|
133
|
+
createOnly: boolean,
|
|
134
|
+
): Promise<void> {
|
|
135
|
+
const destinationDirectory = path.dirname(destination);
|
|
136
|
+
if (project) {
|
|
137
|
+
const agentsRoot = path.dirname(destinationDirectory);
|
|
138
|
+
const agentRoot = path.dirname(agentsRoot);
|
|
139
|
+
await ensureRealDirectoryPathNoFollow(agentRoot, path.dirname(agentRoot), "agent destination", true, 0o700);
|
|
140
|
+
await ensureRealDirectoryPathNoFollow(agentsRoot, agentRoot, "agent destination", true, 0o700);
|
|
141
|
+
await ensureRealDirectoryPathNoFollow(destinationDirectory, agentsRoot, "agent destination", true, 0o700);
|
|
142
|
+
} else {
|
|
143
|
+
await ensureInstructionDirectory(destinationDirectory, false, "agent destination");
|
|
144
|
+
}
|
|
145
|
+
const handle = await fs.open(
|
|
146
|
+
destination,
|
|
147
|
+
createOnly
|
|
148
|
+
? fsSync.constants.O_WRONLY | fsSync.constants.O_CREAT | fsSync.constants.O_EXCL | fsSync.constants.O_NOFOLLOW
|
|
149
|
+
: fsSync.constants.O_WRONLY |
|
|
150
|
+
fsSync.constants.O_CREAT |
|
|
151
|
+
fsSync.constants.O_TRUNC |
|
|
152
|
+
fsSync.constants.O_NOFOLLOW,
|
|
153
|
+
0o666,
|
|
154
|
+
);
|
|
155
|
+
try {
|
|
156
|
+
await handle.writeFile(content, "utf-8");
|
|
157
|
+
} finally {
|
|
158
|
+
await handle.close();
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/** Generated hook modules live beside the agent's own hooks, so they use the same no-follow rules. */
|
|
163
|
+
async function writeHookModuleNoFollow(destination: string, content: string, createOnly: boolean): Promise<void> {
|
|
164
|
+
await ensureInstructionDirectory(path.dirname(destination), false, "hook destination");
|
|
165
|
+
const handle = await fs.open(
|
|
166
|
+
destination,
|
|
167
|
+
createOnly
|
|
168
|
+
? fsSync.constants.O_WRONLY | fsSync.constants.O_CREAT | fsSync.constants.O_EXCL | fsSync.constants.O_NOFOLLOW
|
|
169
|
+
: fsSync.constants.O_WRONLY |
|
|
170
|
+
fsSync.constants.O_CREAT |
|
|
171
|
+
fsSync.constants.O_TRUNC |
|
|
172
|
+
fsSync.constants.O_NOFOLLOW,
|
|
173
|
+
0o666,
|
|
174
|
+
);
|
|
175
|
+
try {
|
|
176
|
+
await handle.writeFile(content, "utf-8");
|
|
177
|
+
} finally {
|
|
178
|
+
await handle.close();
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
async function writeCommandFileNoFollow(
|
|
183
|
+
destination: string,
|
|
184
|
+
content: string,
|
|
185
|
+
name: string,
|
|
186
|
+
project: boolean,
|
|
187
|
+
createOnly: boolean,
|
|
188
|
+
): Promise<void> {
|
|
189
|
+
const destinationDirectory = path.dirname(destination);
|
|
190
|
+
if (project) {
|
|
191
|
+
const nameParts = name.split(":");
|
|
192
|
+
let commandsRoot = destinationDirectory;
|
|
193
|
+
for (let index = 1; index < nameParts.length; index++) commandsRoot = path.dirname(commandsRoot);
|
|
194
|
+
const agentRoot = path.dirname(commandsRoot);
|
|
195
|
+
await ensureRealDirectoryPathNoFollow(
|
|
196
|
+
agentRoot,
|
|
197
|
+
path.dirname(agentRoot),
|
|
198
|
+
"slash command destination",
|
|
199
|
+
true,
|
|
200
|
+
0o700,
|
|
201
|
+
);
|
|
202
|
+
await ensureRealDirectoryPathNoFollow(commandsRoot, agentRoot, "slash command destination", true, 0o700);
|
|
203
|
+
await ensureRealDirectoryPathNoFollow(
|
|
204
|
+
destinationDirectory,
|
|
205
|
+
commandsRoot,
|
|
206
|
+
"slash command destination",
|
|
207
|
+
true,
|
|
208
|
+
0o700,
|
|
209
|
+
);
|
|
210
|
+
} else {
|
|
211
|
+
await ensureInstructionDirectory(destinationDirectory, false, "slash command destination");
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
const handle = await fs.open(
|
|
215
|
+
destination,
|
|
216
|
+
fsSync.constants.O_WRONLY |
|
|
217
|
+
fsSync.constants.O_CREAT |
|
|
218
|
+
fsSync.constants.O_TRUNC |
|
|
219
|
+
fsSync.constants.O_NOFOLLOW |
|
|
220
|
+
(createOnly ? fsSync.constants.O_EXCL : 0),
|
|
221
|
+
0o666,
|
|
222
|
+
);
|
|
223
|
+
try {
|
|
224
|
+
await handle.writeFile(content, "utf-8");
|
|
225
|
+
} finally {
|
|
226
|
+
await handle.close();
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
|
|
125
230
|
export async function executeActions(
|
|
126
231
|
actions: MigrateAction[],
|
|
127
232
|
options?: { project?: boolean },
|
|
@@ -159,10 +264,43 @@ export async function executeActions(
|
|
|
159
264
|
options?.project ?? false,
|
|
160
265
|
);
|
|
161
266
|
out.push(action);
|
|
267
|
+
} else if (action.type === "hook" && action.hook && action.destination) {
|
|
268
|
+
await writeHookModuleNoFollow(action.destination, action.hook.content, action.operation === "create");
|
|
269
|
+
out.push(action);
|
|
270
|
+
} else if (action.type === "agent" && action.agent && action.destination) {
|
|
271
|
+
await writeAgentFileNoFollow(
|
|
272
|
+
action.destination,
|
|
273
|
+
action.agent.content,
|
|
274
|
+
options?.project ?? false,
|
|
275
|
+
action.operation === "create",
|
|
276
|
+
);
|
|
277
|
+
out.push(action);
|
|
278
|
+
} else if (action.type === "command" && action.command && action.destination && action.name) {
|
|
279
|
+
await writeCommandFileNoFollow(
|
|
280
|
+
action.destination,
|
|
281
|
+
action.command.content,
|
|
282
|
+
action.name,
|
|
283
|
+
options?.project ?? false,
|
|
284
|
+
action.operation === "create",
|
|
285
|
+
);
|
|
286
|
+
out.push(action);
|
|
162
287
|
} else {
|
|
163
288
|
out.push(action);
|
|
164
289
|
}
|
|
165
290
|
} catch (error) {
|
|
291
|
+
if (
|
|
292
|
+
(action.type === "command" || action.type === "agent") &&
|
|
293
|
+
action.operation === "create" &&
|
|
294
|
+
(error as NodeJS.ErrnoException).code === "EEXIST"
|
|
295
|
+
) {
|
|
296
|
+
out.push({
|
|
297
|
+
...action,
|
|
298
|
+
operation: "skip",
|
|
299
|
+
status: "skipped_exists",
|
|
300
|
+
reason: "destination appeared after planning; refusing to overwrite it",
|
|
301
|
+
});
|
|
302
|
+
continue;
|
|
303
|
+
}
|
|
166
304
|
out.push({
|
|
167
305
|
...action,
|
|
168
306
|
operation: "fail",
|
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Render a gjc hook module from imported foreign hook commands.
|
|
3
|
+
*
|
|
4
|
+
* Claude Code hooks are shell commands: the runtime writes a JSON payload to
|
|
5
|
+
* stdin and reads the exit code. `PreToolUse` blocks on exit 2 with stderr as the
|
|
6
|
+
* reason; every other event is observational. gjc hooks are TypeScript modules
|
|
7
|
+
* that subscribe to events in-process, so the importer reproduces the foreign
|
|
8
|
+
* contract rather than rewriting the commands.
|
|
9
|
+
*
|
|
10
|
+
* Only events whose semantics are unambiguous are translated. The payload keeps
|
|
11
|
+
* the foreign field names so an existing script that reads its JSON keeps working;
|
|
12
|
+
* fields this host cannot supply are omitted instead of guessed, which the module
|
|
13
|
+
* header states.
|
|
14
|
+
*/
|
|
15
|
+
import type { HookCandidate, MigrateSource } from "./types";
|
|
16
|
+
|
|
17
|
+
/** Foreign event -> gjc hook event. Blocking events are marked separately. */
|
|
18
|
+
const EVENT_MAP: Record<string, { event: string; blocking: boolean }> = {
|
|
19
|
+
PreToolUse: { event: "tool_call", blocking: true },
|
|
20
|
+
PostToolUse: { event: "tool_result", blocking: false },
|
|
21
|
+
SessionStart: { event: "session_start", blocking: false },
|
|
22
|
+
Stop: { event: "turn_end", blocking: false },
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
export function isSupportedHookEvent(event: string): boolean {
|
|
26
|
+
return event in EVENT_MAP;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/** Foreign events this importer translates, for user-facing messages. */
|
|
30
|
+
export const SUPPORTED_HOOK_EVENTS: readonly string[] = Object.keys(EVENT_MAP);
|
|
31
|
+
|
|
32
|
+
interface RenderedHook {
|
|
33
|
+
event: string;
|
|
34
|
+
blocking: boolean;
|
|
35
|
+
matcher?: string;
|
|
36
|
+
command: string;
|
|
37
|
+
sourceEvent: string;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function constantName(event: string): string {
|
|
41
|
+
return event.replace(/[^a-z_]/g, "_").toUpperCase();
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function renderHookLiteral(hook: RenderedHook): string {
|
|
45
|
+
const parts = [`event: ${JSON.stringify(hook.sourceEvent)}`, `command: ${JSON.stringify(hook.command)}`];
|
|
46
|
+
if (hook.matcher) parts.push(`matcher: ${JSON.stringify(hook.matcher)}`);
|
|
47
|
+
return `\t{ ${parts.join(", ")} },`;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Build the module source. The result is deterministic for a given candidate
|
|
52
|
+
* list, so a dry-run report and a live write describe the same file.
|
|
53
|
+
*/
|
|
54
|
+
export function renderHookModule(source: MigrateSource, candidates: readonly HookCandidate[]): string {
|
|
55
|
+
const hooks: RenderedHook[] = [];
|
|
56
|
+
for (const candidate of candidates) {
|
|
57
|
+
const mapped = EVENT_MAP[candidate.event];
|
|
58
|
+
if (!mapped) continue;
|
|
59
|
+
hooks.push({
|
|
60
|
+
event: mapped.event,
|
|
61
|
+
blocking: mapped.blocking,
|
|
62
|
+
matcher: candidate.matcher,
|
|
63
|
+
command: candidate.command,
|
|
64
|
+
sourceEvent: candidate.event,
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
const byEvent = new Map<string, RenderedHook[]>();
|
|
68
|
+
for (const hook of hooks) {
|
|
69
|
+
const list = byEvent.get(hook.event) ?? [];
|
|
70
|
+
list.push(hook);
|
|
71
|
+
byEvent.set(hook.event, list);
|
|
72
|
+
}
|
|
73
|
+
const eventNames = [...byEvent.keys()].sort();
|
|
74
|
+
const blocks = eventNames
|
|
75
|
+
.map(event => {
|
|
76
|
+
const entries = (byEvent.get(event) ?? []).map(renderHookLiteral).join("\n");
|
|
77
|
+
return `const ${constantName(event)}_HOOKS: readonly ImportedHook[] = [\n${entries}\n];`;
|
|
78
|
+
})
|
|
79
|
+
.join("\n\n");
|
|
80
|
+
const registrations = eventNames
|
|
81
|
+
.map(event => {
|
|
82
|
+
const blocking = byEvent.get(event)?.some(hook => hook.blocking) ?? false;
|
|
83
|
+
const register = blocking ? "registerBlocking" : "registerObservational";
|
|
84
|
+
return `\t${register}(api, ${JSON.stringify(event)}, ${constantName(event)}_HOOKS);`;
|
|
85
|
+
})
|
|
86
|
+
.join("\n");
|
|
87
|
+
|
|
88
|
+
return `/**
|
|
89
|
+
* Generated by \`gjc migrate --from ${source}\`. Re-run with --force to regenerate.
|
|
90
|
+
*
|
|
91
|
+
* These hooks were shell commands in ${source}. Each command receives a JSON
|
|
92
|
+
* payload on stdin under the origin's field names; fields this runtime cannot
|
|
93
|
+
* supply (session id, transcript path) are omitted rather than invented.
|
|
94
|
+
* \`PreToolUse\` blocks the tool when the command exits 2 and uses its stderr as
|
|
95
|
+
* the reason, which is the origin contract; the other events only run the
|
|
96
|
+
* command.
|
|
97
|
+
*/
|
|
98
|
+
import { spawn } from "node:child_process";
|
|
99
|
+
|
|
100
|
+
interface ImportedHook {
|
|
101
|
+
event: string;
|
|
102
|
+
command: string;
|
|
103
|
+
matcher?: string;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
interface HookCommandResult {
|
|
107
|
+
code: number;
|
|
108
|
+
stdout: string;
|
|
109
|
+
stderr: string;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
interface HookApi {
|
|
113
|
+
on(event: string, handler: (payload: unknown, context: { cwd?: string }) => unknown): void;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
${blocks}
|
|
117
|
+
|
|
118
|
+
function matches(matcher: string | undefined, toolName: string | undefined): boolean {
|
|
119
|
+
if (!matcher || !matcher.trim()) return true;
|
|
120
|
+
if (!toolName) return false;
|
|
121
|
+
try {
|
|
122
|
+
return new RegExp(\`^(?:\${matcher})$\`).test(toolName);
|
|
123
|
+
} catch {
|
|
124
|
+
return matcher === toolName;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
function shellCommand(command: string): string[] {
|
|
129
|
+
return process.platform === "win32"
|
|
130
|
+
? [process.env.ComSpec ?? "cmd.exe", "/d", "/s", "/c", command]
|
|
131
|
+
: ["/bin/bash", "-lc", command];
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
async function runHook(hook: ImportedHook, payload: unknown): Promise<HookCommandResult> {
|
|
135
|
+
const [file, ...args] = shellCommand(hook.command);
|
|
136
|
+
const child = spawn(file, args, { stdio: ["pipe", "pipe", "pipe"] });
|
|
137
|
+
child.stdin?.end(JSON.stringify(payload));
|
|
138
|
+
let stdout = "";
|
|
139
|
+
let stderr = "";
|
|
140
|
+
child.stdout?.on("data", chunk => {
|
|
141
|
+
stdout += String(chunk);
|
|
142
|
+
});
|
|
143
|
+
child.stderr?.on("data", chunk => {
|
|
144
|
+
stderr += String(chunk);
|
|
145
|
+
});
|
|
146
|
+
const code = await new Promise<number>(resolve => {
|
|
147
|
+
child.on("error", () => resolve(1));
|
|
148
|
+
child.on("close", value => resolve(value ?? 1));
|
|
149
|
+
});
|
|
150
|
+
return { code, stdout, stderr };
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
function registerBlocking(api: HookApi, event: string, hooks: readonly ImportedHook[]): void {
|
|
154
|
+
api.on(event, async (payload, context) => {
|
|
155
|
+
const record = payload as { toolName?: string; input?: unknown };
|
|
156
|
+
for (const hook of hooks) {
|
|
157
|
+
if (!matches(hook.matcher, record?.toolName)) continue;
|
|
158
|
+
const result = await runHook(hook, {
|
|
159
|
+
hook_event_name: hook.event,
|
|
160
|
+
tool_name: record?.toolName,
|
|
161
|
+
tool_input: record?.input,
|
|
162
|
+
cwd: context?.cwd,
|
|
163
|
+
});
|
|
164
|
+
if (result.code === 2) {
|
|
165
|
+
return { block: true, reason: result.stderr.trim() || \`blocked by imported \${hook.event} hook\` };
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
return undefined;
|
|
169
|
+
});
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
function registerObservational(api: HookApi, event: string, hooks: readonly ImportedHook[]): void {
|
|
173
|
+
api.on(event, async (payload, context) => {
|
|
174
|
+
const record = payload as { toolName?: string; input?: unknown; turnIndex?: number };
|
|
175
|
+
for (const hook of hooks) {
|
|
176
|
+
if (event === "tool_result" && !matches(hook.matcher, record?.toolName)) continue;
|
|
177
|
+
await runHook(hook, {
|
|
178
|
+
hook_event_name: hook.event,
|
|
179
|
+
tool_name: record?.toolName,
|
|
180
|
+
tool_input: record?.input,
|
|
181
|
+
turn_index: record?.turnIndex,
|
|
182
|
+
cwd: context?.cwd,
|
|
183
|
+
});
|
|
184
|
+
}
|
|
185
|
+
return undefined;
|
|
186
|
+
});
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
export default (api: HookApi): void => {
|
|
190
|
+
${registrations}
|
|
191
|
+
};
|
|
192
|
+
`;
|
|
193
|
+
}
|
package/src/migrate/report.ts
CHANGED
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
import {
|
|
8
8
|
emptyStatusCounts,
|
|
9
9
|
isFailureStatus,
|
|
10
|
+
type ManualAction,
|
|
10
11
|
type MigrateAction,
|
|
11
12
|
type MigrateDestinations,
|
|
12
13
|
type MigrateReport,
|
|
@@ -20,6 +21,7 @@ export interface BuildReportInput {
|
|
|
20
21
|
warnings: MigrateWarning[];
|
|
21
22
|
sources: MigrateSource[];
|
|
22
23
|
destinations: MigrateDestinations;
|
|
24
|
+
manualActions: ManualAction[];
|
|
23
25
|
dryRun: boolean;
|
|
24
26
|
project: boolean;
|
|
25
27
|
force: boolean;
|
|
@@ -30,6 +32,9 @@ export function buildReport(input: BuildReportInput): MigrateReport {
|
|
|
30
32
|
const byType = {
|
|
31
33
|
mcp: emptyStatusCounts(),
|
|
32
34
|
skill: emptyStatusCounts(),
|
|
35
|
+
command: emptyStatusCounts(),
|
|
36
|
+
agent: emptyStatusCounts(),
|
|
37
|
+
hook: emptyStatusCounts(),
|
|
33
38
|
instruction: emptyStatusCounts(),
|
|
34
39
|
source: emptyStatusCounts(),
|
|
35
40
|
};
|
|
@@ -52,6 +57,7 @@ export function buildReport(input: BuildReportInput): MigrateReport {
|
|
|
52
57
|
sources: input.sources,
|
|
53
58
|
destinations: input.destinations,
|
|
54
59
|
summary: { total, byType, bySource },
|
|
60
|
+
manualActions: input.manualActions,
|
|
55
61
|
actions: input.actions.map(action => ({
|
|
56
62
|
source: action.source,
|
|
57
63
|
type: action.type,
|
|
@@ -81,12 +87,42 @@ export function renderHuman(report: MigrateReport): string {
|
|
|
81
87
|
lines.push(`gjc migrate${mode}: ${report.ok ? "ok" : "completed with failures"}`);
|
|
82
88
|
lines.push(`Sources: ${report.sources.join(", ") || "none"}`);
|
|
83
89
|
lines.push(
|
|
84
|
-
`Destination: mcp=${report.destinations.mcpConfigPath} skills=${report.destinations.skillsDir} instructions=${report.destinations.instructionsPath}`,
|
|
90
|
+
`Destination: mcp=${report.destinations.mcpConfigPath} skills=${report.destinations.skillsDir} commands=${report.destinations.commandsDir} agents=${report.destinations.agentsDir} hooks=${report.destinations.hooksDir} instructions=${report.destinations.instructionsPath}`,
|
|
85
91
|
);
|
|
86
92
|
lines.push("");
|
|
87
93
|
lines.push(`MCP: ${summarizeCounts(report.summary.byType.mcp)}`);
|
|
88
94
|
lines.push(`Skills: ${summarizeCounts(report.summary.byType.skill)}`);
|
|
95
|
+
lines.push(`Commands: ${summarizeCounts(report.summary.byType.command)}`);
|
|
96
|
+
lines.push(`Agents: ${summarizeCounts(report.summary.byType.agent)}`);
|
|
97
|
+
lines.push(`Hooks: ${summarizeCounts(report.summary.byType.hook)}`);
|
|
89
98
|
lines.push(`Instructions: ${summarizeCounts(report.summary.byType.instruction)}`);
|
|
99
|
+
const commandActions = report.actions.filter(action => action.type === "command");
|
|
100
|
+
if (commandActions.length > 0) {
|
|
101
|
+
lines.push("Slash commands:");
|
|
102
|
+
for (const action of commandActions) {
|
|
103
|
+
lines.push(
|
|
104
|
+
` - [${action.source}] /${action.name ?? ""}: ${action.status}${action.reason ? ` (${action.reason})` : ""}`,
|
|
105
|
+
);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
const agentActions = report.actions.filter(action => action.type === "agent");
|
|
109
|
+
if (agentActions.length > 0) {
|
|
110
|
+
lines.push("Subagents:");
|
|
111
|
+
for (const action of agentActions) {
|
|
112
|
+
lines.push(
|
|
113
|
+
` - [${action.source}] ${action.name ?? ""}: ${action.status}${action.reason ? ` (${action.reason})` : ""}`,
|
|
114
|
+
);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
const hookActions = report.actions.filter(action => action.type === "hook");
|
|
118
|
+
if (hookActions.length > 0) {
|
|
119
|
+
lines.push("Hooks:");
|
|
120
|
+
for (const action of hookActions) {
|
|
121
|
+
lines.push(
|
|
122
|
+
` - [${action.source}] ${action.name ?? ""}: ${action.status}${action.reason ? ` (${action.reason})` : ""}`,
|
|
123
|
+
);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
90
126
|
const instructionActions = report.actions.filter(action => action.type === "instruction");
|
|
91
127
|
if (instructionActions.length > 0) {
|
|
92
128
|
lines.push("Instruction files:");
|
|
@@ -109,6 +145,14 @@ export function renderHuman(report: MigrateReport): string {
|
|
|
109
145
|
}
|
|
110
146
|
}
|
|
111
147
|
|
|
148
|
+
if (report.manualActions.length > 0) {
|
|
149
|
+
lines.push("");
|
|
150
|
+
lines.push("Manual actions:");
|
|
151
|
+
for (const m of report.manualActions) {
|
|
152
|
+
lines.push(` - [${m.source}] ${m.area}: ${m.detail}`);
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
|
|
112
156
|
if (report.warnings.length > 0) {
|
|
113
157
|
lines.push("");
|
|
114
158
|
lines.push("Warnings:");
|
package/src/migrate/types.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Shared types for `gjc migrate`.
|
|
3
3
|
*
|
|
4
|
-
* Imports MCP servers, skills, and instruction files from other coding agents
|
|
4
|
+
* Imports MCP servers, skills, slash commands, and instruction files from other coding agents
|
|
5
5
|
* (Claude Code, Codex, OpenCode) into native GJC config. See the consensus plan under
|
|
6
6
|
* `.gjc/plans/ralplan/` for the full taxonomy and force/collision semantics.
|
|
7
7
|
*/
|
|
@@ -16,7 +16,7 @@ export const MIGRATE_SOURCES: readonly MigrateSource[] = ["claude-code", "codex"
|
|
|
16
16
|
export const CANONICAL_SOURCE_ORDER: readonly MigrateSource[] = MIGRATE_SOURCES;
|
|
17
17
|
|
|
18
18
|
/** What kind of thing an action/coverage row is about. */
|
|
19
|
-
export type MigrateItemType = "mcp" | "skill" | "instruction" | "source";
|
|
19
|
+
export type MigrateItemType = "mcp" | "skill" | "command" | "agent" | "hook" | "instruction" | "source";
|
|
20
20
|
|
|
21
21
|
/**
|
|
22
22
|
* Per-item outcome taxonomy.
|
|
@@ -84,6 +84,24 @@ export interface InstructionCandidate {
|
|
|
84
84
|
content: string;
|
|
85
85
|
}
|
|
86
86
|
|
|
87
|
+
/** A Markdown subagent collected from a foreign coding-agent setup. */
|
|
88
|
+
export interface AgentCandidate {
|
|
89
|
+
source: MigrateSource;
|
|
90
|
+
/** Agent name exactly as it will be loaded from `<agentsDir>/<name>.md`. */
|
|
91
|
+
name: string;
|
|
92
|
+
/** Full Markdown content with normalized GJC frontmatter. */
|
|
93
|
+
content: string;
|
|
94
|
+
warnings: string[];
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/** A Markdown slash command collected from a foreign coding-agent setup. */
|
|
98
|
+
export interface SlashCommandCandidate {
|
|
99
|
+
source: MigrateSource;
|
|
100
|
+
/** Name without the leading slash; nested path components use `:`. */
|
|
101
|
+
name: string;
|
|
102
|
+
content: string;
|
|
103
|
+
}
|
|
104
|
+
|
|
87
105
|
/**
|
|
88
106
|
* Source-level diagnostic for a single source/type pair (e.g. "codex mcp config
|
|
89
107
|
* was malformed"). Distinct from per-item actions so absent/unreadable sources
|
|
@@ -96,12 +114,48 @@ export interface SourceDiagnostic {
|
|
|
96
114
|
message: string;
|
|
97
115
|
}
|
|
98
116
|
|
|
117
|
+
/**
|
|
118
|
+
* A foreign hook command that maps onto a gjc hook event.
|
|
119
|
+
*
|
|
120
|
+
* Claude Code hooks are shell commands that receive a JSON payload on stdin and
|
|
121
|
+
* signal a block with exit code 2; gjc hooks are TypeScript modules. The
|
|
122
|
+
* importer generates a module that reproduces that contract for the events whose
|
|
123
|
+
* semantics are unambiguous.
|
|
124
|
+
*/
|
|
125
|
+
export interface HookCandidate {
|
|
126
|
+
source: MigrateSource;
|
|
127
|
+
/** Foreign event name, e.g. `PreToolUse`. */
|
|
128
|
+
event: string;
|
|
129
|
+
/** Tool-name matcher; empty or absent means every tool. */
|
|
130
|
+
matcher?: string;
|
|
131
|
+
/** Shell command exactly as the source declared it. */
|
|
132
|
+
command: string;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* A source setting the migration cannot express in gjc. Reported instead of
|
|
137
|
+
* guessed at: silently translating an approval or hook policy would change what
|
|
138
|
+
* the agent is allowed to do. Secret values are never read, so detail may name
|
|
139
|
+
* keys but never their contents.
|
|
140
|
+
*/
|
|
141
|
+
export interface ManualAction {
|
|
142
|
+
source: MigrateSource;
|
|
143
|
+
/** Setting area, e.g. `hooks`, `settings.model`, `env`. */
|
|
144
|
+
area: string;
|
|
145
|
+
/** One-line instruction naming the concrete follow-up. */
|
|
146
|
+
detail: string;
|
|
147
|
+
}
|
|
148
|
+
|
|
99
149
|
/** Normalized candidates + diagnostics returned by an adapter. */
|
|
100
150
|
export interface AdapterResult {
|
|
101
151
|
mcpCandidates: McpCandidate[];
|
|
102
152
|
skillCandidates: SkillCandidate[];
|
|
153
|
+
slashCommandCandidates: SlashCommandCandidate[];
|
|
154
|
+
agentCandidates: AgentCandidate[];
|
|
155
|
+
hookCandidates: HookCandidate[];
|
|
103
156
|
instructionCandidates: InstructionCandidate[];
|
|
104
157
|
diagnostics: SourceDiagnostic[];
|
|
158
|
+
manualActions: ManualAction[];
|
|
105
159
|
}
|
|
106
160
|
|
|
107
161
|
/** A single planned action consumed identically by dry-run and live execution. */
|
|
@@ -120,6 +174,9 @@ export interface MigrateAction {
|
|
|
120
174
|
/** Resolved payload the executor needs; never serialized to the report. */
|
|
121
175
|
mcp?: { config: MCPServerConfig; force: boolean };
|
|
122
176
|
skill?: { content: string };
|
|
177
|
+
command?: { content: string };
|
|
178
|
+
agent?: { content: string };
|
|
179
|
+
hook?: { content: string };
|
|
123
180
|
instruction?: { content: string };
|
|
124
181
|
}
|
|
125
182
|
|
|
@@ -135,6 +192,10 @@ export type StatusCounts = Record<MigrationStatus, number>;
|
|
|
135
192
|
export interface MigrateDestinations {
|
|
136
193
|
mcpConfigPath: string;
|
|
137
194
|
skillsDir: string;
|
|
195
|
+
commandsDir: string;
|
|
196
|
+
agentsDir: string;
|
|
197
|
+
/** Directory that receives generated hook modules. */
|
|
198
|
+
hooksDir: string;
|
|
138
199
|
instructionsPath: string;
|
|
139
200
|
}
|
|
140
201
|
|
|
@@ -148,7 +209,15 @@ export interface MigrateReport {
|
|
|
148
209
|
destinations: MigrateDestinations;
|
|
149
210
|
summary: {
|
|
150
211
|
total: StatusCounts;
|
|
151
|
-
byType: {
|
|
212
|
+
byType: {
|
|
213
|
+
mcp: StatusCounts;
|
|
214
|
+
skill: StatusCounts;
|
|
215
|
+
command: StatusCounts;
|
|
216
|
+
agent: StatusCounts;
|
|
217
|
+
hook: StatusCounts;
|
|
218
|
+
instruction: StatusCounts;
|
|
219
|
+
source: StatusCounts;
|
|
220
|
+
};
|
|
152
221
|
bySource: Record<MigrateSource, StatusCounts>;
|
|
153
222
|
};
|
|
154
223
|
actions: Array<{
|
|
@@ -163,6 +232,7 @@ export interface MigrateReport {
|
|
|
163
232
|
warnings?: string[];
|
|
164
233
|
}>;
|
|
165
234
|
warnings: MigrateWarning[];
|
|
235
|
+
manualActions: ManualAction[];
|
|
166
236
|
}
|
|
167
237
|
|
|
168
238
|
/** Create a zeroed status-count record. */
|
|
@@ -631,6 +631,11 @@ function isAbortNoEffect(value: unknown): boolean {
|
|
|
631
631
|
const result = object(candidate?.result) ?? candidate;
|
|
632
632
|
return result?.turn === "no_active_turn" || result?.turn === "no_effect" || result?.turn === "no_store";
|
|
633
633
|
}
|
|
634
|
+
function isAbortUncertain(value: unknown): boolean {
|
|
635
|
+
const candidate = object(value);
|
|
636
|
+
const result = object(candidate?.result) ?? candidate;
|
|
637
|
+
return result?.turn === "uncertain";
|
|
638
|
+
}
|
|
634
639
|
|
|
635
640
|
function strictCorrelationFrom(...values: unknown[]): PromptCorrelation | undefined {
|
|
636
641
|
const correlation: PromptCorrelation = {};
|
|
@@ -3064,12 +3069,16 @@ export class AcpAgent implements Agent {
|
|
|
3064
3069
|
let acknowledgementScope = scope;
|
|
3065
3070
|
if (
|
|
3066
3071
|
!isAbortAcknowledged(acknowledgement, scope) &&
|
|
3067
|
-
isAbortNoEffect(acknowledgement) &&
|
|
3072
|
+
(isAbortNoEffect(acknowledgement) || isAbortUncertain(acknowledgement)) &&
|
|
3068
3073
|
(waiterWasUnacknowledged || waiterWasBeforeActivity) &&
|
|
3069
3074
|
waiter &&
|
|
3070
3075
|
record.activePrompt === waiter &&
|
|
3071
3076
|
!waiter.settled
|
|
3072
3077
|
) {
|
|
3078
|
+
// An uncertain replay result is not proof that the worker stopped, but it
|
|
3079
|
+
// also must not erase a cancellation that arrived before prompt admission.
|
|
3080
|
+
// Retain the intent and retry once the prompt acknowledgement/start gives
|
|
3081
|
+
// the abort a concrete active turn to target.
|
|
3073
3082
|
waiter.cancelBeforeAdmission = true;
|
|
3074
3083
|
waiter.cancelBeforeAdmissionScope =
|
|
3075
3084
|
waiter.cancelBeforeAdmissionScope === "owned" || scope === "owned" ? "owned" : "turn";
|
|
@@ -90,14 +90,22 @@ export function resolveAgentCreationModel(
|
|
|
90
90
|
modelPatterns: string[],
|
|
91
91
|
modelRegistry: ModelRegistry,
|
|
92
92
|
settings: Settings | undefined,
|
|
93
|
+
credentialSessionId?: string,
|
|
93
94
|
) {
|
|
94
|
-
const { model } = resolveModelOverride(
|
|
95
|
+
const { model } = resolveModelOverride(
|
|
96
|
+
modelPatterns,
|
|
97
|
+
modelRegistry,
|
|
98
|
+
settings,
|
|
99
|
+
undefined,
|
|
100
|
+
undefined,
|
|
101
|
+
credentialSessionId,
|
|
102
|
+
);
|
|
95
103
|
if (model) return model;
|
|
96
104
|
if (modelPatterns.length > 0) {
|
|
97
105
|
throw new Error(`Configured model selector(s) did not resolve: ${modelPatterns.join(", ")}.`);
|
|
98
106
|
}
|
|
99
107
|
|
|
100
|
-
const fallbackModel = modelRegistry.getAvailable()[0];
|
|
108
|
+
const fallbackModel = modelRegistry.getAvailable(credentialSessionId)[0];
|
|
101
109
|
if (!fallbackModel) {
|
|
102
110
|
throw new Error("No available model to generate agent specification.");
|
|
103
111
|
}
|
|
@@ -612,7 +620,12 @@ export class AgentDashboard extends Container {
|
|
|
612
620
|
settings?.getModelRole("default"),
|
|
613
621
|
settings,
|
|
614
622
|
);
|
|
615
|
-
const selectedModel = resolveAgentCreationModel(
|
|
623
|
+
const selectedModel = resolveAgentCreationModel(
|
|
624
|
+
modelPatterns,
|
|
625
|
+
modelRegistry,
|
|
626
|
+
settings,
|
|
627
|
+
this.modelContext.credentialSessionId,
|
|
628
|
+
);
|
|
616
629
|
|
|
617
630
|
const systemPrompt = prompt.render(agentCreationArchitectPrompt, { TASK_TOOL_NAME: "task" });
|
|
618
631
|
const userPrompt = prompt.render(agentCreationUserPrompt, { request: description });
|
|
@@ -701,7 +714,7 @@ export class AgentDashboard extends Container {
|
|
|
701
714
|
if (!modelRegistry) return [];
|
|
702
715
|
const query = input.trim().toLowerCase();
|
|
703
716
|
if (!query) return [];
|
|
704
|
-
const available = modelRegistry.getAvailable();
|
|
717
|
+
const available = modelRegistry.getAvailable(this.modelContext.credentialSessionId);
|
|
705
718
|
const seen = new Set<string>();
|
|
706
719
|
const matches: string[] = [];
|
|
707
720
|
for (const model of available) {
|
|
@@ -757,6 +770,9 @@ export class AgentDashboard extends Container {
|
|
|
757
770
|
patterns,
|
|
758
771
|
modelRegistry,
|
|
759
772
|
this.#settingsManager ?? undefined,
|
|
773
|
+
undefined,
|
|
774
|
+
undefined,
|
|
775
|
+
this.modelContext.credentialSessionId,
|
|
760
776
|
);
|
|
761
777
|
if (!model) return undefined;
|
|
762
778
|
return {
|