@sema-agent/client-core 0.82.0 → 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 +22 -0
- package/README.md +7 -5
- 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 +37 -7
- 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/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 +2 -0
- package/dist/hitl/askGateWire.d.ts +1 -1
- package/dist/hitl/askGateWire.js +1 -1
- package/dist/hitl/hitlHostSurface.js +2 -3
- package/dist/hitl/parkResolver.d.ts +1 -0
- package/dist/hitl/parkResolver.js +11 -7
- package/dist/hitl/planReviewWire.js +7 -1
- package/dist/hitl/toolApprovalWire.js +3 -7
- package/dist/index.d.ts +2 -0
- package/dist/index.js +2 -0
- package/dist/modelIdentityResolvability.d.ts +31 -0
- package/dist/modelIdentityResolvability.js +145 -0
- package/dist/printToolResultFrame.js +15 -1
- package/dist/resumeRefusalCopy.d.ts +12 -0
- package/dist/resumeRefusalCopy.js +45 -1
- package/dist/seam.d.ts +2 -0
- package/dist/toolResult.d.ts +2 -2
- package/dist/toolResult.js +7 -6
- package/docs/INTEGRATION-CLIENTS.md +123 -10
- package/package.json +3 -3
|
@@ -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
|
+
}
|
|
@@ -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
|
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export type FileHistoryCaptureReading = {
|
|
2
|
+
kind: 'unobserved';
|
|
3
|
+
} | {
|
|
4
|
+
kind: 'not_reported';
|
|
5
|
+
} | {
|
|
6
|
+
kind: 'present';
|
|
7
|
+
word: string;
|
|
8
|
+
};
|
|
9
|
+
export type FileHistoryCaptureMode = 'off' | 'on-always' | 'unknown';
|
|
10
|
+
export declare function projectFileHistoryCaptureCapability(caps: unknown): FileHistoryCaptureReading | undefined;
|
|
11
|
+
export declare function noteEngineCapsForFileHistoryCapture(baseUrl: string, caps: unknown, opts?: {
|
|
12
|
+
generation?: number;
|
|
13
|
+
}): void;
|
|
14
|
+
export declare function observedFileHistoryCapture(baseUrl?: string | undefined): FileHistoryCaptureReading;
|
|
15
|
+
export declare function fileHistoryCaptureMode(reading: FileHistoryCaptureReading): FileHistoryCaptureMode;
|
|
16
|
+
export declare function fileHistoryCaptureDoctorDetail(reading: FileHistoryCaptureReading): string;
|
|
17
|
+
export declare function forgetFileHistoryCaptureReading(baseUrl: string | undefined): void;
|
|
18
|
+
export declare function __resetFileHistoryCaptureReadingsForTests(): void;
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { capForDisplay } from './fleetTaskDesc.js';
|
|
2
|
+
import { engineWireTarget } from './engineWireTarget.js';
|
|
3
|
+
import { createEngineCapReader } from './engineCapReader.js';
|
|
4
|
+
export function projectFileHistoryCaptureCapability(caps) {
|
|
5
|
+
if (caps === null || typeof caps !== 'object' || Array.isArray(caps))
|
|
6
|
+
return undefined;
|
|
7
|
+
if (!Object.hasOwn(caps, 'fileHistoryCapture'))
|
|
8
|
+
return { kind: 'not_reported' };
|
|
9
|
+
const v = caps.fileHistoryCapture;
|
|
10
|
+
if (v === undefined)
|
|
11
|
+
return { kind: 'not_reported' };
|
|
12
|
+
if (typeof v !== 'string' || v.length === 0)
|
|
13
|
+
return undefined;
|
|
14
|
+
return { kind: 'present', word: v };
|
|
15
|
+
}
|
|
16
|
+
const reader = createEngineCapReader({
|
|
17
|
+
name: 'fileHistoryCapture',
|
|
18
|
+
project: projectFileHistoryCaptureCapability,
|
|
19
|
+
makeUnobserved: () => ({ kind: 'unobserved' }),
|
|
20
|
+
});
|
|
21
|
+
export function noteEngineCapsForFileHistoryCapture(baseUrl, caps, opts) {
|
|
22
|
+
reader.note(baseUrl, caps, opts);
|
|
23
|
+
}
|
|
24
|
+
export function observedFileHistoryCapture(baseUrl = engineWireTarget()?.baseUrl) {
|
|
25
|
+
return reader.observed(baseUrl);
|
|
26
|
+
}
|
|
27
|
+
export function fileHistoryCaptureMode(reading) {
|
|
28
|
+
if (reading.kind !== 'present')
|
|
29
|
+
return 'unknown';
|
|
30
|
+
if (reading.word === 'off')
|
|
31
|
+
return 'off';
|
|
32
|
+
if (reading.word === 'on-always')
|
|
33
|
+
return 'on-always';
|
|
34
|
+
return 'unknown';
|
|
35
|
+
}
|
|
36
|
+
const WORD_DETAIL_MAX = 40;
|
|
37
|
+
export function fileHistoryCaptureDoctorDetail(reading) {
|
|
38
|
+
switch (reading.kind) {
|
|
39
|
+
case 'unobserved':
|
|
40
|
+
return 'file history capture not observed — the engine reports it on /v1/capabilities; this process has no usable capabilities reading cached for it, so whether this deployment captures file history is unknown';
|
|
41
|
+
case 'not_reported':
|
|
42
|
+
return 'file history capture not reported by this engine — only newer engines advertise it; this does not say whether file history is captured here';
|
|
43
|
+
case 'present':
|
|
44
|
+
if (reading.word === 'off') {
|
|
45
|
+
return 'file history capture off on this deployment — the engine takes no snapshots of the files it changes; whether code can be rewound here is answered separately by the rewind readings';
|
|
46
|
+
}
|
|
47
|
+
if (reading.word === 'on-always') {
|
|
48
|
+
return 'file history capture always on — the engine snapshots files before changing them in every session, and it cannot be turned off for a single session here';
|
|
49
|
+
}
|
|
50
|
+
return `file history capture reported as "${capForDisplay(reading.word, WORD_DETAIL_MAX)}" — a mode this client does not know yet, so whether it captures is shown as unknown`;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
export function forgetFileHistoryCaptureReading(baseUrl) {
|
|
54
|
+
reader.forget(baseUrl);
|
|
55
|
+
}
|
|
56
|
+
export function __resetFileHistoryCaptureReadingsForTests() {
|
|
57
|
+
reader.__resetForTests();
|
|
58
|
+
}
|
package/dist/gateOutcome.js
CHANGED
|
@@ -146,6 +146,8 @@ export function ccToolDenialKindForToolEnd(frame) {
|
|
|
146
146
|
const stamped = frame._sema_denial_kind;
|
|
147
147
|
if (isCcToolDenialKind(stamped))
|
|
148
148
|
return stamped;
|
|
149
|
+
if (frame._sema_denial_unclassified === true)
|
|
150
|
+
return undefined;
|
|
149
151
|
const outcome = gateOutcomeOf(frame);
|
|
150
152
|
const kind = outcome?.settlement?.kind;
|
|
151
153
|
if (kind === 'human_refused')
|
|
@@ -8,7 +8,7 @@ export type { AskAnsweredOutput } from './gateLedger.js';
|
|
|
8
8
|
export { SEMA_COLLATERAL_ABORT_KEY } from './gateLedger.js';
|
|
9
9
|
export { SEMA_DENIAL_KIND_KEY } from './gateLedger.js';
|
|
10
10
|
export type { GateDenyAttribution } from './gateLedger.js';
|
|
11
|
-
export { toAnsweredOutput, isAlreadyResolvedGateReason, GATE_FAILURE_CODES, nextHopBudget, MAX_GATE_HOPS, MAX_TOTAL_PARKS } from './parkResolver.js';
|
|
11
|
+
export { toAnsweredOutput, isAlreadyResolvedGateReason, isGateStandingErrorCode, GATE_FAILURE_CODES, nextHopBudget, MAX_GATE_HOPS, MAX_TOTAL_PARKS } from './parkResolver.js';
|
|
12
12
|
export type { HopBudget, HopRound } from './parkResolver.js';
|
|
13
13
|
export type { GateOutcome, GateFailureCode } from './parkResolver.js';
|
|
14
14
|
export declare function bridgeAskUserQuestionGates(source: AsyncGenerator<AgentEvent>, deps: AskGateWireDeps, opts?: {
|
package/dist/hitl/askGateWire.js
CHANGED
|
@@ -6,7 +6,7 @@ export { HITL_INTERRUPT_MESSAGE_FOR_TOOL_USE } from './frameRouter.js';
|
|
|
6
6
|
export { HITL_POLICY_DENY_MESSAGE } from './frameRouter.js';
|
|
7
7
|
export { SEMA_COLLATERAL_ABORT_KEY } from './gateLedger.js';
|
|
8
8
|
export { SEMA_DENIAL_KIND_KEY } from './gateLedger.js';
|
|
9
|
-
export { toAnsweredOutput, isAlreadyResolvedGateReason, GATE_FAILURE_CODES, nextHopBudget, MAX_GATE_HOPS, MAX_TOTAL_PARKS } from './parkResolver.js';
|
|
9
|
+
export { toAnsweredOutput, isAlreadyResolvedGateReason, isGateStandingErrorCode, GATE_FAILURE_CODES, nextHopBudget, MAX_GATE_HOPS, MAX_TOTAL_PARKS } from './parkResolver.js';
|
|
10
10
|
export async function* bridgeAskUserQuestionGates(source, deps, opts) {
|
|
11
11
|
const led = createGateLedger();
|
|
12
12
|
const taskId = { current: opts?.taskId };
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { createSessionSlot, DEFAULT_SESSION_KEY } from '../sessionSlot.js';
|
|
2
2
|
import { hostLog } from '../host.js';
|
|
3
3
|
import { unrefTimer } from '../unrefTimer.js';
|
|
4
|
-
import {
|
|
4
|
+
import { decideFailureNote } from '../decideFailureNote.js';
|
|
5
5
|
import { HitlSafetyError } from './hitlBridge.js';
|
|
6
6
|
const hostSurfaceByKey = createSessionSlot();
|
|
7
7
|
const hostSurfaceMissesByKey = new Map();
|
|
@@ -113,8 +113,7 @@ export function observeCancelByDeny(settle, taskId) {
|
|
|
113
113
|
hostLog('debug', `[hitl-cancel-deny] cancel-by-deny no_pending for run ${taskId} (benign — already resolved)`);
|
|
114
114
|
return;
|
|
115
115
|
}
|
|
116
|
-
const
|
|
117
|
-
const rowNote = where === undefined ? '' : ` — ${corruptStoredRowContent(where)}`;
|
|
116
|
+
const rowNote = decideFailureNote(e);
|
|
118
117
|
hostLog('error', `[hitl-cancel-deny] cancel-by-deny FAILED for run ${taskId}: ${String(e)}${rowNote}`);
|
|
119
118
|
surfaceCancelDenyWarn(`${CANCEL_DENY_WARN_TEXT}${rowNote}`);
|
|
120
119
|
})
|
|
@@ -33,6 +33,7 @@ export type GateOutcome = {
|
|
|
33
33
|
currentPending?: GateCurrentPending | undefined;
|
|
34
34
|
};
|
|
35
35
|
export declare function isAlreadyResolvedGateReason(reason: unknown): boolean;
|
|
36
|
+
export declare function isGateStandingErrorCode(code: string | null | undefined): boolean;
|
|
36
37
|
export declare function toAnsweredOutput(questions: unknown[], answer: QuestionAnswer): AskAnsweredOutput;
|
|
37
38
|
export type ParkResolution = {
|
|
38
39
|
kind: 'reattach';
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { putOwnKey } from '../ownKey.js';
|
|
2
2
|
import { DecideTransportRetryExhaustedError, HitlBridge, HitlSafetyError, findPendingForTask, readDecideCurrentPending, readWireErrorCode, } from './hitlBridge.js';
|
|
3
|
+
import { RESUME_POLICY_FOLD_CODE } from '../engineErrorCodes.js';
|
|
3
4
|
import { publishQuestionFrame, registerLocalQuestionResponder, hasQuestionOverlay, } from '../liveQuestionStore.js';
|
|
4
5
|
import { hostLog } from '../host.js';
|
|
5
6
|
import { surfaceFsApprovalAndDecide } from './toolApprovalWire.js';
|
|
@@ -7,7 +8,7 @@ import { observeCancelByDeny } from './hitlHostSurface.js';
|
|
|
7
8
|
import { flushHeldWithInterruptRewrite, isAskTool } from './frameRouter.js';
|
|
8
9
|
import { approvalCallKey, askGateQuestionId } from './gateIdentity.js';
|
|
9
10
|
import { readDecideReceipt } from '../decideReceipt.js';
|
|
10
|
-
import {
|
|
11
|
+
import { decideFailureNote } from '../decideFailureNote.js';
|
|
11
12
|
export const MAX_GATE_HOPS = 2;
|
|
12
13
|
export function nextHopBudget(prev, round) {
|
|
13
14
|
const total = (prev?.total ?? 0) + 1;
|
|
@@ -41,7 +42,14 @@ function alreadyResolvedReasonToken(reason) {
|
|
|
41
42
|
}
|
|
42
43
|
function isAlreadyResolvedFailure(outcome) {
|
|
43
44
|
const code = 'code' in outcome && isGateFailureCode(outcome.code) ? outcome.code : undefined;
|
|
44
|
-
|
|
45
|
+
if (code !== undefined)
|
|
46
|
+
return code === 'no_pending';
|
|
47
|
+
if ('errorCode' in outcome && isGateStandingErrorCode(outcome.errorCode))
|
|
48
|
+
return false;
|
|
49
|
+
return isAlreadyResolvedGateReason(outcome.reason);
|
|
50
|
+
}
|
|
51
|
+
export function isGateStandingErrorCode(code) {
|
|
52
|
+
return code === RESUME_POLICY_FOLD_CODE || code === 'corrupt';
|
|
45
53
|
}
|
|
46
54
|
export function toAnsweredOutput(questions, answer) {
|
|
47
55
|
const answers = {};
|
|
@@ -141,7 +149,7 @@ async function surfaceGateAndDecide(deps, taskId, askArgsByCall, signal, parkGat
|
|
|
141
149
|
kind: 'failed',
|
|
142
150
|
stage: 'decide',
|
|
143
151
|
gatedCallId,
|
|
144
|
-
reason:
|
|
152
|
+
reason: `decide failed: ${String(e)}${decideFailureNote(e)}`,
|
|
145
153
|
...(code !== undefined ? { code } : {}),
|
|
146
154
|
...(e instanceof DecideTransportRetryExhaustedError ? { retryExhausted: true } : {}),
|
|
147
155
|
...(currentPending !== undefined ? { currentPending } : {}),
|
|
@@ -149,10 +157,6 @@ async function surfaceGateAndDecide(deps, taskId, askArgsByCall, signal, parkGat
|
|
|
149
157
|
};
|
|
150
158
|
}
|
|
151
159
|
}
|
|
152
|
-
function withCorruptStoredRow(reason, err) {
|
|
153
|
-
const where = corruptStoredRowWhere(err);
|
|
154
|
-
return where === undefined ? reason : `${reason} — ${corruptStoredRowContent(where)}`;
|
|
155
|
-
}
|
|
156
160
|
function argsByCallOf(led) {
|
|
157
161
|
return new Map(led.gatedStartArgs());
|
|
158
162
|
}
|