@sema-agent/client-core 0.112.1 → 0.113.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 +130 -0
- package/README.md +36 -23
- package/dist/adapt/arms.js +7 -1
- package/dist/adapt/textStream.d.ts +2 -0
- package/dist/adapt/textStream.js +27 -1
- package/dist/adapter/activeRunSelfHeal.d.ts +7 -5
- package/dist/adapter/activeRunSelfHeal.js +38 -66
- package/dist/adapter/downstream/eventToSdkMessage.js +4 -0
- package/dist/adapter/downstream/terminalToSdkResult.d.ts +8 -0
- package/dist/adapter/downstream/terminalToSdkResult.js +36 -1
- package/dist/adapter/runStream.d.ts +2 -12
- package/dist/adapter/runStream.js +46 -19
- package/dist/agentSession/engineAgentRegistryRead.js +2 -1
- package/dist/agentSession/engineSessionBackgroundStop.js +2 -2
- package/dist/attachmentOutcome.d.ts +35 -0
- package/dist/attachmentOutcome.js +192 -0
- package/dist/attachmentPathPlan.d.ts +67 -0
- package/dist/attachmentPathPlan.js +203 -0
- package/dist/attachmentRoute.d.ts +9 -1
- package/dist/attachmentRoute.js +23 -2
- package/dist/declared/supportFloorDeclared.d.ts +1 -1
- package/dist/declared/supportFloorDeclared.js +1 -0
- package/dist/deviceRebind.js +2 -3
- package/dist/displayUntrusted.js +126 -19
- package/dist/engineAddress.d.ts +1 -1
- package/dist/engineAddress.js +9 -6
- package/dist/engineErrorCodes.d.ts +3 -0
- package/dist/engineErrorCodes.js +4 -0
- package/dist/engineNoticeCodes.d.ts +34 -4
- package/dist/engineNoticeCodes.js +133 -6
- package/dist/engineUnavailable.d.ts +4 -2
- package/dist/engineUnavailable.js +99 -17
- package/dist/fleet/fleetLedger.d.ts +9 -3
- package/dist/fleet/fleetLedger.js +112 -16
- package/dist/fleet/fleetProjection.d.ts +5 -0
- package/dist/fleet/fleetProjection.js +9 -1
- package/dist/gateVocabulary.d.ts +7 -0
- package/dist/gateVocabulary.js +40 -14
- package/dist/generated/engineFactTables.d.ts +4 -2
- package/dist/generated/engineFactTables.js +198 -2
- package/dist/generated/engineNoticeTables.js +56 -8
- package/dist/generated/settingsSchemaFns.d.ts +7 -0
- package/dist/generated/settingsSchemaFns.js +11 -0
- package/dist/generated/settingsSchemaHash.d.ts +1 -0
- package/dist/generated/settingsSchemaHash.js +58 -0
- package/dist/generated/settingsSchemaOwnKeyRead.js +6 -1
- package/dist/generated/settingsSchemaTables.d.ts +3 -2
- package/dist/generated/settingsSchemaTables.js +14 -0
- package/dist/generated/settingsSchemaTightenVerdict.d.ts +1 -0
- package/dist/generated/settingsSchemaTightenVerdict.js +125 -0
- package/dist/headlessPermissionModeWire.d.ts +7 -2
- package/dist/headlessPermissionModeWire.js +77 -8
- package/dist/hitl/approvalDecisionDoor.d.ts +24 -0
- package/dist/hitl/approvalDecisionDoor.js +77 -0
- package/dist/hitl/approvalDelivery.d.ts +6 -0
- package/dist/hitl/approvalDelivery.js +57 -7
- package/dist/hitl/approvalResolution.d.ts +6 -3
- package/dist/hitl/approvalResolution.js +24 -2
- package/dist/hitl/approvalsFeed.js +1 -1
- package/dist/hitl/automaticAnswerVerdict.d.ts +1 -1
- package/dist/hitl/decisionSend.d.ts +7 -4
- package/dist/hitl/decisionSend.js +90 -41
- package/dist/hitl/frameRouter.d.ts +3 -3
- package/dist/hitl/frameRouter.js +5 -0
- package/dist/hitl/hitlHostSurface.d.ts +3 -2
- package/dist/hitl/hitlHostSurface.js +5 -3
- package/dist/hitl/legacyApprovalShapes.d.ts +142 -0
- package/dist/hitl/legacyApprovalShapes.js +1 -0
- package/dist/hitl/livePendingAsk.d.ts +4 -4
- package/dist/hitl/livePendingAsk.js +5 -3
- package/dist/hitl/parkResolver.js +1 -1
- package/dist/hitl/persistedRulesWire.js +2 -2
- package/dist/hitl/planReviewCard.d.ts +2 -1
- package/dist/hitl/planReviewCard.js +64 -1
- package/dist/hitl/planReviewWire.d.ts +30 -0
- package/dist/hitl/planReviewWire.js +204 -65
- package/dist/hitl/sessionPolicyWire.js +16 -7
- package/dist/hitl/subagentSource.d.ts +1 -0
- package/dist/hitl/subagentSource.js +11 -0
- package/dist/hitl/toolApprovalWire.d.ts +34 -10
- package/dist/hitl/toolApprovalWire.js +182 -33
- package/dist/hookMatcherVerdict.d.ts +2 -1
- package/dist/hookMatcherVerdict.js +3 -2
- package/dist/hooksReaderGenerations.d.ts +8 -0
- package/dist/hooksReaderGenerations.js +49 -0
- package/dist/hooksWireCaps.js +43 -18
- package/dist/hostSettingsSource.d.ts +1 -1
- package/dist/hostSettingsSource.js +2 -2
- package/dist/hostTurnSettings.d.ts +2 -2
- package/dist/hostTurnSettings.js +87 -47
- package/dist/humanTurn.d.ts +2 -1
- package/dist/humanTurn.js +44 -13
- package/dist/humanTurnRead.d.ts +7 -0
- package/dist/humanTurnRead.js +23 -14
- package/dist/humanTurnSentences.js +3 -0
- package/dist/i18n/messages.en.d.ts +91 -5
- package/dist/i18n/messages.en.js +98 -12
- package/dist/i18n/messages.zh.js +108 -15
- package/dist/index.d.ts +29 -15
- package/dist/index.js +17 -9
- package/dist/liveModelCatalog.js +1 -0
- package/dist/liveQuestionStore.d.ts +2 -0
- package/dist/liveQuestionStore.js +7 -2
- package/dist/localConfig/kernel.d.ts +2 -1
- package/dist/localConfig/kernel.js +12 -18
- package/dist/localConfig/mcp/edit.js +25 -27
- package/dist/localConfig/mcp/servers.js +5 -8
- package/dist/localConfig/modelPool.d.ts +6 -1
- package/dist/localConfig/modelPool.js +104 -27
- package/dist/localConfig/modelPoolWords.d.ts +2 -0
- package/dist/localConfig/modelPoolWords.js +1 -0
- package/dist/localConfig/settingsWrite.js +1 -14
- package/dist/localConfig/skills.js +9 -16
- package/dist/localEngineSpawn.d.ts +29 -1
- package/dist/localEngineSpawn.js +62 -1
- package/dist/mcpLiveness.d.ts +1 -1
- package/dist/mcpProbeWire.js +2 -2
- package/dist/mcpReconnect.js +2 -2
- package/dist/model/catalogLoader.js +3 -17
- package/dist/model/onboardJudgement.d.ts +8 -0
- package/dist/model/onboardJudgement.js +8 -3
- package/dist/model/onboardSteps.d.ts +19 -0
- package/dist/model/onboardSteps.js +76 -0
- package/dist/notifications.js +6 -0
- package/dist/ownKey.d.ts +4 -0
- package/dist/ownKey.js +61 -0
- package/dist/pathShape.d.ts +14 -0
- package/dist/pathShape.js +44 -0
- package/dist/planReviewCardFacts.d.ts +3 -0
- package/dist/planReviewCardFacts.js +20 -3
- package/dist/pluginHooksWire.d.ts +20 -8
- package/dist/pluginHooksWire.js +204 -86
- package/dist/prepareRefusal.d.ts +2 -0
- package/dist/prepareRefusal.js +11 -1
- package/dist/request/taskRequest.js +3 -1
- package/dist/resultFrameKeys.d.ts +1 -0
- package/dist/resultFrameKeys.js +3 -0
- package/dist/resumeRefusalCopy.js +3 -1
- package/dist/retryStatus.d.ts +11 -1
- package/dist/retryStatus.js +15 -4
- package/dist/runTerminal.d.ts +1 -0
- package/dist/runTerminal.js +15 -1
- package/dist/sdkWireTransit.d.ts +1 -1
- package/dist/seatContract.d.ts +12 -3
- package/dist/seatContract.js +24 -13
- package/dist/sessionEngine.js +1 -1
- package/dist/sessionHooksRoster.js +8 -6
- package/dist/sessionIdSegment.d.ts +0 -1
- package/dist/sessionIdSegment.js +1 -3
- package/dist/sessionMemoryErase.js +2 -2
- package/dist/sessionMemoryStatus.js +2 -1
- package/dist/sessionOccupant.d.ts +29 -0
- package/dist/sessionOccupant.js +78 -0
- package/dist/settingsHooksSalvage.d.ts +4 -1
- package/dist/settingsHooksSalvage.js +22 -9
- package/dist/skillOverrideChain.d.ts +3 -0
- package/dist/skillOverrideChain.js +31 -4
- package/dist/skillsWireCaps.d.ts +16 -2
- package/dist/skillsWireCaps.js +47 -20
- package/dist/subagent/agentStateDetail.js +32 -8
- package/dist/subagent/engineCompactWire.js +48 -36
- package/dist/subagent/engineDelegatedPrompt.d.ts +3 -2
- package/dist/subagent/engineDelegatedPrompt.js +65 -28
- package/dist/subagentEditOutcomes.d.ts +9 -0
- package/dist/subagentEditOutcomes.js +43 -0
- package/dist/supportFloor.d.ts +2 -1
- package/dist/supportFloor.js +1 -1
- package/dist/toolResult.js +4 -2
- package/dist/toolRoster.d.ts +1 -0
- package/dist/toolRoster.js +1 -0
- package/dist/usageAnalytics.js +2 -1
- package/dist/userAttachmentEnvelope.d.ts +11 -0
- package/dist/userAttachmentEnvelope.js +7 -2
- package/dist/userAttachmentSeat.d.ts +7 -1
- package/dist/userAttachmentSeat.js +44 -7
- package/dist/wiringInstructionsAttribution.js +8 -2
- package/docs/INTEGRATION-CLIENTS.md +1588 -16
- package/package.json +5 -5
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import { publishQuestionFrame, publishQuestionFrameFor, registerLocalQuestionResponder, unregisterLocalQuestionResponder, hasLocalQuestionResponder, hasQuestionOverlayFor, } from '../liveQuestionStore.js';
|
|
1
|
+
import { deliverQuestionFrameFor, publishQuestionFrame, publishQuestionFrameFor, registerLocalQuestionResponder, unregisterLocalQuestionResponder, hasLocalQuestionResponder, hasQuestionOverlayFor, } from '../liveQuestionStore.js';
|
|
2
2
|
import { hostLog } from '../host.js';
|
|
3
|
+
import { displayUntrusted } from '../displayUntrusted.js';
|
|
3
4
|
import { invokeHostCallbackIsolated } from '../hostCallbackGuard.js';
|
|
4
5
|
import { abortableSleep, settleWithinDeadline } from '../abortableSleep.js';
|
|
5
6
|
import { clientHasVerbs, engineWireFor, noAnswerKindOf, sendOnce } from '../engineWireFor.js';
|
|
@@ -8,15 +9,16 @@ import { enqueuePlanReviewOutcome } from '../notifications.js';
|
|
|
8
9
|
import { corruptStoredRowContent, corruptStoredRowWhere } from '../wireErrorTriage.js';
|
|
9
10
|
import { decideFailureNote } from '../decideFailureNote.js';
|
|
10
11
|
import { readThrownExtra, wireFailureTextsOf } from '../wireFailureShape.js';
|
|
11
|
-
import { appendOwnElement, readOwnDataNonEmptyString, readOwnDataValue } from '../ownKey.js';
|
|
12
|
+
import { appendOwnElement, guardedRead, hostPresenceOf, readOwnDataNonEmptyString, readOwnDataValue } from '../ownKey.js';
|
|
12
13
|
import { checkpointUnsupportedVersionContent, checkpointUnsupportedVersionFromError, resumeContextUnavailableContent, resumeContextUnavailableFromError } from '../resumeRefusalCopy.js';
|
|
13
14
|
import { APPROVAL_STALE, REQUEST_FIELD_CONFLICT } from '../engineErrorCodes.js';
|
|
14
|
-
import { refusalDeliveryOf } from './decisionRefusal.js';
|
|
15
|
+
import { readDecisionRefusalReading, refusalDeliveryOf } from './decisionRefusal.js';
|
|
16
|
+
import { decisionRefusalWayOutContent } from '../checkpointRowVerdict.js';
|
|
15
17
|
import { directFailureVerdictOf, directSafetyStopOf } from './approvalResolution.js';
|
|
16
18
|
import { __resetDecisionLedgerForTests, decisionInFlightOf, decisionKeyInFlight, decisionLedgerKeysOf, decisionOpsWithKey, decisionReadMark, dropDecisionOp, latestDecisionHandedMark, markLatestDecisionNotApplied, openDecisionOp, planDecisionKey, readMarkOf, settleDecisionOp, settleDecisionOpsOnGateLeft, } from './decisionLedger.js';
|
|
17
19
|
import { denyReasonForWire, isPlanReviewModeAfter } from './hitlBridge.js';
|
|
18
20
|
import { freshPlanReviewQuestionId, planReviewQuestionId, REOPEN_ID_TAIL, taskIdFromPlanReviewQuestionId } from './gateIdentity.js';
|
|
19
|
-
import { isPlanReviewPark, mintPlanReviewCardFacts, notePlanReviewCardIssued, pausedCheckpointIdOf, planReviewCardFactsFrom, planReviewFactsCurrent, registeredPlanReviewCardFacts, samePlanReviewCardFacts, unknownBody, _resetPlanReviewRunsForTest, } from '../planReviewCardFacts.js';
|
|
21
|
+
import { isPlanReviewPark, mintPlanReviewCardFacts, noteCardGateHandedOver, notePlanReviewCardIssued, pausedCheckpointIdOf, planReviewCardFactsFrom, planReviewCardReplacesEarlier, planReviewFactsCurrent, registeredPlanReviewCardFacts, samePlanReviewCardFacts, unknownBody, _resetPlanReviewRunsForTest, } from '../planReviewCardFacts.js';
|
|
20
22
|
import { planReviewAskAgainKey, planReviewCardNoteRef, planReviewDecisionReasonRef, planReviewUnreadableNoteRef, takePlanReviewFacts, _resetPlanReviewFactsFlightsForTest, } from './planReviewCard.js';
|
|
21
23
|
import { planReviewOutcomeCauseOf } from '../planReviewOutcomeCause.js';
|
|
22
24
|
import { EN_MESSAGES } from '../i18n/messages.en.js';
|
|
@@ -133,6 +135,14 @@ function cardShapeOf(facts, submittedInPlanMode) {
|
|
|
133
135
|
}
|
|
134
136
|
return submittedInPlanMode ? 'three_choice' : 'two_choice';
|
|
135
137
|
}
|
|
138
|
+
const PLAN_REVIEW_HEADING_GATE_MAX = 120;
|
|
139
|
+
function planReviewHeadingOf(facts, replaces, lang) {
|
|
140
|
+
const gate = facts?.checkpointId === undefined ? '' : displayUntrusted(facts.checkpointId, { max: PLAN_REVIEW_HEADING_GATE_MAX });
|
|
141
|
+
if (gate === '')
|
|
142
|
+
return renderMessage(msg('approval.planReview.heading.plain'), lang);
|
|
143
|
+
const head = msg('approval.planReview.heading.gate', { gate });
|
|
144
|
+
return renderMessage(replaces ? msg('approval.planReview.heading.replaces', { head }) : head, lang);
|
|
145
|
+
}
|
|
136
146
|
export function planReviewCardViewOf(frame, lang) {
|
|
137
147
|
try {
|
|
138
148
|
const questionId = readOwnDataNonEmptyString(frame ?? undefined, 'questionId');
|
|
@@ -141,14 +151,20 @@ export function planReviewCardViewOf(frame, lang) {
|
|
|
141
151
|
return undefined;
|
|
142
152
|
const facts = planReviewCardFactsFrom(readOwnDataValue(frame ?? undefined, '_sema_planReview'));
|
|
143
153
|
if (facts === undefined || facts.taskId !== taskId) {
|
|
144
|
-
return Object.freeze({ options: OPTIONS_BY_SHAPE.withheld, note: renderMessage(planReviewUnreadableNoteRef(), lang) });
|
|
154
|
+
return Object.freeze({ options: OPTIONS_BY_SHAPE.withheld, note: renderMessage(planReviewUnreadableNoteRef(), lang), heading: planReviewHeadingOf(undefined, false, lang) });
|
|
145
155
|
}
|
|
146
156
|
const shape = cardShapeOf(facts, frame !== null && frame !== undefined && offeredModeFromLabels(frame) === 'three_choice');
|
|
147
157
|
const noteRef = planReviewCardNoteRef(facts);
|
|
148
|
-
|
|
158
|
+
const replaces = readOwnDataValue(frame ?? undefined, '_sema_planReviewReplaces') === true;
|
|
159
|
+
return Object.freeze({
|
|
160
|
+
facts,
|
|
161
|
+
options: OPTIONS_BY_SHAPE[shape],
|
|
162
|
+
...(noteRef !== undefined ? { note: renderMessage(noteRef, lang) } : {}),
|
|
163
|
+
heading: planReviewHeadingOf(facts, replaces, lang),
|
|
164
|
+
});
|
|
149
165
|
}
|
|
150
166
|
catch {
|
|
151
|
-
return Object.freeze({ options: OPTIONS_BY_SHAPE.withheld, note: renderMessage(planReviewUnreadableNoteRef(), lang) });
|
|
167
|
+
return Object.freeze({ options: OPTIONS_BY_SHAPE.withheld, note: renderMessage(planReviewUnreadableNoteRef(), lang), heading: planReviewHeadingOf(undefined, false, lang) });
|
|
152
168
|
}
|
|
153
169
|
}
|
|
154
170
|
export function planReviewEvidenceMovedOf(shown, latest) {
|
|
@@ -305,7 +321,7 @@ function planReviewQuestions(question, shape) {
|
|
|
305
321
|
},
|
|
306
322
|
];
|
|
307
323
|
}
|
|
308
|
-
function planReviewQuestionFrame(questionId, question, shape, facts) {
|
|
324
|
+
function planReviewQuestionFrame(questionId, question, shape, facts, sessionKey = DEFAULT_SESSION_KEY) {
|
|
309
325
|
offeredModeByQuestionId.delete(questionId);
|
|
310
326
|
offeredModeByQuestionId.set(questionId, shape);
|
|
311
327
|
while (offeredModeByQuestionId.size > OFFERED_MODE_LEDGER_MAX) {
|
|
@@ -319,10 +335,17 @@ function planReviewQuestionFrame(questionId, question, shape, facts) {
|
|
|
319
335
|
questionId,
|
|
320
336
|
questions: planReviewQuestions(question, shape),
|
|
321
337
|
...(facts !== undefined ? { _sema_planReview: facts } : {}),
|
|
338
|
+
...(facts !== undefined && planReviewCardReplacesEarlier(sessionKey, facts) ? { _sema_planReviewReplaces: true } : {}),
|
|
322
339
|
};
|
|
323
340
|
}
|
|
341
|
+
function deliverPlanReviewCard(sessionKey, frame, facts) {
|
|
342
|
+
if (deliverQuestionFrameFor(sessionKey, frame) !== 'delivered')
|
|
343
|
+
return;
|
|
344
|
+
if (facts !== undefined)
|
|
345
|
+
noteCardGateHandedOver(sessionKey, facts.taskId, facts.checkpointId);
|
|
346
|
+
}
|
|
324
347
|
function publishArmedPlanReviewCard(inst) {
|
|
325
|
-
|
|
348
|
+
deliverPlanReviewCard(DEFAULT_SESSION_KEY, planReviewQuestionFrame(inst.questionId, ARM_QUESTION, inst.shape, inst.facts, DEFAULT_SESSION_KEY), inst.facts);
|
|
326
349
|
}
|
|
327
350
|
function retireArmedPlanReview(taskId, inst) {
|
|
328
351
|
if (armedPlanReviews.get(taskId) === inst)
|
|
@@ -446,9 +469,7 @@ export function armPlanReviewApproval(result, sessionKey, opts) {
|
|
|
446
469
|
else if (!engineWireTarget()) {
|
|
447
470
|
return false;
|
|
448
471
|
}
|
|
449
|
-
const delivery = wire
|
|
450
|
-
? { ...(wire !== undefined ? { wire } : {}), ...(opts?.onOutcome !== undefined ? { onOutcome: opts.onOutcome } : {}) }
|
|
451
|
-
: undefined;
|
|
472
|
+
const delivery = planReviewDeliveryFrom(wire, opts?.onOutcome, opts?.onAccepted);
|
|
452
473
|
const { taskId } = result;
|
|
453
474
|
const gateBefore = pausedCheckpointIdOf(result);
|
|
454
475
|
if (gateBefore === undefined) {
|
|
@@ -464,12 +485,12 @@ export function armPlanReviewApproval(result, sessionKey, opts) {
|
|
|
464
485
|
void taking.pending.then((taken) => {
|
|
465
486
|
try {
|
|
466
487
|
if (taken !== undefined && planReviewFactsCurrent(taken)) {
|
|
467
|
-
|
|
488
|
+
presentAfterAwait(taken.facts, delivery);
|
|
468
489
|
return;
|
|
469
490
|
}
|
|
470
491
|
const current = registeredPlanReviewCardFacts(DEFAULT_SESSION_KEY, taskId, gateBefore);
|
|
471
492
|
if (current !== undefined) {
|
|
472
|
-
|
|
493
|
+
presentAfterAwait(current, delivery);
|
|
473
494
|
return;
|
|
474
495
|
}
|
|
475
496
|
hostLog('debug', `planReviewWire: arm-superseded task=${taskId}`);
|
|
@@ -488,7 +509,17 @@ export function armPlanReviewApproval(result, sessionKey, opts) {
|
|
|
488
509
|
hostLog('debug', `planReviewWire: arm failed (fail-soft): ${String(e)}`);
|
|
489
510
|
return false;
|
|
490
511
|
}
|
|
491
|
-
function
|
|
512
|
+
function presentAfterAwait(facts, delivery) {
|
|
513
|
+
if (!isPlanReviewPark(result))
|
|
514
|
+
return;
|
|
515
|
+
const presence = publishPresenceOf(opts);
|
|
516
|
+
if (presence !== 'here') {
|
|
517
|
+
hostLog('debug', `planReviewWire: arm-withheld-${presence} task=${result.taskId}`);
|
|
518
|
+
return;
|
|
519
|
+
}
|
|
520
|
+
presentArmedPlanReview(facts, delivery, () => publishPresenceOf(opts) === 'here');
|
|
521
|
+
}
|
|
522
|
+
function presentArmedPlanReview(facts, delivery, recheckHere) {
|
|
492
523
|
if (!isPlanReviewPark(result))
|
|
493
524
|
return;
|
|
494
525
|
const { taskId } = result;
|
|
@@ -502,6 +533,10 @@ export function armPlanReviewApproval(result, sessionKey, opts) {
|
|
|
502
533
|
prev.checkpointId !== undefined &&
|
|
503
534
|
prev.checkpointId === gateBefore &&
|
|
504
535
|
samePlanReviewCardFacts(prev.facts, facts)) {
|
|
536
|
+
if (recheckHere !== undefined && !recheckHere()) {
|
|
537
|
+
hostLog('debug', `planReviewWire: arm-withheld-after-retire task=${taskId}`);
|
|
538
|
+
return;
|
|
539
|
+
}
|
|
505
540
|
hostLog('debug', `planReviewWire: arm replayed — re-presenting the still-armed card for task ${taskId}`);
|
|
506
541
|
publishArmedPlanReviewCard(prev);
|
|
507
542
|
return;
|
|
@@ -509,6 +544,10 @@ export function armPlanReviewApproval(result, sessionKey, opts) {
|
|
|
509
544
|
hostLog('debug', `planReviewWire: arm-retire task=${taskId} card=${prev.questionId}`);
|
|
510
545
|
retireArmedPlanReview(taskId, prev);
|
|
511
546
|
}
|
|
547
|
+
if (recheckHere !== undefined && !recheckHere()) {
|
|
548
|
+
hostLog('debug', `planReviewWire: arm-withheld-after-retire task=${taskId}`);
|
|
549
|
+
return;
|
|
550
|
+
}
|
|
512
551
|
const issuedBefore = notePlanReviewCardIssued(DEFAULT_SESSION_KEY, taskId);
|
|
513
552
|
const questionId = issuedBefore || hadArmedInstance ? freshPlanReviewQuestionId(taskId, reopenIdSuffix()) : planReviewQuestionId(taskId);
|
|
514
553
|
const instance = Object.freeze({ questionId, ...(gateBefore !== undefined ? { checkpointId: gateBefore } : {}), facts, shape });
|
|
@@ -570,6 +609,11 @@ export function planReviewOutcomeTail(taskId, decision, effect, cause) {
|
|
|
570
609
|
return `Do not state the result as fact: tell the user the ${decision} was submitted but its effect could not be confirmed; the background task list or the next notification of task ${JSON.stringify(taskId)} shows where it stands.`;
|
|
571
610
|
}
|
|
572
611
|
}
|
|
612
|
+
function planReviewDeliveryFrom(wire, onOutcome, onAccepted) {
|
|
613
|
+
if (wire === undefined && onOutcome === undefined && onAccepted === undefined)
|
|
614
|
+
return undefined;
|
|
615
|
+
return { ...(wire !== undefined ? { wire } : {}), ...(onOutcome !== undefined ? { onOutcome } : {}), ...(onAccepted !== undefined ? { onAccepted } : {}) };
|
|
616
|
+
}
|
|
573
617
|
function planReviewDecisionTimedOutOutcome(decision) {
|
|
574
618
|
return (`The plan_review ${decision} was sent, but no answer came back from the engine within the time limit this host allows for it — ` +
|
|
575
619
|
`it may have taken effect (${decision === 'approve' ? 'an approval keeps the engine busy until the resumed work finishes' : 'the engine may still be applying it'}).`);
|
|
@@ -747,13 +791,43 @@ function bodyWordOf(body, key) {
|
|
|
747
791
|
return undefined;
|
|
748
792
|
}
|
|
749
793
|
}
|
|
750
|
-
function
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
794
|
+
function planReviewAcceptanceOf(body) {
|
|
795
|
+
const obj = body !== null && typeof body === 'object' ? body : undefined;
|
|
796
|
+
const str = (key) => (obj === undefined ? undefined : guardedRead(() => readOwnDataNonEmptyString(obj, key)));
|
|
797
|
+
const isTrue = (key) => obj !== undefined && guardedRead(() => readOwnDataValue(obj, key)) === true;
|
|
798
|
+
const taskId = str('taskId');
|
|
799
|
+
const sessionId = str('sessionId');
|
|
800
|
+
const status = str('status');
|
|
801
|
+
const checkpointId = str('checkpointId');
|
|
802
|
+
const engine = Object.freeze({
|
|
803
|
+
...(taskId !== undefined ? { taskId } : {}),
|
|
804
|
+
...(sessionId !== undefined ? { sessionId } : {}),
|
|
805
|
+
...(status !== undefined ? { status } : {}),
|
|
806
|
+
...(isTrue('decisionCommitted') ? { decisionCommitted: true } : {}),
|
|
807
|
+
...(checkpointId !== undefined ? { checkpointId } : {}),
|
|
808
|
+
});
|
|
809
|
+
const errorCode = str('errorCode');
|
|
810
|
+
return Object.freeze({ engine, reparked: isTrue('retriable'), ...(errorCode !== undefined ? { errorCode } : {}) });
|
|
811
|
+
}
|
|
812
|
+
function acceptanceSettled(a) {
|
|
813
|
+
return a !== undefined && a.engine.decisionCommitted === true && !a.reparked;
|
|
814
|
+
}
|
|
815
|
+
function planReviewAcceptedPayloadOf(taskId, dispatchNo, semanticDecision, cardId, a, wireGate, card) {
|
|
816
|
+
const body = card?.body;
|
|
817
|
+
const decided = acceptanceSettled(a) && semanticDecision !== 'ask_again' && wireGate !== undefined && body !== undefined && body.kind === 'shown'
|
|
818
|
+
? Object.freeze({ verdict: semanticDecision === 'approve' ? 'approved' : 'rejected', plan: body.markdown })
|
|
819
|
+
: undefined;
|
|
820
|
+
return Object.freeze({
|
|
821
|
+
taskId,
|
|
822
|
+
dispatchNo,
|
|
823
|
+
decision: semanticDecision,
|
|
824
|
+
...(cardId !== undefined ? { questionId: cardId } : {}),
|
|
825
|
+
engine: a.engine,
|
|
826
|
+
...(decided !== undefined ? { decided } : {}),
|
|
827
|
+
});
|
|
828
|
+
}
|
|
829
|
+
function planReviewDeliveryOf(a, semanticDecision) {
|
|
830
|
+
return acceptanceSettled(a) && semanticDecision !== 'approve' ? 'follow' : 'submit';
|
|
757
831
|
}
|
|
758
832
|
function askAgainReasonOf(card, opts) {
|
|
759
833
|
try {
|
|
@@ -831,6 +905,8 @@ async function decidePlanReviewInner(taskId, decision, permissionModeAfter, disp
|
|
|
831
905
|
let outcome;
|
|
832
906
|
let cause;
|
|
833
907
|
let unsentProven = false;
|
|
908
|
+
let acceptance;
|
|
909
|
+
let wayOut;
|
|
834
910
|
const gateBefore = card?.checkpointId;
|
|
835
911
|
const approvalWithheld = decision !== 'approve' ? undefined : card === undefined ? 'card_missing' : card.body.kind !== 'shown' || card.checkpointId === undefined ? 'card_withholds' : undefined;
|
|
836
912
|
let effect = 'unconfirmed';
|
|
@@ -913,7 +989,14 @@ async function decidePlanReviewInner(taskId, decision, permissionModeAfter, disp
|
|
|
913
989
|
finally {
|
|
914
990
|
releaseLatch();
|
|
915
991
|
}
|
|
916
|
-
|
|
992
|
+
const accepted = planReviewAcceptanceOf(body);
|
|
993
|
+
acceptance = accepted;
|
|
994
|
+
hostLog('debug', `planReviewWire: ${decision} → ok (dispatch #${dispatchNo}) ${JSON.stringify(accepted).slice(0, 200)}`);
|
|
995
|
+
const onAccepted = opts?.onAccepted;
|
|
996
|
+
if (onAccepted !== undefined) {
|
|
997
|
+
const payload = planReviewAcceptedPayloadOf(taskId, dispatchNo, semanticDecision, cardId, accepted, wireGate, card);
|
|
998
|
+
invokeHostCallbackIsolated(`planReviewWire: onAccepted task=${taskId}`, () => onAccepted(payload));
|
|
999
|
+
}
|
|
917
1000
|
let postStatus;
|
|
918
1001
|
let gateAfter;
|
|
919
1002
|
let gateKindAfter;
|
|
@@ -928,12 +1011,12 @@ async function decidePlanReviewInner(taskId, decision, permissionModeAfter, disp
|
|
|
928
1011
|
catch (e) {
|
|
929
1012
|
hostLog('debug', `planReviewWire: post-decide status re-pull failed (fail-soft): ${String(e)}`);
|
|
930
1013
|
}
|
|
931
|
-
const bodyStatus =
|
|
1014
|
+
const bodyStatus = accepted.engine.status;
|
|
932
1015
|
const effective = postStatus ?? bodyStatus;
|
|
933
1016
|
statusSeen = effective;
|
|
934
|
-
effect = planReviewSettledVerdict({ decision, postStatus, bodyStatus, reparked:
|
|
935
|
-
const code =
|
|
936
|
-
if (effect === 'still_parked' && postStatus === 'needs_review' && !
|
|
1017
|
+
effect = planReviewSettledVerdict({ decision, postStatus, bodyStatus, reparked: accepted.reparked, gateBefore, gateAfter, gateKindAfter });
|
|
1018
|
+
const code = accepted.errorCode;
|
|
1019
|
+
if (effect === 'still_parked' && postStatus === 'needs_review' && !accepted.reparked) {
|
|
937
1020
|
outcome =
|
|
938
1021
|
`The plan_review ${decision} returned HTTP 200 but the session is STILL locked on the same review gate ` +
|
|
939
1022
|
`(post-decide status: needs_review). The decision did NOT take effect (known engine issue, RB-471 family). ` +
|
|
@@ -994,33 +1077,27 @@ async function decidePlanReviewInner(taskId, decision, permissionModeAfter, disp
|
|
|
994
1077
|
'This is NOT a rejection of the plan and NOT a failure of the task; the answer just given simply had nothing left to act on.';
|
|
995
1078
|
}
|
|
996
1079
|
else if (status === 409 && refusedCode === APPROVAL_STALE) {
|
|
997
|
-
let
|
|
998
|
-
if (wireGate !== undefined) {
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
(
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
const row = await repullRow();
|
|
1012
|
-
untrusted = row !== undefined && pausedCheckpointIdOf(row?.result) === wireGate;
|
|
1013
|
-
}
|
|
1014
|
-
catch {
|
|
1015
|
-
untrusted = false;
|
|
1016
|
-
}
|
|
1080
|
+
let verdict = 'gate_moved';
|
|
1081
|
+
if (wireGate !== undefined && planReviewCurrentGateReadingOf(readThrownExtra(e)) === 'absent') {
|
|
1082
|
+
try {
|
|
1083
|
+
const row = await repullRow();
|
|
1084
|
+
const rowGate = pausedCheckpointIdOf(row?.result);
|
|
1085
|
+
verdict =
|
|
1086
|
+
row !== undefined && rowGate === wireGate
|
|
1087
|
+
? 'gate_untrusted'
|
|
1088
|
+
: row !== undefined && rowGate === undefined && bodyWordOf(row, 'status') === 'needs_review'
|
|
1089
|
+
? 'unreadable'
|
|
1090
|
+
: 'gate_moved';
|
|
1091
|
+
}
|
|
1092
|
+
catch {
|
|
1093
|
+
verdict = 'gate_moved';
|
|
1017
1094
|
}
|
|
1018
1095
|
}
|
|
1019
|
-
if (
|
|
1096
|
+
if (verdict === 'gate_untrusted' && wireGate !== undefined)
|
|
1020
1097
|
noteEngineRefusedGate(wireGate);
|
|
1021
|
-
hostLog('debug', `planReviewWire: approval-stale decision=${decision} reason=${
|
|
1098
|
+
hostLog('debug', `planReviewWire: approval-stale decision=${decision} reason=${verdict}`);
|
|
1022
1099
|
effect = 'not_applied';
|
|
1023
|
-
cause =
|
|
1100
|
+
cause = verdict === 'unreadable' ? undefined : verdict;
|
|
1024
1101
|
outcome = renderMessage(msg('approval.planReview.decision.refused', { decision }), DATA_FACE_LANGUAGE);
|
|
1025
1102
|
}
|
|
1026
1103
|
else if (checkpointUnsupportedVersionFromError(e) !== null) {
|
|
@@ -1046,7 +1123,11 @@ async function decidePlanReviewInner(taskId, decision, permissionModeAfter, disp
|
|
|
1046
1123
|
const failureTexts = wireFailureTextsOf(e);
|
|
1047
1124
|
hostLog('debug', `planReviewWire: ${decision} → ${status} ${failureTexts.log.slice(0, 200)}`);
|
|
1048
1125
|
effect = refusalDeliveryOf(e, { route: 'plan_review' }).delivery === 'refused' ? 'not_applied' : 'unconfirmed';
|
|
1049
|
-
|
|
1126
|
+
const engineWayOut = status === 409 ? readDecisionRefusalReading(e, { route: 'plan_review' }).wayOut : undefined;
|
|
1127
|
+
const wayOutClause = engineWayOut === undefined ? undefined : decisionRefusalWayOutContent(engineWayOut);
|
|
1128
|
+
if (wayOutClause !== undefined)
|
|
1129
|
+
wayOut = engineWayOut;
|
|
1130
|
+
outcome = `${`The plan_review decision failed: HTTP ${status} ${failureTexts.reason}`.trim()}${decideFailureNote(e)}${wayOutClause === undefined ? '' : ` — ${wayOutClause}.`}`;
|
|
1050
1131
|
}
|
|
1051
1132
|
else if (noAnswer === 'time-limit' || noAnswer === 'deadline') {
|
|
1052
1133
|
hostLog('debug', `planReviewWire: ${decision} sent but no answer within the request time limit (task ${taskId})`);
|
|
@@ -1066,13 +1147,25 @@ async function decidePlanReviewInner(taskId, decision, permissionModeAfter, disp
|
|
|
1066
1147
|
}
|
|
1067
1148
|
}
|
|
1068
1149
|
const outcomeCause = planReviewOutcomeCauseOf(effect, cause);
|
|
1069
|
-
|
|
1150
|
+
const sentWithoutGate = gateBefore !== undefined && wireGate === undefined && effect !== 'not_sent' && !unsentProven;
|
|
1151
|
+
outcome = `${outcome}${planReviewDecisionReasonSegment(outcomeCause, card)}${sentWithoutGate ? planReviewSentWithoutGateSegment() : ''}`;
|
|
1070
1152
|
const tail = planReviewOutcomeTail(taskId, decision, effect, outcomeCause);
|
|
1071
1153
|
const prompt = `<plan-review-outcome>\n${outcome}\n${tail}\n</plan-review-outcome>`;
|
|
1072
1154
|
const onOutcome = opts?.onOutcome;
|
|
1155
|
+
const meta = {
|
|
1156
|
+
taskId,
|
|
1157
|
+
dispatchNo,
|
|
1158
|
+
decision: semanticDecision,
|
|
1159
|
+
effect,
|
|
1160
|
+
...(outcomeCause !== undefined ? { cause: outcomeCause } : {}),
|
|
1161
|
+
...(cardId !== undefined ? { questionId: cardId } : {}),
|
|
1162
|
+
delivery: planReviewDeliveryOf(acceptance, semanticDecision),
|
|
1163
|
+
...(sentWithoutGate ? { gateIdOmitted: true } : {}),
|
|
1164
|
+
...(wayOut !== undefined ? { wayOut } : {}),
|
|
1165
|
+
};
|
|
1073
1166
|
settle(effect === 'not_sent' || effect === 'still_parked');
|
|
1074
1167
|
try {
|
|
1075
|
-
const delivered = enqueuePlanReviewOutcome(prompt,
|
|
1168
|
+
const delivered = enqueuePlanReviewOutcome(prompt, meta);
|
|
1076
1169
|
if (!delivered) {
|
|
1077
1170
|
if (onOutcome !== undefined) {
|
|
1078
1171
|
hostLog('debug', `planReviewWire: outcome queue port not installed for task ${taskId} — outcome handed to the host's onOutcome callback only`);
|
|
@@ -1087,12 +1180,7 @@ async function decidePlanReviewInner(taskId, decision, permissionModeAfter, disp
|
|
|
1087
1180
|
}
|
|
1088
1181
|
if (onOutcome !== undefined) {
|
|
1089
1182
|
invokeHostCallbackIsolated(`planReviewWire: onOutcome callback (task ${taskId})`, () => onOutcome({
|
|
1090
|
-
|
|
1091
|
-
dispatchNo,
|
|
1092
|
-
decision: semanticDecision,
|
|
1093
|
-
effect,
|
|
1094
|
-
...(outcomeCause !== undefined ? { cause: outcomeCause } : {}),
|
|
1095
|
-
...(cardId !== undefined ? { questionId: cardId } : {}),
|
|
1183
|
+
...meta,
|
|
1096
1184
|
detail: outcome,
|
|
1097
1185
|
tail,
|
|
1098
1186
|
prompt,
|
|
@@ -1174,6 +1262,9 @@ function mintPlanReviewReopenCard(taskId, opts, sessionKey, receiptMs, taken) {
|
|
|
1174
1262
|
hostLog('debug', `planReviewWire: reopen-superseded task=${taskId}`);
|
|
1175
1263
|
return Promise.resolve(refusedReopen('not_presented'));
|
|
1176
1264
|
}
|
|
1265
|
+
const early = reopenPresenceRefusal(taskId, opts, 'before');
|
|
1266
|
+
if (early !== undefined)
|
|
1267
|
+
return Promise.resolve(early);
|
|
1177
1268
|
const facts = taken.facts;
|
|
1178
1269
|
const markBefore = dismissalEntryFor(sessionKey, taskId);
|
|
1179
1270
|
const settleOnReceipt = (receipt, firstSight) => receipt.then((presented) => {
|
|
@@ -1195,8 +1286,11 @@ function mintPlanReviewReopenCard(taskId, opts, sessionKey, receiptMs, taken) {
|
|
|
1195
1286
|
samePlanReviewCardFacts(armed.facts, facts)) {
|
|
1196
1287
|
hostLog('debug', `planReviewWire: reopen for task ${taskId} reusing the canonical responder (path=reopen-canonical)`);
|
|
1197
1288
|
retireActiveReopen(sessionKey, canonicalId);
|
|
1289
|
+
const lateReuse = reopenPresenceRefusal(taskId, opts, 'after-retire');
|
|
1290
|
+
if (lateReuse !== undefined)
|
|
1291
|
+
return Promise.resolve(lateReuse);
|
|
1198
1292
|
const receipt = waitForGateArmedFor(sessionKey, [armed.questionId], receiptMs);
|
|
1199
|
-
|
|
1293
|
+
deliverPlanReviewCard(sessionKey, planReviewQuestionFrame(armed.questionId, ARM_QUESTION, armed.shape, armed.facts, sessionKey), armed.facts);
|
|
1200
1294
|
return settleOnReceipt(receipt, firstSight);
|
|
1201
1295
|
}
|
|
1202
1296
|
retireActiveReopen(sessionKey, canonicalId);
|
|
@@ -1205,11 +1299,12 @@ function mintPlanReviewReopenCard(taskId, opts, sessionKey, receiptMs, taken) {
|
|
|
1205
1299
|
hostLog('debug', `planReviewWire: reopen-retire-armed task=${taskId} card=${armedId}`);
|
|
1206
1300
|
}
|
|
1207
1301
|
publishQuestionFrameFor(sessionKey, { type: 'question_complete', questionId: armedId });
|
|
1302
|
+
const lateFresh = reopenPresenceRefusal(taskId, opts, 'after-retire');
|
|
1303
|
+
if (lateFresh !== undefined)
|
|
1304
|
+
return Promise.resolve(lateFresh);
|
|
1208
1305
|
const questionId = freshPlanReviewQuestionId(taskId, reopenIdSuffix());
|
|
1209
1306
|
hostLog('debug', `planReviewWire: path=reopen-fresh task=${taskId} card=${questionId}`);
|
|
1210
|
-
const delivery = opts?.wire
|
|
1211
|
-
? { ...(opts.wire !== undefined ? { wire: opts.wire } : {}), ...(opts.onOutcome !== undefined ? { onOutcome: opts.onOutcome } : {}) }
|
|
1212
|
-
: undefined;
|
|
1307
|
+
const delivery = planReviewDeliveryFrom(opts?.wire, opts?.onOutcome, opts?.onAccepted);
|
|
1213
1308
|
const deliver = opts?.deliverDecision ??
|
|
1214
1309
|
((tid, decision, reason, card) => {
|
|
1215
1310
|
hostLog('debug', `planReviewWire: reopen responder delivering ${decision} for task ${tid} (path=reopen-default)`);
|
|
@@ -1257,12 +1352,32 @@ function mintPlanReviewReopenCard(taskId, opts, sessionKey, receiptMs, taken) {
|
|
|
1257
1352
|
unregister = registerLocalQuestionResponder(questionId, responder, onWithdraw);
|
|
1258
1353
|
activeReopenResponders.set(activeKey, { questionId, unregister: () => unregister(), shape });
|
|
1259
1354
|
const receipt = waitForGateArmedFor(sessionKey, [questionId], receiptMs);
|
|
1260
|
-
|
|
1355
|
+
deliverPlanReviewCard(sessionKey, planReviewQuestionFrame(questionId, REOPEN_QUESTION, shape, facts, sessionKey), facts);
|
|
1261
1356
|
return settleOnReceipt(receipt, firstSight);
|
|
1262
1357
|
}
|
|
1263
1358
|
function refusedReopen(cause) {
|
|
1264
1359
|
return { reopened: false, _sema_reopenRefusal: cause };
|
|
1265
1360
|
}
|
|
1361
|
+
function publishPresenceOf(opts) {
|
|
1362
|
+
return hostPresenceOf(opts, (ask) => {
|
|
1363
|
+
const got = { answered: false };
|
|
1364
|
+
invokeHostCallbackIsolated('planReviewWire: stillHere', () => {
|
|
1365
|
+
got.answer = ask();
|
|
1366
|
+
got.answered = true;
|
|
1367
|
+
return got.answer;
|
|
1368
|
+
});
|
|
1369
|
+
if (!got.answered)
|
|
1370
|
+
throw new Error('stillHere did not answer');
|
|
1371
|
+
return got.answer;
|
|
1372
|
+
});
|
|
1373
|
+
}
|
|
1374
|
+
function reopenPresenceRefusal(taskId, opts, stage) {
|
|
1375
|
+
const presence = publishPresenceOf(opts);
|
|
1376
|
+
if (presence === 'here')
|
|
1377
|
+
return undefined;
|
|
1378
|
+
hostLog('debug', `planReviewWire: reopen-withheld-${presence} task=${taskId} at=${stage}`);
|
|
1379
|
+
return refusedReopen(presence === 'left' ? 'conversation_left' : 'conversation_unconfirmed');
|
|
1380
|
+
}
|
|
1266
1381
|
function noPresentationSurfaceVerdict(taskId, sessionKey) {
|
|
1267
1382
|
const verdict = { reopened: false, _sema_noPresentationSurface: true };
|
|
1268
1383
|
hostLog('debug', `planReviewWire: reopen for task ${taskId} refused — nothing is subscribed to question frames for session ` +
|
|
@@ -1370,7 +1485,8 @@ export function reopenPlanReviewCard(taskId, opts) {
|
|
|
1370
1485
|
return Promise.resolve(refusedReopen('invalid_call'));
|
|
1371
1486
|
}
|
|
1372
1487
|
const injected = opts;
|
|
1373
|
-
if (opts?.mintFreshQuestionId === false &&
|
|
1488
|
+
if (opts?.mintFreshQuestionId === false &&
|
|
1489
|
+
(injected?.deliverDecision !== undefined || injected?.wire !== undefined || injected?.onOutcome !== undefined || injected?.onAccepted !== undefined)) {
|
|
1374
1490
|
hostLog('error', `planReviewWire: plan_review reopen for task ${taskId} refused — mintFreshQuestionId:false reuses the armed card's responder and its delivery, ` +
|
|
1375
1491
|
'so deliverDecision / wire / onOutcome cannot be given with it; drop mintFreshQuestionId:false to deliver through the injected path');
|
|
1376
1492
|
return Promise.resolve(refusedReopen('invalid_call'));
|
|
@@ -1501,3 +1617,26 @@ export async function readPlanReviewCardFacts(taskId, opts) {
|
|
|
1501
1617
|
return mintPlanReviewCardFacts(taskId, checkpointId, unknownBody('unreadable'));
|
|
1502
1618
|
}
|
|
1503
1619
|
}
|
|
1620
|
+
function planReviewCurrentGateReadingOf(bag) {
|
|
1621
|
+
if (bag.state !== 'object')
|
|
1622
|
+
return 'malformed';
|
|
1623
|
+
try {
|
|
1624
|
+
const d = Object.getOwnPropertyDescriptor(bag.value, 'currentPending');
|
|
1625
|
+
if (d === undefined)
|
|
1626
|
+
return 'absent';
|
|
1627
|
+
if (!Object.hasOwn(d, 'value'))
|
|
1628
|
+
return 'malformed';
|
|
1629
|
+
const slot = d.value;
|
|
1630
|
+
if (typeof slot !== 'object' || slot === null || Array.isArray(slot))
|
|
1631
|
+
return 'malformed';
|
|
1632
|
+
const checkpointId = readOwnDataNonEmptyString(slot, 'checkpointId');
|
|
1633
|
+
return checkpointId === undefined ? 'malformed' : { checkpointId };
|
|
1634
|
+
}
|
|
1635
|
+
catch {
|
|
1636
|
+
return 'malformed';
|
|
1637
|
+
}
|
|
1638
|
+
}
|
|
1639
|
+
export function readPlanReviewCurrentGate(e) {
|
|
1640
|
+
const reading = planReviewCurrentGateReadingOf(readThrownExtra(e));
|
|
1641
|
+
return typeof reading === 'object' ? reading.checkpointId : undefined;
|
|
1642
|
+
}
|
|
@@ -6,7 +6,7 @@ import { ruleToolNameVerdictOf } from '../protocolSegment.js';
|
|
|
6
6
|
import { CORRUPT_ROW_ERROR_CODE, RULES_INERT_ENTRY, RULES_INVALID_SHAPE, isRulesRefusalCode } from '../engineErrorCodes.js';
|
|
7
7
|
import { deniedEntryToolName } from '../engineToolDirectory.js';
|
|
8
8
|
import { rosterMountedNames } from '../rosterMountedNames.js';
|
|
9
|
-
import { CORE_ALLOW_ENTRY_INERT_REASONS, CORE_RULE_TOOL_NAME_VERDICTS } from '../generated/engineFactTables.js';
|
|
9
|
+
import { CORE_ALLOW_ENTRY_INERT_REASONS, CORE_RULE_TOOL_NAME_VERDICTS, CORE_SESSION_RULES_GRAMMAR } from '../generated/engineFactTables.js';
|
|
10
10
|
const EARLIER_GRAMMAR_REASON = 'earlier_grammar';
|
|
11
11
|
const _inertReasonPin = true;
|
|
12
12
|
void _inertReasonPin;
|
|
@@ -234,7 +234,7 @@ function receiptMismatch(sent, receipt, sentRev, receiptRev, sentGrammar, receip
|
|
|
234
234
|
if (receiptRev <= sentRev) {
|
|
235
235
|
return `policy write receipt did not advance the version stamp (sent with ${sentRev}, receipt says ${receiptRev})`;
|
|
236
236
|
}
|
|
237
|
-
if (
|
|
237
|
+
if (receiptGrammar === undefined || receiptGrammar < sentGrammar) {
|
|
238
238
|
return `policy write receipt does not carry the rule-grammar stamp that was sent (sent ${sentGrammar}, receipt ${receiptGrammar === undefined ? 'carries none' : `says ${receiptGrammar}`}) — the stored entries may now read differently`;
|
|
239
239
|
}
|
|
240
240
|
for (const field of SESSION_POLICY_RULE_FIELDS) {
|
|
@@ -342,7 +342,14 @@ function earlierGrammarOutcome(failure) {
|
|
|
342
342
|
};
|
|
343
343
|
}
|
|
344
344
|
function rulesWithStamp(rules, grammar) {
|
|
345
|
-
return
|
|
345
|
+
return { ...rules, grammar };
|
|
346
|
+
}
|
|
347
|
+
const GRAMMAR_BEFORE_STAMP = 0;
|
|
348
|
+
function writeGrammarOf(read) {
|
|
349
|
+
const stamp = grammarStampOf(read);
|
|
350
|
+
if (stamp !== undefined)
|
|
351
|
+
return stamp;
|
|
352
|
+
return read.rev >= 1 ? GRAMMAR_BEFORE_STAMP : CORE_SESSION_RULES_GRAMMAR;
|
|
346
353
|
}
|
|
347
354
|
function grammarStampOf(read) {
|
|
348
355
|
return Object.hasOwn(read, 'grammar') ? read.grammar : undefined;
|
|
@@ -361,9 +368,10 @@ async function attemptTighten(facade, req, opts) {
|
|
|
361
368
|
if (earlier.length > 0)
|
|
362
369
|
return { done: { kind: 'refused', why: 'earlier_grammar_rows', entries: earlier } };
|
|
363
370
|
}
|
|
371
|
+
const declared = writeGrammarOf(read);
|
|
364
372
|
let receipt;
|
|
365
373
|
try {
|
|
366
|
-
receipt = await facade.putPolicy(req.sessionId, { rules: rulesWithStamp(rules,
|
|
374
|
+
receipt = await facade.putPolicy(req.sessionId, { rules: rulesWithStamp(rules, declared), expectedRev: read.rev }, opts);
|
|
367
375
|
}
|
|
368
376
|
catch (e) {
|
|
369
377
|
const failure = classifySessionPolicyFailure(e);
|
|
@@ -389,7 +397,7 @@ async function attemptTighten(facade, req, opts) {
|
|
|
389
397
|
if (!narrowed.ok) {
|
|
390
398
|
return { done: { kind: 'unknown', why: 'write_receipt_unreadable', message: narrowed.detail } };
|
|
391
399
|
}
|
|
392
|
-
const mismatch = receiptMismatch(rules, narrowed.rules, read.rev, narrowed.rev,
|
|
400
|
+
const mismatch = receiptMismatch(rules, narrowed.rules, read.rev, narrowed.rev, declared, narrowed.grammar);
|
|
393
401
|
if (mismatch !== null)
|
|
394
402
|
return { done: { kind: 'unknown', why: 'write_receipt_mismatch', message: mismatch } };
|
|
395
403
|
return { done: { kind: 'written', rev: narrowed.rev } };
|
|
@@ -626,9 +634,10 @@ async function attemptRemoval(facade, sessionId, mounted, opts) {
|
|
|
626
634
|
if (earlier.length > 0)
|
|
627
635
|
return { done: { kind: 'refused', why: 'earlier_grammar_rows', entries: earlier } };
|
|
628
636
|
}
|
|
637
|
+
const declared = writeGrammarOf(read);
|
|
629
638
|
let receipt;
|
|
630
639
|
try {
|
|
631
|
-
receipt = await facade.putPolicy(sessionId, { rules: rulesWithStamp(next,
|
|
640
|
+
receipt = await facade.putPolicy(sessionId, { rules: rulesWithStamp(next, declared), expectedRev: read.rev }, opts);
|
|
632
641
|
}
|
|
633
642
|
catch (e) {
|
|
634
643
|
const failure = classifySessionPolicyFailure(e);
|
|
@@ -651,7 +660,7 @@ async function attemptRemoval(facade, sessionId, mounted, opts) {
|
|
|
651
660
|
}
|
|
652
661
|
if (!narrowed.ok)
|
|
653
662
|
return { done: { kind: 'write_unconfirmed', why: 'write_receipt_unreadable', message: narrowed.detail } };
|
|
654
|
-
const mismatch = receiptMismatch(next, narrowed.rules, read.rev, narrowed.rev,
|
|
663
|
+
const mismatch = receiptMismatch(next, narrowed.rules, read.rev, narrowed.rev, declared, narrowed.grammar) ??
|
|
655
664
|
(removed.some((e) => {
|
|
656
665
|
const r = Object.hasOwn(narrowed.rules, e.field) ? narrowed.rules[e.field] : undefined;
|
|
657
666
|
return r !== undefined && r.includes(e.rule);
|
|
@@ -11,3 +11,14 @@ export function subagentSourceSessionOf(carrier) {
|
|
|
11
11
|
const retired = slotReadOf(carrier, 'sourceTaskId');
|
|
12
12
|
return retired.presence === 'present' ? nonEmpty(retired.value) : undefined;
|
|
13
13
|
}
|
|
14
|
+
export function livePendingRowSourceSessionOf(row) {
|
|
15
|
+
if (typeof row !== 'object' || row === null)
|
|
16
|
+
return undefined;
|
|
17
|
+
const current = slotReadOf(row, 'sourceSessionId');
|
|
18
|
+
if (current.presence === 'present')
|
|
19
|
+
return nonEmpty(current.value);
|
|
20
|
+
if (current.presence !== 'absent')
|
|
21
|
+
return undefined;
|
|
22
|
+
const retired = slotReadOf(row, 'originTaskId');
|
|
23
|
+
return retired.presence === 'present' ? nonEmpty(retired.value) : undefined;
|
|
24
|
+
}
|