@winmatrix/agent-sdk 1.0.4 → 1.0.6

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 (75) hide show
  1. package/dist/adapters/claude/ClaudeAdapter.d.ts +71 -1
  2. package/dist/adapters/claude/ClaudeAdapter.d.ts.map +1 -1
  3. package/dist/adapters/claude/ClaudeAdapter.js +490 -86
  4. package/dist/adapters/claude/ClaudeAdapter.js.map +1 -1
  5. package/dist/adapters/claude/ClaudeInvocationUsageTracker.d.ts +17 -0
  6. package/dist/adapters/claude/ClaudeInvocationUsageTracker.d.ts.map +1 -0
  7. package/dist/adapters/claude/ClaudeInvocationUsageTracker.js +79 -0
  8. package/dist/adapters/claude/ClaudeInvocationUsageTracker.js.map +1 -0
  9. package/dist/adapters/claude/ClaudeStreamParser.d.ts +19 -0
  10. package/dist/adapters/claude/ClaudeStreamParser.d.ts.map +1 -1
  11. package/dist/adapters/claude/ClaudeStreamParser.js +92 -0
  12. package/dist/adapters/claude/ClaudeStreamParser.js.map +1 -1
  13. package/dist/adapters/codex/CodexAdapter.d.ts +15 -1
  14. package/dist/adapters/codex/CodexAdapter.d.ts.map +1 -1
  15. package/dist/adapters/codex/CodexAdapter.js +164 -14
  16. package/dist/adapters/codex/CodexAdapter.js.map +1 -1
  17. package/dist/adapters/codex/CodexSessionStore.d.ts +42 -0
  18. package/dist/adapters/codex/CodexSessionStore.d.ts.map +1 -0
  19. package/dist/adapters/codex/CodexSessionStore.js +279 -0
  20. package/dist/adapters/codex/CodexSessionStore.js.map +1 -0
  21. package/dist/adapters/hermes/HermesAdapter.d.ts +10 -1
  22. package/dist/adapters/hermes/HermesAdapter.d.ts.map +1 -1
  23. package/dist/adapters/hermes/HermesAdapter.js +177 -10
  24. package/dist/adapters/hermes/HermesAdapter.js.map +1 -1
  25. package/dist/adapters/hermes/HermesInvocationUsageReader.d.ts +26 -0
  26. package/dist/adapters/hermes/HermesInvocationUsageReader.d.ts.map +1 -0
  27. package/dist/adapters/hermes/HermesInvocationUsageReader.js +128 -0
  28. package/dist/adapters/hermes/HermesInvocationUsageReader.js.map +1 -0
  29. package/dist/adapters/openclaw/OpenClawAdapter.d.ts +3 -7
  30. package/dist/adapters/openclaw/OpenClawAdapter.d.ts.map +1 -1
  31. package/dist/adapters/openclaw/OpenClawAdapter.js +116 -45
  32. package/dist/adapters/openclaw/OpenClawAdapter.js.map +1 -1
  33. package/dist/core/AgentAdapter.d.ts +51 -2
  34. package/dist/core/AgentAdapter.d.ts.map +1 -1
  35. package/dist/core/AgentAdapter.js.map +1 -1
  36. package/dist/core/WinMatrixAgentClient.d.ts +23 -1
  37. package/dist/core/WinMatrixAgentClient.d.ts.map +1 -1
  38. package/dist/core/WinMatrixAgentClient.js +115 -12
  39. package/dist/core/WinMatrixAgentClient.js.map +1 -1
  40. package/dist/index.d.ts +18 -12
  41. package/dist/index.d.ts.map +1 -1
  42. package/dist/index.js +10 -13
  43. package/dist/index.js.map +1 -1
  44. package/dist/observability/WorkstationTokenUsageRecorder.d.ts +54 -0
  45. package/dist/observability/WorkstationTokenUsageRecorder.d.ts.map +1 -0
  46. package/dist/observability/WorkstationTokenUsageRecorder.js +264 -0
  47. package/dist/observability/WorkstationTokenUsageRecorder.js.map +1 -0
  48. package/dist/utils/contextBudget.d.ts +27 -0
  49. package/dist/utils/contextBudget.d.ts.map +1 -0
  50. package/dist/utils/contextBudget.js +86 -0
  51. package/dist/utils/contextBudget.js.map +1 -0
  52. package/dist/utils/envSanitizer.d.ts +38 -0
  53. package/dist/utils/envSanitizer.d.ts.map +1 -0
  54. package/dist/utils/envSanitizer.js +94 -0
  55. package/dist/utils/envSanitizer.js.map +1 -0
  56. package/dist/utils/errorClassification.d.ts +26 -0
  57. package/dist/utils/errorClassification.d.ts.map +1 -0
  58. package/dist/utils/errorClassification.js +52 -0
  59. package/dist/utils/errorClassification.js.map +1 -0
  60. package/dist/utils/mcpUrlValidation.d.ts +10 -0
  61. package/dist/utils/mcpUrlValidation.d.ts.map +1 -0
  62. package/dist/utils/mcpUrlValidation.js +18 -0
  63. package/dist/utils/mcpUrlValidation.js.map +1 -0
  64. package/dist/utils/processUtils.d.ts +10 -0
  65. package/dist/utils/processUtils.d.ts.map +1 -1
  66. package/dist/utils/processUtils.js +42 -9
  67. package/dist/utils/processUtils.js.map +1 -1
  68. package/dist/utils/promptGuardrails.d.ts +15 -0
  69. package/dist/utils/promptGuardrails.d.ts.map +1 -0
  70. package/dist/utils/promptGuardrails.js +26 -0
  71. package/dist/utils/promptGuardrails.js.map +1 -0
  72. package/dist/utils/resumeFallback.d.ts.map +1 -1
  73. package/dist/utils/resumeFallback.js +39 -1
  74. package/dist/utils/resumeFallback.js.map +1 -1
  75. package/package.json +3 -4
