@sema-agent/client-core 0.93.0 → 0.94.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 (156) hide show
  1. package/CHANGELOG.md +261 -0
  2. package/README.md +31 -18
  3. package/dist/adapt/arms.js +8 -5
  4. package/dist/adapter/downstream/terminalToSdkResult.js +2 -3
  5. package/dist/agentSession/engineSessionBackgroundStop.js +2 -1
  6. package/dist/agentsWireCaps.d.ts +15 -0
  7. package/dist/agentsWireCaps.js +111 -1
  8. package/dist/approvalReachWire.d.ts +18 -0
  9. package/dist/approvalReachWire.js +58 -0
  10. package/dist/approvalsStreamLiveCapability.js +16 -11
  11. package/dist/callerCwdCapability.d.ts +16 -0
  12. package/dist/callerCwdCapability.js +47 -0
  13. package/dist/controlRouter.js +3 -10
  14. package/dist/coreValuePorts.d.ts +64 -1
  15. package/dist/coreValuePorts.js +30 -0
  16. package/dist/detachWire.d.ts +1 -4
  17. package/dist/detachWire.js +1 -1
  18. package/dist/deviceExecutorManagementCapability.js +39 -34
  19. package/dist/deviceFlowPolling.d.ts +5 -0
  20. package/dist/deviceFlowPolling.js +28 -0
  21. package/dist/displayUntrusted.js +652 -21
  22. package/dist/engineErrorCodes.d.ts +5 -1
  23. package/dist/engineErrorCodes.js +9 -12
  24. package/dist/engineNoticeCodes.d.ts +22 -2
  25. package/dist/engineNoticeCodes.js +73 -6
  26. package/dist/engineRefusedToolName.d.ts +1 -0
  27. package/dist/engineRefusedToolName.js +8 -0
  28. package/dist/executionLaneCapability.js +14 -9
  29. package/dist/fileHistoryCaptureCapability.js +14 -9
  30. package/dist/fileHistoryLegNotice.d.ts +11 -0
  31. package/dist/fileHistoryLegNotice.js +50 -13
  32. package/dist/fleet/fleetLedger.js +5 -0
  33. package/dist/gateOutcome.d.ts +2 -1
  34. package/dist/gateOutcome.js +3 -0
  35. package/dist/gateVocabulary.js +1 -1
  36. package/dist/generated/engineAskFaceTables.d.ts +3 -0
  37. package/dist/generated/engineAskFaceTables.js +12 -0
  38. package/dist/generated/engineFactTables.d.ts +4 -2
  39. package/dist/generated/engineFactTables.js +13 -0
  40. package/dist/generated/engineNoticeTables.d.ts +5 -0
  41. package/dist/generated/engineNoticeTables.js +7 -0
  42. package/dist/generated/sdkFactTables.d.ts +3 -0
  43. package/dist/generated/sdkFactTables.js +17 -0
  44. package/dist/generated/settingsSchemaTables.d.ts +3 -0
  45. package/dist/generated/settingsSchemaTables.js +3 -0
  46. package/dist/headlessPermissionModeWire.d.ts +2 -2
  47. package/dist/headlessPermissionModeWire.js +11 -21
  48. package/dist/hitl/approvalHolds.d.ts +36 -0
  49. package/dist/hitl/approvalHolds.js +89 -0
  50. package/dist/hitl/approvalOutcomeNote.js +2 -1
  51. package/dist/hitl/approvalResolution.d.ts +3 -2
  52. package/dist/hitl/approvalResolution.js +21 -0
  53. package/dist/hitl/approvalsFeed.js +5 -0
  54. package/dist/hitl/askGateWire.js +3 -0
  55. package/dist/hitl/automaticAnswerVerdict.d.ts +8 -2
  56. package/dist/hitl/automaticAnswerVerdict.js +69 -21
  57. package/dist/hitl/decisionRefusal.d.ts +19 -0
  58. package/dist/hitl/decisionRefusal.js +63 -0
  59. package/dist/hitl/frameRouter.d.ts +10 -0
  60. package/dist/hitl/frameRouter.js +59 -3
  61. package/dist/hitl/gateLedger.d.ts +3 -0
  62. package/dist/hitl/gateLedger.js +17 -0
  63. package/dist/hitl/hitlBridge.d.ts +2 -2
  64. package/dist/hitl/hitlBridge.js +8 -10
  65. package/dist/hitl/hitlHostSurface.d.ts +6 -0
  66. package/dist/hitl/hitlHostSurface.js +25 -4
  67. package/dist/hitl/localAllowRule.js +2 -1
  68. package/dist/hitl/noAllowCause.d.ts +9 -0
  69. package/dist/hitl/noAllowCause.js +26 -0
  70. package/dist/hitl/parkResolver.js +6 -3
  71. package/dist/hitl/parkRowBirthWait.js +2 -1
  72. package/dist/hitl/persistedRulesWire.d.ts +6 -1
  73. package/dist/hitl/persistedRulesWire.js +12 -4
  74. package/dist/hitl/planReviewWire.d.ts +0 -1
  75. package/dist/hitl/planReviewWire.js +30 -9
  76. package/dist/hitl/sessionPolicyDeliverable.d.ts +1 -1
  77. package/dist/hitl/sessionPolicyDeliverable.js +3 -8
  78. package/dist/hitl/sessionPolicyWire.js +1 -1
  79. package/dist/hitl/toolApprovalWire.d.ts +37 -2
  80. package/dist/hitl/toolApprovalWire.js +392 -13
  81. package/dist/humanTurnRead.d.ts +15 -1
  82. package/dist/humanTurnRead.js +28 -2
  83. package/dist/index.d.ts +18 -6
  84. package/dist/index.js +14 -5
  85. package/dist/limitsWire.d.ts +0 -1
  86. package/dist/limitsWire.js +2 -10
  87. package/dist/liveModelCatalog.d.ts +14 -0
  88. package/dist/liveModelCatalog.js +61 -0
  89. package/dist/mcpProbeCapability.js +14 -9
  90. package/dist/mcpReconnect.js +13 -8
  91. package/dist/memoryComplianceCapability.js +15 -10
  92. package/dist/memoryOriginCapability.js +15 -10
  93. package/dist/modelIdentityResolvability.d.ts +4 -2
  94. package/dist/modelIdentityResolvability.js +31 -4
  95. package/dist/outputStyleSeatCapability.d.ts +16 -0
  96. package/dist/outputStyleSeatCapability.js +47 -0
  97. package/dist/ownKey.d.ts +1 -0
  98. package/dist/ownKey.js +10 -0
  99. package/dist/peerLaneCapability.js +16 -11
  100. package/dist/permissionRuleIssue.js +1 -1
  101. package/dist/permissionRulesWriteCapability.js +16 -11
  102. package/dist/permissionsAllowGrantCapability.d.ts +2 -0
  103. package/dist/permissionsAllowGrantCapability.js +38 -16
  104. package/dist/permissionsDeclaration.d.ts +10 -1
  105. package/dist/permissionsDeclaration.js +97 -15
  106. package/dist/protocolSegment.d.ts +1 -0
  107. package/dist/protocolSegment.js +13 -1
  108. package/dist/request/taskRequest.d.ts +11 -3
  109. package/dist/request/taskRequest.js +13 -1
  110. package/dist/resultFrameKeys.d.ts +4 -0
  111. package/dist/resultFrameKeys.js +13 -0
  112. package/dist/resumeRefusalCopy.js +6 -16
  113. package/dist/retryStatus.js +6 -4
  114. package/dist/rewindArchiveCapability.js +13 -8
  115. package/dist/{hitl/rosterMountedNames.d.ts → rosterMountedNames.d.ts} +1 -1
  116. package/dist/runCancelContext.js +2 -1
  117. package/dist/sealedKeyCapability.js +19 -14
  118. package/dist/seatContract.d.ts +33 -5
  119. package/dist/seatContract.js +143 -2
  120. package/dist/sessionPolicyCapability.js +15 -10
  121. package/dist/sessionReadRedactionCapability.d.ts +16 -0
  122. package/dist/sessionReadRedactionCapability.js +47 -0
  123. package/dist/sessionSyncFileHistoryCapability.js +14 -9
  124. package/dist/skillToolListsCapability.js +14 -9
  125. package/dist/sqlEngineCapability.js +18 -13
  126. package/dist/steerPriorityCapability.js +21 -16
  127. package/dist/subagent/agentStateDetail.d.ts +1 -0
  128. package/dist/subagent/agentStateDetail.js +41 -0
  129. package/dist/subagent/engineCompactWire.d.ts +15 -0
  130. package/dist/subagent/engineCompactWire.js +75 -13
  131. package/dist/supportFloor.js +1 -1
  132. package/dist/taskRequestWords.d.ts +2 -4
  133. package/dist/taskRequestWords.js +3 -2
  134. package/dist/types/engineState.d.ts +1 -1
  135. package/dist/userAttachmentEnvelope.d.ts +22 -1
  136. package/dist/userAttachmentEnvelope.js +90 -2
  137. package/dist/userInvokedSkillsCapability.js +14 -9
  138. package/dist/webSearchBackendCapability.js +21 -16
  139. package/dist/wireErrorTriage.d.ts +11 -0
  140. package/dist/wireErrorTriage.js +35 -0
  141. package/dist/wireFailureShape.d.ts +6 -0
  142. package/dist/wireFailureShape.js +30 -3
  143. package/dist/wireRefusalCopy.d.ts +6 -1
  144. package/dist/wireRefusalCopy.js +22 -8
  145. package/dist/wiringInstructionsAttribution.d.ts +10 -0
  146. package/dist/wiringInstructionsAttribution.js +67 -0
  147. package/dist/workflowClient.js +22 -1
  148. package/dist/workflowsJournalCapability.js +14 -9
  149. package/dist/writeProtectionCapability.js +28 -23
  150. package/docs/INTEGRATION-CLIENTS.md +817 -45
  151. package/package.json +5 -5
  152. package/dist/askSurvivesStandingAnswer.d.ts +0 -13
  153. package/dist/askSurvivesStandingAnswer.js +0 -53
  154. package/dist/hitl/askSurvivesPosture.d.ts +0 -20
  155. package/dist/hitl/askSurvivesPosture.js +0 -48
  156. /package/dist/{hitl/rosterMountedNames.js → rosterMountedNames.js} +0 -0
