@symerian/symi 3.0.18 → 3.0.19

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 (116) hide show
  1. package/dist/build-info.json +3 -3
  2. package/dist/canvas-host/a2ui/.bundle.hash +1 -1
  3. package/package.json +1 -1
  4. package/extensions/copilot-proxy/README.md +0 -24
  5. package/extensions/copilot-proxy/index.ts +0 -154
  6. package/extensions/copilot-proxy/node_modules/.bin/symi +0 -21
  7. package/extensions/copilot-proxy/package.json +0 -15
  8. package/extensions/copilot-proxy/symi.plugin.json +0 -9
  9. package/extensions/device-pair/index.ts +0 -642
  10. package/extensions/device-pair/symi.plugin.json +0 -20
  11. package/extensions/diagnostics-otel/index.ts +0 -15
  12. package/extensions/diagnostics-otel/node_modules/.bin/acorn +0 -21
  13. package/extensions/diagnostics-otel/node_modules/.bin/symi +0 -21
  14. package/extensions/diagnostics-otel/package.json +0 -27
  15. package/extensions/diagnostics-otel/src/service.test.ts +0 -290
  16. package/extensions/diagnostics-otel/src/service.ts +0 -666
  17. package/extensions/diagnostics-otel/symi.plugin.json +0 -8
  18. package/extensions/google-antigravity-auth/README.md +0 -24
  19. package/extensions/google-antigravity-auth/index.ts +0 -424
  20. package/extensions/google-antigravity-auth/node_modules/.bin/symi +0 -21
  21. package/extensions/google-antigravity-auth/package.json +0 -15
  22. package/extensions/google-antigravity-auth/symi.plugin.json +0 -9
  23. package/extensions/google-gemini-cli-auth/README.md +0 -35
  24. package/extensions/google-gemini-cli-auth/index.ts +0 -75
  25. package/extensions/google-gemini-cli-auth/node_modules/.bin/symi +0 -21
  26. package/extensions/google-gemini-cli-auth/oauth.test.ts +0 -162
  27. package/extensions/google-gemini-cli-auth/oauth.ts +0 -636
  28. package/extensions/google-gemini-cli-auth/package.json +0 -15
  29. package/extensions/google-gemini-cli-auth/symi.plugin.json +0 -9
  30. package/extensions/learning-loop/index.ts +0 -159
  31. package/extensions/learning-loop/node_modules/.bin/symi +0 -21
  32. package/extensions/learning-loop/package.json +0 -18
  33. package/extensions/learning-loop/src/analytics/gateway-methods.ts +0 -230
  34. package/extensions/learning-loop/src/analytics/metrics-aggregator.ts +0 -153
  35. package/extensions/learning-loop/src/capture/run-tracker.ts +0 -181
  36. package/extensions/learning-loop/src/capture/serializer.ts +0 -74
  37. package/extensions/learning-loop/src/db.ts +0 -583
  38. package/extensions/learning-loop/src/feedback/explicit-feedback.ts +0 -58
  39. package/extensions/learning-loop/src/feedback/implicit-signals.ts +0 -89
  40. package/extensions/learning-loop/src/graph/edge-inference.ts +0 -189
  41. package/extensions/learning-loop/src/graph/graph-retrieval.ts +0 -144
  42. package/extensions/learning-loop/src/graph/graph-store.ts +0 -183
  43. package/extensions/learning-loop/src/hooks.ts +0 -244
  44. package/extensions/learning-loop/src/injection/cache.ts +0 -73
  45. package/extensions/learning-loop/src/injection/context-injector.ts +0 -104
  46. package/extensions/learning-loop/src/injection/prompt-builder.ts +0 -43
  47. package/extensions/learning-loop/src/learning/embedding-bridge.ts +0 -54
  48. package/extensions/learning-loop/src/learning/learning-extractor.ts +0 -217
  49. package/extensions/learning-loop/src/learning/learning-store.ts +0 -158
  50. package/extensions/learning-loop/src/learning/retrieval.ts +0 -87
  51. package/extensions/learning-loop/src/math/confidence-intervals.ts +0 -62
  52. package/extensions/learning-loop/src/math/ewma.ts +0 -51
  53. package/extensions/learning-loop/src/math/weighted-scorer.ts +0 -42
  54. package/extensions/learning-loop/src/schema.ts +0 -176
  55. package/extensions/learning-loop/src/scoring/normalization.ts +0 -32
  56. package/extensions/learning-loop/src/scoring/quality-engine.ts +0 -78
  57. package/extensions/learning-loop/src/scoring/signal-extractors.ts +0 -155
  58. package/extensions/learning-loop/src/test/context-injector.test.ts +0 -142
  59. package/extensions/learning-loop/src/test/fixes.test.ts +0 -1286
  60. package/extensions/learning-loop/src/test/graph.test.ts +0 -711
  61. package/extensions/learning-loop/src/test/integration.test.ts +0 -312
  62. package/extensions/learning-loop/src/test/learning-store.test.ts +0 -191
  63. package/extensions/learning-loop/src/test/math.test.ts +0 -148
  64. package/extensions/learning-loop/src/test/quality-engine.test.ts +0 -231
  65. package/extensions/learning-loop/src/test/run-tracker.test.ts +0 -143
  66. package/extensions/learning-loop/src/types.ts +0 -281
  67. package/extensions/learning-loop/symi.plugin.json +0 -46
  68. package/extensions/llm-task/README.md +0 -97
  69. package/extensions/llm-task/index.ts +0 -6
  70. package/extensions/llm-task/package.json +0 -12
  71. package/extensions/llm-task/src/llm-task-tool.test.ts +0 -138
  72. package/extensions/llm-task/src/llm-task-tool.ts +0 -249
  73. package/extensions/llm-task/symi.plugin.json +0 -21
  74. package/extensions/memory-lancedb/config.ts +0 -161
  75. package/extensions/memory-lancedb/index.test.ts +0 -330
  76. package/extensions/memory-lancedb/index.ts +0 -670
  77. package/extensions/memory-lancedb/node_modules/.bin/arrow2csv +0 -21
  78. package/extensions/memory-lancedb/node_modules/.bin/openai +0 -21
  79. package/extensions/memory-lancedb/node_modules/.bin/symi +0 -21
  80. package/extensions/memory-lancedb/package.json +0 -20
  81. package/extensions/memory-lancedb/symi.plugin.json +0 -71
  82. package/extensions/minimax-portal-auth/README.md +0 -33
  83. package/extensions/minimax-portal-auth/index.ts +0 -161
  84. package/extensions/minimax-portal-auth/node_modules/.bin/symi +0 -21
  85. package/extensions/minimax-portal-auth/oauth.ts +0 -247
  86. package/extensions/minimax-portal-auth/package.json +0 -15
  87. package/extensions/minimax-portal-auth/symi.plugin.json +0 -9
  88. package/extensions/model-equalizer/index.ts +0 -80
  89. package/extensions/model-equalizer/skills/model-equalizer/SKILL.md +0 -58
  90. package/extensions/model-equalizer/src/detection.ts +0 -62
  91. package/extensions/model-equalizer/src/enhancer.ts +0 -63
  92. package/extensions/model-equalizer/src/test/detection.test.ts +0 -218
  93. package/extensions/model-equalizer/src/test/enhancer.test.ts +0 -137
  94. package/extensions/model-equalizer/src/test/integration.test.ts +0 -185
  95. package/extensions/model-equalizer/src/types.ts +0 -24
  96. package/extensions/model-equalizer/symi.plugin.json +0 -12
  97. package/extensions/phone-control/index.ts +0 -421
  98. package/extensions/phone-control/symi.plugin.json +0 -10
  99. package/extensions/pipeline/README.md +0 -75
  100. package/extensions/pipeline/SKILL.md +0 -97
  101. package/extensions/pipeline/index.ts +0 -18
  102. package/extensions/pipeline/package.json +0 -11
  103. package/extensions/pipeline/src/pipeline-tool.test.ts +0 -345
  104. package/extensions/pipeline/src/pipeline-tool.ts +0 -266
  105. package/extensions/pipeline/src/windows-spawn.test.ts +0 -148
  106. package/extensions/pipeline/src/windows-spawn.ts +0 -193
  107. package/extensions/pipeline/symi.plugin.json +0 -10
  108. package/extensions/qwen-portal-auth/README.md +0 -24
  109. package/extensions/qwen-portal-auth/index.ts +0 -134
  110. package/extensions/qwen-portal-auth/oauth.ts +0 -190
  111. package/extensions/qwen-portal-auth/symi.plugin.json +0 -9
  112. package/extensions/talk-voice/index.ts +0 -150
  113. package/extensions/talk-voice/symi.plugin.json +0 -10
  114. package/extensions/thread-ownership/index.test.ts +0 -180
  115. package/extensions/thread-ownership/index.ts +0 -133
  116. package/extensions/thread-ownership/symi.plugin.json +0 -28
