@code-yeongyu/senpi 2026.11.6-3 → 2026.11.9-2

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 (147) hide show
  1. package/CHANGELOG.md +34 -0
  2. package/dist/bundle/chunks/{chunk-5LKB732P.js → chunk-3TZQXZKH.js} +1 -1
  3. package/dist/bundle/chunks/{chunk-X53PD4ZC.js → chunk-45MVD7BU.js} +1 -1
  4. package/dist/bundle/chunks/{chunk-L7ZIS3MC.js → chunk-4JIBEZZE.js} +3 -3
  5. package/dist/bundle/chunks/{chunk-CF5OGBC4.js → chunk-4NYNMAYF.js} +1 -1
  6. package/dist/bundle/chunks/{chunk-6CEDL5S2.js → chunk-4UR6UFBH.js} +1 -1
  7. package/dist/bundle/chunks/{chunk-F5JLDCYD.js → chunk-527Q2UYM.js} +5 -5
  8. package/dist/bundle/chunks/{chunk-7DRWPZ3N.js → chunk-622VYTRI.js} +1 -1
  9. package/dist/bundle/chunks/{chunk-V2QAYGOK.js → chunk-7CFORZPE.js} +22 -22
  10. package/dist/bundle/chunks/{chunk-GKAYPJ4E.js → chunk-7PROV6BB.js} +1 -1
  11. package/dist/bundle/chunks/{chunk-4E7GDG4H.js → chunk-7VMEE62L.js} +1 -1
  12. package/dist/bundle/chunks/{chunk-4BATXPB7.js → chunk-K3UHGDUQ.js} +1 -1
  13. package/dist/bundle/chunks/{chunk-MJTCZUXS.js → chunk-LHSWAJJL.js} +1 -1
  14. package/dist/bundle/chunks/{chunk-SB6MXNO5.js → chunk-M7GF73FO.js} +2 -2
  15. package/dist/bundle/chunks/{chunk-CFEHJ4VU.js → chunk-NDSWTAYJ.js} +1 -1
  16. package/dist/bundle/chunks/{chunk-JMQOLOC2.js → chunk-Q6NR6644.js} +1 -1
  17. package/dist/bundle/chunks/{chunk-EO2VPZ2K.js → chunk-SGAFIMAU.js} +1 -1
  18. package/dist/bundle/chunks/{chunk-BASS47UD.js → chunk-STGOYIXC.js} +1 -1
  19. package/dist/bundle/chunks/{chunk-DSWDJ7HT.js → chunk-WQ4RBPMS.js} +1 -1
  20. package/dist/bundle/chunks/chunk-XZDCMDUH.js +2 -0
  21. package/dist/bundle/chunks/{chunk-LUZVGPQ7.js → chunk-Y5L4UFMI.js} +1 -1
  22. package/dist/bundle/chunks/{cli-main-RWHDZKTQ.js → cli-main-XBEFQW5A.js} +1 -1
  23. package/dist/bundle/chunks/{hand-entry-G5LYF62Q.js → hand-entry-QFEY3JMQ.js} +1 -1
  24. package/dist/bundle/chunks/{headless-host-ES74ZB6M.js → headless-host-YXUGONR3.js} +1 -1
  25. package/dist/bundle/chunks/{host-command-I2JZ5JM7.js → host-command-VHX2ILLM.js} +1 -1
  26. package/dist/bundle/chunks/{host-session-command-Y52NFPP2.js → host-session-command-P3MRW63Z.js} +1 -1
  27. package/dist/bundle/chunks/{interactive-mode-PA7DGVS6.js → interactive-mode-YGL3QHU6.js} +1 -1
  28. package/dist/bundle/chunks/{main-OBL6375C.js → main-5SD4DHMC.js} +1 -1
  29. package/dist/bundle/chunks/{multi-session-host-NM6I5BWZ.js → multi-session-host-PGRHY3JP.js} +1 -1
  30. package/dist/bundle/chunks/{package-manager-cli-GNRQ5MVR.js → package-manager-cli-SADLU7QX.js} +1 -1
  31. package/dist/bundle/chunks/{role-F56BS2ZQ.js → role-4BUU46VW.js} +3 -3
  32. package/dist/bundle/chunks/{rotation-stream-BN2YK2MC.js → rotation-stream-TODBIHXJ.js} +1 -1
  33. package/dist/bundle/chunks/{rpc-mode-G4KIP7XS.js → rpc-mode-27TC7W6K.js} +1 -1
  34. package/dist/bundle/chunks/{run-attached-tui-XTGPAGYM.js → run-attached-tui-MXCC5IZR.js} +1 -1
  35. package/dist/bundle/chunks/{schedule-command-L4M3TSQ3.js → schedule-command-MX56D6UE.js} +1 -1
  36. package/dist/bundle/chunks/{session-control-endpoint-V46WMCDF.js → session-control-endpoint-IL6ERD4U.js} +1 -1
  37. package/dist/bundle/chunks/{session-picker-AG6KSR5A.js → session-picker-6VRC25MJ.js} +1 -1
  38. package/dist/bundle/chunks/session-worker.js +44 -44
  39. package/dist/bundle/cli.js +2 -2
  40. package/dist/bundle/index.js +1 -1
  41. package/dist/bundle/rpc-entry.js +1 -1
  42. package/dist/bundle/runtime-manifest.json +1 -1
  43. package/dist/core/agent-session-services.d.ts +2 -0
  44. package/dist/core/agent-session-services.js +1 -0
  45. package/dist/core/agent-session.d.ts +8 -0
  46. package/dist/core/agent-session.js +51 -4
  47. package/dist/core/egress/classify.d.ts +8 -3
  48. package/dist/core/egress/classify.js +177 -158
  49. package/dist/core/egress/console-redactor.d.ts +7 -0
  50. package/dist/core/egress/console-redactor.js +74 -0
  51. package/dist/core/egress/credentials.d.ts +73 -0
  52. package/dist/core/egress/credentials.js +20 -0
  53. package/dist/core/egress/device-custody.d.ts +1 -1
  54. package/dist/core/egress/device-custody.js +18 -15
  55. package/dist/core/egress/device-proxy-call.d.ts +59 -0
  56. package/dist/core/egress/device-proxy-call.js +148 -0
  57. package/dist/core/egress/device-proxy-lost-calls.d.ts +27 -0
  58. package/dist/core/egress/device-proxy-lost-calls.js +28 -0
  59. package/dist/core/egress/device-proxy-ports.d.ts +49 -0
  60. package/dist/core/egress/device-proxy-ports.js +2 -0
  61. package/dist/core/egress/device-proxy-request.d.ts +34 -0
  62. package/dist/core/egress/device-proxy-request.js +69 -0
  63. package/dist/core/egress/device-proxy-waits.d.ts +40 -0
  64. package/dist/core/egress/device-proxy-waits.js +67 -0
  65. package/dist/core/egress/device-proxy.d.ts +3 -0
  66. package/dist/core/egress/device-proxy.js +214 -0
  67. package/dist/core/egress/endpoint.js +2 -1
  68. package/dist/core/egress/entry-verify.d.ts +13 -0
  69. package/dist/core/egress/entry-verify.js +33 -0
  70. package/dist/core/egress/hpke.js +13 -9
  71. package/dist/core/egress/key-policy-template.d.ts +7 -1
  72. package/dist/core/egress/ledger-schema.d.ts +80 -0
  73. package/dist/core/egress/ledger-schema.js +117 -0
  74. package/dist/core/egress/ledger.d.ts +55 -0
  75. package/dist/core/egress/ledger.js +159 -0
  76. package/dist/core/egress/pinned-infra.d.ts +23 -2
  77. package/dist/core/egress/pinned-infra.js +9 -2
  78. package/dist/core/egress/redact.d.ts +10 -0
  79. package/dist/core/egress/redact.js +134 -0
  80. package/dist/core/egress/sealed-source.d.ts +35 -0
  81. package/dist/core/egress/sealed-source.js +140 -0
  82. package/dist/core/egress/verified-keys.js +1 -1
  83. package/dist/core/egress/wire.d.ts +5 -3
  84. package/dist/core/egress/wire.js +5 -3
  85. package/dist/core/extensions/builtin/monitor-state-event.d.ts +26 -0
  86. package/dist/core/extensions/builtin/monitor-state-event.js +2 -0
  87. package/dist/core/extensions/builtin/terminal/background-age.d.ts +25 -0
  88. package/dist/core/extensions/builtin/terminal/background-age.js +91 -0
  89. package/dist/core/extensions/builtin/terminal/extension-state.d.ts +1 -1
  90. package/dist/core/extensions/builtin/terminal/extension-state.js +21 -3
  91. package/dist/core/extensions/builtin/terminal/notify.d.ts +6 -0
  92. package/dist/core/extensions/builtin/terminal/notify.js +39 -5
  93. package/dist/core/extensions/builtin/terminal/session-bundle.d.ts +9 -2
  94. package/dist/core/extensions/builtin/terminal/session-bundle.js +36 -1
  95. package/dist/core/extensions/builtin/terminal/settings.d.ts +2 -0
  96. package/dist/core/extensions/builtin/terminal/settings.js +8 -0
  97. package/dist/core/extensions/builtin/terminal/tools/background-auto-reap.d.ts +15 -0
  98. package/dist/core/extensions/builtin/terminal/tools/background-auto-reap.js +87 -0
  99. package/dist/core/extensions/builtin/terminal/tools/bash.d.ts +2 -0
  100. package/dist/core/extensions/builtin/terminal/tools/bash.js +6 -6
  101. package/dist/core/extensions/builtin/terminal/tools/child-placement.d.ts +8 -0
  102. package/dist/core/extensions/builtin/terminal/tools/child-placement.js +43 -6
  103. package/dist/core/extensions/builtin/terminal/tools/context.d.ts +11 -2
  104. package/dist/core/extensions/session-control-types.d.ts +15 -0
  105. package/dist/core/extensions/session-control-types.js +11 -0
  106. package/dist/core/external-admission.d.ts +16 -2
  107. package/dist/core/external-admission.js +22 -4
  108. package/dist/core/retry-fallback/controller.d.ts +1 -0
  109. package/dist/core/retry-fallback/controller.js +3 -0
  110. package/dist/core/retry-fallback/unusable.d.ts +2 -0
  111. package/dist/core/retry-fallback/unusable.js +7 -1
  112. package/dist/core/retry-fallback/usage-limit.d.ts +6 -1
  113. package/dist/core/retry-fallback/usage-limit.js +9 -4
  114. package/dist/core/sdk.d.ts +6 -1
  115. package/dist/core/sdk.js +1 -0
  116. package/dist/core/terminal-settings.d.ts +1 -0
  117. package/dist/core/turn-origin.d.ts +4 -0
  118. package/dist/core/turn-origin.js +10 -0
  119. package/dist/modes/interactive/components/remote-delivery-message.d.ts +2 -1
  120. package/dist/modes/interactive/components/remote-delivery-message.js +13 -5
  121. package/dist/modes/rpc/admit-external-message-command.d.ts +37 -0
  122. package/dist/modes/rpc/admit-external-message-command.js +61 -0
  123. package/dist/modes/rpc/admit-external-message-wire.d.ts +42 -0
  124. package/dist/modes/rpc/admit-external-message-wire.js +55 -0
  125. package/dist/modes/rpc/connection-handler.js +14 -0
  126. package/dist/modes/rpc/effect-commands.d.ts +1 -1
  127. package/dist/modes/rpc/host-idle-handover.js +1 -0
  128. package/dist/modes/rpc/method-classes-schema-delivery.d.ts +32 -0
  129. package/dist/modes/rpc/method-classes-schema-delivery.js +22 -0
  130. package/dist/modes/rpc/method-classes-schema-session.d.ts +27 -0
  131. package/dist/modes/rpc/method-classes-schema-session.js +2 -0
  132. package/dist/modes/rpc/method-classes-schema.d.ts +27 -0
  133. package/dist/modes/rpc/method-classes.d.ts +1 -0
  134. package/dist/modes/rpc/method-classes.js +2 -0
  135. package/dist/modes/rpc/rpc-input-validation.js +4 -3
  136. package/dist/modes/rpc/rpc-response-types.d.ts +2 -1
  137. package/dist/modes/rpc/rpc-session-command-types.d.ts +2 -1
  138. package/dist/modes/rpc/tool-child/terminal-jobs.d.ts +1 -0
  139. package/dist/modes/rpc/tool-child/terminal-jobs.js +17 -1
  140. package/docs/engine-socket/fixtures/admission-routes/h-legacy.json +1 -1
  141. package/docs/engine-socket/fixtures/rpc-inbound-samples.json +19 -2
  142. package/docs/rpc.md +57 -0
  143. package/docs/terminal-tools.md +25 -1
  144. package/native/socket-secret/prebuilds/win32-arm64/senpi_socket_secret.win32-arm64.node +0 -0
  145. package/native/socket-secret/prebuilds/win32-x64/senpi_socket_secret.win32-x64.node +0 -0
  146. package/package.json +6 -6
  147. package/dist/bundle/chunks/chunk-UODSYQ2D.js +0 -2
