@xyagent/cli 0.0.1
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/README.md +860 -0
- package/bin/agentlink +8585 -0
- package/bin/agentlink-agent +7 -0
- package/bin/agentlink-mcp-stdio +173 -0
- package/docs/LOGGING.md +159 -0
- package/package.json +38 -0
- package/src/core.mjs +320 -0
- package/src/tunnel.mjs +291 -0
- package/src/tunnel_proxy.mjs +913 -0
- package/src/tunnel_service.mjs +699 -0
- package/src/tunnel_state.mjs +301 -0
- package/src/unified_producer.mjs +259 -0
- package/src-ext/README.md +75 -0
- package/src-ext/bin.mjs +128 -0
- package/src-ext/commands/agent.mjs +66 -0
- package/src-ext/commands/help.mjs +31 -0
- package/src-ext/commands/pair.mjs +455 -0
- package/src-ext/commands/reload.mjs +78 -0
- package/src-ext/commands/service.mjs +64 -0
- package/src-ext/core/accountResolver.mjs +137 -0
- package/src-ext/core/activeRuns.mjs +76 -0
- package/src-ext/core/agentlinkToolsBootstrap.mjs +75 -0
- package/src-ext/core/agentlinkToolsRegistry.mjs +323 -0
- package/src-ext/core/agentlinkToolsServer.mjs +109 -0
- package/src-ext/core/approvalGateway.mjs +132 -0
- package/src-ext/core/attachAuditLog.mjs +79 -0
- package/src-ext/core/attachDenylist.mjs +128 -0
- package/src-ext/core/autoTunnelDetector.mjs +98 -0
- package/src-ext/core/autoTunnelGateway.mjs +393 -0
- package/src-ext/core/autoTunnelWiring.mjs +184 -0
- package/src-ext/core/boundedMap.mjs +98 -0
- package/src-ext/core/bridgeAttachAutoScanner.mjs +237 -0
- package/src-ext/core/bridgeHooks.mjs +168 -0
- package/src-ext/core/bridgeSelfUninstall.mjs +133 -0
- package/src-ext/core/claudeTrust.mjs +74 -0
- package/src-ext/core/codexTrust.mjs +77 -0
- package/src-ext/core/cosUploadClient.mjs +326 -0
- package/src-ext/core/createWorkspace.mjs +170 -0
- package/src-ext/core/daemonRestart.mjs +49 -0
- package/src-ext/core/defaultWorkspace.mjs +65 -0
- package/src-ext/core/ensureGitRepo.mjs +57 -0
- package/src-ext/core/hermesProfile.mjs +75 -0
- package/src-ext/core/interruptGateway.mjs +119 -0
- package/src-ext/core/interruptRunListener.mjs +106 -0
- package/src-ext/core/mcpRuntimeFanout.mjs +236 -0
- package/src-ext/core/mcpStdioClient.mjs +316 -0
- package/src-ext/core/openclawGatewayClient.mjs +230 -0
- package/src-ext/core/pairCodeClient.mjs +331 -0
- package/src-ext/core/pairInventory.mjs +59 -0
- package/src-ext/core/pathExpand.mjs +34 -0
- package/src-ext/core/preprocessAttachments.mjs +113 -0
- package/src-ext/core/relayWorker.mjs +284 -0
- package/src-ext/core/relayWorkerHttp.mjs +58 -0
- package/src-ext/core/scanWorkspaces.mjs +842 -0
- package/src-ext/core/sessionSlot.mjs +70 -0
- package/src-ext/core/stateMachine.mjs +85 -0
- package/src-ext/core/toolsServerLifecycle.mjs +169 -0
- package/src-ext/core/unifiedDispatchHandler.mjs +439 -0
- package/src-ext/core/usageReporter.mjs +207 -0
- package/src-ext/openclaw-plugin/envelope-builder.cjs +619 -0
- package/src-ext/openclaw-plugin/index.cjs +1919 -0
- package/src-ext/openclaw-plugin/openclaw.plugin.json +20 -0
- package/src-ext/openclaw-plugin/package.json +12 -0
- package/src-ext/openclaw-plugin/relay-defaults.json +3 -0
- package/src-ext/openclaw-plugin/todoTranslatorUtils.cjs +101 -0
- package/src-ext/runtime/_shared/cliExec.mjs +138 -0
- package/src-ext/runtime/_shared/cosTextRefs.mjs +120 -0
- package/src-ext/runtime/_shared/fetchAndBase64Encode.mjs +52 -0
- package/src-ext/runtime/_shared/relayObjectToBlock.mjs +307 -0
- package/src-ext/runtime/_shared/sessionPicker.mjs +418 -0
- package/src-ext/runtime/_shared/slashCommandRouter.mjs +1346 -0
- package/src-ext/runtime/_shared/todoTranslatorUtils.mjs +94 -0
- package/src-ext/runtime/claude/askUserQuestionTranslator.mjs +241 -0
- package/src-ext/runtime/claude/customCommandsCollector.mjs +132 -0
- package/src-ext/runtime/claude/handleRequest.mjs +1418 -0
- package/src-ext/runtime/claude/index.mjs +160 -0
- package/src-ext/runtime/claude/internalCommandNames.mjs +58 -0
- package/src-ext/runtime/claude/launcher.mjs +313 -0
- package/src-ext/runtime/claude/mcpConfigAdapter.mjs +174 -0
- package/src-ext/runtime/claude/mcpConfigWriter.mjs +57 -0
- package/src-ext/runtime/claude/modelContextWindow.mjs +30 -0
- package/src-ext/runtime/claude/modelScanner.mjs +257 -0
- package/src-ext/runtime/claude/permissionInterruptBlock.mjs +86 -0
- package/src-ext/runtime/claude/permissionPromptServer.mjs +170 -0
- package/src-ext/runtime/claude/permissionPromptServerWorker.mjs +214 -0
- package/src-ext/runtime/claude/preflight.mjs +62 -0
- package/src-ext/runtime/claude/stdoutParser.mjs +365 -0
- package/src-ext/runtime/claude/todoTranslator.mjs +66 -0
- package/src-ext/runtime/claude/usageProbe.mjs +576 -0
- package/src-ext/runtime/claude/usageProbeStrip.mjs +14 -0
- package/src-ext/runtime/codex/codexCustomCommandsCollector.mjs +87 -0
- package/src-ext/runtime/codex/elicitationHandler.mjs +91 -0
- package/src-ext/runtime/codex/eventMapper.mjs +154 -0
- package/src-ext/runtime/codex/handleRequest.mjs +1152 -0
- package/src-ext/runtime/codex/index.mjs +142 -0
- package/src-ext/runtime/codex/mcpConfigAdapter.mjs +155 -0
- package/src-ext/runtime/codex/modelScanner.mjs +305 -0
- package/src-ext/runtime/codex/planNotificationHandler.mjs +61 -0
- package/src-ext/runtime/codex/preflight.mjs +67 -0
- package/src-ext/runtime/hermes/commandBackend.mjs +117 -0
- package/src-ext/runtime/hermes/envSetup.mjs +229 -0
- package/src-ext/runtime/hermes/gatewayManager.mjs +625 -0
- package/src-ext/runtime/hermes/handleRequest.mjs +1257 -0
- package/src-ext/runtime/hermes/httpBackend.mjs +219 -0
- package/src-ext/runtime/hermes/index.mjs +196 -0
- package/src-ext/runtime/hermes/mcpConfigAdapter.mjs +189 -0
- package/src-ext/runtime/hermes/preflight.mjs +359 -0
- package/src-ext/runtime/hermes/responsesBackend.mjs +276 -0
- package/src-ext/runtime/hermes/sessionStore.mjs +78 -0
- package/src-ext/runtime/hermes/todoTranslator.mjs +90 -0
- package/src-ext/runtime/hermes/workspaceRpc.mjs +124 -0
- package/src-ext/runtime/openclaw/buildOpenclawDaemonInput.mjs +143 -0
- package/src-ext/runtime/openclaw/mcpConfigAdapter.mjs +91 -0
- package/src-ext/runtime/openclaw/openclawConfig.mjs +145 -0
- package/src-ext/runtime/picoclaw/constants.mjs +39 -0
- package/src-ext/runtime/picoclaw/handleRequest.mjs +289 -0
- package/src-ext/runtime/picoclaw/index.mjs +314 -0
- package/src-ext/runtime/picoclaw/pairFlow.mjs +224 -0
- package/src-ext/runtime/picoclaw/state.mjs +78 -0
- package/src-ext/runtime/picoclaw/todoTranslator.mjs +67 -0
- package/src-ext/runtime/picoclaw/translator.mjs +272 -0
- package/src-ext/runtime/picoclaw/wsClient.mjs +129 -0
- package/src-ext/service/serviceManager.mjs +521 -0
- package/src-shared/README.md +44 -0
- package/src-shared/chat_push_normalizer.mjs +113 -0
- package/src-shared/correlation.mjs +25 -0
- package/src-shared/envelope_builder.mjs +612 -0
- package/src-shared/logger.mjs +167 -0
- package/src-shared/relay-defaults.json +3 -0
- package/src-shared/relay_revoke.mjs +46 -0
- package/src-shared/vendor/README.md +60 -0
- package/src-shared/vendor/smol-toml/LICENSE +24 -0
- package/src-shared/vendor/smol-toml/README.md +236 -0
- package/src-shared/vendor/smol-toml/VENDORED.md +37 -0
- package/src-shared/vendor/smol-toml/dist/date.d.ts +41 -0
- package/src-shared/vendor/smol-toml/dist/date.js +127 -0
- package/src-shared/vendor/smol-toml/dist/error.d.ts +38 -0
- package/src-shared/vendor/smol-toml/dist/error.js +63 -0
- package/src-shared/vendor/smol-toml/dist/extract.d.ts +30 -0
- package/src-shared/vendor/smol-toml/dist/extract.js +109 -0
- package/src-shared/vendor/smol-toml/dist/index.cjs +900 -0
- package/src-shared/vendor/smol-toml/dist/index.d.ts +43 -0
- package/src-shared/vendor/smol-toml/dist/index.js +33 -0
- package/src-shared/vendor/smol-toml/dist/parse.d.ts +37 -0
- package/src-shared/vendor/smol-toml/dist/parse.js +148 -0
- package/src-shared/vendor/smol-toml/dist/primitive.d.ts +31 -0
- package/src-shared/vendor/smol-toml/dist/primitive.js +178 -0
- package/src-shared/vendor/smol-toml/dist/stringify.d.ts +31 -0
- package/src-shared/vendor/smol-toml/dist/stringify.js +163 -0
- package/src-shared/vendor/smol-toml/dist/struct.d.ts +32 -0
- package/src-shared/vendor/smol-toml/dist/struct.js +194 -0
- package/src-shared/vendor/smol-toml/dist/util.d.ts +42 -0
- package/src-shared/vendor/smol-toml/dist/util.js +100 -0
- package/src-shared/vendor/smol-toml/package.json +54 -0
- package/src-shared/vendor/yaml/LICENSE +13 -0
- package/src-shared/vendor/yaml/README.md +155 -0
- package/src-shared/vendor/yaml/VENDORED.md +29 -0
- package/src-shared/vendor/yaml/bin.mjs +11 -0
- package/src-shared/vendor/yaml/browser/dist/compose/compose-collection.js +88 -0
- package/src-shared/vendor/yaml/browser/dist/compose/compose-doc.js +42 -0
- package/src-shared/vendor/yaml/browser/dist/compose/compose-node.js +92 -0
- package/src-shared/vendor/yaml/browser/dist/compose/compose-scalar.js +80 -0
- package/src-shared/vendor/yaml/browser/dist/compose/composer.js +217 -0
- package/src-shared/vendor/yaml/browser/dist/compose/resolve-block-map.js +113 -0
- package/src-shared/vendor/yaml/browser/dist/compose/resolve-block-scalar.js +198 -0
- package/src-shared/vendor/yaml/browser/dist/compose/resolve-block-seq.js +47 -0
- package/src-shared/vendor/yaml/browser/dist/compose/resolve-end.js +37 -0
- package/src-shared/vendor/yaml/browser/dist/compose/resolve-flow-collection.js +203 -0
- package/src-shared/vendor/yaml/browser/dist/compose/resolve-flow-scalar.js +223 -0
- package/src-shared/vendor/yaml/browser/dist/compose/resolve-props.js +148 -0
- package/src-shared/vendor/yaml/browser/dist/compose/util-contains-newline.js +34 -0
- package/src-shared/vendor/yaml/browser/dist/compose/util-empty-scalar-position.js +27 -0
- package/src-shared/vendor/yaml/browser/dist/compose/util-flow-indent-check.js +15 -0
- package/src-shared/vendor/yaml/browser/dist/compose/util-map-includes.js +17 -0
- package/src-shared/vendor/yaml/browser/dist/doc/Document.js +334 -0
- package/src-shared/vendor/yaml/browser/dist/doc/anchors.js +72 -0
- package/src-shared/vendor/yaml/browser/dist/doc/applyReviver.js +55 -0
- package/src-shared/vendor/yaml/browser/dist/doc/createNode.js +89 -0
- package/src-shared/vendor/yaml/browser/dist/doc/directives.js +176 -0
- package/src-shared/vendor/yaml/browser/dist/errors.js +57 -0
- package/src-shared/vendor/yaml/browser/dist/index.js +17 -0
- package/src-shared/vendor/yaml/browser/dist/log.js +14 -0
- package/src-shared/vendor/yaml/browser/dist/nodes/Alias.js +101 -0
- package/src-shared/vendor/yaml/browser/dist/nodes/Collection.js +147 -0
- package/src-shared/vendor/yaml/browser/dist/nodes/Node.js +38 -0
- package/src-shared/vendor/yaml/browser/dist/nodes/Pair.js +36 -0
- package/src-shared/vendor/yaml/browser/dist/nodes/Scalar.js +24 -0
- package/src-shared/vendor/yaml/browser/dist/nodes/YAMLMap.js +144 -0
- package/src-shared/vendor/yaml/browser/dist/nodes/YAMLSeq.js +113 -0
- package/src-shared/vendor/yaml/browser/dist/nodes/addPairToJSMap.js +104 -0
- package/src-shared/vendor/yaml/browser/dist/nodes/identity.js +36 -0
- package/src-shared/vendor/yaml/browser/dist/nodes/toJS.js +37 -0
- package/src-shared/vendor/yaml/browser/dist/parse/cst-scalar.js +214 -0
- package/src-shared/vendor/yaml/browser/dist/parse/cst-stringify.js +61 -0
- package/src-shared/vendor/yaml/browser/dist/parse/cst-visit.js +97 -0
- package/src-shared/vendor/yaml/browser/dist/parse/cst.js +98 -0
- package/src-shared/vendor/yaml/browser/dist/parse/lexer.js +717 -0
- package/src-shared/vendor/yaml/browser/dist/parse/line-counter.js +39 -0
- package/src-shared/vendor/yaml/browser/dist/parse/parser.js +954 -0
- package/src-shared/vendor/yaml/browser/dist/public-api.js +99 -0
- package/src-shared/vendor/yaml/browser/dist/schema/Schema.js +38 -0
- package/src-shared/vendor/yaml/browser/dist/schema/common/map.js +17 -0
- package/src-shared/vendor/yaml/browser/dist/schema/common/null.js +15 -0
- package/src-shared/vendor/yaml/browser/dist/schema/common/seq.js +17 -0
- package/src-shared/vendor/yaml/browser/dist/schema/common/string.js +14 -0
- package/src-shared/vendor/yaml/browser/dist/schema/core/bool.js +19 -0
- package/src-shared/vendor/yaml/browser/dist/schema/core/float.js +43 -0
- package/src-shared/vendor/yaml/browser/dist/schema/core/int.js +38 -0
- package/src-shared/vendor/yaml/browser/dist/schema/core/schema.js +23 -0
- package/src-shared/vendor/yaml/browser/dist/schema/json/schema.js +62 -0
- package/src-shared/vendor/yaml/browser/dist/schema/tags.js +83 -0
- package/src-shared/vendor/yaml/browser/dist/schema/yaml-1.1/binary.js +66 -0
- package/src-shared/vendor/yaml/browser/dist/schema/yaml-1.1/bool.js +26 -0
- package/src-shared/vendor/yaml/browser/dist/schema/yaml-1.1/float.js +46 -0
- package/src-shared/vendor/yaml/browser/dist/schema/yaml-1.1/int.js +71 -0
- package/src-shared/vendor/yaml/browser/dist/schema/yaml-1.1/omap.js +74 -0
- package/src-shared/vendor/yaml/browser/dist/schema/yaml-1.1/pairs.js +78 -0
- package/src-shared/vendor/yaml/browser/dist/schema/yaml-1.1/schema.js +37 -0
- package/src-shared/vendor/yaml/browser/dist/schema/yaml-1.1/set.js +93 -0
- package/src-shared/vendor/yaml/browser/dist/schema/yaml-1.1/timestamp.js +101 -0
- package/src-shared/vendor/yaml/browser/dist/stringify/foldFlowLines.js +146 -0
- package/src-shared/vendor/yaml/browser/dist/stringify/stringify.js +124 -0
- package/src-shared/vendor/yaml/browser/dist/stringify/stringifyCollection.js +143 -0
- package/src-shared/vendor/yaml/browser/dist/stringify/stringifyComment.js +20 -0
- package/src-shared/vendor/yaml/browser/dist/stringify/stringifyDocument.js +85 -0
- package/src-shared/vendor/yaml/browser/dist/stringify/stringifyNumber.js +24 -0
- package/src-shared/vendor/yaml/browser/dist/stringify/stringifyPair.js +150 -0
- package/src-shared/vendor/yaml/browser/dist/stringify/stringifyString.js +328 -0
- package/src-shared/vendor/yaml/browser/dist/util.js +11 -0
- package/src-shared/vendor/yaml/browser/dist/visit.js +233 -0
- package/src-shared/vendor/yaml/browser/index.js +5 -0
- package/src-shared/vendor/yaml/browser/package.json +3 -0
- package/src-shared/vendor/yaml/dist/cli.d.ts +8 -0
- package/src-shared/vendor/yaml/dist/cli.mjs +199 -0
- package/src-shared/vendor/yaml/dist/compose/compose-collection.d.ts +11 -0
- package/src-shared/vendor/yaml/dist/compose/compose-collection.js +90 -0
- package/src-shared/vendor/yaml/dist/compose/compose-doc.d.ts +7 -0
- package/src-shared/vendor/yaml/dist/compose/compose-doc.js +44 -0
- package/src-shared/vendor/yaml/dist/compose/compose-node.d.ts +28 -0
- package/src-shared/vendor/yaml/dist/compose/compose-node.js +95 -0
- package/src-shared/vendor/yaml/dist/compose/compose-scalar.d.ts +5 -0
- package/src-shared/vendor/yaml/dist/compose/compose-scalar.js +82 -0
- package/src-shared/vendor/yaml/dist/compose/composer.d.ts +62 -0
- package/src-shared/vendor/yaml/dist/compose/composer.js +221 -0
- package/src-shared/vendor/yaml/dist/compose/resolve-block-map.d.ts +6 -0
- package/src-shared/vendor/yaml/dist/compose/resolve-block-map.js +115 -0
- package/src-shared/vendor/yaml/dist/compose/resolve-block-scalar.d.ts +11 -0
- package/src-shared/vendor/yaml/dist/compose/resolve-block-scalar.js +200 -0
- package/src-shared/vendor/yaml/dist/compose/resolve-block-seq.d.ts +6 -0
- package/src-shared/vendor/yaml/dist/compose/resolve-block-seq.js +49 -0
- package/src-shared/vendor/yaml/dist/compose/resolve-end.d.ts +6 -0
- package/src-shared/vendor/yaml/dist/compose/resolve-end.js +39 -0
- package/src-shared/vendor/yaml/dist/compose/resolve-flow-collection.d.ts +7 -0
- package/src-shared/vendor/yaml/dist/compose/resolve-flow-collection.js +205 -0
- package/src-shared/vendor/yaml/dist/compose/resolve-flow-scalar.d.ts +10 -0
- package/src-shared/vendor/yaml/dist/compose/resolve-flow-scalar.js +225 -0
- package/src-shared/vendor/yaml/dist/compose/resolve-props.d.ts +23 -0
- package/src-shared/vendor/yaml/dist/compose/resolve-props.js +150 -0
- package/src-shared/vendor/yaml/dist/compose/util-contains-newline.d.ts +2 -0
- package/src-shared/vendor/yaml/dist/compose/util-contains-newline.js +36 -0
- package/src-shared/vendor/yaml/dist/compose/util-empty-scalar-position.d.ts +2 -0
- package/src-shared/vendor/yaml/dist/compose/util-empty-scalar-position.js +29 -0
- package/src-shared/vendor/yaml/dist/compose/util-flow-indent-check.d.ts +3 -0
- package/src-shared/vendor/yaml/dist/compose/util-flow-indent-check.js +17 -0
- package/src-shared/vendor/yaml/dist/compose/util-map-includes.d.ts +4 -0
- package/src-shared/vendor/yaml/dist/compose/util-map-includes.js +19 -0
- package/src-shared/vendor/yaml/dist/doc/Document.d.ts +141 -0
- package/src-shared/vendor/yaml/dist/doc/Document.js +336 -0
- package/src-shared/vendor/yaml/dist/doc/anchors.d.ts +24 -0
- package/src-shared/vendor/yaml/dist/doc/anchors.js +77 -0
- package/src-shared/vendor/yaml/dist/doc/applyReviver.d.ts +9 -0
- package/src-shared/vendor/yaml/dist/doc/applyReviver.js +57 -0
- package/src-shared/vendor/yaml/dist/doc/createNode.d.ts +17 -0
- package/src-shared/vendor/yaml/dist/doc/createNode.js +91 -0
- package/src-shared/vendor/yaml/dist/doc/directives.d.ts +49 -0
- package/src-shared/vendor/yaml/dist/doc/directives.js +178 -0
- package/src-shared/vendor/yaml/dist/errors.d.ts +21 -0
- package/src-shared/vendor/yaml/dist/errors.js +62 -0
- package/src-shared/vendor/yaml/dist/index.d.ts +22 -0
- package/src-shared/vendor/yaml/dist/index.js +50 -0
- package/src-shared/vendor/yaml/dist/log.d.ts +3 -0
- package/src-shared/vendor/yaml/dist/log.js +17 -0
- package/src-shared/vendor/yaml/dist/nodes/Alias.d.ts +28 -0
- package/src-shared/vendor/yaml/dist/nodes/Alias.js +103 -0
- package/src-shared/vendor/yaml/dist/nodes/Collection.d.ts +73 -0
- package/src-shared/vendor/yaml/dist/nodes/Collection.js +151 -0
- package/src-shared/vendor/yaml/dist/nodes/Node.d.ts +47 -0
- package/src-shared/vendor/yaml/dist/nodes/Node.js +40 -0
- package/src-shared/vendor/yaml/dist/nodes/Pair.d.ts +21 -0
- package/src-shared/vendor/yaml/dist/nodes/Pair.js +39 -0
- package/src-shared/vendor/yaml/dist/nodes/Scalar.d.ts +42 -0
- package/src-shared/vendor/yaml/dist/nodes/Scalar.js +27 -0
- package/src-shared/vendor/yaml/dist/nodes/YAMLMap.d.ts +53 -0
- package/src-shared/vendor/yaml/dist/nodes/YAMLMap.js +147 -0
- package/src-shared/vendor/yaml/dist/nodes/YAMLSeq.d.ts +60 -0
- package/src-shared/vendor/yaml/dist/nodes/YAMLSeq.js +115 -0
- package/src-shared/vendor/yaml/dist/nodes/addPairToJSMap.d.ts +4 -0
- package/src-shared/vendor/yaml/dist/nodes/addPairToJSMap.js +106 -0
- package/src-shared/vendor/yaml/dist/nodes/identity.d.ts +23 -0
- package/src-shared/vendor/yaml/dist/nodes/identity.js +53 -0
- package/src-shared/vendor/yaml/dist/nodes/toJS.d.ts +27 -0
- package/src-shared/vendor/yaml/dist/nodes/toJS.js +39 -0
- package/src-shared/vendor/yaml/dist/options.d.ts +338 -0
- package/src-shared/vendor/yaml/dist/parse/cst-scalar.d.ts +64 -0
- package/src-shared/vendor/yaml/dist/parse/cst-scalar.js +218 -0
- package/src-shared/vendor/yaml/dist/parse/cst-stringify.d.ts +8 -0
- package/src-shared/vendor/yaml/dist/parse/cst-stringify.js +63 -0
- package/src-shared/vendor/yaml/dist/parse/cst-visit.d.ts +39 -0
- package/src-shared/vendor/yaml/dist/parse/cst-visit.js +99 -0
- package/src-shared/vendor/yaml/dist/parse/cst.d.ts +108 -0
- package/src-shared/vendor/yaml/dist/parse/cst.js +112 -0
- package/src-shared/vendor/yaml/dist/parse/lexer.d.ts +87 -0
- package/src-shared/vendor/yaml/dist/parse/lexer.js +719 -0
- package/src-shared/vendor/yaml/dist/parse/line-counter.d.ts +22 -0
- package/src-shared/vendor/yaml/dist/parse/line-counter.js +41 -0
- package/src-shared/vendor/yaml/dist/parse/parser.d.ts +84 -0
- package/src-shared/vendor/yaml/dist/parse/parser.js +958 -0
- package/src-shared/vendor/yaml/dist/public-api.d.ts +43 -0
- package/src-shared/vendor/yaml/dist/public-api.js +104 -0
- package/src-shared/vendor/yaml/dist/schema/Schema.d.ts +18 -0
- package/src-shared/vendor/yaml/dist/schema/Schema.js +40 -0
- package/src-shared/vendor/yaml/dist/schema/common/map.d.ts +2 -0
- package/src-shared/vendor/yaml/dist/schema/common/map.js +19 -0
- package/src-shared/vendor/yaml/dist/schema/common/null.d.ts +4 -0
- package/src-shared/vendor/yaml/dist/schema/common/null.js +17 -0
- package/src-shared/vendor/yaml/dist/schema/common/seq.d.ts +2 -0
- package/src-shared/vendor/yaml/dist/schema/common/seq.js +19 -0
- package/src-shared/vendor/yaml/dist/schema/common/string.d.ts +2 -0
- package/src-shared/vendor/yaml/dist/schema/common/string.js +16 -0
- package/src-shared/vendor/yaml/dist/schema/core/bool.d.ts +4 -0
- package/src-shared/vendor/yaml/dist/schema/core/bool.js +21 -0
- package/src-shared/vendor/yaml/dist/schema/core/float.d.ts +4 -0
- package/src-shared/vendor/yaml/dist/schema/core/float.js +47 -0
- package/src-shared/vendor/yaml/dist/schema/core/int.d.ts +4 -0
- package/src-shared/vendor/yaml/dist/schema/core/int.js +42 -0
- package/src-shared/vendor/yaml/dist/schema/core/schema.d.ts +1 -0
- package/src-shared/vendor/yaml/dist/schema/core/schema.js +25 -0
- package/src-shared/vendor/yaml/dist/schema/json/schema.d.ts +2 -0
- package/src-shared/vendor/yaml/dist/schema/json/schema.js +64 -0
- package/src-shared/vendor/yaml/dist/schema/json-schema.d.ts +69 -0
- package/src-shared/vendor/yaml/dist/schema/tags.d.ts +40 -0
- package/src-shared/vendor/yaml/dist/schema/tags.js +86 -0
- package/src-shared/vendor/yaml/dist/schema/types.d.ts +90 -0
- package/src-shared/vendor/yaml/dist/schema/yaml-1.1/binary.d.ts +2 -0
- package/src-shared/vendor/yaml/dist/schema/yaml-1.1/binary.js +68 -0
- package/src-shared/vendor/yaml/dist/schema/yaml-1.1/bool.d.ts +7 -0
- package/src-shared/vendor/yaml/dist/schema/yaml-1.1/bool.js +29 -0
- package/src-shared/vendor/yaml/dist/schema/yaml-1.1/float.d.ts +4 -0
- package/src-shared/vendor/yaml/dist/schema/yaml-1.1/float.js +50 -0
- package/src-shared/vendor/yaml/dist/schema/yaml-1.1/int.d.ts +5 -0
- package/src-shared/vendor/yaml/dist/schema/yaml-1.1/int.js +76 -0
- package/src-shared/vendor/yaml/dist/schema/yaml-1.1/omap.d.ts +28 -0
- package/src-shared/vendor/yaml/dist/schema/yaml-1.1/omap.js +77 -0
- package/src-shared/vendor/yaml/dist/schema/yaml-1.1/pairs.d.ts +10 -0
- package/src-shared/vendor/yaml/dist/schema/yaml-1.1/pairs.js +82 -0
- package/src-shared/vendor/yaml/dist/schema/yaml-1.1/schema.d.ts +1 -0
- package/src-shared/vendor/yaml/dist/schema/yaml-1.1/schema.js +39 -0
- package/src-shared/vendor/yaml/dist/schema/yaml-1.1/set.d.ts +28 -0
- package/src-shared/vendor/yaml/dist/schema/yaml-1.1/set.js +96 -0
- package/src-shared/vendor/yaml/dist/schema/yaml-1.1/timestamp.d.ts +6 -0
- package/src-shared/vendor/yaml/dist/schema/yaml-1.1/timestamp.js +105 -0
- package/src-shared/vendor/yaml/dist/stringify/foldFlowLines.d.ts +34 -0
- package/src-shared/vendor/yaml/dist/stringify/foldFlowLines.js +151 -0
- package/src-shared/vendor/yaml/dist/stringify/stringify.d.ts +21 -0
- package/src-shared/vendor/yaml/dist/stringify/stringify.js +127 -0
- package/src-shared/vendor/yaml/dist/stringify/stringifyCollection.d.ts +17 -0
- package/src-shared/vendor/yaml/dist/stringify/stringifyCollection.js +145 -0
- package/src-shared/vendor/yaml/dist/stringify/stringifyComment.d.ts +10 -0
- package/src-shared/vendor/yaml/dist/stringify/stringifyComment.js +24 -0
- package/src-shared/vendor/yaml/dist/stringify/stringifyDocument.d.ts +4 -0
- package/src-shared/vendor/yaml/dist/stringify/stringifyDocument.js +87 -0
- package/src-shared/vendor/yaml/dist/stringify/stringifyNumber.d.ts +2 -0
- package/src-shared/vendor/yaml/dist/stringify/stringifyNumber.js +26 -0
- package/src-shared/vendor/yaml/dist/stringify/stringifyPair.d.ts +3 -0
- package/src-shared/vendor/yaml/dist/stringify/stringifyPair.js +152 -0
- package/src-shared/vendor/yaml/dist/stringify/stringifyString.d.ts +9 -0
- package/src-shared/vendor/yaml/dist/stringify/stringifyString.js +330 -0
- package/src-shared/vendor/yaml/dist/test-events.d.ts +4 -0
- package/src-shared/vendor/yaml/dist/test-events.js +134 -0
- package/src-shared/vendor/yaml/dist/util.d.ts +12 -0
- package/src-shared/vendor/yaml/dist/util.js +28 -0
- package/src-shared/vendor/yaml/dist/visit.d.ts +102 -0
- package/src-shared/vendor/yaml/dist/visit.js +236 -0
- package/src-shared/vendor/yaml/package.json +96 -0
- package/src-shared/vendor/yaml/util.js +2 -0
|
@@ -0,0 +1,439 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* unifiedDispatchHandler.mjs — D-C1
|
|
3
|
+
*
|
|
4
|
+
* createUnifiedDispatchHandler({ relayUrl, bridgeToken, log, producerFactory?, openclawInvoke? })
|
|
5
|
+
* → async (request, ctx) => boolean
|
|
6
|
+
*
|
|
7
|
+
* Returns true if the request was consumed (agent.run.dispatch), false otherwise.
|
|
8
|
+
* The handler is idempotent per run_id (in-memory Set, process-scoped).
|
|
9
|
+
*
|
|
10
|
+
* Injection points for testing:
|
|
11
|
+
* producerFactory({ relayUrl, bridgeToken, threadId }) → { postEvents }
|
|
12
|
+
* openclawInvoke({ input, onChunk }) → async void (default: picoclaw wsHolder)
|
|
13
|
+
* flushPolicy({ size, timeMs }) → 控制 micro-batched flush 触发阈值(默认 {size:1, timeMs:0} —— 即来即发,保流式体验)
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
import { createHookedUnifiedProducer } from "../../src/unified_producer.mjs";
|
|
17
|
+
import {
|
|
18
|
+
translateOpenClawChunkToEnvelope,
|
|
19
|
+
buildRunCompleted,
|
|
20
|
+
buildRunFailed,
|
|
21
|
+
newBlockState,
|
|
22
|
+
} from "../../src-shared/envelope_builder.mjs";
|
|
23
|
+
import { applyIncomingHooks } from "./bridgeHooks.mjs";
|
|
24
|
+
import { preprocessAttachmentsToText } from "./preprocessAttachments.mjs";
|
|
25
|
+
import { BoundedSet } from "./boundedMap.mjs";
|
|
26
|
+
import { resolveCosImageRefs } from "../runtime/_shared/cosTextRefs.mjs";
|
|
27
|
+
import { signGetCosObject } from "./cosUploadClient.mjs";
|
|
28
|
+
|
|
29
|
+
// Streaming-first policy: size:1 means every envelope (especially
|
|
30
|
+
// content_block.delta) posts immediately, so the App SSE stream receives
|
|
31
|
+
// chunks as the runtime produces them rather than in 8-envelope batches.
|
|
32
|
+
// Tests/picoclaw replay can override via flushPolicy if they want fewer POSTs.
|
|
33
|
+
const DEFAULT_FLUSH_POLICY = Object.freeze({ size: 1, timeMs: 0 });
|
|
34
|
+
const STREAM_DEBUG = process.env.BUBBO_DEBUG_STREAM === "1";
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* createEnvelopeFlusher — micro-batched fire-and-forget envelope publisher.
|
|
38
|
+
*
|
|
39
|
+
* Solves Bug #2-streaming: previous implementation accumulated all envelopes
|
|
40
|
+
* into a single array and POSTed once per turn → "fake streaming" experience.
|
|
41
|
+
* The flusher enqueues envelopes and flushes when either size threshold or
|
|
42
|
+
* time threshold fires (whichever comes first). flush calls `postEvents`
|
|
43
|
+
* fire-and-forget; `waitAll()` lets the caller await all in-flight POSTs
|
|
44
|
+
* before declaring the turn complete.
|
|
45
|
+
*
|
|
46
|
+
* Retry: a single failed `postEvents` call pushes its snapshot into
|
|
47
|
+
* `retryQueue`; the next flush drains retryQueue before mainQueue so order
|
|
48
|
+
* is preserved (modulo server-side ord field which is authoritative).
|
|
49
|
+
*
|
|
50
|
+
* @param {{
|
|
51
|
+
* postEvents: (envelopes: any[]) => Promise<any>,
|
|
52
|
+
* flushPolicy?: { size?: number, timeMs?: number },
|
|
53
|
+
* log?: { info?: Function, warn?: Function, error?: Function },
|
|
54
|
+
* }} opts
|
|
55
|
+
*/
|
|
56
|
+
export function createEnvelopeFlusher({ postEvents, flushPolicy, log }) {
|
|
57
|
+
if (typeof postEvents !== "function") {
|
|
58
|
+
throw new Error("createEnvelopeFlusher: postEvents required");
|
|
59
|
+
}
|
|
60
|
+
const policy = {
|
|
61
|
+
size: flushPolicy?.size ?? DEFAULT_FLUSH_POLICY.size,
|
|
62
|
+
timeMs: flushPolicy?.timeMs ?? DEFAULT_FLUSH_POLICY.timeMs,
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
const mainQueue = [];
|
|
66
|
+
const retryQueue = [];
|
|
67
|
+
const inFlight = new Set();
|
|
68
|
+
let timer = null;
|
|
69
|
+
let retriedOnce = false; // 整 turn 最多 retry 1 次(per design D4.5)
|
|
70
|
+
// postChain 是串行 POST tail —— 每次 flushNow 把自己 chain 到上一个 POST
|
|
71
|
+
// 的 .finally 后面再发,确保 server 看到的 envelope 顺序与 enqueue 顺序一致。
|
|
72
|
+
// 这是为了配合 size:1 的真流式:之前 fire-and-forget 的并发 POST 会被 JS
|
|
73
|
+
// event loop 乱序投递到 relay,导致 content_block.delta 早于
|
|
74
|
+
// content_block.started 到达 → server §9.4 orphan-delta 400 → run.completed
|
|
75
|
+
// 也跟着乱序,App 端 reducer 的 cursor 单调过滤把整轮事件全跳过 → 永远 loading。
|
|
76
|
+
let postChain = Promise.resolve();
|
|
77
|
+
|
|
78
|
+
function debugLog(msg, extra) {
|
|
79
|
+
if (STREAM_DEBUG && log?.info) {
|
|
80
|
+
log.info(msg, "stream.flush", { ts: Date.now(), ...extra });
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
function startTimerIfNeeded() {
|
|
85
|
+
if (timer != null) return;
|
|
86
|
+
if (mainQueue.length === 0 && retryQueue.length === 0) return;
|
|
87
|
+
timer = setTimeout(() => {
|
|
88
|
+
timer = null;
|
|
89
|
+
flushNow();
|
|
90
|
+
}, policy.timeMs);
|
|
91
|
+
// Don't keep event loop alive solely for this timer.
|
|
92
|
+
if (typeof timer?.unref === "function") timer.unref();
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
function clearTimer() {
|
|
96
|
+
if (timer != null) {
|
|
97
|
+
clearTimeout(timer);
|
|
98
|
+
timer = null;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* splitAndRetry — 413 binary split recursive retry.
|
|
104
|
+
* When a batch returns 413 (stream.payload_too_large), split into halves and
|
|
105
|
+
* retry each independently. Stops at size=1 and logs stream.envelope_too_large.
|
|
106
|
+
* Does NOT consume the turn-level retriedOnce quota.
|
|
107
|
+
*
|
|
108
|
+
* @param {any[]} envelopes
|
|
109
|
+
* @returns {Promise<void>}
|
|
110
|
+
*/
|
|
111
|
+
async function splitAndRetry(envelopes) {
|
|
112
|
+
if (envelopes.length === 0) return;
|
|
113
|
+
|
|
114
|
+
if (envelopes.length === 1) {
|
|
115
|
+
// Terminal: single envelope still 413 → log error, skip it
|
|
116
|
+
const env = envelopes[0];
|
|
117
|
+
log?.error?.("stream.envelope_too_large", "single envelope exceeds relay size limit — skipping", {
|
|
118
|
+
event: "stream.envelope_too_large",
|
|
119
|
+
envelope_type: env?.type,
|
|
120
|
+
envelope_index: env?.index,
|
|
121
|
+
});
|
|
122
|
+
return;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
// Split into two halves
|
|
126
|
+
const mid = Math.floor(envelopes.length / 2);
|
|
127
|
+
const first = envelopes.slice(0, mid);
|
|
128
|
+
const second = envelopes.slice(mid);
|
|
129
|
+
|
|
130
|
+
for (const half of [first, second]) {
|
|
131
|
+
try {
|
|
132
|
+
await postEvents(half);
|
|
133
|
+
} catch (err) {
|
|
134
|
+
if (err?.code === "stream.payload_too_large") {
|
|
135
|
+
// Recurse
|
|
136
|
+
await splitAndRetry(half);
|
|
137
|
+
} else {
|
|
138
|
+
// Non-413 error: re-throw or handle as regular failure
|
|
139
|
+
log?.warn?.("stream.post_failed", "postEvents failed during split retry", {
|
|
140
|
+
err: err?.message || String(err),
|
|
141
|
+
batch_size: half.length,
|
|
142
|
+
});
|
|
143
|
+
// Don't consume retriedOnce here; treat like a transient failure
|
|
144
|
+
if (!retriedOnce) {
|
|
145
|
+
retryQueue.unshift(...half);
|
|
146
|
+
retriedOnce = true;
|
|
147
|
+
startTimerIfNeeded();
|
|
148
|
+
} else {
|
|
149
|
+
log?.error?.("stream.post_failed.final", "postEvents failed in split retry — giving up", {
|
|
150
|
+
err: err?.message || String(err),
|
|
151
|
+
batch_size: half.length,
|
|
152
|
+
});
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
function flushNow() {
|
|
160
|
+
clearTimer();
|
|
161
|
+
const fromRetry = retryQueue.splice(0, retryQueue.length);
|
|
162
|
+
const fromMain = mainQueue.splice(0, mainQueue.length);
|
|
163
|
+
const snapshot = [...fromRetry, ...fromMain];
|
|
164
|
+
if (snapshot.length === 0) return;
|
|
165
|
+
|
|
166
|
+
debugLog("flushing snapshot", { size: snapshot.length });
|
|
167
|
+
// 串行化:等前一个 POST 完结(成功 / 失败都行)再发本次 POST,避免顺序乱。
|
|
168
|
+
const p = postChain
|
|
169
|
+
.catch(() => {}) // 上一次失败不阻断后续
|
|
170
|
+
.then(() => postEvents(snapshot))
|
|
171
|
+
.catch(async (err) => {
|
|
172
|
+
if (err?.code === "stream.payload_too_large") {
|
|
173
|
+
// 413: binary split and retry — does NOT consume retriedOnce quota
|
|
174
|
+
debugLog("413 split retry", { size: snapshot.length });
|
|
175
|
+
await splitAndRetry(snapshot);
|
|
176
|
+
return;
|
|
177
|
+
}
|
|
178
|
+
log?.warn?.("stream.post_failed", "postEvents failed in flush", {
|
|
179
|
+
err: err?.message || String(err),
|
|
180
|
+
batch_size: snapshot.length,
|
|
181
|
+
});
|
|
182
|
+
// Retry once per turn: push the failed snapshot back into retryQueue
|
|
183
|
+
// so the next flush drains it before any new envelope.
|
|
184
|
+
if (!retriedOnce) {
|
|
185
|
+
retryQueue.unshift(...snapshot);
|
|
186
|
+
retriedOnce = true;
|
|
187
|
+
startTimerIfNeeded();
|
|
188
|
+
} else {
|
|
189
|
+
log?.error?.("stream.post_failed.final", "postEvents failed twice — giving up", {
|
|
190
|
+
err: err?.message || String(err),
|
|
191
|
+
batch_size: snapshot.length,
|
|
192
|
+
});
|
|
193
|
+
}
|
|
194
|
+
})
|
|
195
|
+
.finally(() => inFlight.delete(p));
|
|
196
|
+
inFlight.add(p);
|
|
197
|
+
postChain = p; // 下一次 flush 会等这次 settle 再发
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
function enqueue(envelopes) {
|
|
201
|
+
if (!envelopes || envelopes.length === 0) return;
|
|
202
|
+
for (const env of envelopes) mainQueue.push(env);
|
|
203
|
+
if (mainQueue.length + retryQueue.length >= policy.size) {
|
|
204
|
+
flushNow();
|
|
205
|
+
} else {
|
|
206
|
+
startTimerIfNeeded();
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
async function waitAll() {
|
|
211
|
+
// Keep flushing until both queues are drained AND no in-flight POSTs.
|
|
212
|
+
// A retry can push envelopes back into retryQueue after the first flush,
|
|
213
|
+
// so a single flush+await pair is insufficient. The loop bounds itself
|
|
214
|
+
// because retry is capped at once per turn (retriedOnce gate).
|
|
215
|
+
while (mainQueue.length > 0 || retryQueue.length > 0 || inFlight.size > 0) {
|
|
216
|
+
if (mainQueue.length > 0 || retryQueue.length > 0) {
|
|
217
|
+
flushNow();
|
|
218
|
+
}
|
|
219
|
+
if (inFlight.size > 0) {
|
|
220
|
+
await Promise.allSettled([...inFlight]);
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
return { enqueue, flushNow, waitAll, _policy: policy };
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
/**
|
|
229
|
+
* @param {{
|
|
230
|
+
* relayUrl: string,
|
|
231
|
+
* bridgeToken: string,
|
|
232
|
+
* log: { info, warn, error },
|
|
233
|
+
* producerFactory?: (opts: { relayUrl, bridgeToken, threadId }) => { postEvents },
|
|
234
|
+
* openclawInvoke?: (opts: { input, onChunk }) => Promise<void>,
|
|
235
|
+
* flushPolicy?: { size?: number, timeMs?: number },
|
|
236
|
+
* gatewayId?: string, // 提供后启用正文 agentlink-cos:// 图片引用解析(sign-get)
|
|
237
|
+
* _signGetImpl?: Function, // 测试注入:(cosObjectKey) => Promise<{getUrl}>
|
|
238
|
+
* }} opts
|
|
239
|
+
* @returns {(request: any, ctx: any) => Promise<boolean>}
|
|
240
|
+
*/
|
|
241
|
+
export function createUnifiedDispatchHandler({
|
|
242
|
+
relayUrl,
|
|
243
|
+
bridgeToken,
|
|
244
|
+
log,
|
|
245
|
+
producerFactory,
|
|
246
|
+
openclawInvoke,
|
|
247
|
+
flushPolicy,
|
|
248
|
+
gatewayId,
|
|
249
|
+
_signGetImpl,
|
|
250
|
+
}) {
|
|
251
|
+
// Default producer factory uses createHookedUnifiedProducer so outgoing hooks
|
|
252
|
+
// (e.g. bridge-auto-tunnel URL rewriting) apply to every postEvents call.
|
|
253
|
+
const mkProducer = producerFactory ?? (({ relayUrl: ru, bridgeToken: bt, threadId }) =>
|
|
254
|
+
createHookedUnifiedProducer({ relayUrl: ru, bridgeToken: bt, threadId, logger: log })
|
|
255
|
+
);
|
|
256
|
+
|
|
257
|
+
// In-memory dedup set — process-scoped, cleared on restart.
|
|
258
|
+
// audit 修复:长驻 bridge 上无界 Set 必膨胀,改 LRU+TTL(幂等窗口 24h 足够:
|
|
259
|
+
// relay 重投递只发生在分钟级,超窗重放按新 run 处理也无害——envelope 落库幂等)。
|
|
260
|
+
const dispatchedRunIds = new BoundedSet({ maxSize: 50_000, ttlMs: 24 * 60 * 60 * 1000 });
|
|
261
|
+
|
|
262
|
+
return async function handleUnifiedDispatch(request, _ctx) {
|
|
263
|
+
const kind = request?.request?.__kind;
|
|
264
|
+
if (kind !== "agent.run.dispatch") {
|
|
265
|
+
return false; // not a dispatch request — let caller handle it
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
const { thread_id, run_id, input, blocks = [], workspace_path } = request.request ?? {};
|
|
269
|
+
|
|
270
|
+
// Idempotency check
|
|
271
|
+
if (dispatchedRunIds.has(run_id)) {
|
|
272
|
+
log.warn("dispatch.duplicate", "run already dispatched, skipping", { run_id });
|
|
273
|
+
return true;
|
|
274
|
+
}
|
|
275
|
+
dispatchedRunIds.add(run_id);
|
|
276
|
+
|
|
277
|
+
const producer = mkProducer({ relayUrl, bridgeToken, threadId: thread_id });
|
|
278
|
+
const flusher = createEnvelopeFlusher({
|
|
279
|
+
postEvents: (envs) => producer.postEvents(envs),
|
|
280
|
+
flushPolicy,
|
|
281
|
+
log,
|
|
282
|
+
});
|
|
283
|
+
|
|
284
|
+
// Incoming hook: intercept chat-side commands (e.g. /tunnel ls) before runtime dispatch.
|
|
285
|
+
// 失败 / 抛错由 applyIncomingHooks 自己吞 + 兜底走 pass-through,这里只看返回值。
|
|
286
|
+
let hookResult;
|
|
287
|
+
try {
|
|
288
|
+
hookResult = await applyIncomingHooks({ thread_id, run_id, input }, { logger: log });
|
|
289
|
+
} catch (err) {
|
|
290
|
+
// 防御:applyIncomingHooks 本身 throw(理论上不会)→ 兜底 pass-through
|
|
291
|
+
log.warn("dispatch.incoming_hook_err", "incoming hooks threw, falling back to runtime", {
|
|
292
|
+
run_id, err: String(err?.message || err),
|
|
293
|
+
});
|
|
294
|
+
hookResult = { intercept: false };
|
|
295
|
+
}
|
|
296
|
+
if (hookResult && hookResult.intercept === true) {
|
|
297
|
+
try {
|
|
298
|
+
if (Array.isArray(hookResult.replies) && hookResult.replies.length > 0) {
|
|
299
|
+
// Intercept replies typically a small fixed batch — flush via flusher
|
|
300
|
+
// for consistent semantics + retry / waitAll guarantees.
|
|
301
|
+
flusher.enqueue(hookResult.replies);
|
|
302
|
+
await flusher.waitAll();
|
|
303
|
+
}
|
|
304
|
+
} catch (err) {
|
|
305
|
+
log.error("dispatch.intercept_post_failed", "failed to post intercept replies", {
|
|
306
|
+
run_id, err: String(err?.message || err),
|
|
307
|
+
});
|
|
308
|
+
}
|
|
309
|
+
return true; // 请求已消费,跳过 runtime 派发
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
try {
|
|
313
|
+
// Task 2.5: 预处理 text-like mime attachment,替换为 text block;
|
|
314
|
+
// PDF / image / 其他 block 原样保留,由各 runtime 自行处理。
|
|
315
|
+
let processedBlocks = await preprocessAttachmentsToText(blocks, { log });
|
|
316
|
+
|
|
317
|
+
// P0「读取笔记内图片失败」:正文中的 agentlink-cos:// 图片引用
|
|
318
|
+
// → sign-get 换临时 URL → 合成 image block,正文替换为占位文案。
|
|
319
|
+
let effectiveInput = input;
|
|
320
|
+
const signGet = _signGetImpl ?? (gatewayId
|
|
321
|
+
? (key) => signGetCosObject({ baseUrl: relayUrl, gatewayId, token: bridgeToken }, key)
|
|
322
|
+
: null);
|
|
323
|
+
if (signGet) {
|
|
324
|
+
const resolvedRefs = await resolveCosImageRefs({
|
|
325
|
+
input: effectiveInput, blocks: processedBlocks, signGet, log,
|
|
326
|
+
});
|
|
327
|
+
if (resolvedRefs.resolved > 0) {
|
|
328
|
+
log.info("dispatch.cos_refs", "resolved inline cos image refs", {
|
|
329
|
+
run_id, resolved: resolvedRefs.resolved,
|
|
330
|
+
});
|
|
331
|
+
}
|
|
332
|
+
effectiveInput = resolvedRefs.input;
|
|
333
|
+
processedBlocks = resolvedRefs.blocks;
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
let blockState = newBlockState();
|
|
337
|
+
let runEndedFromRuntime = false;
|
|
338
|
+
// run.paused(HITL interrupt,如 /resume picker)意味着 run 的生命周期
|
|
339
|
+
// 移交给了异步 resume listener(收到 run.resumed 后才补 block.completed
|
|
340
|
+
// + run.completed)。此时绝不能合成收尾——paused 后 21ms 跟一条
|
|
341
|
+
// run.completed 会让 App 端「run 终态移除 interrupt 卡」的兜底立即删掉
|
|
342
|
+
// 刚渲染的选择卡(P0 /resume 无反应)。
|
|
343
|
+
let runPausedFromRuntime = false;
|
|
344
|
+
|
|
345
|
+
if (typeof openclawInvoke === "function") {
|
|
346
|
+
// Injected (test mode or picoclaw ws integration)
|
|
347
|
+
// thread_id 透传给 runtime adapter,让需要"按 thread 维护原生 session
|
|
348
|
+
// 续接"的 runtime(如 claude --resume <sid>)可以做 mapping。
|
|
349
|
+
await openclawInvoke({
|
|
350
|
+
thread_id,
|
|
351
|
+
run_id,
|
|
352
|
+
input: effectiveInput,
|
|
353
|
+
blocks: processedBlocks,
|
|
354
|
+
workspace_path: workspace_path || null,
|
|
355
|
+
// relay 信封顶层 sessionKey(App 侧 abort RPC 用它定位要杀的 run)
|
|
356
|
+
session_key: request.sessionKey || null,
|
|
357
|
+
onChunk: async (chunk) => {
|
|
358
|
+
const { envelopes, blockState: next } = translateOpenClawChunkToEnvelope(
|
|
359
|
+
chunk,
|
|
360
|
+
run_id,
|
|
361
|
+
blockState,
|
|
362
|
+
);
|
|
363
|
+
blockState = next;
|
|
364
|
+
for (const env of envelopes) {
|
|
365
|
+
if (env.type === "run.completed" || env.type === "run.failed") {
|
|
366
|
+
runEndedFromRuntime = true;
|
|
367
|
+
}
|
|
368
|
+
// 只认本 run 的 paused/resumed(hermes __envelopes 透传的
|
|
369
|
+
// child-run 信封带不同 run_id,不影响父 run 的收尾判定)。
|
|
370
|
+
if (env.run_id === run_id) {
|
|
371
|
+
if (env.type === "run.paused") runPausedFromRuntime = true;
|
|
372
|
+
if (env.type === "run.resumed") runPausedFromRuntime = false;
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
flusher.enqueue(envelopes);
|
|
376
|
+
},
|
|
377
|
+
});
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
// If runtime didn't emit response.completed itself, feed a synthetic
|
|
381
|
+
// one through translate. This both (a) emits content_block.completed
|
|
382
|
+
// for any open text/thinking blocks and (b) emits run.completed —
|
|
383
|
+
// matching the protocol contract that all blocks must close before
|
|
384
|
+
// run.completed.
|
|
385
|
+
//
|
|
386
|
+
// Exception: run paused awaiting an interrupt → the async resume
|
|
387
|
+
// listener owns the run's terminal envelopes; skip synthesis entirely.
|
|
388
|
+
if (runPausedFromRuntime && !runEndedFromRuntime) {
|
|
389
|
+
log.info?.("dispatch.run.paused", "run paused awaiting interrupt; skipping synthetic completion", {
|
|
390
|
+
run_id,
|
|
391
|
+
});
|
|
392
|
+
} else if (!runEndedFromRuntime) {
|
|
393
|
+
const { envelopes } = translateOpenClawChunkToEnvelope(
|
|
394
|
+
{ event: "response.completed" },
|
|
395
|
+
run_id,
|
|
396
|
+
blockState,
|
|
397
|
+
);
|
|
398
|
+
// If no blocks were ever opened (e.g. zero chunks), translate returns
|
|
399
|
+
// only run.completed; if blocks were open, it returns [block_completed*, run.completed].
|
|
400
|
+
if (envelopes.length === 0) {
|
|
401
|
+
// Safety net: no blocks, no synthetic event produced (shouldn't
|
|
402
|
+
// happen with the current translate impl, but guard anyway).
|
|
403
|
+
flusher.enqueue([buildRunCompleted(run_id, "end_turn")]);
|
|
404
|
+
} else {
|
|
405
|
+
flusher.enqueue(envelopes);
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
// Drain queue + await all in-flight POSTs before returning.
|
|
410
|
+
await flusher.waitAll();
|
|
411
|
+
} catch (err) {
|
|
412
|
+
log.error("dispatch.invoke.error", "openclaw invocation failed", {
|
|
413
|
+
run_id,
|
|
414
|
+
err: String(err?.message || err),
|
|
415
|
+
});
|
|
416
|
+
// CRITICAL: await all in-flight envelopes BEFORE posting run.failed to
|
|
417
|
+
// avoid out-of-order arrival (envelope after run.failed).
|
|
418
|
+
try {
|
|
419
|
+
await flusher.waitAll();
|
|
420
|
+
} catch (waitErr) {
|
|
421
|
+
log.warn("dispatch.flush.wait_err", "waitAll before run.failed errored (continuing)", {
|
|
422
|
+
run_id, err: String(waitErr?.message || waitErr),
|
|
423
|
+
});
|
|
424
|
+
}
|
|
425
|
+
try {
|
|
426
|
+
await producer.postEvents([
|
|
427
|
+
buildRunFailed(run_id, "runtime.crash", String(err?.message || err)),
|
|
428
|
+
]);
|
|
429
|
+
} catch (postErr) {
|
|
430
|
+
log.error("dispatch.post_failed.error", "failed to post run.failed envelope", {
|
|
431
|
+
run_id,
|
|
432
|
+
err: String(postErr?.message || postErr),
|
|
433
|
+
});
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
return true;
|
|
438
|
+
};
|
|
439
|
+
}
|
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
// usageReporter — 三个 runtime 共用的"用量快照累加器 + 节流发布器"。
|
|
2
|
+
//
|
|
3
|
+
// 设计原则(第一性原理):
|
|
4
|
+
// - 所有字段都是真数据 — runtime 能拿到什么就填什么,拿不到就 null
|
|
5
|
+
// - 节流:同一会话内 usage_report 连续触发时只发一次(默认 800ms debounce),
|
|
6
|
+
// 带 force 标志的(rate_limit / TUI probe)立即发
|
|
7
|
+
// - 调用方只管 update/patch,节流/序列化/发送都在本模块
|
|
8
|
+
//
|
|
9
|
+
// 与旧版本差异:
|
|
10
|
+
// - 不再持有 sessionId / appendEvent,改持有 publishEvent(event) 单一回调
|
|
11
|
+
// - publishEvent 由调用方注入,通常是 relayWorker 的 publishEvent;但
|
|
12
|
+
// usage 是"被动累积 + 周期 flush",没有 requestId 上下文,所以调用方需要
|
|
13
|
+
// 在每次 flush 前给一个 fallback requestId(例如 latest in-flight request)
|
|
14
|
+
// - 发出去的 native event:
|
|
15
|
+
// { event: "usage_report", payload: <UsageSnapshot 同 schema> }
|
|
16
|
+
// 服务端 adapter 看到 event="usage_report" 时透传到 unified usage.report。
|
|
17
|
+
//
|
|
18
|
+
// 字段:
|
|
19
|
+
// tokens.{input,cached_input,output,reasoning_output,total}
|
|
20
|
+
// context.window
|
|
21
|
+
// rate_limits.{primary,secondary,plan_type,reached_type}
|
|
22
|
+
// cost.{total_usd, turns, duration_ms}
|
|
23
|
+
// rate_limit_alert.{retry_after_ms, message, triggered_at}
|
|
24
|
+
|
|
25
|
+
const SCHEMA_VERSION = 1;
|
|
26
|
+
const DEFAULT_DEBOUNCE_MS = 800;
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* @param {{
|
|
30
|
+
* publishEvent: (event:object)=>Promise<any>,
|
|
31
|
+
* runtime: "claude" | "codex" | "hermes",
|
|
32
|
+
* log: any,
|
|
33
|
+
* debounceMs?: number,
|
|
34
|
+
* }} opts
|
|
35
|
+
*/
|
|
36
|
+
export function createUsageReporter({ publishEvent, runtime, log, debounceMs = DEFAULT_DEBOUNCE_MS }) {
|
|
37
|
+
if (typeof publishEvent !== "function") {
|
|
38
|
+
throw new Error("createUsageReporter: publishEvent function required");
|
|
39
|
+
}
|
|
40
|
+
if (
|
|
41
|
+
runtime !== "claude" &&
|
|
42
|
+
runtime !== "codex" &&
|
|
43
|
+
runtime !== "hermes" &&
|
|
44
|
+
runtime !== "picoclaw"
|
|
45
|
+
) {
|
|
46
|
+
throw new Error(`createUsageReporter: bad runtime ${runtime}`);
|
|
47
|
+
}
|
|
48
|
+
const rLog = log ? log.child({ comp: "usage" }) : null;
|
|
49
|
+
|
|
50
|
+
const state = {
|
|
51
|
+
tokens: { input: 0, cached_input: 0, output: 0, reasoning_output: 0, total: 0 },
|
|
52
|
+
context: { window: null },
|
|
53
|
+
rate_limits: null,
|
|
54
|
+
cost: { total_usd: 0, turns: 0, duration_ms: 0 },
|
|
55
|
+
rate_limit_alert: null,
|
|
56
|
+
account: null,
|
|
57
|
+
hasTokens: false,
|
|
58
|
+
hasContext: false,
|
|
59
|
+
hasCost: false,
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
let debounceTimer = null;
|
|
63
|
+
let pendingFlush = false;
|
|
64
|
+
let disposed = false;
|
|
65
|
+
|
|
66
|
+
function addTokensIncrement(delta) {
|
|
67
|
+
if (!delta || typeof delta !== "object") return;
|
|
68
|
+
const add = (k) => { if (Number.isFinite(delta[k])) state.tokens[k] += delta[k]; };
|
|
69
|
+
add("input"); add("cached_input"); add("output"); add("reasoning_output");
|
|
70
|
+
if (Number.isFinite(delta.total)) {
|
|
71
|
+
state.tokens.total += delta.total;
|
|
72
|
+
} else {
|
|
73
|
+
state.tokens.total += (delta.input || 0) + (delta.output || 0);
|
|
74
|
+
}
|
|
75
|
+
state.hasTokens = true;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
function setTokensAbsolute(snap) {
|
|
79
|
+
if (!snap || typeof snap !== "object") return;
|
|
80
|
+
for (const k of ["input", "cached_input", "output", "reasoning_output", "total"]) {
|
|
81
|
+
if (Number.isFinite(snap[k])) state.tokens[k] = snap[k];
|
|
82
|
+
}
|
|
83
|
+
state.hasTokens = true;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
function setContextWindow(n) {
|
|
87
|
+
if (Number.isFinite(n) && n > 0) {
|
|
88
|
+
state.context.window = n;
|
|
89
|
+
state.hasContext = true;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
function setRateLimits(rl) {
|
|
94
|
+
if (rl === null) { state.rate_limits = null; return; }
|
|
95
|
+
if (!rl || typeof rl !== "object") return;
|
|
96
|
+
state.rate_limits = {
|
|
97
|
+
primary: normalizeWindow(rl.primary),
|
|
98
|
+
secondary: normalizeWindow(rl.secondary),
|
|
99
|
+
plan_type: typeof rl.plan_type === "string" ? rl.plan_type : null,
|
|
100
|
+
reached_type: typeof rl.reached_type === "string" ? rl.reached_type : null,
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
function normalizeWindow(w) {
|
|
105
|
+
if (!w || typeof w !== "object") return null;
|
|
106
|
+
const out = {};
|
|
107
|
+
if (Number.isFinite(w.used_percent)) out.used_percent = w.used_percent;
|
|
108
|
+
else return null;
|
|
109
|
+
if (Number.isFinite(w.window_minutes)) out.window_minutes = Math.trunc(w.window_minutes);
|
|
110
|
+
if (Number.isFinite(w.resets_at)) out.resets_at = Math.trunc(w.resets_at);
|
|
111
|
+
return out;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
function addCost({ total_usd, turns, duration_ms }) {
|
|
115
|
+
if (Number.isFinite(total_usd)) state.cost.total_usd += total_usd;
|
|
116
|
+
if (Number.isFinite(turns)) state.cost.turns += turns;
|
|
117
|
+
if (Number.isFinite(duration_ms)) state.cost.duration_ms += duration_ms;
|
|
118
|
+
state.hasCost = true;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
function setRateLimitAlert({ retry_after_ms, message }) {
|
|
122
|
+
state.rate_limit_alert = {
|
|
123
|
+
retry_after_ms: Number.isFinite(retry_after_ms) ? Math.trunc(retry_after_ms) : 0,
|
|
124
|
+
message: typeof message === "string" ? message.slice(0, 400) : "",
|
|
125
|
+
triggered_at: Math.floor(Date.now() / 1000),
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
function clearRateLimitAlert() { state.rate_limit_alert = null; }
|
|
130
|
+
|
|
131
|
+
function setAccount(email) {
|
|
132
|
+
if (typeof email === "string" && email.trim()) {
|
|
133
|
+
state.account = email.trim().slice(0, 200);
|
|
134
|
+
} else if (email === null) {
|
|
135
|
+
state.account = null;
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
function buildPayload() {
|
|
140
|
+
return {
|
|
141
|
+
schema_version: SCHEMA_VERSION,
|
|
142
|
+
runtime,
|
|
143
|
+
collected_at: Math.floor(Date.now() / 1000),
|
|
144
|
+
tokens: state.hasTokens ? { ...state.tokens } : null,
|
|
145
|
+
context: state.hasContext ? { window: state.context.window } : null,
|
|
146
|
+
rate_limits: state.rate_limits ? { ...state.rate_limits } : null,
|
|
147
|
+
cost: state.hasCost ? { ...state.cost } : null,
|
|
148
|
+
rate_limit_alert: state.rate_limit_alert,
|
|
149
|
+
account: state.account,
|
|
150
|
+
};
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
async function publishNow(reason) {
|
|
154
|
+
if (disposed) return;
|
|
155
|
+
if (debounceTimer) { clearTimeout(debounceTimer); debounceTimer = null; }
|
|
156
|
+
pendingFlush = false;
|
|
157
|
+
const payload = buildPayload();
|
|
158
|
+
try {
|
|
159
|
+
await publishEvent({ event: "usage_report", payload });
|
|
160
|
+
rLog?.info("usage.publish", "usage report published", {
|
|
161
|
+
reason,
|
|
162
|
+
has_tokens: !!payload.tokens,
|
|
163
|
+
has_context: !!payload.context,
|
|
164
|
+
context_window: payload.context?.window ?? 0,
|
|
165
|
+
has_rate_limits: !!payload.rate_limits,
|
|
166
|
+
has_cost: !!payload.cost,
|
|
167
|
+
account: payload.account || null,
|
|
168
|
+
alert: !!payload.rate_limit_alert,
|
|
169
|
+
});
|
|
170
|
+
} catch (err) {
|
|
171
|
+
rLog?.warn("usage.publish.failed", "publish failed", {
|
|
172
|
+
err: err?.message || String(err),
|
|
173
|
+
});
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
function schedulePublish(reason) {
|
|
178
|
+
if (disposed) return;
|
|
179
|
+
pendingFlush = true;
|
|
180
|
+
if (debounceTimer) return;
|
|
181
|
+
debounceTimer = setTimeout(() => {
|
|
182
|
+
debounceTimer = null;
|
|
183
|
+
if (pendingFlush) void publishNow(reason || "debounce");
|
|
184
|
+
}, debounceMs);
|
|
185
|
+
debounceTimer.unref?.();
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
function dispose() {
|
|
189
|
+
disposed = true;
|
|
190
|
+
if (debounceTimer) { clearTimeout(debounceTimer); debounceTimer = null; }
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
return {
|
|
194
|
+
addTokensIncrement,
|
|
195
|
+
setTokensAbsolute,
|
|
196
|
+
setContextWindow,
|
|
197
|
+
setRateLimits,
|
|
198
|
+
addCost,
|
|
199
|
+
setRateLimitAlert,
|
|
200
|
+
clearRateLimitAlert,
|
|
201
|
+
setAccount,
|
|
202
|
+
publishNow,
|
|
203
|
+
schedulePublish,
|
|
204
|
+
dispose,
|
|
205
|
+
_snapshot: () => buildPayload(),
|
|
206
|
+
};
|
|
207
|
+
}
|