@sema-agent/client-core 0.98.0 → 0.100.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 +376 -0
- package/README.md +61 -35
- package/dist/adapt/arms.js +120 -55
- package/dist/adapt/panelTasks.d.ts +3 -2
- package/dist/adapt/panelTasks.js +34 -11
- package/dist/adapt/textStream.d.ts +1 -1
- package/dist/adapt/textStream.js +51 -28
- package/dist/adapter/activeRunSelfHeal.d.ts +11 -3
- package/dist/adapter/activeRunSelfHeal.js +210 -294
- package/dist/adapter/downstream/eventToSdkMessage.d.ts +25 -6
- package/dist/adapter/downstream/eventToSdkMessage.js +176 -12
- package/dist/adapter/downstream/terminalToSdkResult.js +10 -0
- package/dist/adapter/downstream/wiringManifestView.d.ts +3 -1
- package/dist/adapter/downstream/wiringManifestView.js +1 -0
- package/dist/adapter/runStream.js +82 -40
- package/dist/adapter/types.d.ts +2 -0
- package/dist/adapter/types.js +2 -1
- package/dist/agentSession/engineSessionBackgroundStop.js +16 -8
- package/dist/agentsWireCaps.d.ts +7 -3
- package/dist/agentsWireCaps.js +133 -39
- package/dist/cloudConfigWireCaps.d.ts +10 -1
- package/dist/cloudConfigWireCaps.js +181 -65
- package/dist/compactionStatus.d.ts +34 -0
- package/dist/compactionStatus.js +196 -0
- package/dist/controlRouter.d.ts +4 -2
- package/dist/controlRouter.js +39 -51
- package/dist/coreValuePorts.d.ts +26 -1
- package/dist/coreValuePorts.js +10 -0
- package/dist/corruptSessionCopy.d.ts +2 -1
- package/dist/corruptSessionCopy.js +3 -3
- package/dist/decideFailureNote.js +3 -3
- package/dist/declared/supportFloorDeclared.js +1 -1
- package/dist/detachWire.js +3 -2
- package/dist/devicesFace.d.ts +34 -0
- package/dist/devicesFace.js +90 -0
- package/dist/displayUntrusted.d.ts +1 -0
- package/dist/displayUntrusted.js +153 -5
- package/dist/engineAgentPanelStore.d.ts +14 -4
- package/dist/engineAgentPanelStore.js +14 -0
- package/dist/engineCapReader.d.ts +2 -1
- package/dist/engineCapReader.js +7 -5
- package/dist/engineCapsCache.d.ts +8 -0
- package/dist/engineCapsCache.js +59 -6
- package/dist/engineCapsChange.d.ts +2 -0
- package/dist/engineCapsChange.js +9 -0
- package/dist/engineErrorCodes.d.ts +11 -2
- package/dist/engineErrorCodes.js +11 -12
- package/dist/engineRowRef.js +4 -0
- package/dist/engineSessionMessages.d.ts +1 -0
- package/dist/engineSessionMessages.js +7 -1
- package/dist/engineTargetSeat.d.ts +27 -0
- package/dist/engineTargetSeat.js +199 -0
- package/dist/engineToolDirectory.d.ts +3 -0
- package/dist/engineToolDirectory.js +4 -2
- package/dist/engineWireFor.js +3 -8
- package/dist/env/uiLanguage.d.ts +2 -1
- package/dist/env/uiLanguage.js +3 -2
- package/dist/eventLeg.d.ts +7 -5
- package/dist/eventLeg.js +68 -49
- package/dist/fileHistoryCaptureCapability.d.ts +2 -4
- package/dist/fileHistoryCaptureCapability.js +12 -22
- package/dist/fileHistoryLegNotice.d.ts +2 -1
- package/dist/fileHistoryLegNotice.js +3 -3
- package/dist/frameBinding.d.ts +3 -0
- package/dist/frameBinding.js +12 -0
- package/dist/gateOutcome.js +16 -2
- package/dist/gateVocabulary.d.ts +8 -7
- package/dist/gateVocabulary.js +71 -73
- package/dist/generated/engineFactTables.d.ts +18 -1
- package/dist/generated/engineFactTables.js +56 -0
- package/dist/generated/engineNoticeTables.js +12 -3
- package/dist/generated/settingsSchemaFns.d.ts +18 -0
- package/dist/generated/settingsSchemaFns.js +70 -0
- package/dist/generated/settingsSchemaHooksClassifier.d.ts +2 -0
- package/dist/generated/settingsSchemaHooksClassifier.js +254 -0
- package/dist/generated/settingsSchemaMcpRequestEntry.d.ts +1 -0
- package/dist/generated/settingsSchemaMcpRequestEntry.js +162 -0
- package/dist/generated/settingsSchemaTables.d.ts +2 -0
- package/dist/generated/settingsSchemaTables.js +9 -0
- package/dist/generated/settingsSchemaTypes.d.ts +133 -0
- package/dist/generated/settingsSchemaTypes.js +1 -0
- package/dist/hitl/allowRuleShadow.d.ts +2 -1
- package/dist/hitl/allowRuleShadow.js +18 -15
- package/dist/hitl/approvalCardReasons.d.ts +3 -2
- package/dist/hitl/approvalCardReasons.js +29 -30
- package/dist/hitl/approvalHolds.d.ts +17 -0
- package/dist/hitl/approvalHolds.js +114 -29
- package/dist/hitl/askClassPair.d.ts +2 -2
- package/dist/hitl/askClassPair.js +16 -0
- package/dist/hitl/askGateWire.js +2 -1
- package/dist/hitl/askSettlementLine.d.ts +14 -4
- package/dist/hitl/askSettlementLine.js +67 -47
- package/dist/hitl/attributionLadder.d.ts +23 -0
- package/dist/hitl/attributionLadder.js +67 -0
- package/dist/hitl/automaticAnswerVerdict.d.ts +2 -1
- package/dist/hitl/automaticAnswerVerdict.js +8 -8
- package/dist/hitl/crashConvergedNotice.d.ts +5 -4
- package/dist/hitl/crashConvergedNotice.js +28 -41
- package/dist/hitl/decisionLedger.d.ts +55 -0
- package/dist/hitl/decisionLedger.js +156 -0
- package/dist/hitl/decisionRefusal.d.ts +1 -0
- package/dist/hitl/decisionRefusal.js +1 -1
- package/dist/hitl/decisionSend.d.ts +113 -0
- package/dist/hitl/decisionSend.js +579 -0
- package/dist/hitl/frameRouter.js +7 -8
- package/dist/hitl/hitlBridge.d.ts +1 -0
- package/dist/hitl/hitlBridge.js +21 -9
- package/dist/hitl/persistedRulesWire.d.ts +6 -5
- package/dist/hitl/persistedRulesWire.js +55 -73
- package/dist/hitl/planReviewWire.d.ts +5 -0
- package/dist/hitl/planReviewWire.js +53 -76
- package/dist/hitl/sessionPolicyDeliverable.d.ts +2 -0
- package/dist/hitl/sessionPolicyDeliverable.js +49 -9
- package/dist/hitl/sessionPolicyWire.d.ts +2 -1
- package/dist/hitl/sessionPolicyWire.js +23 -24
- package/dist/hitl/suspendedReopen.d.ts +2 -1
- package/dist/hitl/suspendedReopen.js +7 -10
- package/dist/hitl/toolApprovalWire.d.ts +17 -5
- package/dist/hitl/toolApprovalWire.js +120 -147
- package/dist/hooksWireCaps.js +145 -56
- package/dist/host.d.ts +21 -0
- package/dist/hostTurnSettings.d.ts +110 -0
- package/dist/hostTurnSettings.js +692 -0
- package/dist/humanTurn.d.ts +15 -1
- package/dist/humanTurn.js +100 -12
- package/dist/humanTurnRead.d.ts +2 -0
- package/dist/humanTurnRead.js +22 -1
- package/dist/i18n/messages.en.d.ts +713 -0
- package/dist/i18n/messages.en.js +713 -0
- package/dist/i18n/messages.zh.js +854 -1
- package/dist/index.d.ts +45 -13
- package/dist/index.js +27 -9
- package/dist/inheritEnvCapability.d.ts +4 -2
- package/dist/inheritEnvCapability.js +12 -5
- package/dist/inheritEnvWire.d.ts +3 -2
- package/dist/inheritEnvWire.js +14 -16
- package/dist/injectionLaneNotice.d.ts +5 -0
- package/dist/injectionLaneNotice.js +46 -0
- package/dist/interactiveHalt.js +17 -6
- package/dist/liveModelCatalog.d.ts +2 -1
- package/dist/liveModelCatalog.js +14 -8
- package/dist/mcpPanel.d.ts +10 -1
- package/dist/mcpPanel.js +42 -13
- package/dist/mcpProbeCapability.d.ts +2 -1
- package/dist/mcpProbeCapability.js +7 -11
- package/dist/mcpProbeWire.d.ts +3 -1
- package/dist/mcpProbeWire.js +5 -0
- package/dist/mcpReconnect.js +2 -2
- package/dist/mcpToolToggles.d.ts +6 -0
- package/dist/mcpToolToggles.js +46 -0
- package/dist/mcpWireCaps.d.ts +43 -3
- package/dist/mcpWireCaps.js +194 -59
- package/dist/mcpWireTransport.d.ts +4 -0
- package/dist/mcpWireTransport.js +18 -0
- package/dist/memoryEntriesWire.js +5 -10
- package/dist/model/onboardJudgement.d.ts +93 -0
- package/dist/model/onboardJudgement.js +415 -0
- package/dist/model/tierVocabulary.d.ts +2 -0
- package/dist/model/tierVocabulary.js +17 -5
- package/dist/modelCapabilityProbe.d.ts +65 -0
- package/dist/modelCapabilityProbe.js +601 -0
- package/dist/modelIdentityResolvability.d.ts +5 -0
- package/dist/modelIdentityResolvability.js +49 -22
- package/dist/ownKey.d.ts +33 -0
- package/dist/ownKey.js +109 -0
- package/dist/panelReduce.d.ts +11 -1
- package/dist/panelReduce.js +95 -16
- package/dist/panelStateHolder.d.ts +1 -0
- package/dist/panelStateHolder.js +10 -1
- package/dist/permissionRuleIssue.d.ts +2 -1
- package/dist/permissionRuleIssue.js +23 -20
- package/dist/permissionRulesWriteCapability.d.ts +2 -1
- package/dist/permissionRulesWriteCapability.js +8 -6
- package/dist/pluginHooksWire.d.ts +46 -3
- package/dist/pluginHooksWire.js +220 -12
- package/dist/prepareRefusal.d.ts +8 -0
- package/dist/prepareRefusal.js +72 -0
- package/dist/printModelStatus.d.ts +63 -0
- package/dist/printModelStatus.js +155 -0
- package/dist/request/taskRequest.d.ts +16 -10
- package/dist/request/taskRequest.js +195 -58
- package/dist/resumeRefusalCopy.d.ts +14 -9
- package/dist/resumeRefusalCopy.js +89 -144
- package/dist/retryStatus.d.ts +55 -0
- package/dist/retryStatus.js +155 -19
- package/dist/ruleRefusalSentence.d.ts +4 -0
- package/dist/ruleRefusalSentence.js +58 -0
- package/dist/sandboxWire.d.ts +2 -1
- package/dist/sandboxWire.js +55 -21
- package/dist/scenarioRefusal.d.ts +10 -0
- package/dist/scenarioRefusal.js +111 -0
- package/dist/seam.d.ts +48 -9
- package/dist/seam.js +54 -5
- package/dist/seatContract.d.ts +272 -14
- package/dist/seatContract.js +711 -23
- package/dist/selfOrchestrationDenial.js +7 -19
- package/dist/sessionIdSegment.d.ts +6 -0
- package/dist/sessionIdSegment.js +24 -0
- package/dist/sessionMemoryStatus.js +6 -11
- package/dist/sessionPolicyCapability.d.ts +2 -1
- package/dist/sessionPolicyCapability.js +8 -6
- package/dist/settingsCredentialKeyPaths.d.ts +8 -0
- package/dist/settingsCredentialKeyPaths.js +219 -0
- package/dist/settingsHooksSalvage.d.ts +44 -0
- package/dist/settingsHooksSalvage.js +211 -0
- package/dist/skillToolListsCapability.d.ts +2 -1
- package/dist/skillToolListsCapability.js +8 -6
- package/dist/skillsWireCaps.d.ts +0 -3
- package/dist/skillsWireCaps.js +1 -2
- package/dist/sseIdleTriage.js +3 -2
- package/dist/subagent/engineSubagentResume.js +15 -9
- package/dist/subagentContentStore.d.ts +4 -0
- package/dist/subagentContentStore.js +157 -27
- package/dist/toolHistoryMismatch.js +3 -2
- package/dist/toolRoster.d.ts +3 -2
- package/dist/toolRoster.js +21 -20
- package/dist/toolsFlagNames.d.ts +8 -0
- package/dist/toolsFlagNames.js +110 -0
- package/dist/turnFailurePresentation.d.ts +12 -1
- package/dist/turnFailurePresentation.js +220 -15
- package/dist/usageAnalytics.js +6 -11
- package/dist/userAttachmentEnvelope.d.ts +1 -0
- package/dist/userAttachmentEnvelope.js +37 -7
- package/dist/userAttachmentSeat.d.ts +21 -0
- package/dist/userAttachmentSeat.js +75 -0
- package/dist/userInvokedSkillsCapability.d.ts +11 -1
- package/dist/userInvokedSkillsCapability.js +31 -7
- package/dist/wireErrorTriage.d.ts +7 -0
- package/dist/wireErrorTriage.js +23 -1
- package/dist/wireFailureShape.js +2 -1
- package/dist/wireRefusalCopy.d.ts +62 -16
- package/dist/wireRefusalCopy.js +307 -148
- package/dist/workflowsJournalCapability.d.ts +2 -1
- package/dist/workflowsJournalCapability.js +8 -6
- package/docs/INTEGRATION-CLIENTS.md +951 -42
- package/package.json +5 -5
package/dist/hitl/hitlBridge.js
CHANGED
|
@@ -7,8 +7,9 @@ import { ownDataField, readOwnDataValue } from '../ownKey.js';
|
|
|
7
7
|
import { PROTOCOL_ERROR_NAME, THROWN_SLOT_UNREADABLE, protocolErrorNameReading, readBlockedBy, readThrownSlot, thrownIsInstanceOf, wireFailureTextsOf } from '../wireFailureShape.js';
|
|
8
8
|
import { APPROVAL_BINDING_MISMATCH, CHECKPOINT_INVALID_OUTCOME, RESUME_POLICY_FOLD_CODE } from '../engineErrorCodes.js';
|
|
9
9
|
import { suspendedReopenOf } from './suspendedReopen.js';
|
|
10
|
+
import { closeDecisionOp, decisionLedgerKeysOf, openDecisionOp } from './decisionLedger.js';
|
|
10
11
|
export const DEFAULT_DENY_REASON = 'The user rejected this tool use';
|
|
11
|
-
const MAX_DENY_REASON_CHARS = 4096;
|
|
12
|
+
export const MAX_DENY_REASON_CHARS = 4096;
|
|
12
13
|
export function denyReasonForWire(reason, tag) {
|
|
13
14
|
if (typeof reason !== 'string')
|
|
14
15
|
return undefined;
|
|
@@ -146,13 +147,13 @@ function isTransientDecideTransportFailure(e, depth = 0) {
|
|
|
146
147
|
return false;
|
|
147
148
|
if (thrownIsInstanceOf(e, HitlSafetyError))
|
|
148
149
|
return false;
|
|
149
|
-
const status = readThrownSlot(e, 'status');
|
|
150
|
+
const status = readThrownSlot(e, 'status', 'data');
|
|
150
151
|
if (status === THROWN_SLOT_UNREADABLE || typeof status === 'number')
|
|
151
152
|
return false;
|
|
152
153
|
const name = readThrownSlot(e, 'name');
|
|
153
154
|
if (name === 'TimeoutError' || name === 'AbortError')
|
|
154
155
|
return true;
|
|
155
|
-
const code = readThrownSlot(e, 'code');
|
|
156
|
+
const code = readThrownSlot(e, 'code', 'data');
|
|
156
157
|
if (typeof code === 'string' && TRANSIENT_NETWORK_CODES.has(code))
|
|
157
158
|
return true;
|
|
158
159
|
const message = readThrownSlot(e, 'message');
|
|
@@ -172,6 +173,7 @@ class DecideSpan {
|
|
|
172
173
|
owner;
|
|
173
174
|
observer;
|
|
174
175
|
identity;
|
|
176
|
+
ledgerOp;
|
|
175
177
|
attempts = 0;
|
|
176
178
|
ok = false;
|
|
177
179
|
retryExhausted = false;
|
|
@@ -189,9 +191,11 @@ class DecideSpan {
|
|
|
189
191
|
return mine === undefined || (mine.sessionId === identity.sessionId && mine.boundCallId === identity.boundCallId);
|
|
190
192
|
}
|
|
191
193
|
async run(identity, send) {
|
|
192
|
-
if (this.
|
|
194
|
+
if (this.identity === undefined) {
|
|
193
195
|
this.identity = identity;
|
|
194
|
-
|
|
196
|
+
this.ledgerOp = openDecideLedgerOp(identity);
|
|
197
|
+
if (this.observer !== undefined)
|
|
198
|
+
notifyDecideObserver(this.observer, Object.freeze({ ...this.identity, phase: 'start' }));
|
|
195
199
|
}
|
|
196
200
|
this.inFlight++;
|
|
197
201
|
const tally = { attempts: 0 };
|
|
@@ -224,11 +228,16 @@ class DecideSpan {
|
|
|
224
228
|
if (this.ended)
|
|
225
229
|
return;
|
|
226
230
|
this.ended = true;
|
|
231
|
+
if (this.ledgerOp !== undefined)
|
|
232
|
+
closeDecisionOp(this.ledgerOp, 'package');
|
|
227
233
|
if (this.observer === undefined || this.identity === undefined)
|
|
228
234
|
return;
|
|
229
235
|
notifyDecideObserver(this.observer, Object.freeze({ ...this.identity, phase: 'end', ok: this.ok, attempts: this.attempts, retryExhausted: this.retryExhausted }));
|
|
230
236
|
}
|
|
231
237
|
}
|
|
238
|
+
function openDecideLedgerOp(identity) {
|
|
239
|
+
return openDecisionOp('tool_approval', decisionLedgerKeysOf({ taskId: identity.taskId, callId: identity.boundCallId }), 'posting', true);
|
|
240
|
+
}
|
|
232
241
|
function wireOptsOf(opts) {
|
|
233
242
|
if (opts === undefined || opts.operation === undefined)
|
|
234
243
|
return opts;
|
|
@@ -408,10 +417,10 @@ export class HitlBridge {
|
|
|
408
417
|
}
|
|
409
418
|
const observer = this.decideObserver;
|
|
410
419
|
const tally = { attempts: 0 };
|
|
411
|
-
if (observer === undefined)
|
|
412
|
-
return this.decideRawAttempts(sessionId, decision, opts, tally);
|
|
413
420
|
const identity = this.decideIdentityOf(sessionId, decision);
|
|
414
|
-
|
|
421
|
+
const ledgerOp = openDecideLedgerOp(identity);
|
|
422
|
+
if (observer !== undefined)
|
|
423
|
+
notifyDecideObserver(observer, Object.freeze({ ...identity, phase: 'start' }));
|
|
415
424
|
let ok = false;
|
|
416
425
|
let retryExhausted = false;
|
|
417
426
|
try {
|
|
@@ -424,7 +433,10 @@ export class HitlBridge {
|
|
|
424
433
|
throw e;
|
|
425
434
|
}
|
|
426
435
|
finally {
|
|
427
|
-
|
|
436
|
+
closeDecisionOp(ledgerOp, 'package');
|
|
437
|
+
if (observer !== undefined) {
|
|
438
|
+
notifyDecideObserver(observer, Object.freeze({ ...identity, phase: 'end', ok, attempts: tally.attempts, retryExhausted }));
|
|
439
|
+
}
|
|
428
440
|
}
|
|
429
441
|
}
|
|
430
442
|
decideIdentityOf(sessionId, decision) {
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { CcImportLayer, CcImportPrepareResult, CcImportRedeemResult, PersistedRule, RuleBehavior, RuleListParams, RuleListResult, RuleRevokeRequest, RemovalLiveness, RuleRevokeResult, RuleWriteRequest, RuleWriteResult } from '@sema-agent/sdk';
|
|
2
|
+
import type { UiLanguage } from '../env/uiLanguage.js';
|
|
2
3
|
export interface RulesFacade {
|
|
3
4
|
list(params?: RuleListParams, opts?: {
|
|
4
5
|
signal?: AbortSignal;
|
|
@@ -93,9 +94,9 @@ export interface PersistedRuleOriginsRow {
|
|
|
93
94
|
readonly origin?: string | undefined;
|
|
94
95
|
}[] | undefined;
|
|
95
96
|
}
|
|
96
|
-
export declare function persistedRuleAddOriginLabel(origin: string | undefined): string;
|
|
97
|
-
export declare function persistedRuleOriginsLabel(rule: PersistedRuleOriginsRow): string;
|
|
98
|
-
export declare function persistedRuleScopeLabel(scope: string | undefined): string;
|
|
97
|
+
export declare function persistedRuleAddOriginLabel(origin: string | undefined, lang?: UiLanguage): string;
|
|
98
|
+
export declare function persistedRuleOriginsLabel(rule: PersistedRuleOriginsRow, lang?: UiLanguage): string;
|
|
99
|
+
export declare function persistedRuleScopeLabel(scope: string | undefined, lang?: UiLanguage): string;
|
|
99
100
|
export type PersistedRuleRevokeOutcomeWord = 'removed' | 'nothing-written' | 'unknown-status';
|
|
100
101
|
export interface PersistedRuleRevokeResultBody {
|
|
101
102
|
readonly status?: string | undefined;
|
|
@@ -107,11 +108,11 @@ export interface PersistedRuleRevokeOutcome {
|
|
|
107
108
|
readonly notice: string;
|
|
108
109
|
readonly change: string;
|
|
109
110
|
}
|
|
110
|
-
export declare function persistedRuleRevokeOutcome(ruleText: string | undefined, result: PersistedRuleRevokeResultBody | undefined): PersistedRuleRevokeOutcome;
|
|
111
|
+
export declare function persistedRuleRevokeOutcome(ruleText: string | undefined, result: PersistedRuleRevokeResultBody | undefined, lang?: UiLanguage): PersistedRuleRevokeOutcome;
|
|
111
112
|
export interface PersistedRuleMatchRow {
|
|
112
113
|
readonly match?: string | undefined;
|
|
113
114
|
}
|
|
114
|
-
export declare function persistedRuleMatchLabel(ruleOrMatch: string | PersistedRuleMatchRow | undefined): string;
|
|
115
|
+
export declare function persistedRuleMatchLabel(ruleOrMatch: string | PersistedRuleMatchRow | undefined, lang?: UiLanguage): string;
|
|
115
116
|
export declare function revokeTargetFromPersistedRule(rule: unknown, opts?: {
|
|
116
117
|
principal?: string;
|
|
117
118
|
}): RuleRevokeRequest | undefined;
|
|
@@ -4,6 +4,7 @@ import { CORE_RULE_BEHAVIORS } from '../generated/engineFactTables.js';
|
|
|
4
4
|
import { wireFailureShapeOf } from '../wireFailureShape.js';
|
|
5
5
|
import { CAPABILITY_OPERATOR_PRINCIPALS_REQUIRED } from '../engineErrorCodes.js';
|
|
6
6
|
import { hostLog } from '../host.js';
|
|
7
|
+
import { msg, renderMessage } from '../i18n/translate.js';
|
|
7
8
|
import { guardedRead, readOwnDataValue } from '../ownKey.js';
|
|
8
9
|
import { observedPermissionRulesWrite, permissionRulesWriteAvailable } from '../permissionRulesWriteCapability.js';
|
|
9
10
|
import { isToolApprovalRespondReplayed, readToolApprovalRespondAck } from './toolApprovalWire.js';
|
|
@@ -114,123 +115,104 @@ export const PERSISTED_RULE_BEHAVIOR_UNKNOWN = '(unknown)';
|
|
|
114
115
|
export function persistedRuleBehaviorLabel(rule) {
|
|
115
116
|
return rowStr(rule, 'behavior') ?? PERSISTED_RULE_BEHAVIOR_UNKNOWN;
|
|
116
117
|
}
|
|
117
|
-
const
|
|
118
|
-
user: '
|
|
119
|
-
'imported-cc': '
|
|
120
|
-
starter: '
|
|
118
|
+
const PERSISTED_RULE_ORIGIN_KEYS = Object.freeze({
|
|
119
|
+
user: 'rules.origin.user',
|
|
120
|
+
'imported-cc': 'rules.origin.importedCc',
|
|
121
|
+
starter: 'rules.origin.starter',
|
|
121
122
|
});
|
|
122
|
-
const
|
|
123
|
-
const
|
|
123
|
+
const UNKNOWN_ORIGIN_KEY = 'rules.origin.unknownWord';
|
|
124
|
+
const NO_ORIGIN_KEY = 'rules.origin.none';
|
|
125
|
+
function originKeyOf(origin) {
|
|
126
|
+
return typeof origin === 'string' && Object.hasOwn(PERSISTED_RULE_ORIGIN_KEYS, origin) ? PERSISTED_RULE_ORIGIN_KEYS[origin] : UNKNOWN_ORIGIN_KEY;
|
|
127
|
+
}
|
|
124
128
|
function ownSlot(o, key) {
|
|
125
129
|
if (typeof o !== 'object' || o === null)
|
|
126
130
|
return undefined;
|
|
127
131
|
return guardedRead(() => readOwnDataValue(o, key));
|
|
128
132
|
}
|
|
129
133
|
const ORIGINS_READ_MAX = 256;
|
|
130
|
-
export function persistedRuleAddOriginLabel(origin) {
|
|
131
|
-
return
|
|
132
|
-
? PERSISTED_RULE_ORIGIN_PHRASES[origin]
|
|
133
|
-
: UNKNOWN_ORIGIN_PHRASE;
|
|
134
|
+
export function persistedRuleAddOriginLabel(origin, lang) {
|
|
135
|
+
return renderMessage(msg(originKeyOf(origin)), lang);
|
|
134
136
|
}
|
|
135
|
-
export function persistedRuleOriginsLabel(rule) {
|
|
137
|
+
export function persistedRuleOriginsLabel(rule, lang) {
|
|
136
138
|
const adds = ownSlot(rule, 'adds');
|
|
137
139
|
const isArr = guardedRead(() => (Array.isArray(adds) ? true : undefined));
|
|
138
140
|
if (isArr !== true)
|
|
139
|
-
return
|
|
141
|
+
return renderMessage(msg(NO_ORIGIN_KEY), lang);
|
|
140
142
|
const lenRaw = ownSlot(adds, 'length');
|
|
141
143
|
const len = typeof lenRaw === 'number' && Number.isSafeInteger(lenRaw) && lenRaw > 0 ? Math.min(lenRaw, ORIGINS_READ_MAX) : 0;
|
|
142
144
|
const seen = [];
|
|
143
145
|
for (let i = 0; i < len; i++) {
|
|
144
146
|
const origin = ownSlot(ownSlot(adds, String(i)), 'origin');
|
|
145
|
-
const
|
|
146
|
-
if (!seen.includes(
|
|
147
|
-
seen.push(
|
|
147
|
+
const key = originKeyOf(typeof origin === 'string' ? origin : undefined);
|
|
148
|
+
if (!seen.includes(key))
|
|
149
|
+
seen.push(key);
|
|
148
150
|
}
|
|
149
|
-
return seen.length === 0 ?
|
|
151
|
+
return seen.length === 0 ? renderMessage(msg(NO_ORIGIN_KEY), lang) : seen.map((k) => renderMessage(msg(k), lang)).join(renderMessage(msg('rules.origin.join'), lang));
|
|
150
152
|
}
|
|
151
|
-
const
|
|
152
|
-
export function persistedRuleScopeLabel(scope) {
|
|
153
|
+
const UNKNOWN_SCOPE_KEY = 'rules.scope.unknown';
|
|
154
|
+
export function persistedRuleScopeLabel(scope, lang) {
|
|
153
155
|
if (scope === 'global')
|
|
154
|
-
return 'all
|
|
156
|
+
return renderMessage(msg('rules.scope.all'), lang);
|
|
155
157
|
if (typeof scope === 'string' && scope.startsWith('project:') && scope.length > 'project:'.length) {
|
|
156
158
|
const shown = displayUntrusted(scope.slice('project:'.length), { max: 400, blanks: true });
|
|
157
|
-
return shown === '' ?
|
|
159
|
+
return shown === '' ? renderMessage(msg(UNKNOWN_SCOPE_KEY), lang) : shown;
|
|
158
160
|
}
|
|
159
|
-
return
|
|
161
|
+
return renderMessage(msg(UNKNOWN_SCOPE_KEY), lang);
|
|
160
162
|
}
|
|
161
|
-
const
|
|
163
|
+
const PERSISTED_RULE_REVOKE_KEYS = Object.freeze({
|
|
162
164
|
removed: {
|
|
163
|
-
yes: {
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
},
|
|
167
|
-
unknown: {
|
|
168
|
-
notice: '{rule}: a tombstone was written, but whether the rule is still live could not be confirmed — treat it as still in effect and check the list.',
|
|
169
|
-
change: 'Revoked saved rule {rule} (liveness could not be confirmed — treated as still in effect)',
|
|
170
|
-
},
|
|
171
|
-
no: { notice: 'Removed {rule}.', change: 'Revoked saved rule {rule}' },
|
|
165
|
+
yes: { notice: 'rules.revoke.removed.yes.notice', change: 'rules.revoke.removed.yes.change' },
|
|
166
|
+
unknown: { notice: 'rules.revoke.removed.unknown.notice', change: 'rules.revoke.removed.unknown.change' },
|
|
167
|
+
no: { notice: 'rules.revoke.removed.no.notice', change: 'rules.revoke.removed.no.change' },
|
|
172
168
|
},
|
|
173
169
|
'nothing-written': {
|
|
174
|
-
yes: {
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
},
|
|
178
|
-
unknown: {
|
|
179
|
-
notice: '{rule}: nothing was written, and whether the rule is still live could not be confirmed — treat it as still in effect and check the list.',
|
|
180
|
-
change: 'Removing saved rule {rule} wrote nothing — liveness could not be confirmed',
|
|
181
|
-
},
|
|
182
|
-
no: { notice: '{rule}: nothing to remove.', change: 'Removing saved rule {rule} wrote nothing (nothing to remove)' },
|
|
170
|
+
yes: { notice: 'rules.revoke.nothingWritten.yes.notice', change: 'rules.revoke.nothingWritten.yes.change' },
|
|
171
|
+
unknown: { notice: 'rules.revoke.nothingWritten.unknown.notice', change: 'rules.revoke.nothingWritten.unknown.change' },
|
|
172
|
+
no: { notice: 'rules.revoke.nothingWritten.no.notice', change: 'rules.revoke.nothingWritten.no.change' },
|
|
183
173
|
},
|
|
184
174
|
});
|
|
185
|
-
const
|
|
186
|
-
absent: {
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
},
|
|
190
|
-
unrecognised: {
|
|
191
|
-
notice: '{rule}: the engine answered with a status this build does not know, so whether anything was removed is unclear — check the list.',
|
|
192
|
-
change: 'Removing saved rule {rule} returned a status this build does not know',
|
|
193
|
-
},
|
|
194
|
-
liveness: { yes: ' It also reports the rule is still live.', unknown: ' Whether the rule is still live could not be confirmed either.', no: '' },
|
|
175
|
+
const REVOKE_UNKNOWN_STATUS_KEYS = Object.freeze({
|
|
176
|
+
absent: { notice: 'rules.revoke.absent.notice', change: 'rules.revoke.absent.change' },
|
|
177
|
+
unrecognised: { notice: 'rules.revoke.unrecognised.notice', change: 'rules.revoke.unrecognised.change' },
|
|
178
|
+
liveness: { yes: 'rules.revoke.liveness.yes', unknown: 'rules.revoke.liveness.unknown', no: undefined },
|
|
195
179
|
});
|
|
196
|
-
|
|
197
|
-
export function persistedRuleRevokeOutcome(ruleText, result) {
|
|
180
|
+
export function persistedRuleRevokeOutcome(ruleText, result, lang) {
|
|
198
181
|
const quoted = typeof ruleText === 'string' ? quoteUntrustedForSentence(ruleText, 120) : undefined;
|
|
199
|
-
const rule = quoted ?? '
|
|
182
|
+
const rule = quoted ?? msg('rules.revoke.thisRule');
|
|
200
183
|
const status = ownSlot(result, 'status');
|
|
201
184
|
const rawLive = ownSlot(result, 'stillLive');
|
|
202
185
|
const liveness = rawLive === 'yes' || rawLive === 'no' || rawLive === 'unknown' ? rawLive : 'unknown';
|
|
203
186
|
if (status === 'removed' || status === 'no-op') {
|
|
204
187
|
const outcome = status === 'removed' ? 'removed' : 'nothing-written';
|
|
205
|
-
const line =
|
|
206
|
-
return Object.freeze({ outcome, liveness, notice:
|
|
188
|
+
const line = PERSISTED_RULE_REVOKE_KEYS[outcome][liveness];
|
|
189
|
+
return Object.freeze({ outcome, liveness, notice: renderMessage(msg(line.notice, { rule }), lang), change: renderMessage(msg(line.change, { rule }), lang) });
|
|
207
190
|
}
|
|
208
|
-
const head = typeof status === 'string' && status !== '' ?
|
|
191
|
+
const head = typeof status === 'string' && status !== '' ? REVOKE_UNKNOWN_STATUS_KEYS.unrecognised : REVOKE_UNKNOWN_STATUS_KEYS.absent;
|
|
192
|
+
const tailKey = REVOKE_UNKNOWN_STATUS_KEYS.liveness[liveness];
|
|
209
193
|
return Object.freeze({
|
|
210
194
|
outcome: 'unknown-status',
|
|
211
195
|
liveness,
|
|
212
|
-
notice: `${
|
|
213
|
-
change:
|
|
196
|
+
notice: `${renderMessage(msg(head.notice, { rule }), lang)}${tailKey === undefined ? '' : renderMessage(msg(tailKey), lang)}`,
|
|
197
|
+
change: renderMessage(msg(head.change, { rule }), lang),
|
|
214
198
|
});
|
|
215
199
|
}
|
|
216
|
-
const
|
|
217
|
-
exact: 'exact
|
|
218
|
-
prefix: 'prefix
|
|
219
|
-
wildcard: 'wildcard
|
|
220
|
-
subpath: 'subpath
|
|
221
|
-
path: 'path
|
|
222
|
-
name: '
|
|
223
|
-
name_prefix: '
|
|
224
|
-
host: '
|
|
225
|
-
subdomain: '
|
|
200
|
+
const PERSISTED_RULE_MATCH_KEYS = Object.freeze({
|
|
201
|
+
exact: 'rules.match.exact',
|
|
202
|
+
prefix: 'rules.match.prefix',
|
|
203
|
+
wildcard: 'rules.match.wildcard',
|
|
204
|
+
subpath: 'rules.match.subpath',
|
|
205
|
+
path: 'rules.match.path',
|
|
206
|
+
name: 'rules.match.name',
|
|
207
|
+
name_prefix: 'rules.match.namePrefix',
|
|
208
|
+
host: 'rules.match.host',
|
|
209
|
+
subdomain: 'rules.match.subdomain',
|
|
226
210
|
});
|
|
227
|
-
export function persistedRuleMatchLabel(ruleOrMatch) {
|
|
211
|
+
export function persistedRuleMatchLabel(ruleOrMatch, lang) {
|
|
228
212
|
const word = typeof ruleOrMatch === 'string' ? ruleOrMatch : ownSlot(ruleOrMatch, 'match');
|
|
229
213
|
if (typeof word !== 'string' || word === '')
|
|
230
|
-
return '
|
|
231
|
-
return Object.hasOwn(
|
|
232
|
-
? PERSISTED_RULE_MATCH_LABELS[word]
|
|
233
|
-
: 'a match form this build does not know';
|
|
214
|
+
return renderMessage(msg('rules.match.unreported'), lang);
|
|
215
|
+
return renderMessage(msg(Object.hasOwn(PERSISTED_RULE_MATCH_KEYS, word) ? PERSISTED_RULE_MATCH_KEYS[word] : 'rules.match.unknown'), lang);
|
|
234
216
|
}
|
|
235
217
|
export function revokeTargetFromPersistedRule(rule, opts) {
|
|
236
218
|
const behavior = persistedRuleBehaviorOf(rule);
|
|
@@ -37,6 +37,11 @@ export interface PlanReviewCardLike {
|
|
|
37
37
|
}>;
|
|
38
38
|
}>;
|
|
39
39
|
}
|
|
40
|
+
export interface PlanReviewQuestionIdReading {
|
|
41
|
+
readonly taskId: string;
|
|
42
|
+
readonly reopened: boolean;
|
|
43
|
+
}
|
|
44
|
+
export declare function planReviewTaskIdFromQuestionId(questionId: {} | null | undefined): PlanReviewQuestionIdReading | undefined;
|
|
40
45
|
export declare function planReviewOfferedModeOf(card: string | PlanReviewCardLike): PlanReviewOfferedMode;
|
|
41
46
|
export interface ArmPlanReviewOptions {
|
|
42
47
|
submittedInPlanMode?: boolean;
|
|
@@ -11,6 +11,7 @@ import { readOwnDataNonEmptyString, readOwnDataValue } from '../ownKey.js';
|
|
|
11
11
|
import { checkpointUnsupportedVersionContent, checkpointUnsupportedVersionFromError, resumeContextUnavailableContent, resumeContextUnavailableFromError } from '../resumeRefusalCopy.js';
|
|
12
12
|
import { REQUEST_FIELD_CONFLICT } from '../engineErrorCodes.js';
|
|
13
13
|
import { refusalDeliveryOf } from './decisionRefusal.js';
|
|
14
|
+
import { __resetDecisionLedgerForTests, decisionInFlightOf, decisionKeyInFlight, decisionLedgerKeysOf, decisionOpsWithKey, decisionReadMark, dropDecisionOp, latestDecisionHandedMark, markLatestDecisionNotApplied, openDecisionOp, planDecisionKey, readMarkOf, settleDecisionOp, settleDecisionOpsOnGateLeft, } from './decisionLedger.js';
|
|
14
15
|
import { denyReasonForWire, isPlanReviewModeAfter } from './hitlBridge.js';
|
|
15
16
|
import { planReviewQuestionId, REOPEN_ID_TAIL, taskIdFromPlanReviewQuestionId } from './gateIdentity.js';
|
|
16
17
|
import { notePlanReviewAnswered, notePlanReviewAnsweredFor, planReviewArmedKeyFor, waitForGateArmedFor, wasGateArmedFor, } from './armedGateRegistry.js';
|
|
@@ -300,6 +301,14 @@ function offeredModeFromLabels(card) {
|
|
|
300
301
|
return 'two_choice';
|
|
301
302
|
return undefined;
|
|
302
303
|
}
|
|
304
|
+
export function planReviewTaskIdFromQuestionId(questionId) {
|
|
305
|
+
if (typeof questionId !== 'string')
|
|
306
|
+
return undefined;
|
|
307
|
+
const taskId = taskIdFromPlanReviewQuestionId(questionId);
|
|
308
|
+
if (taskId === undefined)
|
|
309
|
+
return undefined;
|
|
310
|
+
return Object.freeze({ taskId, reopened: questionId.includes(REOPEN_ID_TAIL) });
|
|
311
|
+
}
|
|
303
312
|
function offeredModeFromQuestionId(questionId) {
|
|
304
313
|
if (taskIdFromPlanReviewQuestionId(questionId) === undefined)
|
|
305
314
|
return 'unknown';
|
|
@@ -449,42 +458,21 @@ function usablePlanReviewWireClient(client) {
|
|
|
449
458
|
return clientHasVerbs(client, [['assistant', 'planReview'], ['runs', 'get']]);
|
|
450
459
|
}
|
|
451
460
|
const PLAN_REVIEW_INJECTED_REPULL_TIMEOUT_MS = 15_000;
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
let planReviewLedgerClock = 0;
|
|
455
|
-
let decideDispatchSeq = 0;
|
|
456
|
-
function planReviewOpsOf(taskId) {
|
|
457
|
-
return planReviewDecisionOps.filter((op) => op.taskId === taskId);
|
|
458
|
-
}
|
|
459
|
-
function settlePlanReviewDecisionOp(op, by) {
|
|
460
|
-
if (op.settledAt !== null)
|
|
461
|
-
return;
|
|
462
|
-
op.settledAt = ++planReviewLedgerClock;
|
|
463
|
-
op.settler = by;
|
|
464
|
-
}
|
|
465
|
-
function pushPlanReviewDecisionOp(op) {
|
|
466
|
-
planReviewDecisionOps.push(op);
|
|
467
|
-
while (planReviewDecisionOps.length > MAX_PLAN_REVIEW_DECISION_OPS) {
|
|
468
|
-
let victim = planReviewDecisionOps.findIndex((o) => o.settledAt !== null && !o.ownCallOpen);
|
|
469
|
-
if (victim < 0)
|
|
470
|
-
victim = planReviewDecisionOps.findIndex((o) => o !== op && o.phase === 'handed');
|
|
471
|
-
if (victim < 0)
|
|
472
|
-
break;
|
|
473
|
-
planReviewDecisionOps.splice(victim, 1);
|
|
474
|
-
}
|
|
461
|
+
function planReviewLedgerKeys(taskId) {
|
|
462
|
+
return taskId === '' ? [planDecisionKey('')] : decisionLedgerKeysOf({ taskId, planTaskId: taskId });
|
|
475
463
|
}
|
|
476
|
-
function
|
|
477
|
-
|
|
478
|
-
if (i >= 0)
|
|
479
|
-
planReviewDecisionOps.splice(i, 1);
|
|
464
|
+
function planReviewOpsOf(taskId) {
|
|
465
|
+
return decisionOpsWithKey('plan_review', planDecisionKey(taskId));
|
|
480
466
|
}
|
|
467
|
+
let decideDispatchSeq = 0;
|
|
481
468
|
function claimablePlanReviewDecisionOp(taskId) {
|
|
482
|
-
const
|
|
469
|
+
const ops = planReviewOpsOf(taskId);
|
|
470
|
+
const open = ops.find((o) => o.phase === 'handed' && o.settledAt === null);
|
|
483
471
|
if (open !== undefined)
|
|
484
472
|
return open;
|
|
485
473
|
let leftBehind;
|
|
486
|
-
for (const o of
|
|
487
|
-
if (o.
|
|
474
|
+
for (const o of ops) {
|
|
475
|
+
if (o.phase !== 'handed' || o.settler !== 'gate_left')
|
|
488
476
|
continue;
|
|
489
477
|
if (leftBehind === undefined || o.handedAt > leftBehind.handedAt)
|
|
490
478
|
leftBehind = o;
|
|
@@ -494,62 +482,47 @@ function claimablePlanReviewDecisionOp(taskId) {
|
|
|
494
482
|
export function notePlanReviewDecisionHandedOver(taskId) {
|
|
495
483
|
if (typeof taskId !== 'string' || taskId === '')
|
|
496
484
|
return null;
|
|
497
|
-
if (
|
|
485
|
+
if (planReviewOpsOf(taskId).some((op) => op.settledAt === null))
|
|
498
486
|
return null;
|
|
499
|
-
const op =
|
|
500
|
-
pushPlanReviewDecisionOp(op);
|
|
487
|
+
const op = openDecisionOp('plan_review', planReviewLedgerKeys(taskId), 'handed', false);
|
|
501
488
|
return {
|
|
502
489
|
settle: () => {
|
|
503
490
|
if (op.phase !== 'handed')
|
|
504
491
|
return;
|
|
505
|
-
|
|
492
|
+
settleDecisionOp(op, 'host');
|
|
506
493
|
},
|
|
507
494
|
revoke: () => {
|
|
508
495
|
if (op.phase !== 'handed' || op.settledAt !== null)
|
|
509
496
|
return;
|
|
510
|
-
|
|
497
|
+
dropDecisionOp(op);
|
|
511
498
|
},
|
|
512
499
|
};
|
|
513
500
|
}
|
|
514
501
|
export function planReviewPendingReadMark() {
|
|
515
|
-
return
|
|
502
|
+
return decisionReadMark();
|
|
516
503
|
}
|
|
517
504
|
export function planReviewDecisionInFlight(taskId, pendingReadStartedAt) {
|
|
518
505
|
if (typeof taskId !== 'string' || taskId === '')
|
|
519
506
|
return false;
|
|
520
|
-
|
|
521
|
-
return planReviewDecisionOps.some((op) => op.taskId === taskId && (op.settledAt === null || (mark !== undefined && op.settledAt > mark && !op.notApplied)));
|
|
507
|
+
return decisionKeyInFlight(planDecisionKey(taskId), readMarkOf(pendingReadStartedAt));
|
|
522
508
|
}
|
|
523
509
|
export function notePlanReviewDecisionNotApplied(taskId) {
|
|
524
510
|
if (typeof taskId !== 'string' || taskId === '')
|
|
525
511
|
return;
|
|
526
|
-
|
|
527
|
-
if (ops.some((op) => op.settledAt === null))
|
|
528
|
-
return;
|
|
529
|
-
let latest;
|
|
530
|
-
for (const op of ops)
|
|
531
|
-
if (latest === undefined || op.handedAt > latest.handedAt)
|
|
532
|
-
latest = op;
|
|
533
|
-
if (latest !== undefined)
|
|
534
|
-
latest.notApplied = true;
|
|
512
|
+
markLatestDecisionNotApplied('plan_review', planDecisionKey(taskId));
|
|
535
513
|
}
|
|
536
514
|
export function notePlanReviewGateLeft(taskId) {
|
|
537
515
|
if (typeof taskId !== 'string' || taskId === '')
|
|
538
516
|
return;
|
|
539
|
-
|
|
540
|
-
settlePlanReviewDecisionOp(op, 'gate_left');
|
|
517
|
+
settleDecisionOpsOnGateLeft('plan_review', planDecisionKey(taskId));
|
|
541
518
|
}
|
|
542
519
|
export function latestPlanReviewDecisionMark(taskId) {
|
|
543
520
|
if (typeof taskId !== 'string' || taskId === '')
|
|
544
521
|
return null;
|
|
545
|
-
|
|
546
|
-
for (const op of planReviewOpsOf(taskId))
|
|
547
|
-
if (mark === null || op.handedAt > mark)
|
|
548
|
-
mark = op.handedAt;
|
|
549
|
-
return mark;
|
|
522
|
+
return latestDecisionHandedMark('plan_review', planDecisionKey(taskId));
|
|
550
523
|
}
|
|
551
524
|
export function __resetPlanReviewDecisionLatchForTests() {
|
|
552
|
-
|
|
525
|
+
__resetDecisionLedgerForTests('plan_review');
|
|
553
526
|
}
|
|
554
527
|
export async function decidePlanReview(taskId, decision, permissionModeAfter, opts) {
|
|
555
528
|
return decidePlanReviewFor(taskId, decision, permissionModeAfter, opts, parkedGateIdentityOf(opts));
|
|
@@ -601,14 +574,13 @@ function bodySaysReparked(body) {
|
|
|
601
574
|
}
|
|
602
575
|
}
|
|
603
576
|
async function decidePlanReviewFor(taskId, decision, permissionModeAfter, opts, gateBefore) {
|
|
604
|
-
if (typeof taskId === 'string' &&
|
|
577
|
+
if (typeof taskId === 'string' && planReviewOpsOf(taskId).some((op) => op.phase === 'posting')) {
|
|
605
578
|
hostLog('error', `planReviewWire: decidePlanReview(${decision}) for task ${taskId} REFUSED — a decision for this task is already in flight (duplicate delivery latched)`);
|
|
606
579
|
return;
|
|
607
580
|
}
|
|
608
|
-
let op = claimablePlanReviewDecisionOp(taskId);
|
|
581
|
+
let op = typeof taskId === 'string' ? claimablePlanReviewDecisionOp(taskId) : undefined;
|
|
609
582
|
if (op === undefined) {
|
|
610
|
-
op =
|
|
611
|
-
pushPlanReviewDecisionOp(op);
|
|
583
|
+
op = openDecisionOp('plan_review', planReviewLedgerKeys(typeof taskId === 'string' ? taskId : String(taskId)), 'posting', true);
|
|
612
584
|
}
|
|
613
585
|
else {
|
|
614
586
|
if (op.settler === 'gate_left') {
|
|
@@ -627,7 +599,7 @@ async function decidePlanReviewFor(taskId, decision, permissionModeAfter, opts,
|
|
|
627
599
|
const settle = (notApplied) => {
|
|
628
600
|
if (notApplied)
|
|
629
601
|
mine.notApplied = true;
|
|
630
|
-
|
|
602
|
+
settleDecisionOp(mine, 'package');
|
|
631
603
|
};
|
|
632
604
|
const dispatchNo = ++decideDispatchSeq;
|
|
633
605
|
hostLog('debug', `planReviewWire: decidePlanReview dispatch #${dispatchNo} task=${taskId} decision=${decision} mode=${permissionModeAfter ?? '-'}`);
|
|
@@ -636,7 +608,7 @@ async function decidePlanReviewFor(taskId, decision, permissionModeAfter, opts,
|
|
|
636
608
|
}
|
|
637
609
|
finally {
|
|
638
610
|
releaseLatch();
|
|
639
|
-
|
|
611
|
+
settleDecisionOp(mine, 'package');
|
|
640
612
|
mine.ownCallOpen = false;
|
|
641
613
|
}
|
|
642
614
|
}
|
|
@@ -904,10 +876,10 @@ function joinReopenFlight(flight, taskId, receiptMs) {
|
|
|
904
876
|
abortableSleep(Math.max(0, receiptMs), ac.signal).then(() => false),
|
|
905
877
|
]).then((presented) => {
|
|
906
878
|
ac.abort();
|
|
907
|
-
return presented ? { reopened: true, firstSight: false } :
|
|
879
|
+
return presented ? { reopened: true, firstSight: false } : refusedReopen('not_presented');
|
|
908
880
|
}, () => {
|
|
909
881
|
ac.abort();
|
|
910
|
-
return
|
|
882
|
+
return refusedReopen('not_presented');
|
|
911
883
|
});
|
|
912
884
|
}
|
|
913
885
|
function mintPlanReviewReopen(taskId, opts, sessionKey, receiptMs) {
|
|
@@ -947,10 +919,10 @@ function mintPlanReviewReopenCard(taskId, opts, sessionKey, receiptMs) {
|
|
|
947
919
|
const markBefore = dismissalEntryFor(sessionKey, taskId);
|
|
948
920
|
const settleOnReceipt = (receipt, firstSight) => receipt.then((presented) => {
|
|
949
921
|
if (!presented)
|
|
950
|
-
return
|
|
922
|
+
return refusedReopen('not_presented');
|
|
951
923
|
revokePlanReviewUserDismissal(sessionKey, taskId, markBefore);
|
|
952
924
|
return { reopened: true, firstSight };
|
|
953
|
-
}, () => (
|
|
925
|
+
}, () => refusedReopen('not_presented'));
|
|
954
926
|
const canonicalId = planReviewQuestionId(taskId);
|
|
955
927
|
const armedKey = planReviewArmedKeyFor(sessionKey, taskId);
|
|
956
928
|
const firstSight = !wasGateArmedFor(sessionKey, armedKey);
|
|
@@ -1009,6 +981,9 @@ function mintPlanReviewReopenCard(taskId, opts, sessionKey, receiptMs) {
|
|
|
1009
981
|
publishQuestionFrameFor(sessionKey, planReviewQuestionFrame(questionId, REOPEN_QUESTION, false, true));
|
|
1010
982
|
return settleOnReceipt(receipt, firstSight);
|
|
1011
983
|
}
|
|
984
|
+
function refusedReopen(cause) {
|
|
985
|
+
return { reopened: false, _sema_reopenRefusal: cause };
|
|
986
|
+
}
|
|
1012
987
|
function noPresentationSurfaceVerdict(taskId, sessionKey) {
|
|
1013
988
|
const verdict = { reopened: false, _sema_noPresentationSurface: true };
|
|
1014
989
|
hostLog('debug', `planReviewWire: reopen for task ${taskId} refused — nothing is subscribed to question frames for session ` +
|
|
@@ -1031,7 +1006,7 @@ async function reopenAfterDismissalCheck(taskId, opts, sessionKey, receiptMs, ma
|
|
|
1031
1006
|
}
|
|
1032
1007
|
}
|
|
1033
1008
|
}
|
|
1034
|
-
const ledgerSaysInFlight =
|
|
1009
|
+
const ledgerSaysInFlight = decisionInFlightOf({ gate: 'plan_review', taskId }, recheckReadStart);
|
|
1035
1010
|
if (ledgerSaysInFlight) {
|
|
1036
1011
|
hostLog('debug', `planReviewWire: reopen for task ${taskId} refused after the dismissal check — a decision for this task went out while the check was reading`);
|
|
1037
1012
|
return { reopened: false, _sema_decisionInFlight: true };
|
|
@@ -1080,24 +1055,26 @@ async function staleDismissalVerdict(current, sessionKey, taskId, receiptMs) {
|
|
|
1080
1055
|
const windowEnd = abortableSleep(Math.max(0, receiptMs), ac.signal).then(() => false);
|
|
1081
1056
|
try {
|
|
1082
1057
|
let entry = current;
|
|
1058
|
+
let lastWaitPresented;
|
|
1083
1059
|
for (let round = 0; round < 8; round++) {
|
|
1084
1060
|
if (entry?.kind === 'decided')
|
|
1085
1061
|
return { reopened: false, _sema_decisionInFlight: true };
|
|
1086
1062
|
if (entry?.kind === 'dismissed')
|
|
1087
1063
|
return { reopened: false, dismissedByUser: true };
|
|
1088
1064
|
if (entry?.kind !== 'reopened' || entry.onScreen === undefined)
|
|
1089
|
-
return
|
|
1065
|
+
return refusedReopen(entry === undefined && lastWaitPresented === false ? 'not_presented' : 'gate_record_gone');
|
|
1090
1066
|
const onScreen = await Promise.race([
|
|
1091
1067
|
entry.onScreen.then((v) => v === true, () => false),
|
|
1092
1068
|
windowEnd,
|
|
1093
1069
|
]);
|
|
1094
1070
|
const now = dismissalEntryFor(sessionKey, taskId);
|
|
1095
1071
|
if (now === entry) {
|
|
1096
|
-
return onScreen ? { reopened: true, firstSight: !wasGateArmedFor(sessionKey, planReviewArmedKeyFor(sessionKey, taskId)) } :
|
|
1072
|
+
return onScreen ? { reopened: true, firstSight: !wasGateArmedFor(sessionKey, planReviewArmedKeyFor(sessionKey, taskId)) } : refusedReopen('not_presented');
|
|
1097
1073
|
}
|
|
1074
|
+
lastWaitPresented = onScreen;
|
|
1098
1075
|
entry = now;
|
|
1099
1076
|
}
|
|
1100
|
-
return
|
|
1077
|
+
return refusedReopen('gate_record_gone');
|
|
1101
1078
|
}
|
|
1102
1079
|
finally {
|
|
1103
1080
|
ac.abort();
|
|
@@ -1107,25 +1084,25 @@ export function reopenPlanReviewCard(taskId, opts) {
|
|
|
1107
1084
|
const receiptMs = opts?.presentationReceiptMs;
|
|
1108
1085
|
if (typeof receiptMs !== 'number' || Number.isNaN(receiptMs)) {
|
|
1109
1086
|
hostLog('debug', `planReviewWire: reopen for task ${String(taskId)} refused — presentationReceiptMs is required (a number of milliseconds); the synchronous form was retired in 0.98.0`);
|
|
1110
|
-
return Promise.resolve(
|
|
1087
|
+
return Promise.resolve(refusedReopen('invalid_call'));
|
|
1111
1088
|
}
|
|
1112
1089
|
try {
|
|
1113
1090
|
if (typeof taskId !== 'string' || taskId.length === 0) {
|
|
1114
|
-
return Promise.resolve(
|
|
1091
|
+
return Promise.resolve(refusedReopen('invalid_call'));
|
|
1115
1092
|
}
|
|
1116
1093
|
const injected = opts;
|
|
1117
1094
|
if (opts?.mintFreshQuestionId === false && (injected?.deliverDecision !== undefined || injected?.wire !== undefined || injected?.onOutcome !== undefined)) {
|
|
1118
1095
|
hostLog('error', `planReviewWire: plan_review reopen for task ${taskId} refused — mintFreshQuestionId:false reuses the armed card's responder and its delivery, ` +
|
|
1119
1096
|
'so deliverDecision / wire / onOutcome cannot be given with it; drop mintFreshQuestionId:false to deliver through the injected path');
|
|
1120
|
-
return Promise.resolve(
|
|
1097
|
+
return Promise.resolve(refusedReopen('invalid_call'));
|
|
1121
1098
|
}
|
|
1122
1099
|
const readStart = typeof opts.pendingReadStartedAt === 'number' && Number.isFinite(opts.pendingReadStartedAt) ? opts.pendingReadStartedAt : undefined;
|
|
1123
|
-
if (
|
|
1100
|
+
if (decisionInFlightOf({ gate: 'plan_review', taskId }, readStart)) {
|
|
1124
1101
|
hostLog('debug', `planReviewWire: reopen for task ${taskId} refused — a decision for this task is in flight (its answer is still being delivered)`);
|
|
1125
1102
|
const refused = { reopened: false, _sema_decisionInFlight: true };
|
|
1126
1103
|
return Promise.resolve(refused);
|
|
1127
1104
|
}
|
|
1128
|
-
const guardStartedAt =
|
|
1105
|
+
const guardStartedAt = decisionReadMark();
|
|
1129
1106
|
const recheckReadStart = readStart === undefined ? guardStartedAt : Math.min(readStart, guardStartedAt);
|
|
1130
1107
|
const sessionKey = opts.sessionKey ?? DEFAULT_SESSION_KEY;
|
|
1131
1108
|
if (!hasQuestionOverlayFor(sessionKey)) {
|
|
@@ -1136,7 +1113,7 @@ export function reopenPlanReviewCard(taskId, opts) {
|
|
|
1136
1113
|
'而缺省决断投递口(decidePlanReview)今天固定打默认槽的 baseUrl/token/principal。' +
|
|
1137
1114
|
'整条链转 keyed 之前,非默认槽要重开请自带 deliverDecision(按本槽投递);' +
|
|
1138
1115
|
'此处拒绝而不是照发 —— 照发会把这条会话的决断发到另一个槽上。');
|
|
1139
|
-
return Promise.resolve(
|
|
1116
|
+
return Promise.resolve(refusedReopen('no_delivery_path'));
|
|
1140
1117
|
}
|
|
1141
1118
|
const trigger = opts.trigger === undefined ? undefined : opts.trigger === 'user' ? 'user' : 'automatic';
|
|
1142
1119
|
if (trigger === 'automatic') {
|
|
@@ -1147,6 +1124,6 @@ export function reopenPlanReviewCard(taskId, opts) {
|
|
|
1147
1124
|
return mintPlanReviewReopen(taskId, opts, sessionKey, receiptMs);
|
|
1148
1125
|
}
|
|
1149
1126
|
catch {
|
|
1150
|
-
return Promise.resolve(
|
|
1127
|
+
return Promise.resolve(refusedReopen('internal_error'));
|
|
1151
1128
|
}
|
|
1152
1129
|
}
|
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
import type { SessionPermissionRules } from '@sema-agent/sdk';
|
|
2
2
|
import type { ToolRosterView } from '../toolRoster.js';
|
|
3
|
+
import type { SessionRuleInertReason } from './sessionPolicyWire.js';
|
|
3
4
|
export type SessionPolicyWithheldWhy = 'ask_no_bucket' | 'qualified_deny' | 'peer_wide' | 'wildcard' | 'not_a_tool_name' | 'legacy_tool_name' | 'not_in_roster';
|
|
4
5
|
export declare const SESSION_POLICY_WITHHELD_WHY: readonly SessionPolicyWithheldWhy[];
|
|
5
6
|
export interface SessionPolicyWithheldRule {
|
|
6
7
|
readonly rule: string;
|
|
7
8
|
readonly why: SessionPolicyWithheldWhy;
|
|
8
9
|
readonly currentName?: string;
|
|
10
|
+
readonly inertReason?: SessionRuleInertReason;
|
|
9
11
|
}
|
|
10
12
|
export interface SessionPolicyDeliverability {
|
|
11
13
|
readonly deliverable: SessionPermissionRules;
|