@@ -1,666 +0,0 @@
1
- import { metrics, trace, SpanStatusCode } from "@opentelemetry/api";
2
- import type { SeverityNumber } from "@opentelemetry/api-logs";
3
- import { OTLPLogExporter } from "@opentelemetry/exporter-logs-otlp-http";
4
- import { OTLPMetricExporter } from "@opentelemetry/exporter-metrics-otlp-http";
5
- import { OTLPTraceExporter } from "@opentelemetry/exporter-trace-otlp-http";
6
- import { resourceFromAttributes } from "@opentelemetry/resources";
7
- import { BatchLogRecordProcessor, LoggerProvider } from "@opentelemetry/sdk-logs";
8
- import { PeriodicExportingMetricReader } from "@opentelemetry/sdk-metrics";
9
- import { NodeSDK } from "@opentelemetry/sdk-node";
10
- import { ParentBasedSampler, TraceIdRatioBasedSampler } from "@opentelemetry/sdk-trace-base";
11
- import { ATTR_SERVICE_NAME } from "@opentelemetry/semantic-conventions";
12
- import type { DiagnosticEventPayload, SymiPluginService } from "symi/plugin-sdk";
13
- import { onDiagnosticEvent, registerLogTransport } from "symi/plugin-sdk";
14
-
15
- const DEFAULT_SERVICE_NAME = "symi";
16
-
17
- function normalizeEndpoint(endpoint?: string): string | undefined {
18
- const trimmed = endpoint?.trim();
19
- return trimmed ? trimmed.replace(/\/+$/, "") : undefined;
20
- }
21
-
22
- function resolveOtelUrl(endpoint: string | undefined, path: string): string | undefined {
23
- if (!endpoint) {
24
- return undefined;
25
- }
26
- const endpointWithoutQueryOrFragment = endpoint.split(/[?#]/, 1)[0] ?? endpoint;
27
- if (/\/v1\/(?:traces|metrics|logs)$/i.test(endpointWithoutQueryOrFragment)) {
28
- return endpoint;
29
- }
30
- return `${endpoint}/${path}`;
31
- }
32
-
33
- function resolveSampleRate(value: number | undefined): number | undefined {
34
- if (typeof value !== "number" || !Number.isFinite(value)) {
35
- return undefined;
36
- }
37
- if (value < 0 || value > 1) {
38
- return undefined;
39
- }
40
- return value;
41
- }
42
-
43
- function formatError(err: unknown): string {
44
- if (err instanceof Error) {
45
- return err.stack ?? err.message;
46
- }
47
- if (typeof err === "string") {
48
- return err;
49
- }
50
- try {
51
- return JSON.stringify(err);
52
- } catch {
53
- return String(err);
54
- }
55
- }
56
-
57
- export function createDiagnosticsOtelService(): SymiPluginService {
58
- let sdk: NodeSDK | null = null;
59
- let logProvider: LoggerProvider | null = null;
60
- let stopLogTransport: (() => void) | null = null;
61
- let unsubscribe: (() => void) | null = null;
62
-
63
- return {
64
- id: "diagnostics-otel",
65
- async start(ctx) {
66
- const cfg = ctx.config.diagnostics;
67
- const otel = cfg?.otel;
68
- if (!cfg?.enabled || !otel?.enabled) {
69
- return;
70
- }
71
-
72
- const protocol = otel.protocol ?? process.env.OTEL_EXPORTER_OTLP_PROTOCOL ?? "http/protobuf";
73
- if (protocol !== "http/protobuf") {
74
- ctx.logger.warn(`diagnostics-otel: unsupported protocol ${protocol}`);
75
- return;
76
- }
77
-
78
- const endpoint = normalizeEndpoint(otel.endpoint ?? process.env.OTEL_EXPORTER_OTLP_ENDPOINT);
79
- const headers = otel.headers ?? undefined;
80
- const serviceName =
81
- otel.serviceName?.trim() || process.env.OTEL_SERVICE_NAME || DEFAULT_SERVICE_NAME;
82
- const sampleRate = resolveSampleRate(otel.sampleRate);
83
-
84
- const tracesEnabled = otel.traces !== false;
85
- const metricsEnabled = otel.metrics !== false;
86
- const logsEnabled = otel.logs === true;
87
- if (!tracesEnabled && !metricsEnabled && !logsEnabled) {
88
- return;
89
- }
90
-
91
- const resource = resourceFromAttributes({
92
- [ATTR_SERVICE_NAME]: serviceName,
93
- });
94
-
95
- const traceUrl = resolveOtelUrl(endpoint, "v1/traces");
96
- const metricUrl = resolveOtelUrl(endpoint, "v1/metrics");
97
- const logUrl = resolveOtelUrl(endpoint, "v1/logs");
98
- const traceExporter = tracesEnabled
99
- ? new OTLPTraceExporter({
100
- ...(traceUrl ? { url: traceUrl } : {}),
101
- ...(headers ? { headers } : {}),
102
- })
103
- : undefined;
104
-
105
- const metricExporter = metricsEnabled
106
- ? new OTLPMetricExporter({
107
- ...(metricUrl ? { url: metricUrl } : {}),
108
- ...(headers ? { headers } : {}),
109
- })
110
- : undefined;
111
-
112
- const metricReader = metricExporter
113
- ? new PeriodicExportingMetricReader({
114
- exporter: metricExporter,
115
- ...(typeof otel.flushIntervalMs === "number"
116
- ? { exportIntervalMillis: Math.max(1000, otel.flushIntervalMs) }
117
- : {}),
118
- })
119
- : undefined;
120
-
121
- if (tracesEnabled || metricsEnabled) {
122
- sdk = new NodeSDK({
123
- resource,
124
- ...(traceExporter ? { traceExporter } : {}),
125
- ...(metricReader ? { metricReader } : {}),
126
- ...(sampleRate !== undefined
127
- ? {
128
- sampler: new ParentBasedSampler({
129
- root: new TraceIdRatioBasedSampler(sampleRate),
130
- }),
131
- }
132
- : {}),
133
- });
134
-
135
- try {
136
- await sdk.start();
137
- } catch (err) {
138
- ctx.logger.error(`diagnostics-otel: failed to start SDK: ${formatError(err)}`);
139
- throw err;
140
- }
141
- }
142
-
143
- const logSeverityMap: Record<string, SeverityNumber> = {
144
- TRACE: 1 as SeverityNumber,
145
- DEBUG: 5 as SeverityNumber,
146
- INFO: 9 as SeverityNumber,
147
- WARN: 13 as SeverityNumber,
148
- ERROR: 17 as SeverityNumber,
149
- FATAL: 21 as SeverityNumber,
150
- };
151
-
152
- const meter = metrics.getMeter("symi");
153
- const tracer = trace.getTracer("symi");
154
-
155
- const tokensCounter = meter.createCounter("symi.tokens", {
156
- unit: "1",
157
- description: "Token usage by type",
158
- });
159
- const costCounter = meter.createCounter("symi.cost.usd", {
160
- unit: "1",
161
- description: "Estimated model cost (USD)",
162
- });
163
- const durationHistogram = meter.createHistogram("symi.run.duration_ms", {
164
- unit: "ms",
165
- description: "Agent run duration",
166
- });
167
- const contextHistogram = meter.createHistogram("symi.context.tokens", {
168
- unit: "1",
169
- description: "Context window size and usage",
170
- });
171
- const webhookReceivedCounter = meter.createCounter("symi.webhook.received", {
172
- unit: "1",
173
- description: "Webhook requests received",
174
- });
175
- const webhookErrorCounter = meter.createCounter("symi.webhook.error", {
176
- unit: "1",
177
- description: "Webhook processing errors",
178
- });
179
- const webhookDurationHistogram = meter.createHistogram("symi.webhook.duration_ms", {
180
- unit: "ms",
181
- description: "Webhook processing duration",
182
- });
183
- const messageQueuedCounter = meter.createCounter("symi.message.queued", {
184
- unit: "1",
185
- description: "Messages queued for processing",
186
- });
187
- const messageProcessedCounter = meter.createCounter("symi.message.processed", {
188
- unit: "1",
189
- description: "Messages processed by outcome",
190
- });
191
- const messageDurationHistogram = meter.createHistogram("symi.message.duration_ms", {
192
- unit: "ms",
193
- description: "Message processing duration",
194
- });
195
- const queueDepthHistogram = meter.createHistogram("symi.queue.depth", {
196
- unit: "1",
197
- description: "Queue depth on enqueue/dequeue",
198
- });
199
- const queueWaitHistogram = meter.createHistogram("symi.queue.wait_ms", {
200
- unit: "ms",
201
- description: "Queue wait time before execution",
202
- });
203
- const laneEnqueueCounter = meter.createCounter("symi.queue.lane.enqueue", {
204
- unit: "1",
205
- description: "Command queue lane enqueue events",
206
- });
207
- const laneDequeueCounter = meter.createCounter("symi.queue.lane.dequeue", {
208
- unit: "1",
209
- description: "Command queue lane dequeue events",
210
- });
211
- const sessionStateCounter = meter.createCounter("symi.session.state", {
212
- unit: "1",
213
- description: "Session state transitions",
214
- });
215
- const sessionStuckCounter = meter.createCounter("symi.session.stuck", {
216
- unit: "1",
217
- description: "Sessions stuck in processing",
218
- });
219
- const sessionStuckAgeHistogram = meter.createHistogram("symi.session.stuck_age_ms", {
220
- unit: "ms",
221
- description: "Age of stuck sessions",
222
- });
223
- const runAttemptCounter = meter.createCounter("symi.run.attempt", {
224
- unit: "1",
225
- description: "Run attempts",
226
- });
227
-
228
- if (logsEnabled) {
229
- const logExporter = new OTLPLogExporter({
230
- ...(logUrl ? { url: logUrl } : {}),
231
- ...(headers ? { headers } : {}),
232
- });
233
- const logProcessor = new BatchLogRecordProcessor(
234
- logExporter,
235
- typeof otel.flushIntervalMs === "number"
236
- ? { scheduledDelayMillis: Math.max(1000, otel.flushIntervalMs) }
237
- : {},
238
- );
239
- logProvider = new LoggerProvider({
240
- resource,
241
- processors: [logProcessor],
242
- });
243
- const otelLogger = logProvider.getLogger("symi");
244
-
245
- stopLogTransport = registerLogTransport((logObj) => {
246
- try {
247
- const safeStringify = (value: unknown) => {
248
- try {
249
- return JSON.stringify(value);
250
- } catch {
251
- return String(value);
252
- }
253
- };
254
- const meta = (logObj as Record<string, unknown>)._meta as
255
- | {
256
- logLevelName?: string;
257
- date?: Date;
258
- name?: string;
259
- parentNames?: string[];
260
- path?: {
261
- filePath?: string;
262
- fileLine?: string;
263
- fileColumn?: string;
264
- filePathWithLine?: string;
265
- method?: string;
266
- };
267
- }
268
- | undefined;
269
- const logLevelName = meta?.logLevelName ?? "INFO";
270
- const severityNumber = logSeverityMap[logLevelName] ?? (9 as SeverityNumber);
271
-
272
- const numericArgs = Object.entries(logObj)
273
- .filter(([key]) => /^\d+$/.test(key))
274
- .toSorted((a, b) => Number(a[0]) - Number(b[0]))
275
- .map(([, value]) => value);
276
-
277
- let bindings: Record<string, unknown> | undefined;
278
- if (typeof numericArgs[0] === "string" && numericArgs[0].trim().startsWith("{")) {
279
- try {
280
- const parsed = JSON.parse(numericArgs[0]);
281
- if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
282
- bindings = parsed as Record<string, unknown>;
283
- numericArgs.shift();
284
- }
285
- } catch {
286
- // ignore malformed json bindings
287
- }
288
- }
289
-
290
- let message = "";
291
- if (numericArgs.length > 0 && typeof numericArgs[numericArgs.length - 1] === "string") {
292
- message = String(numericArgs.pop());
293
- } else if (numericArgs.length === 1) {
294
- message = safeStringify(numericArgs[0]);
295
- numericArgs.length = 0;
296
- }
297
- if (!message) {
298
- message = "log";
299
- }
300
-
301
- const attributes: Record<string, string | number | boolean> = {
302
- "symi.log.level": logLevelName,
303
- };
304
- if (meta?.name) {
305
- attributes["symi.logger"] = meta.name;
306
- }
307
- if (meta?.parentNames?.length) {
308
- attributes["symi.logger.parents"] = meta.parentNames.join(".");
309
- }
310
- if (bindings) {
311
- for (const [key, value] of Object.entries(bindings)) {
312
- if (
313
- typeof value === "string" ||
314
- typeof value === "number" ||
315
- typeof value === "boolean"
316
- ) {
317
- attributes[`symi.${key}`] = value;
318
- } else if (value != null) {
319
- attributes[`symi.${key}`] = safeStringify(value);
320
- }
321
- }
322
- }
323
- if (numericArgs.length > 0) {
324
- attributes["symi.log.args"] = safeStringify(numericArgs);
325
- }
326
- if (meta?.path?.filePath) {
327
- attributes["code.filepath"] = meta.path.filePath;
328
- }
329
- if (meta?.path?.fileLine) {
330
- attributes["code.lineno"] = Number(meta.path.fileLine);
331
- }
332
- if (meta?.path?.method) {
333
- attributes["code.function"] = meta.path.method;
334
- }
335
- if (meta?.path?.filePathWithLine) {
336
- attributes["symi.code.location"] = meta.path.filePathWithLine;
337
- }
338
-
339
- otelLogger.emit({
340
- body: message,
341
- severityText: logLevelName,
342
- severityNumber,
343
- attributes,
344
- timestamp: meta?.date ?? new Date(),
345
- });
346
- } catch (err) {
347
- ctx.logger.error(`diagnostics-otel: log transport failed: ${formatError(err)}`);
348
- }
349
- });
350
- }
351
-
352
- const spanWithDuration = (
353
- name: string,
354
- attributes: Record<string, string | number>,
355
- durationMs?: number,
356
- ) => {
357
- const startTime =
358
- typeof durationMs === "number" ? Date.now() - Math.max(0, durationMs) : undefined;
359
- const span = tracer.startSpan(name, {
360
- attributes,
361
- ...(startTime ? { startTime } : {}),
362
- });
363
- return span;
364
- };
365
-
366
- const recordModelUsage = (evt: Extract<DiagnosticEventPayload, { type: "model.usage" }>) => {
367
- const attrs = {
368
- "symi.channel": evt.channel ?? "unknown",
369
- "symi.provider": evt.provider ?? "unknown",
370
- "symi.model": evt.model ?? "unknown",
371
- };
372
-
373
- const usage = evt.usage;
374
- if (usage.input) {
375
- tokensCounter.add(usage.input, { ...attrs, "symi.token": "input" });
376
- }
377
- if (usage.output) {
378
- tokensCounter.add(usage.output, { ...attrs, "symi.token": "output" });
379
- }
380
- if (usage.cacheRead) {
381
- tokensCounter.add(usage.cacheRead, { ...attrs, "symi.token": "cache_read" });
382
- }
383
- if (usage.cacheWrite) {
384
- tokensCounter.add(usage.cacheWrite, { ...attrs, "symi.token": "cache_write" });
385
- }
386
- if (usage.promptTokens) {
387
- tokensCounter.add(usage.promptTokens, { ...attrs, "symi.token": "prompt" });
388
- }
389
- if (usage.total) {
390
- tokensCounter.add(usage.total, { ...attrs, "symi.token": "total" });
391
- }
392
-
393
- if (evt.costUsd) {
394
- costCounter.add(evt.costUsd, attrs);
395
- }
396
- if (evt.durationMs) {
397
- durationHistogram.record(evt.durationMs, attrs);
398
- }
399
- if (evt.context?.limit) {
400
- contextHistogram.record(evt.context.limit, {
401
- ...attrs,
402
- "symi.context": "limit",
403
- });
404
- }
405
- if (evt.context?.used) {
406
- contextHistogram.record(evt.context.used, {
407
- ...attrs,
408
- "symi.context": "used",
409
- });
410
- }
411
-
412
- if (!tracesEnabled) {
413
- return;
414
- }
415
- const spanAttrs: Record<string, string | number> = {
416
- ...attrs,
417
- "symi.sessionKey": evt.sessionKey ?? "",
418
- "symi.sessionId": evt.sessionId ?? "",
419
- "symi.tokens.input": usage.input ?? 0,
420
- "symi.tokens.output": usage.output ?? 0,
421
- "symi.tokens.cache_read": usage.cacheRead ?? 0,
422
- "symi.tokens.cache_write": usage.cacheWrite ?? 0,
423
- "symi.tokens.total": usage.total ?? 0,
424
- };
425
-
426
- const span = spanWithDuration("symi.model.usage", spanAttrs, evt.durationMs);
427
- span.end();
428
- };
429
-
430
- const recordWebhookReceived = (
431
- evt: Extract<DiagnosticEventPayload, { type: "webhook.received" }>,
432
- ) => {
433
- const attrs = {
434
- "symi.channel": evt.channel ?? "unknown",
435
- "symi.webhook": evt.updateType ?? "unknown",
436
- };
437
- webhookReceivedCounter.add(1, attrs);
438
- };
439
-
440
- const recordWebhookProcessed = (
441
- evt: Extract<DiagnosticEventPayload, { type: "webhook.processed" }>,
442
- ) => {
443
- const attrs = {
444
- "symi.channel": evt.channel ?? "unknown",
445
- "symi.webhook": evt.updateType ?? "unknown",
446
- };
447
- if (typeof evt.durationMs === "number") {
448
- webhookDurationHistogram.record(evt.durationMs, attrs);
449
- }
450
- if (!tracesEnabled) {
451
- return;
452
- }
453
- const spanAttrs: Record<string, string | number> = { ...attrs };
454
- if (evt.chatId !== undefined) {
455
- spanAttrs["symi.chatId"] = String(evt.chatId);
456
- }
457
- const span = spanWithDuration("symi.webhook.processed", spanAttrs, evt.durationMs);
458
- span.end();
459
- };
460
-
461
- const recordWebhookError = (
462
- evt: Extract<DiagnosticEventPayload, { type: "webhook.error" }>,
463
- ) => {
464
- const attrs = {
465
- "symi.channel": evt.channel ?? "unknown",
466
- "symi.webhook": evt.updateType ?? "unknown",
467
- };
468
- webhookErrorCounter.add(1, attrs);
469
- if (!tracesEnabled) {
470
- return;
471
- }
472
- const spanAttrs: Record<string, string | number> = {
473
- ...attrs,
474
- "symi.error": evt.error,
475
- };
476
- if (evt.chatId !== undefined) {
477
- spanAttrs["symi.chatId"] = String(evt.chatId);
478
- }
479
- const span = tracer.startSpan("symi.webhook.error", {
480
- attributes: spanAttrs,
481
- });
482
- span.setStatus({ code: SpanStatusCode.ERROR, message: evt.error });
483
- span.end();
484
- };
485
-
486
- const recordMessageQueued = (
487
- evt: Extract<DiagnosticEventPayload, { type: "message.queued" }>,
488
- ) => {
489
- const attrs = {
490
- "symi.channel": evt.channel ?? "unknown",
491
- "symi.source": evt.source ?? "unknown",
492
- };
493
- messageQueuedCounter.add(1, attrs);
494
- if (typeof evt.queueDepth === "number") {
495
- queueDepthHistogram.record(evt.queueDepth, attrs);
496
- }
497
- };
498
-
499
- const recordMessageProcessed = (
500
- evt: Extract<DiagnosticEventPayload, { type: "message.processed" }>,
501
- ) => {
502
- const attrs = {
503
- "symi.channel": evt.channel ?? "unknown",
504
- "symi.outcome": evt.outcome ?? "unknown",
505
- };
506
- messageProcessedCounter.add(1, attrs);
507
- if (typeof evt.durationMs === "number") {
508
- messageDurationHistogram.record(evt.durationMs, attrs);
509
- }
510
- if (!tracesEnabled) {
511
- return;
512
- }
513
- const spanAttrs: Record<string, string | number> = { ...attrs };
514
- if (evt.sessionKey) {
515
- spanAttrs["symi.sessionKey"] = evt.sessionKey;
516
- }
517
- if (evt.sessionId) {
518
- spanAttrs["symi.sessionId"] = evt.sessionId;
519
- }
520
- if (evt.chatId !== undefined) {
521
- spanAttrs["symi.chatId"] = String(evt.chatId);
522
- }
523
- if (evt.messageId !== undefined) {
524
- spanAttrs["symi.messageId"] = String(evt.messageId);
525
- }
526
- if (evt.reason) {
527
- spanAttrs["symi.reason"] = evt.reason;
528
- }
529
- const span = spanWithDuration("symi.message.processed", spanAttrs, evt.durationMs);
530
- if (evt.outcome === "error") {
531
- span.setStatus({ code: SpanStatusCode.ERROR, message: evt.error });
532
- }
533
- span.end();
534
- };
535
-
536
- const recordLaneEnqueue = (
537
- evt: Extract<DiagnosticEventPayload, { type: "queue.lane.enqueue" }>,
538
- ) => {
539
- const attrs = { "symi.lane": evt.lane };
540
- laneEnqueueCounter.add(1, attrs);
541
- queueDepthHistogram.record(evt.queueSize, attrs);
542
- };
543
-
544
- const recordLaneDequeue = (
545
- evt: Extract<DiagnosticEventPayload, { type: "queue.lane.dequeue" }>,
546
- ) => {
547
- const attrs = { "symi.lane": evt.lane };
548
- laneDequeueCounter.add(1, attrs);
549
- queueDepthHistogram.record(evt.queueSize, attrs);
550
- if (typeof evt.waitMs === "number") {
551
- queueWaitHistogram.record(evt.waitMs, attrs);
552
- }
553
- };
554
-
555
- const recordSessionState = (
556
- evt: Extract<DiagnosticEventPayload, { type: "session.state" }>,
557
- ) => {
558
- const attrs: Record<string, string> = { "symi.state": evt.state };
559
- if (evt.reason) {
560
- attrs["symi.reason"] = evt.reason;
561
- }
562
- sessionStateCounter.add(1, attrs);
563
- };
564
-
565
- const recordSessionStuck = (
566
- evt: Extract<DiagnosticEventPayload, { type: "session.stuck" }>,
567
- ) => {
568
- const attrs: Record<string, string> = { "symi.state": evt.state };
569
- sessionStuckCounter.add(1, attrs);
570
- if (typeof evt.ageMs === "number") {
571
- sessionStuckAgeHistogram.record(evt.ageMs, attrs);
572
- }
573
- if (!tracesEnabled) {
574
- return;
575
- }
576
- const spanAttrs: Record<string, string | number> = { ...attrs };
577
- if (evt.sessionKey) {
578
- spanAttrs["symi.sessionKey"] = evt.sessionKey;
579
- }
580
- if (evt.sessionId) {
581
- spanAttrs["symi.sessionId"] = evt.sessionId;
582
- }
583
- spanAttrs["symi.queueDepth"] = evt.queueDepth ?? 0;
584
- spanAttrs["symi.ageMs"] = evt.ageMs;
585
- const span = tracer.startSpan("symi.session.stuck", { attributes: spanAttrs });
586
- span.setStatus({ code: SpanStatusCode.ERROR, message: "session stuck" });
587
- span.end();
588
- };
589
-
590
- const recordRunAttempt = (evt: Extract<DiagnosticEventPayload, { type: "run.attempt" }>) => {
591
- runAttemptCounter.add(1, { "symi.attempt": evt.attempt });
592
- };
593
-
594
- const recordHeartbeat = (
595
- evt: Extract<DiagnosticEventPayload, { type: "diagnostic.heartbeat" }>,
596
- ) => {
597
- queueDepthHistogram.record(evt.queued, { "symi.channel": "heartbeat" });
598
- };
599
-
600
- unsubscribe = onDiagnosticEvent((evt: DiagnosticEventPayload) => {
601
- try {
602
- switch (evt.type) {
603
- case "model.usage":
604
- recordModelUsage(evt);
605
- return;
606
- case "webhook.received":
607
- recordWebhookReceived(evt);
608
- return;
609
- case "webhook.processed":
610
- recordWebhookProcessed(evt);
611
- return;
612
- case "webhook.error":
613
- recordWebhookError(evt);
614
- return;
615
- case "message.queued":
616
- recordMessageQueued(evt);
617
- return;
618
- case "message.processed":
619
- recordMessageProcessed(evt);
620
- return;
621
- case "queue.lane.enqueue":
622
- recordLaneEnqueue(evt);
623
- return;
624
- case "queue.lane.dequeue":
625
- recordLaneDequeue(evt);
626
- return;
627
- case "session.state":
628
- recordSessionState(evt);
629
- return;
630
- case "session.stuck":
631
- recordSessionStuck(evt);
632
- return;
633
- case "run.attempt":
634
- recordRunAttempt(evt);
635
- return;
636
- case "diagnostic.heartbeat":
637
- recordHeartbeat(evt);
638
- return;
639
- }
640
- } catch (err) {
641
- ctx.logger.error(
642
- `diagnostics-otel: event handler failed (${evt.type}): ${formatError(err)}`,
643
- );
644
- }
645
- });
646
-
647
- if (logsEnabled) {
648
- ctx.logger.info("diagnostics-otel: logs exporter enabled (OTLP/HTTP)");
649
- }
650
- },
651
- async stop() {
652
- unsubscribe?.();
653
- unsubscribe = null;
654
- stopLogTransport?.();
655
- stopLogTransport = null;
656
- if (logProvider) {
657
- await logProvider.shutdown().catch(() => undefined);
658
- logProvider = null;
659
- }
660
- if (sdk) {
661
- await sdk.shutdown().catch(() => undefined);
662
- sdk = null;
663
- }
664
- },
665
- } satisfies SymiPluginService;
666
- }
@@ -1,8 +0,0 @@
1
- {
2
- "id": "diagnostics-otel",
3
- "configSchema": {
4
- "type": "object",
5
- "additionalProperties": false,
6
- "properties": {}
7
- }
8
- }
@@ -1,24 +0,0 @@
1
- # Google Antigravity Auth (Symi plugin)
2
-
3
- OAuth provider plugin for **Google Antigravity** (Cloud Code Assist).
4
-
5
- ## Enable
6
-
7
- Bundled plugins are disabled by default. Enable this one:
8
-
9
- ```bash
10
- symi plugins enable google-antigravity-auth
11
- ```
12
-
13
- Restart the Gateway after enabling.
14
-
15
- ## Authenticate
16
-
17
- ```bash
18
- symi models auth login --provider google-antigravity --set-default
19
- ```
20
-
21
- ## Notes
22
-
23
- - Antigravity uses Google Cloud project quotas.
24
- - If requests fail, ensure Gemini for Google Cloud is enabled.