@sema-agent/client-core 0.86.0 → 0.87.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 +55 -0
- package/README.md +6 -4
- package/dist/adapt/arms.js +9 -0
- package/dist/adapter/downstream/terminalToSdkResult.js +4 -1
- package/dist/adapter/runStream.js +75 -2
- package/dist/adapter/types.d.ts +3 -0
- package/dist/decideReceipt.d.ts +1 -1
- package/dist/decideReceipt.js +1 -1
- package/dist/detachWire.d.ts +6 -1
- package/dist/detachWire.js +22 -1
- package/dist/hitl/crashConverged.d.ts +1 -0
- package/dist/hitl/crashConverged.js +17 -2
- package/dist/hitl/hitlBridge.d.ts +4 -0
- package/dist/hitl/hitlBridge.js +6 -0
- package/dist/hitl/parkResolver.js +17 -12
- package/dist/hitl/suspendedReopen.d.ts +7 -0
- package/dist/hitl/suspendedReopen.js +38 -7
- package/dist/hitl/toolApprovalWire.js +4 -5
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/resumeRefusalCopy.d.ts +2 -7
- package/dist/resumeRefusalCopy.js +2 -24
- package/dist/rewindArchiveCapability.js +5 -0
- package/dist/seam.d.ts +11 -1
- package/dist/seam.js +7 -0
- package/dist/toolHistoryMismatch.d.ts +63 -0
- package/dist/toolHistoryMismatch.js +425 -0
- package/dist/workflowClient.d.ts +3 -2
- package/dist/workflowClient.js +3 -3
- package/docs/INTEGRATION-CLIENTS.md +232 -8
- package/package.json +1 -1
package/dist/detachWire.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { hostEnv } from './hostEnv.js';
|
|
2
2
|
import { envFlagOff } from './envFlag.js';
|
|
3
3
|
import { versionSupportsLimits } from './limitsWire.js';
|
|
4
|
+
import { resolveWireAuth } from './engineWireSdk.js';
|
|
4
5
|
export const DETACH_HEADER = 'x-detach-on-disconnect';
|
|
5
6
|
export const HEADLESS_DETACH_ENV = 'SEMA_HEADLESS_DETACH';
|
|
6
7
|
export const DETACH_WIRE_MIN_ENGINE = [1, 196, 0];
|
|
@@ -80,6 +81,17 @@ let cancelArm = null;
|
|
|
80
81
|
export function armDetachCancel(arm) {
|
|
81
82
|
cancelArm = arm;
|
|
82
83
|
}
|
|
84
|
+
function armAuthNow(baseUrl, source) {
|
|
85
|
+
let token;
|
|
86
|
+
try {
|
|
87
|
+
const v = source();
|
|
88
|
+
token = typeof v === 'string' ? v : undefined;
|
|
89
|
+
}
|
|
90
|
+
catch {
|
|
91
|
+
token = undefined;
|
|
92
|
+
}
|
|
93
|
+
return resolveWireAuth(baseUrl, token);
|
|
94
|
+
}
|
|
83
95
|
export function isDetachArmed() {
|
|
84
96
|
return cancelArm !== null;
|
|
85
97
|
}
|
|
@@ -87,7 +99,16 @@ export function detachedTaskId() {
|
|
|
87
99
|
return cancelArm?.getTaskId() ?? null;
|
|
88
100
|
}
|
|
89
101
|
export function detachCancelArm() {
|
|
90
|
-
|
|
102
|
+
const arm = cancelArm;
|
|
103
|
+
if (arm === null)
|
|
104
|
+
return null;
|
|
105
|
+
const source = arm.authToken;
|
|
106
|
+
if (typeof source === 'function') {
|
|
107
|
+
const view = Object.defineProperties(Object.create(Object.getPrototypeOf(arm)), Object.getOwnPropertyDescriptors(arm));
|
|
108
|
+
Object.defineProperty(view, 'authToken', { value: armAuthNow(arm.baseUrl, source), enumerable: true, writable: true, configurable: true });
|
|
109
|
+
return view;
|
|
110
|
+
}
|
|
111
|
+
return arm;
|
|
91
112
|
}
|
|
92
113
|
export function _resetDetachWireForTest() {
|
|
93
114
|
cancelArm = null;
|
|
@@ -14,6 +14,7 @@ export interface CrashConvergedRow {
|
|
|
14
14
|
readonly decidedAtMs?: number;
|
|
15
15
|
readonly convergedAtMs: number;
|
|
16
16
|
readonly resumeSafe: boolean;
|
|
17
|
+
readonly needsHumanReason?: 'approved_then_interrupted' | 'pending_unsafe' | 'unstable_row' | 'contradictory';
|
|
17
18
|
}
|
|
18
19
|
export interface ApprovalsListEnvelope {
|
|
19
20
|
pending: PendingCheckpoint[];
|
|
@@ -71,6 +71,15 @@ function readCrashConvergedRow(v) {
|
|
|
71
71
|
return null;
|
|
72
72
|
}
|
|
73
73
|
}
|
|
74
|
+
function needsHumanReasonOf(reading) {
|
|
75
|
+
if (reading.unstable)
|
|
76
|
+
return 'unstable_row';
|
|
77
|
+
if (reading.orphanState === 'pending' ? reading.hasApprovalEvidence : reading.resumeSafe === true)
|
|
78
|
+
return 'contradictory';
|
|
79
|
+
if (reading.orphanState === 'decided')
|
|
80
|
+
return 'approved_then_interrupted';
|
|
81
|
+
return 'pending_unsafe';
|
|
82
|
+
}
|
|
74
83
|
export function projectCrashConverged(env) {
|
|
75
84
|
const resumeSafe = [];
|
|
76
85
|
const needsHuman = [];
|
|
@@ -96,10 +105,16 @@ export function projectCrashConverged(env) {
|
|
|
96
105
|
}
|
|
97
106
|
const row = reading.row;
|
|
98
107
|
const consistentlyUnexecuted = reading.orphanState === 'pending' && !reading.hasApprovalEvidence;
|
|
99
|
-
if (!reading.unstable && consistentlyUnexecuted && reading.resumeSafe === true)
|
|
108
|
+
if (!reading.unstable && consistentlyUnexecuted && reading.resumeSafe === true) {
|
|
109
|
+
Reflect.deleteProperty(row, 'needsHumanReason');
|
|
100
110
|
resumeSafe.push(row);
|
|
101
|
-
|
|
111
|
+
}
|
|
112
|
+
else {
|
|
113
|
+
Object.defineProperty(row, 'needsHumanReason', {
|
|
114
|
+
value: needsHumanReasonOf(reading), enumerable: true, writable: true, configurable: true,
|
|
115
|
+
});
|
|
102
116
|
needsHuman.push(row);
|
|
117
|
+
}
|
|
103
118
|
}
|
|
104
119
|
}
|
|
105
120
|
catch {
|
|
@@ -47,6 +47,10 @@ export type PlanReviewModeAfter = (typeof PLAN_REVIEW_MODE_AFTER_WORDS)[number];
|
|
|
47
47
|
export declare function isPlanReviewModeAfter(v: string | undefined): v is PlanReviewModeAfter;
|
|
48
48
|
export type HitlSafetyCode = 'binding_mismatch' | 'no_pending' | 'wrong_gate' | 'bad_plan_edit' | 'bad_plan_mode' | 'empty_answer';
|
|
49
49
|
export type HitlFailureStage = 'fetch' | 'input' | 'card' | 'decide' | 'orchestration';
|
|
50
|
+
export declare const NO_APPROVAL_CARD_PORT_REASON = "no approval card port installed (print/non-REPL mode, or host forgot installApprovalCardPort)";
|
|
51
|
+
export declare const APPROVAL_CARD_PORT_KEY_MISMATCH_REASON = "no approval card port installed for the default session key, but a non-default sessionKey has one \u2014 likely a keyed-install/default-read mismatch (installApprovalCardPortFor vs the zero-arg default-key read), not a missing install";
|
|
52
|
+
export declare const NO_QUESTION_OVERLAY_REASON = "no question overlay mounted (print/non-REPL mode)";
|
|
53
|
+
export declare function isNoDecisionSurfaceReason(reason: string): boolean;
|
|
50
54
|
export interface HitlDecideDenyOutcome {
|
|
51
55
|
decision: 'deny';
|
|
52
56
|
reason?: string | undefined;
|
package/dist/hitl/hitlBridge.js
CHANGED
|
@@ -25,6 +25,12 @@ export const PLAN_REVIEW_MODE_AFTER_WORDS = Object.freeze(['default', 'acceptEdi
|
|
|
25
25
|
export function isPlanReviewModeAfter(v) {
|
|
26
26
|
return v !== undefined && PLAN_REVIEW_MODE_AFTER_WORDS.includes(v);
|
|
27
27
|
}
|
|
28
|
+
export const NO_APPROVAL_CARD_PORT_REASON = 'no approval card port installed (print/non-REPL mode, or host forgot installApprovalCardPort)';
|
|
29
|
+
export const APPROVAL_CARD_PORT_KEY_MISMATCH_REASON = 'no approval card port installed for the default session key, but a non-default sessionKey has one — likely a keyed-install/default-read mismatch (installApprovalCardPortFor vs the zero-arg default-key read), not a missing install';
|
|
30
|
+
export const NO_QUESTION_OVERLAY_REASON = 'no question overlay mounted (print/non-REPL mode)';
|
|
31
|
+
export function isNoDecisionSurfaceReason(reason) {
|
|
32
|
+
return reason === NO_APPROVAL_CARD_PORT_REASON || reason === APPROVAL_CARD_PORT_KEY_MISMATCH_REASON || reason === NO_QUESTION_OVERLAY_REASON;
|
|
33
|
+
}
|
|
28
34
|
export class HitlSafetyError extends Error {
|
|
29
35
|
code;
|
|
30
36
|
constructor(message, code) {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { putOwnKey } from '../ownKey.js';
|
|
2
|
-
import { DecideTransportRetryExhaustedError, HitlBridge, HitlSafetyError, findPendingForTask, readDecideCurrentPending, readWireErrorCode, } from './hitlBridge.js';
|
|
2
|
+
import { DecideTransportRetryExhaustedError, HitlBridge, HitlSafetyError, NO_QUESTION_OVERLAY_REASON, findPendingForTask, isNoDecisionSurfaceReason, readDecideCurrentPending, readWireErrorCode, } from './hitlBridge.js';
|
|
3
3
|
import { PARKED_RESUME_STARTUP_FAILED, RESUME_POLICY_FOLD_CODE, isDecideParkMovedCode } from '../engineErrorCodes.js';
|
|
4
4
|
import { publishQuestionFrame, registerLocalQuestionResponder, hasQuestionOverlay, } from '../liveQuestionStore.js';
|
|
5
5
|
import { hostLog } from '../host.js';
|
|
@@ -75,7 +75,7 @@ export function toAnsweredOutput(questions, answer) {
|
|
|
75
75
|
}
|
|
76
76
|
async function surfaceGateAndDecide(deps, taskId, askArgsByCall, signal, parkGatedCallId, onPresented) {
|
|
77
77
|
if (!hasQuestionOverlay()) {
|
|
78
|
-
return { kind: 'failed', stage: 'card', reason:
|
|
78
|
+
return { kind: 'failed', stage: 'card', reason: NO_QUESTION_OVERLAY_REASON };
|
|
79
79
|
}
|
|
80
80
|
const found = await findPendingForTask(deps.client, taskId, isAskTool, signal ? { signal } : undefined, parkGatedCallId, (row) => isAskTool(typeof row.toolName === 'string' ? row.toolName : undefined));
|
|
81
81
|
if (!found.ok)
|
|
@@ -301,7 +301,8 @@ export async function resolvePark(park, ctx) {
|
|
|
301
301
|
: outcome.kind === 'retracted'
|
|
302
302
|
? 'the approval card was retracted by the host without a decision (nothing was sent; the run stays parked and remains decidable elsewhere)'
|
|
303
303
|
: undefined;
|
|
304
|
-
|
|
304
|
+
const noDecisionSurface = outcome.kind === 'failed' && outcome.stage === 'card' && isNoDecisionSurfaceReason(outcome.reason);
|
|
305
|
+
return { kind: 'failsoft', events: failsoftEvents(park, ctx, outcome.kind === 'aborted', failedReason, undefined, noDecisionSurface) };
|
|
305
306
|
}
|
|
306
307
|
led.dropHeldForDecidedPark(outcome.gatedCallId, park.gatedCallId);
|
|
307
308
|
if (outcome.gatedCallId) {
|
|
@@ -324,7 +325,15 @@ export async function resolvePark(park, ctx) {
|
|
|
324
325
|
...(handoffTaskId !== undefined ? { handoffTaskId } : {}),
|
|
325
326
|
};
|
|
326
327
|
}
|
|
327
|
-
function
|
|
328
|
+
function failsoftReasonSentence(park, failedReason, terminal) {
|
|
329
|
+
if (terminal !== undefined)
|
|
330
|
+
return terminal.errorMessage;
|
|
331
|
+
if (failedReason === undefined)
|
|
332
|
+
return undefined;
|
|
333
|
+
return (`${park.gate === 'fs' ? 'Tool approval' : 'AskUserQuestion'} gate could not be answered: ${failedReason}` +
|
|
334
|
+
' — the run is still parked on this approval: decide it on the card when it is shown again, or cancel the run.');
|
|
335
|
+
}
|
|
336
|
+
function failsoftEvents(park, ctx, gateAbortedByUser, failedReason, terminal, noDecisionSurface = false) {
|
|
328
337
|
const events = [
|
|
329
338
|
...flushHeldWithInterruptRewrite(ctx.led, {
|
|
330
339
|
terminal: park.pendingDone,
|
|
@@ -332,19 +341,15 @@ function failsoftEvents(park, ctx, gateAbortedByUser, failedReason, terminal) {
|
|
|
332
341
|
gateAbortedByUser,
|
|
333
342
|
}),
|
|
334
343
|
];
|
|
344
|
+
const reason = failsoftReasonSentence(park, failedReason, terminal);
|
|
335
345
|
if (park.pendingDone) {
|
|
336
|
-
events.push(park.pendingDone);
|
|
346
|
+
events.push(reason === undefined || noDecisionSurface ? park.pendingDone : { ...park.pendingDone, _sema_park_failed_reason: reason });
|
|
337
347
|
}
|
|
338
348
|
else if (terminal !== undefined) {
|
|
339
349
|
events.push({ type: 'failed', ...terminal });
|
|
340
350
|
}
|
|
341
|
-
else if (
|
|
342
|
-
events.push({
|
|
343
|
-
type: 'failed',
|
|
344
|
-
errorCode: 'hitl_unanswered',
|
|
345
|
-
errorMessage: `${park.gate === 'fs' ? 'Tool approval' : 'AskUserQuestion'} gate could not be answered: ${failedReason}` +
|
|
346
|
-
' — the run is still parked on this approval: decide it on the card when it is shown again, or cancel the run.',
|
|
347
|
-
});
|
|
351
|
+
else if (reason !== undefined) {
|
|
352
|
+
events.push({ type: 'failed', errorCode: 'hitl_unanswered', errorMessage: reason });
|
|
348
353
|
}
|
|
349
354
|
return events;
|
|
350
355
|
}
|
|
@@ -7,3 +7,10 @@ export type SuspendedReopenReading = {
|
|
|
7
7
|
readonly kind: 'unstated';
|
|
8
8
|
};
|
|
9
9
|
export declare function suspendedReopenOf(ev: unknown): SuspendedReopenReading;
|
|
10
|
+
export interface ResumeReopenDetail {
|
|
11
|
+
code: string;
|
|
12
|
+
reason: 'env_failed' | 'tool_unavailable' | 'tool_contract_mismatch';
|
|
13
|
+
retriable: true;
|
|
14
|
+
}
|
|
15
|
+
export declare function resumeReopenFromError(err: unknown): ResumeReopenDetail | null;
|
|
16
|
+
export declare function resumeReopenContent(detail: ResumeReopenDetail): string;
|
|
@@ -1,13 +1,44 @@
|
|
|
1
|
+
import { RESUME_REOPEN_CODES, RESUME_ENV_FAILED, RESUME_TOOL_UNAVAILABLE } from '../engineErrorCodes.js';
|
|
1
2
|
const UNSTATED = Object.freeze({ kind: 'unstated' });
|
|
2
3
|
const NOT_REOPENED = Object.freeze({ kind: 'not_reopened' });
|
|
3
4
|
export function suspendedReopenOf(ev) {
|
|
4
|
-
if (ev === null || typeof ev !== 'object'
|
|
5
|
+
if (ev === null || typeof ev !== 'object')
|
|
5
6
|
return UNSTATED;
|
|
6
|
-
|
|
7
|
-
|
|
7
|
+
try {
|
|
8
|
+
if (Array.isArray(ev))
|
|
9
|
+
return UNSTATED;
|
|
10
|
+
const o = ev;
|
|
11
|
+
if (!Object.hasOwn(o, 'reopened'))
|
|
12
|
+
return UNSTATED;
|
|
13
|
+
const v = o.reopened;
|
|
14
|
+
if (v === null)
|
|
15
|
+
return NOT_REOPENED;
|
|
16
|
+
return typeof v === 'string' && v.length > 0 ? { kind: 'reopened', code: v } : UNSTATED;
|
|
17
|
+
}
|
|
18
|
+
catch {
|
|
8
19
|
return UNSTATED;
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
export function resumeReopenFromError(err) {
|
|
23
|
+
if (typeof err !== 'object' || err === null)
|
|
24
|
+
return null;
|
|
25
|
+
const code = err.errorCode;
|
|
26
|
+
if (typeof code !== 'string' || !RESUME_REOPEN_CODES.includes(code))
|
|
27
|
+
return null;
|
|
28
|
+
if (err.retriable !== true)
|
|
29
|
+
return null;
|
|
30
|
+
const reason = code.slice('resume.'.length);
|
|
31
|
+
return { code, reason, retriable: true };
|
|
32
|
+
}
|
|
33
|
+
export function resumeReopenContent(detail) {
|
|
34
|
+
const first = detail.code === RESUME_ENV_FAILED
|
|
35
|
+
? 'The execution environment for this parked work could not be brought back'
|
|
36
|
+
: detail.code === RESUME_TOOL_UNAVAILABLE
|
|
37
|
+
? 'The tool this approval was granted for is not available on this engine'
|
|
38
|
+
: 'The tool this approval was granted for has changed since the approval was recorded';
|
|
39
|
+
return [
|
|
40
|
+
first,
|
|
41
|
+
'The engine re-parked the work before consuming anything — the approval is still pending and your decision was not consumed',
|
|
42
|
+
'Re-fetch the pending approvals and decide again on an engine where the same tool is available',
|
|
43
|
+
].join(' · ');
|
|
13
44
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { DEFAULT_DENY_REASON, DecideTransportRetryExhaustedError, HitlBridge, HitlSafetyError, denyReasonForWire, findPendingForTask, readDecideCurrentPending, readWireErrorCode, } from './hitlBridge.js';
|
|
1
|
+
import { APPROVAL_CARD_PORT_KEY_MISMATCH_REASON, DEFAULT_DENY_REASON, DecideTransportRetryExhaustedError, HitlBridge, NO_APPROVAL_CARD_PORT_REASON, HitlSafetyError, denyReasonForWire, findPendingForTask, readDecideCurrentPending, readWireErrorCode, } from './hitlBridge.js';
|
|
2
2
|
import { askParkRowArm } from './askParkRowRouting.js';
|
|
3
3
|
import { hostLog } from '../host.js';
|
|
4
4
|
import { readOwnNonEmptyString } from '../ownKey.js';
|
|
@@ -120,10 +120,9 @@ function cardPortMissReason() {
|
|
|
120
120
|
const otherKeysInstalled = cardPortByKey
|
|
121
121
|
.keys()
|
|
122
122
|
.some((k) => k !== DEFAULT_SESSION_KEY && cardPortByKey.get(k) != null);
|
|
123
|
-
if (otherKeysInstalled)
|
|
124
|
-
return
|
|
125
|
-
|
|
126
|
-
return 'no approval card port installed (print/non-REPL mode, or host forgot installApprovalCardPort)';
|
|
123
|
+
if (otherKeysInstalled)
|
|
124
|
+
return APPROVAL_CARD_PORT_KEY_MISMATCH_REASON;
|
|
125
|
+
return NO_APPROVAL_CARD_PORT_REASON;
|
|
127
126
|
}
|
|
128
127
|
function rememberRefusedByServer(e) {
|
|
129
128
|
switch (readWireErrorCode(e)) {
|
package/dist/index.d.ts
CHANGED
|
@@ -145,6 +145,7 @@ export * from './principalWire.js';
|
|
|
145
145
|
export * from './wireErrorTriage.js';
|
|
146
146
|
export * from './corruptSession.js';
|
|
147
147
|
export * from './corruptSessionCopy.js';
|
|
148
|
+
export { classifyToolHistoryMismatch, resolveToolHistoryRewindTarget } from './toolHistoryMismatch.js';
|
|
148
149
|
export * from './resumeRefusalCopy.js';
|
|
149
150
|
export * from './wireRefusalCopy.js';
|
|
150
151
|
export * from './inheritEnvWire.js';
|
package/dist/index.js
CHANGED
|
@@ -133,6 +133,7 @@ export * from './principalWire.js';
|
|
|
133
133
|
export * from './wireErrorTriage.js';
|
|
134
134
|
export * from './corruptSession.js';
|
|
135
135
|
export * from './corruptSessionCopy.js';
|
|
136
|
+
export { classifyToolHistoryMismatch, resolveToolHistoryRewindTarget } from './toolHistoryMismatch.js';
|
|
136
137
|
export * from './resumeRefusalCopy.js';
|
|
137
138
|
export * from './wireRefusalCopy.js';
|
|
138
139
|
export * from './inheritEnvWire.js';
|
|
@@ -19,13 +19,8 @@ export interface ResumePolicyBlockDetail {
|
|
|
19
19
|
}
|
|
20
20
|
export declare function resumePolicyBlockFromError(err: unknown): ResumePolicyBlockDetail | null;
|
|
21
21
|
export declare function resumePolicyBlockContent(detail: ResumePolicyBlockDetail): string;
|
|
22
|
-
export
|
|
23
|
-
|
|
24
|
-
reason: 'env_failed' | 'tool_unavailable' | 'tool_contract_mismatch';
|
|
25
|
-
retriable: true;
|
|
26
|
-
}
|
|
27
|
-
export declare function resumeReopenFromError(err: unknown): ResumeReopenDetail | null;
|
|
28
|
-
export declare function resumeReopenContent(detail: ResumeReopenDetail): string;
|
|
22
|
+
export { resumeReopenFromError, resumeReopenContent } from './hitl/suspendedReopen.js';
|
|
23
|
+
export type { ResumeReopenDetail } from './hitl/suspendedReopen.js';
|
|
29
24
|
export interface ResumeContextUnavailableDetail {
|
|
30
25
|
code: typeof RESUME_CONTEXT_UNAVAILABLE;
|
|
31
26
|
staleAfterSec?: number;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { readErrorNumber, readErrorString } from './wireFailureShape.js';
|
|
2
|
-
import { RESUME_CONTEXT_UNAVAILABLE, RESUME_PLACEMENT_MISMATCH, RESUME_PREFLIGHT_REJECTED,
|
|
2
|
+
import { RESUME_CONTEXT_UNAVAILABLE, RESUME_PLACEMENT_MISMATCH, RESUME_PREFLIGHT_REJECTED, RESUME_POLICY_FOLD_CODE, PARKED_RESUME_STARTUP_FAILED, isStatusDerivedCoarseCode, } from './engineErrorCodes.js';
|
|
3
3
|
import { capForDisplay } from './fleetTaskDesc.js';
|
|
4
4
|
import { resumeRetryLaterFromError } from './wireErrorTriage.js';
|
|
5
5
|
export const RESUME_REFUSAL_CODES = Object.freeze([
|
|
@@ -85,29 +85,7 @@ export function resumePolicyBlockContent(detail) {
|
|
|
85
85
|
"This refusal does not say whether anything was consumed before it or whether a retry would pass — check the task's current state before deciding again, and ask whoever operates this deployment if it keeps happening",
|
|
86
86
|
].join(' · ');
|
|
87
87
|
}
|
|
88
|
-
export
|
|
89
|
-
if (typeof err !== 'object' || err === null)
|
|
90
|
-
return null;
|
|
91
|
-
const code = err.errorCode;
|
|
92
|
-
if (typeof code !== 'string' || !RESUME_REOPEN_CODES.includes(code))
|
|
93
|
-
return null;
|
|
94
|
-
if (err.retriable !== true)
|
|
95
|
-
return null;
|
|
96
|
-
const reason = code.slice('resume.'.length);
|
|
97
|
-
return { code, reason, retriable: true };
|
|
98
|
-
}
|
|
99
|
-
export function resumeReopenContent(detail) {
|
|
100
|
-
const first = detail.code === RESUME_ENV_FAILED
|
|
101
|
-
? 'The execution environment for this parked work could not be brought back'
|
|
102
|
-
: detail.code === RESUME_TOOL_UNAVAILABLE
|
|
103
|
-
? 'The tool this approval was granted for is not available on this engine'
|
|
104
|
-
: 'The tool this approval was granted for has changed since the approval was recorded';
|
|
105
|
-
return [
|
|
106
|
-
first,
|
|
107
|
-
'The engine re-parked the work before consuming anything — the approval is still pending and your decision was not consumed',
|
|
108
|
-
'Re-fetch the pending approvals and decide again on an engine where the same tool is available',
|
|
109
|
-
].join(' · ');
|
|
110
|
-
}
|
|
88
|
+
export { resumeReopenFromError, resumeReopenContent } from './hitl/suspendedReopen.js';
|
|
111
89
|
export function resumeContextUnavailableFromError(err) {
|
|
112
90
|
if (typeof err !== 'object' || err === null)
|
|
113
91
|
return null;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { engineWireTarget } from './engineWireTarget.js';
|
|
2
2
|
import { createEngineCapReader } from './engineCapReader.js';
|
|
3
|
+
import { _wireConversationRewindJudge } from './toolHistoryMismatch.js';
|
|
3
4
|
const CAPS_KEYS = {
|
|
4
5
|
conversationAnchor: 'resumeAt',
|
|
5
6
|
forkRestore: 'rewindFiles',
|
|
@@ -37,8 +38,12 @@ const reader = createEngineCapReader({
|
|
|
37
38
|
makeUnobserved: () => ({ kind: 'unobserved' }),
|
|
38
39
|
});
|
|
39
40
|
export function noteEngineCapsForRewindArchive(baseUrl, caps, opts) {
|
|
41
|
+
_wireConversationRewindJudge(conversationRewindJudge);
|
|
40
42
|
reader.note(baseUrl, caps, opts);
|
|
41
43
|
}
|
|
44
|
+
function conversationRewindJudge(engineBaseUrl) {
|
|
45
|
+
return rewindConversationAvailability(observedRewindArchive(engineBaseUrl));
|
|
46
|
+
}
|
|
42
47
|
export function observedRewindArchive(baseUrl = engineWireTarget()?.baseUrl) {
|
|
43
48
|
return reader.observed(baseUrl);
|
|
44
49
|
}
|
package/dist/seam.d.ts
CHANGED
|
@@ -3,6 +3,7 @@ import type { EngineTurnUsage } from './adapter/downstream/turnUsageToModelUsage
|
|
|
3
3
|
import type { RunCostReconcile } from './adapter/downstream/terminalToSdkResult.js';
|
|
4
4
|
import type { WiringManifestAutoMode, WiringManifestModelGate, WiringManifestMcpEntryView, WiringManifestHookEntryView, WiringManifestLspSeamView, WiringManifestWriteProtectionView, WiringManifestAutoConsolidationView, WiringManifestReadDenyView, ContextUsageSectionView, PromptManifestSectionView, PromptManifestBlockView, PromptManifestToolView } from './adapter/downstream/eventToSdkMessage.js';
|
|
5
5
|
import type { ToolRosterView, ToolRosterDeltaView, WiringManifestHandsView } from './toolRoster.js';
|
|
6
|
+
import type { SuspendedReopenReading } from './hitl/suspendedReopen.js';
|
|
6
7
|
export interface AbortSignalLike {
|
|
7
8
|
readonly aborted: boolean;
|
|
8
9
|
}
|
|
@@ -149,7 +150,16 @@ export type ChromeEvent = {
|
|
|
149
150
|
} | ({
|
|
150
151
|
kind: 'run_cost_reconciled';
|
|
151
152
|
laneProof: LaneProof;
|
|
152
|
-
} & RunCostReconcile) | HumanInputChromeEvent | MessageCommittedChromeEvent | EngineNoticeChromeEvent | TextSegmentEndChromeEvent | ThinkingSegmentEndChromeEvent | ToolDisclosureChromeEvent | ToolProgressChromeEvent | ToolRosterDeltaChromeEvent | ContextUsageChromeEvent | CompactionOutcomeChromeEvent | PromptAssembledChromeEvent | MemorySavedChromeEvent | WiringManifestChromeEvent | ResultTextDivergedChromeEvent | ApprovalRequestChromeEvent | ApprovalRevokeChromeEvent
|
|
153
|
+
} & RunCostReconcile) | HumanInputChromeEvent | MessageCommittedChromeEvent | EngineNoticeChromeEvent | TextSegmentEndChromeEvent | ThinkingSegmentEndChromeEvent | ToolDisclosureChromeEvent | ToolProgressChromeEvent | ToolRosterDeltaChromeEvent | ContextUsageChromeEvent | CompactionOutcomeChromeEvent | PromptAssembledChromeEvent | MemorySavedChromeEvent | WiringManifestChromeEvent | ResultTextDivergedChromeEvent | ApprovalRequestChromeEvent | ApprovalRevokeChromeEvent | {
|
|
154
|
+
kind: 'suspended_reopened';
|
|
155
|
+
laneProof: LaneProof;
|
|
156
|
+
reopen: Extract<SuspendedReopenReading, {
|
|
157
|
+
kind: 'reopened';
|
|
158
|
+
}>;
|
|
159
|
+
content?: string;
|
|
160
|
+
eventId?: string;
|
|
161
|
+
taskId?: string;
|
|
162
|
+
};
|
|
153
163
|
export interface WiringManifestChromeEvent {
|
|
154
164
|
kind: 'wiring_manifest';
|
|
155
165
|
laneProof: LaneProof;
|
package/dist/seam.js
CHANGED
|
@@ -126,6 +126,13 @@ const CHROME_ARM_TABLE = {
|
|
|
126
126
|
'🔴 账本重放的历史帧只用于时间线渲染,卡集的全量对账基准是开流 preamble(它带的 expiresInMs 是铸帧时刻的旧值);' +
|
|
127
127
|
'card 的 message/args 是模型/工具产文,只渲染永不回喂模型',
|
|
128
128
|
},
|
|
129
|
+
suspended_reopened: {
|
|
130
|
+
required: false,
|
|
131
|
+
duty: '可选:引擎确认把一次已送出的决定重新挂回待决 ⇒ 上屏一句「决定没被消费、再决一次」(`content`,单源句;缺席 = 码不在 reopen 族闭集,可照 `reopen.code` 原词显示,开集按成员渲染必带 default 臂)。' +
|
|
132
|
+
'🔴 按 `eventId` 幂等(同一条流的重放本包已去重;前台腿与接管腿各发一条、同 eventId,去重归宿主);' +
|
|
133
|
+
'🔴 接管 / 从头重放的腿:本臂之后同流又来「门已不在」的帧(text / tool_start / turn_end / done / failed 或新的挂起)⇒ 那次重开是历史,别当现状上屏;会话已切走的别补发 —— 这两件归宿主判;' +
|
|
134
|
+
'明说没重开 / 老引擎缺键一条都不发,本臂没来 ≠ 确认没重开;`taskId` 只在帧上自带时在场',
|
|
135
|
+
},
|
|
129
136
|
approval_revoke: {
|
|
130
137
|
required: true,
|
|
131
138
|
duty: '用 sdk isApprovalRevokeFrameV1 窄化 frame 取 batchId/askIds/taskId;窄不下来按通用撤卡处理(该批本地卡照清);' +
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
export declare const TOOL_HISTORY_MISMATCH_KINDS: readonly ["tool_use_without_result", "orphan_tool_result", "duplicate_tool_use_id"];
|
|
2
|
+
export type ToolHistoryMismatchKind = (typeof TOOL_HISTORY_MISMATCH_KINDS)[number];
|
|
3
|
+
export interface ToolHistoryMismatch {
|
|
4
|
+
kind: ToolHistoryMismatchKind;
|
|
5
|
+
toolUseId?: string;
|
|
6
|
+
}
|
|
7
|
+
export interface ToolHistoryErrorCarrier {
|
|
8
|
+
readonly code?: string | null | undefined;
|
|
9
|
+
readonly status?: number | null | undefined;
|
|
10
|
+
readonly message?: string | null | undefined;
|
|
11
|
+
}
|
|
12
|
+
export interface ToolHistoryMismatchAnchor {
|
|
13
|
+
readonly kind: ToolHistoryMismatchKind;
|
|
14
|
+
readonly template: RegExp;
|
|
15
|
+
readonly idAfter: RegExp | null;
|
|
16
|
+
}
|
|
17
|
+
export declare const TOOL_HISTORY_MISMATCH_ANCHORS: readonly ToolHistoryMismatchAnchor[];
|
|
18
|
+
export declare function classifyToolHistoryMismatch(err: ToolHistoryErrorCarrier | null | undefined): ToolHistoryMismatch | null;
|
|
19
|
+
export declare const TOOL_HISTORY_REWIND_SENTENCE = "Run /rewind to recover the conversation.";
|
|
20
|
+
export declare function toolHistoryRepeatSentence(n: number): string;
|
|
21
|
+
type ConversationRewindVerdict = 'available' | 'unavailable' | 'unknown';
|
|
22
|
+
export declare function _wireConversationRewindJudge(judge: (engineBaseUrl: string) => ConversationRewindVerdict): void;
|
|
23
|
+
export declare function toolHistoryRewindOffered(declared: boolean | {
|
|
24
|
+
readonly engineBaseUrl: string;
|
|
25
|
+
} | undefined): boolean;
|
|
26
|
+
export interface ToolHistoryMismatchRowKey {
|
|
27
|
+
kind: ToolHistoryMismatchKind;
|
|
28
|
+
toolUseId?: string;
|
|
29
|
+
consecutive?: number;
|
|
30
|
+
}
|
|
31
|
+
export declare function toolHistoryMismatchRowKey(m: ToolHistoryMismatch, consecutive: number | undefined): ToolHistoryMismatchRowKey;
|
|
32
|
+
export declare function readToolHistoryMismatchKey(v: object | null | undefined): ToolHistoryMismatchRowKey | undefined;
|
|
33
|
+
export declare const TOOL_HISTORY_LEDGER_CAP = 64;
|
|
34
|
+
export declare function openToolHistoryGeneration(): number;
|
|
35
|
+
export interface ToolHistoryTerminalNote {
|
|
36
|
+
sessionId: string | undefined;
|
|
37
|
+
sessionEchoed: boolean;
|
|
38
|
+
generation: number;
|
|
39
|
+
runId: string | undefined;
|
|
40
|
+
replay: boolean;
|
|
41
|
+
mismatch: ToolHistoryMismatch | null;
|
|
42
|
+
}
|
|
43
|
+
export declare function noteToolHistoryTerminal(note: ToolHistoryTerminalNote): number | undefined;
|
|
44
|
+
export declare function _resetToolHistoryLedgerForTest(): void;
|
|
45
|
+
export interface ToolHistoryRow {
|
|
46
|
+
readonly toolUseIds?: readonly string[];
|
|
47
|
+
readonly toolResultIds?: readonly string[];
|
|
48
|
+
readonly prompt?: boolean;
|
|
49
|
+
readonly engineHandle?: string;
|
|
50
|
+
readonly local?: boolean;
|
|
51
|
+
readonly sessionRoot?: boolean;
|
|
52
|
+
}
|
|
53
|
+
export declare const TOOL_HISTORY_UNDECIDABLE_REASONS: readonly ["no-mismatch", "no-tool-use-id", "id-not-found", "prompt-not-found", "prompt-without-handle", "unreadable-rows"];
|
|
54
|
+
export type ToolHistoryUndecidableReason = (typeof TOOL_HISTORY_UNDECIDABLE_REASONS)[number];
|
|
55
|
+
export type ToolHistoryRewindResolution<R> = {
|
|
56
|
+
target: R;
|
|
57
|
+
} | {
|
|
58
|
+
rootReopen: true;
|
|
59
|
+
} | {
|
|
60
|
+
undecidable: ToolHistoryUndecidableReason;
|
|
61
|
+
};
|
|
62
|
+
export declare function resolveToolHistoryRewindTarget<R extends ToolHistoryRow>(rows: readonly (R | null | undefined)[] | null | undefined, mismatch: ToolHistoryMismatch | null | undefined): ToolHistoryRewindResolution<R>;
|
|
63
|
+
export {};
|