@sema-agent/client-core 0.94.0 → 0.95.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 (255) hide show
  1. package/CHANGELOG.md +341 -0
  2. package/README.md +70 -47
  3. package/dist/adapt/arms.js +5 -6
  4. package/dist/adapt/panelTasks.js +4 -10
  5. package/dist/adapt/toolCards.js +2 -2
  6. package/dist/adapt/wireShapes.js +3 -8
  7. package/dist/adapter/activeRunSelfHeal.d.ts +8 -3
  8. package/dist/adapter/activeRunSelfHeal.js +20 -12
  9. package/dist/adapter/downstream/eventToSdkMessage.js +24 -17
  10. package/dist/adapter/downstream/terminalToSdkResult.js +11 -3
  11. package/dist/adapter/runStream.js +2 -1
  12. package/dist/adapter/types.d.ts +7 -0
  13. package/dist/adapter/types.js +15 -0
  14. package/dist/agentSession/backgroundView.js +5 -1
  15. package/dist/agentSession/engineAgentRegistryRead.js +2 -1
  16. package/dist/agentSession/engineSessionBackgroundStop.js +3 -2
  17. package/dist/agentsWireCaps.d.ts +2 -0
  18. package/dist/approvalDecidedByCapability.d.ts +0 -1
  19. package/dist/approvalDecidedByCapability.js +2 -7
  20. package/dist/approvalsStreamLiveCapability.d.ts +0 -1
  21. package/dist/approvalsStreamLiveCapability.js +2 -6
  22. package/dist/atMostOnce.d.ts +8 -0
  23. package/dist/atMostOnce.js +36 -0
  24. package/dist/attachmentSeatCapability.d.ts +0 -1
  25. package/dist/attachmentSeatCapability.js +0 -3
  26. package/dist/attachmentsWireCaps.js +11 -6
  27. package/dist/bashExitReading.d.ts +10 -0
  28. package/dist/bashExitReading.js +12 -0
  29. package/dist/callerCwdCapability.d.ts +0 -1
  30. package/dist/callerCwdCapability.js +2 -7
  31. package/dist/centerWiringCapability.d.ts +1 -3
  32. package/dist/centerWiringCapability.js +18 -21
  33. package/dist/classifierStatus.js +4 -4
  34. package/dist/cloudConfigWireCaps.js +43 -21
  35. package/dist/compensations.js +1 -1
  36. package/dist/cycleOrder.d.ts +15 -0
  37. package/dist/cycleOrder.js +45 -0
  38. package/dist/decideFailureNote.js +4 -1
  39. package/dist/decideReceipt.d.ts +2 -1
  40. package/dist/decideReceipt.js +41 -13
  41. package/dist/declared/supportFloorDeclared.d.ts +1 -0
  42. package/dist/declared/supportFloorDeclared.js +5 -0
  43. package/dist/deviceExecutorManagementCapability.d.ts +0 -1
  44. package/dist/deviceExecutorManagementCapability.js +7 -16
  45. package/dist/deviceRebind.d.ts +88 -0
  46. package/dist/deviceRebind.js +242 -0
  47. package/dist/displayUntrusted.d.ts +1 -0
  48. package/dist/displayUntrusted.js +313 -11
  49. package/dist/engineAgentAbsence.d.ts +2 -2
  50. package/dist/engineAgentPanelStore.d.ts +8 -18
  51. package/dist/engineAgentPanelStore.js +19 -537
  52. package/dist/engineCapReader.d.ts +6 -6
  53. package/dist/engineCapReader.js +30 -43
  54. package/dist/engineCapsCache.d.ts +7 -2
  55. package/dist/engineCapsCache.js +64 -26
  56. package/dist/engineCredentialRequirement.d.ts +9 -0
  57. package/dist/engineCredentialRequirement.js +23 -0
  58. package/dist/engineEnvSourcing.d.ts +53 -0
  59. package/dist/engineEnvSourcing.js +81 -0
  60. package/dist/engineErrorCodes.d.ts +14 -3
  61. package/dist/engineErrorCodes.js +31 -10
  62. package/dist/engineIdentity.js +2 -0
  63. package/dist/engineWireSdk.js +2 -2
  64. package/dist/executionLaneCapability.d.ts +2 -3
  65. package/dist/executionLaneCapability.js +18 -23
  66. package/dist/fileHistoryCaptureCapability.d.ts +0 -1
  67. package/dist/fileHistoryCaptureCapability.js +2 -6
  68. package/dist/fileHistoryLegNotice.d.ts +3 -2
  69. package/dist/fileHistoryLegNotice.js +51 -2
  70. package/dist/fleet/fleetLedger.d.ts +2 -0
  71. package/dist/fleet/fleetLedger.js +69 -52
  72. package/dist/fleet/fleetProjection.d.ts +0 -3
  73. package/dist/fleet/fleetProjection.js +9 -16
  74. package/dist/fleetAgentPanelProjection.d.ts +4 -17
  75. package/dist/fleetAgentPanelProjection.js +7 -197
  76. package/dist/fleetAgentRowLike.d.ts +16 -0
  77. package/dist/fleetAgentRowLike.js +1 -0
  78. package/dist/gateOutcome.js +30 -18
  79. package/dist/generated/sdkFactTables.d.ts +2 -0
  80. package/dist/generated/sdkFactTables.js +17 -0
  81. package/dist/generated/settingsSchemaFns.d.ts +10 -0
  82. package/dist/generated/settingsSchemaFns.js +22 -0
  83. package/dist/headlessPermissionModeWire.d.ts +1 -1
  84. package/dist/headlessPermissionModeWire.js +1 -1
  85. package/dist/headlessReconnectWire.js +49 -20
  86. package/dist/hitl/allowRuleShadow.d.ts +1 -1
  87. package/dist/hitl/allowRuleShadow.js +49 -8
  88. package/dist/hitl/approvalCompleteParked.d.ts +1 -0
  89. package/dist/hitl/approvalCompleteParked.js +14 -0
  90. package/dist/hitl/approvalEndingCause.d.ts +5 -0
  91. package/dist/hitl/approvalEndingCause.js +61 -0
  92. package/dist/hitl/approvalHolds.d.ts +12 -2
  93. package/dist/hitl/approvalHolds.js +53 -2
  94. package/dist/hitl/approvalsFeed.js +23 -11
  95. package/dist/hitl/askGateWire.d.ts +2 -1
  96. package/dist/hitl/askGateWire.js +26 -1
  97. package/dist/hitl/automaticAnswerVerdict.js +2 -2
  98. package/dist/hitl/crashConverged.js +2 -0
  99. package/dist/hitl/decisionConflict.d.ts +3 -0
  100. package/dist/hitl/decisionConflict.js +43 -0
  101. package/dist/hitl/decisionRefusal.d.ts +4 -0
  102. package/dist/hitl/decisionRefusal.js +26 -1
  103. package/dist/hitl/frameRouter.js +26 -43
  104. package/dist/hitl/gateLedger.d.ts +5 -0
  105. package/dist/hitl/gateLedger.js +21 -0
  106. package/dist/hitl/hitlBridge.d.ts +12 -1
  107. package/dist/hitl/hitlBridge.js +68 -53
  108. package/dist/hitl/hitlHostSurface.js +3 -11
  109. package/dist/hitl/livePendingAsk.d.ts +16 -6
  110. package/dist/hitl/livePendingAsk.js +36 -4
  111. package/dist/hitl/parkExpiryJudgements.d.ts +19 -0
  112. package/dist/hitl/parkExpiryJudgements.js +46 -0
  113. package/dist/hitl/parkResolver.d.ts +3 -7
  114. package/dist/hitl/parkResolver.js +50 -26
  115. package/dist/hitl/persistedRulesWire.js +5 -1
  116. package/dist/hitl/planReviewWire.d.ts +2 -2
  117. package/dist/hitl/planReviewWire.js +56 -30
  118. package/dist/hitl/replayedApproval.d.ts +8 -0
  119. package/dist/hitl/replayedApproval.js +125 -10
  120. package/dist/hitl/resumeRunningCard.d.ts +2 -0
  121. package/dist/hitl/resumeRunningCard.js +15 -4
  122. package/dist/hitl/toolApprovalWire.d.ts +24 -4
  123. package/dist/hitl/toolApprovalWire.js +115 -38
  124. package/dist/hooksWireCaps.js +38 -6
  125. package/dist/host.d.ts +5 -0
  126. package/dist/host.js +3 -1
  127. package/dist/hostCallbackGuard.d.ts +1 -0
  128. package/dist/hostCallbackGuard.js +25 -0
  129. package/dist/index.d.ts +30 -11
  130. package/dist/index.js +20 -8
  131. package/dist/interactiveInterrupt.d.ts +8 -0
  132. package/dist/interactiveInterrupt.js +54 -0
  133. package/dist/limitsWire.d.ts +5 -0
  134. package/dist/limitsWire.js +49 -3
  135. package/dist/liveModelCatalog.d.ts +95 -0
  136. package/dist/liveModelCatalog.js +297 -32
  137. package/dist/mcpEngineLeg.d.ts +25 -0
  138. package/dist/mcpEngineLeg.js +71 -15
  139. package/dist/mcpPanel.js +2 -1
  140. package/dist/mcpProbeCapability.d.ts +0 -1
  141. package/dist/mcpProbeCapability.js +2 -6
  142. package/dist/mcpReconnect.d.ts +1 -3
  143. package/dist/mcpReconnect.js +10 -5
  144. package/dist/memoryComplianceCapability.d.ts +0 -1
  145. package/dist/memoryComplianceCapability.js +2 -6
  146. package/dist/memoryOriginCapability.d.ts +0 -1
  147. package/dist/memoryOriginCapability.js +2 -6
  148. package/dist/memoryVerbsWire.d.ts +1 -1
  149. package/dist/memoryVerbsWire.js +4 -0
  150. package/dist/model/liveCatalogShape.d.ts +14 -0
  151. package/dist/model/liveCatalogShape.js +71 -0
  152. package/dist/model/modelSupplyRules.d.ts +1 -0
  153. package/dist/model/modelSupplyRules.js +30 -9
  154. package/dist/model/modelsPolicy.d.ts +21 -0
  155. package/dist/model/modelsPolicy.js +90 -0
  156. package/dist/modelIdentityResolvability.d.ts +1 -1
  157. package/dist/modelIdentityResolvability.js +5 -12
  158. package/dist/modelSwitchFallback.d.ts +69 -0
  159. package/dist/modelSwitchFallback.js +206 -0
  160. package/dist/notifications.js +12 -11
  161. package/dist/outputStyleSeatCapability.d.ts +0 -1
  162. package/dist/outputStyleSeatCapability.js +2 -7
  163. package/dist/ownKey.d.ts +6 -0
  164. package/dist/ownKey.js +48 -0
  165. package/dist/panelReduce.d.ts +114 -0
  166. package/dist/panelReduce.js +1135 -0
  167. package/dist/panelStateHolder.d.ts +17 -0
  168. package/dist/panelStateHolder.js +201 -0
  169. package/dist/peerLaneCapability.d.ts +0 -1
  170. package/dist/peerLaneCapability.js +2 -6
  171. package/dist/permissionModesCapability.d.ts +0 -1
  172. package/dist/permissionModesCapability.js +5 -10
  173. package/dist/permissionRulesWriteCapability.d.ts +0 -1
  174. package/dist/permissionRulesWriteCapability.js +2 -6
  175. package/dist/permissionsAllowGrantCapability.d.ts +0 -1
  176. package/dist/permissionsAllowGrantCapability.js +3 -9
  177. package/dist/permissionsDeclaration.d.ts +6 -0
  178. package/dist/permissionsDeclaration.js +45 -0
  179. package/dist/printToolResultFrame.js +2 -10
  180. package/dist/promptDelivery.js +0 -8
  181. package/dist/resumeRefusalCopy.d.ts +18 -1
  182. package/dist/resumeRefusalCopy.js +126 -15
  183. package/dist/rewindArchiveCapability.d.ts +2 -1
  184. package/dist/rewindArchiveCapability.js +15 -7
  185. package/dist/rewindEngineAction.d.ts +21 -0
  186. package/dist/rewindEngineAction.js +36 -0
  187. package/dist/rewindWireCaps.d.ts +2 -1
  188. package/dist/rewindWireCaps.js +1 -0
  189. package/dist/runTerminal.d.ts +0 -11
  190. package/dist/runTerminal.js +4 -27
  191. package/dist/sdkRegistryTransit.d.ts +2 -1
  192. package/dist/sdkRegistryTransit.js +1 -0
  193. package/dist/sealedKeyCapability.d.ts +0 -1
  194. package/dist/sealedKeyCapability.js +3 -9
  195. package/dist/seatContract.d.ts +54 -25
  196. package/dist/seatContract.js +394 -171
  197. package/dist/sessionIdSegment.d.ts +1 -0
  198. package/dist/sessionIdSegment.js +11 -0
  199. package/dist/sessionMemoryErase.d.ts +1 -1
  200. package/dist/sessionMemoryErase.js +6 -1
  201. package/dist/sessionMemoryStatus.js +3 -2
  202. package/dist/sessionPolicyCapability.d.ts +0 -1
  203. package/dist/sessionPolicyCapability.js +2 -6
  204. package/dist/sessionReadRedactionCapability.d.ts +0 -1
  205. package/dist/sessionReadRedactionCapability.js +2 -7
  206. package/dist/sessionSyncFileHistoryCapability.d.ts +0 -1
  207. package/dist/sessionSyncFileHistoryCapability.js +2 -6
  208. package/dist/skillToolListsCapability.d.ts +0 -1
  209. package/dist/skillToolListsCapability.js +2 -6
  210. package/dist/skillsWireCaps.d.ts +4 -0
  211. package/dist/skillsWireCaps.js +66 -1
  212. package/dist/sqlEngineCapability.d.ts +0 -1
  213. package/dist/sqlEngineCapability.js +9 -14
  214. package/dist/steerPriorityCapability.d.ts +0 -1
  215. package/dist/steerPriorityCapability.js +3 -9
  216. package/dist/storePostureCapability.d.ts +1 -1
  217. package/dist/storePostureCapability.js +16 -16
  218. package/dist/streamDrainingLegs.d.ts +6 -0
  219. package/dist/streamDrainingLegs.js +23 -0
  220. package/dist/subagent/engineCompactWire.js +13 -38
  221. package/dist/subagent/engineDelegatedPrompt.js +10 -6
  222. package/dist/subagent/engineSubagentOutput.d.ts +0 -1
  223. package/dist/subagent/engineSubagentOutput.js +5 -20
  224. package/dist/subagent/engineSubagentTail.d.ts +0 -1
  225. package/dist/subagent/engineSubagentTail.js +7 -32
  226. package/dist/subagentContentStore.js +3 -3
  227. package/dist/supportFloor.js +2 -5
  228. package/dist/terminalStatuses.d.ts +30 -0
  229. package/dist/terminalStatuses.js +67 -0
  230. package/dist/toolResult.d.ts +5 -0
  231. package/dist/toolResult.js +88 -36
  232. package/dist/types/engineState.d.ts +2 -1
  233. package/dist/usageAnalytics.d.ts +146 -0
  234. package/dist/usageAnalytics.js +311 -0
  235. package/dist/userInvokedSkillsCapability.d.ts +0 -1
  236. package/dist/userInvokedSkillsCapability.js +2 -6
  237. package/dist/webSearchBackendCapability.d.ts +0 -1
  238. package/dist/webSearchBackendCapability.js +3 -7
  239. package/dist/webSearchWireCaps.js +3 -11
  240. package/dist/wireFailureShape.d.ts +4 -0
  241. package/dist/wireFailureShape.js +42 -2
  242. package/dist/wireRefusalCopy.js +2 -18
  243. package/dist/workflowClient.js +7 -11
  244. package/dist/workflowsJournalCapability.d.ts +0 -1
  245. package/dist/workflowsJournalCapability.js +2 -6
  246. package/dist/writeProtectionCapability.d.ts +0 -1
  247. package/dist/writeProtectionCapability.js +5 -10
  248. package/docs/INTEGRATION-CLIENTS.md +1033 -26
  249. package/package.json +5 -5
  250. package/dist/engineCapsGenerationGuard.d.ts +0 -4
  251. package/dist/engineCapsGenerationGuard.js +0 -19
  252. package/dist/panelRunningHistory.d.ts +0 -22
  253. package/dist/panelRunningHistory.js +0 -82
  254. package/dist/registryConflict.d.ts +0 -3
  255. package/dist/registryConflict.js +0 -44
