@sema-agent/client-core 0.84.1 → 0.85.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (84) hide show
  1. package/CHANGELOG.md +149 -0
  2. package/README.md +18 -11
  3. package/dist/adapt/arms.js +1 -1
  4. package/dist/adapt.d.ts +1 -1
  5. package/dist/adapt.js +1 -1
  6. package/dist/adapter/downstream/terminalToSdkResult.js +9 -5
  7. package/dist/adapter/runStream.js +6 -2
  8. package/dist/agentSession/engineSessionBackgroundStop.d.ts +50 -0
  9. package/dist/agentSession/engineSessionBackgroundStop.js +291 -0
  10. package/dist/agentsWireCaps.d.ts +2 -1
  11. package/dist/cloudConfigWireCaps.js +2 -1
  12. package/dist/corruptSession.d.ts +3 -0
  13. package/dist/corruptSession.js +29 -0
  14. package/dist/corruptSessionCopy.d.ts +1 -0
  15. package/dist/corruptSessionCopy.js +4 -0
  16. package/dist/decideFailureNote.js +4 -1
  17. package/dist/decideReceipt.d.ts +6 -0
  18. package/dist/decideReceipt.js +30 -1
  19. package/dist/effortWire.d.ts +2 -1
  20. package/dist/effortWire.js +2 -4
  21. package/dist/engineErrorCodes.d.ts +9 -0
  22. package/dist/engineErrorCodes.js +29 -0
  23. package/dist/engineNoticeCodes.d.ts +9 -0
  24. package/dist/engineNoticeCodes.js +44 -156
  25. package/dist/engineWireFor.d.ts +43 -0
  26. package/dist/engineWireFor.js +399 -0
  27. package/dist/engineWireSdk.d.ts +4 -0
  28. package/dist/fileHistoryCaptureCapability.d.ts +4 -1
  29. package/dist/fileHistoryCaptureCapability.js +43 -9
  30. package/dist/gateVocabulary.d.ts +1 -2
  31. package/dist/gateVocabulary.js +1 -22
  32. package/dist/generated/engineNoticeTables.d.ts +3 -0
  33. package/dist/generated/engineNoticeTables.js +156 -0
  34. package/dist/generated/toolNameTables.d.ts +2 -0
  35. package/dist/generated/toolNameTables.js +43 -0
  36. package/dist/headlessPermissionModeWire.js +2 -4
  37. package/dist/hitl/frameRouter.js +4 -2
  38. package/dist/hitl/parkResolver.js +3 -1
  39. package/dist/hitl/planReviewWire.d.ts +11 -0
  40. package/dist/hitl/planReviewWire.js +177 -69
  41. package/dist/hitl/rosterMountedNames.d.ts +2 -0
  42. package/dist/hitl/rosterMountedNames.js +38 -0
  43. package/dist/hitl/sessionPolicyDeliverable.d.ts +6 -2
  44. package/dist/hitl/sessionPolicyDeliverable.js +25 -45
  45. package/dist/hitl/sessionPolicyWire.js +1 -38
  46. package/dist/hitl/toolApprovalWire.d.ts +4 -2
  47. package/dist/hitl/toolApprovalWire.js +4 -2
  48. package/dist/index.d.ts +9 -3
  49. package/dist/index.js +6 -2
  50. package/dist/permissionWireCaps.d.ts +2 -1
  51. package/dist/permissionWireCaps.js +2 -7
  52. package/dist/request/taskRequest.d.ts +2 -0
  53. package/dist/request/taskRequest.js +18 -1
  54. package/dist/resumeRefusalCopy.js +4 -2
  55. package/dist/runTerminal.js +4 -0
  56. package/dist/sandboxWire.js +1 -1
  57. package/dist/sealedKeyCapability.d.ts +17 -0
  58. package/dist/sealedKeyCapability.js +42 -0
  59. package/dist/seatContract.d.ts +2 -1
  60. package/dist/seatContract.js +5 -21
  61. package/dist/subagent/engineCompactWire.d.ts +13 -2
  62. package/dist/subagent/engineCompactWire.js +48 -37
  63. package/dist/subagent/engineDelegatedPrompt.d.ts +9 -1
  64. package/dist/subagent/engineDelegatedPrompt.js +16 -16
  65. package/dist/subagent/engineRowStopGate.d.ts +2 -2
  66. package/dist/subagent/engineRowStopGate.js +4 -4
  67. package/dist/subagent/engineSubagentOutput.d.ts +8 -0
  68. package/dist/subagent/engineSubagentOutput.js +37 -18
  69. package/dist/subagent/engineSubagentResume.d.ts +11 -2
  70. package/dist/subagent/engineSubagentResume.js +28 -14
  71. package/dist/subagent/engineSubagentSteer.d.ts +10 -1
  72. package/dist/subagent/engineSubagentSteer.js +16 -11
  73. package/dist/subagent/engineSubagentTail.d.ts +10 -1
  74. package/dist/subagent/engineSubagentTail.js +35 -17
  75. package/dist/subagent/engineTaskHandleWire.d.ts +13 -0
  76. package/dist/subagent/engineTaskHandleWire.js +41 -20
  77. package/dist/taskRequestWords.d.ts +5 -0
  78. package/dist/taskRequestWords.js +4 -0
  79. package/dist/wireErrorTriage.d.ts +1 -0
  80. package/dist/wireErrorTriage.js +3 -1
  81. package/dist/wireRefusalCopy.d.ts +10 -0
  82. package/dist/wireRefusalCopy.js +62 -2
  83. package/docs/INTEGRATION-CLIENTS.md +679 -18
  84. package/package.json +1 -1
