openmeld 0.3.222 → 0.3.224
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/dist/{add-me-membership-Wc69O6rA.js → add-me-membership-CGISMRPF.js} +2 -2
- package/dist/{add-me-membership-Wc69O6rA.js.map → add-me-membership-CGISMRPF.js.map} +1 -1
- package/dist/{agents-B8zhQnGw.js → agents-CR7BebgO.js} +12 -12
- package/dist/{agents-B8zhQnGw.js.map → agents-CR7BebgO.js.map} +1 -1
- package/dist/{api-client-foundation-DTiFoGpI.js → api-client-foundation-CqbIn_de.js} +1 -1
- package/dist/{api-client-foundation-DnJ_JYli.js → api-client-foundation-DpVwrgh-.js} +3 -3
- package/dist/{api-client-foundation-DnJ_JYli.js.map → api-client-foundation-DpVwrgh-.js.map} +1 -1
- package/dist/auth-session-BJfKu74u.js +4 -0
- package/dist/{auth-session-nhS8jyux.js → auth-session-uEk_Tx6T.js} +8 -8
- package/dist/auth-session-uEk_Tx6T.js.map +1 -0
- package/dist/{base-url-CMBApK1J.js → base-url-DXrUiSMI.js} +1 -1
- package/dist/{base-url-D7uE_xTo.js → base-url-DvjOkKPs.js} +2 -2
- package/dist/{base-url-D7uE_xTo.js.map → base-url-DvjOkKPs.js.map} +1 -1
- package/dist/{channel-ggx0o_aG.js → channel-B2fD2_be.js} +4 -4
- package/dist/{channel-ggx0o_aG.js.map → channel-B2fD2_be.js.map} +1 -1
- package/dist/{cli-binary-manifest-DiUulkp0.js → cli-binary-manifest-CT5X05lk.js} +30 -4
- package/dist/cli-binary-manifest-CT5X05lk.js.map +1 -0
- package/dist/{command-Bz9x7V2T.js → command-CQ9XRwqf.js} +22 -165
- package/dist/command-CQ9XRwqf.js.map +1 -0
- package/dist/{computer-connection-M3N6plkj.js → computer-connection-Dm7iTnRK.js} +2 -2
- package/dist/{computer-connection-M3N6plkj.js.map → computer-connection-Dm7iTnRK.js.map} +1 -1
- package/dist/{computer-connection-review-DY5BK_27.js → computer-connection-review-RHAoddSS.js} +6 -6
- package/dist/{computer-connection-review-DY5BK_27.js.map → computer-connection-review-RHAoddSS.js.map} +1 -1
- package/dist/{computer-disconnect-DR1WDSPK.js → computer-disconnect-BWRsge_2.js} +10 -10
- package/dist/{computer-disconnect-DR1WDSPK.js.map → computer-disconnect-BWRsge_2.js.map} +1 -1
- package/dist/{computer-setup-continuation-2gs8z-nB.js → computer-setup-continuation-BOCRH0qq.js} +4 -4
- package/dist/{computer-setup-continuation-2gs8z-nB.js.map → computer-setup-continuation-BOCRH0qq.js.map} +1 -1
- package/dist/context-Bdh3nAzw.js +162 -0
- package/dist/context-Bdh3nAzw.js.map +1 -0
- package/dist/{control-plane-e3W6PD2M.js → control-plane-rTE2rHZ5.js} +2 -2
- package/dist/{control-plane-e3W6PD2M.js.map → control-plane-rTE2rHZ5.js.map} +1 -1
- package/dist/{daemon-device-id-DWO9_QW6.js → daemon-device-id-B1Af2Zmn.js} +344 -36
- package/dist/daemon-device-id-B1Af2Zmn.js.map +1 -0
- package/dist/{daemon-frame-transport-D2-tazuK.js → daemon-frame-transport-WiB73yhT.js} +519 -12
- package/dist/daemon-frame-transport-WiB73yhT.js.map +1 -0
- package/dist/{daemon-lifecycle-journal-DyKx1xzm.js → daemon-lifecycle-journal-CDZLWgMj.js} +11 -11
- package/dist/daemon-lifecycle-journal-CDZLWgMj.js.map +1 -0
- package/dist/{daemon-runtime-lease-BUr4qsLo.js → daemon-runtime-lease-1LMeZ6IS.js} +2 -2
- package/dist/{daemon-runtime-lease-BUr4qsLo.js.map → daemon-runtime-lease-1LMeZ6IS.js.map} +1 -1
- package/dist/{daemon-runtime-lease-C9X8xV_F.js → daemon-runtime-lease-Bz0ucJj3.js} +1 -1
- package/dist/{daemon-service-contract-BpOBMGX0.js → daemon-service-contract-GLmAxsq-.js} +4 -4
- package/dist/{daemon-service-contract-BpOBMGX0.js.map → daemon-service-contract-GLmAxsq-.js.map} +1 -1
- package/dist/{daemon-service-manager-HL4VKbIU.js → daemon-service-manager-C8RUCMDo.js} +9 -7
- package/dist/daemon-service-manager-C8RUCMDo.js.map +1 -0
- package/dist/{daemon-service-manager-B-QrDe5p.js → daemon-service-manager-CU-79a4z.js} +1 -1
- package/dist/{daemon-service-runtime-bKt0YjC7.js → daemon-service-runtime-CBJtgM4P.js} +397 -51
- package/dist/daemon-service-runtime-CBJtgM4P.js.map +1 -0
- package/dist/{daemon-service-update-coordinator-D8O1uTtu.js → daemon-service-update-coordinator-0LB_XGVZ.js} +48 -33
- package/dist/daemon-service-update-coordinator-0LB_XGVZ.js.map +1 -0
- package/dist/{daemon-state-B44aq2nm.js → daemon-state-Ca0WK5x0.js} +3 -3
- package/dist/{daemon-state-B44aq2nm.js.map → daemon-state-Ca0WK5x0.js.map} +1 -1
- package/dist/{dist-B07ziN_u.js → dist-BeJcIdCZ.js} +25 -17
- package/dist/dist-BeJcIdCZ.js.map +1 -0
- package/dist/dist-C32frPV2.js +4 -0
- package/dist/{dist-B2EauBG6.js → dist-YbTZ-Y-2.js} +16 -12
- package/dist/dist-YbTZ-Y-2.js.map +1 -0
- package/dist/{elapsed-time-Ooo4IC25.js → elapsed-time-q8lSPcfm.js} +6 -6
- package/dist/{elapsed-time-Ooo4IC25.js.map → elapsed-time-q8lSPcfm.js.map} +1 -1
- package/dist/{entry-DeIRMCiW.js → entry-CpS6tnIk.js} +12 -12
- package/dist/{entry-DeIRMCiW.js.map → entry-CpS6tnIk.js.map} +1 -1
- package/dist/{installation-inventory-DThnBEhV.js → installation-inventory-C5FsAvrf.js} +5 -5
- package/dist/{installation-inventory-DThnBEhV.js.map → installation-inventory-C5FsAvrf.js.map} +1 -1
- package/dist/{installer-D2bWZT88.js → installer-054NLJg6.js} +2 -2
- package/dist/{installer-D2bWZT88.js.map → installer-054NLJg6.js.map} +1 -1
- package/dist/io-DT66typ4.js +16 -0
- package/dist/io-DT66typ4.js.map +1 -0
- package/dist/{local-components-B6SCXZf9.js → local-components-DleLEzSU.js} +2 -1
- package/dist/local-components-DleLEzSU.js.map +1 -0
- package/dist/{lock-Bsl_ElWy.js → lock-iue1J2xy.js} +8 -5
- package/dist/lock-iue1J2xy.js.map +1 -0
- package/dist/{cli-space-api-CQtKu9Zt.js → meta-api-B1k3mwHN.js} +203 -19
- package/dist/meta-api-B1k3mwHN.js.map +1 -0
- package/dist/{openmeld-BQy0nMox.js → openmeld-BFO3sfC_.js} +93 -1027
- package/dist/openmeld-BFO3sfC_.js.map +1 -0
- package/dist/openmeld-dev.js +1 -1
- package/dist/{openmeld-environment-target-B6HqY0_a.js → openmeld-environment-target-BBn5Ko6p.js} +3 -3
- package/dist/{openmeld-environment-target-B6HqY0_a.js.map → openmeld-environment-target-BBn5Ko6p.js.map} +1 -1
- package/dist/{openmeld-maintenance-worker-BSODPHbn.js → openmeld-maintenance-worker-Ce4ehQ-c.js} +15 -14
- package/dist/openmeld-maintenance-worker-Ce4ehQ-c.js.map +1 -0
- package/dist/{openmeld-tools-update-join-BfY0GnDs.js → openmeld-tools-update-join-D24llJ9L.js} +4 -4
- package/dist/{openmeld-tools-update-join-BfY0GnDs.js.map → openmeld-tools-update-join-D24llJ9L.js.map} +1 -1
- package/dist/{openmeld-tools-update-process-frame-B-XB4dmO.js → openmeld-tools-update-process-frame-BtFKp70p.js} +2 -2
- package/dist/{openmeld-tools-update-process-frame-B-XB4dmO.js.map → openmeld-tools-update-process-frame-BtFKp70p.js.map} +1 -1
- package/dist/{openmeld-tools-update-process-frame-j-RXwFsK.js → openmeld-tools-update-process-frame-DbEZ3fEh.js} +1 -1
- package/dist/openmeld.js +9 -5
- package/dist/openmeld.js.map +1 -1
- package/dist/{prepare-provider-runtime-CWZcuMF6.js → prepare-provider-runtime-MB-BxOgt.js} +3342 -1327
- package/dist/prepare-provider-runtime-MB-BxOgt.js.map +1 -0
- package/dist/{prepare-session-executor-CkDxstsf.js → prepare-session-executor-zvSZ6jqy.js} +29 -28
- package/dist/prepare-session-executor-zvSZ6jqy.js.map +1 -0
- package/dist/{presenter-BcxiQyJg.js → presenter-MJ5YgsOT.js} +3 -13
- package/dist/presenter-MJ5YgsOT.js.map +1 -0
- package/dist/{process-channel-BQwtOlRc.js → process-channel-CgbduFCz.js} +2 -2
- package/dist/{process-channel-BQwtOlRc.js.map → process-channel-CgbduFCz.js.map} +1 -1
- package/dist/process-environment-BWSFeUu6.js +49 -0
- package/dist/process-environment-BWSFeUu6.js.map +1 -0
- package/dist/process-identity-BQLdaZdB.js +190 -0
- package/dist/process-identity-BQLdaZdB.js.map +1 -0
- package/dist/process-launch-B0pHlkV_.js +173 -0
- package/dist/process-launch-B0pHlkV_.js.map +1 -0
- package/dist/{published-production-safety-dp-fOmmB.js → published-production-safety-C40hKrBA.js} +2 -2
- package/dist/{published-production-safety-dp-fOmmB.js.map → published-production-safety-C40hKrBA.js.map} +1 -1
- package/dist/{runtime-BYaFkkLK.js → runtime-D6jItuQ-.js} +155 -702
- package/dist/runtime-D6jItuQ-.js.map +1 -0
- package/dist/{runtime-agent-controller-reporting-submission-BUUub9_1.js → runtime-agent-controller-reporting-submission-D9ysbi7R.js} +9 -9
- package/dist/{runtime-agent-controller-reporting-submission-BUUub9_1.js.map → runtime-agent-controller-reporting-submission-D9ysbi7R.js.map} +1 -1
- package/dist/{runtime-reporting-health-state-BgwfrH7f.js → runtime-reporting-health-state-DA-_vW-n.js} +2210 -60
- package/dist/runtime-reporting-health-state-DA-_vW-n.js.map +1 -0
- package/dist/{runtime-transport-Cc_RN-PL.js → runtime-transport-YIYHSJPn.js} +99 -14
- package/dist/runtime-transport-YIYHSJPn.js.map +1 -0
- package/dist/{service-CfO38_a-.js → service-Dz2MsvGr.js} +126 -550
- package/dist/service-Dz2MsvGr.js.map +1 -0
- package/dist/{service-account-admission-Dy2W3n3_.js → service-account-admission-BfdJ-kKR.js} +18 -10
- package/dist/service-account-admission-BfdJ-kKR.js.map +1 -0
- package/dist/{service-account-authorization-DKpIpxXo.js → service-account-authorization-DZbUOD29.js} +6 -6
- package/dist/{service-account-authorization-DKpIpxXo.js.map → service-account-authorization-DZbUOD29.js.map} +1 -1
- package/dist/{service-account-control-CCPQAZzn.js → service-account-control-DCMOKXDT.js} +16 -16
- package/dist/service-account-control-DCMOKXDT.js.map +1 -0
- package/dist/{service-control-capabilities-C9UHSMPz.js → service-control-capabilities-3_bdeHXu.js} +1 -1
- package/dist/{service-control-capabilities-xGylBNoH.js → service-control-capabilities-Bb738e_d.js} +19 -15
- package/dist/service-control-capabilities-Bb738e_d.js.map +1 -0
- package/dist/{service-control-carrier-capability-DDNyE5cB.js → service-control-carrier-capability-gMk5gMKU.js} +11 -4
- package/dist/service-control-carrier-capability-gMk5gMKU.js.map +1 -0
- package/dist/{state-CQG3k9eh.js → state-GxF_KAM1.js} +4 -4
- package/dist/{state-CQG3k9eh.js.map → state-GxF_KAM1.js.map} +1 -1
- package/dist/{upgrade-D5FxGbh1.js → upgrade-DfJwCD5H.js} +52 -32
- package/dist/upgrade-DfJwCD5H.js.map +1 -0
- package/dist/upgrade-DgmYWeNu.js +4 -0
- package/dist/windows/openmeld-windows-process.exe +0 -0
- package/dist/windows-artifact-DJ9DKjfc.js +16 -0
- package/dist/windows-artifact-DJ9DKjfc.js.map +1 -0
- package/dist/windows-task-command-COu_6Ke3.js +74 -0
- package/dist/windows-task-command-COu_6Ke3.js.map +1 -0
- package/dist/windows-task-entry-CtRpUbiK.js +50 -0
- package/dist/windows-task-entry-CtRpUbiK.js.map +1 -0
- package/dist/{windows-user-task-Bx2yYnOM.js → windows-user-task-B8NcRg_u.js} +1 -1
- package/dist/{windows-user-task-CRGbKggi.js → windows-user-task-Io5QuEVR.js} +108 -51
- package/dist/windows-user-task-Io5QuEVR.js.map +1 -0
- package/package.json +12 -12
- package/skills/openmeld-cli/playbooks/space-ops.md +9 -3
- package/skills/openmeld-cli/references/commands.md +0 -1
- package/dist/auth-session-D8dbMpaK.js +0 -4
- package/dist/auth-session-nhS8jyux.js.map +0 -1
- package/dist/cli-binary-manifest-DiUulkp0.js.map +0 -1
- package/dist/cli-space-api-CQtKu9Zt.js.map +0 -1
- package/dist/command-Bz9x7V2T.js.map +0 -1
- package/dist/context-l_31Mn4X.js +0 -473
- package/dist/context-l_31Mn4X.js.map +0 -1
- package/dist/daemon-device-id-DWO9_QW6.js.map +0 -1
- package/dist/daemon-frame-transport-D2-tazuK.js.map +0 -1
- package/dist/daemon-lifecycle-journal-DyKx1xzm.js.map +0 -1
- package/dist/daemon-service-manager-HL4VKbIU.js.map +0 -1
- package/dist/daemon-service-runtime-bKt0YjC7.js.map +0 -1
- package/dist/daemon-service-update-coordinator-D8O1uTtu.js.map +0 -1
- package/dist/dist-B07ziN_u.js.map +0 -1
- package/dist/dist-B2EauBG6.js.map +0 -1
- package/dist/dist-DhWI1iyi.js +0 -4
- package/dist/local-components-B6SCXZf9.js.map +0 -1
- package/dist/lock-Bsl_ElWy.js.map +0 -1
- package/dist/openmeld-BQy0nMox.js.map +0 -1
- package/dist/openmeld-maintenance-worker-BSODPHbn.js.map +0 -1
- package/dist/prepare-provider-runtime-CWZcuMF6.js.map +0 -1
- package/dist/prepare-session-executor-CkDxstsf.js.map +0 -1
- package/dist/presenter-BcxiQyJg.js.map +0 -1
- package/dist/process-identity-CNus0e6E.js +0 -146
- package/dist/process-identity-CNus0e6E.js.map +0 -1
- package/dist/runtime-BYaFkkLK.js.map +0 -1
- package/dist/runtime-reporting-health-state-BgwfrH7f.js.map +0 -1
- package/dist/runtime-transport-Cc_RN-PL.js.map +0 -1
- package/dist/service-CfO38_a-.js.map +0 -1
- package/dist/service-account-admission-Dy2W3n3_.js.map +0 -1
- package/dist/service-account-control-CCPQAZzn.js.map +0 -1
- package/dist/service-control-capabilities-xGylBNoH.js.map +0 -1
- package/dist/service-control-carrier-capability-DDNyE5cB.js.map +0 -1
- package/dist/upgrade-D5FxGbh1.js.map +0 -1
- package/dist/upgrade-DhFEu0k6.js +0 -4
- package/dist/windows-user-task-CRGbKggi.js.map +0 -1
|
@@ -1,28 +1,35 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
import { g as toSpaceUpdatesUrl } from "./urls-CY2uzH2-.js";
|
|
4
|
-
import { A as OPENMELD_HOME_ENV_KEY,
|
|
5
|
-
import {
|
|
6
|
-
import { Jn as buildCliLaunchInvocation, Tt as withAgentActivityLocalStateLock, _n as resolveInstalledGrokBuildLaunchCandidates, bn as resolveInstalledCodexLaunchCandidates, ft as listProjectBindings, ht as removeAgentActivityBindingsWhileDeliveryLocked, mn as resolveInstalledOpenCodeLaunchCandidates, vt as resolveProjectBindingForPath, wn as resolveInstalledClaudeCodeLaunchCandidates, wt as withAgentActivityDeliveryLock } from "./prepare-provider-runtime-
|
|
7
|
-
import {
|
|
8
|
-
import { d as getAuthSessionState } from "./auth-session-
|
|
9
|
-
import {
|
|
10
|
-
import { C as readAgentActivityOutboxHealth, E as readRecentAgentActivityHookStatus, S as deferAgentActivityOutbox, T as removeProjectOutbox, _ as drainAgentActivityHookInbox, b as AgentActivitySubmissionResultsMismatchError, c as scanLocalAgentActivity, f as resolveOpenCodeDatabasePath, h as normalizeSharedSessionTitle, l as startAgentActivityTranscriptWatchers, m as resolveObservedAgentActivityBinding, u as readOpenCodeSessionRows, w as readDueAgentActivityOutbox, x as applyAgentActivitySubmissionResults, y as removeProjectInbox } from "./daemon-frame-transport-
|
|
4
|
+
import { A as OPENMELD_HOME_ENV_KEY, d as runWithHeldLock, rt as organizationSkillReleaseDir, s as releaseLock, t as acquireLock, ut as resolveOpenMeldRootDir } from "./lock-iue1J2xy.js";
|
|
5
|
+
import { Lr as stringifyOrganizationSkillBundleCanonicalJson, Mt as createAgentActivityApi, Pr as organizationSkillBundleSchema, Ti as signalTextEnvelopeSchema, Zn as OPENMELD_HEADER_SPACE_ADDRESSING_VERSION, _r as spaceTaskMutationReceiptSchema, er as getSpaceUpdatesResponseSchema, gr as spaceTaskIdSchema, hr as mutateSpaceTaskBodySchema, mr as createSpaceTaskBodySchema, on as createRuntimeReportingApi, qr as submitAgentControllerTaskLifecycleEventItemSchema, ri as observedLocalAgentSessionSchema, t as OpenMeldApiClientHttpError } from "./dist-BeJcIdCZ.js";
|
|
6
|
+
import { Jn as buildCliLaunchInvocation, Tt as withAgentActivityLocalStateLock, Xn as resolveExecutablePathsOnPath, Zn as listAgentControllerSupervisedProcessIds, _n as resolveInstalledGrokBuildLaunchCandidates, bn as resolveInstalledCodexLaunchCandidates, er as isDispatchSpaceActionDeliveryOnly, ft as listProjectBindings, ht as removeAgentActivityBindingsWhileDeliveryLocked, mn as resolveInstalledOpenCodeLaunchCandidates, qn as resolveDispatchSpaceActionMcpServer, vt as resolveProjectBindingForPath, wn as resolveInstalledClaudeCodeLaunchCandidates, wt as withAgentActivityDeliveryLock } from "./prepare-provider-runtime-MB-BxOgt.js";
|
|
7
|
+
import { Gt as spaceWorkSelectionSchema, Ht as SPACE_WORK_MAX_BYTES, Kt as spaceWorkingCopyResultSchema, Wt as spaceWorkRelativePathSchema, Yn as executionContextAssignmentSchema, Zn as executionContextTaskSchema, _n as spaceMessageFileReferenceSchema, fn as spaceFileEntryIdSchema, gn as spaceFileRevisionNumberSchema, rn as readSpaceFileTextResponseSchema } from "./runtime-transport-YIYHSJPn.js";
|
|
8
|
+
import { d as getAuthSessionState } from "./auth-session-uEk_Tx6T.js";
|
|
9
|
+
import { O as openMeldRuntimeProfileIdSchema } from "./control-plane-rTE2rHZ5.js";
|
|
10
|
+
import { C as readAgentActivityOutboxHealth, E as readRecentAgentActivityHookStatus, S as deferAgentActivityOutbox, T as removeProjectOutbox, _ as drainAgentActivityHookInbox, b as AgentActivitySubmissionResultsMismatchError, bn as cliJsonErrorEnvelopeSchema, c as scanLocalAgentActivity, f as resolveOpenCodeDatabasePath, h as normalizeSharedSessionTitle, l as startAgentActivityTranscriptWatchers, m as resolveObservedAgentActivityBinding, u as readOpenCodeSessionRows, w as readDueAgentActivityOutbox, x as applyAgentActivitySubmissionResults, y as removeProjectInbox } from "./daemon-frame-transport-WiB73yhT.js";
|
|
11
|
+
import { B as requireOpenMeldProfile, o as dirnamePath } from "./daemon-device-id-B1Af2Zmn.js";
|
|
12
|
+
import { a as resolveBaseUrl } from "./base-url-DvjOkKPs.js";
|
|
11
13
|
import { n as writeDaemonLocalStateFile, t as readDaemonLocalStateFile } from "./daemon-local-state-file-jOXuXrky.js";
|
|
12
|
-
import { _ as daemonRuntimeStateRootPath } from "./daemon-state-
|
|
13
|
-
import { i as readDaemonServiceContract } from "./daemon-service-contract-
|
|
14
|
-
import { l as resolveUserFacingCliEntryCommand } from "./cli-entry-command-CR_9QU1i.js";
|
|
15
|
-
import {
|
|
16
|
-
import {
|
|
17
|
-
import {
|
|
14
|
+
import { _ as daemonRuntimeStateRootPath } from "./daemon-state-Ca0WK5x0.js";
|
|
15
|
+
import { i as readDaemonServiceContract } from "./daemon-service-contract-GLmAxsq-.js";
|
|
16
|
+
import { l as resolveUserFacingCliEntryCommand, n as formatOpenMeldCliCommand } from "./cli-entry-command-CR_9QU1i.js";
|
|
17
|
+
import { r as outLine } from "./io-DT66typ4.js";
|
|
18
|
+
import { d as buildOpenMeldRequestContextFromProfile, l as canAttachAuthTokenToRequest, n as fetchSpaceRequestWithTimeout, s as buildOpenMeldRequestContextHeadersWithAuth, t as createCliApiClientFoundation } from "./api-client-foundation-DpVwrgh-.js";
|
|
19
|
+
import { S as readDispatchSpaceActionRecord, _ as DispatchSpaceActionRejectedError, b as readDispatchSpaceActionContextForReceiptFromEnv, d as uploadPreparedSpaceFiles, f as createCliSpaceApi, g as DispatchSpaceActionClosedError, l as parseJsonResponse, s as readGatewayTextResponse, u as prepareRequestedSpaceSendFiles, w as writeDispatchSpaceActionRecord, x as readDispatchSpaceActionContextFromEnv } from "./meta-api-B1k3mwHN.js";
|
|
20
|
+
import { createHash, randomBytes, randomUUID } from "node:crypto";
|
|
18
21
|
import { homedir } from "node:os";
|
|
19
|
-
import { basename, dirname, isAbsolute, join, resolve } from "node:path";
|
|
20
|
-
import { appendFile, chmod, copyFile, lstat, mkdir, mkdtemp, open, readFile, readdir, realpath, rename, rm, stat, unlink, writeFile } from "node:fs/promises";
|
|
22
|
+
import { basename, dirname, isAbsolute, join, relative, resolve, sep } from "node:path";
|
|
23
|
+
import { appendFile, chmod, copyFile, lstat, mkdir, mkdtemp, open, readFile, readdir, readlink, realpath, rename, rm, stat, unlink, writeFile } from "node:fs/promises";
|
|
21
24
|
import * as z$1 from "zod/v4";
|
|
22
25
|
import { z } from "zod/v4";
|
|
23
|
-
import { constants, createReadStream } from "node:fs";
|
|
24
|
-
import { spawn } from "node:child_process";
|
|
26
|
+
import { constants, createReadStream, readFileSync } from "node:fs";
|
|
27
|
+
import { execFile, spawn } from "node:child_process";
|
|
28
|
+
import { promisify } from "node:util";
|
|
25
29
|
import { createInterface } from "node:readline";
|
|
30
|
+
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
31
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
32
|
+
import { performance } from "node:perf_hooks";
|
|
26
33
|
//#region ../../packages/schemas/dist/schemas/config/agent-activity-route-registry.js
|
|
27
34
|
const agentActivityRouteRegistrySchema = z$1.strictObject({
|
|
28
35
|
routes: z$1.array(z$1.strictObject({ rootDir: z$1.string().trim().min(1).max(4096) })).max(64),
|
|
@@ -74,6 +81,87 @@ const runtimeReportingHealthStateSchema = z$1.strictObject({
|
|
|
74
81
|
providerConversationProof: runtimeReportingChannelHealthSchema
|
|
75
82
|
});
|
|
76
83
|
//#endregion
|
|
84
|
+
//#region ../../packages/schemas/dist/schemas/space-mesh/dispatch-project-shell.js
|
|
85
|
+
const dispatchProjectShellInputSchema = z$1.strictObject({ command: z$1.string().trim().min(1).max(16e3) });
|
|
86
|
+
const dispatchProjectShellResultSchema = z$1.strictObject({
|
|
87
|
+
exitCode: z$1.number().int().min(0).max(255),
|
|
88
|
+
stdout: z$1.string().max(128 * 1024),
|
|
89
|
+
stderr: z$1.string().max(128 * 1024)
|
|
90
|
+
});
|
|
91
|
+
//#endregion
|
|
92
|
+
//#region ../../packages/schemas/dist/schemas/space-mesh/dispatch-space-action-receipt.js
|
|
93
|
+
const dispatchSpaceActionClosedReasonCodeSchema = z$1.enum(["space_action.context_expired", "space_action.context_mismatch"]);
|
|
94
|
+
const dispatchSpaceActionReceiptSchema = z$1.discriminatedUnion("status", [
|
|
95
|
+
z$1.strictObject({
|
|
96
|
+
status: z$1.literal("proposal_saved_locally"),
|
|
97
|
+
actionType: z$1.enum([
|
|
98
|
+
"post_message",
|
|
99
|
+
"post_status",
|
|
100
|
+
"stay_silent"
|
|
101
|
+
]),
|
|
102
|
+
attemptId: z$1.string().min(1),
|
|
103
|
+
contextId: z$1.string().min(1),
|
|
104
|
+
dispatchId: z$1.string().min(1),
|
|
105
|
+
recordedAtMs: z$1.number().int().nonnegative(),
|
|
106
|
+
contextState: z$1.enum(["active", "expired"]),
|
|
107
|
+
coordinatorAcceptance: z$1.literal("unconfirmed"),
|
|
108
|
+
publication: z$1.literal("unconfirmed")
|
|
109
|
+
}),
|
|
110
|
+
z$1.strictObject({
|
|
111
|
+
status: z$1.literal("rejected_closed"),
|
|
112
|
+
reasonCode: dispatchSpaceActionClosedReasonCodeSchema,
|
|
113
|
+
coordinatorAcceptance: z$1.literal("unconfirmed"),
|
|
114
|
+
publication: z$1.literal("unconfirmed")
|
|
115
|
+
}),
|
|
116
|
+
z$1.strictObject({
|
|
117
|
+
status: z$1.literal("rejected_before_recording"),
|
|
118
|
+
reasonCode: z$1.enum([
|
|
119
|
+
"space_action.invalid_input",
|
|
120
|
+
"space_action.not_allowed",
|
|
121
|
+
"space_action.already_recorded",
|
|
122
|
+
"space_action.targets_unavailable"
|
|
123
|
+
]),
|
|
124
|
+
coordinatorAcceptance: z$1.literal("unconfirmed"),
|
|
125
|
+
publication: z$1.literal("unconfirmed")
|
|
126
|
+
}),
|
|
127
|
+
z$1.strictObject({
|
|
128
|
+
status: z$1.literal("outcome_unknown"),
|
|
129
|
+
reasonCode: z$1.literal("space_action.recording_outcome_unknown"),
|
|
130
|
+
coordinatorAcceptance: z$1.literal("unconfirmed"),
|
|
131
|
+
publication: z$1.literal("unconfirmed")
|
|
132
|
+
})
|
|
133
|
+
]);
|
|
134
|
+
const dispatchSpaceActionToolResultSchema = z$1.strictObject({ receipt: dispatchSpaceActionReceiptSchema });
|
|
135
|
+
//#endregion
|
|
136
|
+
//#region ../../packages/schemas/dist/schemas/space-mesh/dispatch-space-history.js
|
|
137
|
+
const dispatchSpaceHistoryRequestSchema = z$1.strictObject({});
|
|
138
|
+
const dispatchSpaceHistoryResultSchema = z$1.strictObject({
|
|
139
|
+
spaceId: z$1.string().min(1),
|
|
140
|
+
sourceSignalId: z$1.string().min(1),
|
|
141
|
+
beforeCount: z$1.literal(20),
|
|
142
|
+
afterCount: z$1.literal(0),
|
|
143
|
+
signals: z$1.array(z$1.record(z$1.string(), z$1.unknown())).max(21),
|
|
144
|
+
nextCursor: z$1.string().min(1).nullable()
|
|
145
|
+
});
|
|
146
|
+
//#endregion
|
|
147
|
+
//#region ../../packages/schemas/dist/schemas/space-mesh/dispatch-space-read-result.js
|
|
148
|
+
const dispatchSpaceReadResultRequestSchema = z$1.strictObject({
|
|
149
|
+
resultId: z$1.string().uuid(),
|
|
150
|
+
offset: z$1.number().int().nonnegative()
|
|
151
|
+
});
|
|
152
|
+
/** A byte cursor addresses one immutable JSON snapshot, not a local file. */
|
|
153
|
+
const dispatchSpaceReadResultPageSchema = z$1.strictObject({
|
|
154
|
+
resultId: z$1.string().uuid(),
|
|
155
|
+
offset: z$1.number().int().nonnegative(),
|
|
156
|
+
nextOffset: z$1.number().int().positive().nullable(),
|
|
157
|
+
totalBytes: z$1.number().int().positive(),
|
|
158
|
+
text: z$1.string()
|
|
159
|
+
});
|
|
160
|
+
const dispatchSpaceReadResultSchema = z$1.strictObject({
|
|
161
|
+
result: z$1.record(z$1.string(), z$1.unknown()).optional(),
|
|
162
|
+
page: dispatchSpaceReadResultPageSchema.optional()
|
|
163
|
+
}).refine((value) => value.result !== void 0 !== (value.page !== void 0), { message: "A read returns either the complete result or one continuation page." });
|
|
164
|
+
//#endregion
|
|
77
165
|
//#region src/agent-activity/managed-integrations.ts
|
|
78
166
|
const AGENT_ACTIVITY_INTEGRATION_OWNER = "openmeld-agent-activity-v1";
|
|
79
167
|
const LEGACY_AGENT_ACTIVITY_INTEGRATION_OWNER = "openmeld-agent-activity-v0";
|
|
@@ -2783,6 +2871,2109 @@ async function readLocalReportingHealth(rootDir) {
|
|
|
2783
2871
|
return await Promise.all([readAgentActivityOutboxHealth({ rootDir }), readControllerTaskLifecycleOutboxHealth({ rootDir })]);
|
|
2784
2872
|
}
|
|
2785
2873
|
//#endregion
|
|
2874
|
+
//#region src/local-service/file-access/claude-project-container.ts
|
|
2875
|
+
const execFileAsync$2 = promisify(execFile);
|
|
2876
|
+
const PROJECT_IMAGE = "node:24-bookworm@sha256:64af3819f9275802414d7cdc38c27e9d82bd564dec4d4da87d008255d36c63b4";
|
|
2877
|
+
const WORKSPACE_IN_CONTAINER = "/workspace";
|
|
2878
|
+
const COMMAND_TIMEOUT_MS = 12e4;
|
|
2879
|
+
const COMMAND_OUTPUT_BYTES = 128 * 1024;
|
|
2880
|
+
const CONTAINER_NAME_PREFIX = "openmeld-claude-project-";
|
|
2881
|
+
const CONTAINER_MISSING_RE = /no such container|no container with name|does not exist/iu;
|
|
2882
|
+
const LINE_BREAK_RE = /\r?\n/u;
|
|
2883
|
+
const CONTEXT_ID_RE = /^[a-f0-9-]{36}$/iu;
|
|
2884
|
+
async function resolveClaudeProjectContainerRuntime() {
|
|
2885
|
+
for (const kind of ["docker", "podman"]) {
|
|
2886
|
+
const binary = resolveExecutablePathsOnPath(kind)[0];
|
|
2887
|
+
if (!binary) continue;
|
|
2888
|
+
try {
|
|
2889
|
+
await execFileAsync$2(binary, [
|
|
2890
|
+
"version",
|
|
2891
|
+
"--format",
|
|
2892
|
+
"{{.Server.Version}}"
|
|
2893
|
+
], {
|
|
2894
|
+
encoding: "utf8",
|
|
2895
|
+
maxBuffer: 4096,
|
|
2896
|
+
timeout: 5e3,
|
|
2897
|
+
windowsHide: true
|
|
2898
|
+
});
|
|
2899
|
+
return {
|
|
2900
|
+
binary,
|
|
2901
|
+
engineIdentity: await readContainerEngineIdentity({
|
|
2902
|
+
binary,
|
|
2903
|
+
kind
|
|
2904
|
+
}),
|
|
2905
|
+
kind
|
|
2906
|
+
};
|
|
2907
|
+
} catch {}
|
|
2908
|
+
}
|
|
2909
|
+
throw new Error("claude_project.container_runtime_unavailable");
|
|
2910
|
+
}
|
|
2911
|
+
async function prepareClaudeProjectContainer(input) {
|
|
2912
|
+
const runtime = await resolveClaudeProjectContainerRuntime();
|
|
2913
|
+
try {
|
|
2914
|
+
await execFileAsync$2(runtime.binary, [
|
|
2915
|
+
"image",
|
|
2916
|
+
"inspect",
|
|
2917
|
+
"--format",
|
|
2918
|
+
"{{.Id}}",
|
|
2919
|
+
PROJECT_IMAGE
|
|
2920
|
+
], {
|
|
2921
|
+
encoding: "utf8",
|
|
2922
|
+
maxBuffer: 4096,
|
|
2923
|
+
timeout: 1e4,
|
|
2924
|
+
windowsHide: true
|
|
2925
|
+
});
|
|
2926
|
+
} catch {
|
|
2927
|
+
try {
|
|
2928
|
+
await execFileAsync$2(runtime.binary, ["pull", PROJECT_IMAGE], {
|
|
2929
|
+
encoding: "utf8",
|
|
2930
|
+
maxBuffer: 1024 * 1024,
|
|
2931
|
+
timeout: 3e5,
|
|
2932
|
+
windowsHide: true
|
|
2933
|
+
});
|
|
2934
|
+
} catch (error) {
|
|
2935
|
+
throw new Error("claude_project.image_unavailable", { cause: error });
|
|
2936
|
+
}
|
|
2937
|
+
}
|
|
2938
|
+
const workspacePath = await realpath(input.workspacePath);
|
|
2939
|
+
const boundaryRoot = await realpath(input.boundaryRoot);
|
|
2940
|
+
if (workspacePath !== input.workspacePath || boundaryRoot !== input.boundaryRoot) throw new Error("claude_project.workspace_path_changed");
|
|
2941
|
+
const relativeWorkspace = assertContainedWorkspace(boundaryRoot, workspacePath);
|
|
2942
|
+
const outsideDirectory = await mkdtemp(join(dirname(boundaryRoot), ".openmeld-container-probe-"));
|
|
2943
|
+
const marker = randomUUID();
|
|
2944
|
+
const insideName = `.openmeld-container-probe-${marker}`;
|
|
2945
|
+
const insidePath = join(workspacePath, insideName);
|
|
2946
|
+
const outsidePath = join(outsideDirectory, "outside");
|
|
2947
|
+
const writeName = `.openmeld-container-write-probe-${marker}`;
|
|
2948
|
+
const writePath = join(workspacePath, writeName);
|
|
2949
|
+
try {
|
|
2950
|
+
await writeFile(insidePath, `inside:${marker}`, {
|
|
2951
|
+
flag: "wx",
|
|
2952
|
+
mode: 384
|
|
2953
|
+
});
|
|
2954
|
+
await writeFile(outsidePath, `outside:${marker}`, {
|
|
2955
|
+
flag: "wx",
|
|
2956
|
+
mode: 384
|
|
2957
|
+
});
|
|
2958
|
+
const inside = await runClaudeProjectContainerCommand({
|
|
2959
|
+
command: `cat ${quoteShellArgument(`${WORKSPACE_IN_CONTAINER}/${relativeWorkspace}/${insideName}`)}`,
|
|
2960
|
+
contextId: input.contextId,
|
|
2961
|
+
runtime,
|
|
2962
|
+
boundaryRoot,
|
|
2963
|
+
workspacePath
|
|
2964
|
+
});
|
|
2965
|
+
if (inside.exitCode !== 0 || inside.stdout.trim() !== `inside:${marker}`) throw new Error("claude_project.inside_access_unverified");
|
|
2966
|
+
const outside = await runClaudeProjectContainerCommand({
|
|
2967
|
+
command: `cat ${quoteShellArgument(outsidePath)}`,
|
|
2968
|
+
contextId: input.contextId,
|
|
2969
|
+
runtime,
|
|
2970
|
+
boundaryRoot,
|
|
2971
|
+
workspacePath
|
|
2972
|
+
});
|
|
2973
|
+
if (outside.exitCode === 0 || outside.stdout.includes(`outside:${marker}`)) throw new Error("claude_project.outside_access_unverified");
|
|
2974
|
+
if ((await runClaudeProjectContainerCommand({
|
|
2975
|
+
command: `printf 'written:${marker}' > ${quoteShellArgument(`${WORKSPACE_IN_CONTAINER}/${relativeWorkspace}/${writeName}`)}`,
|
|
2976
|
+
contextId: input.contextId,
|
|
2977
|
+
runtime,
|
|
2978
|
+
boundaryRoot,
|
|
2979
|
+
workspacePath
|
|
2980
|
+
})).exitCode !== 0 || await readFile(writePath, "utf8") !== `written:${marker}`) throw new Error("claude_project.inside_write_unverified");
|
|
2981
|
+
return runtime;
|
|
2982
|
+
} finally {
|
|
2983
|
+
await rm(insidePath, { force: true });
|
|
2984
|
+
await rm(writePath, { force: true });
|
|
2985
|
+
await rm(outsideDirectory, {
|
|
2986
|
+
recursive: true,
|
|
2987
|
+
force: true
|
|
2988
|
+
});
|
|
2989
|
+
}
|
|
2990
|
+
}
|
|
2991
|
+
function quoteShellArgument(value) {
|
|
2992
|
+
return `'${value.replaceAll("'", `'"'"'`)}'`;
|
|
2993
|
+
}
|
|
2994
|
+
async function runClaudeProjectContainerCommand(input) {
|
|
2995
|
+
await assertContainerEngineIdentity(input.runtime);
|
|
2996
|
+
const workspacePath = await realpath(input.workspacePath);
|
|
2997
|
+
const boundaryRoot = await realpath(input.boundaryRoot);
|
|
2998
|
+
if (workspacePath !== input.workspacePath || boundaryRoot !== input.boundaryRoot) throw new Error("claude_project.workspace_path_changed");
|
|
2999
|
+
const relativeWorkspace = assertContainedWorkspace(boundaryRoot, workspacePath);
|
|
3000
|
+
if (!isAbsolute(workspacePath) || boundaryRoot.includes(",") || boundaryRoot.includes("\n") || boundaryRoot.includes("\r")) throw new Error("claude_project.workspace_path_unsupported");
|
|
3001
|
+
if (!input.command.trim() || input.command.length > 16e3) throw new Error("claude_project.command_invalid");
|
|
3002
|
+
const name = `${containerNamePrefix(input.contextId)}${randomBytes(4).toString("hex")}`;
|
|
3003
|
+
const args = buildClaudeProjectContainerArgs({
|
|
3004
|
+
command: input.command,
|
|
3005
|
+
name,
|
|
3006
|
+
boundaryRoot,
|
|
3007
|
+
relativeWorkspace
|
|
3008
|
+
});
|
|
3009
|
+
let outcome;
|
|
3010
|
+
try {
|
|
3011
|
+
const result = await execFileAsync$2(input.runtime.binary, args, {
|
|
3012
|
+
encoding: "utf8",
|
|
3013
|
+
maxBuffer: COMMAND_OUTPUT_BYTES,
|
|
3014
|
+
timeout: 135e3,
|
|
3015
|
+
windowsHide: true
|
|
3016
|
+
});
|
|
3017
|
+
outcome = {
|
|
3018
|
+
exitCode: 0,
|
|
3019
|
+
stdout: result.stdout,
|
|
3020
|
+
stderr: result.stderr
|
|
3021
|
+
};
|
|
3022
|
+
} catch (error) {
|
|
3023
|
+
const commandError = error;
|
|
3024
|
+
if (typeof commandError.code === "number") outcome = {
|
|
3025
|
+
exitCode: commandError.code,
|
|
3026
|
+
stdout: commandError.stdout ?? "",
|
|
3027
|
+
stderr: commandError.stderr ?? ""
|
|
3028
|
+
};
|
|
3029
|
+
else outcome = /* @__PURE__ */ new Error(`claude_project.container_command_failed: ${commandError.message}`);
|
|
3030
|
+
}
|
|
3031
|
+
try {
|
|
3032
|
+
await execFileAsync$2(input.runtime.binary, [
|
|
3033
|
+
"rm",
|
|
3034
|
+
"-f",
|
|
3035
|
+
name
|
|
3036
|
+
], {
|
|
3037
|
+
encoding: "utf8",
|
|
3038
|
+
maxBuffer: 4096,
|
|
3039
|
+
timeout: 15e3,
|
|
3040
|
+
windowsHide: true
|
|
3041
|
+
});
|
|
3042
|
+
} catch (error) {
|
|
3043
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
3044
|
+
if (!CONTAINER_MISSING_RE.test(message)) throw new Error(`claude_project.container_cleanup_unconfirmed: ${message}`, { cause: outcome });
|
|
3045
|
+
}
|
|
3046
|
+
if (outcome instanceof Error) throw outcome;
|
|
3047
|
+
return outcome;
|
|
3048
|
+
}
|
|
3049
|
+
async function stopClaudeProjectContainersForContext(input) {
|
|
3050
|
+
await assertContainerEngineIdentity(input.runtime);
|
|
3051
|
+
const prefix = containerNamePrefix(input.contextId);
|
|
3052
|
+
let names;
|
|
3053
|
+
try {
|
|
3054
|
+
const { stdout } = await execFileAsync$2(input.runtime.binary, [
|
|
3055
|
+
"ps",
|
|
3056
|
+
"-a",
|
|
3057
|
+
"--filter",
|
|
3058
|
+
`name=${prefix}`,
|
|
3059
|
+
"--format",
|
|
3060
|
+
"{{.Names}}"
|
|
3061
|
+
], {
|
|
3062
|
+
encoding: "utf8",
|
|
3063
|
+
maxBuffer: 16384,
|
|
3064
|
+
timeout: 15e3,
|
|
3065
|
+
windowsHide: true
|
|
3066
|
+
});
|
|
3067
|
+
names = stdout.split(LINE_BREAK_RE).filter((name) => name.startsWith(prefix));
|
|
3068
|
+
} catch (error) {
|
|
3069
|
+
throw new Error("claude_project.container_cleanup_unconfirmed", { cause: error });
|
|
3070
|
+
}
|
|
3071
|
+
for (const name of names) try {
|
|
3072
|
+
await execFileAsync$2(input.runtime.binary, [
|
|
3073
|
+
"rm",
|
|
3074
|
+
"-f",
|
|
3075
|
+
name
|
|
3076
|
+
], {
|
|
3077
|
+
encoding: "utf8",
|
|
3078
|
+
maxBuffer: 4096,
|
|
3079
|
+
timeout: 15e3,
|
|
3080
|
+
windowsHide: true
|
|
3081
|
+
});
|
|
3082
|
+
} catch (error) {
|
|
3083
|
+
throw new Error("claude_project.container_cleanup_unconfirmed", { cause: error });
|
|
3084
|
+
}
|
|
3085
|
+
}
|
|
3086
|
+
async function assertContainerEngineIdentity(runtime) {
|
|
3087
|
+
if (await readContainerEngineIdentity(runtime) !== runtime.engineIdentity) throw new Error("claude_project.container_engine_changed");
|
|
3088
|
+
}
|
|
3089
|
+
async function readContainerEngineIdentity(input) {
|
|
3090
|
+
if (input.kind === "docker") {
|
|
3091
|
+
const { stdout } = await execFileAsync$2(input.binary, [
|
|
3092
|
+
"info",
|
|
3093
|
+
"--format",
|
|
3094
|
+
"{{.ID}}"
|
|
3095
|
+
], {
|
|
3096
|
+
encoding: "utf8",
|
|
3097
|
+
maxBuffer: 4096,
|
|
3098
|
+
timeout: 5e3,
|
|
3099
|
+
windowsHide: true
|
|
3100
|
+
});
|
|
3101
|
+
const identity = stdout.trim();
|
|
3102
|
+
if (!identity) throw new Error("claude_project.container_engine_identity_missing");
|
|
3103
|
+
return identity;
|
|
3104
|
+
}
|
|
3105
|
+
const { stdout } = await execFileAsync$2(input.binary, [
|
|
3106
|
+
"info",
|
|
3107
|
+
"--format",
|
|
3108
|
+
"json"
|
|
3109
|
+
], {
|
|
3110
|
+
encoding: "utf8",
|
|
3111
|
+
maxBuffer: 128 * 1024,
|
|
3112
|
+
timeout: 5e3,
|
|
3113
|
+
windowsHide: true
|
|
3114
|
+
});
|
|
3115
|
+
const info = JSON.parse(stdout);
|
|
3116
|
+
if (typeof info.host?.hostname !== "string" || typeof info.store?.graphRoot !== "string") throw new Error("claude_project.container_engine_identity_missing");
|
|
3117
|
+
return `${info.host.hostname}:${info.store.graphRoot}`;
|
|
3118
|
+
}
|
|
3119
|
+
function containerNamePrefix(contextId) {
|
|
3120
|
+
if (!CONTEXT_ID_RE.test(contextId)) throw new Error("claude_project.context_id_invalid");
|
|
3121
|
+
return `${CONTAINER_NAME_PREFIX}${contextId.replaceAll("-", "")}-`;
|
|
3122
|
+
}
|
|
3123
|
+
function buildClaudeProjectContainerArgs(input) {
|
|
3124
|
+
if (!input.name.startsWith(CONTAINER_NAME_PREFIX)) throw new Error("claude_project.container_name_invalid");
|
|
3125
|
+
const user = typeof process.getuid === "function" && typeof process.getgid === "function" ? `${process.getuid()}:${process.getgid()}` : "1000:1000";
|
|
3126
|
+
return [
|
|
3127
|
+
"run",
|
|
3128
|
+
"--rm",
|
|
3129
|
+
"--pull",
|
|
3130
|
+
"never",
|
|
3131
|
+
"--name",
|
|
3132
|
+
input.name,
|
|
3133
|
+
"--network",
|
|
3134
|
+
"none",
|
|
3135
|
+
"--read-only",
|
|
3136
|
+
"--cap-drop",
|
|
3137
|
+
"ALL",
|
|
3138
|
+
"--security-opt",
|
|
3139
|
+
"no-new-privileges",
|
|
3140
|
+
"--pids-limit",
|
|
3141
|
+
"128",
|
|
3142
|
+
"--memory",
|
|
3143
|
+
"1g",
|
|
3144
|
+
"--cpus",
|
|
3145
|
+
"2",
|
|
3146
|
+
"--user",
|
|
3147
|
+
user,
|
|
3148
|
+
"--mount",
|
|
3149
|
+
`type=bind,src=${input.boundaryRoot},dst=${WORKSPACE_IN_CONTAINER}`,
|
|
3150
|
+
"--workdir",
|
|
3151
|
+
`${WORKSPACE_IN_CONTAINER}/${input.relativeWorkspace}`,
|
|
3152
|
+
"--tmpfs",
|
|
3153
|
+
"/tmp:rw,nosuid,nodev,size=64m",
|
|
3154
|
+
"--env",
|
|
3155
|
+
"HOME=/tmp",
|
|
3156
|
+
"--env",
|
|
3157
|
+
`GIT_DIR=${WORKSPACE_IN_CONTAINER}/repository.git/worktrees/worktree`,
|
|
3158
|
+
"--env",
|
|
3159
|
+
`GIT_COMMON_DIR=${WORKSPACE_IN_CONTAINER}/repository.git`,
|
|
3160
|
+
"--env",
|
|
3161
|
+
`GIT_WORK_TREE=${WORKSPACE_IN_CONTAINER}/worktree`,
|
|
3162
|
+
PROJECT_IMAGE,
|
|
3163
|
+
"/usr/bin/timeout",
|
|
3164
|
+
`${Math.floor(COMMAND_TIMEOUT_MS / 1e3)}s`,
|
|
3165
|
+
"/bin/bash",
|
|
3166
|
+
"-lc",
|
|
3167
|
+
input.command
|
|
3168
|
+
];
|
|
3169
|
+
}
|
|
3170
|
+
function assertContainedWorkspace(boundaryRoot, workspacePath) {
|
|
3171
|
+
const within = relative(boundaryRoot, workspacePath);
|
|
3172
|
+
if (!within || within === ".." || within.startsWith(`..${sep}`) || isAbsolute(within)) throw new Error("claude_project.workspace_outside_boundary");
|
|
3173
|
+
return within.split(sep).join("/");
|
|
3174
|
+
}
|
|
3175
|
+
//#endregion
|
|
3176
|
+
//#region src/local-service/state/ledger-io.ts
|
|
3177
|
+
function createLedgerIo() {
|
|
3178
|
+
return {
|
|
3179
|
+
appendFile: async (filePath, content, encoding) => {
|
|
3180
|
+
await mkdir(dirnamePath(filePath), { recursive: true });
|
|
3181
|
+
await appendFile(filePath, content, { encoding });
|
|
3182
|
+
},
|
|
3183
|
+
copyFile: async (fromPath, toPath) => {
|
|
3184
|
+
await mkdir(dirnamePath(toPath), { recursive: true });
|
|
3185
|
+
await copyFile(fromPath, toPath);
|
|
3186
|
+
},
|
|
3187
|
+
mkdir: async (dirPath, options) => {
|
|
3188
|
+
await mkdir(dirPath, options);
|
|
3189
|
+
},
|
|
3190
|
+
readFile: async (filePath, encoding) => {
|
|
3191
|
+
return await readFile(filePath, encoding);
|
|
3192
|
+
},
|
|
3193
|
+
rename: async (fromPath, toPath) => {
|
|
3194
|
+
await mkdir(dirnamePath(toPath), { recursive: true });
|
|
3195
|
+
await rename(fromPath, toPath);
|
|
3196
|
+
},
|
|
3197
|
+
syncPath: async (fileOrDirectoryPath) => {
|
|
3198
|
+
const isWindows = process.platform === "win32";
|
|
3199
|
+
if (isWindows && (await stat(fileOrDirectoryPath)).isDirectory()) return;
|
|
3200
|
+
const handle = await open(fileOrDirectoryPath, isWindows ? "r+" : "r");
|
|
3201
|
+
try {
|
|
3202
|
+
await handle.sync();
|
|
3203
|
+
} finally {
|
|
3204
|
+
await handle.close();
|
|
3205
|
+
}
|
|
3206
|
+
},
|
|
3207
|
+
rm: async (filePath, options) => {
|
|
3208
|
+
await rm(filePath, options);
|
|
3209
|
+
},
|
|
3210
|
+
writeFile: async (filePath, content, encoding) => {
|
|
3211
|
+
await mkdir(dirnamePath(filePath), { recursive: true });
|
|
3212
|
+
await writeFile(filePath, content, encoding);
|
|
3213
|
+
}
|
|
3214
|
+
};
|
|
3215
|
+
}
|
|
3216
|
+
//#endregion
|
|
3217
|
+
//#region src/local-service/dispatch/execution-context-workspace.ts
|
|
3218
|
+
const execFileAsync$1 = promisify(execFile);
|
|
3219
|
+
const syncDirectory = createLedgerIo().syncPath;
|
|
3220
|
+
const COMMIT_PATTERN = /^(?:[a-f0-9]{40}|[a-f0-9]{64})$/u;
|
|
3221
|
+
const manifestSchema = z.strictObject({
|
|
3222
|
+
version: z.union([z.literal(1), z.literal(2)]),
|
|
3223
|
+
identity: executionContextAssignmentSchema,
|
|
3224
|
+
sourceWorkingDirectory: z.string().nullable(),
|
|
3225
|
+
baseCommit: z.string().regex(COMMIT_PATTERN).nullable(),
|
|
3226
|
+
boundaryRoot: z.string(),
|
|
3227
|
+
workingDirectory: z.string(),
|
|
3228
|
+
status: z.enum([
|
|
3229
|
+
"preparing",
|
|
3230
|
+
"restoring",
|
|
3231
|
+
"completed",
|
|
3232
|
+
"in_use",
|
|
3233
|
+
"retiring",
|
|
3234
|
+
"released"
|
|
3235
|
+
]),
|
|
3236
|
+
pristineFingerprint: z.string().regex(/^[a-f0-9]{64}$/u).optional(),
|
|
3237
|
+
copiedUncommittedFiles: z.literal(false),
|
|
3238
|
+
processSupervisionId: z.uuid().optional(),
|
|
3239
|
+
activeDispatchId: z.string().min(1).optional(),
|
|
3240
|
+
activeAttemptId: z.string().min(1).optional(),
|
|
3241
|
+
containerRuntime: z.strictObject({
|
|
3242
|
+
binary: z.string().min(1),
|
|
3243
|
+
engineIdentity: z.string().min(1),
|
|
3244
|
+
kind: z.enum(["docker", "podman"])
|
|
3245
|
+
}).optional(),
|
|
3246
|
+
retirement: z.strictObject({
|
|
3247
|
+
path: z.string(),
|
|
3248
|
+
pristineFingerprint: z.string().regex(/^[a-f0-9]{64}$/u)
|
|
3249
|
+
}).optional()
|
|
3250
|
+
}).refine((manifest) => manifest.version === 2 || (manifest.status === "preparing" || manifest.status === "completed") && !manifest.pristineFingerprint && !manifest.processSupervisionId && !manifest.activeDispatchId && !manifest.activeAttemptId && !manifest.containerRuntime && !manifest.retirement).refine((manifest) => manifest.identity.access === "space_context" ? manifest.sourceWorkingDirectory === null && manifest.baseCommit === null : manifest.sourceWorkingDirectory !== null && isAbsolute(manifest.sourceWorkingDirectory) && manifest.baseCommit !== null && manifest.identity.workspace === "isolated");
|
|
3251
|
+
async function prepareExecutionContextWorkspace(input) {
|
|
3252
|
+
const parsed = executionContextTaskSchema.safeParse(input.context);
|
|
3253
|
+
if (!parsed.success) throw failure("invalid_workspace_context");
|
|
3254
|
+
const { nativeConversationId, runtimeWorkspaceFingerprint } = parsed.data;
|
|
3255
|
+
const identity = executionContextAssignmentSchema.strip().parse(parsed.data);
|
|
3256
|
+
const source = identity.access !== "space_context" ? await canonicalDirectory(input.currentWorkingDirectory) : null;
|
|
3257
|
+
if (source) {
|
|
3258
|
+
assertSourceAllowed(source, input.policy);
|
|
3259
|
+
if (identity.workspace === "current") return {
|
|
3260
|
+
workingDirectory: source,
|
|
3261
|
+
boundaryRoot: source
|
|
3262
|
+
};
|
|
3263
|
+
}
|
|
3264
|
+
const ledgerRoot = await ensureDirectory(input.ledgerRootPath, true);
|
|
3265
|
+
const workspaceRoot = await ensureDirectory(join(ledgerRoot, "execution-context-workspaces"));
|
|
3266
|
+
const recordsRoot = await ensureDirectory(join(ledgerRoot, "execution-context-workspace-records"));
|
|
3267
|
+
const boundaryRoot = join(workspaceRoot, identity.id);
|
|
3268
|
+
const workingDirectory = source ? join(boundaryRoot, "worktree") : boundaryRoot;
|
|
3269
|
+
const recordPath = join(recordsRoot, `${identity.id}.json`);
|
|
3270
|
+
return await withWorkspaceLock(ledgerRoot, identity.id, async () => {
|
|
3271
|
+
const stored = await readManifest(recordPath);
|
|
3272
|
+
if (stored) {
|
|
3273
|
+
if (JSON.stringify(stored.identity) !== JSON.stringify(identity) || stored.sourceWorkingDirectory !== source || stored.boundaryRoot !== boundaryRoot || stored.workingDirectory !== workingDirectory) throw failure("workspace_identity_mismatch");
|
|
3274
|
+
await reconcileRetainedWorkspace(stored, recordPath, Boolean(runtimeWorkspaceFingerprint || nativeConversationId), ledgerRoot);
|
|
3275
|
+
return await verifyRetainedWorkspace(stored);
|
|
3276
|
+
}
|
|
3277
|
+
if (runtimeWorkspaceFingerprint || nativeConversationId) throw failure("workspace_record_missing");
|
|
3278
|
+
if (await inspectPath(boundaryRoot)) throw failure("workspace_already_exists");
|
|
3279
|
+
const baseCommit = source ? await resolveSourceCommit(source) : null;
|
|
3280
|
+
const manifest = {
|
|
3281
|
+
version: 2,
|
|
3282
|
+
identity,
|
|
3283
|
+
sourceWorkingDirectory: source,
|
|
3284
|
+
baseCommit,
|
|
3285
|
+
boundaryRoot,
|
|
3286
|
+
workingDirectory,
|
|
3287
|
+
status: "preparing",
|
|
3288
|
+
copiedUncommittedFiles: false,
|
|
3289
|
+
processSupervisionId: randomUUID()
|
|
3290
|
+
};
|
|
3291
|
+
await writeManifest(recordPath, manifest, "wx");
|
|
3292
|
+
await finishWorkspacePreparation(manifest, recordPath, true);
|
|
3293
|
+
return workspaceResult(manifest);
|
|
3294
|
+
});
|
|
3295
|
+
}
|
|
3296
|
+
async function reconcileRetainedWorkspace(record, path, hasNativeIdentity, ledgerRoot) {
|
|
3297
|
+
if (record.status === "retiring") await runWithHeldLock({
|
|
3298
|
+
lock: await acquireWorkspaceExecutionLock(ledgerRoot, record.identity.id),
|
|
3299
|
+
run: async () => {
|
|
3300
|
+
if (!record.processSupervisionId || (await listAgentControllerSupervisedProcessIds({
|
|
3301
|
+
fresh: true,
|
|
3302
|
+
processId: null,
|
|
3303
|
+
supervisionId: record.processSupervisionId
|
|
3304
|
+
})).length > 0) throw failure("workspace_execution_quiescence_unconfirmed");
|
|
3305
|
+
const reconciled = await finishWorkspaceRetirement(record, path);
|
|
3306
|
+
if (reconciled.status === "released") await restoreReleasedWorkspace(reconciled, path);
|
|
3307
|
+
}
|
|
3308
|
+
});
|
|
3309
|
+
else if (record.status === "released") await restoreReleasedWorkspace(record, path);
|
|
3310
|
+
else if (record.status === "preparing" || record.status === "restoring") {
|
|
3311
|
+
if (record.status === "preparing" && hasNativeIdentity) throw failure("workspace_creation_incomplete");
|
|
3312
|
+
if (!record.processSupervisionId || (await listAgentControllerSupervisedProcessIds({
|
|
3313
|
+
fresh: true,
|
|
3314
|
+
processId: null,
|
|
3315
|
+
supervisionId: record.processSupervisionId
|
|
3316
|
+
})).length > 0) throw failure("workspace_preparation_quiescence_unconfirmed");
|
|
3317
|
+
await finishWorkspacePreparation(record, path);
|
|
3318
|
+
} else if (record.status === "in_use") await runWithHeldLock({
|
|
3319
|
+
lock: await acquireWorkspaceExecutionLock(ledgerRoot, record.identity.id),
|
|
3320
|
+
run: async () => {
|
|
3321
|
+
if (record.containerRuntime) await stopClaudeProjectContainersForContext({
|
|
3322
|
+
contextId: record.identity.id,
|
|
3323
|
+
runtime: record.containerRuntime
|
|
3324
|
+
});
|
|
3325
|
+
if (!record.processSupervisionId || (await listAgentControllerSupervisedProcessIds({
|
|
3326
|
+
fresh: true,
|
|
3327
|
+
processId: null,
|
|
3328
|
+
supervisionId: record.processSupervisionId
|
|
3329
|
+
})).length > 0) throw failure("workspace_execution_quiescence_unconfirmed");
|
|
3330
|
+
await verifyRetainedWorkspace(record);
|
|
3331
|
+
const { activeAttemptId: _activeAttemptId, activeDispatchId: _activeDispatchId, containerRuntime: _containerRuntime, ...reconciled } = record;
|
|
3332
|
+
await writeManifest(path, {
|
|
3333
|
+
...reconciled,
|
|
3334
|
+
status: "completed"
|
|
3335
|
+
}, "r+");
|
|
3336
|
+
}
|
|
3337
|
+
});
|
|
3338
|
+
}
|
|
3339
|
+
async function finishWorkspacePreparation(manifest, recordPath, pristineCreation = false) {
|
|
3340
|
+
const { boundaryRoot, workingDirectory, sourceWorkingDirectory: source, baseCommit } = manifest;
|
|
3341
|
+
await ensureDirectory(boundaryRoot);
|
|
3342
|
+
const repository = join(boundaryRoot, "repository.git");
|
|
3343
|
+
if (source && baseCommit) {
|
|
3344
|
+
if (!await inspectPath(repository)) {
|
|
3345
|
+
if ((await readdir(boundaryRoot)).length > 0) throw failure("workspace_creation_incomplete");
|
|
3346
|
+
await git(boundaryRoot, [
|
|
3347
|
+
"init",
|
|
3348
|
+
"--bare",
|
|
3349
|
+
"--template=",
|
|
3350
|
+
`--object-format=${baseCommit.length === 64 ? "sha256" : "sha1"}`,
|
|
3351
|
+
repository
|
|
3352
|
+
], manifest.processSupervisionId);
|
|
3353
|
+
}
|
|
3354
|
+
await canonicalDirectory(repository);
|
|
3355
|
+
await assertPrivateMetadata(repository);
|
|
3356
|
+
if ((await readdir(boundaryRoot)).some((name) => name !== "repository.git" && name !== "worktree")) throw failure("workspace_creation_incomplete");
|
|
3357
|
+
if (await inspectPath(join(repository, "objects", "info", "alternates"))) throw failure("workspace_git_metadata_changed");
|
|
3358
|
+
if (!await inspectPath(workingDirectory)) {
|
|
3359
|
+
await git(boundaryRoot, [
|
|
3360
|
+
`--git-dir=${repository}`,
|
|
3361
|
+
"fetch",
|
|
3362
|
+
"--no-tags",
|
|
3363
|
+
"--no-recurse-submodules",
|
|
3364
|
+
"--depth=1",
|
|
3365
|
+
"--no-write-fetch-head",
|
|
3366
|
+
source,
|
|
3367
|
+
`${baseCommit}:refs/openmeld/base`
|
|
3368
|
+
], manifest.processSupervisionId);
|
|
3369
|
+
await git(boundaryRoot, [
|
|
3370
|
+
`--git-dir=${repository}`,
|
|
3371
|
+
"worktree",
|
|
3372
|
+
"add",
|
|
3373
|
+
"--detach",
|
|
3374
|
+
workingDirectory,
|
|
3375
|
+
baseCommit
|
|
3376
|
+
], manifest.processSupervisionId);
|
|
3377
|
+
}
|
|
3378
|
+
await verifyOwnedRepository(manifest);
|
|
3379
|
+
await finishMissingCheckoutFiles(manifest);
|
|
3380
|
+
if (!await isUnchangedCheckout(manifest)) throw failure("workspace_creation_incomplete");
|
|
3381
|
+
} else if ((await readdir(boundaryRoot)).length > 0) throw failure("workspace_creation_incomplete");
|
|
3382
|
+
await writeManifest(recordPath, {
|
|
3383
|
+
...manifest,
|
|
3384
|
+
status: "completed",
|
|
3385
|
+
pristineFingerprint: pristineCreation ? await fingerprintDirectory(boundaryRoot) : void 0
|
|
3386
|
+
}, "r+");
|
|
3387
|
+
}
|
|
3388
|
+
async function readExecutionContextWorkspace(input) {
|
|
3389
|
+
if (!executionContextAssignmentSchema.shape.id.safeParse(input.contextId).success) throw failure("invalid_workspace_context");
|
|
3390
|
+
if (!await inspectPath(input.ledgerRootPath)) return null;
|
|
3391
|
+
const ledgerRoot = await canonicalDirectory(input.ledgerRootPath);
|
|
3392
|
+
const recordsRoot = join(ledgerRoot, "execution-context-workspace-records");
|
|
3393
|
+
if (!await inspectPath(recordsRoot)) return null;
|
|
3394
|
+
await canonicalDirectory(recordsRoot);
|
|
3395
|
+
const manifest = await readManifest(join(recordsRoot, `${input.contextId}.json`));
|
|
3396
|
+
if (!manifest) return null;
|
|
3397
|
+
const boundaryRoot = join(await canonicalDirectory(join(ledgerRoot, "execution-context-workspaces")), input.contextId);
|
|
3398
|
+
const workingDirectory = manifest.identity.access !== "space_context" ? join(boundaryRoot, "worktree") : boundaryRoot;
|
|
3399
|
+
if (manifest.identity.id !== input.contextId || manifest.boundaryRoot !== boundaryRoot || manifest.workingDirectory !== workingDirectory) throw failure("workspace_identity_mismatch");
|
|
3400
|
+
if (manifest.status === "released" || manifest.status === "retiring") return workspaceResult(manifest);
|
|
3401
|
+
if (manifest.status === "preparing" || manifest.status === "restoring") throw failure("workspace_creation_incomplete");
|
|
3402
|
+
return await verifyRetainedWorkspace(manifest);
|
|
3403
|
+
}
|
|
3404
|
+
async function markExecutionContextWorkspaceInUse(input) {
|
|
3405
|
+
const context = input.context;
|
|
3406
|
+
if (!context || context.workspace !== "isolated" || context.access === "space_context") return null;
|
|
3407
|
+
const root = await canonicalDirectory(input.ledgerRootPath);
|
|
3408
|
+
const lock = await acquireWorkspaceExecutionLock(root, context.id);
|
|
3409
|
+
const processSupervisionId = randomUUID();
|
|
3410
|
+
try {
|
|
3411
|
+
await withWorkspaceLock(root, context.id, async () => {
|
|
3412
|
+
await readExecutionContextWorkspace({
|
|
3413
|
+
ledgerRootPath: root,
|
|
3414
|
+
contextId: context.id
|
|
3415
|
+
});
|
|
3416
|
+
const path = join(root, "execution-context-workspace-records", `${context.id}.json`);
|
|
3417
|
+
const record = await readManifest(path);
|
|
3418
|
+
if (record?.status !== "completed") throw failure("workspace_execution_quiescence_unconfirmed");
|
|
3419
|
+
await writeManifest(path, {
|
|
3420
|
+
...record,
|
|
3421
|
+
status: "in_use",
|
|
3422
|
+
processSupervisionId,
|
|
3423
|
+
...input.containerRuntime && input.dispatchId && input.attemptId ? {
|
|
3424
|
+
activeDispatchId: input.dispatchId,
|
|
3425
|
+
activeAttemptId: input.attemptId
|
|
3426
|
+
} : {},
|
|
3427
|
+
...input.containerRuntime ? { containerRuntime: input.containerRuntime } : {}
|
|
3428
|
+
}, "r+");
|
|
3429
|
+
});
|
|
3430
|
+
return {
|
|
3431
|
+
lock,
|
|
3432
|
+
processSupervisionId
|
|
3433
|
+
};
|
|
3434
|
+
} catch (error) {
|
|
3435
|
+
await releaseLock(lock);
|
|
3436
|
+
throw error;
|
|
3437
|
+
}
|
|
3438
|
+
}
|
|
3439
|
+
async function releaseExecutionContextWorkspace(input) {
|
|
3440
|
+
const root = await canonicalDirectory(input.ledgerRootPath);
|
|
3441
|
+
try {
|
|
3442
|
+
return await withWorkspaceLock(root, input.context.id, async () => {
|
|
3443
|
+
await readExecutionContextWorkspace({
|
|
3444
|
+
ledgerRootPath: root,
|
|
3445
|
+
contextId: input.context.id
|
|
3446
|
+
});
|
|
3447
|
+
const path = join(root, "execution-context-workspace-records", `${input.context.id}.json`);
|
|
3448
|
+
let record = await readManifest(path);
|
|
3449
|
+
if (!(record && ["completed", "in_use"].includes(record.status))) throw failure("workspace_creation_incomplete");
|
|
3450
|
+
if (record.containerRuntime) await stopClaudeProjectContainersForContext({
|
|
3451
|
+
contextId: record.identity.id,
|
|
3452
|
+
runtime: record.containerRuntime
|
|
3453
|
+
});
|
|
3454
|
+
if (!input.ownedProcessesQuiescent) return {
|
|
3455
|
+
status: "retained",
|
|
3456
|
+
reason: "execution_quiescence_unconfirmed"
|
|
3457
|
+
};
|
|
3458
|
+
const { activeAttemptId: _activeAttemptId, activeDispatchId: _activeDispatchId, containerRuntime: _containerRuntime, ...quiescentRecord } = record;
|
|
3459
|
+
const completed = {
|
|
3460
|
+
...quiescentRecord,
|
|
3461
|
+
status: "completed"
|
|
3462
|
+
};
|
|
3463
|
+
await writeManifest(path, completed, "r+");
|
|
3464
|
+
record = await removePristineRetirement(completed, path);
|
|
3465
|
+
if (record.retirement) return {
|
|
3466
|
+
status: "retained",
|
|
3467
|
+
reason: "cleanup_incomplete"
|
|
3468
|
+
};
|
|
3469
|
+
if (!record.pristineFingerprint) return {
|
|
3470
|
+
status: "retained",
|
|
3471
|
+
reason: "baseline_unverified"
|
|
3472
|
+
};
|
|
3473
|
+
await verifyRetainedWorkspace(record);
|
|
3474
|
+
if (await fingerprintDirectory(record.boundaryRoot) !== record.pristineFingerprint) return {
|
|
3475
|
+
status: "retained",
|
|
3476
|
+
reason: "workspace_changed"
|
|
3477
|
+
};
|
|
3478
|
+
if (record.sourceWorkingDirectory && !await sourceHasCommit(record)) return {
|
|
3479
|
+
status: "retained",
|
|
3480
|
+
reason: "source_commit_unavailable"
|
|
3481
|
+
};
|
|
3482
|
+
return (await retirePristineWorkspace(record, path)).status === "released" ? { status: "released" } : {
|
|
3483
|
+
status: "retained",
|
|
3484
|
+
reason: "workspace_changed"
|
|
3485
|
+
};
|
|
3486
|
+
});
|
|
3487
|
+
} finally {
|
|
3488
|
+
if (input.use) await releaseLock(input.use.lock);
|
|
3489
|
+
}
|
|
3490
|
+
}
|
|
3491
|
+
async function assertExecutionContextWorkspaceUse(input) {
|
|
3492
|
+
if (!executionContextAssignmentSchema.shape.id.safeParse(input.contextId).success) throw failure("workspace_identity_mismatch");
|
|
3493
|
+
const record = await readManifest(join(await canonicalDirectory(input.ledgerRootPath), "execution-context-workspace-records", `${input.contextId}.json`));
|
|
3494
|
+
if (record?.status !== "in_use" || record.identity.id !== input.contextId || record.activeDispatchId !== input.dispatchId || record.activeAttemptId !== input.attemptId || !record.containerRuntime) throw failure("workspace_execution_authority_mismatch");
|
|
3495
|
+
}
|
|
3496
|
+
async function runExecutionContextWorkspaceProjectShell(input) {
|
|
3497
|
+
const root = await canonicalDirectory(input.ledgerRootPath);
|
|
3498
|
+
return await withWorkspaceLock(root, input.contextId, async () => {
|
|
3499
|
+
await assertExecutionContextWorkspaceUse({
|
|
3500
|
+
ledgerRootPath: root,
|
|
3501
|
+
contextId: input.contextId,
|
|
3502
|
+
dispatchId: input.dispatchId,
|
|
3503
|
+
attemptId: input.attemptId
|
|
3504
|
+
});
|
|
3505
|
+
const record = await readManifest(join(root, "execution-context-workspace-records", `${input.contextId}.json`));
|
|
3506
|
+
if (record?.boundaryRoot !== input.boundaryRoot || record.workingDirectory !== input.workspacePath || record.containerRuntime?.binary !== input.runtime.binary || record.containerRuntime.engineIdentity !== input.runtime.engineIdentity || record.containerRuntime.kind !== input.runtime.kind) throw failure("workspace_execution_authority_mismatch");
|
|
3507
|
+
return await runClaudeProjectContainerCommand({
|
|
3508
|
+
command: input.command,
|
|
3509
|
+
contextId: input.contextId,
|
|
3510
|
+
runtime: input.runtime,
|
|
3511
|
+
boundaryRoot: input.boundaryRoot,
|
|
3512
|
+
workspacePath: input.workspacePath
|
|
3513
|
+
});
|
|
3514
|
+
});
|
|
3515
|
+
}
|
|
3516
|
+
async function retirePristineWorkspace(record, recordPath) {
|
|
3517
|
+
if (!record.pristineFingerprint || await fingerprintDirectory(record.boundaryRoot) !== record.pristineFingerprint) throw failure("workspace_release_incomplete");
|
|
3518
|
+
const retiring = {
|
|
3519
|
+
...record,
|
|
3520
|
+
status: "retiring",
|
|
3521
|
+
retirement: {
|
|
3522
|
+
path: `${recordPath}.retired-${randomUUID()}`,
|
|
3523
|
+
pristineFingerprint: record.pristineFingerprint
|
|
3524
|
+
}
|
|
3525
|
+
};
|
|
3526
|
+
await writeManifest(recordPath, retiring, "r+");
|
|
3527
|
+
return await finishWorkspaceRetirement(retiring, recordPath);
|
|
3528
|
+
}
|
|
3529
|
+
async function finishWorkspaceRetirement(record, recordPath) {
|
|
3530
|
+
const retirement = record.retirement;
|
|
3531
|
+
if (!retirement) throw failure("workspace_release_incomplete");
|
|
3532
|
+
const live = await inspectPath(record.boundaryRoot);
|
|
3533
|
+
const retired = await inspectPath(retirement.path);
|
|
3534
|
+
if (live && retired || !(live || retired)) throw failure("workspace_release_incomplete");
|
|
3535
|
+
if (live) await moveWorkspace(record.boundaryRoot, retirement.path);
|
|
3536
|
+
if (await fingerprintDirectory(retirement.path) !== retirement.pristineFingerprint) {
|
|
3537
|
+
if (await inspectPath(record.boundaryRoot)) throw failure("workspace_release_incomplete");
|
|
3538
|
+
await moveWorkspace(retirement.path, record.boundaryRoot);
|
|
3539
|
+
const retained = {
|
|
3540
|
+
...record,
|
|
3541
|
+
retirement: void 0,
|
|
3542
|
+
status: "completed"
|
|
3543
|
+
};
|
|
3544
|
+
await writeManifest(recordPath, retained, "r+");
|
|
3545
|
+
return retained;
|
|
3546
|
+
}
|
|
3547
|
+
const released = {
|
|
3548
|
+
...record,
|
|
3549
|
+
status: "released"
|
|
3550
|
+
};
|
|
3551
|
+
await writeManifest(recordPath, released, "r+");
|
|
3552
|
+
return await removePristineRetirement(released, recordPath);
|
|
3553
|
+
}
|
|
3554
|
+
async function removePristineRetirement(record, recordPath) {
|
|
3555
|
+
const retirement = record.retirement;
|
|
3556
|
+
if (!retirement) return record;
|
|
3557
|
+
if (await inspectPath(retirement.path)) {
|
|
3558
|
+
if (await fingerprintDirectory(retirement.path) !== retirement.pristineFingerprint) return record;
|
|
3559
|
+
await rm(retirement.path, { recursive: true });
|
|
3560
|
+
}
|
|
3561
|
+
const removed = {
|
|
3562
|
+
...record,
|
|
3563
|
+
retirement: void 0
|
|
3564
|
+
};
|
|
3565
|
+
await writeManifest(recordPath, removed, "r+");
|
|
3566
|
+
return removed;
|
|
3567
|
+
}
|
|
3568
|
+
async function restoreReleasedWorkspace(record, recordPath) {
|
|
3569
|
+
if (record.sourceWorkingDirectory && !await sourceHasCommit(record)) throw failure("workspace_base_commit_unavailable");
|
|
3570
|
+
if (await inspectPath(record.boundaryRoot)) throw failure("workspace_already_exists");
|
|
3571
|
+
const restoring = {
|
|
3572
|
+
...record,
|
|
3573
|
+
status: "restoring",
|
|
3574
|
+
processSupervisionId: randomUUID()
|
|
3575
|
+
};
|
|
3576
|
+
await writeManifest(recordPath, restoring, "r+");
|
|
3577
|
+
await finishWorkspacePreparation(restoring, recordPath, true);
|
|
3578
|
+
}
|
|
3579
|
+
async function sourceHasCommit(record) {
|
|
3580
|
+
if (!(record.sourceWorkingDirectory && record.baseCommit)) return false;
|
|
3581
|
+
try {
|
|
3582
|
+
await git(record.sourceWorkingDirectory, [
|
|
3583
|
+
"cat-file",
|
|
3584
|
+
"-e",
|
|
3585
|
+
`${record.baseCommit}^{commit}`
|
|
3586
|
+
]);
|
|
3587
|
+
return true;
|
|
3588
|
+
} catch {
|
|
3589
|
+
return false;
|
|
3590
|
+
}
|
|
3591
|
+
}
|
|
3592
|
+
async function isUnchangedCheckout(record) {
|
|
3593
|
+
return await git(record.workingDirectory, ["rev-parse", "HEAD"]) === record.baseCommit && await git(record.workingDirectory, [
|
|
3594
|
+
"status",
|
|
3595
|
+
"--porcelain",
|
|
3596
|
+
"--untracked-files=all",
|
|
3597
|
+
"--ignored"
|
|
3598
|
+
]) === "";
|
|
3599
|
+
}
|
|
3600
|
+
async function finishMissingCheckoutFiles(record) {
|
|
3601
|
+
if (await git(record.workingDirectory, ["rev-parse", "HEAD"]) !== record.baseCommit) throw failure("workspace_creation_incomplete");
|
|
3602
|
+
if ((await git(record.workingDirectory, [
|
|
3603
|
+
"ls-files",
|
|
3604
|
+
"-v",
|
|
3605
|
+
"-z"
|
|
3606
|
+
])).split("\0").some((entry) => entry && !entry.startsWith("H "))) throw failure("workspace_creation_incomplete");
|
|
3607
|
+
const status = await git(record.workingDirectory, [
|
|
3608
|
+
"status",
|
|
3609
|
+
"--porcelain",
|
|
3610
|
+
"-z",
|
|
3611
|
+
"--untracked-files=all",
|
|
3612
|
+
"--ignored"
|
|
3613
|
+
]);
|
|
3614
|
+
if (!status) return;
|
|
3615
|
+
const changes = status.split("\0").filter(Boolean);
|
|
3616
|
+
if (changes.some((entry) => !entry.startsWith(" D "))) throw failure("workspace_creation_incomplete");
|
|
3617
|
+
const paths = changes.map((entry) => entry.slice(3));
|
|
3618
|
+
for (let offset = 0; offset < paths.length; offset += 100) await git(record.workingDirectory, [
|
|
3619
|
+
"checkout-index",
|
|
3620
|
+
"--",
|
|
3621
|
+
...paths.slice(offset, offset + 100)
|
|
3622
|
+
], record.processSupervisionId);
|
|
3623
|
+
}
|
|
3624
|
+
async function fingerprintDirectory(root) {
|
|
3625
|
+
const hash = createHash("sha256");
|
|
3626
|
+
const visit = async (directory) => {
|
|
3627
|
+
for (const name of (await readdir(directory)).sort()) {
|
|
3628
|
+
const path = join(directory, name);
|
|
3629
|
+
const info = await lstat(path);
|
|
3630
|
+
hash.update(JSON.stringify([
|
|
3631
|
+
relative(root, path),
|
|
3632
|
+
info.mode,
|
|
3633
|
+
info.nlink
|
|
3634
|
+
]));
|
|
3635
|
+
if (info.isSymbolicLink()) hash.update(JSON.stringify(await readlink(path)));
|
|
3636
|
+
else if (info.isDirectory()) await visit(path);
|
|
3637
|
+
else if (info.isFile() && info.nlink === 1) {
|
|
3638
|
+
hash.update(String(info.size));
|
|
3639
|
+
hash.update(await fingerprintFile(path));
|
|
3640
|
+
} else throw failure("workspace_git_metadata_changed");
|
|
3641
|
+
}
|
|
3642
|
+
};
|
|
3643
|
+
await visit(root);
|
|
3644
|
+
return hash.digest("hex");
|
|
3645
|
+
}
|
|
3646
|
+
async function fingerprintFile(path) {
|
|
3647
|
+
const hash = createHash("sha256");
|
|
3648
|
+
const file = await open(path, "r");
|
|
3649
|
+
try {
|
|
3650
|
+
const buffer = Buffer.alloc(64 * 1024);
|
|
3651
|
+
for (;;) {
|
|
3652
|
+
const { bytesRead } = await file.read(buffer);
|
|
3653
|
+
if (!bytesRead) break;
|
|
3654
|
+
hash.update(buffer.subarray(0, bytesRead));
|
|
3655
|
+
}
|
|
3656
|
+
} finally {
|
|
3657
|
+
await file.close();
|
|
3658
|
+
}
|
|
3659
|
+
return hash.digest("hex");
|
|
3660
|
+
}
|
|
3661
|
+
async function withWorkspaceLock(root, contextId, run) {
|
|
3662
|
+
if (!executionContextAssignmentSchema.shape.id.safeParse(contextId).success) throw failure("invalid_workspace_context");
|
|
3663
|
+
return await runWithHeldLock({
|
|
3664
|
+
lock: await acquireLock({
|
|
3665
|
+
openMeldProfileId: "execution-context-workspaces",
|
|
3666
|
+
key: contextId,
|
|
3667
|
+
pathResolution: { env: { OPENMELD_HOME: root } }
|
|
3668
|
+
}),
|
|
3669
|
+
run
|
|
3670
|
+
});
|
|
3671
|
+
}
|
|
3672
|
+
async function verifyRetainedWorkspace(manifest) {
|
|
3673
|
+
await canonicalDirectory(manifest.boundaryRoot);
|
|
3674
|
+
await canonicalDirectory(manifest.workingDirectory);
|
|
3675
|
+
if (manifest.sourceWorkingDirectory) await verifyOwnedRepository(manifest);
|
|
3676
|
+
return workspaceResult(manifest);
|
|
3677
|
+
}
|
|
3678
|
+
async function acquireWorkspaceExecutionLock(root, contextId) {
|
|
3679
|
+
return await acquireLock({
|
|
3680
|
+
openMeldProfileId: "execution-context-workspaces",
|
|
3681
|
+
key: `${contextId}-execution`,
|
|
3682
|
+
pathResolution: { env: { OPENMELD_HOME: root } }
|
|
3683
|
+
});
|
|
3684
|
+
}
|
|
3685
|
+
function workspaceResult(manifest) {
|
|
3686
|
+
return {
|
|
3687
|
+
workingDirectory: manifest.workingDirectory,
|
|
3688
|
+
boundaryRoot: manifest.boundaryRoot,
|
|
3689
|
+
...manifest.sourceWorkingDirectory ? { sourceWorkingDirectory: manifest.sourceWorkingDirectory } : {},
|
|
3690
|
+
...manifest.baseCommit ? { baseCommit: manifest.baseCommit } : {}
|
|
3691
|
+
};
|
|
3692
|
+
}
|
|
3693
|
+
function assertSourceAllowed(source, policy) {
|
|
3694
|
+
if (policy.mode === "only_selected_folders" && !policy.folders.some((folder) => within(source, folder))) throw failure("workspace_outside_computer_policy");
|
|
3695
|
+
if (policy.mode === "block_selected_folders" && policy.folders.some((folder) => within(source, folder) || within(folder, source))) throw failure("workspace_overlaps_blocked_folder");
|
|
3696
|
+
}
|
|
3697
|
+
async function resolveSourceCommit(source) {
|
|
3698
|
+
if (!await inspectPath(join(source, ".git"))) throw failure("project_repository_required");
|
|
3699
|
+
if (await realpath(await git(source, ["rev-parse", "--show-toplevel"])) !== source) throw failure("project_repository_root_required");
|
|
3700
|
+
const commit = await git(source, [
|
|
3701
|
+
"rev-parse",
|
|
3702
|
+
"--verify",
|
|
3703
|
+
"HEAD^{commit}"
|
|
3704
|
+
]).catch((cause) => {
|
|
3705
|
+
throw new Error("execution_context.project_commit_required", { cause });
|
|
3706
|
+
});
|
|
3707
|
+
if (!COMMIT_PATTERN.test(commit)) throw failure("project_commit_required");
|
|
3708
|
+
return commit;
|
|
3709
|
+
}
|
|
3710
|
+
async function verifyOwnedRepository(manifest) {
|
|
3711
|
+
const repository = join(manifest.boundaryRoot, "repository.git");
|
|
3712
|
+
await canonicalDirectory(repository);
|
|
3713
|
+
await assertPrivateMetadata(repository);
|
|
3714
|
+
if (await inspectPath(join(repository, "objects", "info", "alternates"))) throw failure("workspace_git_metadata_changed");
|
|
3715
|
+
const gitFile = join(manifest.workingDirectory, ".git");
|
|
3716
|
+
const gitFileInfo = await inspectPath(gitFile);
|
|
3717
|
+
if (!gitFileInfo?.isFile() || gitFileInfo.size > 4096) throw failure("workspace_git_metadata_changed");
|
|
3718
|
+
const expectedGitDir = join(repository, "worktrees", "worktree");
|
|
3719
|
+
const gitDirLine = (await readFile(gitFile, "utf8")).trim();
|
|
3720
|
+
const gitDir = gitDirLine.startsWith("gitdir: ") ? gitDirLine.slice(8) : null;
|
|
3721
|
+
if (!(gitDir && isAbsolute(gitDir)) || relative(expectedGitDir, gitDir)) throw failure("workspace_git_metadata_changed");
|
|
3722
|
+
if (await realpath(join(expectedGitDir, (await readFile(join(expectedGitDir, "commondir"), "utf8")).trim())) !== repository) throw failure("workspace_git_metadata_changed");
|
|
3723
|
+
if (await git(manifest.boundaryRoot, [
|
|
3724
|
+
`--git-dir=${repository}`,
|
|
3725
|
+
"rev-parse",
|
|
3726
|
+
"--verify",
|
|
3727
|
+
"refs/openmeld/base^{commit}"
|
|
3728
|
+
]) !== manifest.baseCommit) throw failure("workspace_base_commit_changed");
|
|
3729
|
+
}
|
|
3730
|
+
async function assertPrivateMetadata(path) {
|
|
3731
|
+
for (const entry of await readdir(path, { withFileTypes: true })) {
|
|
3732
|
+
if (entry.isSymbolicLink()) throw failure("workspace_is_symlink");
|
|
3733
|
+
if (entry.isDirectory()) await assertPrivateMetadata(join(path, entry.name));
|
|
3734
|
+
else if ((await lstat(join(path, entry.name))).nlink !== 1) throw failure("workspace_git_metadata_changed");
|
|
3735
|
+
}
|
|
3736
|
+
}
|
|
3737
|
+
async function readManifest(path) {
|
|
3738
|
+
const info = await inspectPath(path);
|
|
3739
|
+
if (!info) return null;
|
|
3740
|
+
if (!info.isFile() || info.size > 32768) throw failure("workspace_record_invalid");
|
|
3741
|
+
let content;
|
|
3742
|
+
try {
|
|
3743
|
+
content = JSON.parse(await readFile(path, "utf8"));
|
|
3744
|
+
} catch {
|
|
3745
|
+
throw failure("workspace_record_invalid");
|
|
3746
|
+
}
|
|
3747
|
+
const parsed = manifestSchema.safeParse(content);
|
|
3748
|
+
if (!parsed.success) throw failure("workspace_record_invalid");
|
|
3749
|
+
const retirement = parsed.data.retirement;
|
|
3750
|
+
const prefix = `${path}.retired-`;
|
|
3751
|
+
if (parsed.data.status === "retiring" && !retirement || retirement && !(retirement.path.startsWith(prefix) && z.uuid().safeParse(retirement.path.slice(prefix.length)).success)) throw failure("workspace_record_invalid");
|
|
3752
|
+
return parsed.data;
|
|
3753
|
+
}
|
|
3754
|
+
async function writeManifest(path, manifest, flags) {
|
|
3755
|
+
const temporaryPath = flags === "wx" ? path : `${path}.${randomUUID()}.tmp`;
|
|
3756
|
+
const file = await open(temporaryPath, "wx", 384);
|
|
3757
|
+
try {
|
|
3758
|
+
await file.writeFile(`${JSON.stringify({
|
|
3759
|
+
...manifest,
|
|
3760
|
+
version: 2
|
|
3761
|
+
})}\n`, "utf8");
|
|
3762
|
+
await file.sync();
|
|
3763
|
+
} finally {
|
|
3764
|
+
await file.close();
|
|
3765
|
+
}
|
|
3766
|
+
if (flags !== "wx") await rename(temporaryPath, path);
|
|
3767
|
+
await syncDirectory(dirname(path));
|
|
3768
|
+
}
|
|
3769
|
+
async function moveWorkspace(from, to) {
|
|
3770
|
+
await rename(from, to);
|
|
3771
|
+
await syncDirectory(dirname(from));
|
|
3772
|
+
await syncDirectory(dirname(to));
|
|
3773
|
+
}
|
|
3774
|
+
async function inspectPath(path) {
|
|
3775
|
+
const info = await lstat(path).catch((error) => {
|
|
3776
|
+
if (error.code === "ENOENT") return null;
|
|
3777
|
+
throw error;
|
|
3778
|
+
});
|
|
3779
|
+
if (info?.isSymbolicLink()) throw failure("workspace_is_symlink");
|
|
3780
|
+
return info;
|
|
3781
|
+
}
|
|
3782
|
+
async function canonicalDirectory(path) {
|
|
3783
|
+
if (!(await inspectPath(path))?.isDirectory()) throw failure("workspace_directory_unavailable");
|
|
3784
|
+
return await realpath(path);
|
|
3785
|
+
}
|
|
3786
|
+
async function ensureDirectory(path, recursive = false) {
|
|
3787
|
+
await inspectPath(path);
|
|
3788
|
+
await mkdir(path, {
|
|
3789
|
+
recursive,
|
|
3790
|
+
mode: 448
|
|
3791
|
+
}).catch((error) => {
|
|
3792
|
+
if (error.code !== "EEXIST") throw error;
|
|
3793
|
+
});
|
|
3794
|
+
return await canonicalDirectory(path);
|
|
3795
|
+
}
|
|
3796
|
+
function within(child, parent) {
|
|
3797
|
+
const path = relative(resolve(parent), child);
|
|
3798
|
+
return path === "" || path !== ".." && !path.startsWith(`..${sep}`) && !isAbsolute(path);
|
|
3799
|
+
}
|
|
3800
|
+
const GIT_SETTINGS = {
|
|
3801
|
+
"core.hooksPath": "/dev/null",
|
|
3802
|
+
"core.attributesFile": "/dev/null",
|
|
3803
|
+
"core.fsmonitor": "false",
|
|
3804
|
+
"credential.helper": "",
|
|
3805
|
+
"protocol.allow": "never",
|
|
3806
|
+
"protocol.file.allow": "always",
|
|
3807
|
+
"fetch.recurseSubmodules": "false",
|
|
3808
|
+
"submodule.recurse": "false",
|
|
3809
|
+
"uploadpack.packObjectsHook": "",
|
|
3810
|
+
"gc.auto": "0",
|
|
3811
|
+
"maintenance.auto": "false"
|
|
3812
|
+
};
|
|
3813
|
+
async function git(cwd, args, processSupervisionId) {
|
|
3814
|
+
const env = Object.fromEntries(Object.entries(process.env).filter(([key]) => !key.startsWith("GIT_")));
|
|
3815
|
+
Object.assign(env, {
|
|
3816
|
+
...processSupervisionId ? { OPENMELD_PROCESS_SUPERVISION_ID: processSupervisionId } : {},
|
|
3817
|
+
GIT_CONFIG_NOSYSTEM: "1",
|
|
3818
|
+
GIT_CONFIG_SYSTEM: "/dev/null",
|
|
3819
|
+
GIT_CONFIG_GLOBAL: "/dev/null",
|
|
3820
|
+
GIT_ATTR_NOSYSTEM: "1",
|
|
3821
|
+
GIT_NO_REPLACE_OBJECTS: "1",
|
|
3822
|
+
GIT_NO_LAZY_FETCH: "1",
|
|
3823
|
+
GIT_ALLOW_PROTOCOL: "file",
|
|
3824
|
+
GIT_TERMINAL_PROMPT: "0",
|
|
3825
|
+
GIT_CONFIG_COUNT: String(Object.keys(GIT_SETTINGS).length)
|
|
3826
|
+
});
|
|
3827
|
+
for (const [index, [key, value]] of Object.entries(GIT_SETTINGS).entries()) {
|
|
3828
|
+
env[`GIT_CONFIG_KEY_${index}`] = key;
|
|
3829
|
+
env[`GIT_CONFIG_VALUE_${index}`] = value;
|
|
3830
|
+
}
|
|
3831
|
+
return (await execFileAsync$1("git", args, {
|
|
3832
|
+
cwd,
|
|
3833
|
+
env,
|
|
3834
|
+
encoding: "utf8",
|
|
3835
|
+
timeout: 3e4,
|
|
3836
|
+
maxBuffer: 1024 * 1024
|
|
3837
|
+
})).stdout.trimEnd();
|
|
3838
|
+
}
|
|
3839
|
+
function failure(code) {
|
|
3840
|
+
return /* @__PURE__ */ new Error(`execution_context.${code}`);
|
|
3841
|
+
}
|
|
3842
|
+
//#endregion
|
|
3843
|
+
//#region src/space/action-command.ts
|
|
3844
|
+
const FINAL_SAFE_STATUSES = /* @__PURE__ */ new Set([
|
|
3845
|
+
"blocked",
|
|
3846
|
+
"done",
|
|
3847
|
+
"handoff",
|
|
3848
|
+
"needs_input"
|
|
3849
|
+
]);
|
|
3850
|
+
const DISPATCH_ACTION_CLI_PLACEHOLDER = "<DISPATCH_ACTION_CLI>";
|
|
3851
|
+
const SPACE_ACTION_REPLY_TIMING_EVENT = "openmeld.space_action.reply_timing";
|
|
3852
|
+
function runSpaceActionReply(input) {
|
|
3853
|
+
const text = resolveReplyMessageText(input);
|
|
3854
|
+
const result = recordDispatchSpaceReply({
|
|
3855
|
+
filePaths: input.filePaths,
|
|
3856
|
+
messagePartCount: input.messageParts.length,
|
|
3857
|
+
mentionTargets: input.mentionTargets,
|
|
3858
|
+
referenceTargets: input.referenceTargets,
|
|
3859
|
+
text,
|
|
3860
|
+
wakeTargets: input.wakeTargets
|
|
3861
|
+
});
|
|
3862
|
+
if (result instanceof Promise) return result.then((record) => {
|
|
3863
|
+
emitActionRecorded(record);
|
|
3864
|
+
});
|
|
3865
|
+
emitActionRecorded(result);
|
|
3866
|
+
}
|
|
3867
|
+
function recordDispatchSpaceReply(input) {
|
|
3868
|
+
const timing = createSpaceActionReplyTiming();
|
|
3869
|
+
timing.emit("command_start", void 0, {
|
|
3870
|
+
messagePartCount: input.messagePartCount ?? 0,
|
|
3871
|
+
mentionTargetCount: input.mentionTargets?.length ?? 0,
|
|
3872
|
+
referenceTargetCount: input.referenceTargets?.length ?? 0,
|
|
3873
|
+
wakeTargetCount: input.wakeTargets?.length ?? 0
|
|
3874
|
+
});
|
|
3875
|
+
const filePaths = input.filePaths ?? [];
|
|
3876
|
+
if (filePaths.length && input.files?.length) throw new DispatchSpaceActionRejectedError("space_action.invalid_input", "Choose uploaded paths or saved file revisions for this reply.");
|
|
3877
|
+
const context = filePaths.length > 0 ? readDispatchSpaceActionContextFromEnv(input.env) : readDispatchSpaceActionContextForReceiptFromEnv(input.env);
|
|
3878
|
+
timing.emit("context_ready", context);
|
|
3879
|
+
assertAllowedAction(context, "post_message");
|
|
3880
|
+
const text = normalizeRequiredText(input.text, "Reply message is required.");
|
|
3881
|
+
const targetIds = resolveMessageEnvelopeTargetIds({
|
|
3882
|
+
context,
|
|
3883
|
+
mentionTargets: input.mentionTargets ?? [],
|
|
3884
|
+
wakeTargets: input.wakeTargets ?? [],
|
|
3885
|
+
referenceTargets: input.referenceTargets ?? []
|
|
3886
|
+
});
|
|
3887
|
+
timing.emit("targets_resolved", context, {
|
|
3888
|
+
activationTargetCount: targetIds.activationTargets.length,
|
|
3889
|
+
messageLength: text.length,
|
|
3890
|
+
mentionTargetCount: targetIds.mentionTargets.length,
|
|
3891
|
+
referenceTargetCount: targetIds.referenceTargets.length
|
|
3892
|
+
});
|
|
3893
|
+
if (filePaths.length === 0) return writeSpaceActionReplyRecord({
|
|
3894
|
+
context,
|
|
3895
|
+
files: input.files ? [...input.files] : [],
|
|
3896
|
+
targetIds,
|
|
3897
|
+
text,
|
|
3898
|
+
timing
|
|
3899
|
+
});
|
|
3900
|
+
return uploadAndRecordSpaceActionReply({
|
|
3901
|
+
context,
|
|
3902
|
+
filePaths,
|
|
3903
|
+
targetIds,
|
|
3904
|
+
text,
|
|
3905
|
+
timing
|
|
3906
|
+
});
|
|
3907
|
+
}
|
|
3908
|
+
function resolveReplyMessageText(input) {
|
|
3909
|
+
const hasInlineMessage = input.messageParts.length > 0;
|
|
3910
|
+
const messageFilePath = input.messageFilePath?.trim();
|
|
3911
|
+
if (hasInlineMessage && messageFilePath) throw new Error("Choose either a reply message or --message-file, not both.");
|
|
3912
|
+
if (!messageFilePath) return normalizeRequiredJoinedText(input.messageParts, "Reply message is required.");
|
|
3913
|
+
return normalizeRequiredText(readFileSync(messageFilePath === "-" ? 0 : messageFilePath, { encoding: "utf8" }), "Reply message is required.");
|
|
3914
|
+
}
|
|
3915
|
+
async function uploadAndRecordSpaceActionReply(input) {
|
|
3916
|
+
const files = await uploadSpaceActionReplyFiles({
|
|
3917
|
+
context: input.context,
|
|
3918
|
+
filePaths: input.filePaths
|
|
3919
|
+
});
|
|
3920
|
+
return writeSpaceActionReplyRecord({
|
|
3921
|
+
...input,
|
|
3922
|
+
files
|
|
3923
|
+
});
|
|
3924
|
+
}
|
|
3925
|
+
function writeSpaceActionReplyRecord(input) {
|
|
3926
|
+
const record = recordAction({
|
|
3927
|
+
action: {
|
|
3928
|
+
type: "post_message",
|
|
3929
|
+
...input.files.length > 0 ? { files: [...input.files] } : {},
|
|
3930
|
+
messageEnvelope: {
|
|
3931
|
+
activationTargets: input.targetIds.activationTargets,
|
|
3932
|
+
mentionTargets: input.targetIds.mentionTargets,
|
|
3933
|
+
referenceTargets: input.targetIds.referenceTargets,
|
|
3934
|
+
...input.context.replyToSignalId ? { replyToSignalId: input.context.replyToSignalId } : {},
|
|
3935
|
+
text: input.text
|
|
3936
|
+
}
|
|
3937
|
+
},
|
|
3938
|
+
command: "reply",
|
|
3939
|
+
context: input.context
|
|
3940
|
+
});
|
|
3941
|
+
input.timing.emit("record_written", input.context, { actionType: record.action.type });
|
|
3942
|
+
input.timing.emit("done", input.context, { actionType: record.action.type });
|
|
3943
|
+
return record;
|
|
3944
|
+
}
|
|
3945
|
+
async function uploadSpaceActionReplyFiles(input) {
|
|
3946
|
+
const prepared = await prepareRequestedSpaceSendFiles({ filePaths: input.filePaths });
|
|
3947
|
+
if (prepared.length === 0) return [];
|
|
3948
|
+
return await uploadPreparedSpaceFiles({
|
|
3949
|
+
files: prepared,
|
|
3950
|
+
requestContext: buildOpenMeldRequestContextFromProfile({ openMeldProfile: await requireOpenMeldProfile({
|
|
3951
|
+
openMeldProfileId: input.context.targetProfileId,
|
|
3952
|
+
persistSelection: false,
|
|
3953
|
+
authAction: "sharing files in a Space reply"
|
|
3954
|
+
}) }),
|
|
3955
|
+
spaceApi: createCliSpaceApi(await resolveBaseUrl()),
|
|
3956
|
+
spaceId: input.context.spaceId
|
|
3957
|
+
});
|
|
3958
|
+
}
|
|
3959
|
+
function runSpaceActionWake(input) {
|
|
3960
|
+
const context = readDispatchSpaceActionContextForReceiptFromEnv();
|
|
3961
|
+
assertAllowedAction(context, "post_message");
|
|
3962
|
+
const text = normalizeRequiredJoinedText(input.messageParts, "Wake handoff message is required.");
|
|
3963
|
+
recordAction({
|
|
3964
|
+
action: {
|
|
3965
|
+
type: "post_message",
|
|
3966
|
+
messageEnvelope: {
|
|
3967
|
+
activationTargets: resolveTargetProfileIds({
|
|
3968
|
+
context,
|
|
3969
|
+
relation: "wake",
|
|
3970
|
+
targets: [input.target]
|
|
3971
|
+
}),
|
|
3972
|
+
mentionTargets: [],
|
|
3973
|
+
referenceTargets: [],
|
|
3974
|
+
...context.replyToSignalId ? { replyToSignalId: context.replyToSignalId } : {},
|
|
3975
|
+
text
|
|
3976
|
+
}
|
|
3977
|
+
},
|
|
3978
|
+
command: "wake",
|
|
3979
|
+
context
|
|
3980
|
+
});
|
|
3981
|
+
}
|
|
3982
|
+
function runSpaceActionStatus(input) {
|
|
3983
|
+
emitActionRecorded(recordDispatchSpaceStatus({
|
|
3984
|
+
status: input.status,
|
|
3985
|
+
text: normalizeRequiredJoinedText(input.textParts, "Status text is required.")
|
|
3986
|
+
}));
|
|
3987
|
+
}
|
|
3988
|
+
/** Legacy CLI/in-flight compatibility only; not offered to current Agent tool catalogs. */
|
|
3989
|
+
function recordDispatchSpaceStatus(input) {
|
|
3990
|
+
const context = readDispatchSpaceActionContextForReceiptFromEnv();
|
|
3991
|
+
assertAllowedAction(context, "post_status");
|
|
3992
|
+
return recordAction({
|
|
3993
|
+
action: {
|
|
3994
|
+
type: "post_status",
|
|
3995
|
+
status: normalizeStatus(input.status),
|
|
3996
|
+
text: normalizeRequiredText(input.text, "Status text is required.")
|
|
3997
|
+
},
|
|
3998
|
+
command: "status",
|
|
3999
|
+
context
|
|
4000
|
+
});
|
|
4001
|
+
}
|
|
4002
|
+
function runSpaceActionSilent(input) {
|
|
4003
|
+
emitActionRecorded(recordDispatchSpaceSilent(input));
|
|
4004
|
+
}
|
|
4005
|
+
function recordDispatchSpaceSilent(input) {
|
|
4006
|
+
const context = readDispatchSpaceActionContextForReceiptFromEnv(input.env);
|
|
4007
|
+
assertAllowedAction(context, "stay_silent");
|
|
4008
|
+
return recordAction({
|
|
4009
|
+
action: {
|
|
4010
|
+
type: "stay_silent",
|
|
4011
|
+
reason: normalizeRequiredText(input.reason, "A silent action requires --reason.")
|
|
4012
|
+
},
|
|
4013
|
+
command: "silent",
|
|
4014
|
+
context
|
|
4015
|
+
});
|
|
4016
|
+
}
|
|
4017
|
+
function runSpaceActionTargets() {
|
|
4018
|
+
const targetReference = requireLoadedTargets(readDispatchSpaceActionContextFromEnv().sendTargetReference);
|
|
4019
|
+
const wakeTargets = targetReference.projection.filter((target) => target.allowedRelations.wake);
|
|
4020
|
+
const referenceTargets = targetReference.projection.filter((target) => target.allowedRelations.reference);
|
|
4021
|
+
const mentionTargets = targetReference.projection.filter((target) => target.allowedRelations.mention);
|
|
4022
|
+
outLine("Wake targets:");
|
|
4023
|
+
if (wakeTargets.length === 0) outLine("- none");
|
|
4024
|
+
else for (const target of wakeTargets) outLine(`- ${formatTargetLine(target, "wake")}`);
|
|
4025
|
+
outLine("");
|
|
4026
|
+
outLine("Mention targets:");
|
|
4027
|
+
if (mentionTargets.length === 0) outLine("- none");
|
|
4028
|
+
else for (const target of mentionTargets) outLine(`- ${formatTargetLine(target, "mention")}`);
|
|
4029
|
+
outLine("");
|
|
4030
|
+
outLine("Reference targets:");
|
|
4031
|
+
if (referenceTargets.length === 0) outLine("- none");
|
|
4032
|
+
else for (const target of referenceTargets) outLine(`- ${formatTargetLine(target, "reference")}`);
|
|
4033
|
+
}
|
|
4034
|
+
function runSpaceActionHelp() {
|
|
4035
|
+
let context = null;
|
|
4036
|
+
try {
|
|
4037
|
+
context = readDispatchSpaceActionContextFromEnv();
|
|
4038
|
+
} catch {
|
|
4039
|
+
context = null;
|
|
4040
|
+
}
|
|
4041
|
+
outLine("OpenMeld Space Action commands:");
|
|
4042
|
+
outLine(formatSpaceActionHelpExample("Reply", "reply \"message\"", context));
|
|
4043
|
+
outLine(formatSpaceActionHelpExample("Handoff and wake another agent", "wake <agent-profile-id> \"summary and request\"", context));
|
|
4044
|
+
outLine(formatSpaceActionHelpExample("Reply and also wake another agent", "reply --wake <agent-profile-id> \"message\"", context));
|
|
4045
|
+
outLine(formatSpaceActionHelpExample("Reply and reference another agent", "reply --reference <agent-profile-id> \"message\"", context));
|
|
4046
|
+
outLine(formatSpaceActionHelpExample("Reply and mention a person", "reply --mention <human-profile-id> \"message\"", context));
|
|
4047
|
+
outLine(formatSpaceActionHelpExample("Stay silent", "silent --reason \"No public reply is needed.\"", context));
|
|
4048
|
+
outLine(formatSpaceActionHelpExample("Current targets", "targets", context));
|
|
4049
|
+
outLine(formatSpaceActionHelpExample("Help", "help", context));
|
|
4050
|
+
outLine("");
|
|
4051
|
+
outLine("Run exactly one final action for the current Wake. OpenMeld posts the accepted action to the Space.");
|
|
4052
|
+
if (!context) {
|
|
4053
|
+
outLine("");
|
|
4054
|
+
outLine(`No active dispatch context was found. ${DISPATCH_ACTION_CLI_PLACEHOLDER} is a placeholder for the exact Space Action command prefix printed in an OpenMeld Space Wake dispatch prompt.`);
|
|
4055
|
+
outLine("Write actions and targets require an active OpenMeld Space Wake dispatch. Do not guess the command prefix or rewrite it to npx.");
|
|
4056
|
+
return;
|
|
4057
|
+
}
|
|
4058
|
+
outLine("");
|
|
4059
|
+
outLine(`Active dispatch: ${context.dispatchId}`);
|
|
4060
|
+
outLine(`Current target profile: ${context.targetProfileId}`);
|
|
4061
|
+
}
|
|
4062
|
+
function formatSpaceActionHelpExample(label, action, context) {
|
|
4063
|
+
return `- ${label}: ${formatSpaceActionHelpCommand(action, context)}`;
|
|
4064
|
+
}
|
|
4065
|
+
function formatSpaceActionHelpCommand(action, context) {
|
|
4066
|
+
if (!context) return `${DISPATCH_ACTION_CLI_PLACEHOLDER} space action ${action}`;
|
|
4067
|
+
return formatOpenMeldCliCommand(`openmeld space action ${action}`);
|
|
4068
|
+
}
|
|
4069
|
+
function recordAction(input) {
|
|
4070
|
+
return writeDispatchSpaceActionRecord(input);
|
|
4071
|
+
}
|
|
4072
|
+
function emitActionRecorded(record) {
|
|
4073
|
+
outLine(`Action recorded: OpenMeld saved this unpublished Space action proposal (${record.action.type}) for the current Wake.`);
|
|
4074
|
+
}
|
|
4075
|
+
function createSpaceActionReplyTiming() {
|
|
4076
|
+
const startedAt = performance.now();
|
|
4077
|
+
let previousAt = startedAt;
|
|
4078
|
+
const commandStartProcessUptimeMs = Math.round(startedAt);
|
|
4079
|
+
return { emit: (phase, context, fields = {}) => {
|
|
4080
|
+
const now = performance.now();
|
|
4081
|
+
const elapsedMs = Math.max(0, Math.round(now - startedAt));
|
|
4082
|
+
const durationMs = Math.max(0, Math.round(now - previousAt));
|
|
4083
|
+
previousAt = now;
|
|
4084
|
+
process.stderr.write(`[${SPACE_ACTION_REPLY_TIMING_EVENT}] ${JSON.stringify({
|
|
4085
|
+
event: SPACE_ACTION_REPLY_TIMING_EVENT,
|
|
4086
|
+
phase,
|
|
4087
|
+
elapsedMs,
|
|
4088
|
+
durationMs,
|
|
4089
|
+
commandStartProcessUptimeMs,
|
|
4090
|
+
attemptId: context?.attemptId ?? null,
|
|
4091
|
+
dispatchId: context?.dispatchId ?? null,
|
|
4092
|
+
spaceId: context?.spaceId ?? null,
|
|
4093
|
+
targetProfileId: context?.targetProfileId ?? null,
|
|
4094
|
+
taskId: context?.taskId ?? null,
|
|
4095
|
+
...fields
|
|
4096
|
+
})}\n`);
|
|
4097
|
+
} };
|
|
4098
|
+
}
|
|
4099
|
+
function resolveMessageEnvelopeTargetIds(input) {
|
|
4100
|
+
const targets = {
|
|
4101
|
+
activationTargets: resolveTargetProfileIds({
|
|
4102
|
+
context: input.context,
|
|
4103
|
+
relation: "wake",
|
|
4104
|
+
targets: input.wakeTargets
|
|
4105
|
+
}),
|
|
4106
|
+
mentionTargets: resolveTargetProfileIds({
|
|
4107
|
+
context: input.context,
|
|
4108
|
+
relation: "mention",
|
|
4109
|
+
targets: input.mentionTargets
|
|
4110
|
+
}),
|
|
4111
|
+
referenceTargets: resolveTargetProfileIds({
|
|
4112
|
+
context: input.context,
|
|
4113
|
+
relation: "reference",
|
|
4114
|
+
targets: input.referenceTargets
|
|
4115
|
+
})
|
|
4116
|
+
};
|
|
4117
|
+
assertDistinctMessageEnvelopeTargets(targets);
|
|
4118
|
+
return targets;
|
|
4119
|
+
}
|
|
4120
|
+
function assertDistinctMessageEnvelopeTargets(input) {
|
|
4121
|
+
const seen = /* @__PURE__ */ new Map();
|
|
4122
|
+
const relationTargets = [
|
|
4123
|
+
["wake", input.activationTargets],
|
|
4124
|
+
["reference", input.referenceTargets],
|
|
4125
|
+
["mention", input.mentionTargets]
|
|
4126
|
+
];
|
|
4127
|
+
for (const [relation, profileIds] of relationTargets) for (const profileId of profileIds) {
|
|
4128
|
+
const existingRelation = seen.get(profileId);
|
|
4129
|
+
if (!existingRelation) {
|
|
4130
|
+
seen.set(profileId, relation);
|
|
4131
|
+
continue;
|
|
4132
|
+
}
|
|
4133
|
+
if (existingRelation === relation) throw new DispatchSpaceActionRejectedError("space_action.invalid_input", `The same target (${profileId}) was listed more than once as ${formatRelation(relation)}. Choose it once, or send separate messages.`);
|
|
4134
|
+
throw new DispatchSpaceActionRejectedError("space_action.invalid_input", `The same target (${profileId}) cannot be both ${formatRelation(existingRelation)} and ${formatRelation(relation)} in one Space Action. Choose one relationship, or send separate messages.`);
|
|
4135
|
+
}
|
|
4136
|
+
}
|
|
4137
|
+
function assertAllowedAction(context, actionType) {
|
|
4138
|
+
const allowedActions = context.spaceContractSnapshot?.allowedActions ?? null;
|
|
4139
|
+
if (allowedActions && !allowedActions.includes(actionType)) throw new DispatchSpaceActionRejectedError("space_action.not_allowed", `This Space does not allow ${actionType}. Do not retry it through another tool or transport.`);
|
|
4140
|
+
}
|
|
4141
|
+
function normalizeStatus(value) {
|
|
4142
|
+
const normalized = value.trim().toLowerCase();
|
|
4143
|
+
if (normalized === "working") throw new Error("`working` would close the current Wake. Non-final progress updates are not supported by OpenMeld Space Action yet.");
|
|
4144
|
+
if (normalized === "blocked" || normalized === "done" || normalized === "handoff" || normalized === "needs_input") return normalized;
|
|
4145
|
+
if (!FINAL_SAFE_STATUSES.has(normalized)) throw new Error("Status must be one of: done, blocked, needs_input, handoff.");
|
|
4146
|
+
return normalized;
|
|
4147
|
+
}
|
|
4148
|
+
function resolveTargetProfileIds(input) {
|
|
4149
|
+
return [...new Set(input.targets)].map((target) => resolveTargetProfileId({
|
|
4150
|
+
context: input.context,
|
|
4151
|
+
relation: input.relation,
|
|
4152
|
+
target
|
|
4153
|
+
}));
|
|
4154
|
+
}
|
|
4155
|
+
function resolveTargetProfileId(input) {
|
|
4156
|
+
const targetReference = requireLoadedTargets(input.context.sendTargetReference);
|
|
4157
|
+
const query = normalizeRequiredText(input.target, "Target is required.");
|
|
4158
|
+
const selected = targetReference.projection.find((target) => target.profileId === query);
|
|
4159
|
+
if (!selected) throw new DispatchSpaceActionRejectedError("space_action.invalid_input", `No current Space target has Profile ID "${query}". Use a full Profile ID from this Wake's current send target reference, or omit the target for a plain reply.`);
|
|
4160
|
+
if (!selected.allowedRelations[input.relation]) throw new DispatchSpaceActionRejectedError("space_action.not_allowed", `${formatRelation(input.relation)} is not allowed for ${selected.primaryMentionLabel}. Do not bypass this restriction through another transport.`);
|
|
4161
|
+
return selected.profileId;
|
|
4162
|
+
}
|
|
4163
|
+
function requireLoadedTargets(targetReference) {
|
|
4164
|
+
if (targetReference.status === "loaded") return targetReference;
|
|
4165
|
+
throw new DispatchSpaceActionRejectedError("space_action.targets_unavailable", `Current Space targets are not available for this dispatch: ${targetReference.errorMessage}. Do not guess recipients. A plain reply needs no recipients.`);
|
|
4166
|
+
}
|
|
4167
|
+
function formatTargetLine(target, relation) {
|
|
4168
|
+
const relationLabel = formatRelation(relation);
|
|
4169
|
+
return `${target.primaryMentionLabel} · ${target.profileKind} · ${relationLabel} · ${target.profileId}`;
|
|
4170
|
+
}
|
|
4171
|
+
function formatRelation(relation) {
|
|
4172
|
+
switch (relation) {
|
|
4173
|
+
case "wake": return "Wake";
|
|
4174
|
+
case "reference": return "Reference";
|
|
4175
|
+
default: return relation;
|
|
4176
|
+
}
|
|
4177
|
+
}
|
|
4178
|
+
function normalizeRequiredJoinedText(parts, errorMessage) {
|
|
4179
|
+
return normalizeRequiredText(parts.join(" "), errorMessage);
|
|
4180
|
+
}
|
|
4181
|
+
function normalizeRequiredText(value, errorMessage) {
|
|
4182
|
+
const normalized = String(value ?? "").trim();
|
|
4183
|
+
if (normalized.length === 0) throw new DispatchSpaceActionRejectedError("space_action.invalid_input", errorMessage);
|
|
4184
|
+
return normalized;
|
|
4185
|
+
}
|
|
4186
|
+
//#endregion
|
|
4187
|
+
//#region src/space/dispatch-file-read.ts
|
|
4188
|
+
const execFileAsync = promisify(execFile);
|
|
4189
|
+
/** Keep file access outside the controller sandbox, under the existing CLI auth contract. */
|
|
4190
|
+
async function readDispatchSpaceFile(input) {
|
|
4191
|
+
const env = input.env ?? process.env;
|
|
4192
|
+
if (env["OPENMELD_DISPATCH_RUNTIME_MODE"] !== "dispatch-owned-runtime") throw new Error("Space file tools require an active OpenMeld dispatch runtime.");
|
|
4193
|
+
const context = readDispatchSpaceActionContextFromEnv(env);
|
|
4194
|
+
const command = resolveDispatchSpaceActionMcpServer(env);
|
|
4195
|
+
if (!command) throw new Error("No active OpenMeld dispatch file reader was found.");
|
|
4196
|
+
const fileId = spaceFileEntryIdSchema.parse(input.fileId);
|
|
4197
|
+
const revision = spaceFileRevisionNumberSchema.optional().parse(input.revision);
|
|
4198
|
+
const args = [
|
|
4199
|
+
"space",
|
|
4200
|
+
"files",
|
|
4201
|
+
"read",
|
|
4202
|
+
"--profile",
|
|
4203
|
+
context.targetProfileId,
|
|
4204
|
+
"--json",
|
|
4205
|
+
"--view",
|
|
4206
|
+
"agent",
|
|
4207
|
+
...revision === void 0 ? [] : ["--revision", String(revision)],
|
|
4208
|
+
"--",
|
|
4209
|
+
context.spaceId,
|
|
4210
|
+
fileId
|
|
4211
|
+
];
|
|
4212
|
+
const { stdout } = await execFileAsync(command.commandPath, args, {
|
|
4213
|
+
env,
|
|
4214
|
+
encoding: "utf8",
|
|
4215
|
+
maxBuffer: 8 * 1024 * 1024,
|
|
4216
|
+
timeout: 3e4
|
|
4217
|
+
}).catch((error) => {
|
|
4218
|
+
if (error && typeof error === "object" && "stdout" in error && typeof error.stdout === "string") {
|
|
4219
|
+
let value;
|
|
4220
|
+
try {
|
|
4221
|
+
value = JSON.parse(error.stdout);
|
|
4222
|
+
} catch {}
|
|
4223
|
+
const parsed = cliJsonErrorEnvelopeSchema.safeParse(value);
|
|
4224
|
+
if (parsed.success) throw new Error(JSON.stringify(parsed.data), { cause: error });
|
|
4225
|
+
}
|
|
4226
|
+
throw error;
|
|
4227
|
+
});
|
|
4228
|
+
const result = readSpaceFileTextResponseSchema.parse(JSON.parse(stdout));
|
|
4229
|
+
if (result.file.spaceId !== context.spaceId || result.file.id !== fileId || revision !== void 0 && result.revision !== revision) throw new Error("The Space file response does not match the requested file revision.");
|
|
4230
|
+
return result;
|
|
4231
|
+
}
|
|
4232
|
+
//#endregion
|
|
4233
|
+
//#region src/space/dispatch-history-read.ts
|
|
4234
|
+
/** Read the current Wake's bounded Space history through the existing member API. */
|
|
4235
|
+
async function readDispatchSpaceHistory(env = process.env) {
|
|
4236
|
+
if (env["OPENMELD_DISPATCH_RUNTIME_MODE"] !== "dispatch-owned-runtime") throw new Error("Space history tools require an active OpenMeld dispatch runtime.");
|
|
4237
|
+
const context = readDispatchSpaceActionContextFromEnv(env);
|
|
4238
|
+
const profile = await requireOpenMeldProfile({
|
|
4239
|
+
openMeldProfileId: context.targetProfileId,
|
|
4240
|
+
persistSelection: false,
|
|
4241
|
+
authAction: "reading the current Wake's Space history"
|
|
4242
|
+
});
|
|
4243
|
+
if (profile.openMeldProfileId !== context.targetProfileId || profile.profileKind !== "agent") throw new Error("The Space history reader does not match the current Agent.");
|
|
4244
|
+
const result = await createCliSpaceApi(await resolveBaseUrl()).listSignals({
|
|
4245
|
+
spaceId: context.spaceId,
|
|
4246
|
+
requestContext: buildOpenMeldRequestContextFromProfile({ openMeldProfile: profile }),
|
|
4247
|
+
query: {
|
|
4248
|
+
aroundSignalId: context.sourceEventId,
|
|
4249
|
+
beforeCount: 20,
|
|
4250
|
+
afterCount: 0,
|
|
4251
|
+
kinds: ["text"]
|
|
4252
|
+
}
|
|
4253
|
+
});
|
|
4254
|
+
const current = readDispatchSpaceActionContextFromEnv(env);
|
|
4255
|
+
if (current.contextId !== context.contextId || current.nonce !== context.nonce || current.spaceId !== context.spaceId || current.sourceEventId !== context.sourceEventId || current.targetProfileId !== context.targetProfileId) throw new Error("The dispatch context changed during the Space history read.");
|
|
4256
|
+
const output = dispatchSpaceHistoryResultSchema.parse({
|
|
4257
|
+
spaceId: context.spaceId,
|
|
4258
|
+
sourceSignalId: context.sourceEventId,
|
|
4259
|
+
beforeCount: 20,
|
|
4260
|
+
afterCount: 0,
|
|
4261
|
+
signals: result.signals,
|
|
4262
|
+
nextCursor: result.nextCursor
|
|
4263
|
+
});
|
|
4264
|
+
const signals = output.signals.map((signal) => signalTextEnvelopeSchema.parse(signal));
|
|
4265
|
+
if (signals.some((signal) => signal.spaceId !== context.spaceId)) throw new Error("The Space history response does not match the current Space.");
|
|
4266
|
+
return {
|
|
4267
|
+
...output,
|
|
4268
|
+
signals
|
|
4269
|
+
};
|
|
4270
|
+
}
|
|
4271
|
+
//#endregion
|
|
4272
|
+
//#region src/space/dispatch-read-result.ts
|
|
4273
|
+
const PAGE_BYTES = 8 * 1024;
|
|
4274
|
+
const SNAPSHOT_BYTES = 32 * 1024 * 1024;
|
|
4275
|
+
const SNAPSHOT_COUNT = 4;
|
|
4276
|
+
var DispatchReadResults = class {
|
|
4277
|
+
#snapshots = /* @__PURE__ */ new Map();
|
|
4278
|
+
#bytes = 0;
|
|
4279
|
+
wrap(result) {
|
|
4280
|
+
if (result.isError) return result;
|
|
4281
|
+
const value = result.structuredContent ?? JSON.parse(result.content.map((item) => item.text).join("\n"));
|
|
4282
|
+
const complete = dispatchSpaceReadResultSchema.parse({ result: value });
|
|
4283
|
+
if (Buffer.byteLength(JSON.stringify(complete)) <= PAGE_BYTES) return this.#result(complete);
|
|
4284
|
+
const bytes = Buffer.from(JSON.stringify(value));
|
|
4285
|
+
if (bytes.length > SNAPSHOT_BYTES) throw new Error("The read exceeds this Wake's in-memory result budget. Request a smaller source; no local file was created.");
|
|
4286
|
+
while (this.#snapshots.size >= SNAPSHOT_COUNT || this.#bytes + bytes.length > SNAPSHOT_BYTES) {
|
|
4287
|
+
const oldest = this.#snapshots.keys().next().value;
|
|
4288
|
+
if (oldest === void 0) break;
|
|
4289
|
+
this.#bytes -= this.#snapshots.get(oldest)?.bytes.length ?? 0;
|
|
4290
|
+
this.#snapshots.delete(oldest);
|
|
4291
|
+
}
|
|
4292
|
+
const resultId = randomUUID();
|
|
4293
|
+
this.#snapshots.set(resultId, {
|
|
4294
|
+
bytes,
|
|
4295
|
+
offsets: /* @__PURE__ */ new Set([0])
|
|
4296
|
+
});
|
|
4297
|
+
this.#bytes += bytes.length;
|
|
4298
|
+
return this.read(resultId, 0);
|
|
4299
|
+
}
|
|
4300
|
+
read(resultId, offset) {
|
|
4301
|
+
const snapshot = this.#snapshots.get(resultId);
|
|
4302
|
+
if (!snapshot?.offsets.has(offset)) throw new Error("This result or offset is unavailable in the current tool invocation. Use a returned nextOffset, or read the source again; do not open a local file.");
|
|
4303
|
+
const { bytes } = snapshot;
|
|
4304
|
+
let end = Math.min(offset + PAGE_BYTES, bytes.length);
|
|
4305
|
+
while (true) {
|
|
4306
|
+
while (end < bytes.length && isUtf8Continuation(bytes[end])) end -= 1;
|
|
4307
|
+
const nextOffset = end === bytes.length ? null : end;
|
|
4308
|
+
const value = dispatchSpaceReadResultSchema.parse({ page: {
|
|
4309
|
+
resultId,
|
|
4310
|
+
offset,
|
|
4311
|
+
nextOffset,
|
|
4312
|
+
totalBytes: bytes.length,
|
|
4313
|
+
text: bytes.subarray(offset, end).toString("utf8")
|
|
4314
|
+
} });
|
|
4315
|
+
if (Buffer.byteLength(JSON.stringify(value)) <= PAGE_BYTES) {
|
|
4316
|
+
if (nextOffset !== null) snapshot.offsets.add(nextOffset);
|
|
4317
|
+
return this.#result(value);
|
|
4318
|
+
}
|
|
4319
|
+
end = offset + Math.floor((end - offset) * .8);
|
|
4320
|
+
}
|
|
4321
|
+
}
|
|
4322
|
+
clear() {
|
|
4323
|
+
this.#snapshots.clear();
|
|
4324
|
+
this.#bytes = 0;
|
|
4325
|
+
}
|
|
4326
|
+
#result(value) {
|
|
4327
|
+
return {
|
|
4328
|
+
structuredContent: value,
|
|
4329
|
+
content: [{
|
|
4330
|
+
type: "text",
|
|
4331
|
+
text: JSON.stringify(value)
|
|
4332
|
+
}]
|
|
4333
|
+
};
|
|
4334
|
+
}
|
|
4335
|
+
};
|
|
4336
|
+
function isUtf8Continuation(byte) {
|
|
4337
|
+
return byte !== void 0 && byte >= 128 && byte < 192;
|
|
4338
|
+
}
|
|
4339
|
+
//#endregion
|
|
4340
|
+
//#region src/space/dispatch-task.ts
|
|
4341
|
+
async function listDispatchSpaceTasks(phase, env = process.env) {
|
|
4342
|
+
return await withDispatchTaskContext(async ({ actionContext, api, requestContext }) => await api.listTasks({
|
|
4343
|
+
spaceId: actionContext.spaceId,
|
|
4344
|
+
requestContext,
|
|
4345
|
+
...phase ? { phase } : {}
|
|
4346
|
+
}), env);
|
|
4347
|
+
}
|
|
4348
|
+
async function createDispatchSpaceTask(body, env = process.env) {
|
|
4349
|
+
return await withDispatchTaskContext(async ({ actionContext, api, requestContext }) => await api.createTask({
|
|
4350
|
+
spaceId: actionContext.spaceId,
|
|
4351
|
+
requestContext,
|
|
4352
|
+
body: createSpaceTaskBodySchema.parse({
|
|
4353
|
+
...body,
|
|
4354
|
+
operationId: crypto.randomUUID(),
|
|
4355
|
+
sourceSignalId: actionContext.sourceEventId,
|
|
4356
|
+
assigneeProfileId: body.assigneeProfileId === void 0 ? actionContext.targetProfileId : body.assigneeProfileId
|
|
4357
|
+
})
|
|
4358
|
+
}), env);
|
|
4359
|
+
}
|
|
4360
|
+
async function mutateDispatchSpaceTask(input, env = process.env) {
|
|
4361
|
+
const taskId = spaceTaskIdSchema.parse(input.taskId);
|
|
4362
|
+
const body = mutateSpaceTaskBodySchema.parse({
|
|
4363
|
+
...input.body,
|
|
4364
|
+
operationId: crypto.randomUUID()
|
|
4365
|
+
});
|
|
4366
|
+
return await withDispatchTaskContext(async ({ actionContext, api, requestContext }) => await api.mutateTask({
|
|
4367
|
+
spaceId: actionContext.spaceId,
|
|
4368
|
+
requestContext,
|
|
4369
|
+
taskId,
|
|
4370
|
+
body
|
|
4371
|
+
}), env);
|
|
4372
|
+
}
|
|
4373
|
+
async function withDispatchTaskContext(operation, env) {
|
|
4374
|
+
if (env["OPENMELD_DISPATCH_RUNTIME_MODE"] !== "dispatch-owned-runtime") throw new Error("Space Task tools require an active OpenMeld dispatch runtime.");
|
|
4375
|
+
const actionContext = readDispatchSpaceActionContextFromEnv(env);
|
|
4376
|
+
const profile = await requireOpenMeldProfile({
|
|
4377
|
+
openMeldProfileId: actionContext.targetProfileId,
|
|
4378
|
+
persistSelection: false,
|
|
4379
|
+
authAction: "working with Tasks in the current OpenMeld Space"
|
|
4380
|
+
});
|
|
4381
|
+
if (profile.openMeldProfileId !== actionContext.targetProfileId || profile.profileKind !== "agent") throw new Error("The Space Task actor does not match the current Agent.");
|
|
4382
|
+
const result = await operation({
|
|
4383
|
+
actionContext,
|
|
4384
|
+
api: createCliSpaceApi(await resolveBaseUrl()),
|
|
4385
|
+
requestContext: buildOpenMeldRequestContextFromProfile({ openMeldProfile: profile })
|
|
4386
|
+
});
|
|
4387
|
+
const current = readDispatchSpaceActionContextFromEnv(env);
|
|
4388
|
+
if (current.contextId !== actionContext.contextId || current.nonce !== actionContext.nonce || current.spaceId !== actionContext.spaceId || current.sourceEventId !== actionContext.sourceEventId || current.targetProfileId !== actionContext.targetProfileId) throw new Error("The dispatch context changed during the Space Task action.");
|
|
4389
|
+
return result;
|
|
4390
|
+
}
|
|
4391
|
+
//#endregion
|
|
4392
|
+
//#region src/space/dispatch-working-copy.ts
|
|
4393
|
+
const run = promisify(execFile);
|
|
4394
|
+
async function runDispatchWorkingCopy(action, resolution, env = process.env) {
|
|
4395
|
+
const context = readDispatchSpaceActionContextFromEnv(env);
|
|
4396
|
+
const command = resolveDispatchSpaceActionMcpServer(env);
|
|
4397
|
+
if (!(command && context.workingCopyId)) throw new Error("This Wake has no prepared working copy.");
|
|
4398
|
+
const { stdout } = await run(command.commandPath, [
|
|
4399
|
+
"space",
|
|
4400
|
+
"files",
|
|
4401
|
+
"working-copy",
|
|
4402
|
+
action,
|
|
4403
|
+
"--profile",
|
|
4404
|
+
context.targetProfileId,
|
|
4405
|
+
"--json",
|
|
4406
|
+
"--view",
|
|
4407
|
+
"agent",
|
|
4408
|
+
...action === "resolve" ? [
|
|
4409
|
+
"--file-id",
|
|
4410
|
+
resolution?.fileId ?? "",
|
|
4411
|
+
"--revision",
|
|
4412
|
+
String(resolution?.revision ?? 0)
|
|
4413
|
+
] : [],
|
|
4414
|
+
"--",
|
|
4415
|
+
context.spaceId,
|
|
4416
|
+
context.workingCopyId
|
|
4417
|
+
], {
|
|
4418
|
+
encoding: "utf8",
|
|
4419
|
+
env,
|
|
4420
|
+
maxBuffer: 1024 * 1024,
|
|
4421
|
+
timeout: 12e4
|
|
4422
|
+
}).catch((error) => {
|
|
4423
|
+
if (error && typeof error === "object" && "stdout" in error && typeof error.stdout === "string") {
|
|
4424
|
+
let value;
|
|
4425
|
+
try {
|
|
4426
|
+
value = JSON.parse(error.stdout);
|
|
4427
|
+
} catch {}
|
|
4428
|
+
const parsed = cliJsonErrorEnvelopeSchema.safeParse(value);
|
|
4429
|
+
if (parsed.success) throw new Error(JSON.stringify(parsed.data), { cause: error });
|
|
4430
|
+
}
|
|
4431
|
+
throw error;
|
|
4432
|
+
});
|
|
4433
|
+
return spaceWorkingCopyResultSchema.parse(JSON.parse(stdout));
|
|
4434
|
+
}
|
|
4435
|
+
//#endregion
|
|
4436
|
+
//#region src/space/action-mcp-server.ts
|
|
4437
|
+
const DISPATCH_SPACE_TOOL_INSTRUCTIONS = "Use read_history for the bounded text history around the current Wake, and read_file for shared text files in the current Space. Use Space Task tools only when a clear deliverable needs durable ownership, progress, review, or later closure; questions and tiny same-turn actions stay in Messages. Task operations are non-final. Then choose exactly one permitted final OpenMeld Space Action: reply for an answer, limitation, error explanation, or request for help; stay_silent only when no public communication is appropriate. Do not create a Task just to acknowledge a message. Parameter validation errors record no action and may be corrected. Use read_receipt to reconcile an unknown outcome before resending. Private assistant output is not posted to the Space.";
|
|
4438
|
+
/** Every carrier uses the same schemas, validation, authority and recorder. */
|
|
4439
|
+
var DispatchSpaceTools = class {
|
|
4440
|
+
tools = [];
|
|
4441
|
+
deliveryOnly;
|
|
4442
|
+
#env;
|
|
4443
|
+
#context;
|
|
4444
|
+
#handlers = /* @__PURE__ */ new Map();
|
|
4445
|
+
#closed = false;
|
|
4446
|
+
#readResults = new DispatchReadResults();
|
|
4447
|
+
constructor(env) {
|
|
4448
|
+
this.#env = Object.freeze({ ...env });
|
|
4449
|
+
this.#context = readDispatchSpaceActionContextForReceiptFromEnv(this.#env);
|
|
4450
|
+
this.deliveryOnly = isDispatchSpaceActionDeliveryOnly(this.#env);
|
|
4451
|
+
}
|
|
4452
|
+
registerTool(name, config, handler) {
|
|
4453
|
+
if (this.deliveryOnly && ![
|
|
4454
|
+
"reply",
|
|
4455
|
+
"stay_silent",
|
|
4456
|
+
"read_receipt"
|
|
4457
|
+
].includes(name)) return;
|
|
4458
|
+
const inputSchema = config.inputSchema instanceof z.ZodObject ? config.inputSchema : z.strictObject(config.inputSchema);
|
|
4459
|
+
const pagedRead = [
|
|
4460
|
+
"read_history",
|
|
4461
|
+
"read_file",
|
|
4462
|
+
"list_tasks"
|
|
4463
|
+
].includes(name);
|
|
4464
|
+
this.tools.push({
|
|
4465
|
+
...config,
|
|
4466
|
+
name,
|
|
4467
|
+
inputSchema,
|
|
4468
|
+
...pagedRead ? {
|
|
4469
|
+
outputSchema: dispatchSpaceReadResultSchema,
|
|
4470
|
+
description: `${config.description} Small reads return result; large reads return page. Continue with read_result using page.resultId and page.nextOffset; no local file is needed.`
|
|
4471
|
+
} : {}
|
|
4472
|
+
});
|
|
4473
|
+
this.#handlers.set(name, (args) => {
|
|
4474
|
+
const parsed = inputSchema.safeParse(args);
|
|
4475
|
+
if (!parsed.success) return executeSpaceAction(() => {
|
|
4476
|
+
throw new DispatchSpaceActionRejectedError("space_action.invalid_input", parsed.error.message);
|
|
4477
|
+
}, this.#env);
|
|
4478
|
+
return handler(parsed.data);
|
|
4479
|
+
});
|
|
4480
|
+
}
|
|
4481
|
+
async call(name, args) {
|
|
4482
|
+
const context = readDispatchSpaceActionContextForReceiptFromEnv(this.#env);
|
|
4483
|
+
if (this.#closed || context.contextId !== this.#context.contextId || context.nonce !== this.#context.nonce) return executeSpaceAction(() => {
|
|
4484
|
+
throw new DispatchSpaceActionClosedError("space_action.context_mismatch", "This tool invocation no longer owns the active Wake. Do not retry through another transport.");
|
|
4485
|
+
}, this.#env);
|
|
4486
|
+
const handler = this.#handlers.get(name);
|
|
4487
|
+
if (!handler) return executeSpaceAction(() => {
|
|
4488
|
+
throw new DispatchSpaceActionRejectedError("space_action.not_allowed", "This tool is not available in the current invocation. Final communication uses reply or stay_silent, subject to the existing Space policy.");
|
|
4489
|
+
}, this.#env);
|
|
4490
|
+
const pagedRead = [
|
|
4491
|
+
"read_history",
|
|
4492
|
+
"read_file",
|
|
4493
|
+
"list_tasks"
|
|
4494
|
+
].includes(name);
|
|
4495
|
+
if (pagedRead || name === "read_result") try {
|
|
4496
|
+
readDispatchSpaceActionContextFromEnv(this.#env);
|
|
4497
|
+
const result = await handler(args);
|
|
4498
|
+
const active = readDispatchSpaceActionContextFromEnv(this.#env);
|
|
4499
|
+
if (this.#closed || active.contextId !== this.#context.contextId || active.nonce !== this.#context.nonce) throw new Error("The Wake changed or closed during this read. No result is available.");
|
|
4500
|
+
return pagedRead ? this.#readResults.wrap(result) : result;
|
|
4501
|
+
} catch (error) {
|
|
4502
|
+
return {
|
|
4503
|
+
isError: true,
|
|
4504
|
+
content: [{
|
|
4505
|
+
type: "text",
|
|
4506
|
+
text: error instanceof Error ? error.message : "The Space read failed."
|
|
4507
|
+
}]
|
|
4508
|
+
};
|
|
4509
|
+
}
|
|
4510
|
+
return await handler(args);
|
|
4511
|
+
}
|
|
4512
|
+
registerResultReader() {
|
|
4513
|
+
this.registerTool("read_result", {
|
|
4514
|
+
title: "Continue reading a large Space tool result",
|
|
4515
|
+
description: "Read the next page of a large read_history, read_file, or list_tasks result. Pass the returned resultId and nextOffset exactly; append page.text in order to reconstruct the complete JSON. No local path or shell is needed. The snapshot belongs only to this active Wake and tool connection; after expiry or eviction, read the source again. Does not publish or repeat an operation.",
|
|
4516
|
+
inputSchema: dispatchSpaceReadResultRequestSchema,
|
|
4517
|
+
outputSchema: dispatchSpaceReadResultSchema,
|
|
4518
|
+
annotations: {
|
|
4519
|
+
readOnlyHint: true,
|
|
4520
|
+
destructiveHint: false,
|
|
4521
|
+
idempotentHint: true,
|
|
4522
|
+
openWorldHint: false
|
|
4523
|
+
}
|
|
4524
|
+
}, async ({ resultId, offset }) => this.#readResults.read(resultId, offset));
|
|
4525
|
+
}
|
|
4526
|
+
close() {
|
|
4527
|
+
this.#closed = true;
|
|
4528
|
+
this.#readResults.clear();
|
|
4529
|
+
}
|
|
4530
|
+
};
|
|
4531
|
+
function createDispatchSpaceTools(input = {}) {
|
|
4532
|
+
const env = Object.freeze({ ...input.env ?? process.env });
|
|
4533
|
+
const server = new DispatchSpaceTools(env);
|
|
4534
|
+
const projectShell = readDispatchSpaceActionContextForReceiptFromEnv(env).projectShell;
|
|
4535
|
+
if (projectShell) server.registerTool("project_shell", {
|
|
4536
|
+
title: "Run a command in this task's project worktree",
|
|
4537
|
+
description: "Run one Bash command inside this task's isolated project container. Only its private worktree is mounted. The container has no network, host credentials, or Docker socket. This tool does not submit the final Space action.",
|
|
4538
|
+
inputSchema: dispatchProjectShellInputSchema,
|
|
4539
|
+
outputSchema: dispatchProjectShellResultSchema,
|
|
4540
|
+
annotations: {
|
|
4541
|
+
readOnlyHint: false,
|
|
4542
|
+
destructiveHint: true,
|
|
4543
|
+
idempotentHint: false,
|
|
4544
|
+
openWorldHint: false
|
|
4545
|
+
}
|
|
4546
|
+
}, async ({ command }) => {
|
|
4547
|
+
try {
|
|
4548
|
+
const context = readDispatchSpaceActionContextForReceiptFromEnv(env);
|
|
4549
|
+
if (!context.projectShell || context.projectShell.executionContextId !== projectShell.executionContextId || context.projectShell.boundaryRoot !== projectShell.boundaryRoot || context.projectShell.ledgerRootPath !== projectShell.ledgerRootPath || context.projectShell.workspacePath !== projectShell.workspacePath || context.projectShell.runtime.binary !== projectShell.runtime.binary || context.projectShell.runtime.engineIdentity !== projectShell.runtime.engineIdentity || context.projectShell.runtime.kind !== projectShell.runtime.kind) throw new Error("claude_project.context_mismatch");
|
|
4550
|
+
const result = dispatchProjectShellResultSchema.parse(await runExecutionContextWorkspaceProjectShell({
|
|
4551
|
+
ledgerRootPath: projectShell.ledgerRootPath,
|
|
4552
|
+
contextId: projectShell.executionContextId,
|
|
4553
|
+
dispatchId: context.dispatchId,
|
|
4554
|
+
attemptId: context.attemptId,
|
|
4555
|
+
command,
|
|
4556
|
+
runtime: projectShell.runtime,
|
|
4557
|
+
boundaryRoot: projectShell.boundaryRoot,
|
|
4558
|
+
workspacePath: projectShell.workspacePath
|
|
4559
|
+
}));
|
|
4560
|
+
return {
|
|
4561
|
+
structuredContent: result,
|
|
4562
|
+
content: [{
|
|
4563
|
+
type: "text",
|
|
4564
|
+
text: JSON.stringify(result)
|
|
4565
|
+
}]
|
|
4566
|
+
};
|
|
4567
|
+
} catch (error) {
|
|
4568
|
+
return {
|
|
4569
|
+
isError: true,
|
|
4570
|
+
content: [{
|
|
4571
|
+
type: "text",
|
|
4572
|
+
text: error instanceof Error && error.message.startsWith("claude_project.") ? error.message.split(":")[0] ?? "claude_project.execution_failed" : "claude_project.execution_failed"
|
|
4573
|
+
}]
|
|
4574
|
+
};
|
|
4575
|
+
}
|
|
4576
|
+
});
|
|
4577
|
+
server.registerTool("working_copy", {
|
|
4578
|
+
title: "Save or inspect this handoff's files",
|
|
4579
|
+
description: "Inspect or commit this handoff's files; reconcile retries an unknown save. On conflict, refresh downloads the shared version, then status shows its path and revision. Merge it into the original working file, then resolve with fileId and incoming revision before committing again. No automatic merge or overwrite. Does not deliver or close the Wake. No Space, profile or path overrides.",
|
|
4580
|
+
inputSchema: {
|
|
4581
|
+
action: z.enum([
|
|
4582
|
+
"status",
|
|
4583
|
+
"commit",
|
|
4584
|
+
"reconcile",
|
|
4585
|
+
"refresh",
|
|
4586
|
+
"resolve"
|
|
4587
|
+
]),
|
|
4588
|
+
fileId: z.string().optional(),
|
|
4589
|
+
revision: z.number().int().positive().optional()
|
|
4590
|
+
}
|
|
4591
|
+
}, async ({ action, fileId, revision }) => {
|
|
4592
|
+
try {
|
|
4593
|
+
return { content: [{
|
|
4594
|
+
type: "text",
|
|
4595
|
+
text: JSON.stringify(await runDispatchWorkingCopy(action, {
|
|
4596
|
+
fileId,
|
|
4597
|
+
revision
|
|
4598
|
+
}, env))
|
|
4599
|
+
}] };
|
|
4600
|
+
} catch (error) {
|
|
4601
|
+
return {
|
|
4602
|
+
isError: true,
|
|
4603
|
+
content: [{
|
|
4604
|
+
type: "text",
|
|
4605
|
+
text: error instanceof Error ? error.message : String(error)
|
|
4606
|
+
}]
|
|
4607
|
+
};
|
|
4608
|
+
}
|
|
4609
|
+
});
|
|
4610
|
+
server.registerTool("read_history", {
|
|
4611
|
+
title: "Read the current Wake's Space history",
|
|
4612
|
+
description: "Read up to 20 text messages before the current Wake, plus its source message. Space, Agent identity and source are fixed by the active dispatch. Use this instead of terminal CLI history. This read does not submit a reply or close the Wake.",
|
|
4613
|
+
inputSchema: dispatchSpaceHistoryRequestSchema,
|
|
4614
|
+
annotations: {
|
|
4615
|
+
readOnlyHint: true,
|
|
4616
|
+
destructiveHint: false,
|
|
4617
|
+
idempotentHint: true,
|
|
4618
|
+
openWorldHint: false
|
|
4619
|
+
}
|
|
4620
|
+
}, async () => {
|
|
4621
|
+
try {
|
|
4622
|
+
const result = await readDispatchSpaceHistory(env);
|
|
4623
|
+
return {
|
|
4624
|
+
structuredContent: result,
|
|
4625
|
+
content: [{
|
|
4626
|
+
type: "text",
|
|
4627
|
+
text: JSON.stringify(result)
|
|
4628
|
+
}]
|
|
4629
|
+
};
|
|
4630
|
+
} catch (error) {
|
|
4631
|
+
return {
|
|
4632
|
+
isError: true,
|
|
4633
|
+
content: [{
|
|
4634
|
+
type: "text",
|
|
4635
|
+
text: error instanceof Error ? error.message : String(error)
|
|
4636
|
+
}]
|
|
4637
|
+
};
|
|
4638
|
+
}
|
|
4639
|
+
});
|
|
4640
|
+
server.registerTool("read_file", {
|
|
4641
|
+
title: "Read a file in the current OpenMeld Space",
|
|
4642
|
+
description: "Read a shared text file as the Agent in the current Wake. Use this for attached CSV, text, Markdown, or JSON files instead of running the CLI in a terminal. Pass the attachment's file ID and revision. This is a context read; it does not submit a reply or close the Wake.",
|
|
4643
|
+
inputSchema: {
|
|
4644
|
+
fileId: spaceFileEntryIdSchema,
|
|
4645
|
+
revision: spaceFileRevisionNumberSchema.optional()
|
|
4646
|
+
},
|
|
4647
|
+
annotations: {
|
|
4648
|
+
readOnlyHint: true,
|
|
4649
|
+
destructiveHint: false,
|
|
4650
|
+
idempotentHint: true,
|
|
4651
|
+
openWorldHint: false
|
|
4652
|
+
}
|
|
4653
|
+
}, async (input) => {
|
|
4654
|
+
try {
|
|
4655
|
+
const result = await readDispatchSpaceFile({
|
|
4656
|
+
...input,
|
|
4657
|
+
env
|
|
4658
|
+
});
|
|
4659
|
+
return { content: [{
|
|
4660
|
+
type: "text",
|
|
4661
|
+
text: JSON.stringify(result)
|
|
4662
|
+
}] };
|
|
4663
|
+
} catch (error) {
|
|
4664
|
+
return {
|
|
4665
|
+
content: [{
|
|
4666
|
+
type: "text",
|
|
4667
|
+
text: error instanceof Error ? error.message : String(error)
|
|
4668
|
+
}],
|
|
4669
|
+
isError: true
|
|
4670
|
+
};
|
|
4671
|
+
}
|
|
4672
|
+
});
|
|
4673
|
+
server.registerTool("list_tasks", {
|
|
4674
|
+
title: "List Tasks in the current OpenMeld Space",
|
|
4675
|
+
description: "Read durable collaboration Tasks for this Space. Use the returned revision before changing a Task. Task phase is collaboration state, not Agent execution truth.",
|
|
4676
|
+
inputSchema: { phase: z.enum([
|
|
4677
|
+
"to_do",
|
|
4678
|
+
"in_progress",
|
|
4679
|
+
"in_review",
|
|
4680
|
+
"done",
|
|
4681
|
+
"canceled"
|
|
4682
|
+
]).optional() },
|
|
4683
|
+
annotations: {
|
|
4684
|
+
readOnlyHint: true,
|
|
4685
|
+
destructiveHint: false,
|
|
4686
|
+
idempotentHint: true,
|
|
4687
|
+
openWorldHint: false
|
|
4688
|
+
}
|
|
4689
|
+
}, async ({ phase }) => executeTaskAction(() => listDispatchSpaceTasks(phase, env)));
|
|
4690
|
+
server.registerTool("create_task", {
|
|
4691
|
+
title: "Create a Task from the current Space message",
|
|
4692
|
+
description: "Create a durable Task only for a clear deliverable that needs ownership, progress, review, or later closure. The source message and Space are fixed by this Wake. The Task is assigned to the current Agent by default and remains To do until start_task is called.",
|
|
4693
|
+
inputSchema: {
|
|
4694
|
+
title: z.string().trim().min(1).max(200),
|
|
4695
|
+
description: z.string().trim().min(1).max(8e3).nullable().optional(),
|
|
4696
|
+
completionConditions: z.array(z.string().trim().min(1).max(500)).max(12).optional(),
|
|
4697
|
+
assigneeProfileId: openMeldRuntimeProfileIdSchema.nullable().optional(),
|
|
4698
|
+
reviewerProfileId: openMeldRuntimeProfileIdSchema.nullable().optional()
|
|
4699
|
+
},
|
|
4700
|
+
outputSchema: spaceTaskMutationReceiptSchema,
|
|
4701
|
+
annotations: {
|
|
4702
|
+
readOnlyHint: false,
|
|
4703
|
+
destructiveHint: false,
|
|
4704
|
+
idempotentHint: false,
|
|
4705
|
+
openWorldHint: false
|
|
4706
|
+
}
|
|
4707
|
+
}, async (body) => executeTaskAction(() => createDispatchSpaceTask(body, env)));
|
|
4708
|
+
server.registerTool("start_task", {
|
|
4709
|
+
title: "Mark a Space Task in progress",
|
|
4710
|
+
description: "Record that the current assigned Agent has begun advancing this Task. This changes collaboration state and does not start a separate Agent execution.",
|
|
4711
|
+
inputSchema: {
|
|
4712
|
+
taskId: spaceTaskIdSchema,
|
|
4713
|
+
expectedRevision: z.number().int().min(1)
|
|
4714
|
+
},
|
|
4715
|
+
outputSchema: spaceTaskMutationReceiptSchema,
|
|
4716
|
+
annotations: {
|
|
4717
|
+
readOnlyHint: false,
|
|
4718
|
+
destructiveHint: false,
|
|
4719
|
+
idempotentHint: false,
|
|
4720
|
+
openWorldHint: false
|
|
4721
|
+
}
|
|
4722
|
+
}, async ({ expectedRevision, taskId }) => executeTaskAction(() => mutateDispatchSpaceTask({
|
|
4723
|
+
taskId,
|
|
4724
|
+
body: {
|
|
4725
|
+
action: "mark_in_progress",
|
|
4726
|
+
expectedRevision
|
|
4727
|
+
}
|
|
4728
|
+
}, env)));
|
|
4729
|
+
server.registerTool("submit_task_result", {
|
|
4730
|
+
title: "Submit a result for a Space Task",
|
|
4731
|
+
description: "Submit the concrete result and verification for an in-progress Task. A Task with a reviewer moves to In review; otherwise the stated completion boundary moves it to Done. The Space delivers the review handoff to the reviewer.",
|
|
4732
|
+
inputSchema: {
|
|
4733
|
+
taskId: spaceTaskIdSchema,
|
|
4734
|
+
expectedRevision: z.number().int().min(1),
|
|
4735
|
+
summary: z.string().trim().min(1).max(8e3)
|
|
4736
|
+
},
|
|
4737
|
+
outputSchema: spaceTaskMutationReceiptSchema,
|
|
4738
|
+
annotations: {
|
|
4739
|
+
readOnlyHint: false,
|
|
4740
|
+
destructiveHint: false,
|
|
4741
|
+
idempotentHint: false,
|
|
4742
|
+
openWorldHint: false
|
|
4743
|
+
}
|
|
4744
|
+
}, async ({ expectedRevision, summary, taskId }) => executeTaskAction(() => mutateDispatchSpaceTask({
|
|
4745
|
+
taskId,
|
|
4746
|
+
body: {
|
|
4747
|
+
action: "submit_result",
|
|
4748
|
+
expectedRevision,
|
|
4749
|
+
summary
|
|
4750
|
+
}
|
|
4751
|
+
}, env)));
|
|
4752
|
+
server.registerTool("review_task", {
|
|
4753
|
+
title: "Review a Space Task result",
|
|
4754
|
+
description: "Accept the exact result you read or request specific changes as this Task's reviewer. Pass that result's resultRevision as expectedResultRevision and the Task's revision as expectedRevision. A conflict requires reading the current Task and deciding again. Change requests require feedback. The Space delivers review and rework handoffs. Agent-to-Agent rework stops for a human decision after two automatic rounds.",
|
|
4755
|
+
inputSchema: {
|
|
4756
|
+
taskId: spaceTaskIdSchema,
|
|
4757
|
+
expectedRevision: z.number().int().min(1),
|
|
4758
|
+
expectedResultRevision: z.number().int().min(1),
|
|
4759
|
+
decision: z.enum(["accepted", "changes_requested"]),
|
|
4760
|
+
feedback: z.string().trim().min(1).max(8e3).nullable().optional()
|
|
4761
|
+
},
|
|
4762
|
+
outputSchema: spaceTaskMutationReceiptSchema,
|
|
4763
|
+
annotations: {
|
|
4764
|
+
readOnlyHint: false,
|
|
4765
|
+
destructiveHint: false,
|
|
4766
|
+
idempotentHint: false,
|
|
4767
|
+
openWorldHint: false
|
|
4768
|
+
}
|
|
4769
|
+
}, async ({ decision, expectedRevision, expectedResultRevision, feedback, taskId }) => executeTaskAction(() => mutateDispatchSpaceTask({
|
|
4770
|
+
taskId,
|
|
4771
|
+
body: {
|
|
4772
|
+
action: "review",
|
|
4773
|
+
expectedRevision,
|
|
4774
|
+
expectedResultRevision,
|
|
4775
|
+
decision,
|
|
4776
|
+
...feedback === void 0 ? {} : { feedback }
|
|
4777
|
+
}
|
|
4778
|
+
}, env)));
|
|
4779
|
+
server.registerTool("cancel_task", {
|
|
4780
|
+
title: "Cancel a Space Task",
|
|
4781
|
+
description: "Cancel a Task when its accepted commitment should no longer be done. Cancellation fences later Task progress; it does not by itself prove that an Agent execution stopped.",
|
|
4782
|
+
inputSchema: {
|
|
4783
|
+
taskId: spaceTaskIdSchema,
|
|
4784
|
+
expectedRevision: z.number().int().min(1),
|
|
4785
|
+
reason: z.string().trim().min(1).max(1e3)
|
|
4786
|
+
},
|
|
4787
|
+
outputSchema: spaceTaskMutationReceiptSchema,
|
|
4788
|
+
annotations: {
|
|
4789
|
+
readOnlyHint: false,
|
|
4790
|
+
destructiveHint: false,
|
|
4791
|
+
idempotentHint: false,
|
|
4792
|
+
openWorldHint: false
|
|
4793
|
+
}
|
|
4794
|
+
}, async ({ expectedRevision, reason, taskId }) => executeTaskAction(() => mutateDispatchSpaceTask({
|
|
4795
|
+
taskId,
|
|
4796
|
+
body: {
|
|
4797
|
+
action: "cancel",
|
|
4798
|
+
expectedRevision,
|
|
4799
|
+
reason
|
|
4800
|
+
}
|
|
4801
|
+
}, env)));
|
|
4802
|
+
server.registerTool("reply", {
|
|
4803
|
+
title: "Reply in the current OpenMeld Space",
|
|
4804
|
+
description: "Record the exact final public message for the active Wake: answers, known limitations, errors, and requests for help all belong here. Use real newlines. No shell, local repository, CLI, or Skill is needed to explain that you cannot complete a task. Omit recipient fields for a plain reply; use full Profile IDs from this Wake's send target reference when addressing recipients. Correct invalid arguments and retry; do not bypass denials or blindly resend an unknown outcome.",
|
|
4805
|
+
inputSchema: {
|
|
4806
|
+
message: z.string().min(1),
|
|
4807
|
+
files: z.array(spaceMessageFileReferenceSchema).max(10).optional(),
|
|
4808
|
+
mentionTargets: z.array(z.string().min(1)).optional(),
|
|
4809
|
+
referenceTargets: z.array(z.string().min(1)).optional(),
|
|
4810
|
+
wakeTargets: z.array(z.string().min(1)).optional()
|
|
4811
|
+
},
|
|
4812
|
+
outputSchema: dispatchSpaceActionToolResultSchema,
|
|
4813
|
+
annotations: {
|
|
4814
|
+
destructiveHint: false,
|
|
4815
|
+
idempotentHint: false,
|
|
4816
|
+
openWorldHint: false,
|
|
4817
|
+
readOnlyHint: false
|
|
4818
|
+
}
|
|
4819
|
+
}, async ({ files, mentionTargets, message, referenceTargets, wakeTargets }) => executeSpaceAction(() => recordDispatchSpaceReply({
|
|
4820
|
+
env,
|
|
4821
|
+
files,
|
|
4822
|
+
mentionTargets,
|
|
4823
|
+
referenceTargets,
|
|
4824
|
+
text: message,
|
|
4825
|
+
wakeTargets
|
|
4826
|
+
}), env));
|
|
4827
|
+
server.registerTool("stay_silent", {
|
|
4828
|
+
title: "Stay silent in the current OpenMeld Space",
|
|
4829
|
+
description: "Record that the active OpenMeld Wake intentionally needs no public reply. Use this only after deciding that silence is the correct final outcome.",
|
|
4830
|
+
inputSchema: { reason: z.string().min(1) },
|
|
4831
|
+
outputSchema: dispatchSpaceActionToolResultSchema,
|
|
4832
|
+
annotations: {
|
|
4833
|
+
destructiveHint: false,
|
|
4834
|
+
idempotentHint: false,
|
|
4835
|
+
openWorldHint: false,
|
|
4836
|
+
readOnlyHint: false
|
|
4837
|
+
}
|
|
4838
|
+
}, async ({ reason }) => executeSpaceAction(() => recordDispatchSpaceSilent({
|
|
4839
|
+
reason,
|
|
4840
|
+
env
|
|
4841
|
+
}), env));
|
|
4842
|
+
server.registerResultReader();
|
|
4843
|
+
server.registerTool("read_receipt", {
|
|
4844
|
+
title: "Check this Wake's recorded final action",
|
|
4845
|
+
description: "Reconcile an uncertain final tool result without posting, replacing, or repeating the action. A saved receipt is a local proposal, not confirmation of publication. A missing record is an observation, not permission to bypass a denial or expiry.",
|
|
4846
|
+
inputSchema: {},
|
|
4847
|
+
annotations: {
|
|
4848
|
+
readOnlyHint: true,
|
|
4849
|
+
destructiveHint: false,
|
|
4850
|
+
idempotentHint: true,
|
|
4851
|
+
openWorldHint: false
|
|
4852
|
+
}
|
|
4853
|
+
}, async () => {
|
|
4854
|
+
const record = readDispatchSpaceActionRecord({ context: readDispatchSpaceActionContextForReceiptFromEnv(env) });
|
|
4855
|
+
if (record.status === "loaded") return await executeSpaceAction(() => record.record, env);
|
|
4856
|
+
if (record.status === "missing") return { content: [{
|
|
4857
|
+
type: "text",
|
|
4858
|
+
text: "No local final action record is present. Correct invalid arguments and use a permitted final tool only while this Wake is still authorized. This read does not reserve a write or override a denial."
|
|
4859
|
+
}] };
|
|
4860
|
+
return executeSpaceAction(() => {
|
|
4861
|
+
throw new Error("The existing final action record cannot be verified. Do not overwrite it or resend through another transport.");
|
|
4862
|
+
}, env);
|
|
4863
|
+
});
|
|
4864
|
+
return server;
|
|
4865
|
+
}
|
|
4866
|
+
function createSpaceActionMcpServer() {
|
|
4867
|
+
const context = readDispatchSpaceActionContextForReceiptFromEnv();
|
|
4868
|
+
const tools = createDispatchSpaceTools();
|
|
4869
|
+
const server = new McpServer({
|
|
4870
|
+
name: "openmeld-dispatch-space-action",
|
|
4871
|
+
version: "1.0.0"
|
|
4872
|
+
}, { instructions: DISPATCH_SPACE_TOOL_INSTRUCTIONS });
|
|
4873
|
+
for (const { name, ...definition } of tools.tools) server.registerTool(name, definition, async (args) => {
|
|
4874
|
+
try {
|
|
4875
|
+
const active = readDispatchSpaceActionContextForReceiptFromEnv();
|
|
4876
|
+
if (active.contextId !== context.contextId || active.nonce !== context.nonce) throw new DispatchSpaceActionClosedError("space_action.context_mismatch", "This MCP server cannot be rebound to another Wake.");
|
|
4877
|
+
return await tools.call(name, args);
|
|
4878
|
+
} catch (error) {
|
|
4879
|
+
return executeSpaceAction(() => {
|
|
4880
|
+
throw error;
|
|
4881
|
+
});
|
|
4882
|
+
}
|
|
4883
|
+
});
|
|
4884
|
+
server.server.onclose = () => tools.close();
|
|
4885
|
+
return server;
|
|
4886
|
+
}
|
|
4887
|
+
async function runSpaceActionMcpServer() {
|
|
4888
|
+
readDispatchSpaceActionContextForReceiptFromEnv();
|
|
4889
|
+
await createSpaceActionMcpServer().connect(new StdioServerTransport());
|
|
4890
|
+
}
|
|
4891
|
+
async function executeSpaceAction(action, env = process.env) {
|
|
4892
|
+
try {
|
|
4893
|
+
const context = readDispatchSpaceActionContextForReceiptFromEnv(env);
|
|
4894
|
+
const record = await action();
|
|
4895
|
+
if (record.contextId !== context.contextId || record.nonce !== context.nonce) throw new DispatchSpaceActionClosedError("space_action.context_mismatch", "The saved action does not match the current dispatch context.");
|
|
4896
|
+
const contextState = Date.now() > context.expiresAtMs ? "expired" : "active";
|
|
4897
|
+
return {
|
|
4898
|
+
structuredContent: dispatchSpaceActionToolResultSchema.parse({ receipt: {
|
|
4899
|
+
status: "proposal_saved_locally",
|
|
4900
|
+
actionType: record.action.type,
|
|
4901
|
+
attemptId: context.attemptId,
|
|
4902
|
+
contextId: context.contextId,
|
|
4903
|
+
dispatchId: context.dispatchId,
|
|
4904
|
+
recordedAtMs: record.createdAtMs,
|
|
4905
|
+
contextState,
|
|
4906
|
+
coordinatorAcceptance: "unconfirmed",
|
|
4907
|
+
publication: "unconfirmed"
|
|
4908
|
+
} }),
|
|
4909
|
+
content: [{
|
|
4910
|
+
type: "text",
|
|
4911
|
+
text: `Local proposal saved (receipt: proposal_saved_locally, action: ${record.action.type}). OpenMeld has recorded this unpublished Space action proposal on this computer. ${contextState === "expired" ? "The local context has expired. This receipt identifies the existing record and does not extend action authority. " : ""}Coordinator acceptance and Space publication are NOT yet confirmed; do not report it as posted.`
|
|
4912
|
+
}]
|
|
4913
|
+
};
|
|
4914
|
+
} catch (error) {
|
|
4915
|
+
if (error instanceof DispatchSpaceActionRejectedError) return {
|
|
4916
|
+
structuredContent: dispatchSpaceActionToolResultSchema.parse({ receipt: {
|
|
4917
|
+
status: "rejected_before_recording",
|
|
4918
|
+
reasonCode: error.reasonCode,
|
|
4919
|
+
coordinatorAcceptance: "unconfirmed",
|
|
4920
|
+
publication: "unconfirmed"
|
|
4921
|
+
} }),
|
|
4922
|
+
content: [{
|
|
4923
|
+
type: "text",
|
|
4924
|
+
text: `Action rejected (receipt: rejected_before_recording, reason: ${error.reasonCode}). This call did not record an action. ${error.message} ${error.reasonCode === "space_action.invalid_input" ? "Correct the indicated arguments and call the same permitted tool again. " : ""}Any earlier records are preserved; Coordinator acceptance and Space publication are unconfirmed.`
|
|
4925
|
+
}],
|
|
4926
|
+
isError: true
|
|
4927
|
+
};
|
|
4928
|
+
if (error instanceof DispatchSpaceActionClosedError) return {
|
|
4929
|
+
structuredContent: dispatchSpaceActionToolResultSchema.parse({ receipt: {
|
|
4930
|
+
status: "rejected_closed",
|
|
4931
|
+
reasonCode: error.reasonCode,
|
|
4932
|
+
coordinatorAcceptance: "unconfirmed",
|
|
4933
|
+
publication: "unconfirmed"
|
|
4934
|
+
} }),
|
|
4935
|
+
content: [{
|
|
4936
|
+
type: "text",
|
|
4937
|
+
text: `Action rejected (receipt: rejected_closed, reason: ${error.reasonCode}). ${error.message} New actions are not permitted in this context. Existing records are preserved; Coordinator acceptance and Space publication are unconfirmed.`
|
|
4938
|
+
}],
|
|
4939
|
+
isError: true
|
|
4940
|
+
};
|
|
4941
|
+
return {
|
|
4942
|
+
structuredContent: dispatchSpaceActionToolResultSchema.parse({ receipt: {
|
|
4943
|
+
status: "outcome_unknown",
|
|
4944
|
+
reasonCode: "space_action.recording_outcome_unknown",
|
|
4945
|
+
coordinatorAcceptance: "unconfirmed",
|
|
4946
|
+
publication: "unconfirmed"
|
|
4947
|
+
} }),
|
|
4948
|
+
content: [{
|
|
4949
|
+
type: "text",
|
|
4950
|
+
text: `Action receipt unavailable (receipt: outcome_unknown). ${error instanceof Error ? error.message : String(error)} Existing records are preserved; Coordinator acceptance and Space publication are unconfirmed.`
|
|
4951
|
+
}],
|
|
4952
|
+
isError: true
|
|
4953
|
+
};
|
|
4954
|
+
}
|
|
4955
|
+
}
|
|
4956
|
+
async function executeTaskAction(action) {
|
|
4957
|
+
try {
|
|
4958
|
+
const result = await action();
|
|
4959
|
+
return {
|
|
4960
|
+
structuredContent: result,
|
|
4961
|
+
content: [{
|
|
4962
|
+
type: "text",
|
|
4963
|
+
text: JSON.stringify(result)
|
|
4964
|
+
}]
|
|
4965
|
+
};
|
|
4966
|
+
} catch (error) {
|
|
4967
|
+
return {
|
|
4968
|
+
isError: true,
|
|
4969
|
+
content: [{
|
|
4970
|
+
type: "text",
|
|
4971
|
+
text: error instanceof Error ? error.message : String(error)
|
|
4972
|
+
}]
|
|
4973
|
+
};
|
|
4974
|
+
}
|
|
4975
|
+
}
|
|
4976
|
+
//#endregion
|
|
2786
4977
|
//#region src/organization-skills/cache.ts
|
|
2787
4978
|
const CACHE_MARKER_FILENAME = ".openmeld-organization-skill.json";
|
|
2788
4979
|
const DIRECTORY_READ_ONLY_MODE = 365;
|
|
@@ -2965,47 +5156,6 @@ async function downloadWorkingCopyFile(api, input) {
|
|
|
2965
5156
|
};
|
|
2966
5157
|
}
|
|
2967
5158
|
//#endregion
|
|
2968
|
-
//#region src/local-service/state/ledger-io.ts
|
|
2969
|
-
function createLedgerIo() {
|
|
2970
|
-
return {
|
|
2971
|
-
appendFile: async (filePath, content, encoding) => {
|
|
2972
|
-
await mkdir(dirnamePath(filePath), { recursive: true });
|
|
2973
|
-
await appendFile(filePath, content, { encoding });
|
|
2974
|
-
},
|
|
2975
|
-
copyFile: async (fromPath, toPath) => {
|
|
2976
|
-
await mkdir(dirnamePath(toPath), { recursive: true });
|
|
2977
|
-
await copyFile(fromPath, toPath);
|
|
2978
|
-
},
|
|
2979
|
-
mkdir: async (dirPath, options) => {
|
|
2980
|
-
await mkdir(dirPath, options);
|
|
2981
|
-
},
|
|
2982
|
-
readFile: async (filePath, encoding) => {
|
|
2983
|
-
return await readFile(filePath, encoding);
|
|
2984
|
-
},
|
|
2985
|
-
rename: async (fromPath, toPath) => {
|
|
2986
|
-
await mkdir(dirnamePath(toPath), { recursive: true });
|
|
2987
|
-
await rename(fromPath, toPath);
|
|
2988
|
-
},
|
|
2989
|
-
syncPath: async (fileOrDirectoryPath) => {
|
|
2990
|
-
const isWindows = process.platform === "win32";
|
|
2991
|
-
if (isWindows && (await stat(fileOrDirectoryPath)).isDirectory()) return;
|
|
2992
|
-
const handle = await open(fileOrDirectoryPath, isWindows ? "r+" : "r");
|
|
2993
|
-
try {
|
|
2994
|
-
await handle.sync();
|
|
2995
|
-
} finally {
|
|
2996
|
-
await handle.close();
|
|
2997
|
-
}
|
|
2998
|
-
},
|
|
2999
|
-
rm: async (filePath, options) => {
|
|
3000
|
-
await rm(filePath, options);
|
|
3001
|
-
},
|
|
3002
|
-
writeFile: async (filePath, content, encoding) => {
|
|
3003
|
-
await mkdir(dirnamePath(filePath), { recursive: true });
|
|
3004
|
-
await writeFile(filePath, content, encoding);
|
|
3005
|
-
}
|
|
3006
|
-
};
|
|
3007
|
-
}
|
|
3008
|
-
//#endregion
|
|
3009
5159
|
//#region src/local-service/file-access/space-working-copy.ts
|
|
3010
5160
|
const hash = (bytes) => createHash("sha256").update(bytes).digest("hex");
|
|
3011
5161
|
const O_NOFOLLOW_ANY = 536870912;
|
|
@@ -3508,6 +5658,6 @@ function isSameServiceIdentity(left, right) {
|
|
|
3508
5658
|
return left?.deviceId === right.deviceId && left.contractGeneration === right.contractGeneration;
|
|
3509
5659
|
}
|
|
3510
5660
|
//#endregion
|
|
3511
|
-
export {
|
|
5661
|
+
export { readControllerTaskLifecycleOutboxHealth as A, uninstallAgentActivityIntegrations as B, startLocalAgentActivitySyncLoop as C, readAgentActivityContextForPath as D, recordOpenMeldCreatedLocalAgentSession as E, AGENT_ACTIVITY_INTEGRATION_OWNER as F, defaultManagedIntegrationPaths as I, installAgentActivityIntegrations as L, registerAgentActivityRoute as M, resolveAgentActivityRouteForPath as N, removeAgentActivityContextCache as O, unregisterAgentActivityRoute as P, readAgentActivityIntegrationStatus as R, prepareClaudeProjectContainer as S, discoverLocalAgentSessionProviders as T, markExecutionContextWorkspaceInUse as _, fetchSpaceUpdates as a, releaseExecutionContextWorkspace as b, materializeOrganizationSkillRelease as c, runSpaceActionHelp as d, runSpaceActionReply as f, runSpaceActionWake as g, runSpaceActionTargets as h, recordRuntimeReportingSuccess as i, isAgentActivityRouteRegistered as j, enqueueControllerTaskLifecycleEventWhileLocked as k, createDispatchSpaceTools as l, runSpaceActionStatus as m, recordRuntimeReportingFailure as n, createSpaceWorkingCopies as o, runSpaceActionSilent as p, recordRuntimeReportingNotApplicable as r, downloadWorkingCopyFile as s, readRuntimeReportingHealthState as t, runSpaceActionMcpServer as u, prepareExecutionContextWorkspace as v, syncLocalAgentActivity as w, createLedgerIo as x, readExecutionContextWorkspace as y, resolveAgentActivityDispatcherCommand as z };
|
|
3512
5662
|
|
|
3513
|
-
//# sourceMappingURL=runtime-reporting-health-state-
|
|
5663
|
+
//# sourceMappingURL=runtime-reporting-health-state-DA-_vW-n.js.map
|