@@ -0,0 +1,146 @@
1
+ import type { UsageAnalyticsParams, UsageBreakdown, UsageDimension, UsageGranularity, UsageMetric, UsageSeries, UsageSummary } from '@sema-agent/sdk';
2
+ export declare const USAGE_SENTENCES: Readonly<{
3
+ analyticsUnavailable: "Usage analytics aren’t available on this backend.";
4
+ analyticsUnread: "Usage analytics couldn’t be read just now.";
5
+ modelsUnavailable: "Per-model usage breakdown isn't available on this backend.";
6
+ modelsUnread: "Per-model usage breakdown couldn’t be read just now.";
7
+ }>;
8
+ export interface UsageSummaryQuery extends UsageAnalyticsParams {
9
+ signal?: AbortSignal;
10
+ }
11
+ export interface UsageSeriesQuery extends UsageAnalyticsParams {
12
+ metric?: UsageMetric;
13
+ granularity?: UsageGranularity;
14
+ signal?: AbortSignal;
15
+ }
16
+ export interface UsageBreakdownQuery extends UsageAnalyticsParams {
17
+ dimension?: UsageDimension;
18
+ signal?: AbortSignal;
19
+ }
20
+ export interface UsageAnalyticsVerbs {
21
+ summary?: (opts?: UsageSummaryQuery) => Promise<UsageSummary>;
22
+ series?: (opts?: UsageSeriesQuery) => Promise<UsageSeries>;
23
+ breakdown?: (opts?: UsageBreakdownQuery) => Promise<UsageBreakdown>;
24
+ }
25
+ export interface UsageAnalyticsClientLike {
26
+ usage: UsageAnalyticsVerbs;
27
+ }
28
+ export interface UsageReadOptions {
29
+ from?: string | number;
30
+ to?: string | number;
31
+ principal?: string;
32
+ signal?: AbortSignal;
33
+ }
34
+ export interface UsageSeriesReadOptions extends UsageReadOptions {
35
+ metric?: UsageMetric;
36
+ granularity?: UsageGranularity;
37
+ }
38
+ export interface UsageBreakdownReadOptions extends UsageReadOptions {
39
+ dimension?: UsageDimension;
40
+ }
41
+ export type UsageBound = 'exact' | 'lower' | 'approximate';
42
+ export type UsagePrecision = 'exact' | 'estimated';
43
+ export interface UsageTotalsView {
44
+ tasks?: number;
45
+ tokensIn?: number;
46
+ tokensOut?: number;
47
+ cost?: number;
48
+ precision: UsagePrecision;
49
+ }
50
+ export interface UsageWindowView {
51
+ from?: string;
52
+ to?: string;
53
+ truncated: boolean;
54
+ }
55
+ export type UsageSummaryView = UsageWindowView & UsageTotalsView;
56
+ export interface UsageSeriesPointView {
57
+ t: string;
58
+ v: number;
59
+ estimated?: boolean;
60
+ }
61
+ export interface UsageSeriesView extends UsageWindowView {
62
+ points: UsageSeriesPointView[];
63
+ unreadable: number;
64
+ }
65
+ export type UsageBreakdownRowView = UsageTotalsView & {
66
+ key: string;
67
+ };
68
+ export interface UsageBreakdownView extends UsageWindowView {
69
+ rows: UsageBreakdownRowView[];
70
+ unreadable: number;
71
+ }
72
+ export type UsageUnavailableWhy = 'capability' | 'feature' | 'route';
73
+ export type UsageReadVerdict<T> = {
74
+ readonly kind: 'ok';
75
+ readonly value: T;
76
+ } | {
77
+ readonly kind: 'unavailable';
78
+ readonly why: UsageUnavailableWhy;
79
+ } | {
80
+ readonly kind: 'failed';
81
+ readonly error: unknown;
82
+ };
83
+ export declare function classifyUsageFailure(e: unknown): UsageReadVerdict<never>;
84
+ export declare function projectUsageSummary(body: unknown): UsageSummaryView | undefined;
85
+ export declare function projectUsageSeries(body: unknown): UsageSeriesView | undefined;
86
+ export declare function projectUsageBreakdown(body: unknown): UsageBreakdownView | undefined;
87
+ export declare function readUsageSummary(client: UsageAnalyticsClientLike, opts?: UsageReadOptions): Promise<UsageReadVerdict<UsageSummaryView>>;
88
+ export declare function readUsageSeries(client: UsageAnalyticsClientLike, opts?: UsageSeriesReadOptions): Promise<UsageReadVerdict<UsageSeriesView>>;
89
+ export declare function readUsageBreakdown(client: UsageAnalyticsClientLike, opts?: UsageBreakdownReadOptions): Promise<UsageReadVerdict<UsageBreakdownView>>;
90
+ export interface UsageAmount {
91
+ readonly value: number;
92
+ readonly bound: UsageBound;
93
+ }
94
+ export declare function usageBoundPrefix(bound: UsageBound): '' | '≥' | '≈';
95
+ export interface UsageHeatmapCell {
96
+ readonly date: string;
97
+ readonly tasks: number;
98
+ }
99
+ export type UsageUnavailableCause = 'no_backend_face' | 'read_failed';
100
+ export type UsageModelsView = {
101
+ readonly state: 'available';
102
+ readonly rows: readonly UsageBreakdownRowView[];
103
+ readonly unreadable: number;
104
+ } | {
105
+ readonly state: 'unavailable';
106
+ readonly cause: UsageUnavailableCause;
107
+ readonly sentence: string;
108
+ };
109
+ export interface UsageAnalyticsReadings {
110
+ readonly state: 'available' | 'estimated';
111
+ readonly truncated: boolean;
112
+ readonly runs?: number;
113
+ readonly tokens?: UsageAmount;
114
+ readonly cost?: UsageAmount;
115
+ readonly activeDays?: number;
116
+ readonly peakHourUtc?: number;
117
+ readonly favoriteModel?: string;
118
+ readonly heatmap: readonly UsageHeatmapCell[] | null;
119
+ readonly models: UsageModelsView;
120
+ }
121
+ export interface UsageAnalyticsUnavailable {
122
+ readonly state: 'unavailable';
123
+ readonly cause: UsageUnavailableCause;
124
+ readonly sentence: string;
125
+ readonly models: Extract<UsageModelsView, {
126
+ state: 'unavailable';
127
+ }>;
128
+ }
129
+ export type UsageAnalyticsView = UsageAnalyticsReadings | UsageAnalyticsUnavailable;
130
+ export interface UsageAnalyticsInputs {
131
+ summary: UsageReadVerdict<UsageSummaryView>;
132
+ daySeries: UsageReadVerdict<UsageSeriesView>;
133
+ hourSeries: UsageReadVerdict<UsageSeriesView>;
134
+ modelBreakdown: UsageReadVerdict<UsageBreakdownView>;
135
+ }
136
+ export interface UsageAnalyticsWindow {
137
+ readonly from: number;
138
+ readonly to: number;
139
+ }
140
+ export type UsageRange = 'all' | '30d' | '7d';
141
+ export declare const USAGE_HEATMAP_MAX_DAYS = 70;
142
+ export declare function usageWindowOfRange(range: UsageRange, now: number): UsageAnalyticsWindow;
143
+ export declare function projectUsageAnalytics(inputs: UsageAnalyticsInputs, window: UsageAnalyticsWindow): UsageAnalyticsView;
144
+ export declare function readUsageAnalytics(client: UsageAnalyticsClientLike, window: UsageAnalyticsWindow, opts?: {
145
+ signal?: AbortSignal;
146
+ }): Promise<UsageAnalyticsView>;
@@ -0,0 +1,311 @@
1
+ export const USAGE_SENTENCES = Object.freeze({
2
+ analyticsUnavailable: 'Usage analytics aren’t available on this backend.',
3
+ analyticsUnread: 'Usage analytics couldn’t be read just now.',
4
+ modelsUnavailable: "Per-model usage breakdown isn't available on this backend.",
5
+ modelsUnread: 'Per-model usage breakdown couldn’t be read just now.',
6
+ });
7
+ const _sdkClientIsAnInjection = [true];
8
+ void _sdkClientIsAnInjection;
9
+ const isRecord = (v) => typeof v === 'object' && v !== null && !Array.isArray(v);
10
+ const nonNeg = (v) => typeof v === 'number' && Number.isFinite(v) && v >= 0;
11
+ const nonEmpty = (v) => typeof v === 'string' && v.length > 0;
12
+ export function classifyUsageFailure(e) {
13
+ let code;
14
+ let status;
15
+ try {
16
+ const o = e;
17
+ const rawCode = o?.errorCode;
18
+ const rawStatus = o?.status;
19
+ code = typeof rawCode === 'string' ? rawCode : undefined;
20
+ status = typeof rawStatus === 'number' ? rawStatus : undefined;
21
+ }
22
+ catch {
23
+ return { kind: 'failed', error: e };
24
+ }
25
+ if (code !== undefined) {
26
+ if (code.startsWith('capability.'))
27
+ return { kind: 'unavailable', why: 'capability' };
28
+ if (code.startsWith('feature.'))
29
+ return { kind: 'unavailable', why: 'feature' };
30
+ if (code === 'not_found.route')
31
+ return { kind: 'unavailable', why: 'route' };
32
+ return { kind: 'failed', error: e };
33
+ }
34
+ if (status === 501)
35
+ return { kind: 'unavailable', why: 'capability' };
36
+ return { kind: 'failed', error: e };
37
+ }
38
+ function windowOf(o) {
39
+ const w = { truncated: o.truncated === true };
40
+ if (nonEmpty(o.from))
41
+ w.from = o.from;
42
+ if (nonEmpty(o.to))
43
+ w.to = o.to;
44
+ return w;
45
+ }
46
+ const precisionOf = (estimated) => (estimated === false ? 'exact' : 'estimated');
47
+ const TOTAL_FIELDS = ['tasks', 'tokensIn', 'tokensOut', 'cost'];
48
+ const TOTAL_WIRE_KEYS = Object.freeze({ tasks: 'tasks', tokensIn: 'tokensIn', tokensOut: 'tokensOut', cost: 'costUsd' });
49
+ function totalsOf(o) {
50
+ const t = { precision: precisionOf(o.estimated) };
51
+ for (const field of TOTAL_FIELDS) {
52
+ const v = o[TOTAL_WIRE_KEYS[field]];
53
+ if (nonNeg(v))
54
+ t[field] = v;
55
+ }
56
+ return t;
57
+ }
58
+ export function projectUsageSummary(body) {
59
+ try {
60
+ if (!isRecord(body))
61
+ return undefined;
62
+ return { ...windowOf(body), ...totalsOf(body) };
63
+ }
64
+ catch {
65
+ return undefined;
66
+ }
67
+ }
68
+ export function projectUsageSeries(body) {
69
+ try {
70
+ if (!isRecord(body))
71
+ return undefined;
72
+ const series = body.series;
73
+ if (!Array.isArray(series))
74
+ return undefined;
75
+ const points = [];
76
+ let unreadable = 0;
77
+ for (const raw of series) {
78
+ if (!isRecord(raw) || !nonEmpty(raw.t) || !nonNeg(raw.v)) {
79
+ unreadable++;
80
+ continue;
81
+ }
82
+ const p = { t: raw.t, v: raw.v };
83
+ if (typeof raw.estimated === 'boolean')
84
+ p.estimated = raw.estimated;
85
+ points.push(p);
86
+ }
87
+ return { ...windowOf(body), points, unreadable };
88
+ }
89
+ catch {
90
+ return undefined;
91
+ }
92
+ }
93
+ export function projectUsageBreakdown(body) {
94
+ try {
95
+ if (!isRecord(body))
96
+ return undefined;
97
+ const breakdown = body.breakdown;
98
+ if (!Array.isArray(breakdown))
99
+ return undefined;
100
+ const rows = [];
101
+ let unreadable = 0;
102
+ for (const raw of breakdown) {
103
+ if (!isRecord(raw) || !nonEmpty(raw.key)) {
104
+ unreadable++;
105
+ continue;
106
+ }
107
+ rows.push({ key: raw.key, ...totalsOf(raw) });
108
+ }
109
+ return { ...windowOf(body), rows, unreadable };
110
+ }
111
+ catch {
112
+ return undefined;
113
+ }
114
+ }
115
+ function windowBag(opts) {
116
+ const bag = {};
117
+ if (opts.from !== undefined)
118
+ bag.from = opts.from;
119
+ if (opts.to !== undefined)
120
+ bag.to = opts.to;
121
+ if (opts.principal !== undefined)
122
+ bag.principal = opts.principal;
123
+ if (opts.signal !== undefined)
124
+ bag.signal = opts.signal;
125
+ return bag;
126
+ }
127
+ async function readOne(call, project, what) {
128
+ try {
129
+ const pending = call();
130
+ if (pending === undefined)
131
+ return { kind: 'unavailable', why: 'route' };
132
+ const view = project(await pending);
133
+ if (view === undefined)
134
+ return { kind: 'failed', error: new Error(`usage ${what}: malformed body`) };
135
+ return { kind: 'ok', value: view };
136
+ }
137
+ catch (e) {
138
+ return classifyUsageFailure(e);
139
+ }
140
+ }
141
+ export function readUsageSummary(client, opts = {}) {
142
+ return readOne(() => (typeof client?.usage?.summary === 'function' ? client.usage.summary(windowBag(opts)) : undefined), projectUsageSummary, 'summary');
143
+ }
144
+ export function readUsageSeries(client, opts = {}) {
145
+ return readOne(() => {
146
+ if (typeof client?.usage?.series !== 'function')
147
+ return undefined;
148
+ const bag = windowBag(opts);
149
+ if (opts.metric !== undefined)
150
+ bag.metric = opts.metric;
151
+ if (opts.granularity !== undefined)
152
+ bag.granularity = opts.granularity;
153
+ return client.usage.series(bag);
154
+ }, projectUsageSeries, 'series');
155
+ }
156
+ export function readUsageBreakdown(client, opts = {}) {
157
+ return readOne(() => {
158
+ if (typeof client?.usage?.breakdown !== 'function')
159
+ return undefined;
160
+ const bag = windowBag(opts);
161
+ if (opts.dimension !== undefined)
162
+ bag.dimension = opts.dimension;
163
+ return client.usage.breakdown(bag);
164
+ }, projectUsageBreakdown, 'breakdown');
165
+ }
166
+ export function usageBoundPrefix(bound) {
167
+ return bound === 'lower' ? '≥' : bound === 'approximate' ? '≈' : '';
168
+ }
169
+ const DAY_MS = 86_400_000;
170
+ export const USAGE_HEATMAP_MAX_DAYS = 70;
171
+ export function usageWindowOfRange(range, now) {
172
+ if (range === 'all')
173
+ return { from: 0, to: now };
174
+ const days = range === '30d' ? 30 : 7;
175
+ return { from: now - days * DAY_MS, to: now };
176
+ }
177
+ function unavailableCauseOf(v) {
178
+ return v.kind === 'unavailable' ? 'no_backend_face' : 'read_failed';
179
+ }
180
+ function modelsUnavailable(cause) {
181
+ return { state: 'unavailable', cause, sentence: cause === 'no_backend_face' ? USAGE_SENTENCES.modelsUnavailable : USAGE_SENTENCES.modelsUnread };
182
+ }
183
+ const DAY_KEY_RE = /^(\d{4})-(\d{2})-(\d{2})(?:[T ]|$)/;
184
+ function dayKeyOf(iso) {
185
+ const m = DAY_KEY_RE.exec(iso);
186
+ if (m === null)
187
+ return undefined;
188
+ const y = Number(m[1]);
189
+ const mo = Number(m[2]);
190
+ const d = Number(m[3]);
191
+ const at = new Date(Date.UTC(y, mo - 1, d));
192
+ return at.getUTCFullYear() === y && at.getUTCMonth() === mo - 1 && at.getUTCDate() === d ? iso.slice(0, 10) : undefined;
193
+ }
194
+ const isWhole = (v) => v.unreadable === 0 && !v.truncated;
195
+ function hourOfDayUtc(iso) {
196
+ const d = new Date(iso);
197
+ return Number.isNaN(d.getTime()) ? undefined : d.getUTCHours();
198
+ }
199
+ function peakHourOf(points) {
200
+ if (points.length === 0)
201
+ return undefined;
202
+ const perHour = new Array(24).fill(0);
203
+ for (const p of points) {
204
+ const h = hourOfDayUtc(p.t);
205
+ if (h === undefined)
206
+ return undefined;
207
+ perHour[h] = (perHour[h] ?? 0) + p.v;
208
+ }
209
+ let peakHour = -1;
210
+ let peakValue = -1;
211
+ perHour.forEach((value, h) => {
212
+ if (value > peakValue) {
213
+ peakValue = value;
214
+ peakHour = h;
215
+ }
216
+ });
217
+ return peakHour >= 0 && peakValue > 0 ? peakHour : undefined;
218
+ }
219
+ function heatmapOf(window, dayValues) {
220
+ if (!Number.isFinite(window.from) || !Number.isFinite(window.to))
221
+ return null;
222
+ const windowDays = Math.max(1, Math.round((window.to - window.from) / DAY_MS) + 1);
223
+ const days = Math.min(windowDays, USAGE_HEATMAP_MAX_DAYS);
224
+ const cells = [];
225
+ for (let i = days - 1; i >= 0; i--) {
226
+ const at = new Date(window.to - i * DAY_MS);
227
+ if (Number.isNaN(at.getTime()))
228
+ return null;
229
+ const key = at.toISOString().slice(0, 10);
230
+ cells.push({ date: key, tasks: dayValues.get(key) ?? 0 });
231
+ }
232
+ return cells;
233
+ }
234
+ export function projectUsageAnalytics(inputs, window) {
235
+ const s = inputs.summary;
236
+ if (s.kind !== 'ok') {
237
+ const cause = unavailableCauseOf(s);
238
+ return {
239
+ state: 'unavailable',
240
+ cause,
241
+ sentence: cause === 'no_backend_face' ? USAGE_SENTENCES.analyticsUnavailable : USAGE_SENTENCES.analyticsUnread,
242
+ models: modelsUnavailable(cause),
243
+ };
244
+ }
245
+ const sv = s.value;
246
+ const readings = {
247
+ state: sv.precision === 'estimated' ? 'estimated' : 'available',
248
+ truncated: sv.truncated,
249
+ heatmap: null,
250
+ models: modelsUnavailable('read_failed'),
251
+ };
252
+ if (sv.tasks !== undefined)
253
+ readings.runs = sv.tasks;
254
+ const exact = sv.precision === 'exact';
255
+ if (sv.tokensIn !== undefined && sv.tokensOut !== undefined) {
256
+ readings.tokens = { value: sv.tokensIn + sv.tokensOut, bound: !exact ? 'approximate' : sv.truncated ? 'lower' : 'exact' };
257
+ }
258
+ if (sv.cost !== undefined)
259
+ readings.cost = { value: sv.cost, bound: exact && !sv.truncated ? 'exact' : 'lower' };
260
+ const day = inputs.daySeries;
261
+ if (day.kind === 'ok' && isWhole(day.value)) {
262
+ const dayValues = new Map();
263
+ let allKeysReadable = true;
264
+ for (const p of day.value.points) {
265
+ const key = dayKeyOf(p.t);
266
+ if (key === undefined)
267
+ allKeysReadable = false;
268
+ else
269
+ dayValues.set(key, (dayValues.get(key) ?? 0) + p.v);
270
+ }
271
+ if (allKeysReadable) {
272
+ readings.activeDays = [...dayValues.values()].filter((v) => v > 0).length;
273
+ readings.heatmap = heatmapOf(window, dayValues);
274
+ }
275
+ }
276
+ const hour = inputs.hourSeries;
277
+ if (hour.kind === 'ok' && isWhole(hour.value)) {
278
+ const peak = peakHourOf(hour.value.points);
279
+ if (peak !== undefined)
280
+ readings.peakHourUtc = peak;
281
+ }
282
+ const br = inputs.modelBreakdown;
283
+ if (br.kind === 'ok') {
284
+ readings.models = { state: 'available', rows: br.value.rows, unreadable: br.value.unreadable };
285
+ if (isWhole(br.value)) {
286
+ let favorite;
287
+ for (const row of br.value.rows) {
288
+ if (row.tasks === undefined)
289
+ continue;
290
+ if (favorite === undefined || row.tasks > (favorite.tasks ?? -1))
291
+ favorite = row;
292
+ }
293
+ if (favorite !== undefined)
294
+ readings.favoriteModel = favorite.key;
295
+ }
296
+ }
297
+ else {
298
+ readings.models = modelsUnavailable(unavailableCauseOf(br));
299
+ }
300
+ return readings;
301
+ }
302
+ export async function readUsageAnalytics(client, window, opts = {}) {
303
+ const base = { from: window.from, to: window.to, ...(opts.signal ? { signal: opts.signal } : {}) };
304
+ const [summary, daySeries, hourSeries, modelBreakdown] = await Promise.all([
305
+ readUsageSummary(client, base),
306
+ readUsageSeries(client, { ...base, metric: 'tasks', granularity: 'day' }),
307
+ readUsageSeries(client, { ...base, metric: 'tasks', granularity: 'hour' }),
308
+ readUsageBreakdown(client, { ...base, dimension: 'model' }),
309
+ ]);
310
+ return projectUsageAnalytics({ summary, daySeries, hourSeries, modelBreakdown }, window);
311
+ }
@@ -14,5 +14,4 @@ export declare function noteEngineCapsForUserInvokedSkills(baseUrl: string, caps
14
14
  export declare function observedUserInvokedSkills(baseUrl?: string | undefined): UserInvokedSkillsReading;
15
15
  export declare function userInvokedSkillsAvailable(reading: UserInvokedSkillsReading): 'yes' | 'no' | 'unknown';
16
16
  export declare function userInvokedSkillsDoctorDetail(reading: UserInvokedSkillsReading): string;
17
- export declare function forgetUserInvokedSkillsReading(baseUrl: string | undefined): void;
18
17
  export declare function __resetUserInvokedSkillsReadingsForTests(): void;
@@ -1,12 +1,11 @@
1
1
  import { engineWireTarget } from './engineWireTarget.js';
2
2
  import { createEngineCapReader } from './engineCapReader.js';
3
+ import { ownDataField } from './ownKey.js';
3
4
  export function projectUserInvokedSkillsCapability(caps) {
4
5
  try {
5
6
  if (caps === null || typeof caps !== 'object' || Array.isArray(caps))
6
7
  return undefined;
7
- if (!Object.hasOwn(caps, 'userInvokedSkills'))
8
- return { kind: 'not_reported' };
9
- const v = caps.userInvokedSkills;
8
+ const v = ownDataField(caps, 'userInvokedSkills');
10
9
  if (v === undefined)
11
10
  return { kind: 'not_reported' };
12
11
  if (typeof v !== 'boolean')
@@ -47,9 +46,6 @@ export function userInvokedSkillsDoctorDetail(reading) {
47
46
  return 'user-invoked skills on — skills the user names on a request take effect when the run starts';
48
47
  }
49
48
  }
50
- export function forgetUserInvokedSkillsReading(baseUrl) {
51
- reader.forget(baseUrl);
52
- }
53
49
  export function __resetUserInvokedSkillsReadingsForTests() {
54
50
  reader.__resetForTests();
55
51
  }
@@ -18,5 +18,4 @@ export declare function noteEngineCapsForWebSearchBackend(baseUrl: string, caps:
18
18
  }): void;
19
19
  export declare function observedWebSearchBackend(baseUrl?: string | undefined): WebSearchBackendReading;
20
20
  export declare function webSearchBackendDoctorDetail(reading: WebSearchBackendReading): string;
21
- export declare function forgetWebSearchBackendReading(baseUrl: string | undefined): void;
22
21
  export declare function __resetWebSearchBackendReadingsForTests(): void;
@@ -1,6 +1,7 @@
1
1
  import { capForDisplay } from './fleetTaskDesc.js';
2
2
  import { engineWireTarget } from './engineWireTarget.js';
3
3
  import { createEngineCapReader } from './engineCapReader.js';
4
+ import { ownDataField } from './ownKey.js';
4
5
  export const WEB_SEARCH_BACKEND_NONE = 'none';
5
6
  export function projectWebSearchBackendCapability(caps) {
6
7
  try {
@@ -8,14 +9,12 @@ export function projectWebSearchBackendCapability(caps) {
8
9
  return undefined;
9
10
  if (caps === null || typeof caps !== 'object' || Array.isArray(caps))
10
11
  return undefined;
11
- if (!Object.hasOwn(caps, 'webSearch'))
12
- return { kind: 'not_reported' };
13
- const ws = caps.webSearch;
12
+ const ws = ownDataField(caps, 'webSearch');
14
13
  if (ws === undefined)
15
14
  return { kind: 'not_reported' };
16
15
  if (ws === null || typeof ws !== 'object' || Array.isArray(ws))
17
16
  return undefined;
18
- const backend = ws.backend;
17
+ const backend = ownDataField(ws, 'backend');
19
18
  if (typeof backend !== 'string' || backend === '')
20
19
  return undefined;
21
20
  if (backend === WEB_SEARCH_BACKEND_NONE)
@@ -56,9 +55,6 @@ export function webSearchBackendDoctorDetail(reading) {
56
55
  '(per-request overrides are not echoed here)');
57
56
  }
58
57
  }
59
- export function forgetWebSearchBackendReading(baseUrl) {
60
- reader.forget(baseUrl);
61
- }
62
58
  export function __resetWebSearchBackendReadingsForTests() {
63
59
  reader.__resetForTests();
64
60
  }
@@ -1,3 +1,4 @@
1
+ import { SETTINGS_IS_HIERARCHICAL_URL } from './generated/settingsSchemaFns.js';
1
2
  import { hostEnv } from './hostEnv.js';
2
3
  export const WEBSEARCH_ENV = {
3
4
  provider: 'SEMA_WEBSEARCH_PROVIDER',
@@ -6,15 +7,6 @@ export const WEBSEARCH_ENV = {
6
7
  maxResults: 'SEMA_WEBSEARCH_MAX_RESULTS',
7
8
  };
8
9
  const isBlankText = (v) => v.trim() === '';
9
- function isHierarchicalUrl(raw) {
10
- try {
11
- const u = new URL(raw);
12
- return u.href.startsWith(u.protocol + '//');
13
- }
14
- catch {
15
- return false;
16
- }
17
- }
18
10
  function judgeProvider(raw) {
19
11
  const word = typeof raw === 'string' ? raw.trim().toLowerCase() : undefined;
20
12
  if (word === 'brave' || word === 'tavily' || word === 'searxng')
@@ -71,7 +63,7 @@ function judgeEndpoint(raw) {
71
63
  return { ok: false, issue: ENDPOINT_ISSUE };
72
64
  if (isBlankText(raw))
73
65
  return { ok: true, value: undefined };
74
- if (!isHierarchicalUrl(raw))
66
+ if (!SETTINGS_IS_HIERARCHICAL_URL(raw))
75
67
  return { ok: false, issue: ENDPOINT_ISSUE };
76
68
  const u = new URL(raw);
77
69
  return u.username !== '' || u.password !== ''
@@ -236,7 +228,7 @@ export function webSearchVerdictMessage(verdict, context = 'submit') {
236
228
  : `Web search is misconfigured: ${where} ${verdict.issue}. Requests will be refused until you ${fix}.`;
237
229
  }
238
230
  export function webSearchEndpointForDisplay(raw) {
239
- if (typeof raw !== 'string' || !isHierarchicalUrl(raw))
231
+ if (typeof raw !== 'string' || !SETTINGS_IS_HIERARCHICAL_URL(raw))
240
232
  return undefined;
241
233
  try {
242
234
  const u = new URL(raw);
@@ -10,6 +10,9 @@ interface WireFailureShape {
10
10
  message: string;
11
11
  ownMessage?: string;
12
12
  }
13
+ export declare const THROWN_SLOT_UNREADABLE: unique symbol;
14
+ export declare function readThrownSlot(o: unknown, key: string, own?: boolean): unknown;
15
+ export declare function thrownIsInstanceOf<C extends abstract new (...args: never[]) => unknown>(e: unknown, cls: C): e is InstanceType<C>;
13
16
  export declare function readErrorNumber(o: object, key: string): number | undefined;
14
17
  export declare function readErrorRecord(o: object, key: string): object | undefined;
15
18
  export declare function readErrorString(o: object, key: string): string | undefined;
@@ -21,4 +24,5 @@ export declare function wireFailureTextsOf(e: unknown): {
21
24
  reason: string;
22
25
  log: string;
23
26
  };
27
+ export declare function readRefusalField(o: object): string | undefined;
24
28
  export {};
@@ -1,9 +1,24 @@
1
- function readGuarded(o, key) {
1
+ export const THROWN_SLOT_UNREADABLE = Symbol('thrown-slot-unreadable');
2
+ export function readThrownSlot(o, key, own = false) {
2
3
  try {
4
+ if (own && !Object.hasOwn(o, key))
5
+ return undefined;
3
6
  return o[key];
4
7
  }
5
8
  catch {
6
- return undefined;
9
+ return THROWN_SLOT_UNREADABLE;
10
+ }
11
+ }
12
+ function readGuarded(o, key) {
13
+ const v = readThrownSlot(o, key);
14
+ return v === THROWN_SLOT_UNREADABLE ? undefined : v;
15
+ }
16
+ export function thrownIsInstanceOf(e, cls) {
17
+ try {
18
+ return e instanceof cls;
19
+ }
20
+ catch {
21
+ return false;
7
22
  }
8
23
  }
9
24
  function readErrorSlot(obj, key, accept) {
@@ -189,3 +204,28 @@ export function wireFailureTextsOf(e) {
189
204
  return { reason: String(e), log };
190
205
  return { reason: WIRE_FAILURE_UNRENDERABLE_TEXT, log };
191
206
  }
207
+ export function readRefusalField(o) {
208
+ const str = (rec) => {
209
+ let v;
210
+ try {
211
+ v = rec.field;
212
+ }
213
+ catch {
214
+ return undefined;
215
+ }
216
+ return typeof v === 'string' && v !== '' ? v : undefined;
217
+ };
218
+ let extra;
219
+ try {
220
+ extra = o.extra;
221
+ }
222
+ catch {
223
+ extra = undefined;
224
+ }
225
+ if (typeof extra === 'object' && extra !== null && !Array.isArray(extra)) {
226
+ const fromExtra = str(extra);
227
+ if (fromExtra !== undefined)
228
+ return fromExtra;
229
+ }
230
+ return str(o);
231
+ }
@@ -1,6 +1,6 @@
1
- import { readErrorNumber, readErrorRecord, readErrorStrictStringList, readErrorString, readErrorStringList } from './wireFailureShape.js';
1
+ import { readErrorNumber, readErrorRecord, readErrorStrictStringList, readErrorString, readErrorStringList, readRefusalField } from './wireFailureShape.js';
2
2
  import { readOwnNonEmptyString } from './ownKey.js';
3
- import { ACTIVE_RUN_BUSY_ERROR_CODE, FLEET_LEASE_UNAVAILABLE, REQUEST_AUTONOMY_LANE_REFUSED, REQUEST_FIELD_CONFLICT, RESUME_POLICY_FOLD_CODE, SCENARIO_ASSIGNMENT_UNKNOWN_ERROR_CODE, USAGE_WINDOW_EXHAUSTED } from './engineErrorCodes.js';
3
+ import { ACTIVE_RUN_BUSY_ERROR_CODE, FLEET_LEASE_UNAVAILABLE, REQUEST_AUTONOMY_LANE_REFUSED, REQUEST_FIELD_CONFLICT, RESUME_OUTCOME_INVALID, RESUME_POLICY_FOLD_CODE, SCENARIO_ASSIGNMENT_UNKNOWN_ERROR_CODE, USAGE_WINDOW_EXHAUSTED } from './engineErrorCodes.js';
4
4
  import { capForDisplay } from './fleetTaskDesc.js';
5
5
  import { effectiveWireErrorCode } from './wireErrorTriage.js';
6
6
  import { CORE_TOOL_DECLARATION_CODES } from './generated/engineFactTables.js';
@@ -184,23 +184,7 @@ export function scenarioAssignmentUnknownFromError(err) {
184
184
  const SETTLED_BY_NOT_IN_PROOF = 'settled_by_not_in_proof';
185
185
  const DECIDED_BY_NOT_IN_PROOF = 'decided_by_not_in_proof';
186
186
  const RETIRED_DECISION_KEY = 'settledBy';
187
- const RESUME_OUTCOME_INVALID = 'resume_outcome_invalid';
188
187
  const APPROVAL_NON_BINDING_FIELDS = new Set(['hostDecision', 'approver']);
189
- function readRefusalField(o) {
190
- let extra;
191
- try {
192
- extra = o.extra;
193
- }
194
- catch {
195
- extra = undefined;
196
- }
197
- if (typeof extra === 'object' && extra !== null && !Array.isArray(extra)) {
198
- const fromExtra = readStringField(extra, 'field');
199
- if (fromExtra !== undefined)
200
- return fromExtra;
201
- }
202
- return readStringField(o, 'field');
203
- }
204
188
  export function denyAttributionRefusalFromError(err, sentAttributionKey) {
205
189
  try {
206
190
  if (err === null || typeof err !== 'object')