@sema-agent/client-core 0.77.1 → 0.78.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +42 -1
- package/README.md +6 -5
- package/dist/abortableSleep.d.ts +0 -28
- package/dist/abortableSleep.js +0 -28
- package/dist/adapt/arms.d.ts +0 -46
- package/dist/adapt/arms.js +9 -544
- package/dist/adapt/ids.d.ts +0 -58
- package/dist/adapt/ids.js +0 -63
- package/dist/adapt/instanceLedger.d.ts +0 -25
- package/dist/adapt/instanceLedger.js +0 -20
- package/dist/adapt/panelTasks.d.ts +0 -69
- package/dist/adapt/panelTasks.js +0 -95
- package/dist/adapt/textSegmentAuthority.d.ts +1 -137
- package/dist/adapt/textSegmentAuthority.js +0 -54
- package/dist/adapt/textStream.d.ts +0 -252
- package/dist/adapt/textStream.js +2 -281
- package/dist/adapt/toolCards.d.ts +0 -46
- package/dist/adapt/toolCards.js +0 -23
- package/dist/adapt/turnFlags.d.ts +0 -54
- package/dist/adapt/turnFlags.js +1 -17
- package/dist/adapt/wireShapes.d.ts +0 -92
- package/dist/adapt/wireShapes.js +0 -83
- package/dist/adapt.d.ts +0 -55
- package/dist/adapt.js +1 -120
- package/dist/adapter/activeRunSelfHeal.d.ts +22 -515
- package/dist/adapter/activeRunSelfHeal.js +10 -625
- package/dist/adapter/downstream/eventToSdkMessage.d.ts +2 -305
- package/dist/adapter/downstream/eventToSdkMessage.js +2 -867
- package/dist/adapter/downstream/terminalToSdkResult.d.ts +2 -314
- package/dist/adapter/downstream/terminalToSdkResult.js +13 -560
- package/dist/adapter/downstream/turnUsageToModelUsage.d.ts +0 -95
- package/dist/adapter/downstream/turnUsageToModelUsage.js +2 -31
- package/dist/adapter/runStream.d.ts +0 -206
- package/dist/adapter/runStream.js +6 -596
- package/dist/adapter/types.d.ts +0 -111
- package/dist/adapter/types.js +0 -29
- package/dist/agentSession/backgroundView.d.ts +0 -106
- package/dist/agentSession/backgroundView.js +3 -49
- package/dist/agentSession/contract.d.ts +0 -98
- package/dist/agentSession/contract.js +0 -12
- package/dist/agentsWireCaps.d.ts +0 -72
- package/dist/agentsWireCaps.js +4 -72
- package/dist/approvalsStreamLiveCapability.d.ts +0 -25
- package/dist/approvalsStreamLiveCapability.js +0 -31
- package/dist/argvFlagValue.d.ts +0 -33
- package/dist/argvFlagValue.js +3 -35
- package/dist/attachmentsWireCaps.d.ts +0 -127
- package/dist/attachmentsWireCaps.js +0 -144
- package/dist/autoModeUnavailable.d.ts +0 -120
- package/dist/autoModeUnavailable.js +0 -144
- package/dist/classifierStatus.d.ts +0 -72
- package/dist/classifierStatus.js +0 -167
- package/dist/classifierVerdictWire.d.ts +0 -54
- package/dist/classifierVerdictWire.js +0 -133
- package/dist/clientContextWireCaps.d.ts +0 -37
- package/dist/clientContextWireCaps.js +0 -36
- package/dist/clientSlice.d.ts +0 -63
- package/dist/clientSlice.js +0 -45
- package/dist/cloudConfigWireCaps.d.ts +0 -86
- package/dist/cloudConfigWireCaps.js +2 -65
- package/dist/compensations.d.ts +0 -52
- package/dist/compensations.js +2 -63
- package/dist/controlRouter.d.ts +2 -208
- package/dist/controlRouter.js +1 -129
- package/dist/coreValuePorts.d.ts +0 -84
- package/dist/coreValuePorts.js +0 -29
- package/dist/decideReceipt.d.ts +0 -110
- package/dist/decideReceipt.js +0 -84
- package/dist/detachWire.d.ts +0 -130
- package/dist/detachWire.js +1 -130
- package/dist/deviceExecutorManagementCapability.d.ts +2 -35
- package/dist/deviceExecutorManagementCapability.js +1 -37
- package/dist/diagnostics.d.ts +0 -8
- package/dist/diagnostics.js +0 -8
- package/dist/diff/patch.d.ts +0 -17
- package/dist/diff/patch.js +0 -19
- package/dist/effectiveFacts.d.ts +0 -40
- package/dist/effectiveFacts.js +0 -22
- package/dist/effortWire.d.ts +0 -11
- package/dist/effortWire.js +0 -12
- package/dist/engineAgentPanelStore.d.ts +0 -169
- package/dist/engineAgentPanelStore.js +13 -293
- package/dist/engineCapReader.d.ts +0 -62
- package/dist/engineCapReader.js +1 -43
- package/dist/engineCapsCache.d.ts +0 -177
- package/dist/engineCapsCache.js +0 -199
- package/dist/engineCapsGenerationGuard.d.ts +0 -2
- package/dist/engineCapsGenerationGuard.js +0 -11
- package/dist/engineErrorCodes.d.ts +1 -316
- package/dist/engineErrorCodes.js +0 -427
- package/dist/engineHttpTools.d.ts +0 -29
- package/dist/engineHttpTools.js +0 -29
- package/dist/engineIdentity.d.ts +0 -83
- package/dist/engineIdentity.js +0 -88
- package/dist/engineInlineTaskStats.d.ts +0 -57
- package/dist/engineInlineTaskStats.js +1 -42
- package/dist/engineNoticeCodes.d.ts +0 -190
- package/dist/engineNoticeCodes.js +0 -182
- package/dist/engineSessionParam.d.ts +0 -30
- package/dist/engineSessionParam.js +0 -52
- package/dist/engineToolLabelStore.d.ts +0 -29
- package/dist/engineToolLabelStore.js +0 -31
- package/dist/engineWireSdk.d.ts +0 -90
- package/dist/engineWireSdk.js +0 -74
- package/dist/engineWireTarget.d.ts +0 -14
- package/dist/engineWireTarget.js +0 -39
- package/dist/env/localeGeo.d.ts +0 -12
- package/dist/env/localeGeo.js +2 -77
- package/dist/env/localeTag.d.ts +0 -34
- package/dist/env/localeTag.js +0 -32
- package/dist/env/uiLanguage.d.ts +0 -13
- package/dist/env/uiLanguage.js +0 -25
- package/dist/envFlag.d.ts +0 -37
- package/dist/envFlag.js +0 -40
- package/dist/executionLaneCapability.d.ts +0 -48
- package/dist/executionLaneCapability.js +0 -54
- package/dist/finalVerifyWire.d.ts +0 -67
- package/dist/finalVerifyWire.js +3 -43
- package/dist/fleet/fleetLedger.d.ts +0 -308
- package/dist/fleet/fleetLedger.js +10 -429
- package/dist/fleet/fleetProjection.d.ts +0 -240
- package/dist/fleet/fleetProjection.js +0 -189
- package/dist/fleet/fleetRowAgentType.d.ts +0 -6
- package/dist/fleet/fleetRowAgentType.js +1 -32
- package/dist/fleet/workflowSizeWarning.d.ts +0 -47
- package/dist/fleet/workflowSizeWarning.js +1 -46
- package/dist/fleetAgentPanelProjection.d.ts +0 -48
- package/dist/fleetAgentPanelProjection.js +8 -173
- package/dist/fleetTaskDesc.d.ts +0 -39
- package/dist/fleetTaskDesc.js +0 -69
- package/dist/forkWireCaps.d.ts +0 -23
- package/dist/forkWireCaps.js +1 -24
- package/dist/gateOutcome.d.ts +0 -140
- package/dist/gateOutcome.js +0 -85
- package/dist/gateVocabulary.d.ts +0 -114
- package/dist/gateVocabulary.js +3 -161
- package/dist/goalStopHook.d.ts +0 -127
- package/dist/goalStopHook.js +0 -178
- package/dist/headlessPermissionModeWire.d.ts +0 -76
- package/dist/headlessPermissionModeWire.js +1 -155
- package/dist/headlessReconnectWire.d.ts +0 -84
- package/dist/headlessReconnectWire.js +13 -67
- package/dist/hitl/approvalDecisionNoteAudit.d.ts +0 -49
- package/dist/hitl/approvalDecisionNoteAudit.js +0 -56
- package/dist/hitl/approvalOutcomeNote.d.ts +0 -2
- package/dist/hitl/approvalOutcomeNote.js +0 -20
- package/dist/hitl/approvalResolution.d.ts +0 -113
- package/dist/hitl/approvalResolution.js +0 -66
- package/dist/hitl/approvalsFeed.d.ts +0 -183
- package/dist/hitl/approvalsFeed.js +12 -243
- package/dist/hitl/armedGateRegistry.d.ts +0 -55
- package/dist/hitl/armedGateRegistry.js +0 -146
- package/dist/hitl/askGateWire.d.ts +0 -86
- package/dist/hitl/askGateWire.js +1 -95
- package/dist/hitl/askParkRowRouting.d.ts +0 -123
- package/dist/hitl/askParkRowRouting.js +1 -89
- package/dist/hitl/crashConverged.d.ts +0 -148
- package/dist/hitl/crashConverged.js +0 -214
- package/dist/hitl/editedRuleTextPrecheck.d.ts +0 -109
- package/dist/hitl/editedRuleTextPrecheck.js +0 -71
- package/dist/hitl/frameRouter.d.ts +0 -134
- package/dist/hitl/frameRouter.js +6 -378
- package/dist/hitl/gateIdentity.d.ts +0 -39
- package/dist/hitl/gateIdentity.js +0 -41
- package/dist/hitl/gateLedger.d.ts +0 -267
- package/dist/hitl/gateLedger.js +0 -121
- package/dist/hitl/hitlBridge.d.ts +7 -431
- package/dist/hitl/hitlBridge.js +7 -476
- package/dist/hitl/hitlHostSurface.d.ts +0 -150
- package/dist/hitl/hitlHostSurface.js +0 -169
- package/dist/hitl/livePendingAsk.d.ts +0 -91
- package/dist/hitl/livePendingAsk.js +0 -74
- package/dist/hitl/localAllowRule.d.ts +0 -62
- package/dist/hitl/localAllowRule.js +1 -33
- package/dist/hitl/parkOwnership.d.ts +0 -56
- package/dist/hitl/parkOwnership.js +0 -22
- package/dist/hitl/parkResolver.d.ts +3 -88
- package/dist/hitl/parkResolver.js +8 -349
- package/dist/hitl/parkRowBirthWait.d.ts +2 -26
- package/dist/hitl/parkRowBirthWait.js +3 -93
- package/dist/hitl/persistedRulesWire.d.ts +23 -371
- package/dist/hitl/persistedRulesWire.js +62 -329
- package/dist/hitl/planReviewWire.d.ts +4 -176
- package/dist/hitl/planReviewWire.js +9 -312
- package/dist/hitl/resumeRunningCard.d.ts +0 -105
- package/dist/hitl/resumeRunningCard.js +0 -105
- package/dist/hitl/sessionPolicyWire.d.ts +11 -191
- package/dist/hitl/sessionPolicyWire.js +0 -149
- package/dist/hitl/suspendedReopen.d.ts +3 -24
- package/dist/hitl/suspendedReopen.js +0 -14
- package/dist/hitl/toolApprovalWire.d.ts +5 -1308
- package/dist/hitl/toolApprovalWire.js +5 -945
- package/dist/hooksWireCaps.d.ts +0 -38
- package/dist/hooksWireCaps.js +0 -190
- package/dist/host.d.ts +0 -105
- package/dist/host.js +0 -33
- package/dist/hostEnv.d.ts +0 -14
- package/dist/hostEnv.js +0 -13
- package/dist/imagesWireCaps.d.ts +0 -21
- package/dist/imagesWireCaps.js +0 -22
- package/dist/index.d.ts +0 -134
- package/dist/index.js +0 -476
- package/dist/interactiveHalt.d.ts +5 -153
- package/dist/interactiveHalt.js +0 -111
- package/dist/interactiveToolsWire.d.ts +0 -62
- package/dist/interactiveToolsWire.js +1 -67
- package/dist/leaderConflict.d.ts +0 -59
- package/dist/leaderConflict.js +0 -48
- package/dist/limitsWire.d.ts +0 -125
- package/dist/limitsWire.js +2 -120
- package/dist/liveInitToolFace.d.ts +0 -77
- package/dist/liveInitToolFace.js +1 -109
- package/dist/liveModelCatalog.d.ts +0 -52
- package/dist/liveModelCatalog.js +0 -48
- package/dist/liveQuestionStore.d.ts +0 -87
- package/dist/liveQuestionStore.js +1 -56
- package/dist/mcpLiveness.d.ts +0 -151
- package/dist/mcpLiveness.js +0 -122
- package/dist/mcpPanel.d.ts +0 -104
- package/dist/mcpPanel.js +0 -49
- package/dist/mcpReconnect.d.ts +0 -58
- package/dist/mcpReconnect.js +0 -69
- package/dist/mcpWireCaps.d.ts +0 -55
- package/dist/mcpWireCaps.js +1 -13
- package/dist/memoryComplianceCapability.d.ts +3 -57
- package/dist/memoryComplianceCapability.js +0 -57
- package/dist/memoryEntriesWire.d.ts +6 -198
- package/dist/memoryEntriesWire.js +0 -144
- package/dist/memoryOriginCapability.d.ts +3 -53
- package/dist/memoryOriginCapability.js +0 -50
- package/dist/memorySpecWire.d.ts +0 -116
- package/dist/memorySpecWire.js +0 -150
- package/dist/model/catalog.d.ts +0 -111
- package/dist/model/catalog.js +0 -87
- package/dist/model/catalogLoader.d.ts +0 -114
- package/dist/model/catalogLoader.js +2 -139
- package/dist/model/modelSupplyRules.d.ts +1 -55
- package/dist/model/modelSupplyRules.js +0 -62
- package/dist/model/providerAuth.d.ts +0 -103
- package/dist/model/providerAuth.js +2 -38
- package/dist/model/providerCatalog.d.ts +0 -45
- package/dist/model/providerCatalog.js +0 -37
- package/dist/model/providerPresets.d.ts +0 -33
- package/dist/model/providerPresets.js +2 -91
- package/dist/model/tierVocabulary.d.ts +0 -31
- package/dist/model/tierVocabulary.js +0 -27
- package/dist/modelBudgetRule.d.ts +0 -39
- package/dist/modelBudgetRule.js +0 -39
- package/dist/modelCapabilityProbe.d.ts +0 -175
- package/dist/modelCapabilityProbe.js +0 -147
- package/dist/modelWireCaps.d.ts +0 -13
- package/dist/modelWireCaps.js +0 -13
- package/dist/notifications.d.ts +0 -233
- package/dist/notifications.js +14 -492
- package/dist/oneShotWireCaps.d.ts +0 -34
- package/dist/oneShotWireCaps.js +0 -35
- package/dist/ownKey.d.ts +0 -33
- package/dist/ownKey.js +0 -33
- package/dist/panelRunningHistory.d.ts +0 -28
- package/dist/panelRunningHistory.js +0 -43
- package/dist/peerFrames.d.ts +0 -71
- package/dist/peerFrames.js +0 -168
- package/dist/peerLaneCapability.d.ts +0 -44
- package/dist/peerLaneCapability.js +0 -52
- package/dist/permissionRuleIssue.d.ts +0 -30
- package/dist/permissionRuleIssue.js +0 -78
- package/dist/permissionRulesWriteCapability.d.ts +0 -46
- package/dist/permissionRulesWriteCapability.js +0 -54
- package/dist/permissionWireCaps.d.ts +0 -37
- package/dist/permissionWireCaps.js +0 -37
- package/dist/postureKnob.d.ts +0 -71
- package/dist/postureKnob.js +0 -86
- package/dist/principalWire.d.ts +0 -17
- package/dist/principalWire.js +0 -17
- package/dist/printToolResultFrame.d.ts +0 -100
- package/dist/printToolResultFrame.js +0 -33
- package/dist/promptProfileWireCaps.d.ts +0 -13
- package/dist/promptProfileWireCaps.js +0 -13
- package/dist/readFacePosture.d.ts +0 -43
- package/dist/readFacePosture.js +0 -46
- package/dist/request/printNotification.d.ts +0 -20
- package/dist/request/printNotification.js +0 -55
- package/dist/request/taskRequest.d.ts +1 -249
- package/dist/request/taskRequest.js +20 -498
- package/dist/resumeRefusalCopy.d.ts +0 -136
- package/dist/resumeRefusalCopy.js +1 -117
- package/dist/retainBackgroundWireCaps.d.ts +0 -48
- package/dist/retainBackgroundWireCaps.js +0 -48
- package/dist/retryStatus.d.ts +2 -221
- package/dist/retryStatus.js +0 -107
- package/dist/rewindWireCaps.d.ts +0 -27
- package/dist/rewindWireCaps.js +0 -24
- package/dist/runCancelContext.d.ts +1 -20
- package/dist/runCancelContext.js +0 -34
- package/dist/runTerminal.d.ts +0 -273
- package/dist/runTerminal.js +0 -177
- package/dist/sandboxWire.d.ts +0 -38
- package/dist/sandboxWire.js +0 -82
- package/dist/scenarioWire.d.ts +0 -60
- package/dist/scenarioWire.js +0 -68
- package/dist/scratchpadWireCaps.d.ts +0 -11
- package/dist/scratchpadWireCaps.js +0 -32
- package/dist/sdkWireTransit.d.ts +0 -38
- package/dist/sdkWireTransit.js +0 -30
- package/dist/seam.d.ts +18 -853
- package/dist/seam.js +0 -52
- package/dist/seatContract.d.ts +0 -555
- package/dist/seatContract.js +5 -304
- package/dist/selfOrchestrationDenial.d.ts +0 -189
- package/dist/selfOrchestrationDenial.js +0 -162
- package/dist/selfOrchestrationWireCaps.d.ts +0 -38
- package/dist/selfOrchestrationWireCaps.js +0 -38
- package/dist/sessionMap.d.ts +0 -60
- package/dist/sessionMap.js +0 -37
- package/dist/sessionMemoryStatus.d.ts +11 -114
- package/dist/sessionMemoryStatus.js +0 -112
- package/dist/sessionModelLatch.d.ts +0 -29
- package/dist/sessionModelLatch.js +0 -40
- package/dist/sessionPolicyCapability.d.ts +3 -53
- package/dist/sessionPolicyCapability.js +0 -52
- package/dist/sessionSlot.d.ts +0 -26
- package/dist/sessionSlot.js +0 -17
- package/dist/skillsWireCaps.d.ts +0 -87
- package/dist/skillsWireCaps.js +0 -38
- package/dist/sqlEngineCapability.d.ts +0 -118
- package/dist/sqlEngineCapability.js +0 -134
- package/dist/sseIdleTriage.d.ts +0 -79
- package/dist/sseIdleTriage.js +1 -80
- package/dist/steering.d.ts +0 -54
- package/dist/steering.js +0 -63
- package/dist/subagent/engineCompactWire.d.ts +0 -34
- package/dist/subagent/engineCompactWire.js +4 -134
- package/dist/subagent/engineDelegatedPrompt.d.ts +0 -31
- package/dist/subagent/engineDelegatedPrompt.js +1 -103
- package/dist/subagent/engineRowStopGate.d.ts +0 -20
- package/dist/subagent/engineRowStopGate.js +0 -49
- package/dist/subagent/engineSubagentOutput.d.ts +0 -7
- package/dist/subagent/engineSubagentOutput.js +1 -51
- package/dist/subagent/engineSubagentResume.d.ts +2 -279
- package/dist/subagent/engineSubagentResume.js +2 -129
- package/dist/subagent/engineSubagentSteer.d.ts +0 -26
- package/dist/subagent/engineSubagentSteer.js +0 -63
- package/dist/subagent/engineSubagentTail.d.ts +0 -38
- package/dist/subagent/engineSubagentTail.js +2 -110
- package/dist/subagent/engineTaskHandleWire.d.ts +6 -67
- package/dist/subagent/engineTaskHandleWire.js +0 -126
- package/dist/subagent/subagentOwnerAbsence.d.ts +0 -2
- package/dist/subagent/subagentOwnerAbsence.js +0 -25
- package/dist/subagentContentStore.d.ts +3 -260
- package/dist/subagentContentStore.js +7 -540
- package/dist/systemReminderTag.d.ts +0 -49
- package/dist/systemReminderTag.js +0 -61
- package/dist/toolResult.d.ts +1 -211
- package/dist/toolResult.js +1 -358
- package/dist/toolRoster.d.ts +0 -140
- package/dist/toolRoster.js +0 -66
- package/dist/typePins.d.ts +0 -46
- package/dist/types/engineState.d.ts +0 -71
- package/dist/types/engineState.js +0 -16
- package/dist/ultracodeWireCaps.d.ts +0 -75
- package/dist/ultracodeWireCaps.js +0 -92
- package/dist/unrefTimer.d.ts +0 -27
- package/dist/webSearchBackendCapability.d.ts +0 -72
- package/dist/webSearchBackendCapability.js +0 -79
- package/dist/webSearchWireCaps.d.ts +0 -37
- package/dist/webSearchWireCaps.js +0 -34
- package/dist/websearch/searchProviderPresets.d.ts +0 -81
- package/dist/websearch/searchProviderPresets.js +0 -27
- package/dist/wireErrorTriage.d.ts +4 -160
- package/dist/wireErrorTriage.js +0 -171
- package/dist/wireRefusalCopy.d.ts +0 -28
- package/dist/wireRefusalCopy.js +0 -28
- package/dist/workflow.d.ts +0 -40
- package/dist/workflow.js +0 -52
- package/dist/workflowClient.d.ts +0 -122
- package/dist/workflowClient.js +8 -360
- package/dist/workflowMonitor.d.ts +2 -72
- package/dist/workflowMonitor.js +0 -26
- package/dist/writeProtectionCapability.d.ts +0 -112
- package/dist/writeProtectionCapability.js +0 -109
- package/docs/INTEGRATION-CLIENTS.md +78 -5
- package/package.json +3 -3
|
@@ -1,83 +1,27 @@
|
|
|
1
1
|
import { eventSeq, } from './types.js';
|
|
2
2
|
import { eventToSdkMessage, turnEndUsage } from './downstream/eventToSdkMessage.js';
|
|
3
3
|
import { turnUsageToModelUsage } from './downstream/turnUsageToModelUsage.js';
|
|
4
|
-
// D-3(0.66.0):终局对账臂与终帧超集键共用**同一个**成本读器(readRunCostFacts)。
|
|
5
4
|
import { readRunCostFacts, terminalToSdkResult, TOOL_INPUT_JOIN_MAX_CALLS } from './downstream/terminalToSdkResult.js';
|
|
6
5
|
import { coerceOutput, publishSubagentContentEvent } from '../subagentContentStore.js';
|
|
7
|
-
/**
|
|
8
|
-
* 409 session-busy 拒绝的 **canonical errorCode**([2377]C-1,server main `049ff2c`,随 5.0.0 发)。
|
|
9
|
-
* 引擎把它 stamp 在 `done{status:'failed'}` / `failed` 终帧上,壳据此**结构判读**,不再读人话。
|
|
10
|
-
* G1 去字面化(2026-08-04):字面收编进 `engineErrorCodes.ts` 单一真源,本文件只 import。
|
|
11
|
-
*/
|
|
12
6
|
import { ACTIVE_RUN_BUSY_ERROR_CODE, OUTPUT_INVALID, isLimitsExceededCode } from '../engineErrorCodes.js';
|
|
13
|
-
// 0.60.0(engine ≥7.64.0 / sdk 8.4.0):终局读数的单一读器(纯叶子:零 Node、零宿主端口)。
|
|
14
7
|
import { isReviewPark, readRunTerminal, runTerminalCode } from '../runTerminal.js';
|
|
15
|
-
/**
|
|
16
|
-
* 本文件发的 chrome 事件全在 leader lane(子代内容在上面就被 divert 走了)。
|
|
17
|
-
*
|
|
18
|
-
* 🔴 **出厂口而不是常量**(0.76.1,与 A 层那半场同一条修法):车道证明在下游会被就地改写,
|
|
19
|
-
* 一只模块级常量对象会让本文件三个发点(用量臂 / 停靠臂 / 对账臂)与**别的流**的同名事件共享
|
|
20
|
-
* 同一只证明,一次改写打穿整个进程。每次新建一只。
|
|
21
|
-
* 🔴 **刻意是本地的一行、不去 import A 层那只同名出厂口**:本文件是适配内核的闭包起点,闭包件数
|
|
22
|
-
* 是只许降的棘轮 —— 为一行 `{lane:'main'}` 把 A 层(或 seam)拖进内核闭包是拿棘轮换重复,
|
|
23
|
-
* 这一行字面量本来就是 `LaneProof` 主臂自己的形,不构成第二份判据。
|
|
24
|
-
*/
|
|
25
8
|
const mainLane = () => ({ lane: 'main' });
|
|
26
|
-
/**
|
|
27
|
-
* **fire-and-forget 的 chrome 发射口**(0.66.0 / 异源对抗复审 [high] 逼出的同形族扫)。
|
|
28
|
-
*
|
|
29
|
-
* 病形:`try { void ctx.emitChrome(...) } catch {}` 只接得住**同步**抛出 —— 而端口的契约是
|
|
30
|
-
* `void | Promise<void>`,一个返回被拒 Promise 的 sink 会在 try/catch **之外**变成
|
|
31
|
-
* `unhandledRejection`;把未处理拒绝当致命错误的宿主(`--unhandled-rejections=strict`、
|
|
32
|
-
* Electron 主进程的全局钩子)会**整只退出**。fail-soft 的承诺在那一刻反而成了最响的失败。
|
|
33
|
-
*
|
|
34
|
-
* ⇒ 两条腿一起接:同步抛在 `try` 里吞,异步拒绝挂 `.catch`。语义与此前逐字一致
|
|
35
|
-
* (不 await、不改时序、sink 失败不影响流);`await` 的那一处(plan_review park)**不走本口**,
|
|
36
|
-
* 它的时序是刻意的(卡片先于完成行上屏)。
|
|
37
|
-
*/
|
|
38
9
|
function emitChromeFireAndForget(ctx, event) {
|
|
39
10
|
try {
|
|
40
11
|
const settled = ctx.emitChrome?.(event);
|
|
41
|
-
// thenable 才挂手柄:`void` 返回的同步 sink 上没有 `.catch` 可挂。
|
|
42
12
|
if (settled !== undefined && settled !== null && typeof settled.catch === 'function') {
|
|
43
13
|
void settled.catch(() => { });
|
|
44
14
|
}
|
|
45
15
|
}
|
|
46
|
-
catch {
|
|
16
|
+
catch { }
|
|
47
17
|
}
|
|
48
|
-
/**
|
|
49
|
-
* `pendingGate` 的**单向**同一性判断(#285 件2,0.36.0):「我现在看到的这道门,能不能**证明**它
|
|
50
|
-
* 不是刚才那一道?」
|
|
51
|
-
*
|
|
52
|
-
* `true` ⇔ 两侧都带 {@link ActiveRunPendingGate.checkpointId} **且**两者不等。其余一律 `false`。
|
|
53
|
-
*
|
|
54
|
-
* 🔴 **`false` 的语义是「证不出」,不是「同一道门」** —— 这不是措辞洁癖,是上游写死的可用性边界:
|
|
55
|
-
* server 选行是无序 `LIMIT 1` 且同 session 可并存多条 pending 行,所以「id 相同」推不出「同一道门」
|
|
56
|
-
* (id 不变而 `activeTaskId` 已换是成文的可能形),「id 缺席」更只是「不知道它叫什么」(三成因合流)。
|
|
57
|
-
* 上游明确只背书**这一个方向**:「id 变了 ⇒ 我看到的确实不是刚才那一行」。
|
|
58
|
-
*
|
|
59
|
-
* 🔴 **为什么归包而不是让三端各写一遍**:这个谓词的价值全在**它拒绝回答的那一半**。三端各自手搓时,
|
|
60
|
-
* 「两个 id 相等就当同一张卡去重」是最自然、也最容易写出来的一行 —— 而它恰恰是上游明说买不到的那个
|
|
61
|
-
* 方向,写出来就是一条按错误前提去重的卡链。把可答的那半做成唯一入口,不可答的那半就没有顺手的写法。
|
|
62
|
-
*
|
|
63
|
-
* ⚠️ 它**不做**任何 kind/decidePath 比较:那两位回答的是「哪种门/去哪决议」,与「是不是同一行」正交
|
|
64
|
-
* (同一行的 kind 不会变,不同行的 kind 完全可能相同)。要判种类变化请直接读 `kind`。
|
|
65
|
-
*/
|
|
66
18
|
export function pendingGateIsProvablyDifferent(prev, next) {
|
|
67
19
|
const a = prev?.checkpointId;
|
|
68
20
|
const b = next?.checkpointId;
|
|
69
|
-
// 任一侧缺席 ⇒ 证不出(缺席 = 不知道这道门叫什么,三成因合流;绝不当成「不同」)。
|
|
70
21
|
if (typeof a !== 'string' || typeof b !== 'string')
|
|
71
22
|
return false;
|
|
72
23
|
return a !== b;
|
|
73
24
|
}
|
|
74
|
-
/**
|
|
75
|
-
* 判别一个**原始 AgentEvent** 是不是 409 active-run 拒收终帧;非 busy ⇒ null。
|
|
76
|
-
* 判据=结构两腿(canonical `errorCode` 优先 → `activeTaskId` 在场);人话文案腿已随 #117 提货
|
|
77
|
-
* 退役(§B8 禁按错误文案判别)。两种 wire 真形:
|
|
78
|
-
* ① `done{result:{status:'failed', errorCode|activeTaskId,…}}`(交互 tasks/stream 腿,409 骑终帧);
|
|
79
|
-
* ② `failed{errorCode|activeTaskId,…}`(任何把它投影成 failed 臂的上游形)。
|
|
80
|
-
*/
|
|
81
25
|
export function activeRunBusySignal(ev) {
|
|
82
26
|
if (typeof ev !== 'object' || ev === null || !('type' in ev))
|
|
83
27
|
return null;
|
|
@@ -109,15 +53,7 @@ export function activeRunBusySignal(ev) {
|
|
|
109
53
|
if (typeof rawGate === 'object' && rawGate !== null) {
|
|
110
54
|
const k = rawGate.kind;
|
|
111
55
|
const d = rawGate.decidePath;
|
|
112
|
-
// 出身位(A-028.1):**只认严格 true**。任何别的形(false / 'true' / 1 / 缺席)一律不置键 ——
|
|
113
|
-
// 键在场即渲徽标类文案,把一个含糊值读成「治理强制」= 对用户下一个证不出的断言。
|
|
114
56
|
const g = rawGate.governanceForced;
|
|
115
|
-
// 身份位(#285 件2):**非空串才置键**,坏形一律降缺席 —— 与 kind/decidePath 同族口径。
|
|
116
|
-
// 🔴 **刻意不做 `cp_`+UUID 格式正则、也不叠长度上限**,与 server 铸点同裁:上游亲跑验过,严格
|
|
117
|
-
// 格式钉**买不到安全性**(32 位 hex token 按 uuid 分组后逐字满足该正则,剥掉装饰就是完整能力键),
|
|
118
|
-
// 只额外买来「core 换形我方静默丢键」的代价;长度域已在 server 投影处夹取,客户端再叠一道
|
|
119
|
-
// **更窄**的域 = 上游放宽当天我方静默丢键,而丢键与 legacy 行在运维面上不可分辨。
|
|
120
|
-
// 窄读域只许等于或宽于铸点域([4050] 随批立的纪律)。
|
|
121
57
|
const cp = rawGate.checkpointId;
|
|
122
58
|
pendingGate = {
|
|
123
59
|
kind: typeof k === 'string' && k.length > 0 ? k : null,
|
|
@@ -126,8 +62,6 @@ export function activeRunBusySignal(ev) {
|
|
|
126
62
|
...(typeof cp === 'string' && cp.length > 0 ? { checkpointId: cp } : {}),
|
|
127
63
|
};
|
|
128
64
|
}
|
|
129
|
-
// [4664] 活性证据位:只认**有限非负数**(NaN/Infinity/负数/串都是坏形 ⇒ 键不 stamp —— 一个
|
|
130
|
-
// 编造的「N 分钟无活动」比没有更坏)。
|
|
131
65
|
const msIdle = carrier.msSinceLastActivity;
|
|
132
66
|
return {
|
|
133
67
|
activeTaskId: cls.handle,
|
|
@@ -136,16 +70,6 @@ export function activeRunBusySignal(ev) {
|
|
|
136
70
|
...(typeof msIdle === 'number' && Number.isFinite(msIdle) && msIdle >= 0 ? { msSinceLastActivity: msIdle } : {}),
|
|
137
71
|
};
|
|
138
72
|
}
|
|
139
|
-
/**
|
|
140
|
-
* 结构位优先的 409 判别(REF-CC-054 / dup-03 + 域词表-02,§B8 禁按错误文案判别)。
|
|
141
|
-
*
|
|
142
|
-
* 顺序即优先级:①引擎 canonical `errorCode`(结构位,机器码);②`activeTaskId` 在场(同样是
|
|
143
|
-
* 结构性判据,[1833] G10 起就在 wire 上)。③人话子串锚已于 2026-08-03 随 #117 提货退役(见上)。
|
|
144
|
-
*
|
|
145
|
-
* 事故出处:此前整条判别是 `errText.includes(锚) || typeof activeTaskId === 'string'`,而 2026-07-31
|
|
146
|
-
* 才发现锚曾是**全等**式、在今天的任何引擎上一次都命中不了 —— 判别一直只靠 activeTaskId 那一半在兜。
|
|
147
|
-
* 上游把人话加长一句判别就死一半,这正是 B8 要禁的那类定时炸弹。
|
|
148
|
-
*/
|
|
149
73
|
function classifyActiveRunBusy(input) {
|
|
150
74
|
const handle = typeof input.activeTaskId === 'string' && input.activeTaskId.length > 0 ? input.activeTaskId : null;
|
|
151
75
|
if (input.errorCode === ACTIVE_RUN_BUSY_ERROR_CODE)
|
|
@@ -154,73 +78,22 @@ function classifyActiveRunBusy(input) {
|
|
|
154
78
|
return { busy: true, handle };
|
|
155
79
|
return { busy: false, handle };
|
|
156
80
|
}
|
|
157
|
-
// ── 治理终态 vs provider/HTTP 真错(#289 批面③ / dogfood api C1+C2+案5)────────────────────────
|
|
158
|
-
/**
|
|
159
|
-
* 中性终态行的前缀。**刻意不是 `API Error`** —— 这一行说的是「这条 run 被本部署的治理旋钮停了」
|
|
160
|
-
* (轮数/预算/墙钟/token 到限、结构化输出重试耗尽),不是 provider 或 HTTP 层出了故障。
|
|
161
|
-
*
|
|
162
|
-
* 事故形(台账 api C1/C2 逐坐标坐实):`sema -p --max-turns N` 的 STDERR 逐字是
|
|
163
|
-
* `API Error: max turns exceeded`,交互面同一构造点也渲成红色「API 错误」行。用户读到 API Error
|
|
164
|
-
* 会去查网络/密钥/额度,而真正的把手是他自己传的 `--max-turns`。CC 对同一件事写的是
|
|
165
|
-
* `Error: Reached max turns (N)` —— 一个字的 `API` 都没有,本前缀与那条身份声明同向。
|
|
166
|
-
*/
|
|
167
81
|
export const RUN_STOPPED_MESSAGE_PREFIX = 'Run stopped';
|
|
168
|
-
/** blocked 终态自带的前缀(`Run blocked: <reason>`,见 errText 构造)。 */
|
|
169
82
|
export const RUN_BLOCKED_MESSAGE_PREFIX = 'Run blocked';
|
|
170
|
-
/**
|
|
171
|
-
* 这条终态行是不是「治理/契约终态」而非 provider 故障 —— 渲染层据此给中性行同样的错误行 chrome
|
|
172
|
-
* (它仍然是一条终态错误行,只是**身份**不是 API 错误),而不必按前缀猜。
|
|
173
|
-
*
|
|
174
|
-
* 🔴 与产出侧同一个真源:产出在 {@link runStream} 的 governance 臂,判别在这里,端只 import。
|
|
175
|
-
* 各写一份正则必漂(记忆 `machine-readable-signal-not-visual-anchor`)。
|
|
176
|
-
*/
|
|
177
83
|
export function isGovernanceStopRowText(text) {
|
|
178
84
|
const t = text.trimStart();
|
|
179
85
|
return t.startsWith(`${RUN_STOPPED_MESSAGE_PREFIX}:`) || t.startsWith(`${RUN_BLOCKED_MESSAGE_PREFIX}`);
|
|
180
86
|
}
|
|
181
|
-
/**
|
|
182
|
-
* 第二条中性臂的前缀(0.72.8 CC-43;cli L-372 ③ 包侧正位):**模型这一轮的输出**坏了 —— core 三个 brain
|
|
183
|
-
* (anthropic / openai / open-responses)把 `tool_use` 参数不是合法 JSON(最常见成因 = 输出被 max_tokens
|
|
184
|
-
* 截断在参数中间)/ tool call 没带工具名 收成一句错误、run 以 `done{status:'failed', errorMessage}` 收口。
|
|
185
|
-
* 这一族**没有一个 provider 请求失败过**:渲成 `API Error:` 把人指去查网络 / key / 配额,而该做的是调小请求、
|
|
186
|
-
* 抬 output cap 或换个说法重问。与治理臂同律:身份换,行类旗(`isApiErrorMessage`)不换。
|
|
187
|
-
*/
|
|
188
87
|
export const MODEL_OUTPUT_ERROR_PREFIX = 'Model output error';
|
|
189
|
-
/**
|
|
190
|
-
* 这条终态错误文本是不是「模型输出问题」(⇒ 中性行 `Model output error:`)。
|
|
191
|
-
* 🔴 **只锚 core 原句的共同前缀,不做模糊匹配**:三个 brain 各带各的终止原因字段名(`stop_reason` /
|
|
192
|
-
* `finish_reason` / `status`)⇒ 只锚括号前那一段;`JSON` / `not valid` 这类词在别的错误里到处都是,放宽一个字
|
|
193
|
-
* 就会把真的传输 / 服务端错误改渲成「模型输出问题」—— 方向相反、代价一样大。core 换句子 ⇒ 本锚当场失效 ⇒
|
|
194
|
-
* 行回落 `API Error:`(开集兜底,不误报)。两句锚 = 同一铸点的两个分支(参数非 JSON / 无工具名),brain 用 ` | ` 连。
|
|
195
|
-
*/
|
|
196
88
|
export function isModelOutputErrorText(text) {
|
|
197
|
-
// 🔴 0.72.9 CC-48(test 0.72.8 验收 #57):**按位置**认锚,不按子串 —— 真实 provider HTTP 400 的错误体会引用这句
|
|
198
|
-
// (`offending text: "tool call argument(s) not valid JSON …"`),子串命中就把 API 侧拒请求说成模型输出坏了。
|
|
199
|
-
// core 的铸点把这两句放在 errorMessage **开头**(两句都出现时 brain 用 " | " 连,第一句仍在开头)⇒ **只认消息开头**;
|
|
200
|
-
// 引用形、加前缀形、`<上游错误> | <引用锚句>` 形(异源复审第二轮抓出:任意段首也是子串匹配的变体)一律不命中。
|
|
201
89
|
const anchors = ['tool call argument(s) not valid JSON', 'tool call(s) arrived with no tool name and cannot be executed'];
|
|
202
90
|
const t = text.trimStart();
|
|
203
91
|
return anchors.some((a) => t.startsWith(a));
|
|
204
92
|
}
|
|
205
|
-
/** 这一行是不是「模型输出问题」行(渲染面 / 归约面的谓词;与铸点同源,端别自抄前缀字面)。与 {@link isGovernanceStopRowText} 互斥。 */
|
|
206
93
|
export function isModelOutputErrorRowText(text) {
|
|
207
94
|
return text.trimStart().startsWith(`${MODEL_OUTPUT_ERROR_PREFIX}:`);
|
|
208
95
|
}
|
|
209
|
-
/**
|
|
210
|
-
* 0.72.12 CC-50:第三条中性终局行身份 —— **结局不知道**(done 帧无终态记录 / 认不出的终态词)。
|
|
211
|
-
* 三端渲染面按行首认它(与 `Model output error:` / `Run stopped:` 同律);它不是 API 错误、不是失败判决,
|
|
212
|
-
* 是「别把这次结果当已验证」。铸点单源在 runStream 终局臂(读结果帧 `_sema_outcome`)。
|
|
213
|
-
*/
|
|
214
96
|
export const OUTCOME_UNKNOWN_ROW_PREFIX = 'Outcome unknown';
|
|
215
|
-
/**
|
|
216
|
-
* 合成终态行的**唯一铸点**(0.72.12 起两处共用:`API Error:` / `Run stopped:` / `Model output error:` 那一行,与
|
|
217
|
-
* `Outcome unknown:` 那一行)。
|
|
218
|
-
* [2084]③:message 内部要有判别哨兵——外层 isApiErrorMessage/err- uuid 只护住读信封的消费者,而转录回喂/摘要器/
|
|
219
|
-
* 评分器读的是 message 本体,没有 in-message 标记就会把这句引擎文案当成模型自己说的话。CC 2.1.220 同款哨兵 =
|
|
220
|
-
* model:'<synthetic>'(下游投影层对 string 型 model 原样保留,不再回填真模型名)。
|
|
221
|
-
* 🔴 `isApiErrorMessage: true` 是 CC 的**行类**旗(「这是合成的终态错误行,不是模型说的话」),不是「provider API
|
|
222
|
-
* 错误」的断言:可见性过滤 / -p 契约投影 / tick 熔断都读它。
|
|
223
|
-
*/
|
|
224
97
|
function syntheticTerminalRow(ctx, text) {
|
|
225
98
|
return {
|
|
226
99
|
session_id: ctx.sessionId ?? '',
|
|
@@ -231,21 +104,9 @@ function syntheticTerminalRow(ctx, text) {
|
|
|
231
104
|
isApiErrorMessage: true,
|
|
232
105
|
};
|
|
233
106
|
}
|
|
234
|
-
/** 这一行是不是「结局不知道」终局行(只认行首)。 */
|
|
235
107
|
export function isOutcomeUnknownRowText(text) {
|
|
236
108
|
return text.trimStart().startsWith(`${OUTCOME_UNKNOWN_ROW_PREFIX}:`);
|
|
237
109
|
}
|
|
238
|
-
/**
|
|
239
|
-
* 终态是不是治理/契约面的(⇒ 中性行),而不是 provider/HTTP 面的(⇒ 保留 `API Error:`)。
|
|
240
|
-
*
|
|
241
|
-
* 🔴 **开集纪律**:只有**认得出**的治理码才走中性臂;认不出的码一律落回 `API Error:` 原臂
|
|
242
|
-
* (原样透传引擎的话)。反过来做 = 替引擎断言「这不是 API 错误」,而那正是本修在骂的那类错档。
|
|
243
|
-
*
|
|
244
|
-
* 成员:
|
|
245
|
-
* · `status:'blocked'`(ReportBlocked 契约终态 —— agent 自己说它无法继续,零 provider 参与);
|
|
246
|
-
* · `limits.*` 全族(开集前缀,{@link isLimitsExceededCode});
|
|
247
|
-
* · `output.invalid`(结构化输出重试耗尽 —— CC 自己也有专门 subtype,同样不叫 API 错误)。
|
|
248
|
-
*/
|
|
249
110
|
export function isGovernanceTerminal(input) {
|
|
250
111
|
if (input.status === 'blocked')
|
|
251
112
|
return true;
|
|
@@ -253,73 +114,15 @@ export function isGovernanceTerminal(input) {
|
|
|
253
114
|
return true;
|
|
254
115
|
return input.errorCode === OUTPUT_INVALID;
|
|
255
116
|
}
|
|
256
|
-
/**
|
|
257
|
-
* 已上报过的未知帧类型(REF-CC-057:每种臂只吼一次,免得一条流刷屏)。
|
|
258
|
-
* 🔴 单例登记见 `docs/refactor/p1-scan/singleton-manifest.json`:复制两份的后果 = 同一种臂多打
|
|
259
|
-
* 几行 stderr,不影响正确性(dupRisk=low)。
|
|
260
|
-
*/
|
|
261
117
|
const reportedDroppedTypes = new Set();
|
|
262
|
-
/**
|
|
263
|
-
* 去重表的容量上限(ADAPTER-F5,2026-08-02)。
|
|
264
|
-
*
|
|
265
|
-
* 表的键取自 **wire 值**(`String(ev.type)` —— 引擎发什么就是什么),而表是 module 级、生产期
|
|
266
|
-
* 不清:一个乱发 `type` 的引擎/中间件能让它无界膨胀。上限到顶后**停止登记**(不是清空):
|
|
267
|
-
* 清空会让同一批乱码 type 循环重登、每轮再吼一遍,交替序列下上限永远到不了
|
|
268
|
-
* (记忆 `loop-termination-requires-reevaluation` 的兜底推论);停止登记的代价只是「顶之后的
|
|
269
|
-
* 新臂每条都吼」—— 而那正是「引擎发了 64 种我不认识的臂」时应该吵的场面。
|
|
270
|
-
*/
|
|
271
118
|
const DROPPED_TYPE_MEMO_CAP = 64;
|
|
272
|
-
/** 留痕文案里 `type` 的呈现上限(超出截断);见 {@link sanitizeFrameType}。 */
|
|
273
119
|
const DROPPED_TYPE_DISPLAY_CAP = 60;
|
|
274
|
-
/**
|
|
275
|
-
* 把不可信的 wire `type` 收拾成**一行日志安全**的呈现形(ADAPTER-F5)。
|
|
276
|
-
*
|
|
277
|
-
* 未处理时它原样进 `console.error` 的模板:一个带 `\n[client-core] …` 的 type 能在运维的 stderr
|
|
278
|
-
* 里伪造出额外的整行(日志注入),控制符还能改终端状态。判据锚在「换行/控制符出不去 + 长度有界」,
|
|
279
|
-
* 不锚在「引擎不会那么发」—— 后者是对上游的假设,不是本层的性质。
|
|
280
|
-
*/
|
|
281
120
|
function sanitizeFrameType(type) {
|
|
282
|
-
// eslint-disable-next-line no-control-regex
|
|
283
121
|
const flattened = type.replace(/[\u0000-\u001f\u007f-\u009f]/g, '\uFFFD');
|
|
284
122
|
return flattened.length > DROPPED_TYPE_DISPLAY_CAP
|
|
285
123
|
? `${flattened.slice(0, DROPPED_TYPE_DISPLAY_CAP)}…`
|
|
286
124
|
: flattened;
|
|
287
125
|
}
|
|
288
|
-
/**
|
|
289
|
-
* 未知/畸形帧的**留痕**(§C5 丢弃必须留痕,且痕迹要到得了能处置它的人)。
|
|
290
|
-
*
|
|
291
|
-
* 为什么是 `console.error` 而不是 `hostLog`:runStream 是**可移植内核**,portability 门对它的
|
|
292
|
-
* 传递闭包断言「零 Node 内建 + 文件数上限 6」,值级 import `host.ts` 会当场把闭包撑破。
|
|
293
|
-
* console 在 Node 与浏览器都在,与 `host.ts` 自己那条「口没装就吼一声」的 miss 提示同款姿势。
|
|
294
|
-
* 🔴 已知限制(诚实披露):Ink 挂载后宿主会吞 console,所以 TUI 里这行未必上屏 —— 那正是
|
|
295
|
-
* `EventProjection.dropped` 存在的意义:想在自己那一端把它渲出来的宿主,读返回值即可。
|
|
296
|
-
*
|
|
297
|
-
* ── [C77]① 宿主出口 `ctx.onDroppedFrame`(2026-08-02,协作板 web-client 请托)────────────────
|
|
298
|
-
* 上一段那条「读返回值即可」只对**自己驱动投影器**的宿主成立;经 `runStream` 消费的宿主
|
|
299
|
-
* (web/desktop 都是)看不到 `EventProjection`,于是丢帧对它只剩一行 stderr。装了 sink 的宿主
|
|
300
|
-
* 从此收到结构化的 `{type, why}`,自己那一端想渲成告警条还是计数器随它。
|
|
301
|
-
*
|
|
302
|
-
* 三条语义,各自的理由:
|
|
303
|
-
* ① **不去重**:去重表 `reportedDroppedTypes` 是给 console 防刷屏用的(一条流同一种臂只吼一次)。
|
|
304
|
-
* sink 那边**每一条都发** —— 宿主的告警面要的是真计数,包侧替它去重就是替它撒谎;它想
|
|
305
|
-
* 去重是它自己一行的事,我们把真数给不出来才是不可逆的损失。
|
|
306
|
-
* ② **装了 sink ⇒ console 让位**:§C5 要的是「痕迹到得了能处置它的人」,不是「痕迹出现两次」。
|
|
307
|
-
* sink 是严格更强的通道(结构化 + 到得了 UI),而 console 这条腿自己承认在 Ink 宿主里会被
|
|
308
|
-
* 吞;再者一个直写 stdout/stderr 的旁路会打乱 TUI 渲染。宿主显式装了 sink = 它认领了这件事。
|
|
309
|
-
* 🔴 让位**不改缺省**:没装 sink 的宿主(含今天所有既有调用点)行为一字不变。
|
|
310
|
-
* ③ **sink 抛错 ⇒ 流照常 + 痕迹落回 console**:宿主的 bug 绝不许打断引擎流(与 `emitChrome`
|
|
311
|
-
* 两处 fail-soft 同款);而②的让位前提是「sink 真接住了」,没接住就得把 console 那腿还回来
|
|
312
|
-
* —— 否则一个坏 sink 会让丢帧比装它之前更隐蔽(装了个东西反而更瞎,是最坏的一种)。
|
|
313
|
-
*/
|
|
314
|
-
/**
|
|
315
|
-
* 判词 → 这一行说给人听的那句话。**开集**(`why` 是投影器给的开集判词)⇒ 表外判词走缺省句,
|
|
316
|
-
* 绝不因为多了一个判词就不说话。
|
|
317
|
-
*
|
|
318
|
-
* 🔴 为什么要分句而不是一句通用的:缺省那句逐字说「this build's projector has no arm for it」——
|
|
319
|
-
* 对 `unknown_arm` / `malformed` 是真话,对 **0.68.0 的契约违约判词是假话**(本 build 有臂,
|
|
320
|
-
* 是上游那一帧违了自己声明的契约)。一句说错方向的诊断会把读它的人指去升级客户端,而该做的是
|
|
321
|
-
* 去看引擎那一侧。
|
|
322
|
-
*/
|
|
323
126
|
const DROPPED_WHY_SENTENCE = Object.freeze({
|
|
324
127
|
duplicate_seq: 'a frame with this event id was already consumed on this run stream, so it was dropped as a replay (durable idempotency, contract 02 §1.1). ' +
|
|
325
128
|
'If the engine reuses an id across two DIFFERENT frames (server 7.77.0 does this for `reasoning_end`, fixed upstream in 7.78.1), the second one is lost here — this line is the only trace.',
|
|
@@ -327,13 +130,6 @@ const DROPPED_WHY_SENTENCE = Object.freeze({
|
|
|
327
130
|
'The turn is counted as UNKNOWN spend (the run total is reported as a lower bound), and the frame itself renders NOWHERE.',
|
|
328
131
|
});
|
|
329
132
|
const DROPPED_WHY_SENTENCE_DEFAULT = "this build's projector has no arm for it (engine newer than the client, or a malformed frame). It renders NOWHERE.";
|
|
330
|
-
/** 🔴 **按自有属性查表**(本仓对措辞表的既定纪律):`Object.freeze` 不移除原型,裸下标会让一个
|
|
331
|
-
* 叫 `constructor` / `toString` 的判词命中 `Object.prototype` 上的**函数**并被拼进日志行。 */
|
|
332
|
-
/**
|
|
333
|
-
* CC-13(0.69.2)**出公面**:一条丢帧判词(`why` 开集进、缺省句兜底)。此前只在 console 兜底腿拼进日志,
|
|
334
|
-
* `ctx.onDroppedFrame` 只收 `{ type, why }` ⇒ 自渲运维面的端只能手抄本表。现在载荷同时带 `sentence`,
|
|
335
|
-
* 本函数给宿主在别处(存量留痕 / 别的 why 来源)复用同一份措辞。
|
|
336
|
-
*/
|
|
337
133
|
export function droppedWhySentence(why) {
|
|
338
134
|
const row = Object.hasOwn(DROPPED_WHY_SENTENCE, why) ? DROPPED_WHY_SENTENCE[why] : undefined;
|
|
339
135
|
return typeof row === 'string' ? row : DROPPED_WHY_SENTENCE_DEFAULT;
|
|
@@ -341,49 +137,25 @@ export function droppedWhySentence(why) {
|
|
|
341
137
|
function reportDroppedFrame(why, type, ctx) {
|
|
342
138
|
if (ctx.onDroppedFrame) {
|
|
343
139
|
try {
|
|
344
|
-
// 以 ctx 为 receiver 调用(宿主写成方法形时 `this` 不丢;与 ctx.emitChrome 同款姿势)。
|
|
345
140
|
ctx.onDroppedFrame({ type, why, sentence: droppedWhySentence(why) });
|
|
346
|
-
return;
|
|
141
|
+
return;
|
|
347
142
|
}
|
|
348
143
|
catch (e) {
|
|
349
|
-
// 宿主 sink 抛错:不 rethrow(理由 ③),但**必须留痕**——先说 sink 自己坏了,
|
|
350
|
-
// 再走下面的 console 腿把这条丢帧本身补上。
|
|
351
|
-
// eslint-disable-next-line no-console
|
|
352
144
|
console.error(`[client-core] ctx.onDroppedFrame threw (host sink); falling back to console: ${String(e)}`);
|
|
353
145
|
}
|
|
354
146
|
}
|
|
355
|
-
// ADAPTER-F5 ①:去重表有界。到顶后不再登记(也不清空,理由见 DROPPED_TYPE_MEMO_CAP 头注)——
|
|
356
|
-
// 键是不可信 wire 值,无界表是一条「引擎乱发 type 就能把宿主内存吃掉」的路。
|
|
357
147
|
if (reportedDroppedTypes.has(type))
|
|
358
148
|
return;
|
|
359
149
|
if (reportedDroppedTypes.size < DROPPED_TYPE_MEMO_CAP)
|
|
360
150
|
reportedDroppedTypes.add(type);
|
|
361
|
-
|
|
362
|
-
console.error(
|
|
363
|
-
// ADAPTER-F5 ②:type 是 wire 值,进日志行前必须 sanitize(裸值能伪造额外的整行)。
|
|
364
|
-
`[client-core] dropped an engine frame (${why}): type="${sanitizeFrameType(type)}" — ${droppedWhySentence(why)}`);
|
|
151
|
+
console.error(`[client-core] dropped an engine frame (${why}): type="${sanitizeFrameType(type)}" — ${droppedWhySentence(why)}`);
|
|
365
152
|
}
|
|
366
|
-
/** 测试钩:清空「已上报过的臂」去重表(去重是**跨调用**状态,不清就只有第一条用例看得见)。 */
|
|
367
153
|
export function _resetDroppedFrameReportForTest() {
|
|
368
154
|
reportedDroppedTypes.clear();
|
|
369
155
|
}
|
|
370
|
-
/** 测试钩:去重表当前条数 —— ADAPTER-F5 的上限断言要读的**决定结果的量**(行数只证「吼了几次」,
|
|
371
|
-
* 证不了「表有没有涨」;两者在到顶之后恰好分道扬镳,所以必须直接读表)。 */
|
|
372
156
|
export function _droppedFrameMemoSizeForTest() {
|
|
373
157
|
return reportedDroppedTypes.size;
|
|
374
158
|
}
|
|
375
|
-
/**
|
|
376
|
-
* Drive one AgentEvent source into a CC SDKMessage stream.
|
|
377
|
-
*
|
|
378
|
-
* @param events the SDK AgentEvent iterable (durable or live).
|
|
379
|
-
* @param ctx identity context (sessionId threaded onto every emitted arm).
|
|
380
|
-
* @param handle optional out-param updated with the latest folded usage.
|
|
381
|
-
*/
|
|
382
|
-
// In-flight turn counter (module-level: every interactive turn flows through
|
|
383
|
-
// runStream). Consumed by the engine versionDrift rolling-upgrade gate — the
|
|
384
|
-
// engine must NEVER be hot-swapped mid-turn (it would sever this stream), so
|
|
385
|
-
// the lifecycle loop defers the swap until this reads false (clay 2026-07-05
|
|
386
|
-
// 滚动升级/一体化继承).
|
|
387
159
|
let inFlightTurns = 0;
|
|
388
160
|
export function isRunStreamActive() {
|
|
389
161
|
return inFlightTurns > 0;
|
|
@@ -399,36 +171,10 @@ export async function* runStream(events, ctx, handle = {}) {
|
|
|
399
171
|
}
|
|
400
172
|
async function* runStreamInner(events, ctx, handle = {}) {
|
|
401
173
|
const seen = new Set();
|
|
402
|
-
// P1-5 (print `--output-format json` 元数据失真): stamp the stream-open instant once so the terminal
|
|
403
|
-
// projector can emit a REAL `duration_ms` (the wire carries no duration; the old hardcoded 0 was fake).
|
|
404
174
|
if (ctx.startedAtMs === undefined)
|
|
405
175
|
ctx.startedAtMs = Date.now();
|
|
406
|
-
// ── L-228(0.67.0):per-subagent turn usage 的**流内**累加表 ─────────────────────────────────
|
|
407
|
-
// 🔴 **每条流一张**(局部量,不是模块级):两条并发的流各自攒各自看见的账;做成模块级单例会让
|
|
408
|
-
// A 流的子代用量落进 B 流的终帧(本仓在册的「共享 store 跨流污染」病形)。
|
|
409
|
-
// 🔴 它**只在终帧那一拍**挂到 `ctx` 上(见下面 `done`/`failed` 分支),不是开流时就挂 ——
|
|
410
|
-
// 终帧的铸点在 `terminalToSdkResult`(单一 mint 点,见该文件顶注:给信封加一个位要改六处
|
|
411
|
-
// 正是它收编掉的病),而 `ctx` 是**调用方**的对象:宿主若把同一个 ctx 复用给两条并发的流,
|
|
412
|
-
// 开流时挂等于让后开的那条把先开的那条的表顶掉,先开的终帧于是报出别人的账。
|
|
413
|
-
// 挂在终帧那一拍 + 与 `terminalToSdkResult(...)` 在**同一个同步步**里,那个窗按构造不存在。
|
|
414
176
|
const nestedUsageByTask = new Map();
|
|
415
|
-
// ── 🔴 0.67.2 / 车 I 件 I-1(异源对抗复审 [medium])—— usage 缺口观测位是 **per-stream** ──
|
|
416
|
-
// 修前它写在 `ctx.usageMissingObserved` 上:`EmitContext` 是**调用方的对象**、可以被复用给多条流
|
|
417
|
-
// (`startedAtMs` 本来就是这么用的;`run-subagent-usage-projection-test.mjs` 的 J3 段明确支持这一形),
|
|
418
|
-
// 而那一位**只置 true、永不清** ⇒
|
|
419
|
-
// · 顺序复用:上一条流里的一轮缺口,让**下一条 usage 完整的流**的终帧铸出 `_sema_usage_lower_bound`、
|
|
420
|
-
// 同一拍的 `run_cost_reconciled` 也被标成下界 —— 宿主据此把一条账数得全的 run 渲成「≥」并持久化;
|
|
421
|
-
// · 并发复用:两条流互相串这一位。
|
|
422
|
-
// 「别人那条流有缺口」不是「这条流的数字是下界」的证据。⇒ 观测位落在**本函数的局部量**上,终局
|
|
423
|
-
// 把**本流快照**同时交给两个投影口(终帧与对账臂),两面读同一份、且谁都读不到别人那份。
|
|
424
|
-
// 🔴 **不是**靠「终帧那一拍把 ctx 上那一位清掉」修的:并发的两条流会互相覆盖那次清除。
|
|
425
177
|
let usageMissingObserved = false;
|
|
426
|
-
// ── 0.73.4 —— 本流 `toolCallId → tool_start.args` 快照(终帧拒绝清单第三键的来源)──
|
|
427
|
-
// 拒绝账本行带得出「哪一次调用」(`toolCallId`),带不出那次调用的入参对象;入参在同一条流的
|
|
428
|
-
// `tool_start` 帧上。与上面两格同款:**本函数的局部量**,终局那一拍作为按次入参交给终帧铸点,
|
|
429
|
-
// 绝不挂 `ctx`(调用方可复用给多条流 ⇒ 上一条流的入参会 join 到下一条流的拒绝行上)。
|
|
430
|
-
// 只存帧上那只对象的引用(转录里的 `tool_use.input` 是同一只),不复制;有条数上限。
|
|
431
|
-
// 同一个 id 见到两份**不同**的入参 ⇒ 判不出被拒的是哪一份 ⇒ 该 id 永久出表(不回头)。
|
|
432
178
|
const toolInputByCallId = new Map();
|
|
433
179
|
const toolInputAmbiguous = new Set();
|
|
434
180
|
let toolInputOverflowed = false;
|
|
@@ -443,27 +189,19 @@ async function* runStreamInner(events, ctx, handle = {}) {
|
|
|
443
189
|
}
|
|
444
190
|
};
|
|
445
191
|
for await (const ev of events) {
|
|
446
|
-
// event-id idempotency — drop a re-seen durable seq (contract 02 §1.1).
|
|
447
192
|
const seq = eventSeq(ev);
|
|
448
193
|
if (seq !== undefined) {
|
|
449
194
|
if (seen.has(seq)) {
|
|
450
|
-
// CC-09(0.69.1;cli L-319 `-p` 真根因的包侧半场):**丢也要留痕**。server 7.77.0 给 `reasoning_end` 的
|
|
451
|
-
// SSE `id:` 复用了该段首枚 `reasoning_delta` 的 id ⇒ 这条去重把**权威段**当重放帧丢了,而修前这里是一条裸
|
|
452
|
-
// `continue` —— 比 0.68.2 的 `unknown_arm`(至少有痕)更静默。归口 = server 改铸唯一 id(7.78.1);本层
|
|
453
|
-
// 不猜「这是真重放还是 id 复用」(去重律不变,契约 02 §1.1),只把这次丢弃交给宿主的丢帧留痕口。
|
|
454
195
|
reportDroppedFrame('duplicate_seq', String(ev.type ?? 'unknown'), ctx);
|
|
455
196
|
continue;
|
|
456
197
|
}
|
|
457
198
|
seen.add(seq);
|
|
458
199
|
}
|
|
459
|
-
// 0.73.4:记本流的 `tool_start` 入参(主会话与子代内部调用同记 —— 拒绝账本是整条 run 的)。
|
|
460
200
|
if (ev.type === 'tool_start') {
|
|
461
201
|
const callId = ev.toolCallId;
|
|
462
202
|
if (typeof callId === 'string' && callId.length > 0 && !toolInputAmbiguous.has(callId)) {
|
|
463
203
|
const args = ev.args;
|
|
464
204
|
if (!toolInputByCallId.has(callId)) {
|
|
465
|
-
// 溢出是单向闩:一旦有调用没记上,之后的新 id 一律不记(出表腾出的空位不回填 ——
|
|
466
|
-
// 否则一只没记上首份入参的 id 会在重放时被当成首见,两份不同入参的判据就漏了)。
|
|
467
205
|
if (toolInputByCallId.size >= TOOL_INPUT_JOIN_MAX_CALLS)
|
|
468
206
|
toolInputOverflowed = true;
|
|
469
207
|
if (!toolInputOverflowed)
|
|
@@ -475,18 +213,6 @@ async function* runStreamInner(events, ctx, handle = {}) {
|
|
|
475
213
|
}
|
|
476
214
|
}
|
|
477
215
|
}
|
|
478
|
-
// C1 — SUBAGENT content divert (service 1.89 forwardSubagentEvents): a CONTENT event stamped with
|
|
479
|
-
// parentToolCallId belongs to a SUBAGENT run (EventIdentity contract: present ONLY on sub-agent
|
|
480
|
-
// events) — side-channel it to the 查看态 transcript store and keep it OFF the main pipeline
|
|
481
|
-
// (a child's tokens rendering as the leader's text is the pollution failure mode). turn_end's
|
|
482
|
-
// sub-flow guard below stays as-is (usage rollup only).
|
|
483
|
-
//
|
|
484
|
-
// 🔴 0.62.0 —— 判据同时收 durable 腿的**聚合形**(`text` / `reasoning`,整段全文)。
|
|
485
|
-
// 修前判据只列了四个**活体**臂,于是 `runs/:id/events` 重放尾上带 parentToolCallId 的聚合帧
|
|
486
|
-
// **漏过分流**,一路落进下面的通用投影口、渲成 leader 自己的 assistant 正文 —— 也就是本臂
|
|
487
|
-
// 整段头注在防的那个污染形,只是走了另一条腿进来(headless 断线续听 / 交互续听重放尾同踩)。
|
|
488
|
-
// 判据的锚仍是**同一个**:`parentToolCallId` 在场 = 这一帧属于子代;帧是增量还是整段与
|
|
489
|
-
// 「它属于谁」无关,所以两类臂必须共用这一条判据,而不是各判一次。
|
|
490
216
|
if ((ev.type === 'text_delta' ||
|
|
491
217
|
ev.type === 'reasoning_delta' ||
|
|
492
218
|
ev.type === 'text' ||
|
|
@@ -497,229 +223,68 @@ async function* runStreamInner(events, ctx, handle = {}) {
|
|
|
497
223
|
const sub = ev;
|
|
498
224
|
publishSubagentContentEvent({
|
|
499
225
|
type: sub.type,
|
|
500
|
-
// 🔴 EventIdentity 上**没有** `taskId`(sdk events.d.ts 的 `interface EventIdentity` 只有
|
|
501
|
-
// eventId / parentToolCallId,加 LIVE 白名单四臂的 sourceTaskId / bgAgentId;`taskId` 只长在
|
|
502
|
-
// `meta` 首帧)⇒ 生产上这里恒走右臂,归账键由 store 的 `parentToTask` 补(canonicalKey)。
|
|
503
|
-
// `??` 保留是**容将来**:哪天上游真在内容帧上发 taskId,这条直接认,不必改形。
|
|
504
|
-
// (0.30.0 发包扫描订正:此前这行注释写「taskId is on every subagent event」,与 store 侧
|
|
505
|
-
// `canonicalKey` 的注释互相矛盾,且被 .d.ts 直接证伪。)
|
|
506
226
|
taskId: sub.taskId ?? sub.parentToolCallId,
|
|
507
227
|
parentToolCallId: sub.parentToolCallId,
|
|
508
228
|
delta: sub.delta,
|
|
509
|
-
// 聚合两臂的载荷:整段全文。store 侧按**吸收**处置(活体已流过的那一段是它的前缀 ⇒
|
|
510
|
-
// 换掉而不是追加),所以「活体流过 + 重放聚合」不会双份。
|
|
511
229
|
text: sub.text,
|
|
512
|
-
// 聚合帧的**幂等键**:durable 重放会把整轮的段再送一遍,而缝前的事件序号去重只在**同一条**
|
|
513
|
-
// 流内有效(重连后是新的一条流)。把身份交给 store,让跨流的重放也只收一次。
|
|
514
|
-
// (工具两臂不读这一位 —— store 侧按 `toolCallId` 与账本上那张卡判,见 subagentContentStore。)
|
|
515
230
|
eventId: sub.eventId,
|
|
516
231
|
toolCallId: sub.toolCallId,
|
|
517
232
|
toolName: sub.toolName,
|
|
518
233
|
args: sub.args,
|
|
519
|
-
// #158 移交①([3674](d) 姊妹病,2026-08-12):此前是 `typeof sub.output === 'string' ?
|
|
520
|
-
// sub.output : undefined` —— 而 wire 的 `tool_end.output` 是非均匀的(块数组形合法),
|
|
521
|
-
// 于是子代 lane 的块数组 output 经本臂进内容账本**恒空**(查看态卡有工具、结果栏永远空白)。
|
|
522
|
-
// 换用 store 自己的那个唯一字符串化口(tail 腿 engineSubagentTail 用的同一份):两条腿喂
|
|
523
|
-
// 同一个账本,字符串化口就不能有第二份。
|
|
524
234
|
output: coerceOutput(sub.output),
|
|
525
235
|
isError: sub.isError,
|
|
526
236
|
});
|
|
527
237
|
continue;
|
|
528
238
|
}
|
|
529
|
-
// Fold usage (footer counters), AND surface the per-turn `outputTokens` as a NEUTRAL `turn_usage` arm so
|
|
530
|
-
// the upstream bridge can drive 187's C1a `end` arm (responseLength reconciles to REAL output_tokens×4 —
|
|
531
|
-
// the spinner-token feeder, verify/TOKEN-187-PORT.md step 3). `turn_end` carries no renderable content, so
|
|
532
|
-
// this arm is metrics-only; the bridge converts it to a `message_delta{usage.output_tokens}` StreamEvent.
|
|
533
239
|
if (ev.type === 'turn_end') {
|
|
534
|
-
// 🔴 L-215③ 异源对抗复审 [medium]②:`usageMissing` 是 core **刻意**造出来的诚实缺席位
|
|
535
|
-
// (臂注逐字:「Consumers must treat the missing usage as UNKNOWN — not zero」;它与
|
|
536
|
-
// `usage` **可以同帧**)。把它剥掉,本批新开的这条 usage 通道就会把「不知道」渲成一笔
|
|
537
|
-
// 全零的已知账 —— 与本批要根治的病(B-073 的成本 0/缺席)逐字同形,只是换了个量。
|
|
538
240
|
const usageMissing = ev.usageMissing === true;
|
|
539
|
-
// 🔴 L-215③ 的**终局对偶**(异源对抗复审 [medium] 实抓):这条流上**只要有一轮**报过
|
|
540
|
-
// 「这一轮没有 usage」,终帧上那些数字就是**下界**。`stats.usageMissing` 只在带得出
|
|
541
|
-
// `TaskResult` 的终帧上有,而 `failed` 事件帧 / 409 拒绝信封 / park 体**根本没有 stats**
|
|
542
|
-
// ⇒ 只读 stats 的话,一条已经观测到缺口的 run 会在终帧上被读成「每一轮都报了 usage」
|
|
543
|
-
// (而 `usage` 那几格恰好是 `flattenUsage(undefined)` 的全零)——「不知道」渲成了精确零。
|
|
544
|
-
// ⇒ 流内观测到就记下来,终帧那一拍与 stats 的读数**取并**(见 costFactParts)。
|
|
545
241
|
if (usageMissing)
|
|
546
242
|
usageMissingObserved = true;
|
|
547
243
|
const stopReasonRaw = ev.stopReason;
|
|
548
244
|
const stopWord = typeof stopReasonRaw === 'string' && stopReasonRaw.length > 0 ? stopReasonRaw : undefined;
|
|
549
|
-
// 🔴 `engineUsage` = 引擎**逐字原形**([2295] 裁 ②);`usage` = 它的 CC 镜像
|
|
550
|
-
// (`turnUsageToModelUsage` 是唯一铸口,footer 折叠与本臂用的是**同一只产物**,不另铸第二份)。
|
|
551
|
-
// 两者同拍取、同拍过下面那道违约闸 —— 闸后**都恒在场**,下游一处条件判都不再需要。
|
|
552
|
-
// 🔴 0.68.0:除它们之外还要**第三只读数**,因为本层要答的是**两个不同的问题**:
|
|
553
|
-
// · `engineUsage` —— 「这一帧有没有 usage 这个格子、而且它**成形**」(违约闸的判据);
|
|
554
|
-
// · `measured` —— 公面读器 `turnEndUsage` 的答案:`undefined` = **这一轮的账不知道**
|
|
555
|
-
// (整格缺席 / 占位六零)。它与 `usage`(逐字镜像)刻意分开 —— 把两件事合成一个读数,
|
|
556
|
-
// 一条**合法**的占位帧就会在违约闸上被误判成违约并整帧丢掉。
|
|
557
|
-
// 🔴 **成形判据不是 `!== undefined`**(异源对抗复审轮四实抓):wire 是 JSON,SSE 解析**原样透传**
|
|
558
|
-
// ⇒ `usage: null` / `usage: 7` 这类形真到得了这里。`null` 会让下面的映射在读 `costMicroUsd`
|
|
559
|
-
// 时抛 `TypeError` —— 那不是「一帧读不懂」,那是**整条流当场断掉**(后面的 `done` 一并丢);
|
|
560
|
-
// 标量则更坏:它会被映射成一份**看起来已测量**的全零账。⇒ 一律先判「非 null 的非数组对象」,
|
|
561
|
-
// 坏形与缺席走**同一条**违约路(响亮 + 不投影 + 立下界位)。
|
|
562
245
|
const rawUsage = ev.usage;
|
|
563
246
|
const engineUsage = typeof rawUsage === 'object' && rawUsage !== null && !Array.isArray(rawUsage)
|
|
564
247
|
? rawUsage
|
|
565
248
|
: undefined;
|
|
566
249
|
const usage = engineUsage !== undefined ? turnUsageToModelUsage(engineUsage) : undefined;
|
|
567
|
-
// 「这一轮的账知不知道」的**唯一判据**(与公面同一只;本层不另写一份「六格全零」的判断)。
|
|
568
250
|
const measured = turnEndUsage(ev);
|
|
569
|
-
// ── 🔴 0.68.0 BREAKING(core 7.17.0 #711)—— `turn_end.usage` **恒在场** ────────────────
|
|
570
|
-
// core 的铸点自 7.17.0 起是无条件的:`const usage = rs.turn.turnUsage ?? {六个 0}` 后
|
|
571
|
-
// `queue.push({type:"turn_end", usage, ...(turnUsageUnknown ? {usageMissing:true} : {}), …})`
|
|
572
|
-
// (`run-harness-handlers.js` `onTurnEnd`)⇒ 「没有 usage 的 turn_end」**不再是一条合法形**:
|
|
573
|
-
// 那一轮真没量出账时,core 发的是**六个 0 + `usageMissing:true`**,而不是不发 usage。
|
|
574
|
-
// ⇒ 0.65.1 / B-088 那条「usage 缺席也要照发」的臂(以及它逼出来的三处
|
|
575
|
-
// `usage !== undefined ? … : …` 条件)在本版**整条删掉**:它守的那个输入形已经不存在,
|
|
576
|
-
// 留着它等于给一个契约违约的帧准备一条静默通道。
|
|
577
|
-
//
|
|
578
|
-
// 🔴 **缺席 = 契约违约 ⇒ 响亮**(§32 的「违约无断言」纪律,本批的反钉格):
|
|
579
|
-
// ① 走宿主的丢帧留痕口({@link EmitContext.onDroppedFrame},判词开集 ⇒ 宿主不必改型),
|
|
580
|
-
// 缺 sink 时落 console —— 两条腿都说得出「哪一帧、为什么」;
|
|
581
|
-
// ② **这一轮的账确实不知道** ⇒ 同时立下界位,终帧那些数字按「≥」交付。不立的话,
|
|
582
|
-
// 一条丢了账的 run 会在终帧上被渲成一笔精确的账 —— 正是本仓反复在修的那条病;
|
|
583
|
-
// ③ 整帧**不投影**:没有 usage 就没有任何数字可交,折 0 就是把「不知道」写成已知账。
|
|
584
|
-
// 丢掉的只有同帧可能带的 `stopReason` —— 一条违约帧上的附带位不值得为它保留一条
|
|
585
|
-
// 「半读」臂(那条臂就是 ① 要消灭的静默通道)。
|
|
586
251
|
if (engineUsage === undefined || usage === undefined) {
|
|
587
252
|
usageMissingObserved = true;
|
|
588
|
-
// 🔴 违约帧同样让 footer 出口说实话:这一轮的账不知道,上面那两份读数属于更早的一轮。
|
|
589
253
|
handle.latestUsageMissing = true;
|
|
590
254
|
reportDroppedFrame('turn_end_usage_absent', ev.type, ctx);
|
|
591
255
|
continue;
|
|
592
256
|
}
|
|
593
|
-
// §E2 identity (service 1.78) — a SUB-FLOW's turn_end (orchestration/subagent round, carries
|
|
594
|
-
// the identity envelope) must NOT drive the leader's C1a `end` reconcile: its outputTokens are
|
|
595
|
-
// the child's, and reconciling the leader's responseLength against them is the token-jump bug
|
|
596
|
-
// (blackboard dogfood #2 / upstreamBridge TODO #9). Fold its usage into the rollup only.
|
|
597
|
-
// ── 🔴 0.67.1 / B-090(下游验收轨实抓)—— **身份键盲区** ────────────────────────────────
|
|
598
|
-
// 修前这条判据只认 `parentToolCallId`,而 §32g「归属钥匙」段自己写的读法是「`sourceTaskId`
|
|
599
|
-
// 优先,wire 缺席时回落 `parentToolCallId`」—— 那句话隐含「只带 `sourceTaskId`」是一条合法
|
|
600
|
-
// 组合。于是那一形的子代轮被判成**主流**,后果双重且都是本件当初要防的病:
|
|
601
|
-
// ① `!isSubFlow` 为真 ⇒ 它的 usage 去驱动 `last_turn_usage` chrome 臂与 `turn_usage` 消息臂
|
|
602
|
-
// —— §E2 断闸要防的 token-jump 在这条路上原样重现(子代的窗口占用落进主会话 Context);
|
|
603
|
-
// ② `isSubFlow` 为假 ⇒ 整条不进分表(下面那条 `if (isSubFlow)` 腿)—— 那只子代的账消失。
|
|
604
|
-
// 🔴 判据锚在 core 真字节上(`task-event.d.ts` `TaskEventIdentity`,不是本包的口味):
|
|
605
|
-
// `sourceTaskId` 逐字「stamped ONLY on the content events of a task running AS A SUB-AGENT
|
|
606
|
-
// (same condition as `parentToolCallId`)」,且「A TOP-LEVEL task's own events carry NO
|
|
607
|
-
// `sourceTaskId`」⇒ **两键同条件盖章、顶层一个都不带** ⇒ 任一在场 = 这条帧属于子流。
|
|
608
|
-
// 🔴 判据是**键在不在**(`!== undefined`),不是「是不是非空串」:空串 / 坏形也算信封在场,
|
|
609
|
-
// 于是按子流走 —— 失效方向是安全的那一侧(宁可少发一拍主会话的 usage,也绝不把子代的账
|
|
610
|
-
// 混进主会话;反过来那一侧就是 token-jump)。**行键**那一层才做非空串窄化(见下面的
|
|
611
|
-
// `parent` / `sourceTaskId`),两层问的不是同一个问题:信封在不在 vs 这一行归到谁名下。
|
|
612
257
|
const isSubFlow = ev.parentToolCallId !== undefined ||
|
|
613
258
|
ev.sourceTaskId !== undefined;
|
|
614
|
-
// 🔴 0.68.0:此处修前是 `if (usage) {` —— 那条 `usage` 在不在的判据随 core 7.17.0 的
|
|
615
|
-
// 「恒在场」一起退役(缺席在上面的违约闸里已经整帧收口)。块保留是为了**不动缩进**,
|
|
616
|
-
// 读法上它已经是无条件的一段。
|
|
617
259
|
{
|
|
618
|
-
// 🔴 0.68.0(异源对抗复审 [medium] 实抓,轮四再订正一次)—— 这个公开出口上**没有**判别位
|
|
619
|
-
// 可以让 footer 分辨「占位」与「真零」,所以它要分两件事各自决定:
|
|
620
|
-
// ① **读数**:只有**占位**(`measured === undefined` 且这一帧成形)才不覆盖 —— 保留上一次
|
|
621
|
-
// 真读数,与 #711 之前的行为逐字相同(那时这种轮根本不带 usage)⇒ 没跟车的消费者零回归。
|
|
622
|
-
// 🔴 **缺账 ≠ 占位**:`usageMissing` 可以与**真数字同帧**(core 在同一轮里攒到过数字而
|
|
623
|
-
// 另一次调用报了缺账)—— 那些数字是真的量到过(是下界),blanket 跳过会把它们丢掉
|
|
624
|
-
// (轮四实测:5 → 42+缺账位,main 的 handle 到 42,blanket 写法停在 5)。
|
|
625
|
-
// ② **判别位**:只要这一轮报了缺账就立(never false;测到账的那一轮删键)。
|
|
626
|
-
// 两件事分开之后,「读数是最新的真值」与「最新那一轮的账不全」可以同时为真。
|
|
627
260
|
if (measured !== undefined) {
|
|
628
261
|
handle.latestUsage = usage;
|
|
629
|
-
// [2295] 裁 ② 逐字通道:与镜像同拍存一份引擎原形(六键含 totalInputTokens)。
|
|
630
262
|
handle.latestEngineUsage = engineUsage;
|
|
631
263
|
}
|
|
632
264
|
if (usageMissing)
|
|
633
265
|
handle.latestUsageMissing = true;
|
|
634
266
|
else
|
|
635
267
|
delete handle.latestUsageMissing;
|
|
636
|
-
// plugins 专项 G1(2026-07-21):同一折叠点多发一份给 lastTurnUsageStore——StatusLine
|
|
637
|
-
// 的 statusline 命令 stdin(context_window.current_usage)在消息面无 usage(seam 合成
|
|
638
|
-
// 消息不带)时回落到这里,claude-hud 类插件的 Context 条才有真值。sub-flow 的 turn_end
|
|
639
|
-
// 不发(§E2 同判据):statusline 显示的是主会话窗口,子 agent 的 usage 会把 Context%
|
|
640
|
-
// 打成孩子的窗口占用(自查+对抗复审确认)。
|
|
641
|
-
// B3 切边:此前是 `void import('../../sema/lastTurnUsageStore.js').then(publishLastTurnUsage)`
|
|
642
|
-
// ——壳内向的动态边。改成 chrome 事件(宿主消费义务见 seam.ts `last_turn_usage` 臂);
|
|
643
|
-
// 与原式同样**不 await**(fire-and-forget),sink 抛错不影响流(fail-soft 原语义)。
|
|
644
268
|
if (!isSubFlow && ctx.emitChrome) {
|
|
645
269
|
{
|
|
646
|
-
// engineUsage = [2295] 裁 ② 逐字原形(additive 键,契约见 seam.ts 本臂 doc):
|
|
647
|
-
// 总量/命中率消费面吃它,镜像 usage 保纯。eopt:additive 键按「诚实缺席」= 键不在
|
|
648
|
-
// (与 turn_end.usage 本身「整体 optional,缺席≠空对象」同一语义,不是「键在值 undefined」)。
|
|
649
270
|
emitChromeFireAndForget(ctx, {
|
|
650
271
|
kind: 'last_turn_usage',
|
|
651
272
|
laneProof: mainLane(),
|
|
652
273
|
usage,
|
|
653
|
-
// 🔴 0.68.0:`engineUsage` 的条件 spread 退役 —— `turn_end.usage` 恒在场
|
|
654
|
-
// (缺席已在违约闸里整帧收口),这里再判一次就是给一个不可能的形留座位。
|
|
655
274
|
engineUsage,
|
|
656
|
-
// L-215③:chrome 腿同批带这两位(message 腿的对偶在 `turn_usage` 臂的 `_sema_` 键上)。
|
|
657
|
-
// 🔴 `usageMissing` 在这条腿上**不能**靠「不发 usage」表达 —— 本臂的 `usage` 是必填位
|
|
658
|
-
// (宿主义务是「落最近一次 turn 真 usage」),所以它只能以判别位在场:
|
|
659
|
-
// `usageMissing === true` ⇒ 同行那份 usage **不是**一笔已知的账,别当真值落槽。
|
|
660
275
|
...(usageMissing ? { usageMissing: true } : {}),
|
|
661
276
|
...(stopWord !== undefined ? { stopReason: stopWord } : {}),
|
|
662
277
|
});
|
|
663
278
|
}
|
|
664
279
|
}
|
|
665
280
|
}
|
|
666
|
-
// ── L-228(0.67.0):子流那条腿 —— **additive 第三条腿**,主臂两处 `!isSubFlow` 断闸不动 ────
|
|
667
|
-
// 病(车 E 件⑥ 实抓):core 终局只有合计 `stats.nested`(**无 per-task 分项**),而流里每条
|
|
668
|
-
// 子流 turn_end 都带着它自己那一轮的 usage —— 本包此前在 §E2 断闸处**折而未读**,于是壳的
|
|
669
|
-
// 子代详情面只渲得出 `_sema_usage_absent`。⇒ 分表只能由流内累加得出,这就是那条通道。
|
|
670
|
-
// 🔴 **归属钥匙**:`sourceTaskId` 优先,wire 缺席时回落 `parentToolCallId`(同一只子代的每一轮
|
|
671
|
-
// 至少归得到同一行)。两个都读不出 ⇒ **整条不入表**(编一个 `"unknown"` 行就是把几只子代的
|
|
672
|
-
// 账混成一只)。身份位为什么**不是** `taskId`,见下面那段 🔴。
|
|
673
|
-
// 🔴 **`usageMissing` 与数字同帧并存**:core 明说那一轮的 usage 是 UNKNOWN 不是 0 ⇒ 数字照
|
|
674
|
-
// 累加(它是**下界**),判别位在行上立起来;把那一轮整个丢掉会让下界更假。
|
|
675
281
|
if (isSubFlow) {
|
|
676
282
|
const parentToolCallId = ev.parentToolCallId;
|
|
677
|
-
// 🔴 **身份键读 `sourceTaskId`,不是 `taskId`**(异源对抗复审 [high] 实抓,core 真字节直证):
|
|
678
|
-
// core `TaskEventIdentity` 顶注逐字「the `WorkflowRun.sourceTaskId` family, **NOT a second
|
|
679
|
-
// `taskId`** — that field already exists on `task_progress` and a duplicate would bite
|
|
680
|
-
// consumers」⇒ 子代内容事件上**根本没有** `taskId` 这一位,读它恒缺席、恒回落到父调用 id,
|
|
681
|
-
// 于是**同一个父调用下的多只子任务会并成一行**(sdk 的 `turn_end` 臂也没有声明任何身份位,
|
|
682
|
-
// 两条腿都读 cast —— 这正是「按 d.ts 抄,不按印象猜」那条纪律要防的形)。
|
|
683
283
|
const rawSourceTaskId = ev.sourceTaskId;
|
|
684
284
|
const parent = typeof parentToolCallId === 'string' && parentToolCallId.length > 0 ? parentToolCallId : undefined;
|
|
685
285
|
const sourceTaskId = typeof rawSourceTaskId === 'string' && rawSourceTaskId.length > 0 ? rawSourceTaskId : undefined;
|
|
686
|
-
// 行键:`sourceTaskId` 优先,缺席回落父调用 id(同一只子代的每一轮至少归得到同一行)。
|
|
687
|
-
// ⚠️ 回落**有损**:同父调用多子任务会并成一行 —— 那时终帧那张表的 `partial` 判别位会因
|
|
688
|
-
// 行数对不上 `nested.tasks` 而立起来(诚实缺席优先于假装分得开)。
|
|
689
286
|
const taskId = sourceTaskId ?? parent;
|
|
690
|
-
// ── 🔴 0.67.2 / 车 I 件 I-2(异源对抗复审 [medium])—— **累加表的键按出身隔离** ──
|
|
691
|
-
// 病:`taskId` 有**两个命名空间**(真身份 `sourceTaskId` / 回落 `parentToolCallId`),而 core 的
|
|
692
|
-
// 合同**没有**保证两者互斥 —— 一只子任务的 id 与另一只子任务的父调用 id 完全可以撞字面。修前
|
|
693
|
-
// 直接拿裸 `taskId` 当累加键,于是撞字面的两行**在累加那一层就已经并掉**,而出身位
|
|
694
|
-
// (`keyFromParentFallback`)是**按行**记的 ⇒ 并掉之后那一行只剩一种出身,「出身混合」这道闸
|
|
695
|
-
// (`nestedUsageByTaskParts` 的 `mixedKeyOrigin`)当场读不出混合,于是被绕过。
|
|
696
|
-
// 三帧反例(异源复审逐字复现):`(sourceTaskId,parentToolCallId,inputTokens)` = ('p','a',10) / (缺席,'p',20) /
|
|
697
|
-
// (缺席,'a',30),终局 `nested={tasks:2,turns:3}` ⇒ 修前输出 `{p:30, a:30}`、行数与轮数两条对账
|
|
698
|
-
// **同时成立** ⇒ `partial` 不铸,而真相是父调用 `a` 那只花了 40、父调用 `p` 那只花了 20。
|
|
699
|
-
// ⇒ 累加键前缀化(`s:` = 真身份 / `p:` = 回落),**逐事件**把出身记进键本身;交付面的键仍是
|
|
700
|
-
// 裸 id(端零改),同字面的跨空间碰撞由 `nestedUsageByTaskParts` 判出来并如实标记。
|
|
701
|
-
// 🔴 前缀只活在**本层的累加表**里:它不是身份的一部分,wire 上自带 `s:`/`p:` 前缀的 id 因此
|
|
702
|
-
// 不会与别人串(两个空间的键各带自己的前缀,`s:` + `"p:x"` ≠ `p:` + `"s:x"`)。
|
|
703
|
-
// 🔴 0.67.1 / B-090:入表条件从「行键 **且** 父调用 id 都读得出」放宽到「**行键**读得出」。
|
|
704
|
-
// 修前那个 `&& parent !== undefined` 把「只带 `sourceTaskId`」的子代整条挡在表外 ——
|
|
705
|
-
// 而 `parent` 在这里的唯一用处是**铸 chrome 增量臂的车道证明**,不是行的身份。
|
|
706
|
-
// 行键读不出(两键都缺 / 都是空串)仍然整条不入表:编一个 `"unknown"` 行会把几只子代
|
|
707
|
-
// 的账混成一只(C3 那一格守的就是这条)。
|
|
708
287
|
if (taskId !== undefined) {
|
|
709
|
-
// 🔴 0.68.0 BREAKING:发臂条件的「三者任一在场」整条退役 —— 它是 0.65.1 / B-088 为
|
|
710
|
-
// 「裸 `{type:'turn_end', usageMissing:true}`(无 usage)」那一形写的,而 core 7.17.0
|
|
711
|
-
// 起那一形不再存在(`usage` 恒在场,缺席在上面的违约闸里整帧收口)⇒ 走到这里就**恒有
|
|
712
|
-
// 话可说**,条件判只剩车道证明那一条(`parent`)与宿主有没有装 sink。
|
|
713
|
-
// 🔴 **`parent` 缺席时本臂不发,而行照进表**(0.67.1 定谳,理由如实写在这里):
|
|
714
|
-
// chrome 信封的车道证明 `LaneProof` 的子流臂是 `{lane:'subagent', parentToolCallId: string}`
|
|
715
|
-
// (`seam.ts`),座位门 `seatContract.checkLaneProof` 对它是**硬要求**(缺伴随位的信封
|
|
716
|
-
// 当场被吃掉,且 `run-client-core-pure-test.mjs` FIX7 ④ 有专门的投毒格守着)。这一形上
|
|
717
|
-
// **没有**父调用 id ⇒ 唯一能把臂发出去的办法是拿 `sourceTaskId` 去填那一格,而那是在
|
|
718
|
-
// 一个「父调用 id」的位上放一个**任务 id** —— 正是 C4/C5 两格守的身份位互串,也是
|
|
719
|
-
// 车道纪律(REF-CC-064)存在的理由。放宽 `LaneProof` 则是**三端 BREAKING**(宿主今天
|
|
720
|
-
// 按 `parentToolCallId: string` 读),不是一个 patch 能做的事。
|
|
721
|
-
// ⇒ 增量腿在这一形上静默,**收口快照(终帧 `_sema_nested_usage_by_task`)照带这一行**
|
|
722
|
-
// —— 账不丢,少的只是这一形的实时增量;两者本来就是「同一份账的两个时刻」。
|
|
723
288
|
if (parent !== undefined && ctx.emitChrome) {
|
|
724
289
|
emitChromeFireAndForget(ctx, {
|
|
725
290
|
kind: 'subagent_turn_usage',
|
|
@@ -733,39 +298,13 @@ async function* runStreamInner(events, ctx, handle = {}) {
|
|
|
733
298
|
...(stopWord !== undefined ? { stopReason: stopWord } : {}),
|
|
734
299
|
});
|
|
735
300
|
}
|
|
736
|
-
// 累加键 = 出身前缀 + 裸 id(见上面那段 🔴)。`sourceTaskId` 缺席时 `taskId === parent`
|
|
737
|
-
// (它就是 `sourceTaskId ?? parent`),所以这里不必再写一次回落判据。
|
|
738
301
|
const rowKey = sourceTaskId !== undefined ? `s:${sourceTaskId}` : `p:${taskId}`;
|
|
739
302
|
const row = nestedUsageByTask.get(rowKey) ?? { taskId, turns: 0, inputTokens: 0, outputTokens: 0 };
|
|
740
|
-
// 🔴 0.67.1(异源复审 [medium] 实抓):记下**这一行的键是回落来的**(真身份缺席)。
|
|
741
|
-
// B-090 放宽入表条件之后行键可以有两种出身,而混合出身时「拆一行 + 并一行」的计数
|
|
742
|
-
// 误差方向相反、可以恰好抵消 ⇒ 终帧那张表的 `partial` 判据要看得见出身
|
|
743
|
-
// (理由与反例逐字在 `MutableSubagentUsageRow.keyFromParentFallback` 的头注里)。
|
|
744
|
-
// 🔴 0.67.2 / 件 I-2:出身按行均匀这件事现在由**键空间**保证(累加键带 `s:`/`p:` 前缀),
|
|
745
|
-
// 不再依赖「`taskId = sourceTaskId ?? parent` 所以真身份在场的轮不会落到回落行上」这条
|
|
746
|
-
// 推理 —— 那条推理在两个空间**撞字面**时不成立(见上面 `rowKey` 的头注)。
|
|
747
303
|
if (sourceTaskId === undefined)
|
|
748
304
|
row.keyFromParentFallback = true;
|
|
749
305
|
row.turns += 1;
|
|
750
|
-
// 🔴 0.68.0:两处 `usage?.x ?? 0` 的 `?.`/`?? 0` 退役 —— `usage` 恒在场(违约闸在上面),
|
|
751
|
-
// 留着「缺席折 0」的写法等于在代码里为一个不可能的形保留一条把「不知道」写成 0 的路。
|
|
752
306
|
row.inputTokens += usage.inputTokens;
|
|
753
307
|
row.outputTokens += usage.outputTokens;
|
|
754
|
-
// 🔴 `cacheReadTokens` 读的是**引擎原形** `engineUsage`,不是 CC 镜像:镜像的
|
|
755
|
-
// `cacheReadInputTokens` 是**必填** number,缺席在那儿已经被折成 0
|
|
756
|
-
// (`toCcModelUsage` 的 `finiteOrZero`)⇒ 从镜像读就再也分不出「没报」与「零命中」。
|
|
757
|
-
// ⇒ 一轮都没报过 ⇒ 键**不铸**;报过之后再加 0 的那些轮是真的零命中。
|
|
758
|
-
// 🔴 **0.68.0 跟车修(#711 的同形后果,族扫捞出;异源对抗复审轮一订正过一次)**:
|
|
759
|
-
// `usageMissing` 的那一轮,core 的 `usage` 是 `rs.turn.turnUsage ?? {六个 0}` ——
|
|
760
|
-
// ⚠️ **`usageMissing` 并不保证那六格是零**:core 在同一轮里可能已经攒到过真数字
|
|
761
|
-
// (`turnUsage` 有值)而**另一次**模型调用报了缺账,于是 `turnUsageMissing` 与真数字
|
|
762
|
-
// **同帧并存**(`run-harness-handlers.js:79-101` 与 `:289-293` 真字节)。
|
|
763
|
-
// ⇒ 判据只能锚在**能证明是真读数的那一半**:占位恒为 `0`,所以一个**非零有限数**
|
|
764
|
-
// 必定是真的量到过 ⇒ 照累加;而 `0` 在这一形上**分不出**占位与「零命中」⇒ 不铸
|
|
765
|
-
// (本行的全部意义就是把「没报」与「零命中」分开,那一格会被 #711 悄悄抹平)。
|
|
766
|
-
// 🔴 轮一的写法是「`usageMissing` 的轮整条跳过」,那会把**真的非零 cache 读数丢掉**
|
|
767
|
-
// (两帧 10 / 100 且第二帧带缺账位 ⇒ 修前 110、轮一写法 10,而 chrome 增量腿仍交
|
|
768
|
-
// 10 与 100 ⇒ 实时面与终局分表对不上)。收窄成「只屏蔽零」两面就一致了。
|
|
769
308
|
const cacheRead = engineUsage.cacheReadTokens;
|
|
770
309
|
if (typeof cacheRead === 'number' && Number.isFinite(cacheRead) && (!usageMissing || cacheRead !== 0)) {
|
|
771
310
|
row.cacheReadTokens = (row.cacheReadTokens ?? 0) + cacheRead;
|
|
@@ -776,36 +315,10 @@ async function* runStreamInner(events, ctx, handle = {}) {
|
|
|
776
315
|
}
|
|
777
316
|
}
|
|
778
317
|
const outputTokens = engineUsage.outputTokens;
|
|
779
|
-
// 🔴 0.68.0 BREAKING:发臂条件的「三者任一在场」整条退役(0.65.1 / B-088 那条判据的
|
|
780
|
-
// 输入形随 core 7.17.0 消失 —— 见本块顶部的违约闸)。走到这里 `usage` 恒在场 ⇒
|
|
781
|
-
// **恒有话可说**:要么是镜像,要么是 `usageMissing` 判别位,两者必有其一。
|
|
782
|
-
// ⚠️ 旧消费者零影响:`outputTokens` 读不出时仍然**整键不铸**,而 adapt 的 `turnUsageArm`
|
|
783
|
-
// 本来就以 `typeof m.outputTokens === 'number'` 开门 ⇒ 那种帧对它照旧是 no-op。
|
|
784
318
|
if (!isSubFlow) {
|
|
785
|
-
// ── L-215③(0.65.0):assistant 行那两个**算不出来**的键的真值出口 ─────────────────
|
|
786
|
-
// `eventToSdkMessage` 的 `assistantArm` 刻意**不**在内容臂上铸 `usage` / `stop_reason`
|
|
787
|
-
// (帧序:内容臂先到、`turn_end` 后到 ⇒ 臂发出时引擎还没说这一轮花了多少;在那里铸只能
|
|
788
|
-
// 是估算,而估算正是本件要根治的病)。真值只能在**这里**给 —— 这条臂本来就是 turn 收尾
|
|
789
|
-
// 那一拍的中性出口。两个都是 `_sema_` 超集键,CC 同名键语义零改:
|
|
790
|
-
// · `_sema_last_assistant_usage` —— 这一轮的 CC `ModelUsage` 镜像(与 footer 折叠用的
|
|
791
|
-
// 是**同一只** `turnUsageToModelUsage()` 产物,不另铸第二份 ⇒ 两面永远不会各漂各的);
|
|
792
|
-
// · `_sema_stop_reason` —— `turn_end.stopReason` **原词透传**(core 归一化后的五词
|
|
793
|
-
// `stop`/`length`/`toolUse`/`error`/`aborted`,sdk 型面是开放 string ⇒ 按开集读;
|
|
794
|
-
// 「这一轮是不是被 max_tokens 截了」就靠它,此前 stream 与 trace 两面互盲)。
|
|
795
|
-
// 缺席一律不铸(旧引擎不发 `stopReason`;`usage` 整体缺席的帧在违约闸那一拍就收口了)。
|
|
796
319
|
yield {
|
|
797
320
|
type: 'turn_usage',
|
|
798
|
-
// 🔴 `usageMissing` 在场 ⇒ **不铸镜像**(0.65.x 起的既有规矩:全零的「不知道」绝不冒充
|
|
799
|
-
// 一笔已知的零账),改铸判别位。
|
|
800
|
-
// 🔴 `outputTokens` 的判据随 #711 多一条(与上面子代腿的 `cacheReadTokens` **同一条**):
|
|
801
|
-
// 缺账轮的 `usage` 是 `turnUsage ?? {六个 0}`,而 `usageMissing` **不保证**那六格是零
|
|
802
|
-
// (同一轮里另一次调用报了缺账时,真数字与判别位同帧并存)⇒ 占位恒为 `0`,所以
|
|
803
|
-
// **非零有限数必定是真读数**(照铸),而 `0` 在这一形上分不出占位与真零 ⇒ 不铸 ——
|
|
804
|
-
// 把占位 `0` 交出去,以 `typeof === 'number'` 开门的既有消费者(adapt 的 `turnUsageArm`
|
|
805
|
-
// → spinner 的 responseLength 对账)会拿它当一次真的「这一轮吐了 0 个 token」。
|
|
806
321
|
...(usageMissing ? { _sema_usage_missing: true } : { _sema_last_assistant_usage: usage }),
|
|
807
|
-
// `outputTokens` 仍按**值**判:`usage` 恒在场不等于它里面每一格都是有限数,而 wire 是
|
|
808
|
-
// JSON —— 坏值折 0 就是把「读不出」写成一笔零账。读不出 ⇒ 整键不铸。
|
|
809
322
|
...(typeof outputTokens === 'number' && Number.isFinite(outputTokens) && (!usageMissing || outputTokens !== 0)
|
|
810
323
|
? { outputTokens }
|
|
811
324
|
: {}),
|
|
@@ -814,25 +327,8 @@ async function* runStreamInner(events, ctx, handle = {}) {
|
|
|
814
327
|
}
|
|
815
328
|
continue;
|
|
816
329
|
}
|
|
817
|
-
// Terminals: project then END (the SDK generator also ends here).
|
|
818
330
|
if (ev.type === 'done' || ev.type === 'failed') {
|
|
819
|
-
// clay P0 报错体系(2026-07-06,402 实测定谳):failed 终态此前只投影成 SDKResult
|
|
820
|
-
// (interactive REPL 不渲染 result 帧)= 上游 4xx/欠费/超限对用户完全静默。这里
|
|
821
|
-
// 额外合成一条 assistant 错误消息(CC 的 isApiErrorMessage 红字渲染线现成),
|
|
822
|
-
// 用户永远能看到 turn 为什么失败。
|
|
823
|
-
// 🔴 0.60.0(engine ≥7.64.0 / sdk 8.4.0):done 帧的终局读数换成带标因由,读法单源
|
|
824
|
-
// `runTerminal.readRunTerminal`(它同时认扁平形的**三个**来源:读盘回放的历史行 /
|
|
825
|
-
// server 自己的 409 拒绝信封 / 同步提交腿的 park 体)。本文件从此一个平面键都不直读。
|
|
826
331
|
const doneTerminal = ev.type === 'done' ? readRunTerminal(ev.result) : null;
|
|
827
|
-
// [909]B1 — failed/blocked 终态同享可见性行(此前只 status:'failed' 有行,墙钟停/blocked
|
|
828
|
-
// 在 REPL 面完全静默)。park(suspended/needs_review)不进此行:有各自 overlay/文案线,
|
|
829
|
-
// 非 provider 故障([884]A1 断言零 assistant 行)。
|
|
830
|
-
//
|
|
831
|
-
// 留档(退役批,2026-08-04):此处曾有第三腿 `status === 'timeout'`(5.7 的墙钟停形)。
|
|
832
|
-
// core 5.8.0([2489])把该终态词退役 —— 墙钟到限改成 `status:'failed'` +
|
|
833
|
-
// `errorCode:'limits.max_walltime_exceeded'`,而 failed 腿本来就在(errorMessage ?? errorCode
|
|
834
|
-
// ?? 'run failed'),新形的可见性**已经**由它覆盖;server 6.0.0 捆 core 5.8.0 发车后旧形不再
|
|
835
|
-
// 上 wire,该腿随之删除,不留静默缺口。
|
|
836
332
|
const errText = ev.type === 'failed'
|
|
837
333
|
? (ev.errorMessage ?? ev.errorCode ?? 'run failed')
|
|
838
334
|
: doneTerminal?.kind === 'failed'
|
|
@@ -843,55 +339,15 @@ async function* runStreamInner(events, ctx, handle = {}) {
|
|
|
843
339
|
: 'Run blocked (the agent reported it cannot proceed)')
|
|
844
340
|
: null;
|
|
845
341
|
if (errText) {
|
|
846
|
-
// 件2b(中断事故修复批 G,2026-07-15,症状2 第二环):409 active-run 拒绝的专属人话。引擎在
|
|
847
|
-
// session 已有挂起 run(上一 turn 的 AskUserQuestion park 未解锁等)时把
|
|
848
|
-
// errorMessage 'session already has an active run'(+ result.activeTaskId)骑在
|
|
849
|
-
// done{status:'failed'} 帧上(ai-agent-service server.ts:1526 同型)——裸 "API Error:" 行看着
|
|
850
|
-
// 像 provider 故障,是误导。识别该终帧 → 一行专属文案(告知在等什么 + 出路);其它错误保持
|
|
851
|
-
// 原 API Error 行(未知错误 fallback 不变)。引擎侧解锁腿已到货([868]:cancel 对
|
|
852
|
-
// suspended/needs_review 就地终态化 ⇒ claim 释放),所以文案指的就是那个端点;
|
|
853
|
-
// 自愈分诊腿(重开卡把决定权还给用户;cancel+重发臂已退役)在 `adapter/activeRunSelfHeal.ts`
|
|
854
|
-
// (A-028.1 上收,宿主经 deps 注入重开口),本层只负责:识别 + 说真话。
|
|
855
|
-
// `activeTaskId` 是**拒绝信封自己的**位(server 的 409 帧,不是引擎结果)⇒ 照旧从帧体直读,
|
|
856
|
-
// 不经因由读器(因由里没有这一位,也不该有)。
|
|
857
342
|
const activeTaskId = (ev.type === 'done' ? ev.result?.activeTaskId : undefined) ??
|
|
858
343
|
(ev.type === 'failed' ? ev.activeTaskId : undefined);
|
|
859
|
-
// REF-CC-054:判别**结构位两腿**(errorCode → activeTaskId;文案兜底腿 2026-08-03 已退役),
|
|
860
|
-
// 判别体见文件上方 `classifyActiveRunBusy`。
|
|
861
344
|
const busy = classifyActiveRunBusy({
|
|
862
345
|
errorCode: ev.type === 'failed' ? ev.errorCode : runTerminalCode(doneTerminal),
|
|
863
346
|
activeTaskId,
|
|
864
347
|
});
|
|
865
|
-
// 件14 rev2 (2026-07-14): provider max_tokens cap rejection (e.g. glm-5.2 preset 131072 >
|
|
866
|
-
// a third-party gateway's 128000) → append the way out. Keyword match on the upstream text,
|
|
867
|
-
// supplement never mask (raw provider words stay on the row).
|
|
868
|
-
// 🟢 B8 ALLOW 清单登记(REF-CC-域词表-08,属主=client-core 规范重构轮,单列一类:命中只
|
|
869
|
-
// 流向字符串拼接、不流向 if/return——supplement-only,零控制流。误判方向单侧:误命中只多
|
|
870
|
-
// 一行提示,误失效只少一行提示,行为面(哪一行 API Error / isError)不受影响。别把它跟
|
|
871
|
-
// 会改判别方向的判据(域词表-01/02 那种)混进同一张门里整体豁免)。
|
|
872
348
|
const maxTokHint = /max_tokens/i.test(errText)
|
|
873
349
|
? '\nyour provider caps max_tokens lower — lower it in /model (press m, or M to type a value)'
|
|
874
350
|
: '';
|
|
875
|
-
// 🔴 2026-07-31 假承诺修复:这里原本写的是「Press Esc to cancel it, or retry shortly.」——
|
|
876
|
-
// 两条出路**都是假的**,而且不是「某个客户端没接」,是没有任何客户端能靠它们脱困:
|
|
877
|
-
// · Esc:全 bundle 里 `/v1/runs/{id}/cancel` 在交互车道一处调用都没有,而且这一行上屏
|
|
878
|
-
// 时那条 turn 已经结束、取消键位根本没注册;
|
|
879
|
-
// · "retry shortly":park(suspended/needs_review)会**永久**留住 session claim ——
|
|
880
|
-
// 重启引擎捞不回来(boot 期孤儿回收只捞 running)、时间型 reap 挂在
|
|
881
|
-
// APPROVAL_TIMEOUT_SEC(默认 0 ⇒ 整条腿不跑),唯一兜底窗是 30 天。
|
|
882
|
-
// 换成真话 + 真的存在的两条动作(引擎的 cancel 端点 / 换一个会话)。
|
|
883
|
-
// #289 批面③ —— **错档前缀**(dogfood api C1/C2 + 案5)。轮数/预算/墙钟/token 到限、
|
|
884
|
-
// 结构化输出重试耗尽、ReportBlocked 的 blocked:这几种是**引擎侧治理/契约终态**,一个
|
|
885
|
-
// provider 请求都没失败过。此前它们与真 401/网关 502 共用 `API Error: ` 身份声明,把
|
|
886
|
-
// 「你自己设的 --max-turns 到了」说成 API 故障(判据与出处见 `isGovernanceTerminal`)。
|
|
887
|
-
// 认得出的治理终态 ⇒ 中性终态行;认不出的码一律走原 `API Error:` 臂(开集纪律)。
|
|
888
|
-
// 🔴 `isApiErrorMessage: true` 仍然置位:它是 CC 的**行类**旗(「这是合成的终态错误行,
|
|
889
|
-
// 不是模型说的话」),不是「这是 provider API 错误」的断言 —— 可见性过滤
|
|
890
|
-
// (`Messages.filterForBriefTool`)、-p 契约投影、tick 熔断都读它,撤掉 = 这一行在
|
|
891
|
-
// brief 模式里静默消失。改的是**身份文案**,不是行类。
|
|
892
|
-
// 🔴 `isGovernanceTerminal` 的入参仍是**词面**(它是一个纯谓词,公面签名不动)——
|
|
893
|
-
// 因由形下「被治理挡下」这件事由 `kind === 'blocked'` 承载,喂给它那一个词即可;
|
|
894
|
-
// 其余臂喂 `undefined`(码那一半照旧走 errorCode 腿)。
|
|
895
351
|
const governance = isGovernanceTerminal({
|
|
896
352
|
status: ev.type === 'failed' ? undefined : doneTerminal?.kind === 'blocked' ? 'blocked' : undefined,
|
|
897
353
|
errorCode: ev.type === 'failed' ? ev.errorCode : runTerminalCode(doneTerminal),
|
|
@@ -902,89 +358,43 @@ async function* runStreamInner(events, ctx, handle = {}) {
|
|
|
902
358
|
`has not been released, so this message was NOT sent. Nothing releases it on its own — ` +
|
|
903
359
|
`cancel that run (POST /v1/runs/${busyHandle ?? '<id>'}/cancel), or start a new session.`
|
|
904
360
|
: governance
|
|
905
|
-
?
|
|
906
|
-
// `Run stopped:` 会变成两个前缀叠字;其余治理码用中性前缀 + 引擎原话。
|
|
361
|
+
?
|
|
907
362
|
`${isGovernanceStopRowText(errText) ? errText : `${RUN_STOPPED_MESSAGE_PREFIX}: ${errText}`}${maxTokHint}`
|
|
908
363
|
: isModelOutputErrorText(errText)
|
|
909
|
-
?
|
|
364
|
+
?
|
|
910
365
|
`${MODEL_OUTPUT_ERROR_PREFIX}: ${errText}${maxTokHint}`
|
|
911
366
|
: `API Error: ${errText}${maxTokHint}`;
|
|
912
367
|
yield syntheticTerminalRow(ctx, rowText);
|
|
913
368
|
}
|
|
914
|
-
// plan_review park(core [508]③ 拍板形状):done `status:"needs_review"` + gate plan_review 不是
|
|
915
|
-
// 普通完成——壳弹审批卡(planReviewWire 合成 question 借 AskUserQuestion overlay;决断走
|
|
916
|
-
// /v1/assistant/tasks/:id/plan_review resume verb)。
|
|
917
|
-
// B3 切边:此前是 `await import('../../sema/planReviewWire.js')` —— **整棵 CC 树进闭包的
|
|
918
|
-
// 那一行**(设计稿 §5.2 根因链)。改成 chrome 事件;`await` 保留(cli 原本就在终帧**之前**
|
|
919
|
-
// 同步等 arm 完成,卡片先于完成行出现),sink 失败 fail-soft 照常投影终帧。
|
|
920
|
-
// 🔴 0.60.0:判据换成 `isReviewPark`,它**两个来源合读**(因由形的门种 ∪ 扁平形的 status 词)。
|
|
921
|
-
// ⚠️ **扁平那一半是必须的,不是残留**(server 回帖确认):同步提交腿命中 park 时,server 自造
|
|
922
|
-
// 的 200 体是 `{taskId, sessionId, status:"suspended"|"needs_review"}` —— **既没有退役的八个
|
|
923
|
-
// 平面键、也没有 `terminal`、连门都不带**,它不是 `TaskResult` 而是独立于它的另一只形。
|
|
924
|
-
// 在那一形上 `status:"needs_review"` 是「这是一次复核」的**唯一**信号;只读门种会让整条
|
|
925
|
-
// 同步 park 腿的 plan-review 卡不弹。因由形那一半覆盖 done 帧的引擎结果,两半各管一条腿。
|
|
926
369
|
if (ev.type === 'done' && isReviewPark(doneTerminal) && ctx.emitChrome) {
|
|
927
370
|
try {
|
|
928
371
|
await ctx.emitChrome({ kind: 'plan_review_park', laneProof: mainLane(), result: ev.result });
|
|
929
372
|
}
|
|
930
|
-
catch {
|
|
373
|
+
catch { }
|
|
931
374
|
}
|
|
932
|
-
// D-3 / B-068 · L-198(0.66.0):**终局对账那一拍**。与终帧超集键
|
|
933
|
-
// (`_sema_cost_breakdown` / `_sema_nested_usage`)同一个读器 `readRunCostFacts` ⇒ 两面同源。
|
|
934
|
-
// 🔴 0.67.1 / B-091:「同源」此前只到**读器**这一层,取并那一层(下界位的两个来源)还留在
|
|
935
|
-
// 终帧那一面上 ⇒ 两面在「流内观测到缺口、终局 stats 缄默」这一形上各说各的。现在取并
|
|
936
|
-
// 在读器里面,两面读的是**同一次计算**(第二参就是那一半来源)。
|
|
937
|
-
// 🔴 没有账就不说话:`stats` 读不出(409 拒绝信封 / park 体 / `failed` 事件帧)⇒ 不发。
|
|
938
|
-
// 🔴 子流断闸(§E2)一字不动:子代花费只经本臂的 `nestedMicroUsd`(终局 stats.nested)到账。
|
|
939
|
-
// fail-soft 同 plan_review_park:sink 抛错不影响终帧照常投影。
|
|
940
375
|
if (ev.type === 'done' && ctx.emitChrome) {
|
|
941
376
|
const doneStats = ev.result?.stats;
|
|
942
|
-
// 🔴 0.67.1 / B-091:**第二参必须传** —— 下界位的取并(`stats.usageMissing` ∪ 流内观测)
|
|
943
|
-
// 已经下沉进读器;不传就是把「臂绕开取并」那条不对称原样种回去(终帧铸了
|
|
944
|
-
// `_sema_usage_lower_bound`、同一拍的臂上却没有 `usageLowerBound`)。
|
|
945
|
-
// 🔴 0.67.2 / 件 I-1:传的是**本流快照**(局部量按值包一层),不再是共享 ctx —— 与下面那行
|
|
946
|
-
// `terminalToSdkResult(..., observed)` 是**同一份**读数。
|
|
947
377
|
const costFacts = readRunCostFacts(doneStats, { usageMissingObserved });
|
|
948
378
|
if (costFacts !== undefined) {
|
|
949
379
|
emitChromeFireAndForget(ctx, { kind: 'run_cost_reconciled', laneProof: mainLane(), ...costFacts.reconcile });
|
|
950
380
|
}
|
|
951
381
|
}
|
|
952
|
-
// L-228 / 🔴 0.67.2 件 I-2b 订正:分表**不再挂到 `ctx` 上**,而是与观测位一起随第三参按值交给
|
|
953
|
-
// 终帧投影器。此前那句「挂表与铸终帧在同一个同步步里,复用 ctx 的并发流不会串账」只覆盖了
|
|
954
|
-
// **两边都经 runStream** 的路径 —— 而这三只终帧投影器是**公面导出**,端完全可以「A 走
|
|
955
|
-
// runStream、B 直调终帧投影」共用一个 ctx,那时 B 的终帧带出的是 A 的分表(B 的 `nested`
|
|
956
|
-
// 计数恰好对得上时连 `partial` 都不铸)。与件 I-1 逐字同形,所以同批一起摘掉。
|
|
957
|
-
// 🔴 0.67.2 / 件 I-1 订正:这里此前还写着「`usageMissingObserved` 是 per-ctx 的单调布尔,
|
|
958
|
-
// 复用 ctx 的两条流里只要有一条观测到缺口,两条都该按下界读 —— 取并是安全的那一侧」。
|
|
959
|
-
// **那句话是错的**:下界位问的是「**这一条 run** 的账数全了没有」,别的流的缺口对它一个
|
|
960
|
-
// 字节的证据都不是;按那句话办,一条账数得全的 run 会被渲成「≥」并被宿主持久化成不完整状态
|
|
961
|
-
// (失效方向在这里**不是**安全的那一侧,它是在断言一件没发生的事)。
|
|
962
|
-
// ⇒ 观测位改为 per-stream 局部量,终帧按值收(第三参),与上面对账臂读的是同一份。
|
|
963
382
|
const resultFrame = terminalToSdkResult(ev, ctx, { usageMissingObserved, nestedUsageByTask, toolInputByCallId });
|
|
964
|
-
// 0.72.12 CC-50(test [7541] G2-d「铸了就要有人读」):结果帧上的 `_sema_outcome:'unknown'`(终态记录整个
|
|
965
|
-
// 缺席 / 认不出的终态词;铸点与取舍见 terminalToSdkResult `ErrorResultParts.outcomeUnknown`)在**这里**被消费 ——
|
|
966
|
-
// 交互面合成一行 `Outcome unknown:` 终局行(句子与结果帧 `errors[0]` 单源),壳零接线即可见;它**不是**
|
|
967
|
-
// `API Error:`(那是 provider 故障身份),也不是「失败」:结果边界 fail-closed 是取舍,行文案把这一点说出来。
|
|
968
|
-
// 🔴 稀疏:真 completed / 真 failed 帧不带旗,这里零行(G2-e 负控)。
|
|
969
383
|
const outcome = resultFrame.
|
|
970
384
|
_sema_outcome;
|
|
971
385
|
if (outcome === 'unknown') {
|
|
972
386
|
const errs = resultFrame.errors;
|
|
973
387
|
const sentence = Array.isArray(errs) && typeof errs[0] === 'string' ? errs[0] : 'the run ended without a terminal record this client can read';
|
|
974
|
-
// 行类旗 isApiErrorMessage 置位(合成终态行),不是「provider 错误」断言 —— 同 Model output error 行。
|
|
975
388
|
yield syntheticTerminalRow(ctx, `${OUTCOME_UNKNOWN_ROW_PREFIX}: ${sentence}`);
|
|
976
389
|
}
|
|
977
390
|
yield resultFrame;
|
|
978
391
|
return;
|
|
979
392
|
}
|
|
980
|
-
// Every other arm → typed three-state projection(REF-CC-058)。
|
|
981
393
|
const projection = eventToSdkMessage(ev, ctx);
|
|
982
394
|
if (projection.kind === 'message') {
|
|
983
395
|
yield projection.message;
|
|
984
396
|
continue;
|
|
985
397
|
}
|
|
986
|
-
// REF-CC-057:`dropped` 是「引擎发了一帧、这个 build 渲不出来」——**必须留痕**;
|
|
987
|
-
// `none` 是设计内无可渲染臂(usage 折叠/终帧代理/HITL 登记),不留痕才是对的。
|
|
988
398
|
if (projection.kind === 'dropped')
|
|
989
399
|
reportDroppedFrame(projection.why, projection.type, ctx);
|
|
990
400
|
}
|