@@ -0,0 +1,264 @@
1
+ import { createHash } from 'node:crypto';
2
+ export class WorkstationTokenUsageConflictError extends Error {
3
+ code = 'WORKSTATION_TOKEN_USAGE_CONFLICT';
4
+ constructor() {
5
+ super('Workstation token usage conflicts with an immutable invocation');
6
+ this.name = 'WorkstationTokenUsageConflictError';
7
+ }
8
+ }
9
+ export class WorkstationTokenUsageValidationError extends Error {
10
+ code = 'WORKSTATION_TOKEN_USAGE_INVALID';
11
+ constructor(message) {
12
+ super(message);
13
+ this.name = 'WorkstationTokenUsageValidationError';
14
+ }
15
+ }
16
+ function sha256(value) {
17
+ return createHash('sha256').update(value).digest('hex');
18
+ }
19
+ function canonicalize(value) {
20
+ if (Array.isArray(value))
21
+ return value.map(canonicalize);
22
+ if (!value || typeof value !== 'object')
23
+ return value;
24
+ const output = {};
25
+ for (const key of Object.keys(value).sort()) {
26
+ output[key] = canonicalize(value[key]);
27
+ }
28
+ return output;
29
+ }
30
+ function digest(value) {
31
+ return sha256(JSON.stringify(canonicalize(value)));
32
+ }
33
+ function requireNonEmpty(value, field) {
34
+ const normalized = value.trim();
35
+ if (!normalized)
36
+ throw new WorkstationTokenUsageValidationError(`${field} must be non-empty`);
37
+ return normalized;
38
+ }
39
+ function requireToken(value, field) {
40
+ const normalized = value ?? 0;
41
+ if (!Number.isSafeInteger(normalized) || normalized < 0) {
42
+ throw new WorkstationTokenUsageValidationError(`${field} must be a non-negative safe integer`);
43
+ }
44
+ return normalized;
45
+ }
46
+ function requireIso(value, field) {
47
+ if (!/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d{1,3})?(?:Z|[+-]\d{2}:\d{2})$/.test(value)) {
48
+ throw new WorkstationTokenUsageValidationError(`${field} must be an offset ISO timestamp`);
49
+ }
50
+ if (!Number.isFinite(Date.parse(value))) {
51
+ throw new WorkstationTokenUsageValidationError(`${field} is invalid`);
52
+ }
53
+ return value;
54
+ }
55
+ function optionalText(value) {
56
+ const normalized = value?.trim();
57
+ return normalized ? normalized : null;
58
+ }
59
+ function normalizeInvocation(input) {
60
+ const workstationId = requireNonEmpty(input.workstationId, 'workstationId');
61
+ const sourceInvocationId = requireNonEmpty(input.sourceInvocationId, 'sourceInvocationId');
62
+ const provider = requireNonEmpty(input.provider, 'provider');
63
+ const startedAt = requireIso(input.startedAt, 'startedAt');
64
+ const occurredAt = requireIso(input.occurredAt, 'occurredAt');
65
+ if (Date.parse(startedAt) > Date.parse(occurredAt)) {
66
+ throw new WorkstationTokenUsageValidationError('startedAt must not be after occurredAt');
67
+ }
68
+ if (input.attemptNo !== undefined && (!Number.isSafeInteger(input.attemptNo) || input.attemptNo < 1)) {
69
+ throw new WorkstationTokenUsageValidationError('attemptNo must be a positive safe integer');
70
+ }
71
+ if (input.modelUsages.length === 0) {
72
+ throw new WorkstationTokenUsageValidationError('modelUsages must be non-empty');
73
+ }
74
+ const seenSourceIds = new Set();
75
+ const modelUsages = input.modelUsages.map((usage) => {
76
+ const sourceModelUsageId = requireNonEmpty(usage.sourceModelUsageId, 'sourceModelUsageId');
77
+ if (seenSourceIds.has(sourceModelUsageId)) {
78
+ throw new WorkstationTokenUsageValidationError('sourceModelUsageId must be unique within an invocation');
79
+ }
80
+ seenSourceIds.add(sourceModelUsageId);
81
+ const inputTokens = requireToken(usage.inputTokens, 'inputTokens');
82
+ const outputTokens = requireToken(usage.outputTokens, 'outputTokens');
83
+ const cacheReadTokens = requireToken(usage.cacheReadTokens, 'cacheReadTokens');
84
+ const cacheCreationTokens = requireToken(usage.cacheCreationTokens, 'cacheCreationTokens');
85
+ const reasoningTokens = requireToken(usage.reasoningTokens, 'reasoningTokens');
86
+ return {
87
+ sourceModelUsageId,
88
+ modelUsageEventId: `coding_model_${sha256(`${input.runtime}:${workstationId}:${sourceModelUsageId}`)}`,
89
+ model: requireNonEmpty(usage.model, 'model'),
90
+ inputTokens,
91
+ outputTokens,
92
+ cacheReadTokens,
93
+ cacheCreationTokens,
94
+ reasoningTokens,
95
+ totalTokens: inputTokens + outputTokens + cacheReadTokens + cacheCreationTokens + reasoningTokens,
96
+ };
97
+ });
98
+ if (!modelUsages.some((usage) => usage.totalTokens > 0)) {
99
+ throw new WorkstationTokenUsageValidationError('invocation must contain token usage');
100
+ }
101
+ const totals = modelUsages.reduce((sum, usage) => ({
102
+ input: sum.input + usage.inputTokens,
103
+ output: sum.output + usage.outputTokens,
104
+ cacheRead: sum.cacheRead + usage.cacheReadTokens,
105
+ cacheCreation: sum.cacheCreation + usage.cacheCreationTokens,
106
+ reasoning: sum.reasoning + usage.reasoningTokens,
107
+ total: sum.total + usage.totalTokens,
108
+ }), { input: 0, output: 0, cacheRead: 0, cacheCreation: 0, reasoning: 0, total: 0 });
109
+ return {
110
+ workstationId,
111
+ runtime: input.runtime,
112
+ sourceInvocationId,
113
+ usageEventId: `coding_inv_${sha256(`${input.runtime}:${workstationId}:${sourceInvocationId}`)}`,
114
+ provider,
115
+ startedAt,
116
+ occurredAt,
117
+ timeSource: input.timeSource,
118
+ sessionId: optionalText(input.sessionId),
119
+ taskRecordId: optionalText(input.taskRecordId),
120
+ taskId: optionalText(input.taskId),
121
+ attemptNo: input.attemptNo ?? null,
122
+ modelUsages,
123
+ tokenInput: totals.input,
124
+ tokenOutput: totals.output,
125
+ tokenCacheRead: totals.cacheRead,
126
+ tokenCacheCreation: totals.cacheCreation,
127
+ tokenReasoning: totals.reasoning,
128
+ tokenTotal: totals.total,
129
+ };
130
+ }
131
+ export class SqliteWorkstationTokenUsageRecorder {
132
+ db;
133
+ constructor(db) {
134
+ this.db = db;
135
+ this.initializeSchema();
136
+ }
137
+ static async open(path) {
138
+ const sqlite = await import('node:sqlite');
139
+ return new SqliteWorkstationTokenUsageRecorder(new sqlite.DatabaseSync(path));
140
+ }
141
+ async recordCompletedInvocation(input) {
142
+ const invocation = normalizeInvocation(input);
143
+ const payloadDigest = digest(invocation);
144
+ this.db.exec('BEGIN IMMEDIATE');
145
+ try {
146
+ const existing = this.db.prepare(`SELECT canonical_payload_digest AS digest
147
+ FROM workstation_token_usage_invocation
148
+ WHERE workstation_id = ? AND source_invocation_id = ?`).get(invocation.workstationId, invocation.sourceInvocationId);
149
+ if (existing) {
150
+ if (existing.digest !== payloadDigest)
151
+ throw new WorkstationTokenUsageConflictError();
152
+ this.db.exec('COMMIT');
153
+ return 'replayed';
154
+ }
155
+ const inserted = this.db.prepare(`INSERT INTO workstation_token_usage_invocation (
156
+ workstation_id, runtime, source_invocation_id, usage_event_id, provider,
157
+ started_at, occurred_at, time_source, session_id, task_record_id, task_id, attempt_no,
158
+ token_input, token_output, token_cache_read, token_cache_creation, token_reasoning,
159
+ token_total, canonical_payload_digest, export_schema_version
160
+ ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 1)`).run(invocation.workstationId, invocation.runtime, invocation.sourceInvocationId, invocation.usageEventId, invocation.provider, invocation.startedAt, invocation.occurredAt, invocation.timeSource, invocation.sessionId, invocation.taskRecordId, invocation.taskId, invocation.attemptNo, invocation.tokenInput, invocation.tokenOutput, invocation.tokenCacheRead, invocation.tokenCacheCreation, invocation.tokenReasoning, invocation.tokenTotal, payloadDigest);
161
+ const invocationRowId = Number(inserted.lastInsertRowid);
162
+ const insertModel = this.db.prepare(`INSERT INTO workstation_token_usage_model (
163
+ invocation_row_id, workstation_id, source_model_usage_id, model_usage_event_id, model,
164
+ token_input, token_output, token_cache_read, token_cache_creation, token_reasoning, token_total
165
+ ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`);
166
+ for (const usage of invocation.modelUsages) {
167
+ insertModel.run(invocationRowId, invocation.workstationId, usage.sourceModelUsageId, usage.modelUsageEventId, usage.model, usage.inputTokens, usage.outputTokens, usage.cacheReadTokens, usage.cacheCreationTokens, usage.reasoningTokens, usage.totalTokens);
168
+ }
169
+ this.db.prepare(`INSERT INTO workstation_token_usage_metadata (workstation_id, tracking_started_at)
170
+ VALUES (?, ?)
171
+ ON CONFLICT(workstation_id) DO NOTHING`).run(invocation.workstationId, invocation.startedAt);
172
+ this.db.exec('COMMIT');
173
+ return 'inserted';
174
+ }
175
+ catch (error) {
176
+ this.db.exec('ROLLBACK');
177
+ throw error;
178
+ }
179
+ }
180
+ async recordCoverageGap(input) {
181
+ const workstationId = requireNonEmpty(input.workstationId, 'workstationId');
182
+ const reason = requireNonEmpty(input.reason, 'reason');
183
+ const occurredAt = requireIso(input.occurredAt, 'occurredAt');
184
+ const stableSourceReference = requireNonEmpty(input.stableSourceReference, 'stableSourceReference');
185
+ const gapKey = sha256(`${input.runtime}:${workstationId}:${reason}:${stableSourceReference}`);
186
+ const result = this.db.prepare(`INSERT INTO workstation_token_usage_gap (
187
+ workstation_id, runtime, reason, occurred_at, gap_key
188
+ ) VALUES (?, ?, ?, ?, ?)
189
+ ON CONFLICT(workstation_id, gap_key) DO NOTHING`).run(workstationId, input.runtime, reason, occurredAt, gapKey);
190
+ return result.changes === 1 ? 'inserted' : 'replayed';
191
+ }
192
+ close() {
193
+ this.db.close();
194
+ }
195
+ initializeSchema() {
196
+ this.db.exec(`
197
+ PRAGMA journal_mode = WAL;
198
+ PRAGMA foreign_keys = ON;
199
+ PRAGMA busy_timeout = 5000;
200
+ CREATE TABLE IF NOT EXISTS workstation_token_usage_metadata (
201
+ workstation_id TEXT PRIMARY KEY,
202
+ tracking_started_at TEXT NOT NULL,
203
+ export_after_row_id INTEGER NOT NULL DEFAULT 0
204
+ );
205
+ CREATE TABLE IF NOT EXISTS workstation_token_usage_invocation (
206
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
207
+ workstation_id TEXT NOT NULL,
208
+ runtime TEXT NOT NULL CHECK(runtime IN ('claude-code', 'hermes')),
209
+ source_invocation_id TEXT NOT NULL,
210
+ usage_event_id TEXT NOT NULL,
211
+ provider TEXT NOT NULL,
212
+ started_at TEXT NOT NULL,
213
+ occurred_at TEXT NOT NULL,
214
+ time_source TEXT NOT NULL CHECK(time_source IN ('source', 'message_start_observed')),
215
+ session_id TEXT,
216
+ task_record_id TEXT,
217
+ task_id TEXT,
218
+ attempt_no INTEGER,
219
+ token_input INTEGER NOT NULL,
220
+ token_output INTEGER NOT NULL,
221
+ token_cache_read INTEGER NOT NULL,
222
+ token_cache_creation INTEGER NOT NULL,
223
+ token_reasoning INTEGER NOT NULL,
224
+ token_total INTEGER NOT NULL,
225
+ canonical_payload_digest TEXT NOT NULL,
226
+ export_schema_version INTEGER NOT NULL,
227
+ UNIQUE(workstation_id, source_invocation_id),
228
+ UNIQUE(workstation_id, usage_event_id)
229
+ );
230
+ CREATE TABLE IF NOT EXISTS workstation_token_usage_model (
231
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
232
+ invocation_row_id INTEGER NOT NULL REFERENCES workstation_token_usage_invocation(id) ON DELETE CASCADE,
233
+ workstation_id TEXT NOT NULL,
234
+ source_model_usage_id TEXT NOT NULL,
235
+ model_usage_event_id TEXT NOT NULL,
236
+ model TEXT NOT NULL,
237
+ token_input INTEGER NOT NULL,
238
+ token_output INTEGER NOT NULL,
239
+ token_cache_read INTEGER NOT NULL,
240
+ token_cache_creation INTEGER NOT NULL,
241
+ token_reasoning INTEGER NOT NULL,
242
+ token_total INTEGER NOT NULL,
243
+ UNIQUE(workstation_id, source_model_usage_id),
244
+ UNIQUE(workstation_id, model_usage_event_id)
245
+ );
246
+ CREATE TABLE IF NOT EXISTS workstation_token_usage_gap (
247
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
248
+ workstation_id TEXT NOT NULL,
249
+ runtime TEXT NOT NULL CHECK(runtime IN ('claude-code', 'hermes')),
250
+ reason TEXT NOT NULL,
251
+ occurred_at TEXT NOT NULL,
252
+ gap_key TEXT NOT NULL,
253
+ UNIQUE(workstation_id, gap_key)
254
+ );
255
+ CREATE INDEX IF NOT EXISTS idx_workstation_usage_invocation_export
256
+ ON workstation_token_usage_invocation(workstation_id, export_schema_version, id);
257
+ CREATE INDEX IF NOT EXISTS idx_workstation_usage_model_invocation
258
+ ON workstation_token_usage_model(invocation_row_id, id);
259
+ CREATE INDEX IF NOT EXISTS idx_workstation_usage_gap_workstation
260
+ ON workstation_token_usage_gap(workstation_id, id);
261
+ `);
262
+ }
263
+ }
264
+ //# sourceMappingURL=WorkstationTokenUsageRecorder.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"WorkstationTokenUsageRecorder.js","sourceRoot":"","sources":["../../src/observability/WorkstationTokenUsageRecorder.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAwFzC,MAAM,OAAO,kCAAmC,SAAQ,KAAK;IAClD,IAAI,GAAG,kCAAkC,CAAC;IAEnD;QACE,KAAK,CAAC,gEAAgE,CAAC,CAAC;QACxE,IAAI,CAAC,IAAI,GAAG,oCAAoC,CAAC;IACnD,CAAC;CACF;AAED,MAAM,OAAO,oCAAqC,SAAQ,KAAK;IACpD,IAAI,GAAG,iCAAiC,CAAC;IAElD,YAAY,OAAe;QACzB,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,sCAAsC,CAAC;IACrD,CAAC;CACF;AAED,SAAS,MAAM,CAAC,KAAa;IAC3B,OAAO,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;AAC1D,CAAC;AAED,SAAS,YAAY,CAAC,KAAc;IAClC,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC;QAAE,OAAO,KAAK,CAAC,GAAG,CAAC,YAAY,CAAC,CAAC;IACzD,IAAI,CAAC,KAAK,IAAI,OAAO,KAAK,KAAK,QAAQ;QAAE,OAAO,KAAK,CAAC;IACtD,MAAM,MAAM,GAA4B,EAAE,CAAC;IAC3C,KAAK,MAAM,GAAG,IAAI,MAAM,CAAC,IAAI,CAAC,KAAgC,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC;QACvE,MAAM,CAAC,GAAG,CAAC,GAAG,YAAY,CAAE,KAAiC,CAAC,GAAG,CAAC,CAAC,CAAC;IACtE,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,SAAS,MAAM,CAAC,KAAc;IAC5B,OAAO,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC,YAAY,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;AACrD,CAAC;AAED,SAAS,eAAe,CAAC,KAAa,EAAE,KAAa;IACnD,MAAM,UAAU,GAAG,KAAK,CAAC,IAAI,EAAE,CAAC;IAChC,IAAI,CAAC,UAAU;QAAE,MAAM,IAAI,oCAAoC,CAAC,GAAG,KAAK,oBAAoB,CAAC,CAAC;IAC9F,OAAO,UAAU,CAAC;AACpB,CAAC;AAED,SAAS,YAAY,CAAC,KAAyB,EAAE,KAAa;IAC5D,MAAM,UAAU,GAAG,KAAK,IAAI,CAAC,CAAC;IAC9B,IAAI,CAAC,MAAM,CAAC,aAAa,CAAC,UAAU,CAAC,IAAI,UAAU,GAAG,CAAC,EAAE,CAAC;QACxD,MAAM,IAAI,oCAAoC,CAAC,GAAG,KAAK,sCAAsC,CAAC,CAAC;IACjG,CAAC;IACD,OAAO,UAAU,CAAC;AACpB,CAAC;AAED,SAAS,UAAU,CAAC,KAAa,EAAE,KAAa;IAC9C,IAAI,CAAC,0EAA0E,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC;QAC5F,MAAM,IAAI,oCAAoC,CAAC,GAAG,KAAK,kCAAkC,CAAC,CAAC;IAC7F,CAAC;IACD,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC;QACxC,MAAM,IAAI,oCAAoC,CAAC,GAAG,KAAK,aAAa,CAAC,CAAC;IACxE,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED,SAAS,YAAY,CAAC,KAAyB;IAC7C,MAAM,UAAU,GAAG,KAAK,EAAE,IAAI,EAAE,CAAC;IACjC,OAAO,UAAU,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,IAAI,CAAC;AACxC,CAAC;AAED,SAAS,mBAAmB,CAAC,KAAqC;IAChE,MAAM,aAAa,GAAG,eAAe,CAAC,KAAK,CAAC,aAAa,EAAE,eAAe,CAAC,CAAC;IAC5E,MAAM,kBAAkB,GAAG,eAAe,CAAC,KAAK,CAAC,kBAAkB,EAAE,oBAAoB,CAAC,CAAC;IAC3F,MAAM,QAAQ,GAAG,eAAe,CAAC,KAAK,CAAC,QAAQ,EAAE,UAAU,CAAC,CAAC;IAC7D,MAAM,SAAS,GAAG,UAAU,CAAC,KAAK,CAAC,SAAS,EAAE,WAAW,CAAC,CAAC;IAC3D,MAAM,UAAU,GAAG,UAAU,CAAC,KAAK,CAAC,UAAU,EAAE,YAAY,CAAC,CAAC;IAC9D,IAAI,IAAI,CAAC,KAAK,CAAC,SAAS,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC,EAAE,CAAC;QACnD,MAAM,IAAI,oCAAoC,CAAC,wCAAwC,CAAC,CAAC;IAC3F,CAAC;IACD,IAAI,KAAK,CAAC,SAAS,KAAK,SAAS,IAAI,CAAC,CAAC,MAAM,CAAC,aAAa,CAAC,KAAK,CAAC,SAAS,CAAC,IAAI,KAAK,CAAC,SAAS,GAAG,CAAC,CAAC,EAAE,CAAC;QACrG,MAAM,IAAI,oCAAoC,CAAC,2CAA2C,CAAC,CAAC;IAC9F,CAAC;IACD,IAAI,KAAK,CAAC,WAAW,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACnC,MAAM,IAAI,oCAAoC,CAAC,+BAA+B,CAAC,CAAC;IAClF,CAAC;IAED,MAAM,aAAa,GAAG,IAAI,GAAG,EAAU,CAAC;IACxC,MAAM,WAAW,GAAG,KAAK,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE;QAClD,MAAM,kBAAkB,GAAG,eAAe,CAAC,KAAK,CAAC,kBAAkB,EAAE,oBAAoB,CAAC,CAAC;QAC3F,IAAI,aAAa,CAAC,GAAG,CAAC,kBAAkB,CAAC,EAAE,CAAC;YAC1C,MAAM,IAAI,oCAAoC,CAAC,wDAAwD,CAAC,CAAC;QAC3G,CAAC;QACD,aAAa,CAAC,GAAG,CAAC,kBAAkB,CAAC,CAAC;QACtC,MAAM,WAAW,GAAG,YAAY,CAAC,KAAK,CAAC,WAAW,EAAE,aAAa,CAAC,CAAC;QACnE,MAAM,YAAY,GAAG,YAAY,CAAC,KAAK,CAAC,YAAY,EAAE,cAAc,CAAC,CAAC;QACtE,MAAM,eAAe,GAAG,YAAY,CAAC,KAAK,CAAC,eAAe,EAAE,iBAAiB,CAAC,CAAC;QAC/E,MAAM,mBAAmB,GAAG,YAAY,CAAC,KAAK,CAAC,mBAAmB,EAAE,qBAAqB,CAAC,CAAC;QAC3F,MAAM,eAAe,GAAG,YAAY,CAAC,KAAK,CAAC,eAAe,EAAE,iBAAiB,CAAC,CAAC;QAC/E,OAAO;YACL,kBAAkB;YAClB,iBAAiB,EAAE,gBAAgB,MAAM,CAAC,GAAG,KAAK,CAAC,OAAO,IAAI,aAAa,IAAI,kBAAkB,EAAE,CAAC,EAAE;YACtG,KAAK,EAAE,eAAe,CAAC,KAAK,CAAC,KAAK,EAAE,OAAO,CAAC;YAC5C,WAAW;YACX,YAAY;YACZ,eAAe;YACf,mBAAmB;YACnB,eAAe;YACf,WAAW,EAAE,WAAW,GAAG,YAAY,GAAG,eAAe,GAAG,mBAAmB,GAAG,eAAe;SAClG,CAAC;IACJ,CAAC,CAAC,CAAC;IACH,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,WAAW,GAAG,CAAC,CAAC,EAAE,CAAC;QACxD,MAAM,IAAI,oCAAoC,CAAC,qCAAqC,CAAC,CAAC;IACxF,CAAC;IAED,MAAM,MAAM,GAAG,WAAW,CAAC,MAAM,CAC/B,CAAC,GAAG,EAAE,KAAK,EAAE,EAAE,CAAC,CAAC;QACf,KAAK,EAAE,GAAG,CAAC,KAAK,GAAG,KAAK,CAAC,WAAW;QACpC,MAAM,EAAE,GAAG,CAAC,MAAM,GAAG,KAAK,CAAC,YAAY;QACvC,SAAS,EAAE,GAAG,CAAC,SAAS,GAAG,KAAK,CAAC,eAAe;QAChD,aAAa,EAAE,GAAG,CAAC,aAAa,GAAG,KAAK,CAAC,mBAAmB;QAC5D,SAAS,EAAE,GAAG,CAAC,SAAS,GAAG,KAAK,CAAC,eAAe;QAChD,KAAK,EAAE,GAAG,CAAC,KAAK,GAAG,KAAK,CAAC,WAAW;KACrC,CAAC,EACF,EAAE,KAAK,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,SAAS,EAAE,CAAC,EAAE,aAAa,EAAE,CAAC,EAAE,SAAS,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,CAChF,CAAC;IAEF,OAAO;QACL,aAAa;QACb,OAAO,EAAE,KAAK,CAAC,OAAO;QACtB,kBAAkB;QAClB,YAAY,EAAE,cAAc,MAAM,CAAC,GAAG,KAAK,CAAC,OAAO,IAAI,aAAa,IAAI,kBAAkB,EAAE,CAAC,EAAE;QAC/F,QAAQ;QACR,SAAS;QACT,UAAU;QACV,UAAU,EAAE,KAAK,CAAC,UAAU;QAC5B,SAAS,EAAE,YAAY,CAAC,KAAK,CAAC,SAAS,CAAC;QACxC,YAAY,EAAE,YAAY,CAAC,KAAK,CAAC,YAAY,CAAC;QAC9C,MAAM,EAAE,YAAY,CAAC,KAAK,CAAC,MAAM,CAAC;QAClC,SAAS,EAAE,KAAK,CAAC,SAAS,IAAI,IAAI;QAClC,WAAW;QACX,UAAU,EAAE,MAAM,CAAC,KAAK;QACxB,WAAW,EAAE,MAAM,CAAC,MAAM;QAC1B,cAAc,EAAE,MAAM,CAAC,SAAS;QAChC,kBAAkB,EAAE,MAAM,CAAC,aAAa;QACxC,cAAc,EAAE,MAAM,CAAC,SAAS;QAChC,UAAU,EAAE,MAAM,CAAC,KAAK;KACzB,CAAC;AACJ,CAAC;AAED,MAAM,OAAO,mCAAmC;IACT;IAArC,YAAqC,EAAgB;QAAhB,OAAE,GAAF,EAAE,CAAc;QACnD,IAAI,CAAC,gBAAgB,EAAE,CAAC;IAC1B,CAAC;IAED,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,IAAY;QAC5B,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,aAAa,CAAC,CAAC;QAC3C,OAAO,IAAI,mCAAmC,CAAC,IAAI,MAAM,CAAC,YAAY,CAAC,IAAI,CAAiB,CAAC,CAAC;IAChG,CAAC;IAED,KAAK,CAAC,yBAAyB,CAAC,KAAqC;QACnE,MAAM,UAAU,GAAG,mBAAmB,CAAC,KAAK,CAAC,CAAC;QAC9C,MAAM,aAAa,GAAG,MAAM,CAAC,UAAU,CAAC,CAAC;QACzC,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,iBAAiB,CAAC,CAAC;QAChC,IAAI,CAAC;YACH,MAAM,QAAQ,GAAG,IAAI,CAAC,EAAE,CAAC,OAAO,CAC9B;;gEAEwD,CACzD,CAAC,GAAG,CAAC,UAAU,CAAC,aAAa,EAAE,UAAU,CAAC,kBAAkB,CAAoC,CAAC;YAClG,IAAI,QAAQ,EAAE,CAAC;gBACb,IAAI,QAAQ,CAAC,MAAM,KAAK,aAAa;oBAAE,MAAM,IAAI,kCAAkC,EAAE,CAAC;gBACtF,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;gBACvB,OAAO,UAAU,CAAC;YACpB,CAAC;YAED,MAAM,QAAQ,GAAG,IAAI,CAAC,EAAE,CAAC,OAAO,CAC9B;;;;;+EAKuE,CACxE,CAAC,GAAG,CACH,UAAU,CAAC,aAAa,EAAE,UAAU,CAAC,OAAO,EAAE,UAAU,CAAC,kBAAkB,EAC3E,UAAU,CAAC,YAAY,EAAE,UAAU,CAAC,QAAQ,EAAE,UAAU,CAAC,SAAS,EAAE,UAAU,CAAC,UAAU,EACzF,UAAU,CAAC,UAAU,EAAE,UAAU,CAAC,SAAS,EAAE,UAAU,CAAC,YAAY,EAAE,UAAU,CAAC,MAAM,EACvF,UAAU,CAAC,SAAS,EAAE,UAAU,CAAC,UAAU,EAAE,UAAU,CAAC,WAAW,EAAE,UAAU,CAAC,cAAc,EAC9F,UAAU,CAAC,kBAAkB,EAAE,UAAU,CAAC,cAAc,EAAE,UAAU,CAAC,UAAU,EAAE,aAAa,CAC/F,CAAC;YACF,MAAM,eAAe,GAAG,MAAM,CAAC,QAAQ,CAAC,eAAe,CAAC,CAAC;YACzD,MAAM,WAAW,GAAG,IAAI,CAAC,EAAE,CAAC,OAAO,CACjC;;;oDAG4C,CAC7C,CAAC;YACF,KAAK,MAAM,KAAK,IAAI,UAAU,CAAC,WAAW,EAAE,CAAC;gBAC3C,WAAW,CAAC,GAAG,CACb,eAAe,EAAE,UAAU,CAAC,aAAa,EAAE,KAAK,CAAC,kBAAkB,EACnE,KAAK,CAAC,iBAAiB,EAAE,KAAK,CAAC,KAAK,EAAE,KAAK,CAAC,WAAW,EAAE,KAAK,CAAC,YAAY,EAC3E,KAAK,CAAC,eAAe,EAAE,KAAK,CAAC,mBAAmB,EAAE,KAAK,CAAC,eAAe,EAAE,KAAK,CAAC,WAAW,CAC3F,CAAC;YACJ,CAAC;YACD,IAAI,CAAC,EAAE,CAAC,OAAO,CACb;;gDAEwC,CACzC,CAAC,GAAG,CAAC,UAAU,CAAC,aAAa,EAAE,UAAU,CAAC,SAAS,CAAC,CAAC;YACtD,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;YACvB,OAAO,UAAU,CAAC;QACpB,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;YACzB,MAAM,KAAK,CAAC;QACd,CAAC;IACH,CAAC;IAED,KAAK,CAAC,iBAAiB,CAAC,KAA6B;QACnD,MAAM,aAAa,GAAG,eAAe,CAAC,KAAK,CAAC,aAAa,EAAE,eAAe,CAAC,CAAC;QAC5E,MAAM,MAAM,GAAG,eAAe,CAAC,KAAK,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC;QACvD,MAAM,UAAU,GAAG,UAAU,CAAC,KAAK,CAAC,UAAU,EAAE,YAAY,CAAC,CAAC;QAC9D,MAAM,qBAAqB,GAAG,eAAe,CAAC,KAAK,CAAC,qBAAqB,EAAE,uBAAuB,CAAC,CAAC;QACpG,MAAM,MAAM,GAAG,MAAM,CAAC,GAAG,KAAK,CAAC,OAAO,IAAI,aAAa,IAAI,MAAM,IAAI,qBAAqB,EAAE,CAAC,CAAC;QAC9F,MAAM,MAAM,GAAG,IAAI,CAAC,EAAE,CAAC,OAAO,CAC5B;;;uDAGiD,CAClD,CAAC,GAAG,CAAC,aAAa,EAAE,KAAK,CAAC,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,CAAC,CAAC;QAChE,OAAO,MAAM,CAAC,OAAO,KAAK,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,UAAU,CAAC;IACxD,CAAC;IAED,KAAK;QACH,IAAI,CAAC,EAAE,CAAC,KAAK,EAAE,CAAC;IAClB,CAAC;IAEO,gBAAgB;QACtB,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;KAiEZ,CAAC,CAAC;IACL,CAAC;CACF"}
@@ -0,0 +1,27 @@
1
+ import type { AgentTaskContext } from '../core/AgentAdapter.js';
2
+ import type { ContextCompressionMode } from '@winmatrix/protocol';
3
+ export interface ContextBudgetDiagnostics {
4
+ originalInputChars: number;
5
+ finalInputChars: number;
6
+ originalSystemChars?: number;
7
+ finalSystemChars?: number;
8
+ action: 'none' | 'truncated_input' | 'truncated_system' | 'truncated_both';
9
+ compression: ContextCompressionMode;
10
+ }
11
+ export interface ContextBudgetResult {
12
+ input: string;
13
+ systemPrompt?: string;
14
+ diagnostics?: ContextBudgetDiagnostics;
15
+ }
16
+ /**
17
+ * 对当前 turn 的 input / systemPrompt 应用上下文预算。
18
+ *
19
+ * 规则:
20
+ * - 无 budget 时原样返回。
21
+ * - compression 缺省为 'truncate_head'(body-first,与 octo 实测模式一致)。
22
+ * - 先按 maxSystemChars 截 systemPrompt,再按 maxInputChars 截 input。
23
+ * - 若指定 maxContextChars,则优先继续压缩 input,必要时再压缩 systemPrompt。
24
+ * - 返回 diagnostics 只含字符数与 action,不含正文。
25
+ */
26
+ export declare function applyContextBudget(task: AgentTaskContext): ContextBudgetResult;
27
+ //# sourceMappingURL=contextBudget.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"contextBudget.d.ts","sourceRoot":"","sources":["../../src/utils/contextBudget.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAChE,OAAO,KAAK,EAAiB,sBAAsB,EAAE,MAAM,qBAAqB,CAAC;AAEjF,MAAM,WAAW,wBAAwB;IACvC,kBAAkB,EAAE,MAAM,CAAC;IAC3B,eAAe,EAAE,MAAM,CAAC;IACxB,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,MAAM,EAAE,MAAM,GAAG,iBAAiB,GAAG,kBAAkB,GAAG,gBAAgB,CAAC;IAC3E,WAAW,EAAE,sBAAsB,CAAC;CACrC;AAED,MAAM,WAAW,mBAAmB;IAClC,KAAK,EAAE,MAAM,CAAC;IACd,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,WAAW,CAAC,EAAE,wBAAwB,CAAC;CACxC;AAcD;;;;;;;;;GASG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,gBAAgB,GAAG,mBAAmB,CAqE9E"}
@@ -0,0 +1,86 @@
1
+ function truncateText(text, maxChars, mode) {
2
+ if (text.length <= maxChars)
3
+ return text;
4
+ if (mode === 'truncate_tail') {
5
+ return text.slice(0, maxChars);
6
+ }
7
+ if (mode === 'none') {
8
+ return text;
9
+ }
10
+ // default / truncate_head: preserve the body/tail, drop the oldest prefix from the front.
11
+ return text.slice(-maxChars);
12
+ }
13
+ /**
14
+ * 对当前 turn 的 input / systemPrompt 应用上下文预算。
15
+ *
16
+ * 规则:
17
+ * - 无 budget 时原样返回。
18
+ * - compression 缺省为 'truncate_head'(body-first,与 octo 实测模式一致)。
19
+ * - 先按 maxSystemChars 截 systemPrompt,再按 maxInputChars 截 input。
20
+ * - 若指定 maxContextChars,则优先继续压缩 input,必要时再压缩 systemPrompt。
21
+ * - 返回 diagnostics 只含字符数与 action,不含正文。
22
+ */
23
+ export function applyContextBudget(task) {
24
+ const budget = task.contextBudget;
25
+ if (!budget) {
26
+ return { input: task.input, systemPrompt: task.systemPrompt };
27
+ }
28
+ const compression = budget.compression ?? 'truncate_head';
29
+ const originalInputChars = task.input.length;
30
+ const originalSystemChars = task.systemPrompt?.length;
31
+ let systemPrompt = task.systemPrompt;
32
+ let input = task.input;
33
+ if (systemPrompt !== undefined && budget.maxSystemChars !== undefined) {
34
+ systemPrompt = truncateText(systemPrompt, budget.maxSystemChars, compression);
35
+ }
36
+ if (budget.maxInputChars !== undefined) {
37
+ input = truncateText(input, budget.maxInputChars, compression);
38
+ }
39
+ if (budget.maxContextChars !== undefined) {
40
+ const systemLen = systemPrompt?.length ?? 0;
41
+ const inputLen = input.length;
42
+ const total = systemLen + inputLen;
43
+ if (total > budget.maxContextChars) {
44
+ const remaining = Math.max(0, budget.maxContextChars - systemLen);
45
+ if (remaining < inputLen) {
46
+ input = truncateText(input, remaining, compression);
47
+ }
48
+ const newTotal = (systemPrompt?.length ?? 0) + input.length;
49
+ if (newTotal > budget.maxContextChars) {
50
+ const systemRemaining = Math.max(0, budget.maxContextChars - input.length);
51
+ if (systemPrompt !== undefined && systemRemaining < systemPrompt.length) {
52
+ systemPrompt = truncateText(systemPrompt, systemRemaining, compression);
53
+ }
54
+ }
55
+ }
56
+ }
57
+ const finalInputChars = input.length;
58
+ const finalSystemChars = systemPrompt?.length;
59
+ let action = 'none';
60
+ const inputTruncated = finalInputChars < originalInputChars;
61
+ const systemTruncated = originalSystemChars !== undefined &&
62
+ finalSystemChars !== undefined &&
63
+ finalSystemChars < originalSystemChars;
64
+ if (inputTruncated && systemTruncated)
65
+ action = 'truncated_both';
66
+ else if (inputTruncated)
67
+ action = 'truncated_input';
68
+ else if (systemTruncated)
69
+ action = 'truncated_system';
70
+ if (action === 'none') {
71
+ return { input, systemPrompt };
72
+ }
73
+ return {
74
+ input,
75
+ systemPrompt,
76
+ diagnostics: {
77
+ originalInputChars,
78
+ finalInputChars,
79
+ ...(originalSystemChars !== undefined ? { originalSystemChars } : {}),
80
+ ...(finalSystemChars !== undefined ? { finalSystemChars } : {}),
81
+ action,
82
+ compression,
83
+ },
84
+ };
85
+ }
86
+ //# sourceMappingURL=contextBudget.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"contextBudget.js","sourceRoot":"","sources":["../../src/utils/contextBudget.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,38 @@
1
+ /**
2
+ * 环境变量凭据剥离(harden-external-computer-daemon P1,design D3)。
3
+ *
4
+ * ## 背景
5
+ * daemon 跑在用户自有机器上,adapter 旧实现把 daemon 进程的全部环境变量
6
+ * (`{ ...process.env, ...(task.env ?? {}) }`)原样传给外部 CLI——用户环境里的
7
+ * ANTHROPIC_API_KEY、各类 SDK token、内部凭据全部被第三方进程继承。
8
+ *
9
+ * ## 策略:先剥后显式加
10
+ * 1. 剥离一切"长得像凭据"的环境变量(命名模式匹配,见下);
11
+ * 2. 叠加 task.env 中显式声明的条目(要传 key 必须在任务配置里写明,
12
+ * 不靠"顺便继承");
13
+ * 3. 剥离动作返回 strippedKeys 供审计日志使用(只记 key 名,不记值)。
14
+ *
15
+ * 与 workstation Pod 侧 env-sanitizer 的差异:Pod 环境由平台控制,凭据本就
16
+ * 是平台下发的,故 supervisor 只过滤内部标记;daemon 环境是用户的,必须
17
+ * 剥离凭据。
18
+ *
19
+ * 回退开关:WINMATRIX_DAEMON_ENV_PASSTHROUGH=true 时恢复旧的全量继承行为
20
+ * (迁移窗口用,默认 false)。
21
+ */
22
+ /** 判断 key 是否为凭据形态(应剥离)。 */
23
+ export declare function isCredentialEnvKey(key: string): boolean;
24
+ /** 回退开关:恢复旧的全量继承行为(迁移窗口用)。 */
25
+ export declare function isEnvPassthroughEnabled(baseEnv?: NodeJS.ProcessEnv): boolean;
26
+ export interface SanitizedEnv {
27
+ env: Record<string, string>;
28
+ /** 被剥离的 key 名(审计用;绝不包含值)。 */
29
+ strippedKeys: string[];
30
+ }
31
+ /**
32
+ * 构建传给外部 CLI 子进程的清洁环境。
33
+ *
34
+ * @param overrides 任务显式声明的 env(task.env / launchSpec.env),最后叠加
35
+ * @param baseEnv 基础环境(默认 process.env,可注入测试)
36
+ */
37
+ export declare function sanitizeTaskEnv(overrides?: Record<string, string | undefined>, baseEnv?: NodeJS.ProcessEnv): SanitizedEnv;
38
+ //# sourceMappingURL=envSanitizer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"envSanitizer.d.ts","sourceRoot":"","sources":["../../src/utils/envSanitizer.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AA6BH,2BAA2B;AAC3B,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAIvD;AAED,8BAA8B;AAC9B,wBAAgB,uBAAuB,CAAC,OAAO,GAAE,MAAM,CAAC,UAAwB,GAAG,OAAO,CAEzF;AAED,MAAM,WAAW,YAAY;IAC3B,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC5B,6BAA6B;IAC7B,YAAY,EAAE,MAAM,EAAE,CAAC;CACxB;AAED;;;;;GAKG;AACH,wBAAgB,eAAe,CAC7B,SAAS,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,EAC9C,OAAO,GAAE,MAAM,CAAC,UAAwB,GACvC,YAAY,CA2Bd"}
@@ -0,0 +1,94 @@
1
+ /**
2
+ * 环境变量凭据剥离(harden-external-computer-daemon P1,design D3)。
3
+ *
4
+ * ## 背景
5
+ * daemon 跑在用户自有机器上,adapter 旧实现把 daemon 进程的全部环境变量
6
+ * (`{ ...process.env, ...(task.env ?? {}) }`)原样传给外部 CLI——用户环境里的
7
+ * ANTHROPIC_API_KEY、各类 SDK token、内部凭据全部被第三方进程继承。
8
+ *
9
+ * ## 策略:先剥后显式加
10
+ * 1. 剥离一切"长得像凭据"的环境变量(命名模式匹配,见下);
11
+ * 2. 叠加 task.env 中显式声明的条目(要传 key 必须在任务配置里写明,
12
+ * 不靠"顺便继承");
13
+ * 3. 剥离动作返回 strippedKeys 供审计日志使用(只记 key 名,不记值)。
14
+ *
15
+ * 与 workstation Pod 侧 env-sanitizer 的差异:Pod 环境由平台控制,凭据本就
16
+ * 是平台下发的,故 supervisor 只过滤内部标记;daemon 环境是用户的,必须
17
+ * 剥离凭据。
18
+ *
19
+ * 回退开关:WINMATRIX_DAEMON_ENV_PASSTHROUGH=true 时恢复旧的全量继承行为
20
+ * (迁移窗口用,默认 false)。
21
+ */
22
+ /** 凭据命名的通用模式:片段为 KEY/TOKEN/SECRET/PASSWORD/CREDENTIAL(S) 等。 */
23
+ const CREDENTIAL_NAME_PATTERN = /(^|_)(API_?KEY|ACCESS_?KEY|SECRET_?KEY|TOKEN|SECRET|PASSWORD|PASSWD|CREDENTIALS?|AUTH_?KEY|PRIVATE_?KEY)(_|$)/i;
24
+ /**
25
+ * 显式凭据前缀清单(模式之外的已知凭据变量,如 AWS_SESSION_TOKEN 已被
26
+ * 模式覆盖,这里补模式覆盖不到的厂商变量)。
27
+ */
28
+ const CREDENTIAL_EXACT_OR_PREFIX = [
29
+ /^ANTHROPIC_/i,
30
+ /^OPENAI_/i,
31
+ /^CLAUDE_CODE_OAUTH/i,
32
+ /^GH_TOKEN$/i,
33
+ /^GITHUB_/i,
34
+ /^GITLAB_/i,
35
+ /^NPM_TOKEN/i,
36
+ /^DOCKER_AUTH/i,
37
+ /^KUBECONFIG$/i, // 指向 kubeconfig 文件路径,内含集群凭据
38
+ /^AWS_/i,
39
+ /^AZURE_/i,
40
+ /^GCLOUD_/i,
41
+ /^GOOGLE_APPLICATION_CREDENTIALS$/i,
42
+ ];
43
+ /** 即使匹配凭据模式也必须保留的运行必需变量(当前为空,模式不会误伤 PATH/HOME 等)。 */
44
+ const ALWAYS_KEEP = new Set([]);
45
+ /** 判断 key 是否为凭据形态(应剥离)。 */
46
+ export function isCredentialEnvKey(key) {
47
+ if (ALWAYS_KEEP.has(key))
48
+ return false;
49
+ if (CREDENTIAL_NAME_PATTERN.test(key))
50
+ return true;
51
+ return CREDENTIAL_EXACT_OR_PREFIX.some((pattern) => pattern.test(key));
52
+ }
53
+ /** 回退开关:恢复旧的全量继承行为(迁移窗口用)。 */
54
+ export function isEnvPassthroughEnabled(baseEnv = process.env) {
55
+ return baseEnv.WINMATRIX_DAEMON_ENV_PASSTHROUGH?.trim() === 'true';
56
+ }
57
+ /**
58
+ * 构建传给外部 CLI 子进程的清洁环境。
59
+ *
60
+ * @param overrides 任务显式声明的 env(task.env / launchSpec.env),最后叠加
61
+ * @param baseEnv 基础环境(默认 process.env,可注入测试)
62
+ */
63
+ export function sanitizeTaskEnv(overrides, baseEnv = process.env) {
64
+ if (isEnvPassthroughEnabled(baseEnv)) {
65
+ const env = {};
66
+ for (const [key, value] of Object.entries(baseEnv)) {
67
+ if (value !== undefined)
68
+ env[key] = value;
69
+ }
70
+ for (const [key, value] of Object.entries(overrides ?? {})) {
71
+ if (value !== undefined)
72
+ env[key] = value;
73
+ }
74
+ return { env, strippedKeys: [] };
75
+ }
76
+ const env = {};
77
+ const strippedKeys = [];
78
+ for (const [key, value] of Object.entries(baseEnv)) {
79
+ if (value === undefined)
80
+ continue;
81
+ if (isCredentialEnvKey(key)) {
82
+ strippedKeys.push(key);
83
+ continue;
84
+ }
85
+ env[key] = value;
86
+ }
87
+ // 显式声明的条目最后叠加(允许显式传凭据——这是"写明"的通道)
88
+ for (const [key, value] of Object.entries(overrides ?? {})) {
89
+ if (value !== undefined)
90
+ env[key] = value;
91
+ }
92
+ return { env, strippedKeys };
93
+ }
94
+ //# sourceMappingURL=envSanitizer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"envSanitizer.js","sourceRoot":"","sources":["../../src/utils/envSanitizer.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAEH,+DAA+D;AAC/D,MAAM,uBAAuB,GAC3B,gHAAgH,CAAC;AAEnH;;;GAGG;AACH,MAAM,0BAA0B,GAAG;IACjC,cAAc;IACd,WAAW;IACX,qBAAqB;IACrB,aAAa;IACb,WAAW;IACX,WAAW;IACX,aAAa;IACb,eAAe;IACf,eAAe,EAAE,4BAA4B;IAC7C,QAAQ;IACR,UAAU;IACV,WAAW;IACX,mCAAmC;CACpC,CAAC;AAEF,qDAAqD;AACrD,MAAM,WAAW,GAAG,IAAI,GAAG,CAAS,EAAE,CAAC,CAAC;AAExC,2BAA2B;AAC3B,MAAM,UAAU,kBAAkB,CAAC,GAAW;IAC5C,IAAI,WAAW,CAAC,GAAG,CAAC,GAAG,CAAC;QAAE,OAAO,KAAK,CAAC;IACvC,IAAI,uBAAuB,CAAC,IAAI,CAAC,GAAG,CAAC;QAAE,OAAO,IAAI,CAAC;IACnD,OAAO,0BAA0B,CAAC,IAAI,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;AACzE,CAAC;AAED,8BAA8B;AAC9B,MAAM,UAAU,uBAAuB,CAAC,UAA6B,OAAO,CAAC,GAAG;IAC9E,OAAO,OAAO,CAAC,gCAAgC,EAAE,IAAI,EAAE,KAAK,MAAM,CAAC;AACrE,CAAC;AAQD;;;;;GAKG;AACH,MAAM,UAAU,eAAe,CAC7B,SAA8C,EAC9C,UAA6B,OAAO,CAAC,GAAG;IAExC,IAAI,uBAAuB,CAAC,OAAO,CAAC,EAAE,CAAC;QACrC,MAAM,GAAG,GAA2B,EAAE,CAAC;QACvC,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC;YACnD,IAAI,KAAK,KAAK,SAAS;gBAAE,GAAG,CAAC,GAAG,CAAC,GAAG,KAAK,CAAC;QAC5C,CAAC;QACD,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,SAAS,IAAI,EAAE,CAAC,EAAE,CAAC;YAC3D,IAAI,KAAK,KAAK,SAAS;gBAAE,GAAG,CAAC,GAAG,CAAC,GAAG,KAAK,CAAC;QAC5C,CAAC;QACD,OAAO,EAAE,GAAG,EAAE,YAAY,EAAE,EAAE,EAAE,CAAC;IACnC,CAAC;IAED,MAAM,GAAG,GAA2B,EAAE,CAAC;IACvC,MAAM,YAAY,GAAa,EAAE,CAAC;IAClC,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC;QACnD,IAAI,KAAK,KAAK,SAAS;YAAE,SAAS;QAClC,IAAI,kBAAkB,CAAC,GAAG,CAAC,EAAE,CAAC;YAC5B,YAAY,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;YACvB,SAAS;QACX,CAAC;QACD,GAAG,CAAC,GAAG,CAAC,GAAG,KAAK,CAAC;IACnB,CAAC;IACD,kCAAkC;IAClC,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,SAAS,IAAI,EAAE,CAAC,EAAE,CAAC;QAC3D,IAAI,KAAK,KAAK,SAAS;YAAE,GAAG,CAAC,GAAG,CAAC,GAAG,KAAK,CAAC;IAC5C,CAAC;IACD,OAAO,EAAE,GAAG,EAAE,YAAY,EAAE,CAAC;AAC/B,CAAC"}
@@ -0,0 +1,26 @@
1
+ /**
2
+ * Adapter 错误统一分类(harden-external-computer-daemon P2.2/D6)
3
+ *
4
+ * 各 adapter 在产出 error 事件时附带 code + retryable,上层(daemon / server)
5
+ * 不再靠 regex 猜 stderr 来决定重试/降级策略。regex 分类仅作为 adapter 未
6
+ * 显式给出 code 时的兜底。
7
+ */
8
+ export declare const ADAPTER_ERROR_CODES: readonly ["timeout", "cancelled", "cli_crash", "auth_failed", "session_expired", "rate_limited", "network", "unknown"];
9
+ export type AdapterErrorCode = (typeof ADAPTER_ERROR_CODES)[number];
10
+ export interface ClassifiedAdapterError {
11
+ code: AdapterErrorCode;
12
+ retryable: boolean;
13
+ }
14
+ export interface ClassifyInput {
15
+ message?: string;
16
+ exitCode?: number | null;
17
+ signal?: string | null;
18
+ /** 取消信号已触发(AbortSignal.aborted)——优先判 cancelled */
19
+ aborted?: boolean;
20
+ }
21
+ /**
22
+ * 将一次失败的原始信号(消息 / 退出码 / 取消标记)归类为统一错误码。
23
+ * 分类优先级:aborted → 消息 pattern → 退出码 → unknown。
24
+ */
25
+ export declare function classifyAdapterError(input: ClassifyInput): ClassifiedAdapterError;
26
+ //# sourceMappingURL=errorClassification.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errorClassification.d.ts","sourceRoot":"","sources":["../../src/utils/errorClassification.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,eAAO,MAAM,mBAAmB,wHAStB,CAAC;AAEX,MAAM,MAAM,gBAAgB,GAAG,CAAC,OAAO,mBAAmB,CAAC,CAAC,MAAM,CAAC,CAAC;AAEpE,MAAM,WAAW,sBAAsB;IACrC,IAAI,EAAE,gBAAgB,CAAC;IACvB,SAAS,EAAE,OAAO,CAAC;CACpB;AAED,MAAM,WAAW,aAAa;IAC5B,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,kDAAkD;IAClD,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAcD;;;GAGG;AACH,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,aAAa,GAAG,sBAAsB,CAkBjF"}
@@ -0,0 +1,52 @@
1
+ /**
2
+ * Adapter 错误统一分类(harden-external-computer-daemon P2.2/D6)
3
+ *
4
+ * 各 adapter 在产出 error 事件时附带 code + retryable,上层(daemon / server)
5
+ * 不再靠 regex 猜 stderr 来决定重试/降级策略。regex 分类仅作为 adapter 未
6
+ * 显式给出 code 时的兜底。
7
+ */
8
+ export const ADAPTER_ERROR_CODES = [
9
+ 'timeout',
10
+ 'cancelled',
11
+ 'cli_crash',
12
+ 'auth_failed',
13
+ 'session_expired',
14
+ 'rate_limited',
15
+ 'network',
16
+ 'unknown',
17
+ ];
18
+ const PATTERNS = [
19
+ { code: 'session_expired', retryable: true, pattern: /session .*(not found|not exist|does not exist|expired|invalid)/i },
20
+ { code: 'session_expired', retryable: true, pattern: /(invalid|unknown|missing) .*session/i },
21
+ { code: 'session_expired', retryable: true, pattern: /resume .*(not found|expired|invalid)/i },
22
+ { code: 'session_expired', retryable: true, pattern: /thread .*(not found|not exist|does not exist|expired|invalid)/i },
23
+ { code: 'session_expired', retryable: true, pattern: /no conversation|conversation .*not found/i },
24
+ { code: 'auth_failed', retryable: false, pattern: /(401|403)\b|unauthorized|forbidden|authentication|invalid .*api.?key|api.?key .*invalid|permission denied/i },
25
+ { code: 'rate_limited', retryable: true, pattern: /\b429\b|rate.?limit|too many requests|quota exceeded|usage limit/i },
26
+ { code: 'network', retryable: true, pattern: /ECONNRESET|ECONNREFUSED|ETIMEDOUT|ENOTFOUND|EAI_AGAIN|ECONNABORTED|socket hang up|fetch failed|network error|connection refused/i },
27
+ { code: 'timeout', retryable: true, pattern: /\btimeout\b|timed out|deadline exceeded/i },
28
+ ];
29
+ /**
30
+ * 将一次失败的原始信号(消息 / 退出码 / 取消标记)归类为统一错误码。
31
+ * 分类优先级:aborted → 消息 pattern → 退出码 → unknown。
32
+ */
33
+ export function classifyAdapterError(input) {
34
+ if (input.aborted) {
35
+ return { code: 'cancelled', retryable: false };
36
+ }
37
+ const message = input.message ?? '';
38
+ for (const { code, retryable, pattern } of PATTERNS) {
39
+ if (pattern.test(message)) {
40
+ return { code, retryable };
41
+ }
42
+ }
43
+ if (input.signal) {
44
+ // 被信号杀死但未标记 aborted(如外部 OOM killer / 手动 kill)
45
+ return { code: 'cli_crash', retryable: false };
46
+ }
47
+ if (typeof input.exitCode === 'number' && input.exitCode !== 0) {
48
+ return { code: 'cli_crash', retryable: false };
49
+ }
50
+ return { code: 'unknown', retryable: false };
51
+ }
52
+ //# sourceMappingURL=errorClassification.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errorClassification.js","sourceRoot":"","sources":["../../src/utils/errorClassification.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,MAAM,CAAC,MAAM,mBAAmB,GAAG;IACjC,SAAS;IACT,WAAW;IACX,WAAW;IACX,aAAa;IACb,iBAAiB;IACjB,cAAc;IACd,SAAS;IACT,SAAS;CACD,CAAC;AAiBX,MAAM,QAAQ,GAA2E;IACvF,EAAE,IAAI,EAAE,iBAAiB,EAAE,SAAS,EAAE,IAAI,EAAE,OAAO,EAAE,iEAAiE,EAAE;IACxH,EAAE,IAAI,EAAE,iBAAiB,EAAE,SAAS,EAAE,IAAI,EAAE,OAAO,EAAE,sCAAsC,EAAE;IAC7F,EAAE,IAAI,EAAE,iBAAiB,EAAE,SAAS,EAAE,IAAI,EAAE,OAAO,EAAE,uCAAuC,EAAE;IAC9F,EAAE,IAAI,EAAE,iBAAiB,EAAE,SAAS,EAAE,IAAI,EAAE,OAAO,EAAE,gEAAgE,EAAE;IACvH,EAAE,IAAI,EAAE,iBAAiB,EAAE,SAAS,EAAE,IAAI,EAAE,OAAO,EAAE,2CAA2C,EAAE;IAClG,EAAE,IAAI,EAAE,aAAa,EAAE,SAAS,EAAE,KAAK,EAAE,OAAO,EAAE,4GAA4G,EAAE;IAChK,EAAE,IAAI,EAAE,cAAc,EAAE,SAAS,EAAE,IAAI,EAAE,OAAO,EAAE,mEAAmE,EAAE;IACvH,EAAE,IAAI,EAAE,SAAS,EAAE,SAAS,EAAE,IAAI,EAAE,OAAO,EAAE,kIAAkI,EAAE;IACjL,EAAE,IAAI,EAAE,SAAS,EAAE,SAAS,EAAE,IAAI,EAAE,OAAO,EAAE,0CAA0C,EAAE;CAC1F,CAAC;AAEF;;;GAGG;AACH,MAAM,UAAU,oBAAoB,CAAC,KAAoB;IACvD,IAAI,KAAK,CAAC,OAAO,EAAE,CAAC;QAClB,OAAO,EAAE,IAAI,EAAE,WAAW,EAAE,SAAS,EAAE,KAAK,EAAE,CAAC;IACjD,CAAC;IACD,MAAM,OAAO,GAAG,KAAK,CAAC,OAAO,IAAI,EAAE,CAAC;IACpC,KAAK,MAAM,EAAE,IAAI,EAAE,SAAS,EAAE,OAAO,EAAE,IAAI,QAAQ,EAAE,CAAC;QACpD,IAAI,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC;YAC1B,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,CAAC;QAC7B,CAAC;IACH,CAAC;IACD,IAAI,KAAK,CAAC,MAAM,EAAE,CAAC;QACjB,8CAA8C;QAC9C,OAAO,EAAE,IAAI,EAAE,WAAW,EAAE,SAAS,EAAE,KAAK,EAAE,CAAC;IACjD,CAAC;IACD,IAAI,OAAO,KAAK,CAAC,QAAQ,KAAK,QAAQ,IAAI,KAAK,CAAC,QAAQ,KAAK,CAAC,EAAE,CAAC;QAC/D,OAAO,EAAE,IAAI,EAAE,WAAW,EAAE,SAAS,EAAE,KAAK,EAAE,CAAC;IACjD,CAAC;IACD,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,SAAS,EAAE,KAAK,EAAE,CAAC;AAC/C,CAAC"}
@@ -0,0 +1,10 @@
1
+ import { validateMcpUrl, mcpUrlFailureReasonToCode } from '@winmatrix/protocol';
2
+ export { validateMcpUrl, mcpUrlFailureReasonToCode };
3
+ /**
4
+ * 校验任务级 MCP Bridge URL;失败时抛出统一前缀错误。
5
+ *
6
+ * 错误消息格式:`TASK_BOUND_TOOL_PROXY_REQUIRED: MCP_BRIDGE_URL_<reason>`,
7
+ * 与现有 adapter(CodexAdapter / OpenClawAdapter)的 fail-fast 行为一致。
8
+ */
9
+ export declare function validateMcpBridgeUrl(url: string | undefined): asserts url is string;
10
+ //# sourceMappingURL=mcpUrlValidation.d.ts.map