@sema-agent/client-core 0.81.1 → 0.82.1
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 +40 -0
- package/README.md +12 -8
- package/dist/adapt/toolCards.d.ts +15 -0
- package/dist/adapt/toolCards.js +34 -19
- package/dist/adapter/activeRunSelfHeal.d.ts +3 -0
- package/dist/adapter/activeRunSelfHeal.js +42 -9
- package/dist/adapter/downstream/eventToSdkMessage.js +7 -4
- package/dist/adapter/downstream/terminalToSdkResult.d.ts +2 -0
- package/dist/adapter/downstream/terminalToSdkResult.js +44 -5
- package/dist/adapter/runStream.js +33 -4
- package/dist/adapter/types.d.ts +2 -0
- package/dist/agentsWireCaps.d.ts +1 -0
- package/dist/agentsWireCaps.js +110 -29
- package/dist/decideFailureNote.d.ts +1 -0
- package/dist/decideFailureNote.js +8 -0
- package/dist/engineErrorCodes.d.ts +1 -0
- package/dist/engineErrorCodes.js +1 -0
- package/dist/engineNoticeCodes.js +2 -0
- package/dist/executionLaneCapability.js +3 -0
- package/dist/fileHistoryCaptureCapability.d.ts +18 -0
- package/dist/fileHistoryCaptureCapability.js +58 -0
- package/dist/gateOutcome.js +22 -2
- package/dist/hitl/askGateWire.d.ts +1 -1
- package/dist/hitl/askGateWire.js +1 -1
- package/dist/hitl/hitlHostSurface.js +6 -4
- package/dist/hitl/parkResolver.d.ts +1 -0
- package/dist/hitl/parkResolver.js +11 -2
- package/dist/hitl/planReviewWire.js +14 -1
- package/dist/hitl/toolApprovalWire.js +3 -2
- package/dist/index.d.ts +4 -0
- package/dist/index.js +4 -0
- package/dist/modelIdentityResolvability.d.ts +31 -0
- package/dist/modelIdentityResolvability.js +145 -0
- package/dist/printToolResultFrame.js +15 -1
- package/dist/registryQuotaUsage.d.ts +44 -0
- package/dist/registryQuotaUsage.js +111 -0
- package/dist/resumeRefusalCopy.d.ts +12 -0
- package/dist/resumeRefusalCopy.js +45 -1
- package/dist/rewindArchiveCapability.d.ts +24 -0
- package/dist/rewindArchiveCapability.js +125 -0
- package/dist/seam.d.ts +2 -0
- package/dist/toolResult.d.ts +2 -2
- package/dist/toolResult.js +7 -6
- package/dist/toolRoster.d.ts +12 -0
- package/dist/toolRoster.js +35 -0
- package/dist/wireErrorTriage.d.ts +4 -0
- package/dist/wireErrorTriage.js +59 -5
- package/dist/wireFailureShape.js +27 -22
- package/docs/INTEGRATION-CLIENTS.md +192 -13
- package/package.json +3 -3
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { abortableSleep } from '../abortableSleep.js';
|
|
2
|
+
import { corruptStoredRowContent, corruptStoredRowWhere } from '../wireErrorTriage.js';
|
|
2
3
|
export const PLAN_REVIEW_GATE_KIND = 'plan_review';
|
|
3
4
|
export const PLAN_REVIEW_GATE_KINDS = [PLAN_REVIEW_GATE_KIND, 'dry_run_review'];
|
|
4
5
|
export const ASK_PARK_GATE_KINDS = ['human', 'irreversible_ask', 'policy_ask', 'tool_approval'];
|
|
@@ -45,12 +46,14 @@ function sessionOpts(deps) {
|
|
|
45
46
|
return typeof sessionId === 'string' && sessionId.length > 0 ? { session: sessionId } : {};
|
|
46
47
|
}
|
|
47
48
|
function describeFailure(e) {
|
|
49
|
+
const where = corruptStoredRowWhere(e);
|
|
50
|
+
const rowNote = where === undefined ? '' : ` — ${corruptStoredRowContent(where)}`;
|
|
48
51
|
if (typeof e === 'object' && e !== null && 'message' in e) {
|
|
49
52
|
const m = e.message;
|
|
50
53
|
if (typeof m === 'string' && m.length > 0)
|
|
51
|
-
return m.length > 200 ? `${m.slice(0, 200)}…` : m
|
|
54
|
+
return `${m.length > 200 ? `${m.slice(0, 200)}…` : m}${rowNote}`;
|
|
52
55
|
}
|
|
53
|
-
return
|
|
56
|
+
return `the engine gave no detail${rowNote}`;
|
|
54
57
|
}
|
|
55
58
|
function readStatus(record) {
|
|
56
59
|
const st = record?.status;
|
|
@@ -76,6 +79,9 @@ export function readCancelRequested(body) {
|
|
|
76
79
|
const v = body.cancelRequested;
|
|
77
80
|
return typeof v === 'boolean' ? v : undefined;
|
|
78
81
|
}
|
|
82
|
+
function reopenRefusedForDecisionInFlight(verdict) {
|
|
83
|
+
return verdict !== null && verdict.reopened === false && verdict._sema_decisionInFlight === true;
|
|
84
|
+
}
|
|
79
85
|
function reopenDelivered(verdict) {
|
|
80
86
|
return verdict.reopened === true && verdict.presented !== false;
|
|
81
87
|
}
|
|
@@ -297,9 +303,14 @@ async function askVerdict(taskId, deps) {
|
|
|
297
303
|
}
|
|
298
304
|
async function planReviewArm(taskId, signal, deps) {
|
|
299
305
|
const verdict = await planVerdict(taskId, deps);
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
306
|
+
if (reopenDelivered(verdict))
|
|
307
|
+
return { kind: 'plan-review-reopened', taskId, firstSight: verdict.firstSight === true };
|
|
308
|
+
return {
|
|
309
|
+
kind: 'plan-review-reopen-failed',
|
|
310
|
+
taskId,
|
|
311
|
+
decidePath: signal.pendingGate?.decidePath ?? null,
|
|
312
|
+
...(reopenRefusedForDecisionInFlight(verdict) ? { decisionInFlight: true } : {}),
|
|
313
|
+
};
|
|
303
314
|
}
|
|
304
315
|
async function askParkArm(taskId, signal, runs, deps) {
|
|
305
316
|
const verdict = await askVerdict(taskId, deps);
|
|
@@ -323,10 +334,16 @@ async function askParkArm(taskId, signal, runs, deps) {
|
|
|
323
334
|
}
|
|
324
335
|
return { kind: 'ask-decided-release-unknown', taskId, lastStatus: release.lastStatus };
|
|
325
336
|
}
|
|
326
|
-
|
|
337
|
+
const inFlight = reopenRefusedForDecisionInFlight(verdict);
|
|
338
|
+
if (verdict.reopened === false && verdict.decidedWithoutCard !== true && verdict.pendingRowGone === true && !inFlight) {
|
|
327
339
|
return staleParkArm(taskId, signal, runs, deps);
|
|
328
340
|
}
|
|
329
|
-
return {
|
|
341
|
+
return {
|
|
342
|
+
kind: 'ask-reopen-failed',
|
|
343
|
+
taskId,
|
|
344
|
+
decidePath: signal.pendingGate?.decidePath ?? null,
|
|
345
|
+
...(inFlight ? { decisionInFlight: true } : {}),
|
|
346
|
+
};
|
|
330
347
|
}
|
|
331
348
|
async function probeRunStatusOnce(taskId, durable, durableGet, deps) {
|
|
332
349
|
let status = null;
|
|
@@ -608,6 +625,10 @@ function injectedSubmissionRow(outcome, following = false) {
|
|
|
608
625
|
: `A system notification was NOT delivered: the engine refused it for the run that is already working${handle} ` +
|
|
609
626
|
`(${outcome.detail}); sema did not retry. The model was not told about it.`;
|
|
610
627
|
}
|
|
628
|
+
if ((outcome.kind === 'ask-reopen-failed' || outcome.kind === 'plan-review-reopen-failed') && outcome.decisionInFlight === true) {
|
|
629
|
+
return (`A system notification was NOT delivered: this session is still held by an earlier run${handle} whose decision ` +
|
|
630
|
+
`is still on its way to the engine, so sema did not retry. The model was not told about it.`);
|
|
631
|
+
}
|
|
611
632
|
switch (selfHealSubmissionDisposition(outcome)) {
|
|
612
633
|
case 'held-for-decision':
|
|
613
634
|
return (`A system notification could not be delivered while an earlier turn${handle} is waiting on a ` +
|
|
@@ -625,6 +646,11 @@ function injectedSubmissionRow(outcome, following = false) {
|
|
|
625
646
|
`sema could not put a decision for it on screen, so it did not retry. The model was not told about it.`);
|
|
626
647
|
}
|
|
627
648
|
}
|
|
649
|
+
function decisionInFlightRow(parkedOn) {
|
|
650
|
+
return (`The previous turn is parked ${parkedOn} and your answer to that card is still on its way to the engine, so ` +
|
|
651
|
+
`sema did not show the card again — answering it a second time would send a second decision. It did NOT ` +
|
|
652
|
+
`cancel the run. Your message was NOT sent; send it again in a moment, once the engine has applied that decision.`);
|
|
653
|
+
}
|
|
628
654
|
function activeRunSelfHealBaseRow(outcome, signal, wayOut, following = false) {
|
|
629
655
|
switch (outcome.kind) {
|
|
630
656
|
case 'decision-pending':
|
|
@@ -662,6 +688,8 @@ function activeRunSelfHealBaseRow(outcome, signal, wayOut, following = false) {
|
|
|
662
688
|
(outcome.lastStatus ? ` (last engine status: ${outcome.lastStatus})` : '') +
|
|
663
689
|
`. sema could not yet confirm it finished, so your message was not sent — wait a moment and send it again.`);
|
|
664
690
|
case 'ask-reopen-failed': {
|
|
691
|
+
if (outcome.decisionInFlight === true)
|
|
692
|
+
return decisionInFlightRow(`waiting for your decision (run ${outcome.taskId})`);
|
|
665
693
|
const viaEngine = outcome.decidePath
|
|
666
694
|
? ` You can also decide it on the engine directly: POST ${outcome.decidePath}.`
|
|
667
695
|
: '';
|
|
@@ -670,6 +698,8 @@ function activeRunSelfHealBaseRow(outcome, signal, wayOut, following = false) {
|
|
|
670
698
|
`Your message was NOT sent; ${wayOut} if you no longer want it.${viaEngine}`);
|
|
671
699
|
}
|
|
672
700
|
case 'plan-review-reopen-failed': {
|
|
701
|
+
if (outcome.decisionInFlight === true)
|
|
702
|
+
return decisionInFlightRow(`on a plan review (run ${outcome.taskId})`);
|
|
673
703
|
const viaEngine = outcome.decidePath
|
|
674
704
|
? ` You can also decide it on the engine directly: POST ${outcome.decidePath}.`
|
|
675
705
|
: '';
|
|
@@ -692,8 +722,11 @@ function activeRunSelfHealBaseRow(outcome, signal, wayOut, following = false) {
|
|
|
692
722
|
const parked = outcome.status ? `parked (status ${outcome.status})` : 'parked';
|
|
693
723
|
const next = outcome.reopened !== null && reopenDelivered(outcome.reopened)
|
|
694
724
|
? `sema has surfaced that decision card — answering it is what lets that run resume and pick your message up.`
|
|
695
|
-
:
|
|
696
|
-
|
|
725
|
+
: reopenRefusedForDecisionInFlight(outcome.reopened)
|
|
726
|
+
? `Your answer to that decision is still on its way to the engine, so sema did not show the card again; ` +
|
|
727
|
+
`that run moves on once the engine applies it.`
|
|
728
|
+
: `sema could not surface that decision card here, so nothing will resume that run until that decision is made; ` +
|
|
729
|
+
`${wayOut} if you no longer want it.`;
|
|
697
730
|
return (`${handle} is not actually working right now: the engine reports it ${parked} on a decision it needs ` +
|
|
698
731
|
`from you, so it queued your message on that park instead of running it. ${next} ${textOnly} ` +
|
|
699
732
|
`Nothing was cancelled, and your message did NOT start a new turn.`);
|
|
@@ -36,9 +36,10 @@ export const INTERNAL_SDK_ARM_TYPES = new Set([
|
|
|
36
36
|
'approval_revoke',
|
|
37
37
|
'message_committed',
|
|
38
38
|
]);
|
|
39
|
-
function assistantArm(ctx, content, eventId) {
|
|
39
|
+
function assistantArm(ctx, content, eventId, parentToolCallId) {
|
|
40
40
|
const id = typeof eventId === 'string' && eventId.length > 0 ? eventId : undefined;
|
|
41
|
-
const
|
|
41
|
+
const parent = typeof parentToolCallId === 'string' && parentToolCallId.length > 0 ? parentToolCallId : undefined;
|
|
42
|
+
const model = parent === undefined && typeof ctx.model === 'string' && ctx.model.length > 0 ? ctx.model : undefined;
|
|
42
43
|
return stamp(ctx, armBody({
|
|
43
44
|
type: 'assistant',
|
|
44
45
|
message: {
|
|
@@ -47,7 +48,7 @@ function assistantArm(ctx, content, eventId) {
|
|
|
47
48
|
...(id !== undefined ? { id } : {}),
|
|
48
49
|
...(model !== undefined ? { model } : {}),
|
|
49
50
|
},
|
|
50
|
-
parent_tool_use_id: null,
|
|
51
|
+
parent_tool_use_id: parent ?? null,
|
|
51
52
|
}));
|
|
52
53
|
}
|
|
53
54
|
function streamEventArm(ctx, event) {
|
|
@@ -136,7 +137,7 @@ export function eventToSdkMessage(ev, ctx) {
|
|
|
136
137
|
? { parentToolCallId: ev.parentToolCallId }
|
|
137
138
|
: {}),
|
|
138
139
|
},
|
|
139
|
-
], ev.eventId));
|
|
140
|
+
], ev.eventId, ev.parentToolCallId));
|
|
140
141
|
case 'compacted': {
|
|
141
142
|
const raw = ev;
|
|
142
143
|
const freedTokens = typeof raw.freedTokens === 'number' && Number.isFinite(raw.freedTokens) && raw.freedTokens >= 0 ? raw.freedTokens : undefined;
|
|
@@ -171,6 +172,7 @@ export function eventToSdkMessage(ev, ctx) {
|
|
|
171
172
|
const toolEndGatedCallId = ev.gatedCallId;
|
|
172
173
|
const collateralAbort = ev._sema_collateral_abort;
|
|
173
174
|
const denialKind = ccToolDenialKindForToolEnd(ev);
|
|
175
|
+
const denialUnclassified = denialKind === undefined && toolEndGate?.disposition.kind === 'denied';
|
|
174
176
|
return projected(stamp(ctx, armBody({
|
|
175
177
|
type: 'tool_end_result',
|
|
176
178
|
toolCallId: ev.toolCallId,
|
|
@@ -192,6 +194,7 @@ export function eventToSdkMessage(ev, ctx) {
|
|
|
192
194
|
: {}),
|
|
193
195
|
...(collateralAbort === true ? { _sema_collateral_abort: true } : {}),
|
|
194
196
|
...(denialKind !== undefined ? { _sema_denial_kind: denialKind } : {}),
|
|
197
|
+
...(denialUnclassified ? { _sema_denial_unclassified: true } : {}),
|
|
195
198
|
...(ev.eventId !== undefined ? { eventId: ev.eventId } : {}),
|
|
196
199
|
...(ev.parentToolCallId !== undefined
|
|
197
200
|
? { parentToolCallId: ev.parentToolCallId }
|
|
@@ -20,6 +20,8 @@ export interface TerminalStreamObserved {
|
|
|
20
20
|
readonly denialKindByCallId?: ReadonlyMap<string, string>;
|
|
21
21
|
readonly successfulToolEndObserved?: boolean;
|
|
22
22
|
readonly streamSawRunOpen?: boolean;
|
|
23
|
+
readonly deniedToolNameByCallId?: ReadonlyMap<string, string | undefined>;
|
|
24
|
+
readonly deniedCallsOverflowed?: boolean;
|
|
23
25
|
}
|
|
24
26
|
export declare const TOOL_INPUT_JOIN_MAX_CALLS = 4096;
|
|
25
27
|
export interface SemaPermissionDenial {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { isCcToolDenialKind, isGateDeniedByWord } from '../../gateVocabulary.js';
|
|
1
|
+
import { isCcToolDenialKind, isCcToolDenialKindADenial, isGateDeniedByWord } from '../../gateVocabulary.js';
|
|
2
2
|
import { stamp } from '../types.js';
|
|
3
3
|
import { putOwnKey } from '../../ownKey.js';
|
|
4
4
|
import { readEffectiveReasoning, readEffectiveMemoryScopes } from '../../effectiveFacts.js';
|
|
@@ -65,11 +65,10 @@ function permissionDenialParts(stats, observed) {
|
|
|
65
65
|
const gateDeniedByByCallId = observed?.gateDeniedByByCallId;
|
|
66
66
|
const denialKindByCallId = observed?.denialKindByCallId;
|
|
67
67
|
const hr = stats?.humanReview;
|
|
68
|
-
const
|
|
69
|
-
if (!Array.isArray(gates))
|
|
70
|
-
return { permission_denials: [], _sema_permission_denials_absent: true };
|
|
68
|
+
const gatesRaw = hr !== null && typeof hr === 'object' ? hr.gates : undefined;
|
|
71
69
|
const denials = [];
|
|
72
|
-
|
|
70
|
+
const gates = Array.isArray(gatesRaw) ? gatesRaw : [];
|
|
71
|
+
let complete = Array.isArray(gatesRaw);
|
|
73
72
|
for (const g of gates) {
|
|
74
73
|
if (g === null || typeof g !== 'object') {
|
|
75
74
|
complete = false;
|
|
@@ -125,6 +124,43 @@ function permissionDenialParts(stats, observed) {
|
|
|
125
124
|
...(toolArg !== undefined ? { _sema_tool_arg: toolArg } : {}),
|
|
126
125
|
});
|
|
127
126
|
}
|
|
127
|
+
const deniedToolNameByCallId = observed?.deniedToolNameByCallId;
|
|
128
|
+
if (deniedToolNameByCallId !== undefined) {
|
|
129
|
+
const ledgerDeniedIds = new Set();
|
|
130
|
+
let ledgerHasIdlessDenial = false;
|
|
131
|
+
for (const d of denials) {
|
|
132
|
+
if (d.tool_use_id !== undefined)
|
|
133
|
+
ledgerDeniedIds.add(d.tool_use_id);
|
|
134
|
+
else
|
|
135
|
+
ledgerHasIdlessDenial = true;
|
|
136
|
+
}
|
|
137
|
+
for (const [callId, toolNameRaw] of deniedToolNameByCallId) {
|
|
138
|
+
if (typeof callId !== 'string' || callId.length === 0 || ledgerDeniedIds.has(callId))
|
|
139
|
+
continue;
|
|
140
|
+
const mergedKind = denialKindByCallId?.has(callId) === true ? denialKindByCallId.get(callId) : undefined;
|
|
141
|
+
if (isCcToolDenialKind(mergedKind) && !isCcToolDenialKindADenial(mergedKind))
|
|
142
|
+
continue;
|
|
143
|
+
if (ledgerHasIdlessDenial) {
|
|
144
|
+
complete = false;
|
|
145
|
+
continue;
|
|
146
|
+
}
|
|
147
|
+
const toolName = nonEmptyStr(toolNameRaw);
|
|
148
|
+
const joinedInput = toolInputByCallId?.has(callId) === true ? joinableToolInput(toolInputByCallId.get(callId)) : undefined;
|
|
149
|
+
const deniedByRaw = gateDeniedByByCallId?.has(callId) === true ? gateDeniedByByCallId.get(callId) : undefined;
|
|
150
|
+
const deniedBy = typeof deniedByRaw === 'string' && isGateDeniedByWord(deniedByRaw) ? deniedByRaw : undefined;
|
|
151
|
+
const toolDenialKind = isCcToolDenialKind(mergedKind) ? mergedKind : undefined;
|
|
152
|
+
denials.push({
|
|
153
|
+
...(toolName !== undefined ? { tool_name: toolName } : {}),
|
|
154
|
+
tool_use_id: callId,
|
|
155
|
+
...(joinedInput !== undefined ? { tool_input: joinedInput, _sema_tool_input_source: 'tool_start' } : {}),
|
|
156
|
+
...(deniedBy !== undefined ? { deniedBy, _sema_denied_by_source: 'tool_end' } : {}),
|
|
157
|
+
...(toolDenialKind !== undefined ? { toolDenialKind } : {}),
|
|
158
|
+
});
|
|
159
|
+
ledgerDeniedIds.add(callId);
|
|
160
|
+
}
|
|
161
|
+
if (observed?.deniedCallsOverflowed === true)
|
|
162
|
+
complete = false;
|
|
163
|
+
}
|
|
128
164
|
const ccList = denials.filter((d) => d.tool_name !== undefined && d.tool_use_id !== undefined && d.tool_input !== undefined);
|
|
129
165
|
const claimable = complete && ccList.length === denials.length;
|
|
130
166
|
const zeroOutputDenied = denials.length > 0 && observed?.streamSawRunOpen === true && observed.successfulToolEndObserved === false;
|
|
@@ -200,15 +236,18 @@ function structuredOutputParts(r) {
|
|
|
200
236
|
function effectiveFactParts(rec) {
|
|
201
237
|
const reasoning = readEffectiveReasoning(rec.effectiveReasoning);
|
|
202
238
|
const scopes = readEffectiveMemoryScopes(rec.effectiveMemoryScopes);
|
|
239
|
+
const responseModel = rec.responseModel;
|
|
203
240
|
return {
|
|
204
241
|
...(reasoning !== undefined ? { _sema_effective_reasoning: reasoning } : {}),
|
|
205
242
|
...(scopes !== undefined ? { _sema_effective_memory_scopes: scopes } : {}),
|
|
243
|
+
...(typeof responseModel === 'string' && responseModel.length > 0 ? { _sema_response_model: responseModel } : {}),
|
|
206
244
|
};
|
|
207
245
|
}
|
|
208
246
|
function reemitEffectiveFacts(f) {
|
|
209
247
|
const out = {
|
|
210
248
|
...(f?._sema_effective_reasoning !== undefined ? { _sema_effective_reasoning: f._sema_effective_reasoning } : {}),
|
|
211
249
|
...(f?._sema_effective_memory_scopes !== undefined ? { _sema_effective_memory_scopes: f._sema_effective_memory_scopes } : {}),
|
|
250
|
+
...(f?._sema_response_model !== undefined ? { _sema_response_model: f._sema_response_model } : {}),
|
|
212
251
|
};
|
|
213
252
|
const _keySetPin = true;
|
|
214
253
|
void _keySetPin;
|
|
@@ -6,7 +6,7 @@ import { coerceOutput, publishSubagentContentEvent } from '../subagentContentSto
|
|
|
6
6
|
import { ACTIVE_RUN_BUSY_ERROR_CODE, OUTPUT_INVALID, isLimitsExceededCode } from '../engineErrorCodes.js';
|
|
7
7
|
import { isReviewPark, readRunTerminal, runTerminalCode } from '../runTerminal.js';
|
|
8
8
|
import { ccToolDenialKindForToolEnd, gateDeniedBy, gateOutcomeOf } from '../gateOutcome.js';
|
|
9
|
-
import { isCcToolDenialKind, isGateDeniedByWord } from '../gateVocabulary.js';
|
|
9
|
+
import { isCcToolDenialKind, isCcToolDenialKindADenial, isGateDeniedByWord } from '../gateVocabulary.js';
|
|
10
10
|
const mainLane = () => ({ lane: 'main' });
|
|
11
11
|
function emitChromeFireAndForget(ctx, event) {
|
|
12
12
|
try {
|
|
@@ -185,6 +185,8 @@ async function* runStreamInner(events, ctx, handle = {}) {
|
|
|
185
185
|
const denialKindSourceByCallId = new Map();
|
|
186
186
|
const denialAmbiguous = new Set();
|
|
187
187
|
let denialJoinOverflowed = false;
|
|
188
|
+
const deniedToolNameByCallId = new Map();
|
|
189
|
+
let deniedCallsOverflowed = false;
|
|
188
190
|
let successfulToolEndObserved = false;
|
|
189
191
|
let streamSawRunOpen = false;
|
|
190
192
|
const sameArgs = (a, b) => {
|
|
@@ -229,7 +231,8 @@ async function* runStreamInner(events, ctx, handle = {}) {
|
|
|
229
231
|
if (ev.type === 'tool_end') {
|
|
230
232
|
const endCallId = ev.toolCallId;
|
|
231
233
|
if (typeof endCallId === 'string' && endCallId.length > 0) {
|
|
232
|
-
const
|
|
234
|
+
const endGate = gateOutcomeOf(ev);
|
|
235
|
+
const deniedByRaw = gateDeniedBy(endGate);
|
|
233
236
|
const deniedBy = isGateDeniedByWord(deniedByRaw) ? deniedByRaw : undefined;
|
|
234
237
|
const kind = ccToolDenialKindForToolEnd(ev);
|
|
235
238
|
const kindSource = kind === undefined
|
|
@@ -237,6 +240,23 @@ async function* runStreamInner(events, ctx, handle = {}) {
|
|
|
237
240
|
: isCcToolDenialKind(ev._sema_denial_kind)
|
|
238
241
|
? 'local'
|
|
239
242
|
: 'wire';
|
|
243
|
+
const deniedCall = endGate !== undefined
|
|
244
|
+
? endGate.disposition.kind === 'denied' && (kind === undefined || isCcToolDenialKindADenial(kind))
|
|
245
|
+
: kind !== undefined && isCcToolDenialKindADenial(kind);
|
|
246
|
+
if (deniedCall) {
|
|
247
|
+
const endToolNameRaw = ev.toolName;
|
|
248
|
+
const endToolName = typeof endToolNameRaw === 'string' && endToolNameRaw.length > 0 ? endToolNameRaw : undefined;
|
|
249
|
+
if (deniedToolNameByCallId.has(endCallId)) {
|
|
250
|
+
if (deniedToolNameByCallId.get(endCallId) !== endToolName)
|
|
251
|
+
deniedToolNameByCallId.set(endCallId, undefined);
|
|
252
|
+
}
|
|
253
|
+
else if (deniedToolNameByCallId.size >= TOOL_INPUT_JOIN_MAX_CALLS) {
|
|
254
|
+
deniedCallsOverflowed = true;
|
|
255
|
+
}
|
|
256
|
+
else if (!deniedCallsOverflowed) {
|
|
257
|
+
deniedToolNameByCallId.set(endCallId, endToolName);
|
|
258
|
+
}
|
|
259
|
+
}
|
|
240
260
|
if (deniedBy !== undefined || kind !== undefined) {
|
|
241
261
|
const isNew = (deniedBy !== undefined && !gateDeniedByByCallId.has(endCallId)) ||
|
|
242
262
|
(kind !== undefined && !denialKindByCallId.has(endCallId));
|
|
@@ -298,14 +318,18 @@ async function* runStreamInner(events, ctx, handle = {}) {
|
|
|
298
318
|
output: coerceOutput(sub.output),
|
|
299
319
|
isError: sub.isError,
|
|
300
320
|
});
|
|
301
|
-
|
|
321
|
+
if (!(ctx.lane === 'print' && (sub.type === 'tool_start' || sub.type === 'tool_end')))
|
|
322
|
+
continue;
|
|
302
323
|
}
|
|
303
324
|
if (ev.type === 'turn_end') {
|
|
304
325
|
const usageMissing = ev.usageMissing === true;
|
|
305
326
|
if (usageMissing)
|
|
306
327
|
usageMissingObserved = true;
|
|
307
|
-
const
|
|
328
|
+
const turnEndLoose = ev;
|
|
329
|
+
const stopReasonRaw = turnEndLoose.stopReason;
|
|
308
330
|
const stopWord = typeof stopReasonRaw === 'string' && stopReasonRaw.length > 0 ? stopReasonRaw : undefined;
|
|
331
|
+
const responseModelRaw = turnEndLoose.responseModel;
|
|
332
|
+
const responseModelWord = typeof responseModelRaw === 'string' && responseModelRaw.length > 0 ? responseModelRaw : undefined;
|
|
309
333
|
const rawUsage = ev.usage;
|
|
310
334
|
const engineUsage = typeof rawUsage === 'object' && rawUsage !== null && !Array.isArray(rawUsage)
|
|
311
335
|
? rawUsage
|
|
@@ -340,6 +364,7 @@ async function* runStreamInner(events, ctx, handle = {}) {
|
|
|
340
364
|
engineUsage,
|
|
341
365
|
...(usageMissing ? { usageMissing: true } : {}),
|
|
342
366
|
...(stopWord !== undefined ? { stopReason: stopWord } : {}),
|
|
367
|
+
...(responseModelWord !== undefined ? { responseModel: responseModelWord } : {}),
|
|
343
368
|
});
|
|
344
369
|
}
|
|
345
370
|
}
|
|
@@ -362,6 +387,7 @@ async function* runStreamInner(events, ctx, handle = {}) {
|
|
|
362
387
|
engineUsage,
|
|
363
388
|
...(usageMissing ? { usageMissing: true } : {}),
|
|
364
389
|
...(stopWord !== undefined ? { stopReason: stopWord } : {}),
|
|
390
|
+
...(responseModelWord !== undefined ? { responseModel: responseModelWord } : {}),
|
|
365
391
|
});
|
|
366
392
|
}
|
|
367
393
|
const rowKey = sourceTaskId !== undefined ? `s:${sourceTaskId}` : `p:${taskId}`;
|
|
@@ -389,6 +415,7 @@ async function* runStreamInner(events, ctx, handle = {}) {
|
|
|
389
415
|
? { outputTokens }
|
|
390
416
|
: {}),
|
|
391
417
|
...(stopWord !== undefined ? { _sema_stop_reason: stopWord } : {}),
|
|
418
|
+
...(responseModelWord !== undefined ? { _sema_response_model: responseModelWord } : {}),
|
|
392
419
|
};
|
|
393
420
|
}
|
|
394
421
|
continue;
|
|
@@ -453,6 +480,8 @@ async function* runStreamInner(events, ctx, handle = {}) {
|
|
|
453
480
|
denialKindByCallId,
|
|
454
481
|
successfulToolEndObserved,
|
|
455
482
|
streamSawRunOpen,
|
|
483
|
+
deniedToolNameByCallId,
|
|
484
|
+
deniedCallsOverflowed,
|
|
456
485
|
});
|
|
457
486
|
const outcome = resultFrame.
|
|
458
487
|
_sema_outcome;
|
package/dist/adapter/types.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { AgentEvent } from '@sema-agent/sdk';
|
|
2
2
|
import type { ModelUsage, SDKMessage } from '@sema-agent/agent-types';
|
|
3
3
|
import type { ChromeEvent } from '../seam.js';
|
|
4
|
+
import type { RequestLane } from '../request/taskRequest.js';
|
|
4
5
|
export type { ModelUsage, SDKMessage };
|
|
5
6
|
export type StampedAgentEvent = AgentEvent & {
|
|
6
7
|
id?: string;
|
|
@@ -16,6 +17,7 @@ export interface EmitContext {
|
|
|
16
17
|
sessionId: string;
|
|
17
18
|
startedAtMs?: number;
|
|
18
19
|
model?: string;
|
|
20
|
+
lane?: RequestLane;
|
|
19
21
|
emitChrome?(event: ChromeEvent): void | Promise<void>;
|
|
20
22
|
onDroppedFrame?(info: DroppedFrameInfo): void;
|
|
21
23
|
}
|
package/dist/agentsWireCaps.d.ts
CHANGED
|
@@ -69,6 +69,7 @@ export type WireConfig = {
|
|
|
69
69
|
};
|
|
70
70
|
export type ProjectorFn = () => Promise<ProjectAgentsResult>;
|
|
71
71
|
export declare function prepareTaskAgentsWireWith(cfg: WireConfig, projector: ProjectorFn): Promise<void>;
|
|
72
|
+
export declare function invalidateTaskAgentsWire(): Promise<void>;
|
|
72
73
|
export declare function awaitTaskAgentsWire(timeoutMs?: number): Promise<void>;
|
|
73
74
|
export declare function taskAgentsField(): {
|
|
74
75
|
agents?: TaskAgentWireItem[];
|
package/dist/agentsWireCaps.js
CHANGED
|
@@ -194,6 +194,7 @@ let preparedKey = null;
|
|
|
194
194
|
let preparedPromise = null;
|
|
195
195
|
let payload;
|
|
196
196
|
let capsVerdict;
|
|
197
|
+
let settleGeneration = null;
|
|
197
198
|
const emittedWarnings = new Set();
|
|
198
199
|
function warnOnce(msg) {
|
|
199
200
|
if (emittedWarnings.has(msg))
|
|
@@ -201,30 +202,57 @@ function warnOnce(msg) {
|
|
|
201
202
|
emittedWarnings.add(msg);
|
|
202
203
|
console.error(`[sema] ${msg}`);
|
|
203
204
|
}
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
principal: cfg.principal,
|
|
210
|
-
}),
|
|
211
|
-
projector(),
|
|
212
|
-
]);
|
|
213
|
-
capsVerdict = caps;
|
|
214
|
-
if (projected.agents && caps === false) {
|
|
215
|
-
warnOnce(`agents wire: this engine does not support per-task custom agents (capabilities.taskAgents absent/false — server <1.205 or multi-tenant) — ${projected.agents.length} custom agent definition(s) will NOT be sent`);
|
|
216
|
-
payload = undefined;
|
|
217
|
-
return;
|
|
218
|
-
}
|
|
205
|
+
function applyProjectionIfCurrent(generation, projected) {
|
|
206
|
+
if (generation === null || preparedPromise !== generation)
|
|
207
|
+
return false;
|
|
208
|
+
const accepted = capsVerdict !== false;
|
|
209
|
+
if (accepted) {
|
|
219
210
|
for (const w of projected.warnings)
|
|
220
211
|
warnOnce(w);
|
|
221
212
|
payload = projected.agents;
|
|
213
|
+
}
|
|
214
|
+
settleGeneration?.();
|
|
215
|
+
return accepted;
|
|
216
|
+
}
|
|
217
|
+
function startCapsProbe(cfg) {
|
|
218
|
+
const land = (verdict) => {
|
|
219
|
+
if (capsVerdict !== probe)
|
|
220
|
+
return verdict;
|
|
221
|
+
capsVerdict = verdict;
|
|
222
|
+
if (verdict === false) {
|
|
223
|
+
payload = undefined;
|
|
224
|
+
settleGeneration?.();
|
|
225
|
+
}
|
|
226
|
+
return verdict;
|
|
227
|
+
};
|
|
228
|
+
const probe = engineSupportsTaskAgents(cfg.baseUrl, {
|
|
229
|
+
authToken: cfg.authToken,
|
|
230
|
+
principal: cfg.principal,
|
|
231
|
+
}).then(land, () => land(undefined));
|
|
232
|
+
capsVerdict = probe;
|
|
233
|
+
return probe;
|
|
234
|
+
}
|
|
235
|
+
function capsSettled() {
|
|
236
|
+
return capsVerdict instanceof Promise ? capsVerdict : Promise.resolve();
|
|
237
|
+
}
|
|
238
|
+
async function runPrepare(cfg, projector, probe, generation) {
|
|
239
|
+
try {
|
|
240
|
+
const [caps, projected] = await Promise.all([probe, projector()]);
|
|
241
|
+
if (caps === false) {
|
|
242
|
+
if (projected.agents && preparedKey === cfg.baseUrl) {
|
|
243
|
+
warnOnce(`agents wire: this engine does not support per-task custom agents (capabilities.taskAgents absent/false — server <1.205 or multi-tenant) — ${projected.agents.length} custom agent definition(s) will NOT be sent`);
|
|
244
|
+
}
|
|
245
|
+
return;
|
|
246
|
+
}
|
|
247
|
+
if (!applyProjectionIfCurrent(generation, projected))
|
|
248
|
+
return;
|
|
222
249
|
if (payload && hostEnv().SEMA_DEBUG) {
|
|
223
250
|
console.error(`[sema] agents wire: ${payload.length} custom agent(s) → TaskRequest.agents (${payload.map(a => a.name).join(', ')})`);
|
|
224
251
|
}
|
|
225
252
|
}
|
|
226
253
|
catch (e) {
|
|
227
|
-
|
|
254
|
+
if (preparedPromise === generation)
|
|
255
|
+
payload = undefined;
|
|
228
256
|
if (hostEnv().SEMA_DEBUG) {
|
|
229
257
|
console.error('[sema] agents wire prepare soft-failed (no agents stamped):', e);
|
|
230
258
|
}
|
|
@@ -235,21 +263,66 @@ export function prepareTaskAgentsWireWith(cfg, projector) {
|
|
|
235
263
|
if (preparedPromise && preparedKey === key)
|
|
236
264
|
return preparedPromise;
|
|
237
265
|
preparedKey = key;
|
|
238
|
-
preparedPromise = runPrepare(cfg, projector);
|
|
239
266
|
activeProjector = projector;
|
|
240
|
-
|
|
267
|
+
const probe = startCapsProbe(cfg);
|
|
268
|
+
return startGeneration(generation => runPrepare(cfg, projector, probe, generation));
|
|
269
|
+
}
|
|
270
|
+
function startGeneration(run) {
|
|
271
|
+
let settle;
|
|
272
|
+
const generation = new Promise(resolve => {
|
|
273
|
+
settle = resolve;
|
|
274
|
+
});
|
|
275
|
+
const superseded = settleGeneration;
|
|
276
|
+
preparedPromise = generation;
|
|
277
|
+
settleGeneration = settle;
|
|
278
|
+
superseded?.();
|
|
279
|
+
void run(generation).then(settle, settle);
|
|
280
|
+
return generation;
|
|
281
|
+
}
|
|
282
|
+
export function invalidateTaskAgentsWire() {
|
|
283
|
+
const projector = activeProjector;
|
|
284
|
+
if (!preparedPromise || !projector)
|
|
285
|
+
return Promise.resolve();
|
|
286
|
+
payload = undefined;
|
|
287
|
+
return startGeneration(generation => runReproject(projector, generation));
|
|
288
|
+
}
|
|
289
|
+
async function runReproject(projector, generation) {
|
|
290
|
+
try {
|
|
291
|
+
let projected;
|
|
292
|
+
try {
|
|
293
|
+
projected = await projector();
|
|
294
|
+
}
|
|
295
|
+
catch (e) {
|
|
296
|
+
if (hostEnv().SEMA_DEBUG) {
|
|
297
|
+
console.error('[sema] agents wire re-project soft-failed (no agents stamped until the next successful projection):', e);
|
|
298
|
+
}
|
|
299
|
+
return;
|
|
300
|
+
}
|
|
301
|
+
await capsSettled();
|
|
302
|
+
applyProjectionIfCurrent(generation, projected);
|
|
303
|
+
}
|
|
304
|
+
catch {
|
|
305
|
+
}
|
|
241
306
|
}
|
|
242
307
|
export async function awaitTaskAgentsWire(timeoutMs = 4000) {
|
|
243
308
|
if (!preparedPromise)
|
|
244
309
|
return;
|
|
310
|
+
if (capsVerdict === false)
|
|
311
|
+
return;
|
|
245
312
|
let timeoutHandle;
|
|
313
|
+
let timedOut = false;
|
|
314
|
+
const deadline = new Promise(resolve => {
|
|
315
|
+
timeoutHandle = setTimeout(() => {
|
|
316
|
+
timedOut = true;
|
|
317
|
+
resolve();
|
|
318
|
+
}, timeoutMs);
|
|
319
|
+
});
|
|
246
320
|
try {
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
]);
|
|
321
|
+
let waited = null;
|
|
322
|
+
while (!timedOut && preparedPromise !== null && preparedPromise !== waited) {
|
|
323
|
+
waited = preparedPromise;
|
|
324
|
+
await Promise.race([waited.catch(() => { }), deadline]);
|
|
325
|
+
}
|
|
253
326
|
}
|
|
254
327
|
finally {
|
|
255
328
|
if (timeoutHandle !== undefined)
|
|
@@ -259,11 +332,18 @@ export async function awaitTaskAgentsWire(timeoutMs = 4000) {
|
|
|
259
332
|
export function taskAgentsField() {
|
|
260
333
|
const current = payload;
|
|
261
334
|
if (preparedPromise && capsVerdict !== false && activeProjector) {
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
335
|
+
const generation = preparedPromise;
|
|
336
|
+
let refreshing;
|
|
337
|
+
try {
|
|
338
|
+
refreshing = activeProjector();
|
|
339
|
+
}
|
|
340
|
+
catch {
|
|
341
|
+
return current ? { agents: current } : {};
|
|
342
|
+
}
|
|
343
|
+
void refreshing
|
|
344
|
+
.then(async (p) => {
|
|
345
|
+
await capsSettled();
|
|
346
|
+
applyProjectionIfCurrent(generation, p);
|
|
267
347
|
})
|
|
268
348
|
.catch(() => { });
|
|
269
349
|
}
|
|
@@ -273,6 +353,7 @@ export function _resetAgentsWireForTest() {
|
|
|
273
353
|
activeProjector = null;
|
|
274
354
|
preparedKey = null;
|
|
275
355
|
preparedPromise = null;
|
|
356
|
+
settleGeneration = null;
|
|
276
357
|
payload = undefined;
|
|
277
358
|
capsVerdict = undefined;
|
|
278
359
|
emittedWarnings.clear();
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function decideFailureNote(err: unknown): string;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { corruptStoredRowContent, corruptStoredRowWhere } from './wireErrorTriage.js';
|
|
2
|
+
import { resumePolicyBlockContent, resumePolicyBlockFromError } from './resumeRefusalCopy.js';
|
|
3
|
+
export function decideFailureNote(err) {
|
|
4
|
+
const where = corruptStoredRowWhere(err);
|
|
5
|
+
const fold = resumePolicyBlockFromError(err);
|
|
6
|
+
return ((where === undefined ? '' : ` — ${corruptStoredRowContent(where)}`) +
|
|
7
|
+
(fold === null ? '' : ` — ${resumePolicyBlockContent(fold)}`));
|
|
8
|
+
}
|
|
@@ -34,6 +34,7 @@ export declare const STOP_NOT_LOCAL = "stop.not_local";
|
|
|
34
34
|
export declare const STOP_PARKED = "stop.parked";
|
|
35
35
|
export declare const STOP_CONFLICT_CODES: readonly ["stop.park_arbiter_unreachable", "stop.park_resume_won", "stop.not_landed", "stop.not_local", "stop.parked"];
|
|
36
36
|
export declare const OUTPUT_INVALID = "output.invalid";
|
|
37
|
+
export declare const RESUME_POLICY_FOLD_CODE = "resume_blocked_by_policy";
|
|
37
38
|
export declare const RESUME_AT_ERROR_CODE_PREFIX = "resume_at.";
|
|
38
39
|
export declare const REWIND_ERROR_CODE_PREFIXES: readonly ["resume_at.", "rewind_snapshot."];
|
|
39
40
|
export declare function isRewindFamilyCode(code: string | undefined): boolean;
|
package/dist/engineErrorCodes.js
CHANGED
|
@@ -69,6 +69,7 @@ export const STOP_CONFLICT_CODES = [
|
|
|
69
69
|
STOP_PARKED,
|
|
70
70
|
];
|
|
71
71
|
export const OUTPUT_INVALID = 'output.invalid';
|
|
72
|
+
export const RESUME_POLICY_FOLD_CODE = 'resume_blocked_by_policy';
|
|
72
73
|
export const RESUME_AT_ERROR_CODE_PREFIX = 'resume_at.';
|
|
73
74
|
export const REWIND_ERROR_CODE_PREFIXES = [RESUME_AT_ERROR_CODE_PREFIX, 'rewind_snapshot.'];
|
|
74
75
|
export function isRewindFamilyCode(code) {
|
|
@@ -55,6 +55,7 @@ export const ENGINE_NOTICE_CODES = Object.freeze([
|
|
|
55
55
|
'route.fallback_to_primary',
|
|
56
56
|
'route.classifier_unroutable',
|
|
57
57
|
'route.base_url_changed_key_unchanged',
|
|
58
|
+
'route.response_model_mismatch',
|
|
58
59
|
'task.user_steer_undrained',
|
|
59
60
|
'task.user_followup_undrained',
|
|
60
61
|
'steering.parked_input_blocked',
|
|
@@ -129,6 +130,7 @@ export const ENGINE_NOTICE_AUDIENCE = Object.freeze({
|
|
|
129
130
|
'route.fallback_to_primary': 'operator',
|
|
130
131
|
'route.classifier_unroutable': 'operator',
|
|
131
132
|
'route.base_url_changed_key_unchanged': 'operator',
|
|
133
|
+
'route.response_model_mismatch': 'operator',
|
|
132
134
|
'task.user_steer_undrained': 'user',
|
|
133
135
|
'task.user_followup_undrained': 'user',
|
|
134
136
|
'steering.parked_input_blocked': 'user',
|
|
@@ -51,6 +51,9 @@ export function executionLaneDoctorDetail(reading) {
|
|
|
51
51
|
if (reading.view.toolsOnThisHost) {
|
|
52
52
|
return `execution lane ${lane} — tools run on the engine host (same filesystem), so skills that have a directory of their own can carry a baseDir`;
|
|
53
53
|
}
|
|
54
|
+
if (reading.view.provider === 'in-process') {
|
|
55
|
+
return 'no execution lane on this deployment (in-process) — the engine has no machine of its own for running tools here, so its built-in file and shell tools are not mounted and skill baseDir is never sent; whether a given run had tools is answered by its tool roster';
|
|
56
|
+
}
|
|
54
57
|
return `execution lane ${lane} — tools run on a different machine than the engine, so skill baseDir is never sent on this deployment`;
|
|
55
58
|
}
|
|
56
59
|
}
|