dsh-agy-cli 0.0.0-stage → 0.12.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.
- package/LICENSE +30 -0
- package/README.en.md +27 -0
- package/README.md +94 -2
- package/agents/dsh-agy-image-view.md +17 -0
- package/agents/dsh-agy-mcp-executor.md +32 -0
- package/agents/dsh-agy-read-only.md +20 -0
- package/agents/dsh-agy-tool-free.md +16 -0
- package/agents/dsh-agy-workspace-write.md +23 -0
- package/bin/dsh-agy-cli.js +9 -0
- package/bin/win32-x64/agy-launcher.exe +0 -0
- package/cordis.patch.yml +16 -0
- package/dist/client.js +4259 -0
- package/lib/agent-installer.d.ts +66 -0
- package/lib/agent-installer.js +147 -0
- package/lib/agent-presets.d.ts +42 -0
- package/lib/agent-presets.js +130 -0
- package/lib/agents-cli.d.ts +3 -0
- package/lib/agents-cli.js +110 -0
- package/lib/agy/diagnostics.d.ts +29 -0
- package/lib/agy/diagnostics.js +109 -0
- package/lib/agy/errors.d.ts +20 -0
- package/lib/agy/errors.js +77 -0
- package/lib/agy/experimental-transport.d.ts +79 -0
- package/lib/agy/experimental-transport.js +612 -0
- package/lib/agy/keepalive.d.ts +140 -0
- package/lib/agy/keepalive.js +424 -0
- package/lib/agy/limiter.d.ts +32 -0
- package/lib/agy/limiter.js +111 -0
- package/lib/agy/log.d.ts +214 -0
- package/lib/agy/log.js +503 -0
- package/lib/agy/models.d.ts +49 -0
- package/lib/agy/models.js +203 -0
- package/lib/agy/overrides.d.ts +17 -0
- package/lib/agy/overrides.js +73 -0
- package/lib/agy/parser.d.ts +102 -0
- package/lib/agy/parser.js +492 -0
- package/lib/agy/persistent-transport.d.ts +101 -0
- package/lib/agy/persistent-transport.js +753 -0
- package/lib/agy/process.d.ts +144 -0
- package/lib/agy/process.js +452 -0
- package/lib/agy/redact.d.ts +3 -0
- package/lib/agy/redact.js +24 -0
- package/lib/agy/stream-protocol.d.ts +50 -0
- package/lib/agy/stream-protocol.js +156 -0
- package/lib/agy/windows-launcher.d.ts +21 -0
- package/lib/agy/windows-launcher.js +76 -0
- package/lib/diagnostics.d.ts +78 -0
- package/lib/diagnostics.js +162 -0
- package/lib/doctor.d.ts +138 -0
- package/lib/doctor.js +923 -0
- package/lib/dsh/context.d.ts +133 -0
- package/lib/dsh/context.js +352 -0
- package/lib/dsh-home.d.ts +6 -0
- package/lib/dsh-home.js +11 -0
- package/lib/index.d.ts +43 -0
- package/lib/index.js +256 -0
- package/lib/mcp/agy-registration.d.ts +70 -0
- package/lib/mcp/agy-registration.js +245 -0
- package/lib/mcp/bridge.d.ts +73 -0
- package/lib/mcp/bridge.js +295 -0
- package/lib/mcp/endpoint.d.ts +41 -0
- package/lib/mcp/endpoint.js +142 -0
- package/lib/mcp/protocol.d.ts +107 -0
- package/lib/mcp/protocol.js +197 -0
- package/lib/mcp/tool-projection.d.ts +64 -0
- package/lib/mcp/tool-projection.js +168 -0
- package/lib/provider/agy.d.ts +153 -0
- package/lib/provider/agy.js +2159 -0
- package/lib/provider/config.d.ts +226 -0
- package/lib/provider/config.js +294 -0
- package/lib/provider/error-codes.d.ts +22 -0
- package/lib/provider/error-codes.js +29 -0
- package/lib/provider/image-bridge.d.ts +60 -0
- package/lib/provider/image-bridge.js +290 -0
- package/lib/provider/mock.d.ts +15 -0
- package/lib/provider/mock.js +78 -0
- package/lib/provider/prompt-budget.d.ts +45 -0
- package/lib/provider/prompt-budget.js +596 -0
- package/lib/provider/serialize.d.ts +15 -0
- package/lib/provider/serialize.js +82 -0
- package/lib/provider/tool-protocol.d.ts +80 -0
- package/lib/provider/tool-protocol.js +848 -0
- package/lib/provider/tool-schema-file.d.ts +11 -0
- package/lib/provider/tool-schema-file.js +27 -0
- package/lib/session/file-store.d.ts +56 -0
- package/lib/session/file-store.js +186 -0
- package/lib/session/store.d.ts +49 -0
- package/lib/session/store.js +62 -0
- package/lib/web/router.d.ts +19 -0
- package/lib/web/router.js +906 -0
- package/package.json +112 -4
package/lib/agy/log.js
ADDED
|
@@ -0,0 +1,503 @@
|
|
|
1
|
+
import { createHash } from 'node:crypto';
|
|
2
|
+
import { appendFileSync } from 'node:fs';
|
|
3
|
+
import { tmpdir } from 'node:os';
|
|
4
|
+
import { join } from 'node:path';
|
|
5
|
+
import { isAgyReasoningEffort, } from './process.js';
|
|
6
|
+
import { redactText } from './redact.js';
|
|
7
|
+
import { isAgyToolEventKind, } from './parser.js';
|
|
8
|
+
import { isModelDiscoveryErrorCode, } from '../provider/error-codes.js';
|
|
9
|
+
const PROTOCOL_ARGUMENT_ISSUES = [
|
|
10
|
+
'missing-required',
|
|
11
|
+
'unexpected-property',
|
|
12
|
+
'type',
|
|
13
|
+
'enum',
|
|
14
|
+
'constraint',
|
|
15
|
+
'combinator',
|
|
16
|
+
'unknown',
|
|
17
|
+
];
|
|
18
|
+
const PROTOCOL_COMPATIBILITIES = [
|
|
19
|
+
'pwsh-description-default',
|
|
20
|
+
'json-control-character-escape',
|
|
21
|
+
'agy-call-envelope',
|
|
22
|
+
'agy-command-envelope',
|
|
23
|
+
'agy-thought-call-envelope',
|
|
24
|
+
'agy-bare-call-envelope',
|
|
25
|
+
];
|
|
26
|
+
function isAgyProtocolArgumentIssue(value) {
|
|
27
|
+
return typeof value === 'string'
|
|
28
|
+
&& PROTOCOL_ARGUMENT_ISSUES.includes(value);
|
|
29
|
+
}
|
|
30
|
+
function isToolProtocolCompatibility(value) {
|
|
31
|
+
return typeof value === 'string'
|
|
32
|
+
&& PROTOCOL_COMPATIBILITIES.includes(value);
|
|
33
|
+
}
|
|
34
|
+
function safeProtocolKeyList(value) {
|
|
35
|
+
if (!Array.isArray(value))
|
|
36
|
+
return undefined;
|
|
37
|
+
return value.slice(0, 16).map(key => typeof key === 'string' && /^[A-Za-z0-9_.:-]{1,128}$/.test(key)
|
|
38
|
+
? key
|
|
39
|
+
: '[redacted]');
|
|
40
|
+
}
|
|
41
|
+
const BRIDGE_OUTCOMES = [
|
|
42
|
+
'text-only',
|
|
43
|
+
'mcp-tools',
|
|
44
|
+
'dsh-pending',
|
|
45
|
+
'dsh-message',
|
|
46
|
+
'dsh-tool-call',
|
|
47
|
+
'agy-owned',
|
|
48
|
+
'context-rejected',
|
|
49
|
+
'schema-rejected',
|
|
50
|
+
'protocol-rejected',
|
|
51
|
+
'agy-internal-tool',
|
|
52
|
+
'permission-required',
|
|
53
|
+
'failed',
|
|
54
|
+
];
|
|
55
|
+
function isAgyBridgeOutcome(value) {
|
|
56
|
+
return typeof value === 'string' && BRIDGE_OUTCOMES.includes(value);
|
|
57
|
+
}
|
|
58
|
+
function isAgyPermissionPreset(value) {
|
|
59
|
+
return value === 'read-only' || value === 'workspace-write' || value === 'danger-full-access';
|
|
60
|
+
}
|
|
61
|
+
function isAgyApprovalPolicy(value) {
|
|
62
|
+
return value === 'ask' || value === 'never';
|
|
63
|
+
}
|
|
64
|
+
const MODEL_DISCOVERY_LOG_SOURCES = [
|
|
65
|
+
'static',
|
|
66
|
+
'discovered',
|
|
67
|
+
'merged',
|
|
68
|
+
'cache',
|
|
69
|
+
'fallback',
|
|
70
|
+
];
|
|
71
|
+
function isModelDiscoveryLogSource(value) {
|
|
72
|
+
return typeof value === 'string'
|
|
73
|
+
&& MODEL_DISCOVERY_LOG_SOURCES.includes(value);
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Temporary test-observability sink. The caller must pass an already
|
|
77
|
+
* sanitized record; this file is intentionally outside the DSH session store
|
|
78
|
+
* because that store currently drops Error.cause and nested diagnostics.
|
|
79
|
+
*/
|
|
80
|
+
export const AGY_DIAGNOSTIC_LOG_PATH = join(tmpdir(), 'dsh-agy-cli-diagnostic.jsonl');
|
|
81
|
+
export function appendAgyDiagnosticRecord(record) {
|
|
82
|
+
try {
|
|
83
|
+
appendFileSync(AGY_DIAGNOSTIC_LOG_PATH, `${JSON.stringify(record)}\n`, 'utf8');
|
|
84
|
+
}
|
|
85
|
+
catch {
|
|
86
|
+
// Diagnostic persistence must never affect a model request.
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
function fingerprint(value) {
|
|
90
|
+
return createHash('sha256').update(value, 'utf8').digest('hex').slice(0, 16);
|
|
91
|
+
}
|
|
92
|
+
function baseRecord(telemetry) {
|
|
93
|
+
return {
|
|
94
|
+
event: 'agy.request.started',
|
|
95
|
+
requestId: telemetry.requestId,
|
|
96
|
+
provider: telemetry.provider,
|
|
97
|
+
model: telemetry.model,
|
|
98
|
+
agent: telemetry.agent,
|
|
99
|
+
toolPolicy: telemetry.toolPolicy,
|
|
100
|
+
toolSchemaCount: telemetry.toolSchemaCount,
|
|
101
|
+
toolCallCount: telemetry.toolCallCount,
|
|
102
|
+
bridgeOutcome: telemetry.bridgeOutcome,
|
|
103
|
+
...(telemetry.permissionPreset === undefined ? {} : { permissionPreset: telemetry.permissionPreset }),
|
|
104
|
+
...(telemetry.sandboxMode === undefined ? {} : { sandboxMode: telemetry.sandboxMode }),
|
|
105
|
+
...(telemetry.approvalPolicy === undefined ? {} : { approvalPolicy: telemetry.approvalPolicy }),
|
|
106
|
+
...(telemetry.reasoningEffort === undefined ? {} : { reasoningEffort: telemetry.reasoningEffort }),
|
|
107
|
+
...(telemetry.purpose === undefined ? {} : { purpose: telemetry.purpose }),
|
|
108
|
+
...(telemetry.modelDiscoverySource === undefined ? {} : { modelDiscoverySource: telemetry.modelDiscoverySource }),
|
|
109
|
+
...(telemetry.modelDiscoveryWarningCode === undefined ? {} : { modelDiscoveryWarningCode: telemetry.modelDiscoveryWarningCode }),
|
|
110
|
+
attempt: telemetry.attempt,
|
|
111
|
+
processAttemptCount: telemetry.processAttemptCount,
|
|
112
|
+
retryMaxRetries: telemetry.retryMaxRetries,
|
|
113
|
+
...(telemetry.inputFrameBytes === undefined ? {} : { inputFrameBytes: telemetry.inputFrameBytes }),
|
|
114
|
+
...(telemetry.inputFrameLimitBytes === undefined ? {} : { inputFrameLimitBytes: telemetry.inputFrameLimitBytes }),
|
|
115
|
+
...(telemetry.promptBytes === undefined ? {} : { promptBytes: telemetry.promptBytes }),
|
|
116
|
+
...(telemetry.promptLimitBytes === undefined ? {} : { promptLimitBytes: telemetry.promptLimitBytes }),
|
|
117
|
+
...(telemetry.toolResultCount === undefined ? {} : { toolResultCount: telemetry.toolResultCount }),
|
|
118
|
+
...(telemetry.largestToolResultBytes === undefined ? {} : { largestToolResultBytes: telemetry.largestToolResultBytes }),
|
|
119
|
+
...(telemetry.truncatedToolResultCount === undefined ? {} : { truncatedToolResultCount: telemetry.truncatedToolResultCount }),
|
|
120
|
+
...(telemetry.historyCompacted ? { historyCompacted: true } : {}),
|
|
121
|
+
...(telemetry.omittedMessageCount === 0 ? {} : { omittedMessageCount: telemetry.omittedMessageCount }),
|
|
122
|
+
...(telemetry.toolEventKind === undefined ? {} : { toolEventKind: telemetry.toolEventKind }),
|
|
123
|
+
...(telemetry.toolEventStreamIndex === undefined ? {} : { toolEventStreamIndex: telemetry.toolEventStreamIndex }),
|
|
124
|
+
...(telemetry.toolEventDiagnostic === undefined ? {} : { toolEventDiagnostic: telemetry.toolEventDiagnostic }),
|
|
125
|
+
...(telemetry.carrierAbortExpected ? { carrierAbortExpected: true } : {}),
|
|
126
|
+
...(telemetry.carrierValidation === undefined ? {} : { carrierValidation: telemetry.carrierValidation }),
|
|
127
|
+
protocolRepairAttempts: telemetry.protocolRepairAttempts,
|
|
128
|
+
...(telemetry.protocolRepairReason === undefined ? {} : { protocolRepairReason: telemetry.protocolRepairReason }),
|
|
129
|
+
...(telemetry.protocolFailureDetail === undefined ? {} : { protocolFailureDetail: telemetry.protocolFailureDetail }),
|
|
130
|
+
...(telemetry.protocolResponseShape === undefined ? {} : { protocolResponseShape: telemetry.protocolResponseShape }),
|
|
131
|
+
...(telemetry.protocolResponseBytes === undefined ? {} : { protocolResponseBytes: telemetry.protocolResponseBytes }),
|
|
132
|
+
...(telemetry.protocolToolName === undefined ? {} : { protocolToolName: telemetry.protocolToolName }),
|
|
133
|
+
...(telemetry.protocolArgumentIssue === undefined ? {} : { protocolArgumentIssue: telemetry.protocolArgumentIssue }),
|
|
134
|
+
...(telemetry.protocolMissingRequiredKeys === undefined ? {} : { protocolMissingRequiredKeys: telemetry.protocolMissingRequiredKeys }),
|
|
135
|
+
...(telemetry.protocolReceivedArgumentKeys === undefined ? {} : { protocolReceivedArgumentKeys: telemetry.protocolReceivedArgumentKeys }),
|
|
136
|
+
...(telemetry.protocolCompatibilityApplied === undefined ? {} : { protocolCompatibilityApplied: telemetry.protocolCompatibilityApplied }),
|
|
137
|
+
eventCount: telemetry.eventCount,
|
|
138
|
+
toolEventCount: telemetry.toolEventCount,
|
|
139
|
+
permissionEventCount: telemetry.permissionEventCount,
|
|
140
|
+
eventCategoryCounts: { ...telemetry.eventCategoryCounts },
|
|
141
|
+
...(telemetry.dispatch === undefined ? {} : { dispatch: telemetry.dispatch }),
|
|
142
|
+
};
|
|
143
|
+
}
|
|
144
|
+
/** Build a whitelist-only record; prompt, stderr, env and executable are absent by design. */
|
|
145
|
+
export function buildAgyLogRecord(telemetry, event, errorCode) {
|
|
146
|
+
const process = telemetry.process;
|
|
147
|
+
const record = {
|
|
148
|
+
...baseRecord(telemetry),
|
|
149
|
+
event,
|
|
150
|
+
...(telemetry.queueWaitMs === undefined ? {} : { queueWaitMs: telemetry.queueWaitMs }),
|
|
151
|
+
...(telemetry.firstEventMs === undefined ? {} : { firstEventMs: telemetry.firstEventMs }),
|
|
152
|
+
...(telemetry.firstTextMs === undefined ? {} : { firstTextMs: telemetry.firstTextMs }),
|
|
153
|
+
...(telemetry.finalStatus === undefined ? {} : { finalStatus: telemetry.finalStatus }),
|
|
154
|
+
...(telemetry.finalResultRaw === undefined ? {} : { finalResultRaw: telemetry.finalResultRaw }),
|
|
155
|
+
...(telemetry.usage === undefined ? {} : { usage: telemetry.usage }),
|
|
156
|
+
...(telemetry.usageSource === undefined ? {} : { usageSource: telemetry.usageSource }),
|
|
157
|
+
...(telemetry.cumulativeUsage === undefined ? {} : { cumulativeUsage: telemetry.cumulativeUsage }),
|
|
158
|
+
...(telemetry.cacheHit === undefined ? {} : { cacheHit: telemetry.cacheHit }),
|
|
159
|
+
...(telemetry.cacheTokenShare === undefined ? {} : { cacheTokenShare: telemetry.cacheTokenShare }),
|
|
160
|
+
...(telemetry.requestMode === undefined ? {} : { requestMode: telemetry.requestMode }),
|
|
161
|
+
...(telemetry.conversationReused === undefined ? {} : { conversationReused: telemetry.conversationReused }),
|
|
162
|
+
...(telemetry.conversationMismatch === undefined ? {} : { conversationMismatch: telemetry.conversationMismatch }),
|
|
163
|
+
...(telemetry.partialAnswerDelivered === undefined ? {} : { partialAnswerDelivered: telemetry.partialAnswerDelivered }),
|
|
164
|
+
...(telemetry.stablePrefixBytes === undefined ? {} : { stablePrefixBytes: telemetry.stablePrefixBytes }),
|
|
165
|
+
...(telemetry.stablePrefixHash === undefined ? {} : { stablePrefixHash: telemetry.stablePrefixHash }),
|
|
166
|
+
...(telemetry.toolSchemaHash === undefined ? {} : { toolSchemaHash: telemetry.toolSchemaHash }),
|
|
167
|
+
...(telemetry.sessionId === undefined ? {} : { sessionFingerprint: fingerprint(telemetry.sessionId) }),
|
|
168
|
+
...(telemetry.conversationId === undefined ? {} : { conversationFingerprint: fingerprint(telemetry.conversationId) }),
|
|
169
|
+
...(telemetry.processDiagnostic === undefined ? {} : { processDiagnostic: telemetry.processDiagnostic }),
|
|
170
|
+
...(process === undefined ? {} : {
|
|
171
|
+
exitCode: process.exitCode,
|
|
172
|
+
termination: process.termination,
|
|
173
|
+
}),
|
|
174
|
+
...(telemetry.durationMs === undefined && process === undefined
|
|
175
|
+
? {}
|
|
176
|
+
: { durationMs: telemetry.durationMs ?? process?.durationMs ?? 0 }),
|
|
177
|
+
...(errorCode === undefined ? {} : { errorCode }),
|
|
178
|
+
};
|
|
179
|
+
return record;
|
|
180
|
+
}
|
|
181
|
+
const PROCESS_DIAGNOSTIC_STAGES = [
|
|
182
|
+
'prepare',
|
|
183
|
+
'spawn',
|
|
184
|
+
'stdin',
|
|
185
|
+
'stdout-handler',
|
|
186
|
+
];
|
|
187
|
+
const TOOL_EVENT_CARRIER_SHAPES = [
|
|
188
|
+
'not-send-message',
|
|
189
|
+
'missing-recipient',
|
|
190
|
+
'missing-message',
|
|
191
|
+
'unreadable-recipient',
|
|
192
|
+
'unreadable-message',
|
|
193
|
+
'complete',
|
|
194
|
+
];
|
|
195
|
+
const TOOL_EVENT_RECIPIENT_CLASSES = [
|
|
196
|
+
'not-applicable',
|
|
197
|
+
'missing',
|
|
198
|
+
'default-api',
|
|
199
|
+
'dsh-recipient',
|
|
200
|
+
'self-conversation',
|
|
201
|
+
'other-conversation',
|
|
202
|
+
'other',
|
|
203
|
+
];
|
|
204
|
+
const CARRIER_VALIDATIONS = [
|
|
205
|
+
'recipient-rejected',
|
|
206
|
+
'valid-message',
|
|
207
|
+
'valid-tool-call',
|
|
208
|
+
'invalid-envelope',
|
|
209
|
+
'unknown-tool',
|
|
210
|
+
'arguments-invalid',
|
|
211
|
+
'response-limit',
|
|
212
|
+
];
|
|
213
|
+
const TOOL_EVENT_STRUCTURAL_KEYS = new Set([
|
|
214
|
+
'event',
|
|
215
|
+
'init',
|
|
216
|
+
'step_update',
|
|
217
|
+
'checkpoint',
|
|
218
|
+
'agent_response',
|
|
219
|
+
'result',
|
|
220
|
+
'tool_call',
|
|
221
|
+
'tool_result',
|
|
222
|
+
'permission_request',
|
|
223
|
+
'error_message',
|
|
224
|
+
'error',
|
|
225
|
+
'step_type',
|
|
226
|
+
'tool_name',
|
|
227
|
+
'tool_input',
|
|
228
|
+
'state',
|
|
229
|
+
'name',
|
|
230
|
+
'recipient',
|
|
231
|
+
'message',
|
|
232
|
+
'arguments',
|
|
233
|
+
'parameters',
|
|
234
|
+
'input',
|
|
235
|
+
'args',
|
|
236
|
+
]);
|
|
237
|
+
function sanitizeStructuralKeys(value) {
|
|
238
|
+
if (!Array.isArray(value))
|
|
239
|
+
return [];
|
|
240
|
+
const safe = value.filter((key) => (typeof key === 'string' && TOOL_EVENT_STRUCTURAL_KEYS.has(key)));
|
|
241
|
+
return [...new Set(safe)].slice(0, 24);
|
|
242
|
+
}
|
|
243
|
+
function sanitizeToolEventDiagnostic(value) {
|
|
244
|
+
if (typeof value !== 'object' || value === null)
|
|
245
|
+
return undefined;
|
|
246
|
+
const candidate = value;
|
|
247
|
+
const kind = candidate.kind;
|
|
248
|
+
const carrierShape = candidate.carrierShape;
|
|
249
|
+
const recipientClass = candidate.recipientClass;
|
|
250
|
+
if (!isAgyToolEventKind(kind)
|
|
251
|
+
|| !TOOL_EVENT_CARRIER_SHAPES.includes(carrierShape)
|
|
252
|
+
|| !TOOL_EVENT_RECIPIENT_CLASSES.includes(recipientClass)) {
|
|
253
|
+
return undefined;
|
|
254
|
+
}
|
|
255
|
+
const safeText = (key) => {
|
|
256
|
+
const text = candidate[key];
|
|
257
|
+
return typeof text === 'string' && text.length > 0 ? redactText(text, 128) : undefined;
|
|
258
|
+
};
|
|
259
|
+
const eventName = safeText('eventName');
|
|
260
|
+
if (eventName === undefined)
|
|
261
|
+
return undefined;
|
|
262
|
+
const stepType = safeText('stepType');
|
|
263
|
+
const toolName = safeText('toolName');
|
|
264
|
+
return {
|
|
265
|
+
eventName,
|
|
266
|
+
kind,
|
|
267
|
+
...(stepType === undefined ? {} : { stepType }),
|
|
268
|
+
...(toolName === undefined ? {} : { toolName }),
|
|
269
|
+
carrierShape: carrierShape,
|
|
270
|
+
recipientClass: recipientClass,
|
|
271
|
+
topLevelKeys: sanitizeStructuralKeys(candidate.topLevelKeys),
|
|
272
|
+
stepKeys: sanitizeStructuralKeys(candidate.stepKeys),
|
|
273
|
+
toolInputKeys: sanitizeStructuralKeys(candidate.toolInputKeys),
|
|
274
|
+
};
|
|
275
|
+
}
|
|
276
|
+
function sanitizeProcessDiagnostic(value) {
|
|
277
|
+
if (typeof value !== 'object' || value === null)
|
|
278
|
+
return undefined;
|
|
279
|
+
const candidate = value;
|
|
280
|
+
const stage = candidate.stage;
|
|
281
|
+
if (!PROCESS_DIAGNOSTIC_STAGES.includes(stage))
|
|
282
|
+
return undefined;
|
|
283
|
+
const safeStage = stage;
|
|
284
|
+
const safeNumber = (key) => {
|
|
285
|
+
const number = candidate[key];
|
|
286
|
+
return typeof number === 'number' && Number.isSafeInteger(number) && number >= 0 ? number : undefined;
|
|
287
|
+
};
|
|
288
|
+
const safeText = (key, maxLength) => {
|
|
289
|
+
const text = candidate[key];
|
|
290
|
+
return typeof text === 'string' && text.length > 0 ? redactText(text, maxLength) : undefined;
|
|
291
|
+
};
|
|
292
|
+
const lineHash = candidate.lineHash;
|
|
293
|
+
const errorName = safeText('errorName', 128);
|
|
294
|
+
const errorCode = safeText('errorCode', 128);
|
|
295
|
+
const lineNumber = safeNumber('lineNumber');
|
|
296
|
+
const lineLength = safeNumber('lineLength');
|
|
297
|
+
const stdoutLineCount = safeNumber('stdoutLineCount');
|
|
298
|
+
const stdoutBytes = safeNumber('stdoutBytes');
|
|
299
|
+
const stderrBytes = safeNumber('stderrBytes');
|
|
300
|
+
const stderrSummary = safeText('stderrSummary', 200);
|
|
301
|
+
return {
|
|
302
|
+
stage: safeStage,
|
|
303
|
+
...(errorName === undefined ? {} : { errorName }),
|
|
304
|
+
...(errorCode === undefined ? {} : { errorCode }),
|
|
305
|
+
...(lineNumber === undefined ? {} : { lineNumber }),
|
|
306
|
+
...(lineLength === undefined ? {} : { lineLength }),
|
|
307
|
+
...(typeof lineHash === 'string' && /^[0-9a-f]{16}$/i.test(lineHash)
|
|
308
|
+
? { lineHash: lineHash.toLowerCase() }
|
|
309
|
+
: {}),
|
|
310
|
+
...(stdoutLineCount === undefined ? {} : { stdoutLineCount }),
|
|
311
|
+
...(stdoutBytes === undefined ? {} : { stdoutBytes }),
|
|
312
|
+
...(stderrBytes === undefined ? {} : { stderrBytes }),
|
|
313
|
+
...(stderrSummary === undefined ? {} : { stderrSummary }),
|
|
314
|
+
};
|
|
315
|
+
}
|
|
316
|
+
/** Apply a final whitelist-only safety pass before the host logger receives metadata. */
|
|
317
|
+
export function sanitizeAgyLogRecord(record) {
|
|
318
|
+
const safeUsage = sanitizeTokenUsage(record.usage);
|
|
319
|
+
const safeCumulativeUsage = sanitizeTokenUsage(record.cumulativeUsage);
|
|
320
|
+
const processDiagnostic = sanitizeProcessDiagnostic(record.processDiagnostic);
|
|
321
|
+
const toolEventDiagnostic = sanitizeToolEventDiagnostic(record.toolEventDiagnostic);
|
|
322
|
+
const protocolMissingRequiredKeys = safeProtocolKeyList(record.protocolMissingRequiredKeys);
|
|
323
|
+
const protocolReceivedArgumentKeys = safeProtocolKeyList(record.protocolReceivedArgumentKeys);
|
|
324
|
+
const sessionFingerprint = record.sessionFingerprint
|
|
325
|
+
?? (record.sessionId === undefined ? undefined : fingerprint(record.sessionId));
|
|
326
|
+
const conversationFingerprint = record.conversationFingerprint
|
|
327
|
+
?? (record.conversationId === undefined ? undefined : fingerprint(record.conversationId));
|
|
328
|
+
return {
|
|
329
|
+
event: record.event,
|
|
330
|
+
requestId: redactText(record.requestId, 256),
|
|
331
|
+
provider: redactText(record.provider, 256),
|
|
332
|
+
model: redactText(record.model, 256),
|
|
333
|
+
agent: redactText(record.agent, 256),
|
|
334
|
+
toolPolicy: record.toolPolicy === 'agy-owned'
|
|
335
|
+
? 'agy-owned'
|
|
336
|
+
: record.toolPolicy === 'dsh-owned' ? 'dsh-owned' : 'reject',
|
|
337
|
+
toolSchemaCount: record.toolSchemaCount,
|
|
338
|
+
toolCallCount: safeNonNegativeNumber(record.toolCallCount),
|
|
339
|
+
bridgeOutcome: isAgyBridgeOutcome(record.bridgeOutcome) ? record.bridgeOutcome : 'failed',
|
|
340
|
+
...(isAgyPermissionPreset(record.permissionPreset) ? { permissionPreset: record.permissionPreset } : {}),
|
|
341
|
+
...(isAgyPermissionPreset(record.sandboxMode) ? { sandboxMode: record.sandboxMode } : {}),
|
|
342
|
+
...(isAgyApprovalPolicy(record.approvalPolicy) ? { approvalPolicy: record.approvalPolicy } : {}),
|
|
343
|
+
...(isAgyReasoningEffort(record.reasoningEffort) ? { reasoningEffort: record.reasoningEffort } : {}),
|
|
344
|
+
...(record.purpose === 'compaction' || record.purpose === 'session-title' ? { purpose: record.purpose } : {}),
|
|
345
|
+
...(isModelDiscoveryLogSource(record.modelDiscoverySource)
|
|
346
|
+
? { modelDiscoverySource: record.modelDiscoverySource }
|
|
347
|
+
: {}),
|
|
348
|
+
...(isModelDiscoveryErrorCode(record.modelDiscoveryWarningCode)
|
|
349
|
+
? { modelDiscoveryWarningCode: record.modelDiscoveryWarningCode }
|
|
350
|
+
: {}),
|
|
351
|
+
attempt: record.attempt,
|
|
352
|
+
processAttemptCount: safeNonNegativeNumber(record.processAttemptCount ?? record.attempt),
|
|
353
|
+
retryMaxRetries: safeNonNegativeNumber(record.retryMaxRetries ?? 0),
|
|
354
|
+
...(record.inputFrameBytes === undefined ? {} : { inputFrameBytes: safeNonNegativeNumber(record.inputFrameBytes) }),
|
|
355
|
+
...(record.inputFrameLimitBytes === undefined ? {} : { inputFrameLimitBytes: safeNonNegativeNumber(record.inputFrameLimitBytes) }),
|
|
356
|
+
...(record.promptBytes === undefined ? {} : { promptBytes: safeNonNegativeNumber(record.promptBytes) }),
|
|
357
|
+
...(record.promptLimitBytes === undefined ? {} : { promptLimitBytes: safeNonNegativeNumber(record.promptLimitBytes) }),
|
|
358
|
+
...(record.toolResultCount === undefined ? {} : { toolResultCount: safeNonNegativeNumber(record.toolResultCount) }),
|
|
359
|
+
...(record.largestToolResultBytes === undefined ? {} : { largestToolResultBytes: safeNonNegativeNumber(record.largestToolResultBytes) }),
|
|
360
|
+
...(record.truncatedToolResultCount === undefined ? {} : { truncatedToolResultCount: safeNonNegativeNumber(record.truncatedToolResultCount) }),
|
|
361
|
+
...(record.historyCompacted === true ? { historyCompacted: true } : {}),
|
|
362
|
+
...(record.omittedMessageCount === undefined ? {} : { omittedMessageCount: safeNonNegativeNumber(record.omittedMessageCount) }),
|
|
363
|
+
...(isAgyToolEventKind(record.toolEventKind) ? { toolEventKind: record.toolEventKind } : {}),
|
|
364
|
+
...(record.toolEventStreamIndex === undefined
|
|
365
|
+
? {}
|
|
366
|
+
: { toolEventStreamIndex: safeNonNegativeNumber(record.toolEventStreamIndex) }),
|
|
367
|
+
...(toolEventDiagnostic === undefined ? {} : { toolEventDiagnostic }),
|
|
368
|
+
...(record.carrierAbortExpected === true ? { carrierAbortExpected: true } : {}),
|
|
369
|
+
...(CARRIER_VALIDATIONS.includes(record.carrierValidation)
|
|
370
|
+
? { carrierValidation: record.carrierValidation }
|
|
371
|
+
: {}),
|
|
372
|
+
protocolRepairAttempts: safeNonNegativeNumber(record.protocolRepairAttempts ?? 0),
|
|
373
|
+
...(record.protocolRepairReason === 'not-json'
|
|
374
|
+
|| record.protocolRepairReason === 'arguments-invalid'
|
|
375
|
+
|| record.protocolRepairReason === 'internal-tool-event'
|
|
376
|
+
? { protocolRepairReason: record.protocolRepairReason }
|
|
377
|
+
: {}),
|
|
378
|
+
...(record.protocolFailureDetail === 'not-json'
|
|
379
|
+
|| record.protocolFailureDetail === 'envelope'
|
|
380
|
+
|| record.protocolFailureDetail === 'message-shape'
|
|
381
|
+
|| record.protocolFailureDetail === 'tool-call-shape'
|
|
382
|
+
|| record.protocolFailureDetail === 'response-limit'
|
|
383
|
+
|| record.protocolFailureDetail === 'validation'
|
|
384
|
+
|| record.protocolFailureDetail === 'unknown'
|
|
385
|
+
? { protocolFailureDetail: record.protocolFailureDetail }
|
|
386
|
+
: {}),
|
|
387
|
+
...(record.protocolResponseShape === 'empty'
|
|
388
|
+
|| record.protocolResponseShape === 'object-like'
|
|
389
|
+
|| record.protocolResponseShape === 'array-like'
|
|
390
|
+
|| record.protocolResponseShape === 'fenced'
|
|
391
|
+
|| record.protocolResponseShape === 'plain-text'
|
|
392
|
+
|| record.protocolResponseShape === 'non-string'
|
|
393
|
+
? { protocolResponseShape: record.protocolResponseShape }
|
|
394
|
+
: {}),
|
|
395
|
+
...(record.protocolResponseBytes === undefined
|
|
396
|
+
? {}
|
|
397
|
+
: { protocolResponseBytes: safeNonNegativeNumber(record.protocolResponseBytes) }),
|
|
398
|
+
...(typeof record.protocolToolName === 'string'
|
|
399
|
+
? { protocolToolName: redactText(record.protocolToolName, 128) }
|
|
400
|
+
: {}),
|
|
401
|
+
...(isAgyProtocolArgumentIssue(record.protocolArgumentIssue)
|
|
402
|
+
? { protocolArgumentIssue: record.protocolArgumentIssue }
|
|
403
|
+
: {}),
|
|
404
|
+
...(protocolMissingRequiredKeys === undefined
|
|
405
|
+
? {}
|
|
406
|
+
: { protocolMissingRequiredKeys }),
|
|
407
|
+
...(protocolReceivedArgumentKeys === undefined
|
|
408
|
+
? {}
|
|
409
|
+
: { protocolReceivedArgumentKeys }),
|
|
410
|
+
...(isToolProtocolCompatibility(record.protocolCompatibilityApplied)
|
|
411
|
+
? { protocolCompatibilityApplied: record.protocolCompatibilityApplied }
|
|
412
|
+
: {}),
|
|
413
|
+
eventCount: record.eventCount,
|
|
414
|
+
toolEventCount: record.toolEventCount,
|
|
415
|
+
permissionEventCount: record.permissionEventCount,
|
|
416
|
+
eventCategoryCounts: {
|
|
417
|
+
init: record.eventCategoryCounts.init,
|
|
418
|
+
step_update: record.eventCategoryCounts.step_update,
|
|
419
|
+
checkpoint: record.eventCategoryCounts.checkpoint,
|
|
420
|
+
agent_response: record.eventCategoryCounts.agent_response,
|
|
421
|
+
result: record.eventCategoryCounts.result,
|
|
422
|
+
tool: record.eventCategoryCounts.tool,
|
|
423
|
+
permission: record.eventCategoryCounts.permission,
|
|
424
|
+
error: record.eventCategoryCounts.error,
|
|
425
|
+
unknown: record.eventCategoryCounts.unknown,
|
|
426
|
+
},
|
|
427
|
+
...(record.durationMs === undefined ? {} : { durationMs: record.durationMs }),
|
|
428
|
+
...(record.dispatch === 'cold' || record.dispatch === 'persistent' ? { dispatch: record.dispatch } : {}),
|
|
429
|
+
...(record.firstEventMs === undefined
|
|
430
|
+
? {}
|
|
431
|
+
: { firstEventMs: safeNonNegativeNumber(record.firstEventMs) }),
|
|
432
|
+
...(record.firstTextMs === undefined
|
|
433
|
+
? {}
|
|
434
|
+
: { firstTextMs: safeNonNegativeNumber(record.firstTextMs) }),
|
|
435
|
+
...(record.exitCode === undefined ? {} : { exitCode: record.exitCode }),
|
|
436
|
+
...(record.termination === undefined ? {} : { termination: record.termination }),
|
|
437
|
+
...(record.queueWaitMs === undefined ? {} : { queueWaitMs: record.queueWaitMs }),
|
|
438
|
+
...(safeUsage === undefined ? {} : { usage: safeUsage }),
|
|
439
|
+
...(processDiagnostic === undefined ? {} : { processDiagnostic }),
|
|
440
|
+
...(record.errorCode === undefined ? {} : { errorCode: redactText(record.errorCode, 128) }),
|
|
441
|
+
...(record.finalStatus === undefined ? {} : { finalStatus: redactText(record.finalStatus, 128) }),
|
|
442
|
+
...(record.finalResultRaw === undefined ? {} : { finalResultRaw: redactText(record.finalResultRaw, 1_024) }),
|
|
443
|
+
...(record.usageSource === 'step' || record.usageSource === 'result-fallback'
|
|
444
|
+
? { usageSource: record.usageSource }
|
|
445
|
+
: {}),
|
|
446
|
+
...(safeCumulativeUsage === undefined ? {} : { cumulativeUsage: safeCumulativeUsage }),
|
|
447
|
+
...(record.cacheHit === undefined ? {} : { cacheHit: record.cacheHit === true }),
|
|
448
|
+
...(record.cacheTokenShare === undefined
|
|
449
|
+
? {}
|
|
450
|
+
: { cacheTokenShare: Math.min(1, Math.max(0, safeNonNegativeNumber(record.cacheTokenShare))) }),
|
|
451
|
+
...(record.requestMode === 'full'
|
|
452
|
+
|| record.requestMode === 'bootstrap'
|
|
453
|
+
|| record.requestMode === 'delta'
|
|
454
|
+
|| record.requestMode === 'full-fallback'
|
|
455
|
+
? { requestMode: record.requestMode }
|
|
456
|
+
: {}),
|
|
457
|
+
...(record.conversationReused === undefined ? {} : { conversationReused: record.conversationReused === true }),
|
|
458
|
+
...(record.conversationMismatch === undefined ? {} : { conversationMismatch: record.conversationMismatch === true }),
|
|
459
|
+
...(record.partialAnswerDelivered === undefined ? {} : { partialAnswerDelivered: record.partialAnswerDelivered === true }),
|
|
460
|
+
...(record.stablePrefixBytes === undefined
|
|
461
|
+
? {}
|
|
462
|
+
: { stablePrefixBytes: safeNonNegativeNumber(record.stablePrefixBytes) }),
|
|
463
|
+
...(typeof record.stablePrefixHash === 'string' && /^[0-9a-f]{16,64}$/i.test(record.stablePrefixHash)
|
|
464
|
+
? { stablePrefixHash: record.stablePrefixHash.toLowerCase() }
|
|
465
|
+
: {}),
|
|
466
|
+
...(typeof record.toolSchemaHash === 'string' && /^[0-9a-f]{16,64}$/i.test(record.toolSchemaHash)
|
|
467
|
+
? { toolSchemaHash: record.toolSchemaHash.toLowerCase() }
|
|
468
|
+
: {}),
|
|
469
|
+
...(sessionFingerprint === undefined ? {} : { sessionFingerprint: redactText(sessionFingerprint, 64) }),
|
|
470
|
+
...(conversationFingerprint === undefined ? {} : { conversationFingerprint: redactText(conversationFingerprint, 64) }),
|
|
471
|
+
};
|
|
472
|
+
}
|
|
473
|
+
function sanitizeTokenUsage(usage) {
|
|
474
|
+
if (usage === undefined)
|
|
475
|
+
return undefined;
|
|
476
|
+
return {
|
|
477
|
+
inputTokens: safeNonNegativeNumber(usage.inputTokens),
|
|
478
|
+
outputTokens: safeNonNegativeNumber(usage.outputTokens),
|
|
479
|
+
...(usage.cacheReadTokens === undefined
|
|
480
|
+
? {}
|
|
481
|
+
: { cacheReadTokens: safeNonNegativeNumber(usage.cacheReadTokens) }),
|
|
482
|
+
...(usage.cacheWriteTokens === undefined
|
|
483
|
+
? {}
|
|
484
|
+
: { cacheWriteTokens: safeNonNegativeNumber(usage.cacheWriteTokens) }),
|
|
485
|
+
...(usage.reasoningTokens === undefined
|
|
486
|
+
? {}
|
|
487
|
+
: { reasoningTokens: safeNonNegativeNumber(usage.reasoningTokens) }),
|
|
488
|
+
};
|
|
489
|
+
}
|
|
490
|
+
function safeNonNegativeNumber(value) {
|
|
491
|
+
return typeof value === 'number' && Number.isFinite(value) && value >= 0 ? value : 0;
|
|
492
|
+
}
|
|
493
|
+
export function emitAgyLog(logger, record) {
|
|
494
|
+
if (logger === undefined)
|
|
495
|
+
return;
|
|
496
|
+
try {
|
|
497
|
+
logger(sanitizeAgyLogRecord(record));
|
|
498
|
+
}
|
|
499
|
+
catch {
|
|
500
|
+
// Observability must never break a model request.
|
|
501
|
+
}
|
|
502
|
+
}
|
|
503
|
+
//# sourceMappingURL=log.js.map
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { type ProcessRequest, type ProcessResult } from './process.js';
|
|
2
|
+
import type { ModelConfig } from '../provider/config.js';
|
|
3
|
+
import { type ModelDiscoveryErrorCode } from '../provider/error-codes.js';
|
|
4
|
+
export type { ModelDiscoveryErrorCode } from '../provider/error-codes.js';
|
|
5
|
+
export declare const DEFAULT_MODEL_DISCOVERY_TTL_MS: number;
|
|
6
|
+
export declare const DEFAULT_MODEL_DISCOVERY_TIMEOUT_MS = 10000;
|
|
7
|
+
export declare const MAX_MODEL_DISCOVERY_OUTPUT_BYTES: number;
|
|
8
|
+
export type ModelDiscoverySource = 'discovered' | 'merged' | 'cache' | 'fallback';
|
|
9
|
+
export interface AgyModelDiscoveryResult {
|
|
10
|
+
models: readonly ModelConfig[];
|
|
11
|
+
source: ModelDiscoverySource;
|
|
12
|
+
stale: boolean;
|
|
13
|
+
warning?: string;
|
|
14
|
+
warningCode?: ModelDiscoveryErrorCode;
|
|
15
|
+
}
|
|
16
|
+
export type AgyModelDiscoveryCommand = (request: ProcessRequest) => Promise<ProcessResult>;
|
|
17
|
+
export interface AgyModelDiscoveryOptions {
|
|
18
|
+
executable?: string;
|
|
19
|
+
ttlMs?: number;
|
|
20
|
+
timeoutMs?: number;
|
|
21
|
+
maxOutputBytes?: number;
|
|
22
|
+
runCommand?: AgyModelDiscoveryCommand;
|
|
23
|
+
now?: () => number;
|
|
24
|
+
}
|
|
25
|
+
/** Merge explicit configuration first, then add AGY-discovered models. Normalizes base ids and dedupes. */
|
|
26
|
+
export declare function mergeModelCatalog(configured: readonly ModelConfig[], discovered: readonly ModelConfig[]): readonly ModelConfig[];
|
|
27
|
+
/** @deprecated Use normalizeModelId/extractModelEffort for effort-split handling. */
|
|
28
|
+
export declare const MODEL_EFFORT_SUFFIX_RE: RegExp;
|
|
29
|
+
/** Parse the tab-separated, non-interactive output of `agy models`. Normalizes -high/-medium/-low suffix to base id and dedupes base. */
|
|
30
|
+
export declare function parseAgyModels(output: string): ModelConfig[];
|
|
31
|
+
/**
|
|
32
|
+
* Discover AGY models without invoking a model request. Results are kept in
|
|
33
|
+
* memory only. A refresh is single-flight and all failures fail open to the
|
|
34
|
+
* last successful catalog or the caller's static catalog.
|
|
35
|
+
*/
|
|
36
|
+
export declare class AgyModelDiscovery {
|
|
37
|
+
private readonly executable;
|
|
38
|
+
private readonly ttlMs;
|
|
39
|
+
private readonly timeoutMs;
|
|
40
|
+
private readonly maxOutputBytes;
|
|
41
|
+
private readonly runCommand;
|
|
42
|
+
private readonly now;
|
|
43
|
+
private cache;
|
|
44
|
+
private inFlight;
|
|
45
|
+
constructor(options?: AgyModelDiscoveryOptions);
|
|
46
|
+
discover(configured: readonly ModelConfig[]): Promise<AgyModelDiscoveryResult>;
|
|
47
|
+
private refresh;
|
|
48
|
+
}
|
|
49
|
+
//# sourceMappingURL=models.d.ts.map
|