@akira-tl/forgerelay 0.8.8 → 0.8.10
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 +32 -0
- package/README.md +10 -18
- package/capabilities/code-intelligence/GUIDE.md +7 -3
- package/capabilities/host-integration/GUIDE.md +2 -2
- package/capabilities/subagents/GUIDE.md +9 -0
- package/capabilities/{workspace-tasks → workspace/workspace-tasks}/GUIDE.md +6 -0
- 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/init.js +180 -0
- package/dist/cli/setup-support.js +181 -0
- package/dist/cli.js +23 -216
- package/dist/lsp/code-intelligence.js +18 -4
- package/dist/lsp/runtime/diagnostic-snapshots.js +52 -1
- package/dist/lsp/runtime/managed-language-servers.js +172 -0
- package/dist/lsp/runtime/manager.js +37 -2
- package/dist/lsp/runtime/process-launch.js +3 -0
- package/dist/lsp/test-support/server-fixture.js +14 -11
- 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/{process-platform.js → mcp/process/process-platform.js} +21 -9
- package/dist/{process-sessions.js → mcp/process/process-sessions.js} +3 -3
- 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} +9 -4
- 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 +210 -0
- package/dist/mcp/server/core/version.js +9 -0
- package/dist/mcp/server/operations/runtime/filesystem-tools.js +455 -0
- package/dist/mcp/server/operations/runtime/mutation-diagnostics.js +54 -0
- package/dist/mcp/server/operations/runtime/operation-runtime.js +524 -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 +242 -0
- package/dist/mcp/server/workspace/runtime/workspace-open-schema.js +187 -0
- package/dist/mcp/server/workspace/runtime/workspace-open.js +555 -0
- package/dist/mcp/server/workspace/runtime/workspace-tools.js +601 -0
- package/dist/mcp/server-instructions.js +1 -1
- package/dist/{config.js → runtime/config/config.js} +4 -3
- package/dist/{user-config.js → runtime/config/user-config.js} +14 -63
- 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 +133 -4189
- 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 +337 -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/{remote-transport.js → workspaces/relay/transport/remote-transport.js} +30 -3
- 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/resource-monitor.js +377 -0
- package/dist/workspaces/resources/skills.js +283 -0
- package/dist/{review-checkpoints.js → workspaces/review/review-checkpoints.js} +2 -2
- package/dist/workspaces/sessions.js +460 -0
- package/dist/{workspace-store.js → workspaces/state/workspace-store.js} +2 -2
- package/dist/workspaces.js +77 -993
- package/docs/chatgpt-coding-workflow.md +7 -14
- package/docs/configuration.md +26 -34
- 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 -9
- 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/release/publish.mjs +12 -9
- package/scripts/release/release-gate.test.mjs +3 -1
- package/scripts/release/release-version.test.mjs +13 -1
- package/scripts/release-parity.mjs +1 -1
- package/scripts/release-proof.mjs +15 -0
- package/scripts/release-proof.test.mjs +27 -1
- package/scripts/release-version.mjs +16 -1
- 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/skills/subagent-delegation/SKILL.md +0 -132
- /package/capabilities/{managed-worktrees → workspace/managed-worktrees}/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/{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/{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
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
import { access } from "node:fs/promises";
|
|
2
|
+
import { fileURLToPath } from "node:url";
|
|
3
|
+
import { RESOURCE_MIME_TYPE } from "@modelcontextprotocol/ext-apps/server";
|
|
4
|
+
import { logEvent, transportSessionIdPrefix } from "../../runtime/logging/logger.js";
|
|
5
|
+
import { MCP_APP_RESOURCE_TEMPLATE_REVISION, readActivityPanelAppManifestEntry, resolveActivityPanelAppIdentity, } from "./mcp-app-template.js";
|
|
6
|
+
const cachedIdentities = new Map();
|
|
7
|
+
/**
|
|
8
|
+
* Build the complete Host-facing Activity Panel resource contract. Keeping URI
|
|
9
|
+
* identity, CSP, bootstrap HTML, and tool metadata in one module prevents a UI
|
|
10
|
+
* revision from changing on one surface while another surface keeps stale Host
|
|
11
|
+
* cache metadata.
|
|
12
|
+
*/
|
|
13
|
+
export function createActivityPanelApp(config, fallbackRevision) {
|
|
14
|
+
const identity = currentIdentity(config, fallbackRevision);
|
|
15
|
+
const domain = new URL(config.publicBaseUrl).origin;
|
|
16
|
+
const csp = activityPanelCsp(config);
|
|
17
|
+
return {
|
|
18
|
+
uri: identity.uri,
|
|
19
|
+
resourceMetadata: {
|
|
20
|
+
description: "ForgeRelay unified Workspace and Activity UI for one Host Turn.",
|
|
21
|
+
_meta: { ui: { domain, csp } },
|
|
22
|
+
},
|
|
23
|
+
toolMeta: config.widgets === "off"
|
|
24
|
+
? { _meta: {} }
|
|
25
|
+
: {
|
|
26
|
+
_meta: {
|
|
27
|
+
ui: {
|
|
28
|
+
resourceUri: identity.uri,
|
|
29
|
+
visibility: ["model", "app"],
|
|
30
|
+
},
|
|
31
|
+
},
|
|
32
|
+
},
|
|
33
|
+
readResource: (requestedUri, transportSessionId) => readActivityPanelAppResource(config, fallbackRevision, requestedUri, transportSessionId),
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
export function activityPanelAssetDirectory() {
|
|
37
|
+
return fileURLToPath(new URL("../../../dist/ui", import.meta.url));
|
|
38
|
+
}
|
|
39
|
+
export function setActivityPanelAssetHeaders(res) {
|
|
40
|
+
res.setHeader("Access-Control-Allow-Origin", "*");
|
|
41
|
+
res.setHeader("Access-Control-Allow-Methods", "GET, HEAD, OPTIONS");
|
|
42
|
+
res.setHeader("Access-Control-Allow-Headers", "Content-Type, Range");
|
|
43
|
+
res.setHeader("Cross-Origin-Resource-Policy", "cross-origin");
|
|
44
|
+
}
|
|
45
|
+
function appResourceContractRevision(config) {
|
|
46
|
+
return [
|
|
47
|
+
MCP_APP_RESOURCE_TEMPLATE_REVISION,
|
|
48
|
+
`publicBaseUrls=${JSON.stringify(config.publicBaseUrls)}`,
|
|
49
|
+
].join("\0");
|
|
50
|
+
}
|
|
51
|
+
function currentIdentity(config, fallbackRevision) {
|
|
52
|
+
const contractRevision = appResourceContractRevision(config);
|
|
53
|
+
const cacheKey = `${fallbackRevision}\0${contractRevision}`;
|
|
54
|
+
let identity = cachedIdentities.get(cacheKey);
|
|
55
|
+
if (!identity) {
|
|
56
|
+
identity = resolveActivityPanelAppIdentity({
|
|
57
|
+
manifestUrl: uiManifestUrl(),
|
|
58
|
+
buildDirectoryUrl: uiBuildDirectoryUrl(),
|
|
59
|
+
fallbackRevision,
|
|
60
|
+
resourceTemplateRevision: contractRevision,
|
|
61
|
+
});
|
|
62
|
+
cachedIdentities.set(cacheKey, identity);
|
|
63
|
+
}
|
|
64
|
+
return identity;
|
|
65
|
+
}
|
|
66
|
+
function activityPanelCsp(config) {
|
|
67
|
+
return {
|
|
68
|
+
resourceDomains: [...config.publicBaseUrls],
|
|
69
|
+
connectDomains: [...config.publicBaseUrls],
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
async function readActivityPanelAppResource(config, fallbackRevision, requestedUri, transportSessionId) {
|
|
73
|
+
const identity = currentIdentity(config, fallbackRevision);
|
|
74
|
+
const entry = readActivityPanelAppManifestEntry(uiManifestUrl());
|
|
75
|
+
const domain = new URL(config.publicBaseUrl).origin;
|
|
76
|
+
const csp = activityPanelCsp(config);
|
|
77
|
+
try {
|
|
78
|
+
await assertMcpAppAssets(entry);
|
|
79
|
+
const result = {
|
|
80
|
+
contents: [{
|
|
81
|
+
uri: requestedUri,
|
|
82
|
+
mimeType: RESOURCE_MIME_TYPE,
|
|
83
|
+
text: activityPanelAppHtml(config, entry),
|
|
84
|
+
_meta: {
|
|
85
|
+
ui: { domain, csp },
|
|
86
|
+
// MCP Apps defines these values under `_meta.ui`. Inspector 2.3.0
|
|
87
|
+
// still reads them directly from the content-item metadata, so mirror
|
|
88
|
+
// them until that installed-host compatibility gap is retired.
|
|
89
|
+
domain,
|
|
90
|
+
csp,
|
|
91
|
+
},
|
|
92
|
+
}],
|
|
93
|
+
};
|
|
94
|
+
logEvent(config.logging, "debug", "mcp_app_template_read", {
|
|
95
|
+
requestedUri,
|
|
96
|
+
currentUri: identity.uri,
|
|
97
|
+
transportSessionIdPrefix: transportSessionIdPrefix(transportSessionId),
|
|
98
|
+
});
|
|
99
|
+
return result;
|
|
100
|
+
}
|
|
101
|
+
catch (error) {
|
|
102
|
+
logEvent(config.logging, "warn", "mcp_app_template_read_failed", {
|
|
103
|
+
requestedUri,
|
|
104
|
+
currentUri: identity.uri,
|
|
105
|
+
error: error instanceof Error ? error.message : String(error),
|
|
106
|
+
transportSessionIdPrefix: transportSessionIdPrefix(transportSessionId),
|
|
107
|
+
});
|
|
108
|
+
throw error;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
function activityPanelAppHtml(config, entry) {
|
|
112
|
+
const baseUrl = `${config.publicBaseUrl.replace(/\/+$/, "")}/mcp-app-assets`;
|
|
113
|
+
const assetUrl = (assetPath) => `${baseUrl}/${assetPath.replace(/^\/+/, "")}`;
|
|
114
|
+
const stylesheets = (entry.css ?? [])
|
|
115
|
+
.map((stylesheet) => ` <link rel="stylesheet" crossorigin href="${assetUrl(stylesheet)}" />`)
|
|
116
|
+
.join("\n");
|
|
117
|
+
return `<!doctype html>
|
|
118
|
+
<html lang="en">
|
|
119
|
+
<head>
|
|
120
|
+
<meta charset="UTF-8" />
|
|
121
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
122
|
+
<title>ForgeRelay Activity Panel</title>
|
|
123
|
+
<script type="module" crossorigin src="${assetUrl(entry.file)}"></script>
|
|
124
|
+
${stylesheets}
|
|
125
|
+
</head>
|
|
126
|
+
<body>
|
|
127
|
+
<main id="app" class="shell">
|
|
128
|
+
<section class="empty">Waiting for Activity Panel state.</section>
|
|
129
|
+
</main>
|
|
130
|
+
</body>
|
|
131
|
+
</html>`;
|
|
132
|
+
}
|
|
133
|
+
async function assertMcpAppAssets(entry) {
|
|
134
|
+
const candidates = [entry.file, ...(entry.css ?? [])].map((assetPath) => new URL(`../../../dist/ui/${assetPath}`, import.meta.url));
|
|
135
|
+
for (const candidate of candidates)
|
|
136
|
+
await access(candidate);
|
|
137
|
+
}
|
|
138
|
+
function uiManifestUrl() {
|
|
139
|
+
return new URL("../../../dist/ui/.vite/manifest.json", import.meta.url);
|
|
140
|
+
}
|
|
141
|
+
function uiBuildDirectoryUrl() {
|
|
142
|
+
return new URL("../../../dist/ui/", import.meta.url);
|
|
143
|
+
}
|
|
@@ -1,26 +1,10 @@
|
|
|
1
1
|
import { createHash } from "node:crypto";
|
|
2
2
|
import { readFileSync } from "node:fs";
|
|
3
|
-
export const WORKSPACE_APP_MANIFEST_ENTRY = "workspace-app.html";
|
|
4
|
-
export const WORKSPACE_LIFECYCLE_APP_MANIFEST_ENTRY = "workspace-lifecycle-app.html";
|
|
5
3
|
export const ACTIVITY_PANEL_APP_MANIFEST_ENTRY = "activity-panel-app.html";
|
|
6
4
|
// Host caches are keyed by the ui:// resource URI, so the revision must cover
|
|
7
5
|
// both bundle assets and the HTML/resource contract that serves them. Bump this
|
|
8
|
-
//
|
|
9
|
-
export const MCP_APP_RESOURCE_TEMPLATE_REVISION = "
|
|
10
|
-
// Historical mixed-widget resource. Keep serving it so existing ChatGPT Web
|
|
11
|
-
// conversations can still render cards created before the UI split.
|
|
12
|
-
export const WORKSPACE_APP_LEGACY_URI = "ui://forgerelay/workspace-app.html";
|
|
13
|
-
export const WORKSPACE_APP_URI_TEMPLATE = "ui://forgerelay/workspace-app-{revision}.html";
|
|
14
|
-
export const WORKSPACE_LIFECYCLE_APP_LEGACY_URI = "ui://forgerelay/workspace-lifecycle-app.html";
|
|
15
|
-
export const WORKSPACE_LIFECYCLE_APP_URI_TEMPLATE = "ui://forgerelay/workspace-lifecycle-app-{revision}.html";
|
|
16
|
-
export const ACTIVITY_PANEL_APP_LEGACY_URI = "ui://forgerelay/activity-panel-app.html";
|
|
17
|
-
export const ACTIVITY_PANEL_APP_URI_TEMPLATE = "ui://forgerelay/activity-panel-app-{revision}.html";
|
|
18
|
-
export function workspaceAppUriForRevision(revision) {
|
|
19
|
-
return `ui://forgerelay/workspace-app-${encodeURIComponent(revision)}.html`;
|
|
20
|
-
}
|
|
21
|
-
export function workspaceLifecycleAppUriForRevision(revision) {
|
|
22
|
-
return `ui://forgerelay/workspace-lifecycle-app-${encodeURIComponent(revision)}.html`;
|
|
23
|
-
}
|
|
6
|
+
// whenever either side changes so a Host never reuses stale UI bytes.
|
|
7
|
+
export const MCP_APP_RESOURCE_TEMPLATE_REVISION = "5";
|
|
24
8
|
export function activityPanelAppUriForRevision(revision) {
|
|
25
9
|
return `ui://forgerelay/activity-panel-app-${encodeURIComponent(revision)}.html`;
|
|
26
10
|
}
|
|
@@ -64,12 +48,6 @@ export function readMcpAppManifestEntry(manifestUrl, manifestEntry) {
|
|
|
64
48
|
...(entry.isEntry !== undefined ? { isEntry: entry.isEntry } : {}),
|
|
65
49
|
};
|
|
66
50
|
}
|
|
67
|
-
export function readWorkspaceAppManifestEntry(manifestUrl) {
|
|
68
|
-
return readMcpAppManifestEntry(manifestUrl, WORKSPACE_APP_MANIFEST_ENTRY);
|
|
69
|
-
}
|
|
70
|
-
export function readWorkspaceLifecycleAppManifestEntry(manifestUrl) {
|
|
71
|
-
return readMcpAppManifestEntry(manifestUrl, WORKSPACE_LIFECYCLE_APP_MANIFEST_ENTRY);
|
|
72
|
-
}
|
|
73
51
|
export function readActivityPanelAppManifestEntry(manifestUrl) {
|
|
74
52
|
return readMcpAppManifestEntry(manifestUrl, ACTIVITY_PANEL_APP_MANIFEST_ENTRY);
|
|
75
53
|
}
|
|
@@ -118,20 +96,6 @@ function resolveAppIdentity(options) {
|
|
|
118
96
|
};
|
|
119
97
|
}
|
|
120
98
|
}
|
|
121
|
-
export function resolveWorkspaceAppIdentity(options) {
|
|
122
|
-
return resolveAppIdentity({
|
|
123
|
-
...options,
|
|
124
|
-
manifestEntry: WORKSPACE_APP_MANIFEST_ENTRY,
|
|
125
|
-
uriForRevision: workspaceAppUriForRevision,
|
|
126
|
-
});
|
|
127
|
-
}
|
|
128
|
-
export function resolveWorkspaceLifecycleAppIdentity(options) {
|
|
129
|
-
return resolveAppIdentity({
|
|
130
|
-
...options,
|
|
131
|
-
manifestEntry: WORKSPACE_LIFECYCLE_APP_MANIFEST_ENTRY,
|
|
132
|
-
uriForRevision: workspaceLifecycleAppUriForRevision,
|
|
133
|
-
});
|
|
134
|
-
}
|
|
135
99
|
export function resolveActivityPanelAppIdentity(options) {
|
|
136
100
|
return resolveAppIdentity({
|
|
137
101
|
...options,
|
|
@@ -11,23 +11,35 @@ const defaultProcessTreeRuntime = {
|
|
|
11
11
|
return !result.error && result.status === 0;
|
|
12
12
|
},
|
|
13
13
|
};
|
|
14
|
-
const
|
|
15
|
-
const POSIX_SHELLS = new Set(["ash", "dash", "sh"]);
|
|
14
|
+
const POSIX_SHELLS = new Set(["ash", "dash", "ksh", "sh"]);
|
|
16
15
|
export function resolveShellCommand(command, platform = process.platform, environment = process.env) {
|
|
17
16
|
if (platform === "win32") {
|
|
18
17
|
return {
|
|
19
18
|
executable: environment.ComSpec ?? environment.COMSPEC ?? "cmd.exe",
|
|
20
|
-
|
|
19
|
+
// Match Node's native `spawn(command, { shell: cmd.exe })` quoting.
|
|
20
|
+
// cmd.exe /S applies special quote stripping, so the whole command must
|
|
21
|
+
// be wrapped even when the executable inside it is already quoted.
|
|
22
|
+
args: ["/d", "/s", "/c", `"${command}"`],
|
|
21
23
|
windowsVerbatimArguments: true,
|
|
22
24
|
};
|
|
23
25
|
}
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
26
|
+
// Agent and Hook commands must not source the user's interactive/login shell
|
|
27
|
+
// configuration. ForgeRelay already inherits PATH and other environment from
|
|
28
|
+
// the process that launched the server; re-running zsh/bash as a login shell
|
|
29
|
+
// can inject prompts, banners, aliases, plugins, or other user-only behavior.
|
|
30
|
+
const configuredShell = environment.FORGERELAY_COMMAND_SHELL?.trim();
|
|
31
|
+
const executable = configuredShell || (platform === "linux" || platform === "darwin"
|
|
32
|
+
? "/bin/bash"
|
|
33
|
+
: "/bin/sh");
|
|
34
|
+
const shellName = basename(executable);
|
|
35
|
+
if (shellName === "bash") {
|
|
36
|
+
return { executable, args: ["--noprofile", "--norc", "-c", command] };
|
|
28
37
|
}
|
|
29
|
-
if (
|
|
30
|
-
return { executable
|
|
38
|
+
if (shellName === "zsh") {
|
|
39
|
+
return { executable, args: ["-f", "-c", command] };
|
|
40
|
+
}
|
|
41
|
+
if (POSIX_SHELLS.has(shellName)) {
|
|
42
|
+
return { executable, args: ["-c", command] };
|
|
31
43
|
}
|
|
32
44
|
return { executable: "/bin/sh", args: ["-c", command] };
|
|
33
45
|
}
|
|
@@ -2,7 +2,7 @@ import { spawn } from "node:child_process";
|
|
|
2
2
|
import { resolveShellCommand, terminateProcessTree } from "./process-platform.js";
|
|
3
3
|
const DEFAULT_EXEC_YIELD_MS = 10_000;
|
|
4
4
|
const DEFAULT_INTERACTIVE_YIELD_MS = 250;
|
|
5
|
-
const DEFAULT_POLL_YIELD_MS =
|
|
5
|
+
export const DEFAULT_POLL_YIELD_MS = 60_000;
|
|
6
6
|
const MAX_START_YIELD_MS = 300_000;
|
|
7
7
|
const MAX_COMMAND_YIELD_MS = 300_000;
|
|
8
8
|
const MAX_POLL_YIELD_MS = 300_000;
|
|
@@ -437,7 +437,7 @@ export class ProcessManager {
|
|
|
437
437
|
startPipe(processEntry, input) {
|
|
438
438
|
const shell = resolveShellCommand(input.command);
|
|
439
439
|
const detached = process.platform !== "win32";
|
|
440
|
-
const child = spawn(
|
|
440
|
+
const child = spawn(shell.executable, shell.args, {
|
|
441
441
|
cwd: input.cwd,
|
|
442
442
|
env: processEnvironment({
|
|
443
443
|
workspaceId: input.workspaceId,
|
|
@@ -446,8 +446,8 @@ export class ProcessManager {
|
|
|
446
446
|
}),
|
|
447
447
|
stdio: "pipe",
|
|
448
448
|
windowsHide: true,
|
|
449
|
+
windowsVerbatimArguments: shell.windowsVerbatimArguments,
|
|
449
450
|
detached,
|
|
450
|
-
shell: shell.executable,
|
|
451
451
|
});
|
|
452
452
|
processEntry.process = {
|
|
453
453
|
write: (data) => child.stdin.write(data),
|
|
@@ -0,0 +1,257 @@
|
|
|
1
|
+
const PROCESS_RESPONSE_OUTPUT_LINES = 10;
|
|
2
|
+
export function attachCompletedProcessNotices(processSessions, workspaceId, result, onCompleted) {
|
|
3
|
+
if (result instanceof Error) {
|
|
4
|
+
const completed = processSessions.takeCompleted(workspaceId);
|
|
5
|
+
for (const snapshot of completed)
|
|
6
|
+
onCompleted?.(snapshot);
|
|
7
|
+
if (completed.length > 0) {
|
|
8
|
+
result.message = [
|
|
9
|
+
result.message,
|
|
10
|
+
...completed.map((snapshot) => completedProcessResult(snapshot)),
|
|
11
|
+
].join("\n\n");
|
|
12
|
+
}
|
|
13
|
+
return result;
|
|
14
|
+
}
|
|
15
|
+
if (typeof result !== "object" || result === null)
|
|
16
|
+
return result;
|
|
17
|
+
const content = result.content;
|
|
18
|
+
if (!Array.isArray(content))
|
|
19
|
+
return result;
|
|
20
|
+
const structured = result.structuredContent;
|
|
21
|
+
const currentProcessId = structured?.running === true
|
|
22
|
+
? typeof structured.processId === "number"
|
|
23
|
+
? structured.processId
|
|
24
|
+
: typeof structured.sessionId === "number"
|
|
25
|
+
? structured.sessionId
|
|
26
|
+
: undefined
|
|
27
|
+
: undefined;
|
|
28
|
+
const completed = processSessions.takeCompleted(workspaceId, undefined, currentProcessId);
|
|
29
|
+
for (const snapshot of completed)
|
|
30
|
+
onCompleted?.(snapshot);
|
|
31
|
+
if (completed.length === 0)
|
|
32
|
+
return result;
|
|
33
|
+
return {
|
|
34
|
+
...result,
|
|
35
|
+
content: [
|
|
36
|
+
...content,
|
|
37
|
+
...completed.map((snapshot) => textBlock(completedProcessResult(snapshot))),
|
|
38
|
+
],
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
export function processToolResponse(tool, workspaceId, snapshot, summary) {
|
|
42
|
+
const compact = compactProcessOutput(snapshot.output);
|
|
43
|
+
const result = processResult(snapshot);
|
|
44
|
+
const content = [textBlock(result)];
|
|
45
|
+
const outputSummary = textSummary(compact.output ? [textBlock(compact.output)] : []);
|
|
46
|
+
return {
|
|
47
|
+
content,
|
|
48
|
+
_meta: {
|
|
49
|
+
tool,
|
|
50
|
+
card: {
|
|
51
|
+
workspaceId,
|
|
52
|
+
summary: { ...summary, ...outputSummary },
|
|
53
|
+
payload: { content },
|
|
54
|
+
},
|
|
55
|
+
},
|
|
56
|
+
structuredContent: {
|
|
57
|
+
result,
|
|
58
|
+
processId: snapshot.processId,
|
|
59
|
+
sessionId: snapshot.sessionId,
|
|
60
|
+
outputId: snapshot.outputId,
|
|
61
|
+
running: snapshot.running,
|
|
62
|
+
exitCode: snapshot.exitCode,
|
|
63
|
+
signal: snapshot.signal,
|
|
64
|
+
timedOut: snapshot.timedOut,
|
|
65
|
+
wallTimeMs: snapshot.wallTimeMs,
|
|
66
|
+
outputTruncated: snapshot.outputTruncated || compact.truncated,
|
|
67
|
+
},
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
export function durableOutputResponse(tool, workspaceId, record) {
|
|
71
|
+
const result = durableOutputResult(record);
|
|
72
|
+
const content = [textBlock(result)];
|
|
73
|
+
const finishedAt = record.finishedAt ? Date.parse(record.finishedAt) : Date.now();
|
|
74
|
+
const startedAt = Date.parse(record.startedAt);
|
|
75
|
+
return {
|
|
76
|
+
content,
|
|
77
|
+
_meta: {
|
|
78
|
+
tool,
|
|
79
|
+
card: {
|
|
80
|
+
workspaceId,
|
|
81
|
+
summary: textSummary(record.output ? [textBlock(record.output)] : []),
|
|
82
|
+
payload: { content },
|
|
83
|
+
},
|
|
84
|
+
},
|
|
85
|
+
structuredContent: {
|
|
86
|
+
result,
|
|
87
|
+
processId: record.processId,
|
|
88
|
+
sessionId: record.processId,
|
|
89
|
+
outputId: record.outputId,
|
|
90
|
+
running: record.status === "running",
|
|
91
|
+
exitCode: record.exitCode,
|
|
92
|
+
signal: record.signal,
|
|
93
|
+
timedOut: record.timedOut,
|
|
94
|
+
wallTimeMs: Math.max(0, Number.isFinite(finishedAt - startedAt) ? finishedAt - startedAt : 0),
|
|
95
|
+
outputTruncated: false,
|
|
96
|
+
},
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
export function readWorkspaceBashOutput(store, workspaceId, outputId) {
|
|
100
|
+
const record = store.read(outputId);
|
|
101
|
+
if (!record)
|
|
102
|
+
throw new Error(`Unknown Bash output: ${outputId}`);
|
|
103
|
+
if (record.workspaceId !== workspaceId) {
|
|
104
|
+
throw new Error(`Bash output ${outputId} does not belong to workspace ${workspaceId}.`);
|
|
105
|
+
}
|
|
106
|
+
return record;
|
|
107
|
+
}
|
|
108
|
+
export function markReturnedOutput(store, result) {
|
|
109
|
+
if (typeof result !== "object" || result === null)
|
|
110
|
+
return;
|
|
111
|
+
const structured = result.structuredContent;
|
|
112
|
+
if (typeof structured !== "object" || structured === null)
|
|
113
|
+
return;
|
|
114
|
+
const record = structured;
|
|
115
|
+
if (record.running === true && typeof record.outputId === "string") {
|
|
116
|
+
store.markReturned(record.outputId);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
export function recordBashCompletion(lifecycle, store, outputId) {
|
|
120
|
+
if (!outputId)
|
|
121
|
+
return;
|
|
122
|
+
const completion = store.claimCompletion(outputId);
|
|
123
|
+
if (!completion)
|
|
124
|
+
return;
|
|
125
|
+
lifecycle.recordLinked({
|
|
126
|
+
sourceActivityId: completion.activityId,
|
|
127
|
+
tool: "bash_result",
|
|
128
|
+
request: {
|
|
129
|
+
processId: completion.processId,
|
|
130
|
+
outputId: completion.outputId,
|
|
131
|
+
},
|
|
132
|
+
result: {
|
|
133
|
+
processId: completion.processId,
|
|
134
|
+
outputId: completion.outputId,
|
|
135
|
+
exitCode: completion.exitCode,
|
|
136
|
+
signal: completion.signal,
|
|
137
|
+
timedOut: completion.timedOut,
|
|
138
|
+
},
|
|
139
|
+
outcome: completion.status === "failed"
|
|
140
|
+
? { type: "failed", error: bashCompletionError(completion) }
|
|
141
|
+
: { type: "succeeded" },
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
export function processActivityOutcome(result) {
|
|
145
|
+
if (toolResultIsError(result))
|
|
146
|
+
return { type: "failed", error: activityFailureMessage(result) };
|
|
147
|
+
if (typeof result !== "object" || result === null)
|
|
148
|
+
return { type: "succeeded" };
|
|
149
|
+
const structured = result.structuredContent;
|
|
150
|
+
if (typeof structured !== "object" || structured === null)
|
|
151
|
+
return { type: "succeeded" };
|
|
152
|
+
const process = structured;
|
|
153
|
+
if (process.running === true)
|
|
154
|
+
return { type: "returned" };
|
|
155
|
+
if (process.timedOut === true ||
|
|
156
|
+
typeof process.signal === "string" ||
|
|
157
|
+
(typeof process.exitCode === "number" && process.exitCode !== 0)) {
|
|
158
|
+
return { type: "failed", error: activityFailureMessage(result) };
|
|
159
|
+
}
|
|
160
|
+
return { type: "succeeded" };
|
|
161
|
+
}
|
|
162
|
+
export function toolResultIsError(result) {
|
|
163
|
+
return typeof result === "object" && result !== null && result.isError === true;
|
|
164
|
+
}
|
|
165
|
+
function compactProcessOutput(output) {
|
|
166
|
+
if (!output)
|
|
167
|
+
return { output: "", truncated: false };
|
|
168
|
+
const trailingNewline = output.endsWith("\n");
|
|
169
|
+
const body = trailingNewline ? output.slice(0, -1) : output;
|
|
170
|
+
const lines = body.split("\n");
|
|
171
|
+
if (lines.length <= PROCESS_RESPONSE_OUTPUT_LINES)
|
|
172
|
+
return { output, truncated: false };
|
|
173
|
+
const compact = lines.slice(-PROCESS_RESPONSE_OUTPUT_LINES).join("\n");
|
|
174
|
+
return {
|
|
175
|
+
output: trailingNewline ? `${compact}\n` : compact,
|
|
176
|
+
truncated: true,
|
|
177
|
+
};
|
|
178
|
+
}
|
|
179
|
+
function processResult(snapshot) {
|
|
180
|
+
const status = snapshot.running
|
|
181
|
+
? `Process running with process ID ${snapshot.processId}.`
|
|
182
|
+
: snapshot.timedOut
|
|
183
|
+
? "Process timed out and was terminated."
|
|
184
|
+
: snapshot.signal
|
|
185
|
+
? `Process exited after signal ${snapshot.signal}.`
|
|
186
|
+
: `Process exited with code ${snapshot.exitCode ?? "unknown"}.`;
|
|
187
|
+
const compact = compactProcessOutput(snapshot.output).output.replace(/\n$/, "");
|
|
188
|
+
return [compact, status, outputIdNotice(snapshot.outputId)].filter(Boolean).join("\n");
|
|
189
|
+
}
|
|
190
|
+
function completedProcessResult(snapshot) {
|
|
191
|
+
const status = snapshot.timedOut
|
|
192
|
+
? `Background process ${snapshot.processId} timed out and was terminated.`
|
|
193
|
+
: snapshot.signal
|
|
194
|
+
? `Background process ${snapshot.processId} exited after signal ${snapshot.signal}.`
|
|
195
|
+
: `Background process ${snapshot.processId} exited with code ${snapshot.exitCode ?? "unknown"}.`;
|
|
196
|
+
const command = `Command: ${snapshot.command}`;
|
|
197
|
+
const output = compactProcessOutput(snapshot.output).output.replace(/\n$/, "");
|
|
198
|
+
return [status, command, output, outputIdNotice(snapshot.outputId)].filter(Boolean).join("\n");
|
|
199
|
+
}
|
|
200
|
+
function outputIdNotice(outputId) {
|
|
201
|
+
return outputId ? `Full output ID: ${outputId}.` : "";
|
|
202
|
+
}
|
|
203
|
+
function durableOutputResult(record) {
|
|
204
|
+
const status = record.status === "running"
|
|
205
|
+
? `Process ${record.processId} is still running.`
|
|
206
|
+
: record.timedOut
|
|
207
|
+
? `Process ${record.processId} timed out and was terminated.`
|
|
208
|
+
: record.signal
|
|
209
|
+
? `Process ${record.processId} exited after signal ${record.signal}.`
|
|
210
|
+
: `Process ${record.processId} exited with code ${record.exitCode ?? "unknown"}.`;
|
|
211
|
+
return [record.output.replace(/\n$/, ""), status, `Full output ID: ${record.outputId}.`]
|
|
212
|
+
.filter(Boolean)
|
|
213
|
+
.join("\n");
|
|
214
|
+
}
|
|
215
|
+
function bashCompletionError(record) {
|
|
216
|
+
if (record.error)
|
|
217
|
+
return record.error;
|
|
218
|
+
if (record.timedOut)
|
|
219
|
+
return `Background process ${record.processId} timed out.`;
|
|
220
|
+
if (record.signal)
|
|
221
|
+
return `Background process ${record.processId} exited after signal ${record.signal}.`;
|
|
222
|
+
return `Background process ${record.processId} exited with code ${record.exitCode ?? "unknown"}.`;
|
|
223
|
+
}
|
|
224
|
+
function activityFailureMessage(result) {
|
|
225
|
+
if (typeof result !== "object" || result === null)
|
|
226
|
+
return "Tool returned a failed result.";
|
|
227
|
+
const record = result;
|
|
228
|
+
if (Array.isArray(record.content)) {
|
|
229
|
+
const text = record.content
|
|
230
|
+
.map((entry) => {
|
|
231
|
+
if (typeof entry !== "object" || entry === null)
|
|
232
|
+
return "";
|
|
233
|
+
const value = entry.text;
|
|
234
|
+
return typeof value === "string" ? value : "";
|
|
235
|
+
})
|
|
236
|
+
.filter(Boolean)
|
|
237
|
+
.join("\n");
|
|
238
|
+
if (text)
|
|
239
|
+
return text;
|
|
240
|
+
}
|
|
241
|
+
if (typeof record.structuredContent === "object" && record.structuredContent !== null) {
|
|
242
|
+
const value = record.structuredContent.result;
|
|
243
|
+
if (typeof value === "string" && value)
|
|
244
|
+
return value;
|
|
245
|
+
}
|
|
246
|
+
return "Tool returned a failed result.";
|
|
247
|
+
}
|
|
248
|
+
function textBlock(text) {
|
|
249
|
+
return { type: "text", text };
|
|
250
|
+
}
|
|
251
|
+
function textSummary(content) {
|
|
252
|
+
const text = content.map((item) => item.text).join("\n");
|
|
253
|
+
return {
|
|
254
|
+
lines: text.length === 0 ? 0 : text.split("\n").length,
|
|
255
|
+
characters: text.length,
|
|
256
|
+
};
|
|
257
|
+
}
|