larkin 0.2.70

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 (144) hide show
  1. package/CONTRIBUTING.md +48 -0
  2. package/LICENSE +201 -0
  3. package/README.md +122 -0
  4. package/SECURITY.md +14 -0
  5. package/artifacts/release/THIRD_PARTY_NOTICES.txt +5488 -0
  6. package/dist/agent/agent-cli-capabilities.cjs +43 -0
  7. package/dist/agent/agent-cli-capabilities.mjs +35 -0
  8. package/dist/agent/agent-state-store.cjs +961 -0
  9. package/dist/agent/agent-state-store.mjs +923 -0
  10. package/dist/agent/agent-transport.cjs +201 -0
  11. package/dist/agent/agent-transport.mjs +194 -0
  12. package/dist/agent/config-cli-contract.cjs +19 -0
  13. package/dist/agent/config-cli-contract.mjs +16 -0
  14. package/dist/agent/context-prompt.cjs +159 -0
  15. package/dist/agent/context-prompt.mjs +155 -0
  16. package/dist/agent/freshness-gate.mjs +24 -0
  17. package/dist/agent/history-projection.cjs +72 -0
  18. package/dist/agent/history-projection.mjs +65 -0
  19. package/dist/agent/host-reminder-orchestrator.cjs +289 -0
  20. package/dist/agent/host-reminder-orchestrator.mjs +252 -0
  21. package/dist/agent/inbox-projection.cjs +92 -0
  22. package/dist/agent/inbox-projection.mjs +85 -0
  23. package/dist/agent/interaction-state-machine.cjs +710 -0
  24. package/dist/agent/interaction-state-machine.mjs +705 -0
  25. package/dist/agent/reminder-routes.cjs +192 -0
  26. package/dist/agent/reminder-routes.mjs +156 -0
  27. package/dist/agent/reminder-store.cjs +220 -0
  28. package/dist/agent/reminder-store.mjs +205 -0
  29. package/dist/agent/transport-business-context.cjs +486 -0
  30. package/dist/agent/transport-business-context.mjs +446 -0
  31. package/dist/agent/transport-shell.cjs +45 -0
  32. package/dist/agent/transport-shell.mjs +39 -0
  33. package/dist/app/agent-cli.mjs +596 -0
  34. package/dist/app/agent-config.mjs +633 -0
  35. package/dist/app/agent-lark-cli-workspace.cjs +95 -0
  36. package/dist/app/agent-lark-cli-workspace.mjs +85 -0
  37. package/dist/app/agent-readiness.cjs +42 -0
  38. package/dist/app/agent-readiness.mjs +38 -0
  39. package/dist/app/binary-entry.mjs +86 -0
  40. package/dist/app/cli.mjs +176 -0
  41. package/dist/app/dashboard.mjs +205 -0
  42. package/dist/app/internal-command.cjs +57 -0
  43. package/dist/app/internal-command.mjs +48 -0
  44. package/dist/app/lark-channel-secret.mjs +22 -0
  45. package/dist/app/lark-cli.mjs +887 -0
  46. package/dist/app/lark.mjs +45 -0
  47. package/dist/app/local-control.mjs +728 -0
  48. package/dist/app/official-lark-cli.cjs +138 -0
  49. package/dist/app/official-lark-cli.mjs +128 -0
  50. package/dist/app/pi-auth-cli.mjs +67 -0
  51. package/dist/app/run.mjs +364 -0
  52. package/dist/app/runtime-agent-config.mjs +416 -0
  53. package/dist/app/runtime-model-directory.mjs +78 -0
  54. package/dist/app/runtime-process.mjs +113 -0
  55. package/dist/app/session-cli.mjs +83 -0
  56. package/dist/app/setup-dashboard.mjs +26 -0
  57. package/dist/app/setup.mjs +166 -0
  58. package/dist/app/telemetry.mjs +83 -0
  59. package/dist/dashboard/dashboard-assets.mjs +21 -0
  60. package/dist/dashboard/dashboard-avatar.mjs +128 -0
  61. package/dist/dashboard/dashboard-config-controller.mjs +525 -0
  62. package/dist/dashboard/dashboard-lifecycle.cjs +97 -0
  63. package/dist/dashboard/dashboard-lifecycle.mjs +88 -0
  64. package/dist/dashboard/dashboard-template.mjs +29 -0
  65. package/dist/dashboard/dashboard-view-model.mjs +556 -0
  66. package/dist/dashboard/dashboard-workspace.mjs +104 -0
  67. package/dist/dashboard/web/assets/dashboard.css +3 -0
  68. package/dist/dashboard/web/assets/dashboard.js +49 -0
  69. package/dist/feishu/channel-transport.cjs +151 -0
  70. package/dist/feishu/channel-transport.mjs +148 -0
  71. package/dist/feishu/document-comment.cjs +120 -0
  72. package/dist/feishu/document-comment.mjs +109 -0
  73. package/dist/feishu/host-business-state.cjs +477 -0
  74. package/dist/feishu/host-business-state.mjs +462 -0
  75. package/dist/feishu/host-channel-business.cjs +161 -0
  76. package/dist/feishu/host-channel-business.mjs +157 -0
  77. package/dist/feishu/host-processing-eye.cjs +185 -0
  78. package/dist/feishu/host-processing-eye.mjs +181 -0
  79. package/dist/feishu/host-shell.cjs +1425 -0
  80. package/dist/feishu/host-shell.mjs +1385 -0
  81. package/dist/feishu/im-freshness-adapter.mjs +89 -0
  82. package/dist/feishu/interaction-orchestrator.cjs +279 -0
  83. package/dist/feishu/interaction-orchestrator.mjs +242 -0
  84. package/dist/feishu/lark-passthrough.mjs +70 -0
  85. package/dist/feishu/message-policy.cjs +111 -0
  86. package/dist/feishu/message-policy.mjs +98 -0
  87. package/dist/feishu/outbound-transport.cjs +221 -0
  88. package/dist/feishu/outbound-transport.mjs +215 -0
  89. package/dist/platform/build-info.cjs +25 -0
  90. package/dist/platform/build-info.mjs +18 -0
  91. package/dist/platform/callback-capability.cjs +301 -0
  92. package/dist/platform/callback-capability.mjs +252 -0
  93. package/dist/platform/check-bun-version.cjs +12 -0
  94. package/dist/platform/config.cjs +751 -0
  95. package/dist/platform/config.mjs +694 -0
  96. package/dist/platform/process-inspect.cjs +89 -0
  97. package/dist/platform/process-state.cjs +237 -0
  98. package/dist/platform/process-state.mjs +223 -0
  99. package/dist/platform/release-artifacts.mjs +61 -0
  100. package/dist/platform/root-layout.cjs +111 -0
  101. package/dist/platform/root-layout.mjs +72 -0
  102. package/dist/platform/telemetry-config.cjs +64 -0
  103. package/dist/platform/telemetry-config.mjs +56 -0
  104. package/dist/platform/telemetry-spool.cjs +661 -0
  105. package/dist/platform/telemetry-spool.mjs +653 -0
  106. package/dist/platform/telemetry-tracing.cjs +602 -0
  107. package/dist/platform/telemetry-tracing.mjs +596 -0
  108. package/dist/platform/telemetry-uploader.cjs +150 -0
  109. package/dist/platform/telemetry-uploader.mjs +146 -0
  110. package/dist/platform/workspace-service.cjs +469 -0
  111. package/dist/platform/workspace-service.mjs +430 -0
  112. package/dist/runtime/builtin-pi-assets.mjs +33 -0
  113. package/dist/runtime/claude-model-catalog.cjs +102 -0
  114. package/dist/runtime/claude-model-catalog.mjs +99 -0
  115. package/dist/runtime/codex-model-catalog.cjs +108 -0
  116. package/dist/runtime/codex-model-catalog.mjs +105 -0
  117. package/dist/runtime/pi-bash-timeout-extension.mjs +60 -0
  118. package/dist/runtime/pi-bash-timeout-injection.mjs +60 -0
  119. package/dist/runtime/pi-bash-timeout.bundle.js +43 -0
  120. package/dist/runtime/pi-model-catalog.cjs +116 -0
  121. package/dist/runtime/pi-model-catalog.mjs +75 -0
  122. package/dist/runtime/pi-official-auth.mjs +480 -0
  123. package/dist/runtime/pi-provider-config.cjs +252 -0
  124. package/dist/runtime/pi-provider-config.mjs +239 -0
  125. package/dist/runtime/pi-rpc-client.cjs +255 -0
  126. package/dist/runtime/pi-rpc-client.mjs +251 -0
  127. package/dist/runtime/pi-subagent-injection.mjs +129 -0
  128. package/dist/runtime/pi-subagents.bundle.js +8990 -0
  129. package/dist/runtime/runtime-adapters.mjs +1019 -0
  130. package/dist/runtime/runtime-contracts.cjs +2 -0
  131. package/dist/runtime/runtime-contracts.mjs +1 -0
  132. package/dist/runtime/runtime-host.cjs +930 -0
  133. package/dist/runtime/runtime-host.mjs +922 -0
  134. package/dist/runtime/runtime-model-catalog.cjs +43 -0
  135. package/dist/runtime/runtime-model-catalog.mjs +40 -0
  136. package/dist/runtime/runtime-readiness.cjs +177 -0
  137. package/dist/runtime/runtime-readiness.mjs +133 -0
  138. package/dist/setup/bot-register.mjs +655 -0
  139. package/dist/setup/grant-scopes.mjs +133 -0
  140. package/dist/setup/run-credential-preflight.mjs +89 -0
  141. package/dist/setup/setup-agent-choice.mjs +172 -0
  142. package/dist/setup/setup-bind.mjs +337 -0
  143. package/dist/setup/setup-binding.mjs +52 -0
  144. package/package.json +112 -0
