@akira-tl/forgerelay 0.8.8 → 0.8.9
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 +12 -0
- package/README.md +10 -18
- package/capabilities/host-integration/GUIDE.md +2 -2
- package/dist/activity/{audit-store.js → history/audit-store.js} +129 -21
- package/dist/activity/history/bash-output-store.js +250 -0
- package/dist/activity/{host-turn-store.js → history/host-turn-store.js} +4 -1
- package/dist/activity/{query-service.js → history/query-service.js} +13 -7
- package/dist/activity/{lifecycle.js → runtime/lifecycle.js} +1 -1
- package/dist/activity/{mcp-query-tools.js → runtime/mcp-query-tools.js} +3 -3
- package/dist/activity/storage/history.js +12 -0
- package/dist/activity/storage/paths.js +56 -0
- package/dist/activity/storage/segmented-log.js +149 -0
- package/dist/cli/setup-support.js +116 -0
- package/dist/cli.js +18 -126
- package/dist/lsp/code-intelligence.js +1 -1
- package/dist/lsp/test-support/server-fixture.js +9 -9
- package/dist/mcp/filesystem/filesystem-tools.js +245 -0
- package/dist/mcp/hooks/command-runner.js +79 -0
- package/dist/{hook-cli.js → mcp/hooks/hook-cli.js} +1 -1
- package/dist/{hooks.js → mcp/hooks/hooks.js} +3 -80
- package/dist/{oauth-provider.js → mcp/oauth/oauth-provider.js} +1 -1
- package/dist/{oauth-store.js → mcp/oauth/oauth-store.js} +5 -4
- package/dist/{operations → mcp/operations}/native-bulk-mutations.js +2 -2
- package/dist/mcp/panel/app.js +143 -0
- package/dist/{mcp-app-template.js → mcp/panel/mcp-app-template.js} +2 -38
- package/dist/mcp/process/runtime.js +257 -0
- package/dist/mcp/process/tools.js +346 -0
- package/dist/mcp/server/core/activity-support.js +70 -0
- package/dist/{capabilities.js → mcp/server/core/capabilities.js} +5 -3
- package/dist/{capability-registry.js → mcp/server/core/capability-registry.js} +2 -2
- package/dist/mcp/server/core/capability-support.js +90 -0
- package/dist/mcp/server/core/schemas.js +216 -0
- package/dist/mcp/server/core/tool-support.js +207 -0
- package/dist/mcp/server/core/version.js +9 -0
- package/dist/mcp/server/operations/runtime/filesystem-tools.js +400 -0
- package/dist/mcp/server/operations/runtime/operation-runtime.js +523 -0
- package/dist/mcp/server/transport/http-server.js +301 -0
- package/dist/mcp/server/transport/http-support.js +43 -0
- package/dist/mcp/server/workspace/runtime/workspace-open-presentation.js +222 -0
- package/dist/mcp/server/workspace/runtime/workspace-open-schema.js +187 -0
- package/dist/mcp/server/workspace/runtime/workspace-open.js +552 -0
- package/dist/mcp/server/workspace/runtime/workspace-tools.js +601 -0
- package/dist/{config.js → runtime/config/config.js} +3 -3
- package/dist/{user-config.js → runtime/config/user-config.js} +14 -54
- package/dist/{db → runtime/state/db}/client.js +1 -1
- package/dist/{db → runtime/state/db}/migrations.js +34 -3
- package/dist/runtime/state/lock/file-lock.js +130 -0
- package/dist/runtime/testing/server-fixture.js +190 -0
- package/dist/server.js +116 -4202
- package/dist/subagents/providers/adapters/claude.js +86 -52
- package/dist/subagents/providers/adapters/codex.js +103 -39
- package/dist/subagents/providers/adapters/opencode.js +114 -84
- package/dist/subagents/providers/adapters/pi.js +59 -8
- package/dist/subagents/providers/availability.js +15 -30
- package/dist/subagents/providers/runtime/external-command.js +56 -0
- package/dist/subagents/sessions/capability.js +1 -1
- package/dist/subagents/sessions/execution.js +1 -1
- package/dist/subagents/sessions/mcp/activity-audit-test-support.js +30 -0
- package/dist/subagents/sessions/store.js +1 -1
- package/dist/ui/.vite/manifest.json +342 -410
- package/dist/ui/activity-panel-app.html +2 -4
- package/dist/ui/assets/activity-panel-app-7Xe0alhm.css +1 -0
- package/dist/ui/assets/activity-panel-app-Sl1Jro8v.js +115 -0
- package/dist/ui/assets/angular-html-B_yn91wW.js +1 -0
- package/dist/ui/assets/{angular-ts-Bxh4-O1-.js → angular-ts-DSgtloKz.js} +1 -1
- package/dist/ui/assets/{apl-BmYHKQ6m.js → apl-NatWtF8X.js} +1 -1
- package/dist/ui/assets/{astro-C51vqDKq.js → astro-BYFEVBuN.js} +1 -1
- package/dist/ui/assets/{blade-CdhVsmwq.js → blade-DoO7yvMR.js} +1 -1
- package/dist/ui/assets/c-Rl2hMCcg.js +1 -0
- package/dist/ui/assets/{cobol-Cf6azoN_.js → cobol-ByC5mWiB.js} +1 -1
- package/dist/ui/assets/{coffee-vVdGzlmt.js → coffee-Dez77Qov.js} +1 -1
- package/dist/ui/assets/cpp-DtaAQT9h.js +1 -0
- package/dist/ui/assets/{crystal-5UZy6KIX.js → crystal-Bgmbf8ec.js} +1 -1
- package/dist/ui/assets/css-C1tjBtVN.js +1 -0
- package/dist/ui/assets/{edge-CESgo4ia.js → edge-DeH6u8Wg.js} +1 -1
- package/dist/ui/assets/{elixir-9FKfw4cJ.js → elixir-_H3KnmWT.js} +1 -1
- package/dist/ui/assets/{elm-Dv7A0aLa.js → elm-CsCe3U28.js} +1 -1
- package/dist/ui/assets/{erb-DHFmDvM0.js → erb-CbM-L2g8.js} +1 -1
- package/dist/ui/assets/{git-rebase-utbU6CzV.js → git-rebase-D0s5pDh9.js} +1 -1
- package/dist/ui/assets/{glimmer-js-t2kiCkyB.js → glimmer-js-BfOToq_A.js} +1 -1
- package/dist/ui/assets/{glimmer-ts-F0Jv5okV.js → glimmer-ts-CCUOkL2K.js} +1 -1
- package/dist/ui/assets/glsl-DH4ec9dE.js +1 -0
- package/dist/ui/assets/graphql-DilAMBoI.js +1 -0
- package/dist/ui/assets/{hack-8yjdbaSA.js → hack-BDkNDZlF.js} +1 -1
- package/dist/ui/assets/haml-D_QBp6K7.js +1 -0
- package/dist/ui/assets/{handlebars-BBFEg4Lo.js → handlebars-DePUrTpC.js} +1 -1
- package/dist/ui/assets/heavy-payload-DOwZpO6n.js +290 -0
- package/dist/ui/assets/html-By4dNnM4.js +1 -0
- package/dist/ui/assets/{html-derivative-DJAQcZyc.js → html-derivative-DDw9Ef4Q.js} +1 -1
- package/dist/ui/assets/{http-Dg5LSYRK.js → http-B0QxWc9M.js} +1 -1
- package/dist/ui/assets/{hurl-Y5uYzMZR.js → hurl-vMQ4lda1.js} +1 -1
- package/dist/ui/assets/java-DYRe2lWG.js +1 -0
- package/dist/ui/assets/javascript-CxyGqwcL.js +1 -0
- package/dist/ui/assets/{jinja-Bt30h8Ik.js → jinja-JUwV-fZt.js} +1 -1
- package/dist/ui/assets/{jison-D5uZ1Nyo.js → jison-7ZqCEvS8.js} +1 -1
- package/dist/ui/assets/json-dbltgiiA.js +1 -0
- package/dist/ui/assets/jsx-oDopT7YQ.js +1 -0
- package/dist/ui/assets/{julia-76hRaaGz.js → julia-CkKiNSav.js} +1 -1
- package/dist/ui/assets/{just-CDE7W0vW.js → just-DuqCAKJ3.js} +1 -1
- package/dist/ui/assets/{latex-CVO2Byab.js → latex-sudcRDSn.js} +1 -1
- package/dist/ui/assets/{liquid-CA-AS3EX.js → liquid-Di-08Js7.js} +1 -1
- package/dist/ui/assets/lua-YlqdMoV0.js +1 -0
- package/dist/ui/assets/{marko-_BWrPfny.js → marko-CJRNyJDb.js} +1 -1
- package/dist/ui/assets/{mdc-PyJv85eS.js → mdc-t12CzZHs.js} +1 -1
- package/dist/ui/assets/{nginx-DAWVw8yu.js → nginx-57-b4XwT.js} +1 -1
- package/dist/ui/assets/{nim-CKE1dzpU.js → nim-BzrswYQa.js} +1 -1
- package/dist/ui/assets/{perl-BBhWva19.js → perl-D9JR_iIh.js} +1 -1
- package/dist/ui/assets/{php-ChIeaAoF.js → php-Py2gFHil.js} +1 -1
- package/dist/ui/assets/{pug-CMgI5afi.js → pug-PzLLy0h1.js} +1 -1
- package/dist/ui/assets/{qml-CjxTkcFH.js → qml-By8fwgV4.js} +1 -1
- package/dist/ui/assets/r-CcezTbLq.js +1 -0
- package/dist/ui/assets/{razor-i4DbHr8p.js → razor-dG-vdhL_.js} +1 -1
- package/dist/ui/assets/regexp-BlsQ-N_8.js +1 -0
- package/dist/ui/assets/{rst-oy_97Kl4.js → rst-C2awDCat.js} +1 -1
- package/dist/ui/assets/{ruby-C5zYZIyl.js → ruby-zA2W_49d.js} +1 -1
- package/dist/ui/assets/{sas-BnMEh2S3.js → sas-D49bhyaN.js} +1 -1
- package/dist/ui/assets/scss-BQb-cp01.js +1 -0
- package/dist/ui/assets/shellscript-Dga4H_Ni.js +1 -0
- package/dist/ui/assets/{shellsession-BpE5o7_3.js → shellsession-n3-5CqcF.js} +1 -1
- package/dist/ui/assets/{soy-COesmhTY.js → soy-XYBOf_BA.js} +1 -1
- package/dist/ui/assets/sql-ET4u5Cpl.js +1 -0
- package/dist/ui/assets/{stata-CGQEuSZq.js → stata-CLHcJHO_.js} +1 -1
- package/dist/ui/assets/{surrealql-Bz4JbU-9.js → surrealql-BdjQ60T4.js} +1 -1
- package/dist/ui/assets/{svelte-1jM2ejXj.js → svelte-B2RPtKJe.js} +1 -1
- package/dist/ui/assets/{templ-C-VwELcp.js → templ-CrByPW_W.js} +1 -1
- package/dist/ui/assets/{tex-t1rUiQCm.js → tex-DZBkg363.js} +1 -1
- package/dist/ui/assets/{ts-tags-Bewoa6pS.js → ts-tags-DJR3Gphb.js} +1 -1
- package/dist/ui/assets/tsx-CajzROyy.js +1 -0
- package/dist/ui/assets/{twig-DVg-vELl.js → twig-BZjk5Zda.js} +1 -1
- package/dist/ui/assets/typescript-B-EWToLj.js +1 -0
- package/dist/ui/assets/{vue-wPQ7UlMy.js → vue-D_rHCCr8.js} +1 -1
- package/dist/ui/assets/{vue-html-BHtJ8geX.js → vue-html-CC4WlvVt.js} +1 -1
- package/dist/ui/assets/{vue-vine-CNSGAWwc.js → vue-vine-BgNMVgpk.js} +1 -1
- package/dist/ui/assets/xml-BI86-WbH.js +1 -0
- package/dist/ui/assets/{xsl-3_vJ27aE.js → xsl-DXeLZV4J.js} +1 -1
- package/dist/ui/assets/yaml-60_PFlWa.js +1 -0
- package/dist/workspaces/bootstrap.js +72 -0
- package/dist/workspaces/context.js +302 -0
- package/dist/workspaces/conversation-test-support.js +86 -0
- package/dist/{git-worktrees.js → workspaces/git/git-worktrees.js} +1 -1
- package/dist/workspaces/inventory.js +131 -0
- package/dist/workspaces/paths.js +55 -0
- package/dist/{remote-auth.js → workspaces/relay/auth/remote-auth.js} +2 -2
- package/dist/workspaces/relay/result-support.js +111 -0
- package/dist/workspaces/relay/tests/test-support.js +203 -0
- package/dist/{remote-mcp-connection-pool.js → workspaces/relay/transport/remote-mcp-connection-pool.js} +1 -1
- package/dist/workspaces/relay/types.js +1 -0
- package/dist/{remote-workspace-relay.js → workspaces/relay/workspace-relay.js} +23 -172
- package/dist/workspaces/resources/skills.js +285 -0
- package/dist/{review-checkpoints.js → workspaces/review/review-checkpoints.js} +2 -2
- package/dist/workspaces/sessions.js +457 -0
- package/dist/{workspace-store.js → workspaces/state/workspace-store.js} +2 -2
- package/dist/workspaces.js +68 -995
- package/docs/chatgpt-coding-workflow.md +3 -11
- package/docs/configuration.md +12 -25
- package/docs/gotchas.md +0 -30
- package/docs/roadmap.md +6 -10
- package/docs/security.md +1 -5
- package/docs/setup.md +0 -11
- package/package.json +6 -8
- package/scripts/ci/architecture.mjs +109 -0
- package/scripts/ci/verify.mjs +5 -0
- package/scripts/debug/accept/bootstrap.mjs +326 -0
- package/scripts/debug/accept/harness.mjs +76 -0
- package/scripts/debug/accept/support.mjs +432 -0
- package/scripts/debug/accept.mjs +43 -806
- package/scripts/debug/relay-accept/support.mjs +135 -0
- package/scripts/debug/relay-accept.mjs +26 -115
- package/scripts/debug/traffic/support.mjs +167 -0
- package/scripts/debug/traffic/traffic-audit.mjs +4 -161
- package/scripts/{lsp-interop-support.test.mjs → lsp/interop-support.test.mjs} +1 -1
- package/scripts/{lsp-interop.mjs → lsp/interop.mjs} +3 -3
- package/scripts/wiki/sync.mjs +21 -10
- package/dist/activity/bash-output-store.js +0 -127
- package/dist/pi-tools.js +0 -110
- package/dist/skills.js +0 -127
- package/dist/ui/assets/activity-panel-app-7AckaxIR.js +0 -2
- package/dist/ui/assets/angular-html-BkgDQl-G.js +0 -1
- package/dist/ui/assets/c-IBzBpxAO.js +0 -1
- package/dist/ui/assets/chunk-EyZ2wyi3.js +0 -1
- package/dist/ui/assets/cpp-CVHeO-Jk.js +0 -1
- package/dist/ui/assets/css-DEfus9J5.js +0 -1
- package/dist/ui/assets/glsl-_ooCjaeB.js +0 -1
- package/dist/ui/assets/graphql-B9LqP-cY.js +0 -1
- package/dist/ui/assets/haml-BU5EoZBn.js +0 -1
- package/dist/ui/assets/heavy-payload-Bol1_mcs.js +0 -4
- package/dist/ui/assets/html-D-C6YZk4.js +0 -1
- package/dist/ui/assets/java-ppHvycj5.js +0 -1
- package/dist/ui/assets/javascript-DDTjKLlQ.js +0 -1
- package/dist/ui/assets/json-Cw91r8h1.js +0 -1
- package/dist/ui/assets/jsx-04X2FT_1.js +0 -1
- package/dist/ui/assets/lua-CiyBp52Y.js +0 -1
- package/dist/ui/assets/r-DfFga4si.js +0 -1
- package/dist/ui/assets/regexp-iC9_nbSI.js +0 -1
- package/dist/ui/assets/review-payload-BqJp0c5e.js +0 -1
- package/dist/ui/assets/scrollbar-CvE-I-jG.js +0 -287
- package/dist/ui/assets/scss-BfZXsAWQ.js +0 -1
- package/dist/ui/assets/shellscript-BC2srNaB.js +0 -1
- package/dist/ui/assets/sql-Ccv_yeUm.js +0 -1
- package/dist/ui/assets/tsx-CTXzrBkN.js +0 -1
- package/dist/ui/assets/typescript--ElHoFkw.js +0 -1
- package/dist/ui/assets/workspace-app-BLW7p6IZ.js +0 -114
- package/dist/ui/assets/workspace-app-BjNqR0en.js +0 -1
- package/dist/ui/assets/workspace-app-CmaYU4DW.js +0 -3
- package/dist/ui/assets/workspace-app-D2bJ5fjt.css +0 -1
- package/dist/ui/assets/workspace-lifecycle-app-BVbI2Ilf.js +0 -1
- package/dist/ui/assets/xml-JnmX6vyS.js +0 -1
- package/dist/ui/assets/yaml-OAcZkP-w.js +0 -1
- package/dist/ui/workspace-app.html +0 -18
- package/dist/ui/workspace-lifecycle-app.html +0 -18
- package/docs/agent-profile-schema.md +0 -114
- package/docs/artifact-exchange.md +0 -65
- package/docs/versioning.md +0 -205
- /package/capabilities/{managed-worktrees → workspace/managed-worktrees}/GUIDE.md +0 -0
- /package/capabilities/{workspace-tasks → workspace/workspace-tasks}/GUIDE.md +0 -0
- /package/dist/activity/{process-output-audit.js → runtime/process-output-audit.js} +0 -0
- /package/dist/{artifact-error.js → mcp/artifacts/artifact-error.js} +0 -0
- /package/dist/{artifact-tools.js → mcp/artifacts/artifact-tools.js} +0 -0
- /package/dist/{incoming-artifacts.js → mcp/artifacts/incoming-artifacts.js} +0 -0
- /package/dist/{advertised-files.js → mcp/filesystem/advertised-files.js} +0 -0
- /package/dist/{apply-patch.js → mcp/filesystem/apply-patch.js} +0 -0
- /package/dist/{file-mutations.js → mcp/filesystem/file-mutations.js} +0 -0
- /package/dist/{roots.js → mcp/filesystem/roots.js} +0 -0
- /package/dist/{oauth → mcp/oauth}/public-url.js +0 -0
- /package/dist/{oauth → mcp/oauth}/router.js +0 -0
- /package/dist/{operations → mcp/operations}/batch/executor.js +0 -0
- /package/dist/{operations → mcp/operations}/batch/scheduler.js +0 -0
- /package/dist/{operations → mcp/operations}/batch/types.js +0 -0
- /package/dist/{operations → mcp/operations}/bulk-mutation.js +0 -0
- /package/dist/{operations → mcp/operations}/bulk-read.js +0 -0
- /package/dist/{operations → mcp/operations}/core-operation-executor.js +0 -0
- /package/dist/{process-platform.js → mcp/process/process-platform.js} +0 -0
- /package/dist/{process-sessions.js → mcp/process/process-sessions.js} +0 -0
- /package/dist/{request-meta.js → mcp/request-meta.js} +0 -0
- /package/dist/{mcp-sessions.js → mcp/server/transport/mcp-sessions.js} +0 -0
- /package/dist/{server-shutdown.js → mcp/server/transport/server-shutdown.js} +0 -0
- /package/dist/{logger.js → runtime/logging/logger.js} +0 -0
- /package/dist/{db → runtime/state/db}/schema.js +0 -0
- /package/dist/{composite-activity.js → workspaces/composite/composite-activity.js} +0 -0
- /package/dist/{composite-workspaces.js → workspaces/composite/composite-workspaces.js} +0 -0
- /package/dist/{git.js → workspaces/git/git.js} +0 -0
- /package/dist/{workspace-presentation.js → workspaces/presentation/workspace-presentation.js} +0 -0
- /package/dist/{remote-transport.js → workspaces/relay/transport/remote-transport.js} +0 -0
- /package/dist/{workspace-task-reminders.js → workspaces/tasks/workspace-task-reminders.js} +0 -0
- /package/dist/{workspace-tasks.js → workspaces/tasks/workspace-tasks.js} +0 -0
- /package/scripts/{lsp-interop-support.mjs → lsp/interop-support.mjs} +0 -0
|
@@ -1,53 +1,94 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { runExternalCommand, } from "../runtime/external-command.js";
|
|
2
|
+
import { removeForgeRelayNodeModulesBinFromPath } from "../path.js";
|
|
3
|
+
import { asRecord, directString, requireFinalResponse } from "../shared.js";
|
|
3
4
|
export class ClaudeSubagentAdapter {
|
|
5
|
+
runner;
|
|
6
|
+
env;
|
|
4
7
|
provider = "claude";
|
|
8
|
+
constructor(runner = runExternalCommand, env = process.env) {
|
|
9
|
+
this.runner = runner;
|
|
10
|
+
this.env = env;
|
|
11
|
+
}
|
|
5
12
|
async run(input) {
|
|
6
|
-
const
|
|
7
|
-
const
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
13
|
+
const command = this.env.CLAUDE_COMMAND?.trim() || "claude";
|
|
14
|
+
const result = await this.runner({
|
|
15
|
+
command,
|
|
16
|
+
args: claudeCommandArgs(input),
|
|
17
|
+
cwd: input.workspace,
|
|
18
|
+
env: claudeCommandEnvironment(this.env),
|
|
19
|
+
signal: input.signal,
|
|
20
|
+
stdin: input.prompt,
|
|
21
|
+
label: "Claude",
|
|
22
|
+
});
|
|
23
|
+
const parsed = parseClaudeJsonOutput(result.stdout);
|
|
24
|
+
if (parsed.error)
|
|
25
|
+
throw new Error(parsed.error);
|
|
26
|
+
return {
|
|
27
|
+
provider: this.provider,
|
|
28
|
+
providerSessionId: parsed.providerSessionId ?? input.providerSessionId ?? null,
|
|
29
|
+
finalResponse: requireFinalResponse("Claude", parsed.finalResponse),
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
export function claudeCommandArgs(input) {
|
|
34
|
+
const args = [
|
|
35
|
+
"--print",
|
|
36
|
+
"--output-format",
|
|
37
|
+
"json",
|
|
38
|
+
"--dangerously-skip-permissions",
|
|
39
|
+
];
|
|
40
|
+
if (input.model)
|
|
41
|
+
args.push("--model", input.model);
|
|
42
|
+
if (input.thinking)
|
|
43
|
+
args.push("--effort", input.thinking);
|
|
44
|
+
if (input.providerSessionId)
|
|
45
|
+
args.push("--resume", input.providerSessionId);
|
|
46
|
+
return args;
|
|
47
|
+
}
|
|
48
|
+
export function parseClaudeJsonOutput(output) {
|
|
49
|
+
const records = parseJsonRecords(output);
|
|
50
|
+
let providerSessionId;
|
|
51
|
+
let finalResponse = "";
|
|
52
|
+
let error;
|
|
53
|
+
for (const record of records) {
|
|
54
|
+
providerSessionId = directString(record.session_id) ?? providerSessionId;
|
|
55
|
+
const resultError = claudeResultError(record);
|
|
56
|
+
if (resultError)
|
|
57
|
+
error = resultError;
|
|
58
|
+
if (typeof record.result === "string")
|
|
59
|
+
finalResponse = record.result;
|
|
60
|
+
}
|
|
61
|
+
return { providerSessionId, finalResponse, error };
|
|
62
|
+
}
|
|
63
|
+
function parseJsonRecords(output) {
|
|
64
|
+
const trimmed = output.trim();
|
|
65
|
+
if (!trimmed)
|
|
66
|
+
return [];
|
|
67
|
+
try {
|
|
68
|
+
const parsed = JSON.parse(trimmed);
|
|
69
|
+
if (Array.isArray(parsed)) {
|
|
70
|
+
return parsed.map(asRecord).filter((record) => Boolean(record));
|
|
43
71
|
}
|
|
44
|
-
|
|
45
|
-
|
|
72
|
+
const record = asRecord(parsed);
|
|
73
|
+
return record ? [record] : [];
|
|
74
|
+
}
|
|
75
|
+
catch {
|
|
76
|
+
const records = [];
|
|
77
|
+
for (const line of trimmed.split(/\r?\n/)) {
|
|
78
|
+
try {
|
|
79
|
+
const record = asRecord(JSON.parse(line));
|
|
80
|
+
if (record)
|
|
81
|
+
records.push(record);
|
|
82
|
+
}
|
|
83
|
+
catch {
|
|
84
|
+
// Ignore non-JSON diagnostic lines; the command runner preserves stderr separately.
|
|
85
|
+
}
|
|
46
86
|
}
|
|
87
|
+
return records;
|
|
47
88
|
}
|
|
48
89
|
}
|
|
49
90
|
function claudeResultError(record) {
|
|
50
|
-
const subtype =
|
|
91
|
+
const subtype = directString(record.subtype);
|
|
51
92
|
const isError = record.is_error === true || subtype?.startsWith("error");
|
|
52
93
|
if (!isError)
|
|
53
94
|
return undefined;
|
|
@@ -58,16 +99,6 @@ function claudeResultError(record) {
|
|
|
58
99
|
"Claude returned an error result.";
|
|
59
100
|
return `Claude returned an error result: ${message}`;
|
|
60
101
|
}
|
|
61
|
-
function resolveExecutable(command) {
|
|
62
|
-
const result = spawnSync(process.platform === "win32" ? "where.exe" : "command", [
|
|
63
|
-
...(process.platform === "win32" ? [command] : ["-v", command]),
|
|
64
|
-
], {
|
|
65
|
-
encoding: "utf8",
|
|
66
|
-
shell: process.platform !== "win32",
|
|
67
|
-
});
|
|
68
|
-
const executable = result.stdout?.split(/\r?\n/).find((line) => line.trim());
|
|
69
|
-
return executable?.trim() || undefined;
|
|
70
|
-
}
|
|
71
102
|
export function claudeCommandEnvironment(env) {
|
|
72
103
|
const next = { ...env };
|
|
73
104
|
for (const key of [
|
|
@@ -78,5 +109,8 @@ export function claudeCommandEnvironment(env) {
|
|
|
78
109
|
]) {
|
|
79
110
|
delete next[key];
|
|
80
111
|
}
|
|
112
|
+
if (!env.CLAUDE_COMMAND && next.PATH) {
|
|
113
|
+
next.PATH = removeForgeRelayNodeModulesBinFromPath(next.PATH);
|
|
114
|
+
}
|
|
81
115
|
return next;
|
|
82
116
|
}
|
|
@@ -1,4 +1,61 @@
|
|
|
1
|
-
|
|
1
|
+
import { runExternalCommand, } from "../runtime/external-command.js";
|
|
2
|
+
import { removeForgeRelayNodeModulesBinFromPath } from "../path.js";
|
|
3
|
+
import { asRecord, directString, requireFinalResponse } from "../shared.js";
|
|
4
|
+
export class CodexSubagentAdapter {
|
|
5
|
+
runner;
|
|
6
|
+
env;
|
|
7
|
+
provider = "codex";
|
|
8
|
+
constructor(runner = runExternalCommand, env = process.env) {
|
|
9
|
+
this.runner = runner;
|
|
10
|
+
this.env = env;
|
|
11
|
+
}
|
|
12
|
+
async run(input) {
|
|
13
|
+
const command = this.env.CODEX_COMMAND?.trim() || "codex";
|
|
14
|
+
const result = await this.runner({
|
|
15
|
+
command,
|
|
16
|
+
args: codexCommandArgs(input),
|
|
17
|
+
cwd: input.workspace,
|
|
18
|
+
env: codexCommandEnvironment(this.env),
|
|
19
|
+
signal: input.signal,
|
|
20
|
+
stdin: input.prompt,
|
|
21
|
+
label: "Codex",
|
|
22
|
+
});
|
|
23
|
+
const parsed = parseCodexJsonLines(result.stdout);
|
|
24
|
+
if (parsed.error)
|
|
25
|
+
throw new Error(`Codex returned an error: ${parsed.error}`);
|
|
26
|
+
if (input.providerSessionId &&
|
|
27
|
+
parsed.providerSessionId &&
|
|
28
|
+
parsed.providerSessionId !== input.providerSessionId) {
|
|
29
|
+
throw new Error(`Codex resume returned a different session id (${parsed.providerSessionId}) than requested (${input.providerSessionId}).`);
|
|
30
|
+
}
|
|
31
|
+
return {
|
|
32
|
+
provider: this.provider,
|
|
33
|
+
providerSessionId: parsed.providerSessionId ?? input.providerSessionId ?? null,
|
|
34
|
+
finalResponse: requireFinalResponse("Codex", parsed.finalResponse),
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
export function codexCommandArgs(input) {
|
|
39
|
+
const args = [
|
|
40
|
+
"exec",
|
|
41
|
+
"--json",
|
|
42
|
+
"--sandbox",
|
|
43
|
+
codexSandboxMode(input.writeMode),
|
|
44
|
+
"--cd",
|
|
45
|
+
input.workspace,
|
|
46
|
+
"--config",
|
|
47
|
+
'approval_policy="never"',
|
|
48
|
+
];
|
|
49
|
+
if (input.model)
|
|
50
|
+
args.push("--model", input.model);
|
|
51
|
+
if (input.thinking) {
|
|
52
|
+
args.push("--config", `model_reasoning_effort=${JSON.stringify(input.thinking)}`);
|
|
53
|
+
}
|
|
54
|
+
if (input.providerSessionId)
|
|
55
|
+
args.push("resume", input.providerSessionId);
|
|
56
|
+
return args;
|
|
57
|
+
}
|
|
58
|
+
function codexSandboxMode(writeMode) {
|
|
2
59
|
switch (writeMode) {
|
|
3
60
|
case "allowed":
|
|
4
61
|
return "workspace-write";
|
|
@@ -9,46 +66,53 @@ function sandboxModeFor(writeMode) {
|
|
|
9
66
|
return "read-only";
|
|
10
67
|
}
|
|
11
68
|
}
|
|
12
|
-
function
|
|
69
|
+
export function codexCommandEnvironment(env) {
|
|
70
|
+
if (env.CODEX_COMMAND || !env.PATH)
|
|
71
|
+
return { ...env };
|
|
13
72
|
return {
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
approvalPolicy: "never",
|
|
17
|
-
model: input.model,
|
|
18
|
-
modelReasoningEffort: input.thinking,
|
|
73
|
+
...env,
|
|
74
|
+
PATH: removeForgeRelayNodeModulesBinFromPath(env.PATH),
|
|
19
75
|
};
|
|
20
76
|
}
|
|
21
|
-
export
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
77
|
+
export function parseCodexJsonLines(output) {
|
|
78
|
+
let providerSessionId;
|
|
79
|
+
let finalResponse = "";
|
|
80
|
+
let error;
|
|
81
|
+
for (const line of output.split(/\r?\n/)) {
|
|
82
|
+
const trimmed = line.trim();
|
|
83
|
+
if (!trimmed)
|
|
84
|
+
continue;
|
|
85
|
+
let event;
|
|
86
|
+
try {
|
|
87
|
+
const parsed = JSON.parse(trimmed);
|
|
88
|
+
const record = asRecord(parsed);
|
|
89
|
+
if (!record)
|
|
90
|
+
continue;
|
|
91
|
+
event = record;
|
|
92
|
+
}
|
|
93
|
+
catch {
|
|
94
|
+
continue;
|
|
95
|
+
}
|
|
96
|
+
if (event.type === "thread.started") {
|
|
97
|
+
providerSessionId = directString(event.thread_id) ?? providerSessionId;
|
|
98
|
+
continue;
|
|
99
|
+
}
|
|
100
|
+
if (event.type === "item.completed") {
|
|
101
|
+
const item = asRecord(event.item);
|
|
102
|
+
if (item?.type === "agent_message") {
|
|
103
|
+
finalResponse = directString(item.text) ?? finalResponse;
|
|
104
|
+
}
|
|
105
|
+
continue;
|
|
106
|
+
}
|
|
107
|
+
if (event.type === "turn.failed") {
|
|
108
|
+
const failure = asRecord(event.error);
|
|
109
|
+
error = directString(failure?.message) ?? directString(event.message) ?? error;
|
|
110
|
+
continue;
|
|
111
|
+
}
|
|
112
|
+
if (event.type === "error") {
|
|
113
|
+
const nested = asRecord(event.error);
|
|
114
|
+
error = directString(event.message) ?? directString(nested?.message) ?? error;
|
|
115
|
+
}
|
|
49
116
|
}
|
|
50
|
-
}
|
|
51
|
-
async function defaultCodexFactory() {
|
|
52
|
-
const module = await import("@openai/codex-sdk");
|
|
53
|
-
return (options) => new module.Codex(options);
|
|
117
|
+
return { providerSessionId, finalResponse, error };
|
|
54
118
|
}
|
|
@@ -1,102 +1,132 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { runExternalCommand, } from "../runtime/external-command.js";
|
|
2
|
+
import { removeForgeRelayNodeModulesBinFromPath } from "../path.js";
|
|
3
|
+
import { asRecord, directString, readArray, requireFinalResponse, unwrapProviderPayload, } from "../shared.js";
|
|
2
4
|
export class OpencodeSubagentAdapter {
|
|
5
|
+
runner;
|
|
6
|
+
env;
|
|
3
7
|
provider = "opencode";
|
|
8
|
+
constructor(runner = runExternalCommand, env = process.env) {
|
|
9
|
+
this.runner = runner;
|
|
10
|
+
this.env = env;
|
|
11
|
+
}
|
|
4
12
|
async run(input) {
|
|
5
|
-
const
|
|
6
|
-
const
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
finally {
|
|
30
|
-
input.signal?.removeEventListener("abort", abort);
|
|
31
|
-
if (abortPromise)
|
|
32
|
-
await abortPromise;
|
|
33
|
-
}
|
|
34
|
-
}
|
|
35
|
-
finally {
|
|
36
|
-
server.close();
|
|
13
|
+
const command = this.env.OPENCODE_COMMAND?.trim() || "opencode";
|
|
14
|
+
const commandEnv = opencodeCommandEnvironment(this.env);
|
|
15
|
+
const result = await this.runner({
|
|
16
|
+
command,
|
|
17
|
+
args: opencodeCommandArgs(input),
|
|
18
|
+
cwd: input.workspace,
|
|
19
|
+
env: commandEnv,
|
|
20
|
+
signal: input.signal,
|
|
21
|
+
stdin: input.prompt,
|
|
22
|
+
label: "OpenCode",
|
|
23
|
+
});
|
|
24
|
+
const streamed = parseOpenCodeJsonLines(result.stdout);
|
|
25
|
+
const providerSessionId = streamed.providerSessionId ?? input.providerSessionId ?? null;
|
|
26
|
+
let finalResponse = streamed.finalResponse;
|
|
27
|
+
if (!finalResponse && providerSessionId) {
|
|
28
|
+
const exported = await this.runner({
|
|
29
|
+
command,
|
|
30
|
+
args: ["export", providerSessionId],
|
|
31
|
+
cwd: input.workspace,
|
|
32
|
+
env: commandEnv,
|
|
33
|
+
signal: input.signal,
|
|
34
|
+
label: "OpenCode export",
|
|
35
|
+
});
|
|
36
|
+
finalResponse = extractOpenCodeExportFinalResponse(exported.stdout);
|
|
37
37
|
}
|
|
38
|
+
return {
|
|
39
|
+
provider: this.provider,
|
|
40
|
+
providerSessionId,
|
|
41
|
+
finalResponse: requireFinalResponse("OpenCode", finalResponse),
|
|
42
|
+
};
|
|
38
43
|
}
|
|
39
44
|
}
|
|
40
|
-
|
|
41
|
-
const
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
if (
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
45
|
+
export function opencodeCommandArgs(input) {
|
|
46
|
+
const args = [
|
|
47
|
+
"run",
|
|
48
|
+
"--format",
|
|
49
|
+
"json",
|
|
50
|
+
"--dir",
|
|
51
|
+
input.workspace,
|
|
52
|
+
"--dangerously-skip-permissions",
|
|
53
|
+
];
|
|
54
|
+
if (input.model)
|
|
55
|
+
args.push("--model", input.model);
|
|
56
|
+
if (input.thinking)
|
|
57
|
+
args.push("--variant", input.thinking);
|
|
58
|
+
if (input.providerSessionId)
|
|
59
|
+
args.push("--session", input.providerSessionId);
|
|
60
|
+
return args;
|
|
55
61
|
}
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
parts: [{ type: "text", text: input.prompt }],
|
|
63
|
-
...(input.model ? { model: parseOpencodeModel(input.model) } : {}),
|
|
64
|
-
...(input.thinking ? { variant: input.thinking } : {}),
|
|
62
|
+
export function opencodeCommandEnvironment(env) {
|
|
63
|
+
if (env.OPENCODE_COMMAND || !env.PATH)
|
|
64
|
+
return { ...env };
|
|
65
|
+
return {
|
|
66
|
+
...env,
|
|
67
|
+
PATH: removeForgeRelayNodeModulesBinFromPath(env.PATH),
|
|
65
68
|
};
|
|
66
|
-
return session.prompt(promptInput, { throwOnError: true, signal: input.signal });
|
|
67
|
-
}
|
|
68
|
-
async function waitForOpencodeSession(client, sessionId, signal) {
|
|
69
|
-
const session = client.session;
|
|
70
|
-
if (!session?.wait)
|
|
71
|
-
return;
|
|
72
|
-
await session.wait({ sessionID: sessionId }, { throwOnError: true, signal });
|
|
73
69
|
}
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
70
|
+
export function parseOpenCodeJsonLines(output) {
|
|
71
|
+
let providerSessionId;
|
|
72
|
+
const textByMessage = new Map();
|
|
73
|
+
const messageOrder = [];
|
|
74
|
+
let fallback = "";
|
|
75
|
+
let anonymousIndex = 0;
|
|
76
|
+
for (const line of output.split(/\r?\n/)) {
|
|
77
|
+
const trimmed = line.trim();
|
|
78
|
+
if (!trimmed)
|
|
79
|
+
continue;
|
|
80
|
+
let record;
|
|
81
|
+
try {
|
|
82
|
+
const parsed = asRecord(JSON.parse(trimmed));
|
|
83
|
+
if (!parsed)
|
|
84
|
+
continue;
|
|
85
|
+
record = parsed;
|
|
86
|
+
}
|
|
87
|
+
catch {
|
|
88
|
+
continue;
|
|
89
|
+
}
|
|
90
|
+
providerSessionId =
|
|
91
|
+
directString(record.sessionID) ??
|
|
92
|
+
directString(record.sessionId) ??
|
|
93
|
+
providerSessionId;
|
|
94
|
+
if (record.type === "text") {
|
|
95
|
+
const part = asRecord(record.part);
|
|
96
|
+
const text = typeof part?.text === "string" ? part.text : undefined;
|
|
97
|
+
if (!text)
|
|
98
|
+
continue;
|
|
99
|
+
const messageId = directString(part?.messageID) ??
|
|
100
|
+
directString(part?.messageId) ??
|
|
101
|
+
`anonymous-${anonymousIndex++}`;
|
|
102
|
+
if (!textByMessage.has(messageId)) {
|
|
103
|
+
textByMessage.set(messageId, []);
|
|
104
|
+
messageOrder.push(messageId);
|
|
105
|
+
}
|
|
106
|
+
textByMessage.get(messageId).push(text);
|
|
107
|
+
continue;
|
|
108
|
+
}
|
|
109
|
+
const extracted = extractOpenCodeFinalResponse(record);
|
|
110
|
+
if (extracted)
|
|
111
|
+
fallback = extracted;
|
|
112
|
+
}
|
|
113
|
+
const lastMessageId = messageOrder.at(-1);
|
|
114
|
+
const finalResponse = lastMessageId
|
|
115
|
+
? (textByMessage.get(lastMessageId) ?? []).join("").trim()
|
|
116
|
+
: fallback;
|
|
117
|
+
return { providerSessionId, finalResponse };
|
|
79
118
|
}
|
|
80
|
-
|
|
81
|
-
const
|
|
82
|
-
if (!
|
|
83
|
-
return;
|
|
119
|
+
function extractOpenCodeExportFinalResponse(output) {
|
|
120
|
+
const trimmed = output.trim();
|
|
121
|
+
if (!trimmed)
|
|
122
|
+
return "";
|
|
84
123
|
try {
|
|
85
|
-
|
|
124
|
+
return extractOpenCodeFinalResponse(JSON.parse(trimmed));
|
|
86
125
|
}
|
|
87
126
|
catch {
|
|
88
|
-
|
|
127
|
+
return "";
|
|
89
128
|
}
|
|
90
129
|
}
|
|
91
|
-
function parseOpencodeModel(model) {
|
|
92
|
-
const separator = model.indexOf("/");
|
|
93
|
-
if (separator === -1)
|
|
94
|
-
return { providerID: "opencode", modelID: model };
|
|
95
|
-
return {
|
|
96
|
-
providerID: model.slice(0, separator),
|
|
97
|
-
modelID: model.slice(separator + 1),
|
|
98
|
-
};
|
|
99
|
-
}
|
|
100
130
|
export function extractOpenCodeFinalResponse(value) {
|
|
101
131
|
const root = unwrapProviderPayload(value);
|
|
102
132
|
const messages = Array.isArray(root) ? root : readArray(root, "messages");
|