@sema-agent/client-core 0.83.3 → 0.83.5
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 +141 -0
- package/README.md +19 -17
- package/dist/adapter/activeRunSelfHeal.d.ts +9 -1
- package/dist/adapter/activeRunSelfHeal.js +75 -16
- package/dist/compensations.js +1 -1
- package/dist/displayUntrusted.d.ts +13 -0
- package/dist/displayUntrusted.js +751 -125
- package/dist/hitl/armedGateRegistry.js +3 -5
- package/dist/hitl/gateIdentity.d.ts +1 -0
- package/dist/hitl/gateIdentity.js +8 -0
- package/dist/hitl/planReviewWire.d.ts +7 -0
- package/dist/hitl/planReviewWire.js +345 -76
- package/dist/hitl/toolApprovalWire.d.ts +2 -0
- package/dist/hitl/toolApprovalWire.js +23 -4
- package/dist/hooksWireCaps.js +78 -8
- package/dist/host.d.ts +2 -0
- package/dist/index.d.ts +4 -3
- package/dist/index.js +3 -2
- package/dist/pluginHooksWire.d.ts +9 -0
- package/dist/pluginHooksWire.js +119 -0
- package/dist/request/taskRequest.js +1 -1
- package/dist/seatContract.d.ts +18 -1
- package/dist/seatContract.js +20 -0
- package/dist/systemReminderTag.js +25 -4
- package/docs/INTEGRATION-CLIENTS.md +413 -68
- package/package.json +2 -2
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { ActiveRunBusySignal } from './runStream.js';
|
|
2
|
+
import type { PlanReviewReopenTrigger } from '../hitl/planReviewWire.js';
|
|
2
3
|
export declare const PLAN_REVIEW_GATE_KIND = "plan_review";
|
|
3
4
|
export declare const PLAN_REVIEW_GATE_KINDS: readonly string[];
|
|
4
5
|
export declare const ASK_PARK_GATE_KINDS: readonly string[];
|
|
@@ -33,6 +34,7 @@ export type ReopenCardVerdict = {
|
|
|
33
34
|
decidedWithoutCard?: true;
|
|
34
35
|
pendingRowGone?: true;
|
|
35
36
|
_sema_decisionInFlight?: true;
|
|
37
|
+
dismissedByUser?: true;
|
|
36
38
|
} | {
|
|
37
39
|
reopened: true;
|
|
38
40
|
firstSight: boolean;
|
|
@@ -54,7 +56,9 @@ export interface ActiveRunSelfHealDeps {
|
|
|
54
56
|
listOwnedPendingApprovals?: (opts?: {
|
|
55
57
|
signal?: AbortSignal;
|
|
56
58
|
}) => Promise<number>;
|
|
57
|
-
reopenPlanReview?: (taskId: string
|
|
59
|
+
reopenPlanReview?: (taskId: string, opts?: {
|
|
60
|
+
trigger?: PlanReviewReopenTrigger;
|
|
61
|
+
}) => ReopenCardVerdict | Promise<ReopenCardVerdict>;
|
|
58
62
|
reopenAskPark?: (taskId: string) => Promise<ReopenCardVerdict>;
|
|
59
63
|
offerRunningChoice?: (req: RunningChoiceRequest) => Promise<'steer' | 'cancel' | 'wait'>;
|
|
60
64
|
offerStaleParkChoice?: (req: StaleParkChoiceRequest) => Promise<'cancel' | 'wait'>;
|
|
@@ -81,6 +85,7 @@ export type SelfHealOutcome = {
|
|
|
81
85
|
decidePath: string | null;
|
|
82
86
|
decisionInFlight?: true;
|
|
83
87
|
pendingRowGone?: true;
|
|
88
|
+
dismissedByUser?: true;
|
|
84
89
|
} | {
|
|
85
90
|
kind: 'not-parked';
|
|
86
91
|
taskId: string;
|
|
@@ -107,6 +112,7 @@ export type SelfHealOutcome = {
|
|
|
107
112
|
kind: 'ask-decided-without-card';
|
|
108
113
|
taskId: string;
|
|
109
114
|
released: true;
|
|
115
|
+
releaseConfirmedByEngine?: true;
|
|
110
116
|
} | {
|
|
111
117
|
kind: 'ask-decided-release-unknown';
|
|
112
118
|
taskId: string;
|
|
@@ -125,6 +131,7 @@ export type SelfHealOutcome = {
|
|
|
125
131
|
} | {
|
|
126
132
|
kind: 'running-cancelled';
|
|
127
133
|
taskId: string;
|
|
134
|
+
releaseConfirmedByEngine?: true;
|
|
128
135
|
} | {
|
|
129
136
|
kind: 'running-cancel-timeout';
|
|
130
137
|
taskId: string;
|
|
@@ -142,6 +149,7 @@ export type SelfHealOutcome = {
|
|
|
142
149
|
} | {
|
|
143
150
|
kind: 'stale-park-cancelled';
|
|
144
151
|
taskId: string;
|
|
152
|
+
releaseConfirmedByEngine?: true;
|
|
145
153
|
} | {
|
|
146
154
|
kind: 'stale-park-cancel-timeout';
|
|
147
155
|
taskId: string;
|
|
@@ -82,6 +82,16 @@ export function readCancelRequested(body) {
|
|
|
82
82
|
function reopenRefusedForDecisionInFlight(verdict) {
|
|
83
83
|
return verdict !== null && verdict.reopened === false && verdict._sema_decisionInFlight === true;
|
|
84
84
|
}
|
|
85
|
+
function reopenRefusedForUserDismissal(verdict) {
|
|
86
|
+
return verdict !== null && verdict.reopened === false && verdict.dismissedByUser === true;
|
|
87
|
+
}
|
|
88
|
+
function planReviewReopenTriggerFor(origin) {
|
|
89
|
+
if (origin === 'user')
|
|
90
|
+
return 'user';
|
|
91
|
+
if (origin === 'injected')
|
|
92
|
+
return 'automatic';
|
|
93
|
+
return undefined;
|
|
94
|
+
}
|
|
85
95
|
function reopenDelivered(verdict) {
|
|
86
96
|
return verdict.reopened === true && verdict.presented !== false;
|
|
87
97
|
}
|
|
@@ -287,7 +297,9 @@ export async function attemptActiveRunSelfHeal(signal, runs, deps) {
|
|
|
287
297
|
}
|
|
288
298
|
async function planVerdict(taskId, deps) {
|
|
289
299
|
try {
|
|
290
|
-
|
|
300
|
+
const trigger = planReviewReopenTriggerFor(deps?.submissionOrigin);
|
|
301
|
+
const verdict = trigger === undefined ? deps?.reopenPlanReview?.(taskId) : deps?.reopenPlanReview?.(taskId, { trigger });
|
|
302
|
+
return (await verdict) ?? { reopened: false };
|
|
291
303
|
}
|
|
292
304
|
catch {
|
|
293
305
|
return { reopened: false };
|
|
@@ -311,6 +323,7 @@ async function planReviewArm(taskId, signal, deps) {
|
|
|
311
323
|
decidePath: signal.pendingGate?.decidePath ?? null,
|
|
312
324
|
...(reopenRefusedForDecisionInFlight(verdict) ? { decisionInFlight: true } : {}),
|
|
313
325
|
...(verdict.pendingRowGone === true ? { pendingRowGone: true } : {}),
|
|
326
|
+
...(reopenRefusedForUserDismissal(verdict) ? { dismissedByUser: true } : {}),
|
|
314
327
|
};
|
|
315
328
|
}
|
|
316
329
|
async function askParkArm(taskId, signal, runs, deps) {
|
|
@@ -328,8 +341,14 @@ async function askParkArm(taskId, signal, runs, deps) {
|
|
|
328
341
|
...(deps?.signal !== undefined ? { signal: deps.signal } : {}),
|
|
329
342
|
...sessionOpts(deps),
|
|
330
343
|
});
|
|
331
|
-
if (release.released)
|
|
332
|
-
return {
|
|
344
|
+
if (release.released) {
|
|
345
|
+
return {
|
|
346
|
+
kind: 'ask-decided-without-card',
|
|
347
|
+
taskId,
|
|
348
|
+
released: true,
|
|
349
|
+
...(release.lastHolder === 'released' ? { releaseConfirmedByEngine: true } : {}),
|
|
350
|
+
};
|
|
351
|
+
}
|
|
333
352
|
if (release.lastStatus !== null && RUNNING_STATES.includes(release.lastStatus)) {
|
|
334
353
|
return runningChoiceArm(taskId, release.lastStatus, signal, false, runs, deps);
|
|
335
354
|
}
|
|
@@ -434,8 +453,9 @@ async function staleParkArm(taskId, busy, runs, deps) {
|
|
|
434
453
|
if (stillGone !== 0)
|
|
435
454
|
return reopenFailedAfterCard;
|
|
436
455
|
const verdict = await cancelAndConfirmRelease(taskId, runs, durableGet, deps);
|
|
437
|
-
if (verdict.outcome === 'released')
|
|
438
|
-
return { kind: 'stale-park-cancelled', taskId };
|
|
456
|
+
if (verdict.outcome === 'released') {
|
|
457
|
+
return { kind: 'stale-park-cancelled', taskId, ...(verdict.engineStatedRelease ? { releaseConfirmedByEngine: true } : {}) };
|
|
458
|
+
}
|
|
439
459
|
if (verdict.outcome === 'failed') {
|
|
440
460
|
return { kind: 'stale-park-cancel-failed', taskId, detail: verdict.detail, delivery: verdict.delivery };
|
|
441
461
|
}
|
|
@@ -475,7 +495,7 @@ async function cancelAndConfirmRelease(taskId, durable, durableGet, deps) {
|
|
|
475
495
|
...sessionOpts(deps),
|
|
476
496
|
});
|
|
477
497
|
if (verdict.released)
|
|
478
|
-
return { outcome: 'released' };
|
|
498
|
+
return { outcome: 'released', engineStatedRelease: verdict.lastHolder === 'released' };
|
|
479
499
|
if (cancelFailure !== null)
|
|
480
500
|
return { outcome: 'failed', ...cancelFailure };
|
|
481
501
|
return {
|
|
@@ -567,8 +587,9 @@ async function runningChoiceArm(taskId, status, busy, statusFromWire, runs, deps
|
|
|
567
587
|
}
|
|
568
588
|
if (choice === 'cancel' && canCancel && runs?.cancel !== undefined && runs.get !== undefined) {
|
|
569
589
|
const verdict = await cancelAndConfirmRelease(taskId, runs, runs.get, deps);
|
|
570
|
-
if (verdict.outcome === 'released')
|
|
571
|
-
return { kind: 'running-cancelled', taskId };
|
|
590
|
+
if (verdict.outcome === 'released') {
|
|
591
|
+
return { kind: 'running-cancelled', taskId, ...(verdict.engineStatedRelease ? { releaseConfirmedByEngine: true } : {}) };
|
|
592
|
+
}
|
|
572
593
|
if (verdict.outcome === 'failed') {
|
|
573
594
|
return { kind: 'running-cancel-failed', taskId, detail: verdict.detail, delivery: verdict.delivery };
|
|
574
595
|
}
|
|
@@ -617,7 +638,8 @@ export function activeRunSelfHealRow(outcome, signal, copy, origin, follow) {
|
|
|
617
638
|
const engineDecide = copy?.engineDecidePath !== false;
|
|
618
639
|
const keep = (outcome.kind === 'plan-review-reopen-failed' || outcome.kind === 'ask-reopen-failed') &&
|
|
619
640
|
outcome.decisionInFlight !== true &&
|
|
620
|
-
outcome.pendingRowGone !== true
|
|
641
|
+
outcome.pendingRowGone !== true &&
|
|
642
|
+
(outcome.kind !== 'plan-review-reopen-failed' || outcome.dismissedByUser !== true)
|
|
621
643
|
? keepSessionWayOutOf(copy)
|
|
622
644
|
: null;
|
|
623
645
|
const base = activeRunSelfHealBaseRow(outcome, signal, copy?.wayOut ?? DEFAULT_WAY_OUT, following, keep, engineDecide);
|
|
@@ -632,6 +654,7 @@ export function activeRunSelfHealRow(outcome, signal, copy, origin, follow) {
|
|
|
632
654
|
}
|
|
633
655
|
}
|
|
634
656
|
const INJECTED_LEAD = 'A follow-up message sema sent on its own (not one you typed)';
|
|
657
|
+
const USER_CLOSED_CARD_NOT_PUT_BACK = 'sema does not put a card you closed back on screen by itself';
|
|
635
658
|
function injectedSubmissionRow(outcome, following = false) {
|
|
636
659
|
const lead = INJECTED_LEAD;
|
|
637
660
|
const id = 'taskId' in outcome && outcome.taskId ? ` (id ${outcome.taskId})` : '';
|
|
@@ -647,6 +670,11 @@ function injectedSubmissionRow(outcome, following = false) {
|
|
|
647
670
|
return (`${lead} was not delivered: this session is still busy with an earlier reply${id}, and your answer to the decision ` +
|
|
648
671
|
`it is waiting on is still on its way. sema did not retry, so the model has not seen it.`);
|
|
649
672
|
}
|
|
673
|
+
if (outcome.kind === 'plan-review-reopen-failed' && outcome.dismissedByUser === true) {
|
|
674
|
+
return (`${lead} was not delivered: this session is held by an earlier reply${id} whose plan is waiting for your review, and ` +
|
|
675
|
+
`you closed that approval card — ${USER_CLOSED_CARD_NOT_PUT_BACK}. Send a message when you want the card back; ` +
|
|
676
|
+
`sema did not retry, so the model has not seen it.`);
|
|
677
|
+
}
|
|
650
678
|
if (outcome.kind === 'running-steered' && outcome.delivery === 'queued') {
|
|
651
679
|
const queuedOn = `${lead} is queued on an earlier reply${id} that is paused waiting for a decision`;
|
|
652
680
|
if (outcome.reopened !== null && reopenDelivered(outcome.reopened)) {
|
|
@@ -655,6 +683,10 @@ function injectedSubmissionRow(outcome, following = false) {
|
|
|
655
683
|
if (reopenRefusedForDecisionInFlight(outcome.reopened)) {
|
|
656
684
|
return `${queuedOn}; your answer to it is still on its way, and the message is picked up once that answer is applied.`;
|
|
657
685
|
}
|
|
686
|
+
if (reopenRefusedForUserDismissal(outcome.reopened)) {
|
|
687
|
+
return (`${queuedOn}; you closed that card yourself, so sema did not put it back on screen — send a message to bring it ` +
|
|
688
|
+
`back, and the message is picked up once that decision is made.`);
|
|
689
|
+
}
|
|
658
690
|
return `${queuedOn} sema could not show here; the message is picked up only once that decision is made.`;
|
|
659
691
|
}
|
|
660
692
|
if (outcome.kind === 'running-steered' && outcome.delivery === 'parked_for_wake') {
|
|
@@ -666,8 +698,12 @@ function injectedSubmissionRow(outcome, following = false) {
|
|
|
666
698
|
return (`${lead} is on hold: this session is busy with an earlier reply${id} that is waiting on a decision. ` +
|
|
667
699
|
`sema kept the message queued and will send it after you answer the open card.`);
|
|
668
700
|
case 'resending':
|
|
669
|
-
return
|
|
670
|
-
|
|
701
|
+
return 'releaseConfirmedByEngine' in outcome && outcome.releaseConfirmedByEngine === true
|
|
702
|
+
? `${lead} was held back because this session was busy with an earlier reply${id}; the session is free again, ` +
|
|
703
|
+
`so sema is sending the message now — nothing for you to do.`
|
|
704
|
+
: `${lead} was held back because this session was busy with an earlier reply${id}; that reply is now reported ` +
|
|
705
|
+
`as finished, so sema is sending the message now — nothing for you to do (if the session has not quite ` +
|
|
706
|
+
`freed up yet, it may be held back once more).`;
|
|
671
707
|
case 'handed-off':
|
|
672
708
|
return following
|
|
673
709
|
? `${lead} was passed to the reply already in progress${id} instead of starting a new one. ` +
|
|
@@ -725,8 +761,14 @@ function activeRunSelfHealBaseRow(outcome, signal, wayOut, following = false, ke
|
|
|
725
761
|
`reopened it. Answer it and send your message again. (sema did NOT cancel the run: cancelling ` +
|
|
726
762
|
`would have thrown the plan away on your behalf.)`);
|
|
727
763
|
case 'ask-decided-without-card':
|
|
764
|
+
if (outcome.releaseConfirmedByEngine === true) {
|
|
765
|
+
return (`The previous turn was parked on an approval that your permission rules decided on their own (no card was ` +
|
|
766
|
+
`needed); run ${outcome.taskId} resumed and the engine confirmed it is no longer holding this session — ` +
|
|
767
|
+
`sema is re-sending your message.`);
|
|
768
|
+
}
|
|
728
769
|
return (`The previous turn was parked on an approval that your permission rules decided on their own (no card was ` +
|
|
729
|
-
`needed); run ${outcome.taskId} resumed and has since finished — sema is re-sending your message
|
|
770
|
+
`needed); run ${outcome.taskId} resumed and has since finished — sema is re-sending your message; if that ` +
|
|
771
|
+
`run has not fully let go of the session yet, you may have to send it once more.`);
|
|
730
772
|
case 'ask-decided-release-unknown':
|
|
731
773
|
return (`The previous turn was parked on an approval that your permission rules decided on their own (no card was ` +
|
|
732
774
|
`needed), so run ${outcome.taskId} should be resuming` +
|
|
@@ -747,6 +789,11 @@ function activeRunSelfHealBaseRow(outcome, signal, wayOut, following = false, ke
|
|
|
747
789
|
case 'plan-review-reopen-failed': {
|
|
748
790
|
if (outcome.decisionInFlight === true)
|
|
749
791
|
return decisionInFlightRow(`on a plan review (run ${outcome.taskId})`);
|
|
792
|
+
if (outcome.dismissedByUser === true) {
|
|
793
|
+
return (`The previous turn is parked waiting for a plan review (run ${outcome.taskId}), and you closed its approval card — ` +
|
|
794
|
+
`${USER_CLOSED_CARD_NOT_PUT_BACK}. It did NOT cancel the run — that would have discarded the plan for you. ` +
|
|
795
|
+
`Your message was NOT sent; send it again to bring the card back.`);
|
|
796
|
+
}
|
|
750
797
|
const viaEngine = engineDecide && outcome.decidePath
|
|
751
798
|
? ` You can also decide it on the engine directly: POST ${outcome.decidePath}.`
|
|
752
799
|
: '';
|
|
@@ -774,8 +821,11 @@ function activeRunSelfHealBaseRow(outcome, signal, wayOut, following = false, ke
|
|
|
774
821
|
: reopenRefusedForDecisionInFlight(outcome.reopened)
|
|
775
822
|
? `Your answer to that decision is still on its way to the engine, so sema did not show the card again; ` +
|
|
776
823
|
`that run moves on once the engine applies it.`
|
|
777
|
-
:
|
|
778
|
-
|
|
824
|
+
: reopenRefusedForUserDismissal(outcome.reopened)
|
|
825
|
+
? `You closed that decision card yourself, so sema did not put it back on screen; nothing will resume that ` +
|
|
826
|
+
`run until that decision is made — send a message to bring the card back.`
|
|
827
|
+
: `sema could not surface that decision card here, so nothing will resume that run until that decision is made; ` +
|
|
828
|
+
`${wayOut} if you no longer want it.`;
|
|
779
829
|
return (`${handle} is not actually working right now: the engine reports it ${parked} on a decision it needs ` +
|
|
780
830
|
`from you, so it queued your message on that park instead of running it. ${next} ${textOnly} ` +
|
|
781
831
|
`Nothing was cancelled, and your message did NOT start a new turn.`);
|
|
@@ -806,8 +856,12 @@ function activeRunSelfHealBaseRow(outcome, signal, wayOut, following = false, ke
|
|
|
806
856
|
`cancel that run. That run may or may not have received your message — watch what it does next ` +
|
|
807
857
|
`before sending it again.`);
|
|
808
858
|
case 'running-cancelled':
|
|
809
|
-
return
|
|
810
|
-
`
|
|
859
|
+
return outcome.releaseConfirmedByEngine === true
|
|
860
|
+
? `You chose to cancel run ${outcome.taskId}. The engine confirmed it is no longer holding this session, ` +
|
|
861
|
+
`so sema is sending your message now.`
|
|
862
|
+
: `You chose to cancel run ${outcome.taskId}. The engine now reports that run as finished, so sema is ` +
|
|
863
|
+
`sending your message now; if that run has not fully let go of the session yet, you may have to send ` +
|
|
864
|
+
`it once more.`;
|
|
811
865
|
case 'running-cancel-timeout': {
|
|
812
866
|
const waited = `${String(Math.max(1, Math.round(outcome.waitedMs / 1000)))}s`;
|
|
813
867
|
if (outcome.aborted) {
|
|
@@ -840,6 +894,11 @@ function activeRunSelfHealBaseRow(outcome, signal, wayOut, following = false, ke
|
|
|
840
894
|
`not allowed to read. Either way sema has nothing it can act on here, so it did NOT cancel anything. ` +
|
|
841
895
|
`Your message was NOT sent; send it again (if the session really is free it will just run), or ${wayOut}.`);
|
|
842
896
|
case 'stale-park-cancelled':
|
|
897
|
+
if (outcome.releaseConfirmedByEngine === true) {
|
|
898
|
+
return (`Run ${outcome.taskId} was parked on an approval that no longer exists on the engine, so there was ` +
|
|
899
|
+
`nothing left for you to answer. You chose to cancel that run, and the engine confirmed it is no longer ` +
|
|
900
|
+
`holding this session, so sema is sending your message again now.`);
|
|
901
|
+
}
|
|
843
902
|
return (`Run ${outcome.taskId} was parked on an approval that no longer exists on the engine, so there was ` +
|
|
844
903
|
`nothing left for you to answer. You chose to cancel that run: the engine now reports it as finished, ` +
|
|
845
904
|
`so it will not resume — whatever that turn had left to do is not coming back. sema is sending your ` +
|
package/dist/compensations.js
CHANGED
|
@@ -30,7 +30,7 @@ export const MIGRATED_COMPENSATIONS = [
|
|
|
30
30
|
},
|
|
31
31
|
{
|
|
32
32
|
id: 'T36',
|
|
33
|
-
what: '
|
|
33
|
+
what: '子代终态 tick 缺席时(老服务端 / 丢帧 / 断流)四处防御 sweep 兜住面板行,不让它们永远转圈;终态 tick 在场时按它落终态',
|
|
34
34
|
disposition: 'in-library',
|
|
35
35
|
libraryHalf: 'adapt.ts settlePanelTasks 的四个调用点(tool_end 关卡 / result / turn 末 / abort)',
|
|
36
36
|
chromeArm: 'inline_task_stats',
|
|
@@ -6,6 +6,7 @@ export interface DisplayFace {
|
|
|
6
6
|
readonly unsafe: RegExp | null;
|
|
7
7
|
readonly fold: boolean;
|
|
8
8
|
readonly mark: DisplayMark;
|
|
9
|
+
readonly keepBlanks?: boolean;
|
|
9
10
|
}
|
|
10
11
|
export declare const LEGACY_ESCAPE_FACE: DisplayFace;
|
|
11
12
|
export declare const LEGACY_LABEL_FACE: DisplayFace;
|
|
@@ -20,5 +21,17 @@ export interface DisplayUntrustedOptions {
|
|
|
20
21
|
readonly keepLayout?: boolean;
|
|
21
22
|
readonly mark?: DisplayMark;
|
|
22
23
|
readonly max?: number;
|
|
24
|
+
readonly escapeBackslashes?: boolean;
|
|
25
|
+
readonly blanks?: boolean;
|
|
26
|
+
}
|
|
27
|
+
export interface DisplayMarkSpan {
|
|
28
|
+
readonly start: number;
|
|
29
|
+
readonly end: number;
|
|
30
|
+
readonly codePoint: number;
|
|
31
|
+
}
|
|
32
|
+
export interface DisplayUntrustedMarked {
|
|
33
|
+
readonly text: string;
|
|
34
|
+
readonly marks: readonly DisplayMarkSpan[];
|
|
23
35
|
}
|
|
24
36
|
export declare function displayUntrusted(text: string, opts?: DisplayUntrustedOptions): string;
|
|
37
|
+
export declare function displayUntrustedMarks(text: string, opts?: DisplayUntrustedOptions): DisplayUntrustedMarked;
|