@@ -1,16 +1,21 @@
1
1
  import { engineWireTarget } from './engineWireTarget.js';
2
2
  import { createEngineCapReader } from './engineCapReader.js';
3
3
  export function projectSkillToolListsCapability(caps) {
4
- if (caps === null || typeof caps !== 'object' || Array.isArray(caps))
5
- return undefined;
6
- if (!Object.hasOwn(caps, 'skillToolLists'))
7
- return { kind: 'not_reported' };
8
- const v = caps.skillToolLists;
9
- if (v === undefined)
10
- return { kind: 'not_reported' };
11
- if (typeof v !== 'boolean')
4
+ try {
5
+ if (caps === null || typeof caps !== 'object' || Array.isArray(caps))
6
+ return undefined;
7
+ if (!Object.hasOwn(caps, 'skillToolLists'))
8
+ return { kind: 'not_reported' };
9
+ const v = caps.skillToolLists;
10
+ if (v === undefined)
11
+ return { kind: 'not_reported' };
12
+ if (typeof v !== 'boolean')
13
+ return undefined;
14
+ return v ? { kind: 'present' } : { kind: 'absent' };
15
+ }
16
+ catch {
12
17
  return undefined;
13
- return v ? { kind: 'present' } : { kind: 'absent' };
18
+ }
14
19
  }
