@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
package/dist/controlRouter.d.ts
CHANGED
|
@@ -1,70 +1,15 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* ⇄ B2 批搬迁(2026-07-27,多端改造设计稿 §3 B2):cli src/seam/adapter/upstream/controlRouter.ts 逐字搬入(闭包 1 文件,只 import SDK)。
|
|
3
|
-
*/
|
|
4
|
-
/**
|
|
5
|
-
* src/seam/adapter/upstream/controlRouter.ts — the UPSTREAM control-VERB router (NEW file, no CC-logic edit).
|
|
6
|
-
*
|
|
7
|
-
* Where `hitlBridge.ts` owns the suspended → decide → resume HITL loop (the gate-resolution verbs:
|
|
8
|
-
* `approvals.decide` / `assistant.planReview` / `assistant.resume`), THIS module owns the OTHER half of the
|
|
9
|
-
* upstream control channel (contract/04 §5/§6, contract/08 IH-7/IH-8): the SUPERVISION verbs that act on a
|
|
10
|
-
* run *while it works* — submit a turn, steer it mid-flight, kill it, and the client-side QUEUED-COMMAND
|
|
11
|
-
* ordering that has no wire of its own. It is the single choke point that maps a shell control INTENT onto
|
|
12
|
-
* the right `client.runs.*` verb, branching the §9.1 error taxonomy fail-closed.
|
|
13
|
-
*
|
|
14
|
-
* The five routes (contract/04 §5/§6; 08 IH-7/IH-8; the catalog rows it binds):
|
|
15
|
-
*
|
|
16
|
-
* 1. **submit** (IH-1/IH-2, catalog L73/L74). A turn → its OWN durable run: `runs.create(TaskRequest)` →
|
|
17
|
-
* 202 `RunReceipt`. The SAME `sessionId` continues the conversation (the service serializes turns per
|
|
18
|
-
* session); the SAME `jobId` groups the sub-runs into ONE Task in the work view (contract/04 §5.2,
|
|
19
|
-
* types.ts:59-63). `runs.create` is a SUBMIT → it carries an idempotency key and the SDK may retry it.
|
|
20
|
-
*
|
|
21
|
-
* 2. **steer** (IH-7, §5.1, catalog L81). Inject mid-flight direction into a RUNNING durable run:
|
|
22
|
-
* `runs.steer(taskId, { text, mode? })`. The text is untrusted DATA — the shell sends it RAW and the
|
|
23
|
-
* SERVER fences it (the shell does NOT pre-sanitize control markers). AT-MOST-ONCE: steer is NOT
|
|
24
|
-
* idempotent, so it is NEVER retried (runs.ts:43). `mode` drains queued steers at the TURN boundary
|
|
25
|
-
* ("all" = inject all next turn; "one-at-a-time" = one per turn, FIFO). Errors are fail-closed and
|
|
26
|
-
* dispatched by the WHOLE `steering.*` family (FIX③ 2026-08-07 — 此前只认 2 个码;server 7.4.0
|
|
27
|
-
* dist 实扫 7 个,主 run steer 路由可达 4 个)。逐码语义 + 处置见 `ControlSafetyCode` 与
|
|
28
|
-
* 文件底部的 `STEERING_CODE_TO_SAFETY` / `STEERING_ADVICE` 两张表。
|
|
29
|
-
*
|
|
30
|
-
* 3. **cancel** (IH-8, §5.3, catalog L84). HARD-STOP an async run: `runs.cancel(taskId)` → 202 `CancelAck`
|
|
31
|
-
* (`status:"cancelling"` or a terminal no-op). The run then SETTLES to `failed` + `errorCode:"cancelled"`
|
|
32
|
-
* (NOT a new status — the UI shows "cancelled", not an error). 🔴 A 409 no longer means "suspended"
|
|
33
|
-
* (server [868] cancels a suspended/needs_review run in place — that IS the unlock handle); it now only
|
|
34
|
-
* means the pending gate was decided/expired concurrently, surfaced as
|
|
35
|
-
* `ControlSafetyError('cancel_lost_race')` = re-read state and retry. 404 =
|
|
36
|
-
* non-owner/unknown (no existence leak). Server-idempotent; not a submit → no SDK retry.
|
|
37
|
-
*
|
|
38
|
-
* 4. **queued commands** (§5.2, catalog L82 `contract-extension`). CC enqueues next turns with a per-message
|
|
39
|
-
* `priority: 'now'|'next'|'later'` and drops a queued message by uuid (`cancel_async_message`). The SDK
|
|
40
|
-
* has NEITHER: no `priority` on `TaskRequest`, no drop-queued verb. The faithful behavior is CLIENT-SIDE:
|
|
41
|
-
* hold a local priority-ordered queue, allow a DEQUEUE *before* submit (the only stop a queued message
|
|
42
|
-
* has — once submitted the nearest lever is `cancel`, which kills the whole run, not one message), and
|
|
43
|
-
* drain by submitting each queued turn as its own run under the same sessionId+jobId.
|
|
44
|
-
*
|
|
45
|
-
* Provider- and presentation-agnostic (contract/04 laws 2-3; 08 invariants): every verb stays at
|
|
46
|
-
* `TaskRequest` / `RunReceipt` / `CancelAck` altitude. No Anthropic `effort`/`fast_mode`, no claude.ai
|
|
47
|
-
* control-message shape, no widget/glyph crosses this seam. The router emits the SDK verb; the shell owns the
|
|
48
|
-
* keybinding, the queue chrome, and the "cancelled" badge.
|
|
49
|
-
*/
|
|
50
1
|
import type { TaskRequest, RunReceipt, CancelAck, Scenario } from '@sema-agent/sdk';
|
|
51
2
|
export interface RunsResourceLike {
|
|
52
|
-
/** POST /v1/runs → 202 RunReceipt. SUBMIT-class: carries an idempotency key, SDK may retry (runs.ts:13). */
|
|
53
3
|
create(req: TaskRequest, opts?: {
|
|
54
4
|
idempotencyKey?: string | null;
|
|
55
5
|
signal?: AbortSignal;
|
|
56
6
|
}): Promise<RunReceipt>;
|
|
57
|
-
/** POST /v1/runs/:id/steer — inject mid-flight direction into a RUNNING run. AT-MOST-ONCE: NOT a submit,
|
|
58
|
-
* NEVER retried (runs.ts:51). The text is untrusted DATA the server fences. */
|
|
59
7
|
steer(taskId: string, steer: {
|
|
60
8
|
text: string;
|
|
61
9
|
mode?: 'all' | 'one-at-a-time';
|
|
62
10
|
}, opts?: {
|
|
63
11
|
signal?: AbortSignal;
|
|
64
12
|
}): Promise<unknown>;
|
|
65
|
-
/** POST /v1/runs/:id/cancel → 202 CancelAck. Hard-stop; server-idempotent; not a submit → no retry
|
|
66
|
-
* (runs.ts:34). 🔴 suspended/needs_review 也走这里就地终态化([868]);409 只剩「挂起的 gate 被
|
|
67
|
-
* 并发决定/过期」这一种(重读状态后重试);404 non-owner。 */
|
|
68
13
|
cancel(taskId: string, opts?: {
|
|
69
14
|
signal?: AbortSignal;
|
|
70
15
|
}): Promise<CancelAck>;
|
|
@@ -72,208 +17,57 @@ export interface RunsResourceLike {
|
|
|
72
17
|
export interface ControlClientLike {
|
|
73
18
|
runs: RunsResourceLike;
|
|
74
19
|
}
|
|
75
|
-
|
|
76
|
-
* A supervision-verb stop the shell must HANDLE, not retry (contract/04 §9.1). The `code` is stable so the
|
|
77
|
-
* shell can branch.
|
|
78
|
-
*
|
|
79
|
-
* 🔴 FIX③(2026-08-07,server 7.4.0 `dist/` 亲扫)—— steering 族此前只认 2 个码。实测 `steering.*`
|
|
80
|
-
* 在 server 7.4.0 里共 **7 个**;主 run 的 `POST /v1/runs/:id/steer` 路由(本 router 唯一调用的那条)
|
|
81
|
-
* 可达其中 4 个:`not_running`(409)/ `invalid_content`(422)/ `queue_full`(409,core 5.14.0 队列
|
|
82
|
-
* 化后新出)/ `duplicate_input_id`(409,调用方带 `Idempotency-Key` 时可达);另外 3 个来自子代
|
|
83
|
-
* steer/resume 面与 workflow agent steer 面(`ambiguous_target` / `ambiguous_label` / `still_running`)。
|
|
84
|
-
* 🔴 **[7226] 包侧缺口 ①(0.68.1)**:码表补**第 8 码** `steering.blocked_by_hook`(422,部署
|
|
85
|
-
* `userPromptSubmit` 门拦下;live 腿可达)。它此前落开集兜底位 `steering_other` —— 兜底位的判词
|
|
86
|
-
* 逐字是「别按成员猜它的意思」,而这一码的处置恰恰是**明确的**(输入未受理、改内容自由重试)⇒
|
|
87
|
-
* 兜底在这一位上不是「安全降级」,是把一条能自救的拒绝渲成一条不知道怎么办的拒绝。
|
|
88
|
-
* SDK 6.3.0 侧已把整族改成**前缀分派**(`SteeringError` 基类),所以「没认全」的后果不是崩溃,
|
|
89
|
-
* 而是那些码原样裸抛给壳 —— 壳只 `catch (e instanceof ControlSafetyError)` 就漏在外面。
|
|
90
|
-
*
|
|
91
|
-
* ⚠️ `still_running` 与 `not_running` **语义正相反**(前者=子代还在飞,该 steer 不该 resume;后者=
|
|
92
|
-
* 它不在飞)。两码塌进同一个判别位会让 UI 说反话,所以它们各占一个码,永不合并。
|
|
93
|
-
*/
|
|
94
|
-
export type ControlSafetyCode =
|
|
95
|
-
/** steer 了一个不在飞的 run(suspended/terminal/跨副本);处置=先解它的 gate,别 steer。 */
|
|
96
|
-
'not_running'
|
|
97
|
-
/** steer 正文带控制面越狱串,core 拒;处置=SURFACE,绝不 strip-and-retry。 */
|
|
98
|
-
| 'invalid_content'
|
|
99
|
-
/** 挂起 checkpoint 上的 steer 队列满了(core fail-loud,绝不驱逐已接受的指令);处置=等它 drain。 */
|
|
100
|
-
| 'queue_full'
|
|
101
|
-
/** 同一个 `Idempotency-Key` 配了**不同**正文;处置=换一个 key,别复用(重发原文没有意义)。 */
|
|
102
|
-
| 'duplicate_input_id'
|
|
103
|
-
/** `ambiguous_target`(子代面)/ `ambiguous_label`(workflow 面)—— 不止一个活体匹配;
|
|
104
|
-
* 两码同一处置(换唯一定位方式),故共用一个判别位,`cause.errorCode` 保留是哪条轴。 */
|
|
105
|
-
| 'ambiguous_target'
|
|
106
|
-
/** 对一个**还在飞**的子代调了 resume;处置=改调 steer,或等它 settle。`not_running` 的反面。 */
|
|
107
|
-
| 'still_running'
|
|
108
|
-
/**
|
|
109
|
-
* 部署的 `userPromptSubmit` 门拦下了这条输入([7226] 包侧缺口 ①,0.68.1;server 契约 (2) 表
|
|
110
|
-
* 第 8 行;core 5.62 design/373 §4.3)。block / 超时 / 崩溃**同码 fail-closed**,成因由 message
|
|
111
|
-
* 判别(携 hook 自己的 bounded reason)。
|
|
112
|
-
* 🔴 **输入未被受理**:没有 `human_input` 帧、`inputId` 不入账 ⇒ 处置 = **改内容自由重试**。
|
|
113
|
-
* 🔴 它与 `invalid_content` / `steering_other` 都**不许合并**:前者是「正文违规,SURFACE 别
|
|
114
|
-
* strip-and-retry」,后者的判词逐字是「别按成员猜它的意思」—— 把一条**能自救**的拒绝塌进这两位
|
|
115
|
-
* 任何一位,UI 都会把「改一句话再发」说成「你没救了」。
|
|
116
|
-
* ⚠️ `/steer` 的 **park 腿结构性不可达本码**(hook 对 parked 转向的拦截发生在 resume 再投递时刻,
|
|
117
|
-
* 走 `steering.parked_input_blocked` 通告);本路由器调的是 live 腿,所以这一位在这里可达。
|
|
118
|
-
*/
|
|
119
|
-
| 'blocked_by_hook'
|
|
120
|
-
/** 开集兜底:`steering.` 前缀但本表不认得的**未来**码(SDK 前缀分派同款姿势)。
|
|
121
|
-
* 处置=按「这条 steer 没落地」呈现,并把 `cause.errorCode` 原样打进日志,别按成员猜语义。 */
|
|
122
|
-
| 'steering_other'
|
|
123
|
-
/** cancel 的 409:挂起的 gate 被并发决定/过期(重读状态后重试 cancel)。码缺席 / 认不出的 409 也落这一位(行为同旧)。 */
|
|
124
|
-
| 'cancel_lost_race'
|
|
125
|
-
/** 0.73.1:cancel 的 409 `conflict.run_not_running` —— 取消旗反复挂不上(行在 running 与 park 之间来回跳);引擎一个字节没改,处置 = **直接重试**。
|
|
126
|
-
* 与 `cancel_lost_race` 处置相反(那一位是「别人已经替这条 run 做了决定,先去看结果」),所以各占一个码。 */
|
|
127
|
-
| 'cancel_run_not_running'
|
|
128
|
-
/** 404 — non-owner / unknown run(无存在性谕示器)。 */
|
|
129
|
-
| 'not_found';
|
|
20
|
+
export type ControlSafetyCode = 'not_running' | 'invalid_content' | 'queue_full' | 'duplicate_input_id' | 'ambiguous_target' | 'still_running' | 'blocked_by_hook' | 'steering_other' | 'cancel_lost_race' | 'cancel_run_not_running' | 'not_found';
|
|
130
21
|
export declare class ControlSafetyError extends Error {
|
|
131
22
|
readonly code: ControlSafetyCode;
|
|
132
|
-
/** The original SDK error, for logging (never re-thrown blind). */
|
|
133
23
|
readonly cause?: unknown | undefined;
|
|
134
|
-
constructor(message: string, code: ControlSafetyCode,
|
|
135
|
-
/** The original SDK error, for logging (never re-thrown blind). */
|
|
136
|
-
cause?: unknown | undefined);
|
|
24
|
+
constructor(message: string, code: ControlSafetyCode, cause?: unknown | undefined);
|
|
137
25
|
}
|
|
138
26
|
export type QueuePriority = 'now' | 'next' | 'later';
|
|
139
|
-
/** A turn the user queued but has not yet submitted. `id` is a local uuid the shell uses to DEQUEUE it. */
|
|
140
27
|
export interface QueuedCommand {
|
|
141
28
|
id: string;
|
|
142
29
|
text: string;
|
|
143
30
|
priority: QueuePriority;
|
|
144
31
|
}
|
|
145
32
|
export interface ControlRouterOptions {
|
|
146
|
-
/** Continue a conversation: the SAME sessionId across turns (contract/04 §5.2; types.ts:59). Omit to start
|
|
147
|
-
* fresh — the service mints one and the shell adopts it from the first `RunReceipt.sessionId`. */
|
|
148
33
|
sessionId?: string;
|
|
149
|
-
/** Group the sub-runs of one logical job into one Task (contract/04 §5.2; types.ts:62). */
|
|
150
34
|
jobId?: string;
|
|
151
|
-
/** Scenario passthrough (default/oa/code-review/team) applied to every submitted turn. */
|
|
152
35
|
scenario?: Scenario;
|
|
153
|
-
/** Per-submit system prompt the integrator owns (types.ts:64 — stable cacheable prefix). */
|
|
154
36
|
systemPrompt?: string;
|
|
155
37
|
}
|
|
156
38
|
export declare class ControlRouter {
|
|
157
39
|
private readonly client;
|
|
158
|
-
/** The run currently in flight — the target of steer/cancel. Null before the first submit. */
|
|
159
40
|
private taskId;
|
|
160
|
-
/** Adopted from the first RunReceipt when the caller did not pin one (service-minted continuity). */
|
|
161
41
|
private sessionId;
|
|
162
42
|
private readonly jobId;
|
|
163
43
|
private readonly scenario;
|
|
164
44
|
private readonly systemPrompt;
|
|
165
|
-
/** The client-side queued turns (contract/04 §5.2 — no wire equivalent). */
|
|
166
45
|
private readonly queue;
|
|
167
46
|
private seq;
|
|
168
47
|
constructor(client: ControlClientLike, opts?: ControlRouterOptions);
|
|
169
|
-
/** The run currently in flight, if any. The shell reads this to know what steer/cancel will target. */
|
|
170
48
|
activeTaskId(): string | null;
|
|
171
|
-
/** The session token threading the conversation (service-minted after the first submit). */
|
|
172
49
|
activeSessionId(): string | undefined;
|
|
173
|
-
/**
|
|
174
|
-
* Submit a turn as a durable async run (`runs.create` → 202 `RunReceipt`). The SAME `sessionId` continues
|
|
175
|
-
* the conversation (the service serializes turns per session — a session-CAS conflict while a prior run
|
|
176
|
-
* holds the claim → 409 `Conflict`, contract/04 §5.2 / openapi:173); the SAME `jobId` groups the sub-runs
|
|
177
|
-
* into one Task in the work view. `runs.create` is a SUBMIT, so the idempotency key rides along (the SDK
|
|
178
|
-
* auto-generates one and may safely retry on transport failure).
|
|
179
|
-
*
|
|
180
|
-
* Adopts the service-minted `sessionId` from the receipt when the caller did not pin one (continuity), and
|
|
181
|
-
* latches the new run as the active steer/cancel target.
|
|
182
|
-
*/
|
|
183
50
|
submit(text: string, opts?: {
|
|
184
51
|
signal?: AbortSignal;
|
|
185
52
|
}): Promise<RunReceipt>;
|
|
186
|
-
/** Project the session context + the typed turn into the `@sema-ai` `TaskRequest` wire shape. Secret
|
|
187
|
-
* discipline (types.ts:54-58): NEVER put tokens in `objective`/`systemPrompt`. */
|
|
188
53
|
private buildTaskRequest;
|
|
189
|
-
/**
|
|
190
|
-
* Steer the RUNNING run: `runs.steer(taskId, { text, mode? })`. Hard rules this method enforces:
|
|
191
|
-
*
|
|
192
|
-
* - **The text is untrusted DATA — sent RAW.** The shell does NOT pre-sanitize control markers; the SERVER
|
|
193
|
-
* fences the text (contract/04 §5.1). A text carrying a control-plane escape → 422
|
|
194
|
-
* `SteeringInvalidContentError` → surfaced as `ControlSafetyError('invalid_content')` (SURFACE the error;
|
|
195
|
-
* never strip-and-retry).
|
|
196
|
-
* - **`trusted` is NOT a client field** — the server derives trust from the authenticated operator role;
|
|
197
|
-
* this method has no way to assert it, by construction.
|
|
198
|
-
* - **AT-MOST-ONCE — never retried.** `runs.steer` is not idempotent and not a submit, so a transport
|
|
199
|
-
* failure propagates; the router does NOT auto-resend (a double-steer must be impossible).
|
|
200
|
-
* - **Steer needs a RUNNING run.** A suspended/terminal run → 409 `SteeringNotRunningError` → surfaced as
|
|
201
|
-
* `ControlSafetyError('not_running')` (resolve its gate via the hitlBridge `decide`, don't steer).
|
|
202
|
-
* 🔴 FIX③:整个 `steering.*` 族(7 员)都归到 `ControlSafetyError` 上,含 core 5.14.0 队列化
|
|
203
|
-
* 新出的 `queue_full` / `duplicate_input_id`,以及与 `not_running` **语义相反**的 `still_running`
|
|
204
|
-
* (两者永不合并);表外的未来码落开集位 `steering_other`,不再裸抛。
|
|
205
|
-
* - **`mode` drains at the TURN boundary** (FORWARD-DRAFT per-call mapping, runs.ts:49-50): `"all"` injects
|
|
206
|
-
* all queued steers at the next turn; `"one-at-a-time"` (default) one per turn, FIFO. Steer applies at
|
|
207
|
-
* the next turn boundary, not instantly.
|
|
208
|
-
*/
|
|
209
54
|
steer(text: string, mode?: 'all' | 'one-at-a-time', opts?: {
|
|
210
55
|
signal?: AbortSignal;
|
|
211
56
|
}): Promise<unknown>;
|
|
212
|
-
/**
|
|
213
|
-
* Cancel (kill) the in-flight run: `runs.cancel(taskId)` → 202 `CancelAck` (`status:"cancelling"` or a
|
|
214
|
-
* terminal no-op). The run then SETTLES to `failed` + `errorCode:"cancelled"` — the UI shows "cancelled",
|
|
215
|
-
* NOT an error (the router does not synthesize that terminal; the downstream stream / `terminalToSdkResult`
|
|
216
|
-
* does). Branching (contract/04 §9.1):
|
|
217
|
-
*
|
|
218
|
-
* - **409 = a LOST CAS RACE, not "suspended".** 🔴 2026-07-31 更正:这段原本写的是「409 = run 是
|
|
219
|
-
* suspended,要改走 deny」—— 那是 **[868] 之前**的世界。服务端自 [868] 起对
|
|
220
|
-
* suspended/needs_review 的 run **就地取消**(先 CAS 结掉挂起的 checkpoint,再 setTerminal
|
|
221
|
-
* 释放 session claim),`cancel` 就是那种 run 的恢复把手,不再 409。今天的 409 只剩一种成因:
|
|
222
|
-
* 挂起的那个 gate 被并发决定/过期了(server `conflict.approval_settled`)。正确处置是**重读
|
|
223
|
-
* run 状态后重试 cancel**,而不是去 deny 一个已经不存在的审批 —— 照旧文指路只会指进空处。
|
|
224
|
-
* Surfaced as `ControlSafetyError('cancel_lost_race')`(干净切:旧码名 `cancel_suspended` 已
|
|
225
|
-
* 退役,本仓/壳/web/桌面均无行为消费方,只有一条注释引用,同批改)。
|
|
226
|
-
* - **404 = non-owner / unknown.** No existence oracle (404, not 403) → `ControlSafetyError('not_found')`.
|
|
227
|
-
*
|
|
228
|
-
* Server-idempotent and NOT a submit, so a successful cancel is safe to repeat (a terminal run no-ops);
|
|
229
|
-
* the router still does not auto-retry on transport failure (the shell re-invokes on the user's repeat).
|
|
230
|
-
*/
|
|
231
57
|
cancel(opts?: {
|
|
232
58
|
signal?: AbortSignal;
|
|
233
59
|
}): Promise<CancelAck>;
|
|
234
|
-
/** Enqueue a turn for later submission. `now` jumps the line, `next` (default) is FIFO, `later` trails.
|
|
235
|
-
* Returns the queued row (its `id` is the dequeue handle). Nothing leaves the machine until `drainNext`. */
|
|
236
60
|
enqueue(text: string, priority?: QueuePriority): QueuedCommand;
|
|
237
|
-
/** Drop a STILL-QUEUED turn by id BEFORE it is submitted (the only stop a queued message has —
|
|
238
|
-
* contract/04 §5.2). Returns true if it was in the queue, false if absent / already drained (mirrors CC's
|
|
239
|
-
* `cancel_async_message` → `{cancelled:false}` "not in the queue / already dequeued"). */
|
|
240
61
|
dequeue(id: string): boolean;
|
|
241
|
-
/** A stable, priority-banded view of the queue (the shell renders this; drain order matches it). */
|
|
242
62
|
pending(): readonly QueuedCommand[];
|
|
243
|
-
/**
|
|
244
|
-
* Drain the highest-priority queued turn: dequeue it and `submit` it as its own durable run (same session
|
|
245
|
-
* → continues the conversation; same jobId → one Task). Returns the `RunReceipt`, or null when the queue is
|
|
246
|
-
* empty. The session serializes turns, so the caller drains one at a time (submit, await the run, drain the
|
|
247
|
-
* next); submitting while a prior run holds the session claim surfaces the SDK's 409 `ConflictError` from
|
|
248
|
-
* `submit` — the shell waits and re-drains (contract/04 §5.2; openapi:173).
|
|
249
|
-
*/
|
|
250
63
|
drainNext(opts?: {
|
|
251
64
|
signal?: AbortSignal;
|
|
252
65
|
}): Promise<RunReceipt | null>;
|
|
253
66
|
private nextId;
|
|
254
67
|
}
|
|
255
|
-
/** design/161 批 A 起导出:错误判型单源 —— 包内新读面(backgroundView 等)一律吃这一口,不再各自
|
|
256
|
-
* 手摸 `(e as any).status`。 */
|
|
257
68
|
export interface ErrCodes {
|
|
258
69
|
code?: string | undefined;
|
|
259
70
|
name?: string | undefined;
|
|
260
71
|
status?: number | undefined;
|
|
261
72
|
}
|
|
262
|
-
/**
|
|
263
|
-
* Read a normalized error code/status off any SDK error shape variant (errorCode / code / name / status).
|
|
264
|
-
* REF-CC-135(TYPESHAPE-15,P3 wave1 卡C-argv):判型单源此前返回 3 个裸 unknown(B2/B4 违规——调用方
|
|
265
|
-
* `code === 404`(status 才是数字位)这类拼写型 bug 能编译期全绿恒 false)。窄化成具名 `ErrCodes`,
|
|
266
|
-
* 非匹配类型一律降到 undefined(而不是把垃圾值原样放行)。
|
|
267
|
-
*
|
|
268
|
-
* 🔴 [2393] sweep-F6(2026-08-03 全窗复审)**这不只是类型收窄,是运行期丢值**(裁定:保留行为,
|
|
269
|
-
* 但把它记成可核的承诺):旧实现 `return { code: o.errorCode, name: o.name, status: o.status ?? o.statusCode }`
|
|
270
|
-
* 是原样透传。今天,包外消费者若拿到的错误对象上 `status` 是数字**串**(某些传输层/代理把 status
|
|
271
|
-
* 序列化成字符串)、或 `errorCode` 是数值码,读到的一律是 `undefined`。
|
|
272
|
-
* 方向对(「拿到一个不好用的值」→「诚实地什么都没有」;下游 `code === 404` 那类拼写型 bug 也因此
|
|
273
|
-
* 编译期就红),但它是**公面行为变更**:本函数是 npm 导出的判型单源,三端各自的错误对象形需要复核。
|
|
274
|
-
* ⇒ 已进 `docs/refactor/README.md` 的提货单/BREAKING 清单(此前只列四条 wire BREAKING,签名级与
|
|
275
|
-
* 行为级的这几条靠人工列表兜底,而列表没列)。
|
|
276
|
-
* 判据:pure 门 B5 段那组钉现在也钉「脏形一律降 undefined、绝不悄悄回落成 0/空串」——
|
|
277
|
-
* 谁要把某一种脏形改回透传,必须先在那里显形。
|
|
278
|
-
*/
|
|
279
73
|
export declare function errCodes(e: unknown): ErrCodes;
|
package/dist/controlRouter.js
CHANGED
|
@@ -3,27 +3,21 @@ import { uuid } from './adapter/types.js';
|
|
|
3
3
|
export class ControlSafetyError extends Error {
|
|
4
4
|
code;
|
|
5
5
|
cause;
|
|
6
|
-
constructor(message, code,
|
|
7
|
-
/** The original SDK error, for logging (never re-thrown blind). */
|
|
8
|
-
cause) {
|
|
6
|
+
constructor(message, code, cause) {
|
|
9
7
|
super(message);
|
|
10
8
|
this.code = code;
|
|
11
9
|
this.cause = cause;
|
|
12
10
|
this.name = 'ControlSafetyError';
|
|
13
11
|
}
|
|
14
12
|
}
|
|
15
|
-
/** Band rank for stable priority ordering: lower drains first. */
|
|
16
13
|
const BAND_RANK = { now: 0, next: 1, later: 2 };
|
|
17
14
|
export class ControlRouter {
|
|
18
15
|
client;
|
|
19
|
-
/** The run currently in flight — the target of steer/cancel. Null before the first submit. */
|
|
20
16
|
taskId = null;
|
|
21
|
-
/** Adopted from the first RunReceipt when the caller did not pin one (service-minted continuity). */
|
|
22
17
|
sessionId;
|
|
23
18
|
jobId;
|
|
24
19
|
scenario;
|
|
25
20
|
systemPrompt;
|
|
26
|
-
/** The client-side queued turns (contract/04 §5.2 — no wire equivalent). */
|
|
27
21
|
queue = [];
|
|
28
22
|
seq = 0;
|
|
29
23
|
constructor(client, opts = {}) {
|
|
@@ -33,40 +27,21 @@ export class ControlRouter {
|
|
|
33
27
|
this.scenario = opts.scenario;
|
|
34
28
|
this.systemPrompt = opts.systemPrompt;
|
|
35
29
|
}
|
|
36
|
-
/** The run currently in flight, if any. The shell reads this to know what steer/cancel will target. */
|
|
37
30
|
activeTaskId() {
|
|
38
31
|
return this.taskId;
|
|
39
32
|
}
|
|
40
|
-
/** The session token threading the conversation (service-minted after the first submit). */
|
|
41
33
|
activeSessionId() {
|
|
42
34
|
return this.sessionId;
|
|
43
35
|
}
|
|
44
|
-
// ── §1. submit — a turn → its own durable run (contract/04 §5.2; 08 IH-2, catalog L74) ───────────────────
|
|
45
|
-
/**
|
|
46
|
-
* Submit a turn as a durable async run (`runs.create` → 202 `RunReceipt`). The SAME `sessionId` continues
|
|
47
|
-
* the conversation (the service serializes turns per session — a session-CAS conflict while a prior run
|
|
48
|
-
* holds the claim → 409 `Conflict`, contract/04 §5.2 / openapi:173); the SAME `jobId` groups the sub-runs
|
|
49
|
-
* into one Task in the work view. `runs.create` is a SUBMIT, so the idempotency key rides along (the SDK
|
|
50
|
-
* auto-generates one and may safely retry on transport failure).
|
|
51
|
-
*
|
|
52
|
-
* Adopts the service-minted `sessionId` from the receipt when the caller did not pin one (continuity), and
|
|
53
|
-
* latches the new run as the active steer/cancel target.
|
|
54
|
-
*/
|
|
55
36
|
async submit(text, opts) {
|
|
56
37
|
const req = this.buildTaskRequest(text);
|
|
57
38
|
const receipt = await this.client.runs.create(req, opts?.signal !== undefined ? { signal: opts.signal } : undefined);
|
|
58
|
-
// Adopt service-minted continuity + latch the in-flight run.
|
|
59
39
|
if (this.sessionId === undefined)
|
|
60
40
|
this.sessionId = receipt.sessionId;
|
|
61
41
|
this.taskId = receipt.taskId;
|
|
62
42
|
return receipt;
|
|
63
43
|
}
|
|
64
|
-
/** Project the session context + the typed turn into the `@sema-ai` `TaskRequest` wire shape. Secret
|
|
65
|
-
* discipline (types.ts:54-58): NEVER put tokens in `objective`/`systemPrompt`. */
|
|
66
44
|
buildTaskRequest(text) {
|
|
67
|
-
// 空内容守卫(2026-07-16 唤醒 400 批,同病同修):SDK/控制面递来的空/全空白 user_message 绝不作为
|
|
68
|
-
// 空 objective 上 wire——引擎会把空 user 消息写进会话持久历史(provider 400 + session 毒化)。
|
|
69
|
-
// 与 CC createUserMessage 的 NO_CONTENT_MESSAGE 同语义占位。
|
|
70
45
|
const req = { objective: text.trim().length > 0 ? text : '(no content)' };
|
|
71
46
|
if (this.sessionId !== undefined)
|
|
72
47
|
req.sessionId = this.sessionId;
|
|
@@ -78,68 +53,23 @@ export class ControlRouter {
|
|
|
78
53
|
req.systemPrompt = this.systemPrompt;
|
|
79
54
|
return req;
|
|
80
55
|
}
|
|
81
|
-
// ── §2. steer — inject mid-flight direction (contract/04 §5.1; 08 IH-7, catalog L81) ─────────────────────
|
|
82
|
-
/**
|
|
83
|
-
* Steer the RUNNING run: `runs.steer(taskId, { text, mode? })`. Hard rules this method enforces:
|
|
84
|
-
*
|
|
85
|
-
* - **The text is untrusted DATA — sent RAW.** The shell does NOT pre-sanitize control markers; the SERVER
|
|
86
|
-
* fences the text (contract/04 §5.1). A text carrying a control-plane escape → 422
|
|
87
|
-
* `SteeringInvalidContentError` → surfaced as `ControlSafetyError('invalid_content')` (SURFACE the error;
|
|
88
|
-
* never strip-and-retry).
|
|
89
|
-
* - **`trusted` is NOT a client field** — the server derives trust from the authenticated operator role;
|
|
90
|
-
* this method has no way to assert it, by construction.
|
|
91
|
-
* - **AT-MOST-ONCE — never retried.** `runs.steer` is not idempotent and not a submit, so a transport
|
|
92
|
-
* failure propagates; the router does NOT auto-resend (a double-steer must be impossible).
|
|
93
|
-
* - **Steer needs a RUNNING run.** A suspended/terminal run → 409 `SteeringNotRunningError` → surfaced as
|
|
94
|
-
* `ControlSafetyError('not_running')` (resolve its gate via the hitlBridge `decide`, don't steer).
|
|
95
|
-
* 🔴 FIX③:整个 `steering.*` 族(7 员)都归到 `ControlSafetyError` 上,含 core 5.14.0 队列化
|
|
96
|
-
* 新出的 `queue_full` / `duplicate_input_id`,以及与 `not_running` **语义相反**的 `still_running`
|
|
97
|
-
* (两者永不合并);表外的未来码落开集位 `steering_other`,不再裸抛。
|
|
98
|
-
* - **`mode` drains at the TURN boundary** (FORWARD-DRAFT per-call mapping, runs.ts:49-50): `"all"` injects
|
|
99
|
-
* all queued steers at the next turn; `"one-at-a-time"` (default) one per turn, FIFO. Steer applies at
|
|
100
|
-
* the next turn boundary, not instantly.
|
|
101
|
-
*/
|
|
102
56
|
async steer(text, mode, opts) {
|
|
103
57
|
if (this.taskId === null) {
|
|
104
58
|
throw new ControlSafetyError('no run in flight to steer — submit a turn first', 'not_running');
|
|
105
59
|
}
|
|
106
|
-
// Raw text, no pre-sanitize (the server fences). `mode` omitted → server default one-at-a-time.
|
|
107
60
|
const body = mode !== undefined ? { text, mode } : { text };
|
|
108
61
|
try {
|
|
109
62
|
return await this.client.runs.steer(this.taskId, body, opts?.signal !== undefined ? { signal: opts.signal } : undefined);
|
|
110
63
|
}
|
|
111
64
|
catch (e) {
|
|
112
|
-
// FIX③:整族逐码分派(表见文件底部)。前缀在场即归族 —— 未来新码落 `steering_other`,
|
|
113
|
-
// 绝不再裸抛给壳(裸抛 = 壳的 `instanceof ControlSafetyError` 分支整条漏在外面)。
|
|
114
65
|
const verdict = steeringVerdict(e);
|
|
115
66
|
if (verdict !== undefined) {
|
|
116
67
|
const wireCode = errCodes(e).code ?? errCodes(e).name ?? 'steering.<unknown>';
|
|
117
68
|
throw new ControlSafetyError(`${wireCode} — ${STEERING_ADVICE.get(verdict) ?? 'steering refused'}`, verdict, e);
|
|
118
69
|
}
|
|
119
|
-
// Any other error (auth/transport): NOT retried (steer is at-most-once) — propagate to the shell.
|
|
120
70
|
throw e;
|
|
121
71
|
}
|
|
122
72
|
}
|
|
123
|
-
// ── §3. cancel — hard-stop / kill the run (contract/04 §5.3; 08 IH-8, catalog L84) ───────────────────────
|
|
124
|
-
/**
|
|
125
|
-
* Cancel (kill) the in-flight run: `runs.cancel(taskId)` → 202 `CancelAck` (`status:"cancelling"` or a
|
|
126
|
-
* terminal no-op). The run then SETTLES to `failed` + `errorCode:"cancelled"` — the UI shows "cancelled",
|
|
127
|
-
* NOT an error (the router does not synthesize that terminal; the downstream stream / `terminalToSdkResult`
|
|
128
|
-
* does). Branching (contract/04 §9.1):
|
|
129
|
-
*
|
|
130
|
-
* - **409 = a LOST CAS RACE, not "suspended".** 🔴 2026-07-31 更正:这段原本写的是「409 = run 是
|
|
131
|
-
* suspended,要改走 deny」—— 那是 **[868] 之前**的世界。服务端自 [868] 起对
|
|
132
|
-
* suspended/needs_review 的 run **就地取消**(先 CAS 结掉挂起的 checkpoint,再 setTerminal
|
|
133
|
-
* 释放 session claim),`cancel` 就是那种 run 的恢复把手,不再 409。今天的 409 只剩一种成因:
|
|
134
|
-
* 挂起的那个 gate 被并发决定/过期了(server `conflict.approval_settled`)。正确处置是**重读
|
|
135
|
-
* run 状态后重试 cancel**,而不是去 deny 一个已经不存在的审批 —— 照旧文指路只会指进空处。
|
|
136
|
-
* Surfaced as `ControlSafetyError('cancel_lost_race')`(干净切:旧码名 `cancel_suspended` 已
|
|
137
|
-
* 退役,本仓/壳/web/桌面均无行为消费方,只有一条注释引用,同批改)。
|
|
138
|
-
* - **404 = non-owner / unknown.** No existence oracle (404, not 403) → `ControlSafetyError('not_found')`.
|
|
139
|
-
*
|
|
140
|
-
* Server-idempotent and NOT a submit, so a successful cancel is safe to repeat (a terminal run no-ops);
|
|
141
|
-
* the router still does not auto-retry on transport failure (the shell re-invokes on the user's repeat).
|
|
142
|
-
*/
|
|
143
73
|
async cancel(opts) {
|
|
144
74
|
if (this.taskId === null) {
|
|
145
75
|
throw new ControlSafetyError('no run in flight to cancel', 'not_found');
|
|
@@ -148,7 +78,6 @@ export class ControlRouter {
|
|
|
148
78
|
return await this.client.runs.cancel(this.taskId, opts?.signal !== undefined ? { signal: opts.signal } : undefined);
|
|
149
79
|
}
|
|
150
80
|
catch (e) {
|
|
151
|
-
// 0.73.1:409 先按机器码分家(见 wireRefusalCopy.ts 头注);码缺席 / 认不出 ⇒ 落到下面那条旧臂,行为逐字同旧。
|
|
152
81
|
if (cancelConflictFromError(e)?.kind === 'run_not_running') {
|
|
153
82
|
throw new ControlSafetyError(`runs.cancel 409 conflict.run_not_running — ${cancelConflictContent({ kind: 'run_not_running' })}`, 'cancel_run_not_running', e);
|
|
154
83
|
}
|
|
@@ -162,21 +91,11 @@ export class ControlRouter {
|
|
|
162
91
|
throw e;
|
|
163
92
|
}
|
|
164
93
|
}
|
|
165
|
-
// ── §4. queued commands — client-side priority + drop (contract/04 §5.2, catalog L82) ────────────────────
|
|
166
|
-
//
|
|
167
|
-
// The SDK has no per-message priority and no drop-queued verb. The router holds the queue locally: enqueue
|
|
168
|
-
// by priority band, dequeue a still-queued turn by its local id, and drain by submitting each as its own
|
|
169
|
-
// run under the same session. A submitted turn LEAVES the queue — its only after-submit stop is `cancel`.
|
|
170
|
-
/** Enqueue a turn for later submission. `now` jumps the line, `next` (default) is FIFO, `later` trails.
|
|
171
|
-
* Returns the queued row (its `id` is the dequeue handle). Nothing leaves the machine until `drainNext`. */
|
|
172
94
|
enqueue(text, priority = 'next') {
|
|
173
95
|
const cmd = { id: this.nextId(), text, priority };
|
|
174
96
|
this.queue.push(cmd);
|
|
175
97
|
return cmd;
|
|
176
98
|
}
|
|
177
|
-
/** Drop a STILL-QUEUED turn by id BEFORE it is submitted (the only stop a queued message has —
|
|
178
|
-
* contract/04 §5.2). Returns true if it was in the queue, false if absent / already drained (mirrors CC's
|
|
179
|
-
* `cancel_async_message` → `{cancelled:false}` "not in the queue / already dequeued"). */
|
|
180
99
|
dequeue(id) {
|
|
181
100
|
const i = this.queue.findIndex((c) => c.id === id);
|
|
182
101
|
if (i < 0)
|
|
@@ -184,17 +103,9 @@ export class ControlRouter {
|
|
|
184
103
|
this.queue.splice(i, 1);
|
|
185
104
|
return true;
|
|
186
105
|
}
|
|
187
|
-
/** A stable, priority-banded view of the queue (the shell renders this; drain order matches it). */
|
|
188
106
|
pending() {
|
|
189
107
|
return [...this.queue].sort((a, b) => BAND_RANK[a.priority] - BAND_RANK[b.priority]);
|
|
190
108
|
}
|
|
191
|
-
/**
|
|
192
|
-
* Drain the highest-priority queued turn: dequeue it and `submit` it as its own durable run (same session
|
|
193
|
-
* → continues the conversation; same jobId → one Task). Returns the `RunReceipt`, or null when the queue is
|
|
194
|
-
* empty. The session serializes turns, so the caller drains one at a time (submit, await the run, drain the
|
|
195
|
-
* next); submitting while a prior run holds the session claim surfaces the SDK's 409 `ConflictError` from
|
|
196
|
-
* `submit` — the shell waits and re-drains (contract/04 §5.2; openapi:173).
|
|
197
|
-
*/
|
|
198
109
|
async drainNext(opts) {
|
|
199
110
|
const ordered = this.pending();
|
|
200
111
|
const head = ordered[0];
|
|
@@ -208,38 +119,16 @@ export class ControlRouter {
|
|
|
208
119
|
return `q-${this.seq}-${uuid()}`;
|
|
209
120
|
}
|
|
210
121
|
}
|
|
211
|
-
/**
|
|
212
|
-
* Read a normalized error code/status off any SDK error shape variant (errorCode / code / name / status).
|
|
213
|
-
* REF-CC-135(TYPESHAPE-15,P3 wave1 卡C-argv):判型单源此前返回 3 个裸 unknown(B2/B4 违规——调用方
|
|
214
|
-
* `code === 404`(status 才是数字位)这类拼写型 bug 能编译期全绿恒 false)。窄化成具名 `ErrCodes`,
|
|
215
|
-
* 非匹配类型一律降到 undefined(而不是把垃圾值原样放行)。
|
|
216
|
-
*
|
|
217
|
-
* 🔴 [2393] sweep-F6(2026-08-03 全窗复审)**这不只是类型收窄,是运行期丢值**(裁定:保留行为,
|
|
218
|
-
* 但把它记成可核的承诺):旧实现 `return { code: o.errorCode, name: o.name, status: o.status ?? o.statusCode }`
|
|
219
|
-
* 是原样透传。今天,包外消费者若拿到的错误对象上 `status` 是数字**串**(某些传输层/代理把 status
|
|
220
|
-
* 序列化成字符串)、或 `errorCode` 是数值码,读到的一律是 `undefined`。
|
|
221
|
-
* 方向对(「拿到一个不好用的值」→「诚实地什么都没有」;下游 `code === 404` 那类拼写型 bug 也因此
|
|
222
|
-
* 编译期就红),但它是**公面行为变更**:本函数是 npm 导出的判型单源,三端各自的错误对象形需要复核。
|
|
223
|
-
* ⇒ 已进 `docs/refactor/README.md` 的提货单/BREAKING 清单(此前只列四条 wire BREAKING,签名级与
|
|
224
|
-
* 行为级的这几条靠人工列表兜底,而列表没列)。
|
|
225
|
-
* 判据:pure 门 B5 段那组钉现在也钉「脏形一律降 undefined、绝不悄悄回落成 0/空串」——
|
|
226
|
-
* 谁要把某一种脏形改回透传,必须先在那里显形。
|
|
227
|
-
*/
|
|
228
122
|
export function errCodes(e) {
|
|
229
123
|
if (!e || typeof e !== 'object')
|
|
230
124
|
return {};
|
|
231
125
|
const o = e;
|
|
232
|
-
// [1912] errorCode 恒在(server ≥1.302 canonical)——`?? o.code` 老兜底已清;status 双键保留(HTTP 层形非 wire 键)。
|
|
233
126
|
const code = typeof o.errorCode === 'string' ? o.errorCode : undefined;
|
|
234
127
|
const name = typeof o.name === 'string' ? o.name : undefined;
|
|
235
128
|
const rawStatus = o.status ?? o.statusCode;
|
|
236
129
|
const status = typeof rawStatus === 'number' ? rawStatus : undefined;
|
|
237
130
|
return { code, name, status };
|
|
238
131
|
}
|
|
239
|
-
// ── steering 错误族的**逐码表**(FIX③,server 7.4.0 dist 亲扫 + SDK 6.3.0 errors.d.ts 对表)────
|
|
240
|
-
//
|
|
241
|
-
// 🔴 用 `Map` 不是对象字面量:键来自 wire 的 `errorCode` 字符串,对象字面量上 `map['__proto__']`
|
|
242
|
-
// 会摸到 Object.prototype(adapt/arms.ts 头注登记的同一条纪律)。
|
|
243
132
|
const STEERING_CODE_TO_SAFETY = new Map([
|
|
244
133
|
['steering.not_running', 'not_running'],
|
|
245
134
|
['steering.invalid_content', 'invalid_content'],
|
|
@@ -248,14 +137,8 @@ const STEERING_CODE_TO_SAFETY = new Map([
|
|
|
248
137
|
['steering.ambiguous_target', 'ambiguous_target'],
|
|
249
138
|
['steering.ambiguous_label', 'ambiguous_target'],
|
|
250
139
|
['steering.still_running', 'still_running'],
|
|
251
|
-
// [7226] 包侧缺口 ①(0.68.1):server 契约 (2) 表第 8 码(422)。SDK 8.8.0 **没有**专属子类
|
|
252
|
-
// (`errors.d.ts` 的 `Steering*Error` 只有六只)⇒ 只能按 `errorCode` 认;认不出时它会落开集位
|
|
253
|
-
// `steering_other`,而那一位的判词是「别猜它的意思」—— 恰好把一条「改内容重试即可」的拒绝
|
|
254
|
-
// 说成没救。所以码表必须点名它,不能靠前缀兜底。
|
|
255
140
|
['steering.blocked_by_hook', 'blocked_by_hook'],
|
|
256
141
|
]);
|
|
257
|
-
/** SDK 的 typed 子类名兜底(错误对象被传输层剥掉 `errorCode` 时仍认得族;`SteeringError` 基类本身
|
|
258
|
-
* = 「是 steering 族但没有专属子类」⇒ 落开集臂)。 */
|
|
259
142
|
const STEERING_NAME_TO_SAFETY = new Map([
|
|
260
143
|
['SteeringNotRunningError', 'not_running'],
|
|
261
144
|
['SteeringInvalidContentError', 'invalid_content'],
|
|
@@ -263,7 +146,6 @@ const STEERING_NAME_TO_SAFETY = new Map([
|
|
|
263
146
|
['SteeringStillRunningError', 'still_running'],
|
|
264
147
|
['SteeringError', 'steering_other'],
|
|
265
148
|
]);
|
|
266
|
-
/** 每个判别位的处置指路(壳把它原样打给用户/日志 —— 判词必须说得出「现在该做什么」)。 */
|
|
267
149
|
const STEERING_ADVICE = new Map([
|
|
268
150
|
['not_running', 'the run is suspended/terminal/on another replica — resolve its gate via decide, do not steer'],
|
|
269
151
|
['invalid_content', 'the steer text carried a control-plane escape; surface it, do not strip-and-retry'],
|
|
@@ -274,29 +156,19 @@ const STEERING_ADVICE = new Map([
|
|
|
274
156
|
['blocked_by_hook', "this deployment's userPromptSubmit gate refused the input (block, timeout and crash all report this one code, fail-closed) — the input was NOT accepted: no human_input frame, the inputId is not on the ledger, so editing the text and sending again is a normal retry, not a duplicate"],
|
|
275
157
|
['steering_other', 'an unrecognized steering.* refusal (open set) — the steer did NOT land; log the errorCode verbatim, do not guess its meaning'],
|
|
276
158
|
]);
|
|
277
|
-
/**
|
|
278
|
-
* 把一个 steer 抛出的错误判成 steering 族的哪一位。不是 steering 族 ⇒ `undefined`(调用方原样抛,
|
|
279
|
-
* 路由器绝不越界吞 auth/transport 的错)。
|
|
280
|
-
*/
|
|
281
159
|
function steeringVerdict(e) {
|
|
282
160
|
const { code, name } = errCodes(e);
|
|
283
161
|
if (typeof code === 'string' && code.startsWith('steering.')) {
|
|
284
|
-
// 前缀在场即归族:表里没有的**未来**码落开集臂,而不是漏出去(SDK 6.3.0 的同款前缀分派)。
|
|
285
162
|
return STEERING_CODE_TO_SAFETY.get(code) ?? 'steering_other';
|
|
286
163
|
}
|
|
287
164
|
if (typeof name === 'string')
|
|
288
165
|
return STEERING_NAME_TO_SAFETY.get(name);
|
|
289
166
|
return undefined;
|
|
290
167
|
}
|
|
291
|
-
/** A 409 on `cancel` = the pending gate was decided/expired concurrently (lost CAS race; server
|
|
292
|
-
* `conflict.approval_settled`). 🔴 它**不再**表示「run 是 suspended」——[868] 起 suspended/needs_review
|
|
293
|
-
* 的 run 由 cancel 就地终态化。
|
|
294
|
-
* The SDK raises a generic `ConflictError` (status 409) for this case — there is no dedicated subclass. */
|
|
295
168
|
function isCancelSuspendedConflict(e) {
|
|
296
169
|
const { name, status } = errCodes(e);
|
|
297
170
|
return name === 'ConflictError' || status === 409;
|
|
298
171
|
}
|
|
299
|
-
/** 404 — non-owner / unknown run; the SDK raises `NotFoundError` (no existence oracle, contract/04 §9.1). */
|
|
300
172
|
function isNotFound(e) {
|
|
301
173
|
const { name, status } = errCodes(e);
|
|
302
174
|
return name === 'NotFoundError' || status === 404;
|