@@ -0,0 +1,596 @@
1
+ import crypto from "node:crypto";
2
+ import fs from "node:fs";
3
+ import path from "node:path";
4
+ import { context, SpanKind, SpanStatusCode, trace } from "@opentelemetry/api";
5
+ import { resourceFromAttributes } from "@opentelemetry/resources";
6
+ import { SimpleSpanProcessor } from "@opentelemetry/sdk-trace-base";
7
+ import { NodeTracerProvider } from "@opentelemetry/sdk-trace-node";
8
+ import { TelemetrySpool } from "./telemetry-spool.mjs";
9
+ import { startTelemetryUploader } from "./telemetry-uploader.mjs";
10
+ import { inspectProcess } from "./process-state.mjs";
11
+ const nanos = ([seconds, nanoseconds]) => (BigInt(seconds) * 1000000000n + BigInt(nanoseconds)).toString();
12
+ // OTel JS uses zero-based SpanKind values while OTLP's enum starts at 1.
13
+ const kind = (value) => value + 1;
14
+ const attributeValue = (value) => {
15
+ if (typeof value === "boolean")
16
+ return { boolValue: value };
17
+ if (typeof value === "number" && Number.isInteger(value))
18
+ return { intValue: String(value) };
19
+ if (typeof value === "number")
20
+ return { doubleValue: value };
21
+ if (Array.isArray(value))
22
+ return { arrayValue: { values: value.map(attributeValue) } };
23
+ return { stringValue: String(value) };
24
+ };
25
+ const attributes = (values) => Object.entries(values)
26
+ .filter(([, value]) => value !== undefined && value !== null)
27
+ .map(([key, value]) => ({ key, value: attributeValue(value) }));
28
+ function serializeSpan(span) {
29
+ const resourceAttributes = span.resource.attributes;
30
+ const spanContext = span.spanContext();
31
+ return { resourceSpans: [{
32
+ resource: { attributes: attributes(resourceAttributes) },
33
+ scopeSpans: [{ scope: { name: span.instrumentationScope.name, version: span.instrumentationScope.version }, spans: [{
34
+ traceId: spanContext.traceId, spanId: spanContext.spanId,
35
+ ...(span.parentSpanContext?.spanId ? { parentSpanId: span.parentSpanContext.spanId } : {}),
36
+ name: span.name, kind: kind(span.kind), startTimeUnixNano: nanos(span.startTime), endTimeUnixNano: nanos(span.endTime),
37
+ attributes: attributes(span.attributes),
38
+ links: span.links.map((link) => ({ traceId: link.context.traceId, spanId: link.context.spanId,
39
+ traceState: link.context.traceState?.serialize(), flags: link.context.traceFlags,
40
+ attributes: attributes((link.attributes ?? {})) })),
41
+ status: { code: span.status.code }, flags: spanContext.traceFlags,
42
+ }] }],
43
+ }] };
44
+ }
45
+ class DurableSpanExporter {
46
+ spool;
47
+ diagnostic;
48
+ constructor(spool, diagnostic) {
49
+ this.spool = spool;
50
+ this.diagnostic = diagnostic;
51
+ }
52
+ export(spans, callback) {
53
+ try {
54
+ for (const span of spans)
55
+ this.spool.enqueue(serializeSpan(span));
56
+ callback({ code: 0 });
57
+ }
58
+ catch {
59
+ this.diagnostic?.("spool");
60
+ callback({ code: 1 });
61
+ }
62
+ }
63
+ async shutdown() { }
64
+ }
65
+ const NOOP = {
66
+ beginMessage() { }, async phase(_id, _name, _kind, operation) { return operation(); }, filterMessage() { }, delivery() { }, runtimeEvent() { },
67
+ externalPhase(_agent, _state, _name, _kind, operation) { return operation(); }, async shutdown() { },
68
+ };
69
+ const safeHash = (value) => crypto.createHash("sha256").update(value).digest("hex").slice(0, 24);
70
+ const activeContextFile = (stateDir) => path.join(stateDir, "telemetry-active-context.json");
71
+ const generationFile = (stateDir) => path.join(stateDir, "telemetry-runtime-generation.json");
72
+ function atomicStateWrite(file, value) {
73
+ const temporary = `${file}.${process.pid}.${crypto.randomUUID()}.tmp`;
74
+ try {
75
+ fs.writeFileSync(temporary, JSON.stringify(value), { mode: 0o600, flag: "wx" });
76
+ fs.renameSync(temporary, file);
77
+ fs.chmodSync(file, 0o600);
78
+ const directory = fs.openSync(path.dirname(file), fs.constants.O_RDONLY);
79
+ try {
80
+ fs.fsyncSync(directory);
81
+ }
82
+ finally {
83
+ fs.closeSync(directory);
84
+ }
85
+ }
86
+ finally {
87
+ try {
88
+ fs.unlinkSync(temporary);
89
+ }
90
+ catch { /* isolated */ }
91
+ }
92
+ }
93
+ function readStateJson(file) {
94
+ const descriptor = fs.openSync(file, fs.constants.O_RDONLY | fs.constants.O_NOFOLLOW);
95
+ try {
96
+ const stat = fs.fstatSync(descriptor);
97
+ if (!stat.isFile())
98
+ throw new Error("invalid state");
99
+ return JSON.parse(fs.readFileSync(descriptor, "utf8"));
100
+ }
101
+ finally {
102
+ fs.closeSync(descriptor);
103
+ }
104
+ }
105
+ function writeActiveContext(stateDir, value, generation, now) {
106
+ if (!stateDir)
107
+ return;
108
+ const file = activeContextFile(stateDir);
109
+ try {
110
+ fs.mkdirSync(stateDir, { recursive: true, mode: 0o700 });
111
+ if (!value) {
112
+ try {
113
+ fs.unlinkSync(file);
114
+ }
115
+ catch (error) {
116
+ if (error.code !== "ENOENT")
117
+ throw error;
118
+ }
119
+ return;
120
+ }
121
+ atomicStateWrite(file, { version: 2, generation, traceId: value.traceId, spanId: value.spanId, traceFlags: value.traceFlags,
122
+ expiresAt: now + 30 * 60 * 1000 });
123
+ }
124
+ catch { /* telemetry cannot alter business behavior */ }
125
+ }
126
+ function readActiveContext(stateDir, now, inspectOwner) {
127
+ try {
128
+ const file = activeContextFile(stateDir);
129
+ const stat = fs.lstatSync(file);
130
+ if (!stat.isFile() || stat.isSymbolicLink())
131
+ return null;
132
+ const value = readStateJson(file);
133
+ const generationStat = fs.lstatSync(generationFile(stateDir));
134
+ if (!generationStat.isFile() || generationStat.isSymbolicLink())
135
+ return null;
136
+ const owner = readStateJson(generationFile(stateDir));
137
+ const ownerPid = Number(owner.pid);
138
+ if (value.version !== 2 || owner.version !== 1 || value.generation !== owner.generation
139
+ || Number(value.expiresAt) <= now || Number(owner.expiresAt) <= now
140
+ || !Number.isSafeInteger(ownerPid) || ownerPid <= 0
141
+ || !/^[0-9a-f]{32}$/.test(String(value.traceId)) || !/^[0-9a-f]{16}$/.test(String(value.spanId)))
142
+ return null;
143
+ const inspected = inspectOwner(ownerPid);
144
+ if (!inspected.ok || !inspected.startToken || inspected.startToken !== owner.processStartToken)
145
+ return null;
146
+ return { traceId: String(value.traceId), spanId: String(value.spanId), traceFlags: Number(value.traceFlags) || 1, isRemote: true };
147
+ }
148
+ catch {
149
+ return null;
150
+ }
151
+ }
152
+ export function createTelemetryRuntime(config, options = {}) {
153
+ const spool = new TelemetrySpool(config);
154
+ const exporter = new DurableSpanExporter(spool);
155
+ const runtimeGeneration = crypto.randomUUID();
156
+ const startupId = crypto.randomUUID();
157
+ const now = options.now ?? Date.now;
158
+ const inspectOwner = options.inspectOwner ?? ((pid) => inspectProcess(pid));
159
+ const processStartToken = options.processStartToken ?? inspectOwner(process.pid).startToken;
160
+ const initializedStateDirs = new Set();
161
+ const initializeStateDir = (stateDir) => {
162
+ if (initializedStateDirs.has(stateDir))
163
+ return;
164
+ try {
165
+ fs.mkdirSync(stateDir, { recursive: true, mode: 0o700 });
166
+ try {
167
+ fs.unlinkSync(activeContextFile(stateDir));
168
+ }
169
+ catch (error) {
170
+ if (error.code !== "ENOENT")
171
+ throw error;
172
+ }
173
+ atomicStateWrite(generationFile(stateDir), { version: 1, generation: runtimeGeneration, pid: process.pid,
174
+ processStartToken, expiresAt: now() + 24 * 60 * 60 * 1000 });
175
+ initializedStateDirs.add(stateDir);
176
+ }
177
+ catch { /* isolated */ }
178
+ };
179
+ const renewStateDir = (stateDir) => {
180
+ try {
181
+ const owner = readStateJson(generationFile(stateDir));
182
+ if (owner.generation === runtimeGeneration && Number(owner.expiresAt) - now() < 12 * 60 * 60 * 1000) {
183
+ atomicStateWrite(generationFile(stateDir), { version: 1, generation: runtimeGeneration, pid: process.pid,
184
+ processStartToken, expiresAt: now() + 24 * 60 * 60 * 1000 });
185
+ }
186
+ }
187
+ catch { /* isolated */ }
188
+ };
189
+ const writeContext = (agentId, value) => {
190
+ const stateDir = options.stateDirFor?.(agentId);
191
+ if (!stateDir)
192
+ return;
193
+ initializeStateDir(stateDir);
194
+ renewStateDir(stateDir);
195
+ writeActiveContext(stateDir, value, runtimeGeneration, now());
196
+ };
197
+ for (const stateDir of options.stateDirs ?? []) {
198
+ initializeStateDir(stateDir);
199
+ }
200
+ const provider = new NodeTracerProvider({
201
+ resource: resourceFromAttributes({ "service.name": "larkin", "service.version": options.serviceVersion ?? "dev",
202
+ "service.instance.id": startupId }),
203
+ spanProcessors: [new SimpleSpanProcessor(exporter)],
204
+ spanLimits: { attributeCountLimit: 16, attributeValueLengthLimit: 96, eventCountLimit: 0, linkCountLimit: 8 },
205
+ });
206
+ provider.register();
207
+ const tracer = provider.getTracer("larkin.telemetry", "1.0.0");
208
+ const messages = new Map();
209
+ const activeByAgent = new Map();
210
+ // Runtime observations can precede the acceptance receipt. Correlate them
211
+ // without publishing or changing the authoritative active delivery state.
212
+ const observingByAgent = new Map();
213
+ const observingHead = (agentId) => observingByAgent.get(agentId)?.[0];
214
+ const enqueueObserving = (current) => {
215
+ const queue = observingByAgent.get(current.agentId) ?? [];
216
+ if (!queue.includes(current))
217
+ queue.push(current);
218
+ observingByAgent.set(current.agentId, queue);
219
+ };
220
+ const removeObserving = (current) => {
221
+ const queue = observingByAgent.get(current.agentId);
222
+ if (!queue)
223
+ return;
224
+ const next = queue.filter((candidate) => candidate !== current);
225
+ if (next.length > 0)
226
+ observingByAgent.set(current.agentId, next);
227
+ else
228
+ observingByAgent.delete(current.agentId);
229
+ };
230
+ const ownershipTimer = setInterval(() => {
231
+ for (const stateDir of initializedStateDirs)
232
+ renewStateDir(stateDir);
233
+ for (const [agentId, current] of activeByAgent)
234
+ writeContext(agentId, current.turn?.spanContext() ?? current.root.spanContext());
235
+ }, options.maintenanceIntervalMs ?? 10 * 60 * 1000);
236
+ ownershipTimer.unref?.();
237
+ const uploader = startTelemetryUploader(spool, config);
238
+ const closeActivity = (current) => {
239
+ current.activitySpan?.end();
240
+ delete current.activitySpan;
241
+ delete current.activityName;
242
+ };
243
+ const closePi = (current, failed = false) => {
244
+ const state = current.pi;
245
+ if (!state)
246
+ return;
247
+ for (const key of ["promptWaitSpan", "compactionSpan", "rpcSpan", "lifecycleSpan", "outputWaitSpan", "generationSpan", "toolSpan", "settleSpan"]) {
248
+ const span = state[key];
249
+ if (!span)
250
+ continue;
251
+ if (failed)
252
+ span.setStatus({ code: SpanStatusCode.ERROR });
253
+ span.end();
254
+ delete state[key];
255
+ }
256
+ };
257
+ const endTrace = (current, failed = false) => {
258
+ closeActivity(current);
259
+ closePi(current, failed);
260
+ if (current.turn) {
261
+ if (failed)
262
+ current.turn.setStatus({ code: SpanStatusCode.ERROR });
263
+ current.turn.end();
264
+ }
265
+ if (failed)
266
+ current.root.setStatus({ code: SpanStatusCode.ERROR });
267
+ current.root.end();
268
+ for (const [messageId, candidate] of messages)
269
+ if (candidate === current)
270
+ messages.delete(messageId);
271
+ if (activeByAgent.get(current.agentId) === current) {
272
+ activeByAgent.delete(current.agentId);
273
+ writeContext(current.agentId, null);
274
+ }
275
+ removeObserving(current);
276
+ };
277
+ return {
278
+ beginMessage(agentId, messageId, source = "im") {
279
+ try {
280
+ if (messages.has(messageId))
281
+ return;
282
+ const active = activeByAgent.get(agentId) ?? observingHead(agentId);
283
+ const linkedContext = active?.turn?.spanContext() ?? active?.root.spanContext();
284
+ const root = tracer.startSpan("larkin.message.process", { kind: SpanKind.CONSUMER,
285
+ attributes: { "larkin.agent.id_hash": safeHash(agentId), "messaging.message.id_hash": safeHash(messageId),
286
+ "larkin.message.source": source,
287
+ ...(linkedContext ? { "larkin.message.relation": "fan_in" } : {}) },
288
+ ...(linkedContext ? { links: [{ context: linkedContext }] } : {}),
289
+ });
290
+ const value = { root, context: trace.setSpan(context.active(), root), agentId, messageHash: safeHash(messageId) };
291
+ messages.set(messageId, value);
292
+ }
293
+ catch { /* isolated */ }
294
+ },
295
+ async phase(messageId, name, spanKind, operation) {
296
+ const current = messages.get(messageId);
297
+ if (!current)
298
+ return operation();
299
+ if (name === "runtime.deliver" && !activeByAgent.has(current.agentId))
300
+ enqueueObserving(current);
301
+ let span;
302
+ try {
303
+ span = tracer.startSpan(name, { kind: spanKind }, current.context);
304
+ }
305
+ catch {
306
+ removeObserving(current);
307
+ return operation();
308
+ }
309
+ try {
310
+ return await context.with(trace.setSpan(current.context, span), operation);
311
+ }
312
+ catch (error) {
313
+ span.setStatus({ code: SpanStatusCode.ERROR });
314
+ if (!name.startsWith("document.comment."))
315
+ endTrace(current, true);
316
+ throw error;
317
+ }
318
+ finally {
319
+ span.end();
320
+ removeObserving(current);
321
+ }
322
+ },
323
+ filterMessage(messageId, reason) {
324
+ try {
325
+ const current = messages.get(messageId);
326
+ if (!current)
327
+ return;
328
+ current.root.setAttribute("larkin.filter.reason", reason);
329
+ endTrace(current, false);
330
+ }
331
+ catch { /* isolated */ }
332
+ },
333
+ delivery(agentId, messageId, status) {
334
+ try {
335
+ const current = messages.get(messageId) ?? activeByAgent.get(agentId);
336
+ if (!current)
337
+ return;
338
+ if (status === "accepted") {
339
+ const active = activeByAgent.get(agentId) ?? observingHead(agentId);
340
+ if (active && active !== current) {
341
+ endTrace(current, false);
342
+ return;
343
+ }
344
+ activeByAgent.set(agentId, current);
345
+ writeContext(agentId, current.turn?.spanContext() ?? current.root.spanContext());
346
+ }
347
+ if (status === "error")
348
+ endTrace(current, true);
349
+ if (status === "deferred")
350
+ endTrace(current, true);
351
+ if (status === "duplicate")
352
+ endTrace(current, false);
353
+ }
354
+ catch { /* isolated */ }
355
+ },
356
+ runtimeEvent(agentId, event) {
357
+ try {
358
+ const current = activeByAgent.get(agentId) ?? observingHead(agentId);
359
+ if (!current)
360
+ return;
361
+ if (event.type === "runtime-observation" && event.runtime === "pi") {
362
+ const state = current.pi ??= { distribution: event.distribution };
363
+ state.distribution = event.distribution;
364
+ const observedAt = now();
365
+ if (state.settledAt !== undefined)
366
+ return;
367
+ const spanAttributes = { "larkin.observation.boundary": "pi_rpc", "larkin.runtime.id": "pi",
368
+ "larkin.runtime.distribution": event.distribution };
369
+ const startPreflight = (name, startTime) => tracer.startSpan(name, { kind: SpanKind.INTERNAL, startTime, attributes: spanAttributes }, current.context);
370
+ const start = (name, startTime) => {
371
+ if (!current.turnContext)
372
+ return undefined;
373
+ return tracer.startSpan(name, { kind: SpanKind.INTERNAL, startTime, attributes: spanAttributes }, current.turnContext);
374
+ };
375
+ if (event.phase === "rpc_submit" && state.submitAt === undefined && state.acceptedAt === undefined) {
376
+ state.submitAt = observedAt;
377
+ state.promptWaitSpan = startPreflight("pi.prompt.wait", observedAt);
378
+ }
379
+ else if (event.phase === "rpc_accepted" && state.submitAt !== undefined && state.acceptedAt === undefined) {
380
+ state.acceptedAt = observedAt;
381
+ state.promptWaitSpan?.setAttribute("larkin.pi.preflight.outcome", "accepted");
382
+ state.promptWaitSpan?.end(observedAt);
383
+ delete state.promptWaitSpan;
384
+ state.compactionSpan?.end(observedAt);
385
+ delete state.compactionSpan;
386
+ if (event.distribution === "builtin") {
387
+ if (state.rpcSpan) {
388
+ state.rpcSpan.end(observedAt);
389
+ delete state.rpcSpan;
390
+ }
391
+ if (current.turnContext && !state.outputWaitSpan)
392
+ state.outputWaitSpan = start("pi.output.wait", observedAt);
393
+ }
394
+ }
395
+ else if (event.phase === "compaction_start" && state.submitAt !== undefined && !state.compactionSpan) {
396
+ state.promptWaitSpan?.setAttribute("larkin.pi.preflight.progress", "compaction");
397
+ state.compactionSpan = startPreflight("pi.compaction", observedAt);
398
+ }
399
+ else if (event.phase === "compaction_end") {
400
+ state.compactionSpan?.end(observedAt);
401
+ delete state.compactionSpan;
402
+ }
403
+ else if (event.phase === "retry_progress") {
404
+ state.promptWaitSpan?.setAttribute("larkin.pi.preflight.progress", "retry");
405
+ }
406
+ else if (event.phase === "rpc_timeout" || event.phase === "rpc_error") {
407
+ const outcome = event.phase === "rpc_timeout" ? "timeout" : "error";
408
+ state.promptWaitSpan?.setAttribute("larkin.pi.preflight.outcome", outcome);
409
+ state.promptWaitSpan?.setStatus({ code: SpanStatusCode.ERROR });
410
+ state.promptWaitSpan?.end(observedAt);
411
+ delete state.promptWaitSpan;
412
+ state.compactionSpan?.setStatus({ code: SpanStatusCode.ERROR });
413
+ state.compactionSpan?.end(observedAt);
414
+ delete state.compactionSpan;
415
+ }
416
+ if (event.distribution !== "builtin")
417
+ return;
418
+ if (event.phase === "turn_start" && state.turnStartAt === undefined)
419
+ state.turnStartAt = observedAt;
420
+ else if (event.phase === "first_output" && state.firstOutputAt === undefined && state.completedAt === undefined) {
421
+ state.firstOutputAt = observedAt;
422
+ if (state.outputWaitSpan) {
423
+ state.outputWaitSpan.end(observedAt);
424
+ delete state.outputWaitSpan;
425
+ }
426
+ if (current.turnContext && !state.generationSpan)
427
+ state.generationSpan = start("pi.generation", observedAt);
428
+ }
429
+ else if (event.phase === "tool_call" && state.completedAt === undefined && !state.toolSpan) {
430
+ state.toolSpan = start("pi.tool.wait", observedAt);
431
+ }
432
+ else if (event.phase === "tool_result" && state.toolSpan) {
433
+ state.toolSpan.end(observedAt);
434
+ delete state.toolSpan;
435
+ }
436
+ else if (event.phase === "completed" && state.completedAt === undefined) {
437
+ state.completedAt = observedAt;
438
+ if (state.generationSpan) {
439
+ state.generationSpan.end(observedAt);
440
+ delete state.generationSpan;
441
+ }
442
+ if (!state.settleSpan)
443
+ state.settleSpan = start("pi.rpc.settle", observedAt);
444
+ }
445
+ else if (event.phase === "settled") {
446
+ state.settledAt = observedAt;
447
+ if (state.toolSpan) {
448
+ state.toolSpan.end(observedAt);
449
+ delete state.toolSpan;
450
+ }
451
+ if (state.generationSpan) {
452
+ state.generationSpan.end(observedAt);
453
+ delete state.generationSpan;
454
+ }
455
+ if (state.settleSpan) {
456
+ state.settleSpan.end(observedAt);
457
+ delete state.settleSpan;
458
+ }
459
+ if (state.lifecycleSpan) {
460
+ state.lifecycleSpan.end(observedAt);
461
+ delete state.lifecycleSpan;
462
+ }
463
+ }
464
+ }
465
+ else if (event.type === "turn-start") {
466
+ if (current.turn)
467
+ return;
468
+ const pi = current.pi;
469
+ current.turn = tracer.startSpan("agent.turn", { kind: SpanKind.INTERNAL,
470
+ ...(pi?.submitAt !== undefined ? { startTime: pi.submitAt } : {}),
471
+ attributes: { "larkin.observation.boundary": "runtime_host",
472
+ ...(pi ? { "larkin.runtime.id": "pi", "larkin.runtime.distribution": pi.distribution } : {}) } }, current.context);
473
+ current.turnContext = trace.setSpan(current.context, current.turn);
474
+ if (pi?.distribution === "builtin") {
475
+ const spanAttributes = { "larkin.observation.boundary": "pi_rpc", "larkin.runtime.id": "pi",
476
+ "larkin.runtime.distribution": "builtin" };
477
+ const submitAt = pi.submitAt ?? pi.turnStartAt ?? now();
478
+ pi.lifecycleSpan = tracer.startSpan("pi.rpc.lifecycle", { kind: SpanKind.INTERNAL, startTime: submitAt,
479
+ attributes: spanAttributes }, current.turnContext);
480
+ if (pi.submitAt !== undefined) {
481
+ pi.rpcSpan = tracer.startSpan("pi.rpc.submit", { kind: SpanKind.INTERNAL, startTime: pi.submitAt,
482
+ attributes: spanAttributes }, current.turnContext);
483
+ if (pi.acceptedAt !== undefined && pi.acceptedAt >= pi.submitAt) {
484
+ pi.rpcSpan.end(pi.acceptedAt);
485
+ delete pi.rpcSpan;
486
+ }
487
+ }
488
+ const waitAt = pi.acceptedAt ?? pi.turnStartAt ?? now();
489
+ pi.outputWaitSpan = tracer.startSpan("pi.output.wait", { kind: SpanKind.INTERNAL, startTime: waitAt,
490
+ attributes: spanAttributes }, current.turnContext);
491
+ if (pi.firstOutputAt !== undefined) {
492
+ pi.outputWaitSpan.end(pi.firstOutputAt);
493
+ delete pi.outputWaitSpan;
494
+ }
495
+ }
496
+ writeContext(agentId, current.turn.spanContext());
497
+ }
498
+ else if (event.type === "activity") {
499
+ const activityName = event.activity === "tool" ? "tool.execute" : "model.activity";
500
+ writeContext(agentId, current.turn?.spanContext() ?? current.root.spanContext());
501
+ if (current.activityName === activityName)
502
+ return;
503
+ closeActivity(current);
504
+ const activitySpan = tracer.startSpan(activityName, { kind: SpanKind.INTERNAL,
505
+ attributes: { "larkin.activity.type": event.activity, "larkin.observation.boundary": "runtime_event_interval" } }, current.turnContext ?? current.context);
506
+ current.activityName = activityName;
507
+ current.activitySpan = activitySpan;
508
+ }
509
+ else if (event.type === "input-error") {
510
+ current.turn?.setStatus({ code: SpanStatusCode.ERROR });
511
+ }
512
+ else if (event.type === "turn-end")
513
+ endTrace(current, false);
514
+ else if (event.type === "error" || event.type === "configuration-error" || event.type === "closed")
515
+ endTrace(current, true);
516
+ }
517
+ catch { /* isolated */ }
518
+ },
519
+ externalPhase(agentId, stateDir, name, spanKind, operation, boundary = "agent_transport") {
520
+ let span;
521
+ let parentContext;
522
+ try {
523
+ const parent = readActiveContext(stateDir, now(), inspectOwner);
524
+ parentContext = parent ? trace.setSpanContext(context.active(), parent) : context.active();
525
+ span = tracer.startSpan(name, { kind: spanKind, attributes: { "larkin.agent.id_hash": safeHash(agentId),
526
+ "larkin.observation.boundary": boundary } }, parentContext);
527
+ }
528
+ catch {
529
+ return operation();
530
+ }
531
+ let result;
532
+ try {
533
+ result = context.with(trace.setSpan(parentContext, span), operation);
534
+ }
535
+ catch (error) {
536
+ if (name === "document.comment.reply")
537
+ span.setAttribute("larkin.operation.outcome", "error");
538
+ span.setStatus({ code: SpanStatusCode.ERROR });
539
+ span.end();
540
+ throw error;
541
+ }
542
+ const recordOutcome = (value) => {
543
+ if (name === "document.comment.reply") {
544
+ const success = typeof value !== "number" || value === 0;
545
+ span.setAttribute("larkin.operation.outcome", success ? "success" : "error");
546
+ if (!success)
547
+ span.setStatus({ code: SpanStatusCode.ERROR });
548
+ }
549
+ return value;
550
+ };
551
+ if (result && typeof result.then === "function") {
552
+ return Promise.resolve(result).then(recordOutcome).catch((error) => {
553
+ if (name === "document.comment.reply")
554
+ span.setAttribute("larkin.operation.outcome", "error");
555
+ span.setStatus({ code: SpanStatusCode.ERROR });
556
+ throw error;
557
+ })
558
+ .finally(() => span.end());
559
+ }
560
+ recordOutcome(result);
561
+ span.end();
562
+ return result;
563
+ },
564
+ async shutdown() {
565
+ uploader?.stop();
566
+ clearInterval(ownershipTimer);
567
+ for (const current of new Set(messages.values()))
568
+ endTrace(current, true);
569
+ for (const stateDir of initializedStateDirs) {
570
+ try {
571
+ const owner = JSON.parse(fs.readFileSync(generationFile(stateDir), "utf8"));
572
+ if (owner.generation !== runtimeGeneration)
573
+ continue;
574
+ for (const file of [activeContextFile(stateDir), generationFile(stateDir)]) {
575
+ try {
576
+ fs.unlinkSync(file);
577
+ }
578
+ catch (error) {
579
+ if (error.code !== "ENOENT")
580
+ throw error;
581
+ }
582
+ }
583
+ }
584
+ catch { /* isolated */ }
585
+ }
586
+ await provider.forceFlush().catch(() => { });
587
+ await provider.shutdown().catch(() => { });
588
+ },
589
+ };
590
+ }
591
+ let singleton = null;
592
+ export function telemetrySingleton(config, options) {
593
+ if (!singleton && config)
594
+ singleton = createTelemetryRuntime(config, options);
595
+ return singleton ?? NOOP;
596
+ }