@sema-agent/client-core 0.84.1 → 0.85.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 +206 -0
- package/README.md +20 -13
- package/dist/adapt/arms.js +1 -1
- package/dist/adapt.d.ts +1 -1
- package/dist/adapt.js +1 -1
- package/dist/adapter/activeRunSelfHeal.d.ts +3 -0
- package/dist/adapter/activeRunSelfHeal.js +149 -17
- package/dist/adapter/downstream/terminalToSdkResult.js +9 -5
- package/dist/adapter/runStream.js +6 -2
- package/dist/agentSession/engineSessionBackgroundStop.d.ts +50 -0
- package/dist/agentSession/engineSessionBackgroundStop.js +291 -0
- package/dist/agentsWireCaps.d.ts +2 -1
- package/dist/cloudConfigWireCaps.js +2 -1
- package/dist/corruptSession.d.ts +3 -0
- package/dist/corruptSession.js +29 -0
- package/dist/corruptSessionCopy.d.ts +1 -0
- package/dist/corruptSessionCopy.js +4 -0
- package/dist/decideFailureNote.js +4 -1
- package/dist/decideReceipt.d.ts +6 -0
- package/dist/decideReceipt.js +30 -1
- package/dist/effortWire.d.ts +2 -1
- package/dist/effortWire.js +2 -4
- package/dist/engineErrorCodes.d.ts +9 -0
- package/dist/engineErrorCodes.js +29 -0
- package/dist/engineNoticeCodes.d.ts +9 -0
- package/dist/engineNoticeCodes.js +44 -156
- package/dist/engineWireFor.d.ts +43 -0
- package/dist/engineWireFor.js +399 -0
- package/dist/engineWireSdk.d.ts +4 -0
- package/dist/fileHistoryCaptureCapability.d.ts +4 -1
- package/dist/fileHistoryCaptureCapability.js +43 -9
- package/dist/gateVocabulary.d.ts +1 -2
- package/dist/gateVocabulary.js +1 -22
- package/dist/generated/engineNoticeTables.d.ts +3 -0
- package/dist/generated/engineNoticeTables.js +156 -0
- package/dist/generated/toolNameTables.d.ts +2 -0
- package/dist/generated/toolNameTables.js +43 -0
- package/dist/headlessPermissionModeWire.js +2 -4
- package/dist/hitl/armedGateRegistry.d.ts +1 -0
- package/dist/hitl/armedGateRegistry.js +1 -1
- package/dist/hitl/frameRouter.js +4 -2
- package/dist/hitl/parkResolver.js +3 -1
- package/dist/hitl/planReviewWire.d.ts +11 -0
- package/dist/hitl/planReviewWire.js +191 -72
- package/dist/hitl/rosterMountedNames.d.ts +2 -0
- package/dist/hitl/rosterMountedNames.js +38 -0
- package/dist/hitl/sessionPolicyDeliverable.d.ts +6 -2
- package/dist/hitl/sessionPolicyDeliverable.js +25 -45
- package/dist/hitl/sessionPolicyWire.js +1 -38
- package/dist/hitl/toolApprovalWire.d.ts +4 -2
- package/dist/hitl/toolApprovalWire.js +4 -2
- package/dist/host.d.ts +1 -1
- package/dist/host.js +11 -2
- package/dist/index.d.ts +10 -4
- package/dist/index.js +7 -3
- package/dist/permissionWireCaps.d.ts +2 -1
- package/dist/permissionWireCaps.js +2 -7
- package/dist/request/taskRequest.d.ts +4 -0
- package/dist/request/taskRequest.js +30 -1
- package/dist/resumeRefusalCopy.js +4 -2
- package/dist/runTerminal.js +4 -0
- package/dist/sandboxWire.js +1 -1
- package/dist/sealedKeyCapability.d.ts +17 -0
- package/dist/sealedKeyCapability.js +42 -0
- package/dist/seatContract.d.ts +2 -1
- package/dist/seatContract.js +5 -21
- package/dist/subagent/engineCompactWire.d.ts +13 -2
- package/dist/subagent/engineCompactWire.js +48 -37
- package/dist/subagent/engineDelegatedPrompt.d.ts +9 -1
- package/dist/subagent/engineDelegatedPrompt.js +16 -16
- package/dist/subagent/engineRowStopGate.d.ts +2 -2
- package/dist/subagent/engineRowStopGate.js +4 -4
- package/dist/subagent/engineSubagentOutput.d.ts +8 -0
- package/dist/subagent/engineSubagentOutput.js +37 -18
- package/dist/subagent/engineSubagentResume.d.ts +11 -2
- package/dist/subagent/engineSubagentResume.js +28 -14
- package/dist/subagent/engineSubagentSteer.d.ts +10 -1
- package/dist/subagent/engineSubagentSteer.js +16 -11
- package/dist/subagent/engineSubagentTail.d.ts +10 -1
- package/dist/subagent/engineSubagentTail.js +35 -17
- package/dist/subagent/engineTaskHandleWire.d.ts +13 -0
- package/dist/subagent/engineTaskHandleWire.js +41 -20
- package/dist/subagent/subagentOwnerAbsence.js +2 -5
- package/dist/taskRequestWords.d.ts +5 -0
- package/dist/taskRequestWords.js +4 -0
- package/dist/wireErrorTriage.d.ts +1 -0
- package/dist/wireErrorTriage.js +3 -1
- package/dist/wireRefusalCopy.d.ts +10 -0
- package/dist/wireRefusalCopy.js +62 -2
- package/docs/INTEGRATION-CLIENTS.md +867 -22
- package/package.json +1 -1
|
@@ -1,34 +1,38 @@
|
|
|
1
1
|
import { hostLog, hostTimersFor } from '../host.js';
|
|
2
|
-
import {
|
|
3
|
-
import { engineWireTargetFor } from '../engineWireTarget.js';
|
|
2
|
+
import { clientHasVerbs, engineWireFor, INJECTED_WIRE_SETTLE_MS, settleInjected, } from '../engineWireFor.js';
|
|
4
3
|
import { activeEngineRunIdFor, engineSessionParamFor } from '../engineSessionParam.js';
|
|
5
4
|
import { DEFAULT_SESSION_KEY } from '../sessionSlot.js';
|
|
5
|
+
function usableCompactClient(client) {
|
|
6
|
+
return clientHasVerbs(client, [['capabilities'], ['runs', 'compact']]);
|
|
7
|
+
}
|
|
8
|
+
const COMPACT_PROBE_SETTLE_MS = 5000;
|
|
6
9
|
const pendingCompactKeys = new Set();
|
|
7
10
|
export function isEngineCompactPending(sessionKey = DEFAULT_SESSION_KEY) {
|
|
8
11
|
return pendingCompactKeys.has(sessionKey);
|
|
9
12
|
}
|
|
10
13
|
const manualCompactCapableByBase = new Map();
|
|
11
14
|
const capabilitiesProbeByBase = new Map();
|
|
12
|
-
function probeManualCompactCapability(
|
|
13
|
-
const
|
|
14
|
-
if (
|
|
15
|
+
function probeManualCompactCapability(wire) {
|
|
16
|
+
const base = wire.kind === 'injected' ? wire.capsBaseUrl : wire.target.baseUrl;
|
|
17
|
+
if (base === undefined)
|
|
15
18
|
return;
|
|
16
|
-
const base = cfg.baseUrl;
|
|
17
19
|
if (capabilitiesProbeByBase.has(base))
|
|
18
20
|
return;
|
|
19
21
|
capabilitiesProbeByBase.set(base, (async () => {
|
|
20
22
|
try {
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
23
|
+
let caps;
|
|
24
|
+
if (wire.kind === 'injected') {
|
|
25
|
+
const injected = wire.client;
|
|
26
|
+
caps = await settleInjected(COMPACT_PROBE_SETTLE_MS, undefined, (signal) => injected.capabilities({ signal }));
|
|
27
|
+
}
|
|
28
|
+
else {
|
|
29
|
+
const client = wire.make({ timeoutMs: 5000 });
|
|
30
|
+
if (!client) {
|
|
31
|
+
capabilitiesProbeByBase.delete(base);
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
caps = await client.capabilities();
|
|
30
35
|
}
|
|
31
|
-
const caps = await client.capabilities();
|
|
32
36
|
if (typeof caps.manualCompact === 'boolean')
|
|
33
37
|
manualCompactCapableByBase.set(base, caps.manualCompact);
|
|
34
38
|
else
|
|
@@ -55,31 +59,35 @@ function deferFire(ms, fn, why, sessionKey) {
|
|
|
55
59
|
timers.setTimer(ms, fn);
|
|
56
60
|
return true;
|
|
57
61
|
}
|
|
58
|
-
function freezeCompactShot(taskId, sessionKey) {
|
|
59
|
-
|
|
60
|
-
if (!cfg)
|
|
62
|
+
function freezeCompactShot(taskId, sessionKey, wire) {
|
|
63
|
+
if (wire.kind === 'no-target' || wire.kind === 'injected-unusable')
|
|
61
64
|
return null;
|
|
62
65
|
const sid = engineSessionParamFor(sessionKey);
|
|
66
|
+
let client;
|
|
67
|
+
if (wire.kind === 'injected') {
|
|
68
|
+
const injected = wire.client;
|
|
69
|
+
client = () => injected;
|
|
70
|
+
}
|
|
71
|
+
else {
|
|
72
|
+
const make = wire.make;
|
|
73
|
+
client = () => make();
|
|
74
|
+
}
|
|
63
75
|
return {
|
|
64
76
|
taskId,
|
|
65
77
|
sessionKey,
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
...(cfg.principal !== undefined ? { principal: cfg.principal } : {}),
|
|
78
|
+
client,
|
|
79
|
+
injected: wire.kind === 'injected',
|
|
69
80
|
...(sid !== undefined ? { session: sid } : {}),
|
|
70
81
|
};
|
|
71
82
|
}
|
|
72
83
|
async function postCompact(shot, retriesLeft = 1) {
|
|
73
|
-
const client =
|
|
74
|
-
baseUrl: shot.baseUrl,
|
|
75
|
-
...(shot.token ? { token: shot.token } : {}),
|
|
76
|
-
principal: shot.principal,
|
|
77
|
-
});
|
|
84
|
+
const client = shot.client();
|
|
78
85
|
if (!client)
|
|
79
86
|
return;
|
|
80
87
|
const taskId = shot.taskId;
|
|
81
88
|
try {
|
|
82
|
-
const
|
|
89
|
+
const fire = (signal) => client.runs.compact(taskId, { ...(shot.session !== undefined ? { session: shot.session } : {}), ...(signal ? { signal } : {}) });
|
|
90
|
+
const ack = shot.injected ? await settleInjected(INJECTED_WIRE_SETTLE_MS, undefined, fire) : await fire(undefined);
|
|
83
91
|
hostLog('debug', `engineCompactWire: POST compact ${taskId} → ok${ack?.status ? ` (${ack.status})` : ''}`);
|
|
84
92
|
}
|
|
85
93
|
catch (e) {
|
|
@@ -94,18 +102,19 @@ async function postCompact(shot, retriesLeft = 1) {
|
|
|
94
102
|
hostLog('debug', `engineCompactWire: compact fire failed (best-effort): ${String(e)}`);
|
|
95
103
|
}
|
|
96
104
|
}
|
|
97
|
-
export function requestEngineCompact(sessionKey = DEFAULT_SESSION_KEY) {
|
|
98
|
-
const
|
|
99
|
-
if (
|
|
105
|
+
export function requestEngineCompact(sessionKey = DEFAULT_SESSION_KEY, opts) {
|
|
106
|
+
const wire = engineWireFor(opts?.wire, sessionKey, usableCompactClient);
|
|
107
|
+
if (wire.kind === 'no-target' || wire.kind === 'injected-unusable')
|
|
100
108
|
return 'offline';
|
|
101
|
-
probeManualCompactCapability(
|
|
102
|
-
|
|
109
|
+
probeManualCompactCapability(wire);
|
|
110
|
+
const capsKey = wire.kind === 'injected' ? wire.capsBaseUrl : wire.target.baseUrl;
|
|
111
|
+
if (capsKey !== undefined && manualCompactCapableByBase.get(capsKey) === false) {
|
|
103
112
|
hostLog('debug', 'engineCompactWire: engine capabilities report manualCompact:false — verb suppressed (TOC-local runStore-less engine, [488])');
|
|
104
113
|
return 'unsupported';
|
|
105
114
|
}
|
|
106
115
|
const taskId = activeEngineRunIdFor(sessionKey);
|
|
107
116
|
if (taskId) {
|
|
108
|
-
const shot = freezeCompactShot(taskId, sessionKey);
|
|
117
|
+
const shot = freezeCompactShot(taskId, sessionKey, wire);
|
|
109
118
|
if (shot !== null)
|
|
110
119
|
void postCompact(shot);
|
|
111
120
|
return 'fired';
|
|
@@ -114,11 +123,13 @@ export function requestEngineCompact(sessionKey = DEFAULT_SESSION_KEY) {
|
|
|
114
123
|
return 'armed';
|
|
115
124
|
}
|
|
116
125
|
const BOUND_FIRE_DELAY_MS = 1500;
|
|
117
|
-
export function onEngineTaskBound(taskId, sessionKey = DEFAULT_SESSION_KEY) {
|
|
118
|
-
|
|
126
|
+
export function onEngineTaskBound(taskId, sessionKey = DEFAULT_SESSION_KEY, opts) {
|
|
127
|
+
const wire = engineWireFor(opts?.wire, sessionKey, usableCompactClient);
|
|
128
|
+
if (wire.kind !== 'no-target' && wire.kind !== 'injected-unusable')
|
|
129
|
+
probeManualCompactCapability(wire);
|
|
119
130
|
if (!pendingCompactKeys.has(sessionKey))
|
|
120
131
|
return;
|
|
121
|
-
const shot = freezeCompactShot(taskId, sessionKey);
|
|
132
|
+
const shot = freezeCompactShot(taskId, sessionKey, wire);
|
|
122
133
|
if (shot === null)
|
|
123
134
|
return;
|
|
124
135
|
if (deferFire(BOUND_FIRE_DELAY_MS, () => void postCompact(shot), 'bind 后发射', sessionKey)) {
|
|
@@ -1,3 +1,9 @@
|
|
|
1
|
+
import type { AgentClient } from '@sema-agent/sdk';
|
|
2
|
+
import type { EngineWireInjection } from '../engineWireSdk.js';
|
|
3
|
+
export type DelegatedPromptWireClient = {
|
|
4
|
+
sessions: Pick<AgentClient['sessions'], 'get'>;
|
|
5
|
+
};
|
|
6
|
+
export type DelegatedPromptWire = EngineWireInjection<DelegatedPromptWireClient>;
|
|
1
7
|
export declare function recordEngineTranscriptId(taskId: string, transcriptId: string): void;
|
|
2
8
|
export declare function getEngineTranscriptId(taskId: string): string | undefined;
|
|
3
9
|
export declare function __resetEngineDelegatedPromptForTests(): void;
|
|
@@ -9,5 +15,7 @@ export interface DelegatedPromptFetch {
|
|
|
9
15
|
prompt?: string;
|
|
10
16
|
outcome: 'hit' | 'skipped' | 'unreadable' | 'absent';
|
|
11
17
|
}
|
|
12
|
-
export declare function fetchDelegatedPrompt(taskId: string
|
|
18
|
+
export declare function fetchDelegatedPrompt(taskId: string, opts?: {
|
|
19
|
+
wire?: DelegatedPromptWire;
|
|
20
|
+
}): Promise<DelegatedPromptFetch>;
|
|
13
21
|
export {};
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import { stripSystemReminderBlocks } from '../systemReminderTag.js';
|
|
2
2
|
import { hostLog } from '../host.js';
|
|
3
|
-
import {
|
|
4
|
-
import { engineWireTargetFor } from '../engineWireTarget.js';
|
|
3
|
+
import { clientHasVerbs, engineWireFor, INJECTED_WIRE_SETTLE_MS, settleInjected } from '../engineWireFor.js';
|
|
5
4
|
import { resolveOwnerContext } from './engineSubagentResume.js';
|
|
5
|
+
function usableDelegatedPromptClient(client) {
|
|
6
|
+
return clientHasVerbs(client, [['sessions', 'get']]);
|
|
7
|
+
}
|
|
6
8
|
const TAIL_WINDOW = 200;
|
|
7
9
|
const BLOB_CAP = 8 * 1024;
|
|
8
10
|
const transcriptIdByTask = new Map();
|
|
@@ -89,27 +91,25 @@ let transcriptWindowReader = null;
|
|
|
89
91
|
export function __setTranscriptWindowReaderForTests(r) {
|
|
90
92
|
transcriptWindowReader = r;
|
|
91
93
|
}
|
|
92
|
-
async function readTranscriptWindow(transcriptId, sessionKey) {
|
|
94
|
+
async function readTranscriptWindow(transcriptId, sessionKey, injectedWire) {
|
|
93
95
|
if (transcriptWindowReader)
|
|
94
96
|
return transcriptWindowReader(transcriptId);
|
|
95
|
-
const
|
|
96
|
-
if (
|
|
97
|
+
const wire = engineWireFor(injectedWire, sessionKey, usableDelegatedPromptClient);
|
|
98
|
+
if (wire.kind === 'no-target' || wire.kind === 'injected-unusable')
|
|
97
99
|
return null;
|
|
98
|
-
const client =
|
|
99
|
-
baseUrl: cfg.baseUrl,
|
|
100
|
-
...(cfg.token ? { token: cfg.token } : {}),
|
|
101
|
-
principal: cfg.principal,
|
|
102
|
-
});
|
|
100
|
+
const client = wire.kind === 'injected' ? wire.client : wire.make();
|
|
103
101
|
if (!client)
|
|
104
102
|
return null;
|
|
105
|
-
const rec =
|
|
106
|
-
tail: TAIL_WINDOW,
|
|
107
|
-
|
|
108
|
-
|
|
103
|
+
const rec = wire.kind === 'injected'
|
|
104
|
+
? await settleInjected(INJECTED_WIRE_SETTLE_MS, undefined, (signal) => client.sessions.get(transcriptId, { tail: TAIL_WINDOW, blobCap: BLOB_CAP, signal }))
|
|
105
|
+
: await client.sessions.get(transcriptId, {
|
|
106
|
+
tail: TAIL_WINDOW,
|
|
107
|
+
blobCap: BLOB_CAP,
|
|
108
|
+
});
|
|
109
109
|
const msgs = rec.messages;
|
|
110
110
|
return Array.isArray(msgs) ? msgs : null;
|
|
111
111
|
}
|
|
112
|
-
export async function fetchDelegatedPrompt(taskId) {
|
|
112
|
+
export async function fetchDelegatedPrompt(taskId, opts) {
|
|
113
113
|
if (promptHydrateInFlight.has(taskId))
|
|
114
114
|
return { outcome: 'skipped' };
|
|
115
115
|
const transcriptId = transcriptIdByTask.get(taskId);
|
|
@@ -118,7 +118,7 @@ export async function fetchDelegatedPrompt(taskId) {
|
|
|
118
118
|
promptHydrateInFlight.add(taskId);
|
|
119
119
|
let msgs = null;
|
|
120
120
|
try {
|
|
121
|
-
msgs = await readTranscriptWindow(transcriptId, resolveOwnerContext(taskId).key);
|
|
121
|
+
msgs = await readTranscriptWindow(transcriptId, resolveOwnerContext(taskId).key, opts?.wire);
|
|
122
122
|
}
|
|
123
123
|
catch (e) {
|
|
124
124
|
promptHydrateInFlight.delete(taskId);
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export declare const engineOwnedRowIds: Set<string>;
|
|
2
2
|
export declare const fleetViewStubRowIds: Set<string>;
|
|
3
3
|
export declare function isEngineViewRow(taskId: string): boolean;
|
|
4
|
-
export declare function engineTaskHandlesCapable(sessionKey?: string): boolean;
|
|
5
|
-
export declare function engineRowNeedsStopConfirm(taskId: string, sessionKey?: string): boolean;
|
|
4
|
+
export declare function engineTaskHandlesCapable(sessionKey?: string, capsBaseUrl?: string): boolean;
|
|
5
|
+
export declare function engineRowNeedsStopConfirm(taskId: string, sessionKey?: string, capsBaseUrl?: string): boolean;
|
|
@@ -6,9 +6,9 @@ export const fleetViewStubRowIds = new Set();
|
|
|
6
6
|
export function isEngineViewRow(taskId) {
|
|
7
7
|
return engineOwnedRowIds.has(taskId) || fleetViewStubRowIds.has(taskId);
|
|
8
8
|
}
|
|
9
|
-
export function engineTaskHandlesCapable(sessionKey = DEFAULT_SESSION_KEY) {
|
|
10
|
-
return engineCapTrue(engineWireTargetFor(sessionKey)?.baseUrl, 'taskHandles');
|
|
9
|
+
export function engineTaskHandlesCapable(sessionKey = DEFAULT_SESSION_KEY, capsBaseUrl) {
|
|
10
|
+
return engineCapTrue(capsBaseUrl ?? engineWireTargetFor(sessionKey)?.baseUrl, 'taskHandles');
|
|
11
11
|
}
|
|
12
|
-
export function engineRowNeedsStopConfirm(taskId, sessionKey) {
|
|
13
|
-
return isEngineViewRow(taskId) && engineTaskHandlesCapable(sessionKey ?? DEFAULT_SESSION_KEY);
|
|
12
|
+
export function engineRowNeedsStopConfirm(taskId, sessionKey, capsBaseUrl) {
|
|
13
|
+
return isEngineViewRow(taskId) && engineTaskHandlesCapable(sessionKey ?? DEFAULT_SESSION_KEY, capsBaseUrl);
|
|
14
14
|
}
|
|
@@ -1,3 +1,10 @@
|
|
|
1
|
+
import type { AgentClient } from '@sema-agent/sdk';
|
|
2
|
+
import type { EngineWireInjection } from '../engineWireSdk.js';
|
|
3
|
+
export type SubagentReportWireClient = {
|
|
4
|
+
capabilities: AgentClient['capabilities'];
|
|
5
|
+
runs: Pick<AgentClient['runs'], 'subagentOutput'>;
|
|
6
|
+
};
|
|
7
|
+
export type SubagentReportWire = EngineWireInjection<SubagentReportWireClient>;
|
|
1
8
|
export declare function __resetSubagentOutputCapCacheForTests(): void;
|
|
2
9
|
export interface EngineSubagentReport {
|
|
3
10
|
content: string;
|
|
@@ -5,4 +12,5 @@ export interface EngineSubagentReport {
|
|
|
5
12
|
}
|
|
6
13
|
export declare function fetchEngineSubagentReport(taskId: string, opts?: {
|
|
7
14
|
signal?: AbortSignal;
|
|
15
|
+
wire?: SubagentReportWire;
|
|
8
16
|
}): Promise<EngineSubagentReport | null>;
|
|
@@ -1,19 +1,24 @@
|
|
|
1
1
|
import { hostLog } from '../host.js';
|
|
2
|
-
import {
|
|
3
|
-
import { engineWireTargetFor } from '../engineWireTarget.js';
|
|
2
|
+
import { clientHasVerbs, engineWireFor, INJECTED_WIRE_SETTLE_MS, settleInjected } from '../engineWireFor.js';
|
|
4
3
|
import { engineSessionParamFor } from '../engineSessionParam.js';
|
|
5
4
|
import { resolveOwnerContext } from './engineSubagentResume.js';
|
|
6
5
|
import { noteBgOwnerAbsence } from './subagentOwnerAbsence.js';
|
|
6
|
+
function usableReportClient(client) {
|
|
7
|
+
return clientHasVerbs(client, [['capabilities'], ['runs', 'subagentOutput']]);
|
|
8
|
+
}
|
|
7
9
|
const capByBase = new Map();
|
|
8
10
|
const CAP_FALSE_RETRY_TTL_MS = 5 * 60_000;
|
|
9
|
-
async function subagentOutputCapable(client,
|
|
10
|
-
const hit = capByBase.get(
|
|
11
|
+
async function subagentOutputCapable(client, cacheKey, signal, deadlineMs) {
|
|
12
|
+
const hit = cacheKey !== undefined ? capByBase.get(cacheKey) : undefined;
|
|
11
13
|
if (hit && (hit.ok || Date.now() - hit.at < CAP_FALSE_RETRY_TTL_MS))
|
|
12
14
|
return hit.ok;
|
|
13
15
|
try {
|
|
14
|
-
const caps =
|
|
16
|
+
const caps = deadlineMs !== undefined
|
|
17
|
+
? await settleInjected(deadlineMs, signal, (s) => client.capabilities({ signal: s }))
|
|
18
|
+
: await client.capabilities({ ...(signal ? { signal } : {}) });
|
|
15
19
|
const ok = caps.subagentOutput === true;
|
|
16
|
-
|
|
20
|
+
if (cacheKey !== undefined)
|
|
21
|
+
capByBase.set(cacheKey, { ok, at: Date.now() });
|
|
17
22
|
return ok;
|
|
18
23
|
}
|
|
19
24
|
catch {
|
|
@@ -25,28 +30,42 @@ export function __resetSubagentOutputCapCacheForTests() {
|
|
|
25
30
|
}
|
|
26
31
|
export async function fetchEngineSubagentReport(taskId, opts) {
|
|
27
32
|
const { record: owner, runId, key } = resolveOwnerContext(taskId);
|
|
28
|
-
const
|
|
29
|
-
if (
|
|
33
|
+
const wire = engineWireFor(opts?.wire, key, usableReportClient);
|
|
34
|
+
if (wire.kind === 'no-target')
|
|
35
|
+
return null;
|
|
36
|
+
if (wire.kind === 'injected-unusable') {
|
|
37
|
+
hostLog('debug', `[subagent-output] ${taskId}: the engine client supplied by the host is not usable (needs capabilities and runs.subagentOutput) — nothing sent`);
|
|
30
38
|
return null;
|
|
39
|
+
}
|
|
31
40
|
if (!runId) {
|
|
32
41
|
noteBgOwnerAbsence('subagent-output', taskId);
|
|
33
42
|
return null;
|
|
34
43
|
}
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
44
|
+
let client;
|
|
45
|
+
let cacheKey;
|
|
46
|
+
let injected;
|
|
47
|
+
if (wire.kind === 'injected') {
|
|
48
|
+
client = wire.client;
|
|
49
|
+
cacheKey = wire.capsBaseUrl;
|
|
50
|
+
injected = true;
|
|
51
|
+
}
|
|
52
|
+
else {
|
|
53
|
+
const made = wire.make();
|
|
54
|
+
if (!made)
|
|
55
|
+
return null;
|
|
56
|
+
client = made;
|
|
57
|
+
cacheKey = wire.target.baseUrl;
|
|
58
|
+
injected = false;
|
|
59
|
+
}
|
|
60
|
+
if (!(await subagentOutputCapable(client, cacheKey, opts?.signal, injected ? INJECTED_WIRE_SETTLE_MS : undefined)))
|
|
43
61
|
return null;
|
|
44
62
|
try {
|
|
45
63
|
const session = owner?.sessionId ?? engineSessionParamFor(key);
|
|
46
|
-
const
|
|
47
|
-
...(
|
|
64
|
+
const read = (signal) => client.runs.subagentOutput(runId, taskId, {
|
|
65
|
+
...(signal ? { signal } : {}),
|
|
48
66
|
...(session ? { session } : {}),
|
|
49
67
|
});
|
|
68
|
+
const r = injected ? await settleInjected(INJECTED_WIRE_SETTLE_MS, opts?.signal, read) : await read(opts?.signal);
|
|
50
69
|
const content = typeof r?.content === 'string' ? r.content : '';
|
|
51
70
|
if (!content.trim())
|
|
52
71
|
return null;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import type { SubagentSteerReceipt } from '@sema-agent/sdk';
|
|
1
|
+
import type { AgentClient, SubagentSteerReceipt } from '@sema-agent/sdk';
|
|
2
|
+
import type { EngineWireInjection } from '../engineWireSdk.js';
|
|
2
3
|
import { type SubagentOwnerRecord } from '../subagentContentStore.js';
|
|
3
4
|
export declare const SUBAGENT_RESUME_CAP = "subagentResume";
|
|
4
5
|
export declare function subagentResumeAvailable(baseUrl?: string, sessionKey?: string): boolean;
|
|
@@ -21,12 +22,14 @@ export type SubagentResumeOutcome = {
|
|
|
21
22
|
detail: string;
|
|
22
23
|
retryAfterSec?: number;
|
|
23
24
|
code?: string;
|
|
25
|
+
unconfirmed?: true;
|
|
24
26
|
};
|
|
25
27
|
export interface SubagentResumeFailureVerdict {
|
|
26
28
|
reason: SubagentResumeFailureKind;
|
|
27
29
|
detail: string;
|
|
28
30
|
retryAfterSec?: number;
|
|
29
31
|
code?: string;
|
|
32
|
+
unconfirmed?: true;
|
|
30
33
|
}
|
|
31
34
|
export declare function classifySubagentResumeFailure(e: unknown): SubagentResumeFailureVerdict;
|
|
32
35
|
export declare function resolveOwnerRunId(childTaskId?: string, activeRunId?: string | undefined): string | undefined;
|
|
@@ -41,7 +44,13 @@ export interface SubagentResumeContext {
|
|
|
41
44
|
runId: string | undefined;
|
|
42
45
|
session: Record<string, string>;
|
|
43
46
|
}
|
|
44
|
-
export
|
|
47
|
+
export type SubagentResumeWireClient = {
|
|
48
|
+
runs: Pick<AgentClient['runs'], 'resumeSubagent'>;
|
|
49
|
+
};
|
|
50
|
+
export type SubagentResumeWire = EngineWireInjection<SubagentResumeWireClient>;
|
|
51
|
+
export declare function resolveSubagentResumeContext(childTaskId?: string, opts?: {
|
|
52
|
+
wire?: SubagentResumeWire;
|
|
53
|
+
}): SubagentResumeContext;
|
|
45
54
|
export declare function resumeSettledSubagent(facade: SubagentResumeFacade | null, runId: string | undefined, target: string, content: string, opts?: {
|
|
46
55
|
session?: string;
|
|
47
56
|
signal?: AbortSignal;
|
|
@@ -2,7 +2,7 @@ import { hostLog } from '../host.js';
|
|
|
2
2
|
import { resumeRetryLaterFromError } from '../wireErrorTriage.js';
|
|
3
3
|
import { displayUntrusted } from '../displayUntrusted.js';
|
|
4
4
|
import { engineCapTrue } from '../engineCapsCache.js';
|
|
5
|
-
import {
|
|
5
|
+
import { clientHasVerbs, engineWireFor, INJECTED_WIRE_SETTLE_MS, noAnswerDetailFrom, sendOnce, settleInjected } from '../engineWireFor.js';
|
|
6
6
|
import { engineWireTargetFor } from '../engineWireTarget.js';
|
|
7
7
|
import { activeEngineRunId, engineSessionParamFor } from '../engineSessionParam.js';
|
|
8
8
|
import { DEFAULT_SESSION_KEY } from '../sessionSlot.js';
|
|
@@ -33,6 +33,11 @@ function shapeOf(e) {
|
|
|
33
33
|
}
|
|
34
34
|
export function classifySubagentResumeFailure(e) {
|
|
35
35
|
const { status, errorCode, message } = shapeOf(e);
|
|
36
|
+
if (status === undefined && errorCode === undefined) {
|
|
37
|
+
const noAnswer = typeof e === 'object' ? noAnswerDetailFrom(e) : null;
|
|
38
|
+
if (noAnswer !== null)
|
|
39
|
+
return { reason: 'error', detail: noAnswer, unconfirmed: true };
|
|
40
|
+
}
|
|
36
41
|
const retryLater = resumeRetryLaterFromError(e);
|
|
37
42
|
if (retryLater !== null) {
|
|
38
43
|
return {
|
|
@@ -77,18 +82,27 @@ export function resolveOwnerContext(childTaskId) {
|
|
|
77
82
|
const record = getBgParentRunOwner(childTaskId);
|
|
78
83
|
return { record, runId: record?.runId, key: record?.sessionKey ?? DEFAULT_SESSION_KEY };
|
|
79
84
|
}
|
|
80
|
-
|
|
85
|
+
function usableResumeClient(client) {
|
|
86
|
+
return clientHasVerbs(client, [['runs', 'resumeSubagent']]);
|
|
87
|
+
}
|
|
88
|
+
export function resolveSubagentResumeContext(childTaskId, opts) {
|
|
81
89
|
const { record: owner, key } = resolveOwnerContext(childTaskId);
|
|
82
|
-
const
|
|
83
|
-
if (
|
|
84
|
-
return { facade: null, runId: undefined, session: {} };
|
|
85
|
-
const client = makeEngineWireClient({
|
|
86
|
-
baseUrl: cfg.baseUrl,
|
|
87
|
-
...(cfg.token ? { token: cfg.token } : {}),
|
|
88
|
-
...(cfg.principal !== undefined ? { principal: cfg.principal } : {}),
|
|
89
|
-
});
|
|
90
|
-
if (!client)
|
|
90
|
+
const wire = engineWireFor(opts?.wire, key, usableResumeClient);
|
|
91
|
+
if (wire.kind === 'no-target' || wire.kind === 'injected-unusable')
|
|
91
92
|
return { facade: null, runId: undefined, session: {} };
|
|
93
|
+
let facade;
|
|
94
|
+
if (wire.kind === 'injected') {
|
|
95
|
+
const injected = wire.client;
|
|
96
|
+
facade = {
|
|
97
|
+
resumeSubagent: (taskId, target, message, o) => settleInjected(INJECTED_WIRE_SETTLE_MS, o?.signal, (signal) => injected.runs.resumeSubagent(taskId, target, message, { ...(o?.session !== undefined ? { session: o.session } : {}), signal })),
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
else {
|
|
101
|
+
const client = wire.make();
|
|
102
|
+
if (!client)
|
|
103
|
+
return { facade: null, runId: undefined, session: {} };
|
|
104
|
+
facade = client.runs;
|
|
105
|
+
}
|
|
92
106
|
const named = childTaskId !== undefined && childTaskId !== '';
|
|
93
107
|
const sessionFromRow = owner?.sessionId;
|
|
94
108
|
const fallback = named ? undefined : engineSessionParamFor(key);
|
|
@@ -97,7 +111,7 @@ export function resolveSubagentResumeContext(childTaskId) {
|
|
|
97
111
|
: fallback !== undefined
|
|
98
112
|
? { session: fallback }
|
|
99
113
|
: {};
|
|
100
|
-
return { facade
|
|
114
|
+
return { facade, runId: resolveOwnerRunId(childTaskId), session };
|
|
101
115
|
}
|
|
102
116
|
function clean(v, max = 200) {
|
|
103
117
|
return displayUntrusted(String(v ?? ''), { credentialUrls: false, credentialWords: false, foldLines: false, mark: 'dot', max });
|
|
@@ -111,7 +125,7 @@ export async function resumeSettledSubagent(facade, runId, target, content, opts
|
|
|
111
125
|
if (text === '')
|
|
112
126
|
return { ok: false, reason: 'bad-request', detail: 'empty message' };
|
|
113
127
|
try {
|
|
114
|
-
const receipt = await facade.resumeSubagent(runId, target, { content: text }, opts);
|
|
128
|
+
const receipt = await sendOnce(opts?.signal, () => facade.resumeSubagent(runId, target, { content: text }, opts));
|
|
115
129
|
const note = receipt?.note;
|
|
116
130
|
return {
|
|
117
131
|
ok: true,
|
|
@@ -122,7 +136,7 @@ export async function resumeSettledSubagent(facade, runId, target, content, opts
|
|
|
122
136
|
}
|
|
123
137
|
catch (e) {
|
|
124
138
|
const f = classifySubagentResumeFailure(e);
|
|
125
|
-
hostLog('debug', `engineSubagentResume: resume(${clean(target, 64)}) → ${f.reason} (${clean(f.detail, 160)})`);
|
|
139
|
+
hostLog('debug', `engineSubagentResume: resume(${clean(target, 64)}) → ${f.reason}${f.unconfirmed === true ? ' (may have been received, no answer)' : ''} (${clean(f.detail, 160)})`);
|
|
126
140
|
return { ok: false, ...f };
|
|
127
141
|
}
|
|
128
142
|
}
|
|
@@ -1,3 +1,9 @@
|
|
|
1
|
+
import type { AgentClient } from '@sema-agent/sdk';
|
|
2
|
+
import type { EngineWireInjection } from '../engineWireSdk.js';
|
|
3
|
+
export type SubagentSteerWireClient = {
|
|
4
|
+
runs: Pick<AgentClient['runs'], 'steerSubagent'>;
|
|
5
|
+
};
|
|
6
|
+
export type SubagentSteerWire = EngineWireInjection<SubagentSteerWireClient>;
|
|
1
7
|
export type SubagentSteerOutcome = {
|
|
2
8
|
ok: true;
|
|
3
9
|
receipt: string;
|
|
@@ -5,5 +11,8 @@ export type SubagentSteerOutcome = {
|
|
|
5
11
|
ok: false;
|
|
6
12
|
reason: 'no-wire' | 'no-run' | 'not-running' | 'error';
|
|
7
13
|
detail?: string;
|
|
14
|
+
unconfirmed?: true;
|
|
8
15
|
};
|
|
9
|
-
export declare function steerEngineSubagent(target: string, text: string, childTaskId?: string
|
|
16
|
+
export declare function steerEngineSubagent(target: string, text: string, childTaskId?: string, opts?: {
|
|
17
|
+
wire?: SubagentSteerWire;
|
|
18
|
+
}): Promise<SubagentSteerOutcome>;
|
|
@@ -1,14 +1,16 @@
|
|
|
1
1
|
import { hostLog } from '../host.js';
|
|
2
|
-
import {
|
|
3
|
-
import { engineWireTargetFor } from '../engineWireTarget.js';
|
|
2
|
+
import { clientHasVerbs, engineWireFor, INJECTED_WIRE_SETTLE_MS, noAnswerDetailFrom, settleInjected } from '../engineWireFor.js';
|
|
4
3
|
import { engineSessionParamFor } from '../engineSessionParam.js';
|
|
5
4
|
import { resolveOwnerContext, resolveOwnerRunId } from './engineSubagentResume.js';
|
|
6
5
|
import { noteBgOwnerAbsence } from './subagentOwnerAbsence.js';
|
|
7
|
-
|
|
6
|
+
function usableSteerClient(client) {
|
|
7
|
+
return clientHasVerbs(client, [['runs', 'steerSubagent']]);
|
|
8
|
+
}
|
|
9
|
+
export async function steerEngineSubagent(target, text, childTaskId, opts) {
|
|
8
10
|
const named = typeof childTaskId === 'string' && childTaskId !== '' ? childTaskId : undefined;
|
|
9
11
|
const { record: owner, key } = resolveOwnerContext(named);
|
|
10
|
-
const
|
|
11
|
-
if (
|
|
12
|
+
const wire = engineWireFor(opts?.wire, key, usableSteerClient);
|
|
13
|
+
if (wire.kind === 'no-target' || wire.kind === 'injected-unusable')
|
|
12
14
|
return { ok: false, reason: 'no-wire' };
|
|
13
15
|
const runId = resolveOwnerRunId(named);
|
|
14
16
|
if (!runId) {
|
|
@@ -16,11 +18,7 @@ export async function steerEngineSubagent(target, text, childTaskId) {
|
|
|
16
18
|
noteBgOwnerAbsence('subagent-steer', named);
|
|
17
19
|
return { ok: false, reason: 'no-run' };
|
|
18
20
|
}
|
|
19
|
-
const client =
|
|
20
|
-
baseUrl: cfg.baseUrl,
|
|
21
|
-
...(cfg.token ? { token: cfg.token } : {}),
|
|
22
|
-
principal: cfg.principal,
|
|
23
|
-
});
|
|
21
|
+
const client = wire.kind === 'injected' ? wire.client : wire.make();
|
|
24
22
|
if (!client)
|
|
25
23
|
return { ok: false, reason: 'no-wire' };
|
|
26
24
|
const rowSession = owner?.sessionId;
|
|
@@ -31,7 +29,9 @@ export async function steerEngineSubagent(target, text, childTaskId) {
|
|
|
31
29
|
? { session: fallbackSession }
|
|
32
30
|
: {};
|
|
33
31
|
try {
|
|
34
|
-
const body =
|
|
32
|
+
const body = wire.kind === 'injected'
|
|
33
|
+
? await settleInjected(INJECTED_WIRE_SETTLE_MS, undefined, (signal) => client.runs.steerSubagent(runId, target, { content: text }, { ...session, signal }))
|
|
34
|
+
: await client.runs.steerSubagent(runId, target, { content: text }, session);
|
|
35
35
|
let receipt = 'Message queued for delivery at the next tool round.';
|
|
36
36
|
const copy = body?.note ?? body?.message;
|
|
37
37
|
if (typeof copy === 'string' && copy.length > 0)
|
|
@@ -48,6 +48,11 @@ export async function steerEngineSubagent(target, text, childTaskId) {
|
|
|
48
48
|
}
|
|
49
49
|
return { ok: false, reason: 'error', detail: `${status}` };
|
|
50
50
|
}
|
|
51
|
+
const noAnswer = typeof e === 'object' ? noAnswerDetailFrom(e) : null;
|
|
52
|
+
if (noAnswer !== null) {
|
|
53
|
+
hostLog('debug', `engineSubagentSteer: may have been received, no answer — ${noAnswer}`);
|
|
54
|
+
return { ok: false, reason: 'error', detail: noAnswer, unconfirmed: true };
|
|
55
|
+
}
|
|
51
56
|
hostLog('debug', `engineSubagentSteer: fetch failed ${String(e)}`);
|
|
52
57
|
return { ok: false, reason: 'error', detail: String(e) };
|
|
53
58
|
}
|
|
@@ -1,3 +1,10 @@
|
|
|
1
|
+
import type { AgentClient } from '@sema-agent/sdk';
|
|
2
|
+
import type { EngineWireInjection } from '../engineWireSdk.js';
|
|
3
|
+
export type SubagentTailWireClient = {
|
|
4
|
+
capabilities: AgentClient['capabilities'];
|
|
5
|
+
runs: Pick<AgentClient['runs'], 'subagentStream'>;
|
|
6
|
+
};
|
|
7
|
+
export type SubagentTailWire = EngineWireInjection<SubagentTailWireClient>;
|
|
1
8
|
export declare function __resetSubagentStreamCapCacheForTests(): void;
|
|
2
9
|
export interface SubagentActivity {
|
|
3
10
|
taskId: string;
|
|
@@ -14,7 +21,9 @@ export interface SubagentTailMeta {
|
|
|
14
21
|
export type SubagentTailMetaSink = (m: SubagentTailMeta) => void;
|
|
15
22
|
export declare function installSubagentTailMetaSink(sink: SubagentTailMetaSink | null): () => void;
|
|
16
23
|
export declare function isEngineSubagentTailActive(taskId: string): boolean;
|
|
17
|
-
export declare function tailEngineSubagent(taskId: string
|
|
24
|
+
export declare function tailEngineSubagent(taskId: string, opts?: {
|
|
25
|
+
wire?: SubagentTailWire;
|
|
26
|
+
}): void;
|
|
18
27
|
export declare function stopEngineSubagentTail(taskId: string): void;
|
|
19
28
|
export declare function hasActiveSubagentTail(taskId: string): boolean;
|
|
20
29
|
export declare function __activeTailCountForTests(): number;
|