@@ -19,165 +19,183 @@ const COST = [
19
19
  "tiers[].cacheWrite",
20
20
  ];
21
21
  const PERCENTILES = ["", ".p50", ".p75", ".p90", ".p99"];
22
- const COMPAT = [
23
- "supportsWebSearch",
24
- "supportsStore",
25
- "supportsDeveloperRole",
26
- "supportsReasoningEffort",
27
- "supportsUsageInStreaming",
28
- "supportsFinishReason",
29
- "maxTokensField",
30
- "requiresToolResultName",
31
- "requiresAssistantAfterToolResult",
32
- "requiresThinkingAsText",
33
- "requiresReasoningContentOnAssistantMessages",
34
- "supportsDisabledThinking",
35
- "thinkingFormat",
36
- "chatTemplateKwargs.{key}",
37
- "chatTemplateKwargs.*",
38
- "chatTemplateKwargs.*.$var",
39
- "chatTemplateKwargs.*.omitWhenOff",
40
- "cacheControlFormat",
41
- ...[
42
- "allow_fallbacks",
43
- "require_parameters",
44
- "data_collection",
45
- "zdr",
46
- "enforce_distillable_text",
47
- "order[]",
48
- "only[]",
49
- "ignore[]",
50
- "quantizations[]",
51
- "sort",
52
- "sort.by",
53
- "sort.partition",
54
- ...["prompt", "completion", "image", "audio", "request"].map((part) => `max_price.${part}`),
55
- ...PERCENTILES.map((suffix) => `preferred_min_throughput${suffix}`),
56
- ...PERCENTILES.map((suffix) => `preferred_max_latency${suffix}`),
57
- ].map((field) => `openRouterRouting.${field}`),
58
- "vercelGatewayRouting.only[]",
59
- "vercelGatewayRouting.order[]",
60
- "zaiToolStream",
61
- "supportsStrictMode",
62
- "toolCallFormat",
63
- "sendSessionAffinityHeaders",
64
- "deferredToolsMode",
65
- "sessionAffinityFormat",
66
- "supportsLongCacheRetention",
67
- "supportsForcedToolChoice",
68
- "webSearchToolType",
69
- "supportsWebSocket",
70
- "supportsRemoteCompactionV2",
71
- "supportsWebSearchPreview",
72
- "supportsToolSearch",
73
- "supportsEagerToolInputStreaming",
74
- "supportsCacheControlOnTools",
75
- "supportsTemperature",
76
- "supportsToolChoice",
77
- "forceAdaptiveThinking",
78
- "allowEmptySignature",
79
- "supportsToolReferences",
80
- "allowedFallbackModels[].provider",
81
- "allowedFallbackModels[].model",
82
- ...COST.map((field) => `allowedFallbackModels[].cost.${field}`),
22
+ function compatFields() {
23
+ return [
24
+ "supportsWebSearch",
25
+ "supportsStore",
26
+ "supportsDeveloperRole",
27
+ "supportsReasoningEffort",
28
+ "supportsUsageInStreaming",
29
+ "supportsFinishReason",
30
+ "maxTokensField",
31
+ "requiresToolResultName",
32
+ "requiresAssistantAfterToolResult",
33
+ "requiresThinkingAsText",
34
+ "requiresReasoningContentOnAssistantMessages",
35
+ "supportsDisabledThinking",
36
+ "thinkingFormat",
37
+ "chatTemplateKwargs.{key}",
38
+ "chatTemplateKwargs.*",
39
+ "chatTemplateKwargs.*.$var",
40
+ "chatTemplateKwargs.*.omitWhenOff",
41
+ "cacheControlFormat",
42
+ ...[
43
+ "allow_fallbacks",
44
+ "require_parameters",
45
+ "data_collection",
46
+ "zdr",
47
+ "enforce_distillable_text",
48
+ "order[]",
49
+ "only[]",
50
+ "ignore[]",
51
+ "quantizations[]",
52
+ "sort",
53
+ "sort.by",
54
+ "sort.partition",
55
+ ...["prompt", "completion", "image", "audio", "request"].map((part) => `max_price.${part}`),
56
+ ...PERCENTILES.map((suffix) => `preferred_min_throughput${suffix}`),
57
+ ...PERCENTILES.map((suffix) => `preferred_max_latency${suffix}`),
58
+ ].map((field) => `openRouterRouting.${field}`),
59
+ "vercelGatewayRouting.only[]",
60
+ "vercelGatewayRouting.order[]",
61
+ "zaiToolStream",
62
+ "supportsStrictMode",
63
+ "toolCallFormat",
64
+ "sendSessionAffinityHeaders",
65
+ "deferredToolsMode",
66
+ "sessionAffinityFormat",
67
+ "supportsLongCacheRetention",
68
+ "supportsForcedToolChoice",
69
+ "webSearchToolType",
70
+ "supportsWebSocket",
71
+ "supportsRemoteCompactionV2",
72
+ "supportsWebSearchPreview",
73
+ "supportsToolSearch",
74
+ "supportsEagerToolInputStreaming",
75
+ "supportsCacheControlOnTools",
76
+ "supportsTemperature",
77
+ "supportsToolChoice",
78
+ "forceAdaptiveThinking",
79
+ "allowEmptySignature",
80
+ "supportsToolReferences",
81
+ "allowedFallbackModels[].provider",
82
+ "allowedFallbackModels[].model",
83
+ ...COST.map((field) => `allowedFallbackModels[].cost.${field}`),
84
+ ];
85
+ }
86
+ const THINKING_LEVEL_MAP = [
87
+ "thinkingLevelMap.off",
88
+ "thinkingLevelMap.minimal",
89
+ "thinkingLevelMap.low",
90
+ "thinkingLevelMap.medium",
91
+ "thinkingLevelMap.high",
92
+ "thinkingLevelMap.xhigh",
93
+ "thinkingLevelMap.max",
83
94
  ];
84
- const THINKING_LEVEL_MAP = ["off", "minimal", "low", "medium", "high", "xhigh", "max"].map((level) => `thinkingLevelMap.${level}`);
85
95
  const INPUT_LIMITS = [
86
- "maxRequestBytes",
87
- "images.resize.maxWidth",
88
- "images.resize.maxHeight",
89
- "images.resize.maxBytes",
90
- "images.resize.jpegQuality",
91
- "images.maxPerMessage",
92
- "images.maxPerRequest",
93
- ].map((field) => `inputLimits.${field}`);
94
- const MODEL_DEFINITION = {
95
- ...fields("public", [
96
- "id",
97
- "name",
98
- "upstreamModelId",
99
- "serviceTier",
100
- "promptPreset",
101
- "recoverTextToolCalls",
102
- "api",
103
- "reasoning",
104
- ...THINKING_LEVEL_MAP,
105
- "defaultThinkingLevel",
106
- "input[]",
107
- ...INPUT_LIMITS,
108
- ...COST.map((field) => `cost.${field}`),
109
- "promptCache.short",
110
- "promptCache.long",
111
- "contextWindow",
112
- "maxTokens",
113
- "cacheRetention",
114
- ...COMPAT.map((field) => `compat.${field}`),
115
- "headers.{key}",
116
- "extraBody.{key}",
117
- ]),
118
- baseUrl: "endpoint",
119
- "headers.*": "sealed",
120
- "extraBody.*": "forces-device",
121
- };
122
- const MODEL_OVERRIDE = {
123
- ...fields("public", [
124
- "name",
125
- "promptPreset",
126
- "recoverTextToolCalls",
127
- "reasoning",
128
- ...THINKING_LEVEL_MAP,
129
- "thinkingLevelMapMode",
130
- "input[]",
131
- ...INPUT_LIMITS,
132
- ...COMPAT.map((field) => `compat.${field}`),
133
- ...COST.map((field) => `cost.${field}`),
134
- "promptCache.short",
135
- "promptCache.long",
136
- "contextWindow",
137
- "maxTokens",
138
- "cacheRetention",
139
- "headers.{key}",
140
- "extraBody.{key}",
141
- ]),
142
- "headers.*": "sealed",
143
- "extraBody.*": "forces-device",
144
- };
145
- const PROVIDER = {
146
- ...fields("public", [
147
- "name",
148
- "disabled",
149
- "api",
150
- "headers.{key}",
151
- "extraBody.{key}",
152
- "cacheRetention",
153
- "authHeader",
154
- "whitelist[]",
155
- "blacklist[]",
156
- "hideFreeModels",
157
- ...COMPAT.map((field) => `compat.${field}`),
158
- "credentials.rotation",
159
- "credentials.affinity",
160
- "credentials.accountStrategy",
161
- "credentials.cooldownBaseMs",
162
- "credentials.cooldownCapMs",
163
- "credentials.slots.{key}",
164
- "modelOverrides.{key}",
165
- ]),
166
- baseUrl: "endpoint",
167
- apiKey: "sealed",
168
- "headers.*": "sealed",
169
- "extraBody.*": "forces-device",
170
- "credentials.slots.*.value": "sealed",
171
- "credentials.slots.*.env": "device-only",
172
- ...prefixed("models[].", MODEL_DEFINITION),
173
- ...prefixed("modelOverrides.*.", MODEL_OVERRIDE),
174
- };
96
+ "inputLimits.maxRequestBytes",
97
+ "inputLimits.images.resize.maxWidth",
98
+ "inputLimits.images.resize.maxHeight",
99
+ "inputLimits.images.resize.maxBytes",
100
+ "inputLimits.images.resize.jpegQuality",
101
+ "inputLimits.images.maxPerMessage",
102
+ "inputLimits.images.maxPerRequest",
103
+ ];
104
+ function modelDefinition() {
105
+ return {
106
+ ...fields("public", [
107
+ "id",
108
+ "name",
109
+ "upstreamModelId",
110
+ "serviceTier",
111
+ "promptPreset",
112
+ "recoverTextToolCalls",
113
+ "api",
114
+ "reasoning",
115
+ ...THINKING_LEVEL_MAP,
116
+ "defaultThinkingLevel",
117
+ "input[]",
118
+ ...INPUT_LIMITS,
119
+ ...COST.map((field) => `cost.${field}`),
120
+ "promptCache.short",
121
+ "promptCache.long",
122
+ "contextWindow",
123
+ "maxTokens",
124
+ "cacheRetention",
125
+ ...compatFields().map((field) => `compat.${field}`),
126
+ "headers.{key}",
127
+ "extraBody.{key}",
128
+ ]),
129
+ baseUrl: "endpoint",
130
+ "headers.*": "sealed",
131
+ "extraBody.*": "forces-device",
132
+ };
133
+ }
134
+ function modelOverride() {
135
+ return {
136
+ ...fields("public", [
137
+ "name",
138
+ "promptPreset",
139
+ "recoverTextToolCalls",
140
+ "reasoning",
141
+ ...THINKING_LEVEL_MAP,
142
+ "thinkingLevelMapMode",
143
+ "input[]",
144
+ ...INPUT_LIMITS,
145
+ ...compatFields().map((field) => `compat.${field}`),
146
+ ...COST.map((field) => `cost.${field}`),
147
+ "promptCache.short",
148
+ "promptCache.long",
149
+ "contextWindow",
150
+ "maxTokens",
151
+ "cacheRetention",
152
+ "headers.{key}",
153
+ "extraBody.{key}",
154
+ ]),
155
+ "headers.*": "sealed",
156
+ "extraBody.*": "forces-device",
157
+ };
158
+ }
159
+ function providerFields() {
160
+ return {
161
+ ...fields("public", [
162
+ "name",
163
+ "disabled",
164
+ "api",
165
+ "headers.{key}",
166
+ "extraBody.{key}",
167
+ "cacheRetention",
168
+ "authHeader",
169
+ "whitelist[]",
170
+ "blacklist[]",
171
+ "hideFreeModels",
172
+ ...compatFields().map((field) => `compat.${field}`),
173
+ "credentials.rotation",
174
+ "credentials.affinity",
175
+ "credentials.accountStrategy",
176
+ "credentials.cooldownBaseMs",
177
+ "credentials.cooldownCapMs",
178
+ "credentials.slots.{key}",
179
+ "modelOverrides.{key}",
180
+ ]),
181
+ baseUrl: "endpoint",
182
+ apiKey: "sealed",
183
+ "headers.*": "sealed",
184
+ "extraBody.*": "forces-device",
185
+ "credentials.slots.*.value": "sealed",
186
+ "credentials.slots.*.env": "device-only",
187
+ ...prefixed("models[].", modelDefinition()),
188
+ ...prefixed("modelOverrides.*.", modelOverride()),
189
+ };
190
+ }
175
191
  /** models.json, keyed by schema path: `.` nests, `[]` is an array item, `.{key}` a record key, `.*` a record value. */