15
20
  const reader = createEngineCapReader({
16
21
  name: 'skillToolLists',
@@ -2,21 +2,26 @@ import { capForDisplay } from './fleetTaskDesc.js';
2
2
  import { engineWireTarget } from './engineWireTarget.js';
3
3
  import { capsGenerationOf, createEngineCapReader } from './engineCapReader.js';
4
4
  export function projectSqlEngineCapability(caps) {
5
- if (caps === null || typeof caps !== 'object')
6
- return undefined;
7
- if (caps === null || typeof caps !== 'object' || Array.isArray(caps))
5
+ try {
6
+ if (caps === null || typeof caps !== 'object')
7
+ return undefined;
8
+ if (caps === null || typeof caps !== 'object' || Array.isArray(caps))
9
+ return undefined;
10
+ const gen = capsGenerationOf(caps);
11
+ if (gen.generation === 'new') {
12
+ const store = gen.store;
13
+ if (!Object.hasOwn(store, 'sql'))
14
+ return { kind: 'none' };
15
+ const nested = store.sql;
16
+ if (nested === undefined)
17
+ return { kind: 'none' };
18
+ return projectSqlEngineView(nested);
19
+ }
20
+ return { kind: 'not_reported' };
21
+ }
22
+ catch {
8
23
  return undefined;
9
- const gen = capsGenerationOf(caps);
10
- if (gen.generation === 'new') {
11
- const store = gen.store;
12
- if (!Object.hasOwn(store, 'sql'))
13
- return { kind: 'none' };
14
- const nested = store.sql;
15
- if (nested === undefined)
16
- return { kind: 'none' };
17
- return projectSqlEngineView(nested);
18
24
  }
19
- return { kind: 'not_reported' };
20
25
  }
21
26
  function projectSqlEngineView(sql) {
22
27
  if (sql === null || typeof sql !== 'object')
@@ -2,23 +2,28 @@ import { capForDisplay } from './fleetTaskDesc.js';
2
2
  import { engineWireTarget } from './engineWireTarget.js';
3
3
  import { createEngineCapReader } from './engineCapReader.js';
4
4
  export function projectSteerPriorityCapability(caps) {
5
- if (caps === null || typeof caps !== 'object' || Array.isArray(caps))
6
- return undefined;
7
- if (!Object.hasOwn(caps, 'fleet'))
8
- return { kind: 'not_reported' };
9
- const fleet = caps.fleet;
10
- if (fleet === undefined || fleet === false)
11
- return { kind: 'not_reported' };
12
- if (fleet === null || typeof fleet !== 'object' || Array.isArray(fleet))
13
- return undefined;
14
- if (!Object.hasOwn(fleet, 'steerPriority'))
15
- return { kind: 'not_reported' };
16
- const v = fleet.steerPriority;
17
- if (v === undefined)
18
- return { kind: 'not_reported' };
19
- if (typeof v !== 'string' || v.length === 0)
5
+ try {
6
+ if (caps === null || typeof caps !== 'object' || Array.isArray(caps))
7
+ return undefined;
8
+ if (!Object.hasOwn(caps, 'fleet'))
9
+ return { kind: 'not_reported' };
10
+ const fleet = caps.fleet;
11
+ if (fleet === undefined || fleet === false)
12
+ return { kind: 'not_reported' };
13
+ if (fleet === null || typeof fleet !== 'object' || Array.isArray(fleet))
14
+ return undefined;
15
+ if (!Object.hasOwn(fleet, 'steerPriority'))
16
+ return { kind: 'not_reported' };
17
+ const v = fleet.steerPriority;
18
+ if (v === undefined)
19
+ return { kind: 'not_reported' };
20
+ if (typeof v !== 'string' || v.length === 0)
21
+ return undefined;
22
+ return { kind: 'present', word: v };
23
+ }
24
+ catch {
20
25
  return undefined;
21
- return { kind: 'present', word: v };
26
+ }
22
27
  }
23
28
  const reader = createEngineCapReader({
24
29
  name: 'fleet.steerPriority',
@@ -0,0 +1 @@
1
+ export declare function subagentStateDetail(carrier: unknown): string | undefined;
@@ -0,0 +1,41 @@
1
+ import { displayUntrusted } from '../displayUntrusted.js';
2
+ function own(v, key) {
3
+ if (typeof v !== 'object' || v === null || Array.isArray(v))
4
+ return undefined;
5
+ try {
6
+ const d = Object.getOwnPropertyDescriptor(v, key);
7
+ return d !== undefined && 'value' in d ? d.value : undefined;
8
+ }
9
+ catch {
10
+ return undefined;
11
+ }
12
+ }
13
+ const finite = (v) => (typeof v === 'number' && Number.isFinite(v) ? v : undefined);
14
+ const word = (v) => (typeof v === 'string' && v.trim() !== '' ? displayUntrusted(v, { max: 64 }) : undefined);
15
+ function span(ms) {
16
+ const s = Math.floor(ms / 1000);
17
+ if (s < 60)
18
+ return `${String(s)} s`;
19
+ const m = Math.floor(s / 60);
20
+ if (m < 60)
21
+ return `${String(m)} min`;
22
+ return `${String(Math.floor(m / 60))} h`;
23
+ }
24
+ export function subagentStateDetail(carrier) {
25
+ const st = own(carrier, 'agentState');
26
+ const status = word(own(st, 'status'));
27
+ if (status === undefined)
28
+ return undefined;
29
+ const readAt = finite(own(st, 'readAt'));
30
+ const last = finite(own(st, 'lastActivityAt'));
31
+ const activity = readAt !== undefined && last !== undefined && last <= readAt ? `last activity ${span(readAt - last)} ago` : 'last activity unknown';
32
+ const approval = own(st, 'approval');
33
+ const approvalState = word(own(approval, 'state'));
34
+ let approvalPart = '';
35
+ if (approvalState !== undefined) {
36
+ const deadline = finite(own(approval, 'deadline'));
37
+ const when = deadline === undefined || readAt === undefined ? '' : deadline > readAt ? ` (deadline in ${span(deadline - readAt)})` : ' (deadline passed)';
38
+ approvalPart = ` · approval ${approvalState}${when}`;
39
+ }
40
+ return `Subagent state: ${status} · ${activity}${approvalPart}`;
41
+ }
@@ -5,11 +5,26 @@ export type EngineCompactWireClient = {
5
5
  runs: Pick<AgentClient['runs'], 'compact'>;
6
6
  };
7
7
  export type EngineCompactWire = EngineWireInjection<EngineCompactWireClient>;
8
+ export type EngineCompactOutcome = {
9
+ readonly kind: 'accepted';
10
+ } | {
11
+ readonly kind: 'refused';
12
+ readonly status: number;
13
+ readonly errorCode?: string;
14
+ } | {
15
+ readonly kind: 'failed';
16
+ readonly detail: string;
17
+ readonly status?: number;
18
+ readonly unconfirmed?: true;
19
+ } | {
20
+ readonly kind: 'offline';
21
+ };
8
22
  export declare function isEngineCompactPending(sessionKey?: string): boolean;
9
23
  export declare function resetManualCompactCapability(): void;
10
24
  export declare function __resetEngineCompactArmForTests(): void;
11
25
  export declare function requestEngineCompact(sessionKey?: string, opts?: {
12
26
  wire?: EngineCompactWire;
27
+ onSettled?: (outcome: EngineCompactOutcome) => void;
13
28
  }): 'fired' | 'armed' | 'offline' | 'unsupported';
14
29
  export declare function onEngineTaskBound(taskId: string, sessionKey?: string, opts?: {
15
30
  wire?: EngineCompactWire;
@@ -1,5 +1,8 @@
1
1
  import { hostLog, hostTimersFor } from '../host.js';
2
- import { clientHasVerbs, engineWireFor, INJECTED_WIRE_SETTLE_MS, settleInjected, } from '../engineWireFor.js';
2
+ import { COMPACT_NOT_RUNNING, isUnavailableFaceCode } from '../engineErrorCodes.js';
3
+ import { surfaceCompactNotRunning } from '../hitl/hitlHostSurface.js';
4
+ import { wireFailureShapeOf, wireFailureTextsOf } from '../wireFailureShape.js';
5
+ import { clientHasVerbs, engineWireFor, INJECTED_WIRE_SETTLE_MS, noAnswerDetailFrom, settleInjected, } from '../engineWireFor.js';
3
6
  import { activeEngineRunIdFor, engineSessionParamFor } from '../engineSessionParam.js';
4
7
  import { DEFAULT_SESSION_KEY } from '../sessionSlot.js';
5
8
  function usableCompactClient(client) {
@@ -7,6 +10,7 @@ function usableCompactClient(client) {
7
10
  }
8
11
  const COMPACT_PROBE_SETTLE_MS = 5000;
9
12
  const pendingCompactKeys = new Set();
13
+ const pendingCompactSinks = new Map();
10
14
  export function isEngineCompactPending(sessionKey = DEFAULT_SESSION_KEY) {
11
15
  return pendingCompactKeys.has(sessionKey);
12
16
  }
@@ -49,6 +53,7 @@ export function resetManualCompactCapability() {
49
53
  }
50
54
  export function __resetEngineCompactArmForTests() {
51
55
  pendingCompactKeys.clear();
56
+ pendingCompactSinks.clear();
52
57
  }
53
58
  function deferFire(ms, fn, why, sessionKey) {
54
59
  const timers = hostTimersFor(sessionKey);
@@ -80,26 +85,70 @@ function freezeCompactShot(taskId, sessionKey, wire) {
80
85
  ...(sid !== undefined ? { session: sid } : {}),
81
86
  };
82
87
  }
83
- async function postCompact(shot, retriesLeft = 1) {
88
+ function emitCompactOutcome(sink, outcome) {
89
+ if (sink === undefined)
90
+ return;
91
+ void Promise.resolve().then(() => callSinkIsolated(sink, outcome));
92
+ }
93
+ function callSinkIsolated(sink, outcome) {
94
+ try {
95
+ sink(outcome);
96
+ }
97
+ catch (e) {
98
+ hostLog('debug', `engineCompactWire: onSettled sink threw (ignored): ${wireFailureTextsOf(e).log}`);
99
+ }
100
+ }
101
+ function fanOutCompactSinks(sinks) {
102
+ if (sinks === undefined || sinks.length === 0)
103
+ return undefined;
104
+ return (outcome) => {
105
+ for (const s of sinks)
106
+ callSinkIsolated(s, outcome);
107
+ };
108
+ }
109
+ function compactFailureOutcomeOf(e) {
110
+ const shape = wireFailureShapeOf(e);
111
+ const status = typeof shape.status === 'number' && Number.isFinite(shape.status) ? shape.status : undefined;
112
+ const code = shape.errorCode !== undefined && shape.errorCode !== '' ? shape.errorCode : undefined;
113
+ if (status !== undefined) {
114
+ if ((status >= 400 && status < 500) || isUnavailableFaceCode(code)) {
115
+ return { kind: 'refused', status, ...(code !== undefined ? { errorCode: code } : {}) };
116
+ }
117
+ return { kind: 'failed', status, detail: wireFailureTextsOf(e).reason };
118
+ }
119
+ const noAnswer = typeof e === 'object' && e !== null ? noAnswerDetailFrom(e) : null;
120
+ if (noAnswer !== null)
121
+ return { kind: 'failed', detail: noAnswer, unconfirmed: true };
122
+ return { kind: 'failed', detail: wireFailureTextsOf(e).reason };
123
+ }
124
+ async function postCompact(shot, sink, retriesLeft = 1) {
84
125
  const client = shot.client();
85
- if (!client)
126
+ if (!client) {
127
+ emitCompactOutcome(sink, { kind: 'offline' });
86
128
  return;
129
+ }
87
130
  const taskId = shot.taskId;
88
131
  try {
89
132
  const fire = (signal) => client.runs.compact(taskId, { ...(shot.session !== undefined ? { session: shot.session } : {}), ...(signal ? { signal } : {}) });
90
133
  const ack = shot.injected ? await settleInjected(INJECTED_WIRE_SETTLE_MS, undefined, fire) : await fire(undefined);
91
134
  hostLog('debug', `engineCompactWire: POST compact ${taskId} → ok${ack?.status ? ` (${ack.status})` : ''}`);
135
+ emitCompactOutcome(sink, { kind: 'accepted' });
92
136
  }
93
137
  catch (e) {
94
- const status = e?.status;
95
- if (typeof status === 'number') {
96
- hostLog('debug', `engineCompactWire: POST compact ${taskId} → ${status}`);
97
- if (status === 404 && retriesLeft > 0) {
98
- deferFire(2000, () => void postCompact(shot, retriesLeft - 1), '404 单次重试', shot.sessionKey);
99
- }
100
- return;
138
+ const outcome = compactFailureOutcomeOf(e);
139
+ if (outcome.kind === 'refused' || (outcome.kind === 'failed' && outcome.status !== undefined)) {
140
+ hostLog('debug', `engineCompactWire: POST compact ${taskId} → ${outcome.status}`);
141
+ }
142
+ else {
143
+ hostLog('debug', `engineCompactWire: compact fire failed (best-effort): ${wireFailureTextsOf(e).log}`);
101
144
  }
102
- hostLog('debug', `engineCompactWire: compact fire failed (best-effort): ${String(e)}`);
145
+ if (outcome.kind === 'refused' && outcome.status === 404 && retriesLeft > 0) {
146
+ if (deferFire(2000, () => void postCompact(shot, sink, retriesLeft - 1), '404 单次重试', shot.sessionKey))
147
+ return;
148
+ }
149
+ if (outcome.kind === 'refused' && outcome.errorCode === COMPACT_NOT_RUNNING)
150
+ surfaceCompactNotRunning(shot.sessionKey);
151
+ emitCompactOutcome(sink, outcome);
103
152
  }
104
153
  }
105
154
  export function requestEngineCompact(sessionKey = DEFAULT_SESSION_KEY, opts) {
@@ -113,13 +162,24 @@ export function requestEngineCompact(sessionKey = DEFAULT_SESSION_KEY, opts) {
113
162
  return 'unsupported';
114
163
  }
115
164
  const taskId = activeEngineRunIdFor(sessionKey);
165
+ const rawSink = opts?.onSettled;
166
+ const sink = typeof rawSink === 'function' ? rawSink : undefined;
116
167
  if (taskId) {
117
168
  const shot = freezeCompactShot(taskId, sessionKey, wire);
118
169
  if (shot !== null)
119
- void postCompact(shot);
170
+ void postCompact(shot, sink);
171
+ else
172
+ emitCompactOutcome(sink, { kind: 'offline' });
120
173
  return 'fired';
121
174
  }
122
175
  pendingCompactKeys.add(sessionKey);
176
+ if (sink !== undefined) {
177
+ const sinks = pendingCompactSinks.get(sessionKey);
178
+ if (sinks === undefined)
179
+ pendingCompactSinks.set(sessionKey, [sink]);
180
+ else
181
+ sinks.push(sink);
182
+ }
123
183
  return 'armed';
124
184
  }
125
185
  const BOUND_FIRE_DELAY_MS = 1500;
@@ -132,7 +192,9 @@ export function onEngineTaskBound(taskId, sessionKey = DEFAULT_SESSION_KEY, opts
132
192
  const shot = freezeCompactShot(taskId, sessionKey, wire);
133
193
  if (shot === null)
134
194
  return;
135
- if (deferFire(BOUND_FIRE_DELAY_MS, () => void postCompact(shot), 'bind 后发射', sessionKey)) {
195
+ const sink = fanOutCompactSinks(pendingCompactSinks.get(sessionKey));
196
+ if (deferFire(BOUND_FIRE_DELAY_MS, () => void postCompact(shot, sink), 'bind 后发射', sessionKey)) {
136
197
  pendingCompactKeys.delete(sessionKey);
198
+ pendingCompactSinks.delete(sessionKey);
137
199
  }
138
200
  }
@@ -1,7 +1,7 @@
1
1
  export const SUPPORT_FLOOR = Object.freeze({
2
2
  server: '7.106.0',
3
3
  core: '7.35.2',
4
- sdk: '12.0.1',
4
+ sdk: '18.1.0',
5
5
  });
6
6
  export function supportFloorOf(component) {
7
7
  if (component === 'server' || component === 'core' || component === 'sdk')
@@ -1,9 +1,8 @@
1
1
  import type { TaskRequest } from '@sema-agent/sdk';
2
2
  export type TaskRequestReasoningEffort = NonNullable<TaskRequest['reasoningEffort']>;
3
3
  export declare const REASONING_EFFORT_WORDS: readonly ["off", "minimal", "low", "medium", "high", "xhigh", "max"];
4
- type SdkTaskRequestPermissionMode = NonNullable<TaskRequest['permissionMode']>;
5
- export type TaskRequestPermissionMode = SdkTaskRequestPermissionMode | 'dontAsk';
6
- export declare const PERMISSION_MODE_WORDS: readonly ["default", "plan", "acceptEdits", "bypassPermissions", "auto", "dontAsk"];
4
+ export type TaskRequestPermissionMode = NonNullable<TaskRequest['permissionMode']>;
5
+ export declare const PERMISSION_MODE_WORDS: readonly ["default", "acceptEdits", "plan", "auto", "bypassPermissions", "dontAsk"];
7
6
  export interface RetiredAttachmentsKey {
8
7
  readonly key: string;
9
8
  readonly why: string;
@@ -11,4 +10,3 @@ export interface RetiredAttachmentsKey {
11
10
  readonly remedy: string;
12
11
  }
13
12
  export declare function retiredAttachmentsKeyOf(nameOrToken: string, form?: 'token' | 'key'): RetiredAttachmentsKey | undefined;
14
- export {};
@@ -1,6 +1,7 @@
1
+ import { CORE_PERMISSION_MODES } from './generated/engineFactTables.js';
1
2
  export const REASONING_EFFORT_WORDS = ['off', 'minimal', 'low', 'medium', 'high', 'xhigh', 'max'];
2
- export const PERMISSION_MODE_WORDS = ['default', 'plan', 'acceptEdits', 'bypassPermissions', 'auto', 'dontAsk'];
3
- const _taskRequestWordPins = [true, true, true, true];
3
+ export const PERMISSION_MODE_WORDS = CORE_PERMISSION_MODES;
4
+ const _taskRequestWordPins = [true, true, true, true, true, true];
4
5
  void _taskRequestWordPins;
5
6
  export function retiredAttachmentsKeyOf(nameOrToken, form = 'token') {
6
7
  const lower = nameOrToken.trim().toLowerCase();
@@ -1,5 +1,5 @@
1
1
  import type { EngineHealth } from '@sema-agent/sdk';
2
- export interface HealthResponse extends Pick<EngineHealth, 'status' | 'model' | 'sessionBackend' | 'sessions' | 'instanceId' | 'dataRoot' | 'configHash' | 'pid' | 'port' | 'restartRequired' | 'version'> {
2
+ export interface HealthResponse extends Pick<EngineHealth, 'status' | 'model' | 'sessionBackend' | 'sessions' | 'instanceId' | 'dataRoot' | 'configHash' | 'pid' | 'port' | 'restartRequired' | 'version' | 'restart'> {
3
3
  }
4
4
  export interface EnginePortFile {
5
5
  port: number;
@@ -1,6 +1,13 @@
1
1
  import { type DescriptorView } from './humanTurnRead.js';
2
2
  export type UserAttachmentSource = 'at-mention' | 'compact-restore';
3
- export type UserAttachmentNotIncludedReason = 'too-large' | 'unreadable' | 'read-denied';
3
+ export type UserAttachmentNotIncludedReason = 'too-large' | 'unreadable' | 'read-denied' | 'approval-required' | 'server-not-connected' | 'resource-not-listed' | 'read-failed';
4
+ export type UserAttachmentMcpResourcePart = {
5
+ readonly kind: 'text';
6
+ readonly text: string;
7
+ } | {
8
+ readonly kind: 'binary';
9
+ readonly mimeType: string | null;
10
+ };
4
11
  export interface UserAttachmentNotebookCell {
5
12
  readonly cellType?: string;
6
13
  readonly source?: string;
@@ -54,6 +61,20 @@ export type UserAttachmentDescriptor = {
54
61
  readonly path: string;
55
62
  readonly sizeBytes: number | null;
56
63
  readonly reason: UserAttachmentNotIncludedReason;
64
+ } | {
65
+ readonly kind: 'not-included';
66
+ readonly target: 'mcp-resource';
67
+ readonly server: string;
68
+ readonly uri: string;
69
+ readonly name: string;
70
+ readonly reason: UserAttachmentNotIncludedReason;
71
+ } | {
72
+ readonly kind: 'mcp-resource';
73
+ readonly server: string;
74
+ readonly uri: string;
75
+ readonly name: string;
76
+ readonly parts: readonly UserAttachmentMcpResourcePart[];
77
+ readonly truncated: boolean;
57
78
  };
58
79
  export declare function showAttachmentName(s: string): string;
59
80
  export declare function mintEnvelopeOf(d: DescriptorView, imageDelivery: 'attached' | 'not-attached'): string;
@@ -162,16 +162,93 @@ const NOT_INCLUDED_REASON = Object.freeze({
162
162
  'too-large': "it is larger than the client's size limit for attaching files",
163
163
  unreadable: 'the client could not read it',
164
164
  'read-denied': "the user's permission rules do not allow reading it",
165
+ 'approval-required': "this deployment's rules require approval before it is read, so the client did not attach it without asking",
166
+ 'server-not-connected': 'the MCP server it comes from is not connected',
167
+ 'resource-not-listed': 'the MCP server does not list that resource',
168
+ 'read-failed': 'reading it from the MCP server failed',
165
169
  });
166
170
  function targetWhat(target) {
167
171
  return target === 'directory' ? 'a directory' : target === 'file' ? 'a file' : 'a file or directory';
168
172
  }
173
+ function mcpWhere(server, uri) {
174
+ return `Server: ${showAttachmentName(server)}. URI: ${showAttachmentName(uri)}`;
175
+ }
176
+ function mintMcpResource(d) {
177
+ const l1 = firstLine('at-mention', 'an MCP resource', d.name);
178
+ const texts = d.parts.flatMap((p) => (p.kind === 'text' ? [p.text] : []));
179
+ const binaries = d.parts.flatMap((p) => (p.kind === 'binary' ? [p.mimeType] : []));
180
+ const typed = [...new Set(binaries.filter((t) => t !== null))];
181
+ const binaryNote = binaries.length > 0
182
+ ? `${plural(binaries.length, 'binary part', 'binary parts')}${typed.length > 0 ? ` (${typed.map(showAttachmentName).join(', ')})` : ''} ${binaries.length === 1 ? 'is' : 'are'} not included.`
183
+ : null;
184
+ const truncNote = d.truncated ? 'The client shortened the text; the rest is not included.' : null;
185
+ if (texts.length === 0) {
186
+ return [`${l1}\n${mcpWhere(d.server, d.uri)} (no text parts). The client read it from that server; it has no text to include.`, binaryNote, truncNote].filter((s) => s !== null).join(' ');
187
+ }
188
+ let anyEscaped = false;
189
+ const body = texts
190
+ .join('\n')
191
+ .split('\n')
192
+ .map((line, i) => {
193
+ const shown = showBodyLine(line);
194
+ if (shown.escaped)
195
+ anyEscaped = true;
196
+ return `${i + 1}\t${shown.text}`;
197
+ });
198
+ const head = [
199
+ l1,
200
+ `${mcpWhere(d.server, d.uri)} (${plural(texts.length, 'text part', 'text parts')}). The client read it from that server and copied the text between the two fence lines below. ${dataNote('at-mention', "the resource's contents")}`,
201
+ ];
202
+ if (binaryNote !== null)
203
+ head.push(binaryNote);
204
+ if (truncNote !== null)
205
+ head.push(truncNote);
206
+ if (anyEscaped)
207
+ head.push(ESCAPE_NOTE);
208
+ return fenced(head, body);
209
+ }
169
210
  function mintNotIncluded(d) {
211
+ if (d.target === 'mcp-resource') {
212
+ return `${firstLine('at-mention', 'an MCP resource', d.name)}\n${mcpWhere(d.server, d.uri)}. It is not included in this message: ${NOT_INCLUDED_REASON[d.reason]}.`;
213
+ }
170
214
  const size = sizePart(d.sizeBytes);
171
215
  return (`${firstLine('at-mention', targetWhat(d.target), d.name)}\n` +
172
216
  `Full path: ${showAttachmentName(d.path)}${size !== null ? ` (${size})` : ''}. It is not included in this message: ${NOT_INCLUDED_REASON[d.reason]}.`);
173
217
  }
174
- export function mintEnvelopeOf(d, imageDelivery) {
218
+ const TAG_OPEN = '<attachment';
219
+ const TAG_CLOSE = '</attachment>';
220
+ function attributeValue(s) {
221
+ let out = '';
222
+ for (const c of s) {
223
+ switch (c) {
224
+ case '&':
225
+ out += '&amp;';
226
+ break;
227
+ case '"':
228
+ out += '&quot;';
229
+ break;
230
+ case '<':
231
+ out += '&lt;';
232
+ break;
233
+ case '>':
234
+ out += '&gt;';
235
+ break;
236
+ default:
237
+ out += c;
238
+ }
239
+ }
240
+ return out;
241
+ }
242
+ function pathOf(d) {
243
+ if (d.kind === 'mcp-resource' || (d.kind === 'not-included' && d.target === 'mcp-resource'))
244
+ return undefined;
245
+ return d.path;
246
+ }
247
+ function tagAttributes(d) {
248
+ const path = pathOf(d);
249
+ return path === undefined ? '' : ` path="${attributeValue(showAttachmentName(path))}"`;
250
+ }
251
+ function envelopeInner(d, imageDelivery) {
175
252
  switch (d.kind) {
176
253
  case 'text':
177
254
  return mintText(d);
@@ -185,10 +262,18 @@ export function mintEnvelopeOf(d, imageDelivery) {
185
262
  return mintDirectory(d);
186
263
  case 'not-included':
187
264
  return mintNotIncluded(d);
265
+ case 'mcp-resource':
266
+ return mintMcpResource(d);
188
267
  }
189
268
  }
190
- export function mintReferenceOf(d) {
269
+ export function mintEnvelopeOf(d, imageDelivery) {
270
+ return `${TAG_OPEN}${tagAttributes(d)}>\n${envelopeInner(d, imageDelivery)}\n${TAG_CLOSE}`;
271
+ }
272
+ function referenceSentence(d) {
191
273
  const name = showAttachmentName(d.name);
274
+ if (d.kind === 'mcp-resource' || (d.kind === 'not-included' && d.target === 'mcp-resource')) {
275
+ return `The user mentioned the MCP resource ${name} (${showAttachmentName(d.uri)} on the server ${showAttachmentName(d.server)}) in this message; its contents are not included here.`;
276
+ }
192
277
  const path = showAttachmentName(d.path);
193
278
  const what = d.kind === 'directory' ? 'directory' : d.kind === 'not-included' ? targetWhat(d.target).replace(/^an? /, '') : 'file';
194
279
  if (d.kind !== 'directory' && d.kind !== 'not-included' && d.source === 'compact-restore') {
@@ -196,6 +281,9 @@ export function mintReferenceOf(d) {
196
281
  }
197
282
  return `The user mentioned the ${what} ${name} (${path}) in this message; its contents are not included here.`;
198
283
  }
284
+ export function mintReferenceOf(d) {
285
+ return `${TAG_OPEN}${tagAttributes(d)} />\n${referenceSentence(d)}`;
286
+ }
199
287
  export function mintUserAttachmentEnvelope(descriptor) {
200
288
  const d = readDescriptor(descriptor, 'required');
201
289
  if (d === undefined)
@@ -1,16 +1,21 @@
1
1
  import { engineWireTarget } from './engineWireTarget.js';
2
2
  import { createEngineCapReader } from './engineCapReader.js';
3
3
  export function projectUserInvokedSkillsCapability(caps) {
4
- if (caps === null || typeof caps !== 'object' || Array.isArray(caps))
5
- return undefined;
6
- if (!Object.hasOwn(caps, 'userInvokedSkills'))
7
- return { kind: 'not_reported' };
8
- const v = caps.userInvokedSkills;
9
- if (v === undefined)
10
- return { kind: 'not_reported' };
11
- if (typeof v !== 'boolean')
4
+ try {
5
+ if (caps === null || typeof caps !== 'object' || Array.isArray(caps))
6
+ return undefined;
7
+ if (!Object.hasOwn(caps, 'userInvokedSkills'))
8
+ return { kind: 'not_reported' };
9
+ const v = caps.userInvokedSkills;
10
+ if (v === undefined)
11
+ return { kind: 'not_reported' };
12
+ if (typeof v !== 'boolean')
13
+ return undefined;
14
+ return v ? { kind: 'present' } : { kind: 'absent' };
15
+ }
16
+ catch {
12
17
  return undefined;
13
- return v ? { kind: 'present' } : { kind: 'absent' };
18
+ }
14
19
  }
15
20
  const reader = createEngineCapReader({
16
21
  name: 'userInvokedSkills',
@@ -3,23 +3,28 @@ import { engineWireTarget } from './engineWireTarget.js';
3
3
  import { createEngineCapReader } from './engineCapReader.js';
4
4
  export const WEB_SEARCH_BACKEND_NONE = 'none';
5
5
  export function projectWebSearchBackendCapability(caps) {
6
- if (caps === null || typeof caps !== 'object')
7
- return undefined;
8
- if (caps === null || typeof caps !== 'object' || Array.isArray(caps))
9
- return undefined;
10
- if (!Object.hasOwn(caps, 'webSearch'))
11
- return { kind: 'not_reported' };
12
- const ws = caps.webSearch;
13
- if (ws === undefined)
14
- return { kind: 'not_reported' };
15
- if (ws === null || typeof ws !== 'object' || Array.isArray(ws))
16
- return undefined;
17
- const backend = ws.backend;
18
- if (typeof backend !== 'string' || backend === '')
6
+ try {
7
+ if (caps === null || typeof caps !== 'object')
8
+ return undefined;
9
+ if (caps === null || typeof caps !== 'object' || Array.isArray(caps))
10
+ return undefined;
11
+ if (!Object.hasOwn(caps, 'webSearch'))
12
+ return { kind: 'not_reported' };
13
+ const ws = caps.webSearch;
14
+ if (ws === undefined)
15
+ return { kind: 'not_reported' };
16
+ if (ws === null || typeof ws !== 'object' || Array.isArray(ws))
17
+ return undefined;
18
+ const backend = ws.backend;
19
+ if (typeof backend !== 'string' || backend === '')
20
+ return undefined;
21
+ if (backend === WEB_SEARCH_BACKEND_NONE)
22
+ return { kind: 'none' };
23
+ return { kind: 'present', view: { backend } };
24
+ }
25
+ catch {
19
26
  return undefined;
20
- if (backend === WEB_SEARCH_BACKEND_NONE)
21
- return { kind: 'none' };
22
- return { kind: 'present', view: { backend } };
27
+ }
23
28
  }
24
29
  const reader = createEngineCapReader({
25
30
  name: 'webSearch.backend',
@@ -28,6 +28,17 @@ export declare function drainingRetryDelayMs(err: unknown, attempt: number, opts
28
28
  ladder?: readonly number[];
29
29
  retryAfterCapMs?: number;
30
30
  }): number;
31
+ export declare function isInStreamDrainingEnd(frame: unknown): boolean;
32
+ export declare const STREAM_DRAINING_RECONNECT_MAX = 3;
33
+ export type StreamDrainingGiveUp = 'not_draining_end' | 'attempts_exhausted';
34
+ export type StreamDrainingDecision = {
35
+ readonly reconnectAfterMs: number;
36
+ } | {
37
+ readonly giveUp: StreamDrainingGiveUp;
38
+ };
39
+ export declare function streamDrainingDecision(frame: unknown, attempt: number, opts?: {
40
+ ladder?: readonly number[];
41
+ }): StreamDrainingDecision;
31
42
  export interface ScenarioDenyDetail {
32
43
  allowlist: string[];
33
44
  }