@sema-agent/client-core 0.105.0 → 0.106.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 +220 -0
- package/README.md +26 -11
- package/dist/adapt/arms.js +10 -1
- package/dist/adapter/activeRunSelfHeal.d.ts +13 -2
- package/dist/adapter/activeRunSelfHeal.js +80 -7
- package/dist/adapter/downstream/eventToSdkMessage.d.ts +0 -5
- package/dist/adapter/downstream/eventToSdkMessage.js +20 -19
- package/dist/adapter/downstream/terminalToSdkResult.js +2 -3
- package/dist/adapter/downstream/wiringManifestView.d.ts +1 -3
- package/dist/adapter/downstream/wiringManifestView.js +0 -1
- package/dist/adapter/runStream.js +3 -4
- package/dist/approvalReachWire.d.ts +1 -0
- package/dist/approvalReachWire.js +4 -1
- package/dist/approvalSeat.d.ts +21 -0
- package/dist/approvalSeat.js +73 -0
- package/dist/assistantInbox.d.ts +2 -0
- package/dist/assistantInbox.js +3 -0
- package/dist/checkpointRowVerdict.d.ts +23 -0
- package/dist/checkpointRowVerdict.js +89 -0
- package/dist/coreValuePorts.d.ts +12 -1
- package/dist/coreValuePorts.js +10 -0
- package/dist/decideFailureNote.js +3 -1
- package/dist/decideReceipt.d.ts +9 -0
- package/dist/decideReceipt.js +29 -1
- package/dist/declared/supportFloorDeclared.js +2 -2
- package/dist/deviceRebind.d.ts +7 -0
- package/dist/deviceRebind.js +39 -5
- package/dist/engineAddress.d.ts +3 -0
- package/dist/engineAddress.js +25 -0
- package/dist/engineDirectory.js +9 -17
- package/dist/engineErrorCodes.d.ts +8 -0
- package/dist/engineErrorCodes.js +8 -0
- package/dist/engineNoticeCodes.d.ts +26 -3
- package/dist/engineNoticeCodes.js +52 -7
- package/dist/engineRowRef.js +1 -1
- package/dist/errorSupplementRoles.js +2 -0
- package/dist/eventLeg.d.ts +2 -2
- package/dist/eventLeg.js +2 -1
- package/dist/fleet/fleetProjection.js +26 -5
- package/dist/generated/engineFactTables.d.ts +2 -1
- package/dist/generated/engineFactTables.js +3 -1
- package/dist/generated/engineNoticeTables.js +19 -4
- package/dist/generated/sdkFactTables.d.ts +3 -0
- package/dist/generated/sdkFactTables.js +15 -0
- package/dist/generated/settingsSchemaFns.d.ts +11 -0
- package/dist/generated/settingsSchemaFns.js +2 -1
- package/dist/generated/settingsSchemaSafetyMergeSpec.js +1 -1
- package/dist/generated/settingsSchemaSettingsSources.d.ts +1 -1
- package/dist/generated/settingsSchemaSettingsSources.js +22 -2
- package/dist/hitl/approvalLiveFace.d.ts +17 -0
- package/dist/hitl/approvalLiveFace.js +35 -0
- package/dist/hitl/approvalQueueRow.d.ts +21 -0
- package/dist/hitl/approvalQueueRow.js +69 -0
- package/dist/hitl/approvalsFeed.d.ts +5 -4
- package/dist/hitl/approvalsFeed.js +38 -6
- package/dist/hitl/askParkRowRouting.d.ts +4 -3
- package/dist/hitl/askParkRowRouting.js +15 -3
- package/dist/hitl/automaticAnswerVerdict.js +13 -10
- package/dist/hitl/crashConverged.d.ts +5 -1
- package/dist/hitl/decisionConflict.js +2 -1
- package/dist/hitl/decisionRefusal.js +1 -1
- package/dist/hitl/decisionSend.d.ts +1 -0
- package/dist/hitl/decisionSend.js +14 -2
- package/dist/hitl/frameRouter.js +4 -5
- package/dist/hitl/hitlBridge.d.ts +1 -1
- package/dist/hitl/hitlBridge.js +26 -5
- package/dist/hitl/livePendingAsk.d.ts +5 -0
- package/dist/hitl/livePendingAsk.js +6 -1
- package/dist/hitl/planReviewCard.d.ts +2 -0
- package/dist/hitl/planReviewCard.js +21 -0
- package/dist/hitl/planReviewWire.d.ts +2 -2
- package/dist/hitl/planReviewWire.js +101 -36
- package/dist/hitl/sessionAllowRoute.d.ts +2 -0
- package/dist/hitl/sessionAllowRoute.js +7 -0
- package/dist/hitl/sessionPolicyWire.d.ts +1 -7
- package/dist/hitl/sessionPolicyWire.js +3 -43
- package/dist/hitl/subagentSource.d.ts +1 -0
- package/dist/hitl/subagentSource.js +13 -0
- package/dist/hitl/toolApprovalWire.d.ts +3 -2
- package/dist/hitl/toolApprovalWire.js +79 -37
- package/dist/hostSettingsSource.d.ts +4 -1
- package/dist/hostSettingsSource.js +11 -6
- package/dist/hostTurnSettings.d.ts +4 -5
- package/dist/hostTurnSettings.js +80 -19
- package/dist/i18n/messages.en.d.ts +35 -12
- package/dist/i18n/messages.en.js +36 -13
- package/dist/i18n/messages.zh.js +38 -13
- package/dist/index.d.ts +21 -2
- package/dist/index.js +11 -2
- package/dist/liveInitToolFace.js +0 -1
- package/dist/liveModelCatalog.d.ts +15 -0
- package/dist/liveModelCatalog.js +40 -0
- package/dist/mcpWireCaps.js +1 -1
- package/dist/modelCapabilityProbe.js +66 -33
- package/dist/ownKey.d.ts +0 -1
- package/dist/ownKey.js +0 -2
- package/dist/permissionsDeclaration.d.ts +2 -2
- package/dist/permissionsDeclaration.js +12 -14
- package/dist/projectSeed.d.ts +39 -0
- package/dist/projectSeed.js +80 -0
- package/dist/promptDelivery.js +1 -1
- package/dist/replayBody.d.ts +7 -6
- package/dist/replayBody.js +62 -7
- package/dist/request/taskRequest.js +3 -4
- package/dist/ruleCompileVerdict.d.ts +0 -2
- package/dist/ruleCompileVerdict.js +0 -31
- package/dist/ruleRefusalSentence.js +0 -3
- package/dist/runReattachPolicy.d.ts +3 -0
- package/dist/runReattachPolicy.js +15 -2
- package/dist/runTerminal.js +30 -8
- package/dist/sandboxWire.d.ts +0 -3
- package/dist/sandboxWire.js +0 -9
- package/dist/seam.d.ts +14 -7
- package/dist/seam.js +12 -0
- package/dist/seatContract.d.ts +57 -7
- package/dist/seatContract.js +132 -3
- package/dist/serverSupportRange.d.ts +18 -0
- package/dist/serverSupportRange.js +25 -0
- package/dist/sessionEngine.d.ts +156 -0
- package/dist/sessionEngine.js +464 -0
- package/dist/sessionMap.d.ts +11 -1
- package/dist/sessionMap.js +35 -9
- package/dist/sessionMemorySync.d.ts +31 -0
- package/dist/sessionMemorySync.js +95 -0
- package/dist/sessionRowTag.d.ts +4 -0
- package/dist/sessionRowTag.js +18 -0
- package/dist/settingsLayerAdmission.d.ts +1 -0
- package/dist/settingsLayerAdmission.js +4 -1
- package/dist/toolResult.js +1 -1
- package/dist/wireRefusalCopy.js +2 -6
- package/docs/INTEGRATION-CLIENTS.md +1022 -16
- package/package.json +5 -5
|
@@ -2,22 +2,21 @@ import { publishQuestionFrame, publishQuestionFrameFor, registerLocalQuestionRes
|
|
|
2
2
|
import { hostLog } from '../host.js';
|
|
3
3
|
import { abortableSleep, settleWithinDeadline } from '../abortableSleep.js';
|
|
4
4
|
import { clientHasVerbs, engineWireFor, noAnswerKindOf, sendOnce } from '../engineWireFor.js';
|
|
5
|
-
import { engineCapTrue } from '../engineCapsCache.js';
|
|
6
5
|
import { engineWireTarget } from '../engineWireTarget.js';
|
|
7
6
|
import { enqueuePlanReviewOutcome } from '../notifications.js';
|
|
8
7
|
import { corruptStoredRowContent, corruptStoredRowWhere } from '../wireErrorTriage.js';
|
|
9
8
|
import { decideFailureNote } from '../decideFailureNote.js';
|
|
10
|
-
import { wireFailureTextsOf } from '../wireFailureShape.js';
|
|
9
|
+
import { readThrownExtra, wireFailureTextsOf } from '../wireFailureShape.js';
|
|
11
10
|
import { appendOwnElement, readOwnDataNonEmptyString, readOwnDataValue } from '../ownKey.js';
|
|
12
11
|
import { checkpointUnsupportedVersionContent, checkpointUnsupportedVersionFromError, resumeContextUnavailableContent, resumeContextUnavailableFromError } from '../resumeRefusalCopy.js';
|
|
13
|
-
import { REQUEST_FIELD_CONFLICT } from '../engineErrorCodes.js';
|
|
12
|
+
import { APPROVAL_STALE, REQUEST_FIELD_CONFLICT } from '../engineErrorCodes.js';
|
|
14
13
|
import { refusalDeliveryOf } from './decisionRefusal.js';
|
|
15
14
|
import { directFailureVerdictOf, directSafetyStopOf } from './approvalResolution.js';
|
|
16
15
|
import { __resetDecisionLedgerForTests, decisionInFlightOf, decisionKeyInFlight, decisionLedgerKeysOf, decisionOpsWithKey, decisionReadMark, dropDecisionOp, latestDecisionHandedMark, markLatestDecisionNotApplied, openDecisionOp, planDecisionKey, readMarkOf, settleDecisionOp, settleDecisionOpsOnGateLeft, } from './decisionLedger.js';
|
|
17
16
|
import { denyReasonForWire, isPlanReviewModeAfter } from './hitlBridge.js';
|
|
18
17
|
import { freshPlanReviewQuestionId, planReviewQuestionId, REOPEN_ID_TAIL, taskIdFromPlanReviewQuestionId } from './gateIdentity.js';
|
|
19
18
|
import { isPlanReviewPark, mintPlanReviewCardFacts, notePlanReviewCardIssued, pausedCheckpointIdOf, planReviewCardFactsFrom, planReviewFactsCurrent, registeredPlanReviewCardFacts, samePlanReviewCardFacts, unknownBody, _resetPlanReviewRunsForTest, } from '../planReviewCardFacts.js';
|
|
20
|
-
import { planReviewAskAgainKey, planReviewCardNoteRef, planReviewUnreadableNoteRef, takePlanReviewFacts, _resetPlanReviewFactsFlightsForTest, } from './planReviewCard.js';
|
|
19
|
+
import { planReviewAskAgainKey, planReviewCardNoteRef, planReviewDecisionReasonRef, planReviewUnreadableNoteRef, takePlanReviewFacts, _resetPlanReviewFactsFlightsForTest, } from './planReviewCard.js';
|
|
21
20
|
import { EN_MESSAGES } from '../i18n/messages.en.js';
|
|
22
21
|
import { msg, renderMessage, DATA_FACE_LANGUAGE } from '../i18n/translate.js';
|
|
23
22
|
import { notePlanReviewAnswered, notePlanReviewAnsweredFor, planReviewArmedKeyFor, waitForGateArmedFor, wasGateArmedFor, } from './armedGateRegistry.js';
|
|
@@ -183,6 +182,7 @@ const armedPlanReviews = new Map();
|
|
|
183
182
|
const offeredModeByQuestionId = new Map();
|
|
184
183
|
const OFFERED_MODE_LEDGER_MAX = 64;
|
|
185
184
|
export function _resetArmedPlanReviewsForTest() {
|
|
185
|
+
engineRefusedGateIds.clear();
|
|
186
186
|
armedPlanReviews.clear();
|
|
187
187
|
offeredModeByQuestionId.clear();
|
|
188
188
|
planReviewDismissalsByKey.clear();
|
|
@@ -519,7 +519,7 @@ export function armPlanReviewApproval(result, sessionKey, opts) {
|
|
|
519
519
|
const cardDelivery = cardReason !== undefined ? { ...delivery, reason: cardReason } : delivery;
|
|
520
520
|
notePlanReviewAnswered(questionId);
|
|
521
521
|
hostLog('debug', `planReviewWire: arm responder delivering ${decided} for task ${taskId} (path=arm)`);
|
|
522
|
-
void decidePlanReviewFor(taskId, decided, modeAfter, cardDelivery,
|
|
522
|
+
void decidePlanReviewFor(taskId, decided, modeAfter, cardDelivery, facts);
|
|
523
523
|
return { ok: true };
|
|
524
524
|
};
|
|
525
525
|
unregister = registerLocalQuestionResponder(questionId, responder);
|
|
@@ -534,7 +534,7 @@ export function planReviewOutcomeTail(taskId, decision, effect, cause) {
|
|
|
534
534
|
switch (effect) {
|
|
535
535
|
case 'took_effect':
|
|
536
536
|
return decision === 'approve'
|
|
537
|
-
? `
|
|
537
|
+
? `The result of the resumed task ${JSON.stringify(taskId)} arrives in its completion notification; when it arrives, give the user a brief report of what was done.`
|
|
538
538
|
: 'Briefly acknowledge to the user that the plan was rejected and continue planning.';
|
|
539
539
|
case 'advanced':
|
|
540
540
|
return `Tell the user the ${decision} went through and that another approval is now waiting for them; do not report the task as finished.`;
|
|
@@ -543,9 +543,9 @@ export function planReviewOutcomeTail(taskId, decision, effect, cause) {
|
|
|
543
543
|
case 'not_sent':
|
|
544
544
|
return `Nothing was sent to the engine. Do not tell the user the plan was ${decision === 'approve' ? 'approved' : 'rejected'}; the plan is still waiting for a decision.`;
|
|
545
545
|
case 'not_applied':
|
|
546
|
-
return `The ${decision} did not take effect. Do not tell the user the plan was ${decision === 'approve' ? 'approved' : 'rejected'};
|
|
546
|
+
return `The ${decision} did not take effect. Do not tell the user the plan was ${decision === 'approve' ? 'approved' : 'rejected'}; find out where task ${JSON.stringify(taskId)} stands (the background task list or its next notification shows it) before saying anything about the plan.`;
|
|
547
547
|
case 'unconfirmed':
|
|
548
|
-
return `Do not state the result as fact: tell the user the ${decision} was submitted but its effect could not be confirmed;
|
|
548
|
+
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.`;
|
|
549
549
|
}
|
|
550
550
|
}
|
|
551
551
|
function planReviewDecisionTimedOutOutcome(decision) {
|
|
@@ -632,12 +632,39 @@ export function latestPlanReviewDecisionMark(taskId) {
|
|
|
632
632
|
export function __resetPlanReviewDecisionLatchForTests() {
|
|
633
633
|
__resetDecisionLedgerForTests('plan_review');
|
|
634
634
|
}
|
|
635
|
+
const engineRefusedGateIds = new Set();
|
|
636
|
+
const ENGINE_REFUSED_GATE_IDS_MAX = 64;
|
|
637
|
+
function noteEngineRefusedGate(id) {
|
|
638
|
+
if (engineRefusedGateIds.has(id))
|
|
639
|
+
return;
|
|
640
|
+
engineRefusedGateIds.add(id);
|
|
641
|
+
while (engineRefusedGateIds.size > ENGINE_REFUSED_GATE_IDS_MAX) {
|
|
642
|
+
const oldest = engineRefusedGateIds.values().next();
|
|
643
|
+
if (oldest.done === true)
|
|
644
|
+
break;
|
|
645
|
+
engineRefusedGateIds.delete(oldest.value);
|
|
646
|
+
}
|
|
647
|
+
}
|
|
648
|
+
function planReviewDecisionReasonSegment(reason, card) {
|
|
649
|
+
if (reason === undefined)
|
|
650
|
+
return '';
|
|
651
|
+
try {
|
|
652
|
+
return ` ${renderMessage(planReviewDecisionReasonRef(reason, card), DATA_FACE_LANGUAGE)}`;
|
|
653
|
+
}
|
|
654
|
+
catch {
|
|
655
|
+
return '';
|
|
656
|
+
}
|
|
657
|
+
}
|
|
635
658
|
export async function decidePlanReview(taskId, decision, permissionModeAfter, opts) {
|
|
636
|
-
return decidePlanReviewFor(taskId, decision, permissionModeAfter, opts,
|
|
659
|
+
return decidePlanReviewFor(taskId, decision, permissionModeAfter, opts, decisionCardOf(taskId, opts));
|
|
637
660
|
}
|
|
638
|
-
function
|
|
661
|
+
function decisionCardOf(taskId, opts) {
|
|
639
662
|
try {
|
|
640
|
-
|
|
663
|
+
if (opts === null || typeof opts !== 'object')
|
|
664
|
+
return undefined;
|
|
665
|
+
const raw = readOwnDataValue(opts, 'card');
|
|
666
|
+
const facts = raw !== null && typeof raw === 'object' ? planReviewCardFactsFrom(raw) : undefined;
|
|
667
|
+
return facts !== undefined && facts.taskId === taskId ? facts : undefined;
|
|
641
668
|
}
|
|
642
669
|
catch {
|
|
643
670
|
return undefined;
|
|
@@ -691,7 +718,7 @@ function askAgainReasonOf(card, opts) {
|
|
|
691
718
|
return renderMessage(msg('approval.planReview.askAgain.unknown'), DATA_FACE_LANGUAGE);
|
|
692
719
|
}
|
|
693
720
|
}
|
|
694
|
-
async function decidePlanReviewFor(taskId, decision, permissionModeAfter, opts,
|
|
721
|
+
async function decidePlanReviewFor(taskId, decision, permissionModeAfter, opts, card) {
|
|
695
722
|
if (typeof taskId === 'string' && planReviewOpsOf(taskId).some((op) => op.phase === 'posting')) {
|
|
696
723
|
hostLog('error', `planReviewWire: decidePlanReview(${decision}) for task ${taskId} REFUSED — a decision for this task is already in flight (duplicate delivery latched)`);
|
|
697
724
|
return;
|
|
@@ -724,7 +751,7 @@ async function decidePlanReviewFor(taskId, decision, permissionModeAfter, opts,
|
|
|
724
751
|
try {
|
|
725
752
|
const wireDecision = decision === 'ask_again' ? 'reject' : decision;
|
|
726
753
|
const wireOpts = decision === 'ask_again' ? { ...opts, reason: askAgainReasonOf(card, opts) } : opts;
|
|
727
|
-
await decidePlanReviewInner(taskId, wireDecision, permissionModeAfter, dispatchNo, releaseLatch, settle, wireOpts,
|
|
754
|
+
await decidePlanReviewInner(taskId, wireDecision, permissionModeAfter, dispatchNo, releaseLatch, settle, wireOpts, card, decision);
|
|
728
755
|
}
|
|
729
756
|
finally {
|
|
730
757
|
releaseLatch();
|
|
@@ -745,8 +772,11 @@ function planReviewClientsFor(resolved) {
|
|
|
745
772
|
repull: () => resolved.make({ timeoutMs: 15_000 }),
|
|
746
773
|
};
|
|
747
774
|
}
|
|
748
|
-
async function decidePlanReviewInner(taskId, decision, permissionModeAfter, dispatchNo, releaseLatch, settle, opts,
|
|
775
|
+
async function decidePlanReviewInner(taskId, decision, permissionModeAfter, dispatchNo, releaseLatch, settle, opts, card, semanticDecision) {
|
|
749
776
|
let outcome;
|
|
777
|
+
let decisionReason;
|
|
778
|
+
const gateBefore = card?.checkpointId;
|
|
779
|
+
const approvalWithheld = decision !== 'approve' ? undefined : card === undefined ? 'card_missing' : card.body.kind !== 'shown' || card.checkpointId === undefined ? 'card_withholds' : undefined;
|
|
750
780
|
let effect = 'unconfirmed';
|
|
751
781
|
let stillParkedCause;
|
|
752
782
|
let statusSeen;
|
|
@@ -758,6 +788,12 @@ async function decidePlanReviewInner(taskId, decision, permissionModeAfter, disp
|
|
|
758
788
|
effect = 'not_sent';
|
|
759
789
|
outcome = `The plan_review ${decision} could NOT be sent: the post-approval permission mode given with it is not valid (it must be "default" or "acceptEdits"). Tell the user plainly that the decision did not go through; the plan is still waiting.`;
|
|
760
790
|
}
|
|
791
|
+
else if (approvalWithheld !== undefined) {
|
|
792
|
+
hostLog('debug', `planReviewWire: not-sent decision=${decision} reason=${approvalWithheld}`);
|
|
793
|
+
effect = 'not_sent';
|
|
794
|
+
decisionReason = approvalWithheld;
|
|
795
|
+
outcome = renderMessage(msg('approval.planReview.decision.notSent', { decision }), DATA_FACE_LANGUAGE);
|
|
796
|
+
}
|
|
761
797
|
else if (resolved.kind === 'no-target') {
|
|
762
798
|
hostLog('error', `planReviewWire: ${decision} NOT sent — engineWireTarget() unavailable`);
|
|
763
799
|
effect = 'not_sent';
|
|
@@ -777,11 +813,24 @@ async function decidePlanReviewInner(taskId, decision, permissionModeAfter, disp
|
|
|
777
813
|
}
|
|
778
814
|
else {
|
|
779
815
|
const client = clients.decide;
|
|
816
|
+
const checkpointId = gateBefore !== undefined && !engineRefusedGateIds.has(gateBefore) ? gateBefore : undefined;
|
|
817
|
+
const repullRow = async () => {
|
|
818
|
+
const probe = clients.repull();
|
|
819
|
+
if (!probe)
|
|
820
|
+
return undefined;
|
|
821
|
+
if (wire !== undefined) {
|
|
822
|
+
const got = await settleWithinDeadline(PLAN_REVIEW_INJECTED_REPULL_TIMEOUT_MS, (signal) => probe.runs.get(taskId, { signal }));
|
|
823
|
+
if (got.kind === 'error')
|
|
824
|
+
throw new Error(got.detail);
|
|
825
|
+
if (got.kind === 'deadline')
|
|
826
|
+
throw new Error(`post-decide status re-pull passed its ${PLAN_REVIEW_INJECTED_REPULL_TIMEOUT_MS} ms deadline`);
|
|
827
|
+
return got.value;
|
|
828
|
+
}
|
|
829
|
+
return (await probe.runs.get(taskId));
|
|
830
|
+
};
|
|
780
831
|
try {
|
|
781
832
|
const mode = isPlanReviewModeAfter(permissionModeAfter) ? permissionModeAfter : undefined;
|
|
782
833
|
const reason = denyReasonForWire(opts?.reason, `plan-review ${taskId}`);
|
|
783
|
-
const capsBase = resolved.kind === 'target' ? resolved.target.baseUrl : resolved.capsBaseUrl;
|
|
784
|
-
const checkpointId = gateBefore !== undefined && engineCapTrue(capsBase, 'planReviewCheckpointId') ? gateBefore : undefined;
|
|
785
834
|
const reqBody = { decision };
|
|
786
835
|
if (reason !== undefined)
|
|
787
836
|
reqBody.reason = reason;
|
|
@@ -812,20 +861,8 @@ async function decidePlanReviewInner(taskId, decision, permissionModeAfter, disp
|
|
|
812
861
|
let gateAfter;
|
|
813
862
|
let gateKindAfter;
|
|
814
863
|
try {
|
|
815
|
-
const
|
|
816
|
-
if (
|
|
817
|
-
let post;
|
|
818
|
-
if (wire !== undefined) {
|
|
819
|
-
const got = await settleWithinDeadline(PLAN_REVIEW_INJECTED_REPULL_TIMEOUT_MS, (signal) => probe.runs.get(taskId, { signal }));
|
|
820
|
-
if (got.kind === 'error')
|
|
821
|
-
throw new Error(got.detail);
|
|
822
|
-
if (got.kind === 'deadline')
|
|
823
|
-
throw new Error(`post-decide status re-pull passed its ${PLAN_REVIEW_INJECTED_REPULL_TIMEOUT_MS} ms deadline`);
|
|
824
|
-
post = got.value;
|
|
825
|
-
}
|
|
826
|
-
else {
|
|
827
|
-
post = (await probe.runs.get(taskId));
|
|
828
|
-
}
|
|
864
|
+
const post = await repullRow();
|
|
865
|
+
if (post !== undefined) {
|
|
829
866
|
postStatus = bodyWordOf(post, 'status');
|
|
830
867
|
gateAfter = pausedCheckpointIdOf(post?.result);
|
|
831
868
|
gateKindAfter = pausedGateKindOf(post?.result);
|
|
@@ -898,6 +935,36 @@ async function decidePlanReviewInner(taskId, decision, permissionModeAfter, disp
|
|
|
898
935
|
`This ${decision} was not applied: the engine no longer has a pending plan review for this task — an earlier decision most likely already went through (or the review expired). ` +
|
|
899
936
|
'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.';
|
|
900
937
|
}
|
|
938
|
+
else if (status === 409 && refusedCode === APPROVAL_STALE) {
|
|
939
|
+
let untrusted = false;
|
|
940
|
+
if (checkpointId !== undefined) {
|
|
941
|
+
const bag = readThrownExtra(e);
|
|
942
|
+
const noCurrentPending = bag.state === 'object' &&
|
|
943
|
+
(() => {
|
|
944
|
+
try {
|
|
945
|
+
return Object.getOwnPropertyDescriptor(bag.value, 'currentPending') === undefined;
|
|
946
|
+
}
|
|
947
|
+
catch {
|
|
948
|
+
return false;
|
|
949
|
+
}
|
|
950
|
+
})();
|
|
951
|
+
if (noCurrentPending) {
|
|
952
|
+
try {
|
|
953
|
+
const row = await repullRow();
|
|
954
|
+
untrusted = row !== undefined && pausedCheckpointIdOf(row?.result) === checkpointId;
|
|
955
|
+
}
|
|
956
|
+
catch {
|
|
957
|
+
untrusted = false;
|
|
958
|
+
}
|
|
959
|
+
}
|
|
960
|
+
}
|
|
961
|
+
if (untrusted && checkpointId !== undefined)
|
|
962
|
+
noteEngineRefusedGate(checkpointId);
|
|
963
|
+
hostLog('debug', `planReviewWire: approval-stale decision=${decision} reason=${untrusted ? 'gate_untrusted' : 'gate_moved'}`);
|
|
964
|
+
effect = 'not_applied';
|
|
965
|
+
decisionReason = untrusted ? 'gate_untrusted' : 'gate_moved';
|
|
966
|
+
outcome = renderMessage(msg('approval.planReview.decision.refused', { decision }), DATA_FACE_LANGUAGE);
|
|
967
|
+
}
|
|
901
968
|
else if (checkpointUnsupportedVersionFromError(e) !== null) {
|
|
902
969
|
const cu = checkpointUnsupportedVersionFromError(e);
|
|
903
970
|
hostLog('debug', `planReviewWire: ${decision} → 409 checkpoint.unsupported_version (the server cannot continue this parked review in its stored form; still pending)`);
|
|
@@ -939,6 +1006,7 @@ async function decidePlanReviewInner(taskId, decision, permissionModeAfter, disp
|
|
|
939
1006
|
}
|
|
940
1007
|
}
|
|
941
1008
|
}
|
|
1009
|
+
outcome = `${outcome}${planReviewDecisionReasonSegment(decisionReason, card)}`;
|
|
942
1010
|
const tail = planReviewOutcomeTail(taskId, decision, effect, stillParkedCause);
|
|
943
1011
|
const prompt = `<plan-review-outcome>\n${outcome}\n${tail}\n</plan-review-outcome>`;
|
|
944
1012
|
const onOutcome = opts?.onOutcome;
|
|
@@ -1087,10 +1155,10 @@ function mintPlanReviewReopenCard(taskId, opts, sessionKey, receiptMs, taken) {
|
|
|
1087
1155
|
? { ...(opts.wire !== undefined ? { wire: opts.wire } : {}), ...(opts.onOutcome !== undefined ? { onOutcome: opts.onOutcome } : {}) }
|
|
1088
1156
|
: undefined;
|
|
1089
1157
|
const deliver = opts?.deliverDecision ??
|
|
1090
|
-
((tid, decision, reason) => {
|
|
1158
|
+
((tid, decision, reason, card) => {
|
|
1091
1159
|
hostLog('debug', `planReviewWire: reopen responder delivering ${decision} for task ${tid} (path=reopen-default)`);
|
|
1092
1160
|
const withReason = reason === undefined ? delivery : { ...delivery, reason };
|
|
1093
|
-
void decidePlanReviewFor(tid, decision, undefined, withReason,
|
|
1161
|
+
void decidePlanReviewFor(tid, decision, undefined, withReason, card);
|
|
1094
1162
|
});
|
|
1095
1163
|
const activeKey = `${sessionKey}\u0000${canonicalId}`;
|
|
1096
1164
|
let unregister = () => { };
|
|
@@ -1116,10 +1184,7 @@ function mintPlanReviewReopenCard(taskId, opts, sessionKey, receiptMs, taken) {
|
|
|
1116
1184
|
notePlanReviewDecisiveAnswerFor(sessionKey, taskId);
|
|
1117
1185
|
notePlanReviewAnsweredFor(sessionKey, questionId);
|
|
1118
1186
|
try {
|
|
1119
|
-
|
|
1120
|
-
await deliver(taskId, decided);
|
|
1121
|
-
else
|
|
1122
|
-
await deliver(taskId, decided, cardReason);
|
|
1187
|
+
await deliver(taskId, decided, cardReason, facts);
|
|
1123
1188
|
}
|
|
1124
1189
|
catch (e) {
|
|
1125
1190
|
hostLog('error', `planReviewWire: reopen decision delivery threw for task ${taskId}: ${String(e)}`);
|
|
@@ -128,17 +128,11 @@ export interface SessionPolicyInertEntryRefusal {
|
|
|
128
128
|
export declare function tightenSessionPolicy(facade: SessionPolicyFacade, req: SessionPolicyTightenRequest, opts?: {
|
|
129
129
|
signal?: AbortSignal;
|
|
130
130
|
}): Promise<SessionPolicyTightenOutcome>;
|
|
131
|
-
export interface SessionPolicyTightenFacts {
|
|
132
|
-
readonly agentsStartedBeforeFirstRule?: number | undefined;
|
|
133
|
-
readonly workflowsRunning?: number | undefined;
|
|
134
|
-
readonly untrackedBackgroundItems?: number | undefined;
|
|
135
|
-
readonly inFlightUnreadable?: true | undefined;
|
|
136
|
-
}
|
|
137
131
|
export interface SessionPolicyDeploymentFacts {
|
|
138
132
|
readonly operator?: 'present' | 'absent';
|
|
139
133
|
readonly identityAttach?: 'available' | 'unavailable';
|
|
140
134
|
}
|
|
141
|
-
export declare function sessionPolicyTightenNotice(outcome: SessionPolicyTightenOutcome, lang?: UiLanguage,
|
|
135
|
+
export declare function sessionPolicyTightenNotice(outcome: SessionPolicyTightenOutcome, lang?: UiLanguage, deployment?: SessionPolicyDeploymentFacts): string;
|
|
142
136
|
export declare function sessionPolicyReadNotice(outcome: Exclude<SessionPolicyReadOutcome, {
|
|
143
137
|
kind: 'present';
|
|
144
138
|
}> | {
|
|
@@ -414,29 +414,6 @@ export async function tightenSessionPolicy(facade, req, opts) {
|
|
|
414
414
|
return { kind: 'conflict', rev: second.retryAtRev };
|
|
415
415
|
}
|
|
416
416
|
const earlierGrammarNote = (lang) => renderMessage(msg('rules.sessionPolicy.tighten.earlierGrammar'), lang);
|
|
417
|
-
function factSlot(facts, key) {
|
|
418
|
-
try {
|
|
419
|
-
return ownRead(facts, key);
|
|
420
|
-
}
|
|
421
|
-
catch {
|
|
422
|
-
return undefined;
|
|
423
|
-
}
|
|
424
|
-
}
|
|
425
|
-
function factCount(facts, key) {
|
|
426
|
-
const n = factSlot(facts, key);
|
|
427
|
-
return typeof n === 'number' && Number.isSafeInteger(n) && n >= 1 ? n : undefined;
|
|
428
|
-
}
|
|
429
|
-
function firstRuleWorkFactsOf(facts) {
|
|
430
|
-
const none = { agents: undefined, workflows: undefined, untracked: undefined, unreadable: false };
|
|
431
|
-
if (typeof facts !== 'object' || facts === null)
|
|
432
|
-
return none;
|
|
433
|
-
return {
|
|
434
|
-
agents: factCount(facts, 'agentsStartedBeforeFirstRule'),
|
|
435
|
-
workflows: factCount(facts, 'workflowsRunning'),
|
|
436
|
-
untracked: factCount(facts, 'untrackedBackgroundItems'),
|
|
437
|
-
unreadable: factSlot(facts, 'inFlightUnreadable') === true,
|
|
438
|
-
};
|
|
439
|
-
}
|
|
440
417
|
function deploymentHasNoOperator(d) {
|
|
441
418
|
try {
|
|
442
419
|
return typeof d === 'object' && d !== null && ownRead(d, 'operator') === 'absent';
|
|
@@ -453,28 +430,11 @@ function deploymentHasNoIdentity(d) {
|
|
|
453
430
|
return false;
|
|
454
431
|
}
|
|
455
432
|
}
|
|
456
|
-
export function sessionPolicyTightenNotice(outcome, lang,
|
|
433
|
+
export function sessionPolicyTightenNotice(outcome, lang, deployment) {
|
|
457
434
|
const say = (key, params) => renderMessage(msg(key, params), lang);
|
|
458
435
|
switch (outcome.kind) {
|
|
459
|
-
case 'written':
|
|
460
|
-
|
|
461
|
-
const w = firstRuleWorkFactsOf(facts);
|
|
462
|
-
const unseen = msg('rules.sessionPolicy.tighten.writtenBeforeFirstRule.unseen');
|
|
463
|
-
if (w.unreadable)
|
|
464
|
-
return `${head} ${renderMessage(msg('rules.sessionPolicy.tighten.writtenBeforeFirstRule.unreadable'), lang)} ${renderMessage(unseen, lang)}`;
|
|
465
|
-
const parts = [];
|
|
466
|
-
if (w.agents !== undefined)
|
|
467
|
-
appendOwnElement(parts, msg(w.agents === 1 ? 'rules.sessionPolicy.tighten.writtenBeforeFirstRule.agents.one' : 'rules.sessionPolicy.tighten.writtenBeforeFirstRule.agents.many', { n: w.agents }));
|
|
468
|
-
if (w.workflows !== undefined)
|
|
469
|
-
appendOwnElement(parts, msg(w.workflows === 1 ? 'rules.sessionPolicy.tighten.writtenBeforeFirstRule.workflows.one' : 'rules.sessionPolicy.tighten.writtenBeforeFirstRule.workflows.many', { n: w.workflows }));
|
|
470
|
-
if (w.untracked !== undefined)
|
|
471
|
-
appendOwnElement(parts, msg(w.untracked === 1 ? 'rules.sessionPolicy.tighten.writtenBeforeFirstRule.untracked.one' : 'rules.sessionPolicy.tighten.writtenBeforeFirstRule.untracked.many', { n: w.untracked }));
|
|
472
|
-
const [p0, p1, p2] = parts;
|
|
473
|
-
if (p0 === undefined)
|
|
474
|
-
return head;
|
|
475
|
-
const list = p1 === undefined ? p0 : p2 === undefined ? msg('rules.sessionPolicy.tighten.writtenBeforeFirstRule.list.two', { a: p0, b: p1 }) : msg('rules.sessionPolicy.tighten.writtenBeforeFirstRule.list.three', { a: p0, b: p1, c: p2 });
|
|
476
|
-
return `${head} ${renderMessage(msg('rules.sessionPolicy.tighten.writtenBeforeFirstRule.counts', { list }), lang)} ${renderMessage(unseen, lang)}`;
|
|
477
|
-
}
|
|
436
|
+
case 'written':
|
|
437
|
+
return say('rules.sessionPolicy.tighten.written', { rev: outcome.rev });
|
|
478
438
|
case 'conflict':
|
|
479
439
|
return say('rules.sessionPolicy.tighten.conflict');
|
|
480
440
|
case 'loosen_forbidden':
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function subagentSourceSessionOf(carrier: object | null | undefined): string | undefined;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { slotReadOf } from '../wireFailureShape.js';
|
|
2
|
+
const nonEmpty = (v) => (typeof v === 'string' && v !== '' ? v : undefined);
|
|
3
|
+
export function subagentSourceSessionOf(carrier) {
|
|
4
|
+
if (typeof carrier !== 'object' || carrier === null)
|
|
5
|
+
return undefined;
|
|
6
|
+
const current = slotReadOf(carrier, 'sourceSessionId');
|
|
7
|
+
if (current.presence === 'present')
|
|
8
|
+
return nonEmpty(current.value);
|
|
9
|
+
if (current.presence !== 'absent')
|
|
10
|
+
return undefined;
|
|
11
|
+
const retired = slotReadOf(carrier, 'sourceTaskId');
|
|
12
|
+
return retired.presence === 'present' ? nonEmpty(retired.value) : undefined;
|
|
13
|
+
}
|
|
@@ -163,7 +163,7 @@ export declare function approvalCardPortFor(sessionKey: string): ApprovalCardPor
|
|
|
163
163
|
export declare function __resetSettledApprovalsForTests(): void;
|
|
164
164
|
export declare function _resetApprovalCardPortForTest(): void;
|
|
165
165
|
export declare function surfaceApprovalCard(req: ApprovalCardRequest): Promise<ApprovalCardDecision>;
|
|
166
|
-
declare const REMEMBER_REFUSAL_CODES: readonly ["remember_not_in_proof", "
|
|
166
|
+
declare const REMEMBER_REFUSAL_CODES: readonly ["remember_not_in_proof", "capability.store_required", "decide.workflow_remember_unsupported", "decide.parked_remember_unsupported"];
|
|
167
167
|
export type RememberRefusalCode = (typeof REMEMBER_REFUSAL_CODES)[number];
|
|
168
168
|
export declare function rememberRefusalOf(error: unknown): RememberRefusalCode | undefined;
|
|
169
169
|
export declare function surfaceFsApprovalAndDecide(deps: FsApprovalWireDeps, taskId: string, argsByCall: Map<string, unknown>, signal?: AbortSignal, parkGatedCallId?: string, parkToolName?: string): Promise<ApprovalResolution>;
|
|
@@ -179,7 +179,7 @@ export interface ReadRootCandidateView {
|
|
|
179
179
|
covers?: 'exact';
|
|
180
180
|
}
|
|
181
181
|
export type ToolApprovalDelegation = ApprovalCardDelegation;
|
|
182
|
-
export declare const TOOL_APPROVAL_FRAME_KEYS_MIRROR: readonly ["type", "approvalId", "toolCallId", "toolName", "sourceTaskId", "fromSubagent", "sourceAgentName", "message", "args", "argsOmitted", "governanceForced", "ruleOffers", "ruleOffersAbsence", "denialLimitFallback", "readRootCandidate", "mandated", "mandate", "origin", "approvalCard", "askClass", "classes", "inputHasBidi", "parked", "persistedRuleShadowed", "allowRuleShadowed", "probeCause", "probeReason", "rememberRefused", "boundGrantDigest", "boundGrantAbsence", "boundGrants", "classifierNoRuling", "ruleEvidence", "requiresRealApproval", "expiresInMs", "expiresAtMs", "serverNowMs", "delegation", "outcome", "cause", "requestedBy"];
|
|
182
|
+
export declare const TOOL_APPROVAL_FRAME_KEYS_MIRROR: readonly ["type", "approvalId", "toolCallId", "toolName", "sourceTaskId", "sourceSessionId", "agentId", "askReasons", "fromSubagent", "sourceAgentName", "message", "args", "argsOmitted", "governanceForced", "ruleOffers", "ruleOffersAbsence", "denialLimitFallback", "readRootCandidate", "mandated", "mandate", "origin", "approvalCard", "askClass", "classes", "inputHasBidi", "parked", "persistedRuleShadowed", "allowRuleShadowed", "probeCause", "probeReason", "rememberRefused", "boundGrantDigest", "boundGrantAbsence", "boundGrants", "classifierNoRuling", "ruleEvidence", "requiresRealApproval", "expiresInMs", "expiresAtMs", "serverNowMs", "delegation", "outcome", "cause", "requestedBy"];
|
|
183
183
|
export interface ApprovalMandatedFacts {
|
|
184
184
|
readonly mandated?: true;
|
|
185
185
|
readonly ruleOffersAbsence?: string;
|
|
@@ -283,6 +283,7 @@ export interface ApprovalCardFrameFactsOptions {
|
|
|
283
283
|
readonly argsUnavailable?: true;
|
|
284
284
|
}
|
|
285
285
|
export declare function approvalCardFactsOfV1(card: object | null | undefined): Readonly<ApprovalCardV1Facts> | undefined;
|
|
286
|
+
export declare function approvalCardFactsOfV1Frame(frame: object | null | undefined): Readonly<ApprovalCardFacts> | undefined;
|
|
286
287
|
export declare function approvalCardFactsOfFrame(frame: object | null | undefined, opts?: ApprovalCardFrameFactsOptions | null): Readonly<ApprovalCardFacts> | undefined;
|
|
287
288
|
export declare function approvalCardFactsOfGeneric(card: object | null | undefined): Readonly<ApprovalCardFacts> | undefined;
|
|
288
289
|
export interface ToolApprovalFrameLaneOpts {
|