176
- export const MODELS_JSON_FIELD_CLASSES = {
177
- "disabledProviders[]": "public",
178
- "providers.{key}": "public",
179
- ...prefixed("providers.*.", PROVIDER),
180
- };
192
+ export function modelsJsonFieldClasses() {
193
+ return {
194
+ "disabledProviders[]": "public",
195
+ "providers.{key}": "public",
196
+ ...prefixed("providers.*.", providerFields()),
197
+ };
198
+ }
181
199
  /** Fields the runtime types add on top of the models.json schema (`model-config.ts`). */
182
200
  export const MODELS_JSON_TYPE_ONLY_FIELDS = {
183
201
  "providers.*.models[].samplingParams.{key}": "public",
@@ -185,8 +203,9 @@ export const MODELS_JSON_TYPE_ONLY_FIELDS = {
185
203
  "providers.*.modelOverrides.*.samplingParams.{key}": "public",
186
204
  "providers.*.modelOverrides.*.samplingParams.*": "forces-device",
187
205
  };
188
- export function classifyModelsJsonField(path) {
189
- return MODELS_JSON_FIELD_CLASSES[path] ?? MODELS_JSON_TYPE_ONLY_FIELDS[path];
206
+ export function classifyModelsJsonField(path, classes = modelsJsonFieldClasses()) {
207
+ const typeOnly = MODELS_JSON_TYPE_ONLY_FIELDS;
208
+ return classes[path] ?? typeOnly[path];
190
209
  }
191
210
  function fields(fieldClass, paths) {
192
211
  return Object.fromEntries(paths.map((path) => [path, fieldClass]));
@@ -0,0 +1,7 @@
1
+ import type { Redactor } from "./redact.ts";
2
+ type ConsoleTarget = object;
3
+ export declare function sealedWindowCount(): number;
4
+ export declare function runSealed<T>(redactor: Redactor, work: () => Promise<T>): Promise<T>;
5
+ export declare function installConsoleRedactor(target: ConsoleTarget): void;
6
+ export {};
7
+ //# sourceMappingURL=console-redactor.d.ts.map
@@ -0,0 +1,74 @@
1
+ import { AsyncLocalStorage } from "node:async_hooks";
2
+ import { format } from "node:util";
3
+ const INSTALLED = Symbol.for("senpi.egress.consoleRedactorInstalled");
4
+ // no-module-state: console-redactor-install
5
+ const sealedContext = new AsyncLocalStorage();
6
+ // no-module-state: sealed-window-counter
7
+ let sealedWindows = 0;
8
+ export function sealedWindowCount() {
9
+ return sealedWindows;
10
+ }
11
+ export async function runSealed(redactor, work) {
12
+ sealedWindows++;
13
+ try {
14
+ return await sealedContext.run(redactor, work);
15
+ }
16
+ finally {
17
+ sealedWindows--;
18
+ }
19
+ }
20
+ /** Console members that never write a line; wrapping them would only break them (Node and Bun consoles). */
21
+ const NON_WRITING_MEMBERS = [
22
+ "context",
23
+ "createTask",
24
+ "profile",
25
+ "profileEnd",
26
+ "timeStamp",
27
+ "takeHeapSnapshot",
28
+ "record",
29
+ "recordEnd",
30
+ "screenshot",
31
+ ];
32
+ /**
33
+ * The console members to wrap: every function-valued member except constructors (`Console`), `_`-prefixed
34
+ * stream internals (`_stdoutErrorHandler`) and the known non-writing members. Anything unknown is wrapped, so a
35
+ * writing method no list names (Bun's `console.write`) cannot bypass the redactor.
36
+ */
37
+ function consoleMethods(target) {
38
+ const names = new Set();
39
+ for (let level = target; level !== null && level !== Object.prototype; level = Object.getPrototypeOf(level)) {
40
+ for (const name of Object.getOwnPropertyNames(level)) {
41
+ if (name === "constructor" || name.startsWith("_") || /^[A-Z]/u.test(name))
42
+ continue;
43
+ if (NON_WRITING_MEMBERS.some((member) => member === name))
44
+ continue;
45
+ if (typeof target[name] === "function")
46
+ names.add(name);
47
+ }
48
+ }
49
+ return [...names];
50
+ }
51
+ export function installConsoleRedactor(target) {
52
+ const console = target;
53
+ if (console[INSTALLED])
54
+ return;
55
+ console[INSTALLED] = true;
56
+ for (const method of consoleMethods(target)) {
57
+ const original = console[method];
58
+ const keepsCondition = method === "assert";
59
+ console[method] = (...args) => {
60
+ const redactor = sealedContext.getStore();
61
+ if (redactor === undefined) {
62
+ if (sealedWindows > 0)
63
+ return;
64
+ return original.apply(target, args);
65
+ }
66
+ if (keepsCondition) {
67
+ const [condition, ...message] = args;
68
+ return original.call(target, condition, redactor.redact(format(...message)));
69
+ }
70
+ return original.call(target, redactor.redact(format(...args)));
71
+ };
72
+ }
73
+ }
74
+ //# sourceMappingURL=console-redactor.js.map
@@ -0,0 +1,73 @@
1
+ import type { HpkeWrap, ProviderId, SecretField } from "./sealed-format.ts";
2
+ export type { SecretField } from "./sealed-format.ts";
3
+ export interface SecretLease extends Disposable {
4
+ /** Throws SecretNotLeased for a field outside this lease, or after dispose. */
5
+ value(field: SecretField): string;
6
+ readonly expiresAt?: number;
7
+ /** Drops references and zeroes the byte buffers (custody design 3.4). */
8
+ [Symbol.dispose](): void;
9
+ }
10
+ /** Audit only; asserted by the brain. */
11
+ export interface ReleaseScope {
12
+ readonly sessionId: string;
13
+ readonly callId: string;
14
+ readonly purpose: "model-call" | "aux-call";
15
+ }
16
+ export interface CredentialSource {
17
+ resolve(providerId: ProviderId, entryDigest: string, fields: readonly SecretField[], scope: ReleaseScope): Promise<SecretLease>;
18
+ }
19
+ /** Registry-signed `{account, placementId, epoch, recipientPkHash, exp <= 60 s}`. */
20
+ export interface EpochLeaseToken {
21
+ readonly token: string;
22
+ }
23
+ export interface ReleaseRequest {
24
+ readonly accountId: string;
25
+ readonly providerId: ProviderId;
26
+ /** Must equal the head entry's. */
27
+ readonly entryDigest: string;
28
+ readonly fields: readonly SecretField[];
29
+ readonly lease: EpochLeaseToken;
30
+ readonly recipientPk: Uint8Array;
31
+ readonly scope: ReleaseScope;
32
+ }
33
+ export interface ReleaseConfigRequest {
34
+ readonly accountId: string;
35
+ readonly objectHash: string;
36
+ readonly lease: EpochLeaseToken;
37
+ readonly recipientPk: Uint8Array;
38
+ readonly scope: ReleaseScope;
39
+ }
40
+ export type ReleaseFailure = "stale_snapshot" | "epoch_mismatch" | "owner_revoked" | "owner_withdrawn" | "unknown_field" | "unresolved_field" | "key_destroyed" | "wrap_missing" | "unavailable" | "denied";
41
+ export interface HeadRef {
42
+ readonly version: number;
43
+ readonly objectHash: string;
44
+ }
45
+ export type ReleaseResult = {
46
+ readonly ok: true;
47
+ readonly items: readonly HpkeWrap[];
48
+ readonly head: HeadRef;
49
+ } | {
50
+ readonly ok: false;
51
+ readonly code: ReleaseFailure;
52
+ readonly head?: HeadRef;
53
+ };
54
+ export interface Unsealer {
55
+ release(req: ReleaseRequest): Promise<ReleaseResult>;
56
+ /** One item: the ProviderConfigPlain sealed to recipientPk. K_cfg and K_proj are never released. */
57
+ releaseConfig(req: ReleaseConfigRequest): Promise<ReleaseResult>;
58
+ }
59
+ export declare class SecretNotLeased extends Error {
60
+ readonly field: SecretField;
61
+ constructor(field: SecretField);
62
+ }
63
+ export type CredentialWaitReason = ReleaseFailure | "config-churn";
64
+ /**
65
+ * A sealed release that did not produce a value. It is never a plaintext fallback: the caller routes the step to a
66
+ * device or waits. `retryable` marks the refusals that clear on their own (custody design 3.3 and the L844 rule).
67
+ */
68
+ export declare class CredentialWait extends Error {
69
+ readonly reason: CredentialWaitReason;
70
+ readonly retryable: boolean;
71
+ constructor(reason: CredentialWaitReason, retryable: boolean);
72
+ }
73
+ //# sourceMappingURL=credentials.d.ts.map
@@ -0,0 +1,20 @@
1
+ export class SecretNotLeased extends Error {
2
+ constructor(field) {
3
+ super(`secret not leased: ${field}`);
4
+ this.name = "SecretNotLeased";
5
+ this.field = field;
6
+ }
7
+ }
8
+ /**
9
+ * A sealed release that did not produce a value. It is never a plaintext fallback: the caller routes the step to a
10
+ * device or waits. `retryable` marks the refusals that clear on their own (custody design 3.3 and the L844 rule).
11
+ */
12
+ export class CredentialWait extends Error {
13
+ constructor(reason, retryable) {
14
+ super(`sealed credential unavailable: ${reason}`);
15
+ this.name = "CredentialWait";
16
+ this.reason = reason;
17
+ this.retryable = retryable;
18
+ }
19
+ }
20
+ //# sourceMappingURL=credentials.js.map
@@ -2,7 +2,7 @@
2
2
  * Why a provider must stay on the device whatever the sidecar asks for (custody gate H1, M1).
3
3
  *
4
4
  * The models.json schema accepts unknown keys at every level, so the walk here applies
5
- * `MODELS_JSON_FIELD_CLASSES` to the concrete provider object with whitelist semantics: a key the
5
+ * `modelsJsonFieldClasses()` to the concrete provider object with whitelist semantics: a key the
6
6
  * table does not name, at any depth, keeps the provider on the device instead of reaching the brain.
7
7
  */
8
8
  import type { ModelsJsonProvider } from "../model-config-schema.ts";
@@ -2,24 +2,26 @@
2
2
  * Why a provider must stay on the device whatever the sidecar asks for (custody gate H1, M1).
3
3
  *
4
4
  * The models.json schema accepts unknown keys at every level, so the walk here applies
5
- * `MODELS_JSON_FIELD_CLASSES` to the concrete provider object with whitelist semantics: a key the
5
+ * `modelsJsonFieldClasses()` to the concrete provider object with whitelist semantics: a key the
6
6
  * table does not name, at any depth, keeps the provider on the device instead of reaching the brain.
7
7
  */
8
- import { classifyModelsJsonField, MODELS_JSON_FIELD_CLASSES, MODELS_JSON_TYPE_ONLY_FIELDS, } from "./classify.js";
8
+ import { classifyModelsJsonField, modelsJsonFieldClasses, MODELS_JSON_TYPE_ONLY_FIELDS, } from "./classify.js";
9
9
  import { splitEndpoint } from "./endpoint.js";
10
- const STOPS_WALK = new Set(["sealed", "device-only"]);
10
+ const STOPS_WALK = ["sealed", "device-only"];
11
11
  /** Every path in the tables plus each of its ancestors (`a.b[].c` -> `a`, `a.b`, `a.b[]`, `a.b[].c`). */
12
- const KNOWN_PATHS = new Set([...Object.keys(MODELS_JSON_FIELD_CLASSES), ...Object.keys(MODELS_JSON_TYPE_ONLY_FIELDS)].flatMap((path) => [
13
- ...[...path.matchAll(/[.[]/g)].map((boundary) => path.slice(0, boundary.index)),
14
- path,
15
- ]));
12
+ function knownPaths(classes) {
13
+ return new Set([...Object.keys(classes), ...Object.keys(MODELS_JSON_TYPE_ONLY_FIELDS)].flatMap((path) => [
14
+ ...[...path.matchAll(/[.[]/g)].map((boundary) => path.slice(0, boundary.index)),
15
+ path,
16
+ ]));
17
+ }
16
18
  /** A property name that would read as path syntax (`a.b`, `m[]`, `*`, `{key}`) is never a declared field. */
17
19
  const PATH_SYNTAX = /[.[\]*{}]/;
18
20
  function isPlainObject(value) {
19
21
  return typeof value === "object" && value !== null && !Array.isArray(value);
20
22
  }
21
- function walk(value, path, reasons) {
22
- const fieldClass = classifyModelsJsonField(path);
23
+ function walk(value, path, reasons, known, classes) {
24
+ const fieldClass = classifyModelsJsonField(path, classes);
23
25
  if (fieldClass === "forces-device") {
24
26
  const sampling = path.endsWith("samplingParams.*");
25
27
  if (!sampling)
@@ -28,11 +30,11 @@ function walk(value, path, reasons) {
28
30
  reasons.add("sampling-params");
29
31
  return;
30
32
  }
31
- if (fieldClass !== undefined && STOPS_WALK.has(fieldClass))
33
+ if (fieldClass !== undefined && STOPS_WALK.some((stop) => stop === fieldClass))
32
34
  return;
33
35
  if (Array.isArray(value)) {
34
36
  for (const item of value)
35
- walk(item, `${path}[]`, reasons);
37
+ walk(item, `${path}[]`, reasons, known, classes);
36
38
  return;
37
39
  }
38
40
  if (!isPlainObject(value)) {
@@ -40,11 +42,11 @@ function walk(value, path, reasons) {
40
42
  reasons.add("undeclared-field");
41
43
  return;
42
44
  }
43
- const isRecord = classifyModelsJsonField(`${path}.{key}`) !== undefined;
45
+ const isRecord = classifyModelsJsonField(`${path}.{key}`, classes) !== undefined;
44
46
  for (const [key, child] of Object.entries(value)) {
45
47
  const childPath = isRecord ? `${path}.*` : `${path}.${key}`;
46
- if ((isRecord || !PATH_SYNTAX.test(key)) && KNOWN_PATHS.has(childPath))
47
- walk(child, childPath, reasons);
48
+ if ((isRecord || !PATH_SYNTAX.test(key)) && known.has(childPath))
49
+ walk(child, childPath, reasons, known, classes);
48
50
  else
49
51
  reasons.add("undeclared-field");
50
52
  }
@@ -63,7 +65,8 @@ function hasModelHeaderCollision(provider) {
63
65
  /** Empty when sealed custody may serve the provider. */
64
66
  export function deviceCustodyReasons(provider) {
65
67
  const reasons = new Set();
66
- walk(provider, "providers.*", reasons);
68
+ const classes = modelsJsonFieldClasses();
69
+ walk(provider, "providers.*", reasons, knownPaths(classes), classes);
67
70
  if (provider.baseUrl !== undefined && !splitEndpoint(provider.baseUrl).ok)
68
71
  reasons.add("unsplittable-base-url");
69
72
  for (const model of provider.models ?? []) {