@@ -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): Promise<DelegatedPromptFetch>;
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 { makeEngineWireClient } from '../engineWireSdk.js';
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 cfg = engineWireTargetFor(sessionKey);
96
- if (!cfg)
97
+ const wire = engineWireFor(injectedWire, sessionKey, usableDelegatedPromptClient);
98
+ if (wire.kind === 'no-target' || wire.kind === 'injected-unusable')
97
99
  return null;
98
- const client = makeEngineWireClient({
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 = await client.sessions.get(transcriptId, {
106
- tail: TAIL_WINDOW,
107
- blobCap: BLOB_CAP,
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 { makeEngineWireClient } from '../engineWireSdk.js';
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, baseUrl, signal) {
10
- const hit = capByBase.get(baseUrl);
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 = await client.capabilities({ ...(signal ? { signal } : {}) });
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
- capByBase.set(baseUrl, { ok, at: Date.now() });
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 cfg = engineWireTargetFor(key);
29
- if (!cfg)
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
- const client = makeEngineWireClient({
36
- baseUrl: cfg.baseUrl,
37
- ...(cfg.token ? { token: cfg.token } : {}),
38
- principal: cfg.principal,
39
- });
40
- if (!client)
41
- return null;
42
- if (!(await subagentOutputCapable(client, cfg.baseUrl, opts?.signal)))
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 r = await client.runs.subagentOutput(runId, taskId, {
47
- ...(opts?.signal ? { signal: opts.signal } : {}),
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 declare function resolveSubagentResumeContext(childTaskId?: string): SubagentResumeContext;
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 { makeEngineWireClient } from '../engineWireSdk.js';
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
- export function resolveSubagentResumeContext(childTaskId) {
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 cfg = engineWireTargetFor(key);
83
- if (!cfg)
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: client.runs, runId: resolveOwnerRunId(childTaskId), session };
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): Promise<SubagentSteerOutcome>;
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 { makeEngineWireClient } from '../engineWireSdk.js';
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
- export async function steerEngineSubagent(target, text, childTaskId) {
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 cfg = engineWireTargetFor(key);
11
- if (!cfg)
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 = makeEngineWireClient({
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 = await client.runs.steerSubagent(runId, target, { content: text }, session);
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): void;
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;
@@ -1,20 +1,25 @@
1
1
  import { hostLog } from '../host.js';
2
- import { makeEngineWireClient } from '../engineWireSdk.js';
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 { coerceOutput, parentToolCallIdOf, publishSubagentContentEvent, settleSubagentContent, } from '../subagentContentStore.js';
7
6
  import { noteBgOwnerAbsence } from './subagentOwnerAbsence.js';
7
+ function usableTailClient(client) {
8
+ return clientHasVerbs(client, [['capabilities'], ['runs', 'subagentStream']]);
9
+ }
8
10
  const capByBase = new Map();
9
11
  const CAP_FALSE_RETRY_TTL_MS = 5 * 60_000;
10
- async function subagentStreamCapable(client, baseUrl, signal) {
11
- const hit = capByBase.get(baseUrl);
12
+ async function subagentStreamCapable(client, cacheKey, signal, deadlineMs) {
13
+ const hit = cacheKey !== undefined ? capByBase.get(cacheKey) : undefined;
12
14
  if (hit && (hit.ok || Date.now() - hit.at < CAP_FALSE_RETRY_TTL_MS))
13
15
  return hit.ok;
14
16
  try {
15
- const caps = await client.capabilities({ ...(signal ? { signal } : {}) });
17
+ const caps = deadlineMs !== undefined
18
+ ? await settleInjected(deadlineMs, signal, (s) => client.capabilities({ signal: s }))
19
+ : await client.capabilities({ ...(signal ? { signal } : {}) });
16
20
  const ok = caps.subagentStream === true;
17
- capByBase.set(baseUrl, { ok, at: Date.now() });
21
+ if (cacheKey !== undefined)
22
+ capByBase.set(cacheKey, { ok, at: Date.now() });
18
23
  return ok;
19
24
  }
20
25
  catch {
@@ -44,24 +49,37 @@ export function installSubagentTailMetaSink(sink) {
44
49
  export function isEngineSubagentTailActive(taskId) {
45
50
  return activeTails.has(taskId);
46
51
  }
47
- export function tailEngineSubagent(taskId) {
52
+ export function tailEngineSubagent(taskId, opts) {
48
53
  if (activeTails.has(taskId))
49
54
  return;
50
55
  const { record: owner, runId, key } = resolveOwnerContext(taskId);
51
- const cfg = engineWireTargetFor(key);
52
- if (!cfg)
56
+ const wire = engineWireFor(opts?.wire, key, usableTailClient);
57
+ if (wire.kind === 'no-target')
58
+ return;
59
+ if (wire.kind === 'injected-unusable') {
60
+ hostLog('debug', `[subagent-tail] ${taskId}: the engine client supplied by the host is not usable (needs capabilities and runs.subagentStream) — tail not opened`);
53
61
  return;
62
+ }
54
63
  if (!runId) {
55
64
  noteBgOwnerAbsence('subagent-tail', taskId);
56
65
  return;
57
66
  }
58
- const client = makeEngineWireClient({
59
- baseUrl: cfg.baseUrl,
60
- ...(cfg.token ? { token: cfg.token } : {}),
61
- principal: cfg.principal,
62
- });
63
- if (!client)
64
- return;
67
+ let client;
68
+ let cacheKey;
69
+ let injected;
70
+ if (wire.kind === 'injected') {
71
+ client = wire.client;
72
+ cacheKey = wire.capsBaseUrl;
73
+ injected = true;
74
+ }
75
+ else {
76
+ const made = wire.make();
77
+ if (!made)
78
+ return;
79
+ client = made;
80
+ cacheKey = wire.target.baseUrl;
81
+ injected = false;
82
+ }
65
83
  const ac = new AbortController();
66
84
  activeTails.set(taskId, ac);
67
85
  const openedAt = Date.now();
@@ -70,7 +88,7 @@ export function tailEngineSubagent(taskId) {
70
88
  let firstContentLogged = false;
71
89
  void (async () => {
72
90
  try {
73
- if (!(await subagentStreamCapable(client, cfg.baseUrl, ac.signal)))
91
+ if (!(await subagentStreamCapable(client, cacheKey, ac.signal, injected ? INJECTED_WIRE_SETTLE_MS : undefined)))
74
92
  return;
75
93
  const session = owner?.sessionId ?? engineSessionParamFor(key);
76
94
  const stream = client.runs.subagentStream(runId, taskId, {
@@ -1,3 +1,13 @@
1
+ import { type AgentClient } from '@sema-agent/sdk';
2
+ import type { EngineWireInjection } from '../engineWireSdk.js';
3
+ export type EngineTaskOutputWireClient = {
4
+ runs: Pick<AgentClient['runs'], 'taskOutput'>;
5
+ };
6
+ export type EngineTaskOutputWire = EngineWireInjection<EngineTaskOutputWireClient>;
7
+ export type EngineTaskStopWireClient = {
8
+ runs: Pick<AgentClient['runs'], 'taskStop'>;
9
+ };
10
+ export type EngineTaskStopWire = EngineWireInjection<EngineTaskStopWireClient>;
1
11
  export interface EngineTaskOutputResult {
2
12
  content: string;
3
13
  status?: string;
@@ -7,6 +17,7 @@ export interface EngineTaskOutputResult {
7
17
  export declare function __resetTaskHandleWireForTests(): void;
8
18
  export declare function fetchEngineTaskOutput(handle: string, opts?: {
9
19
  signal?: AbortSignal;
20
+ wire?: EngineTaskOutputWire;
10
21
  }): Promise<EngineTaskOutputResult | null>;
11
22
  export interface TaskOutputMergeHint {
12
23
  readonly type?: string;
@@ -41,8 +52,10 @@ export type EngineTaskStopOutcome = {
41
52
  ok: false;
42
53
  reason: 'unavailable' | 'error';
43
54
  detail?: string;
55
+ unconfirmed?: true;
44
56
  };
45
57
  export declare function classifyTaskStopConflict(e: unknown): EngineTaskStopOutcome;
46
58
  export declare function stopEngineTask(handle: string, opts?: {
47
59
  signal?: AbortSignal;
60
+ wire?: EngineTaskStopWire;
48
61
  }): Promise<EngineTaskStopOutcome>;