@clinebot/core 0.0.29 → 0.0.30
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +7 -7
- package/dist/session/default-session-manager.d.ts +5 -0
- package/dist/session/default-session-manager.d.ts.map +1 -1
- package/dist/telemetry/OpenTelemetryProvider.d.ts.map +1 -1
- package/dist/telemetry/index.js +2 -2
- package/package.json +4 -4
- package/src/runtime/checkpoint-hooks.test.ts +2 -1
- package/src/session/default-session-manager.ts +22 -0
- package/src/telemetry/OpenTelemetryAdapter.ts +1 -1
- package/src/telemetry/OpenTelemetryProvider.test.ts +191 -0
- package/src/telemetry/OpenTelemetryProvider.ts +2 -2
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@clinebot/core",
|
|
3
3
|
"description": "Cline Core SDK for Node Runtime",
|
|
4
|
-
"version": "0.0.
|
|
4
|
+
"version": "0.0.30",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"types": "./dist/index.d.ts",
|
|
7
7
|
"main": "./dist/index.js",
|
|
@@ -36,8 +36,8 @@
|
|
|
36
36
|
"test:watch": "vitest --config vitest.config.ts"
|
|
37
37
|
},
|
|
38
38
|
"dependencies": {
|
|
39
|
-
"@clinebot/agents": "0.0.
|
|
40
|
-
"@clinebot/llms": "0.0.
|
|
39
|
+
"@clinebot/agents": "0.0.30",
|
|
40
|
+
"@clinebot/llms": "0.0.30",
|
|
41
41
|
"@opentelemetry/api": "^1.9.0",
|
|
42
42
|
"@opentelemetry/api-logs": "^0.214.0",
|
|
43
43
|
"@opentelemetry/exporter-logs-otlp-http": "^0.214.0",
|
|
@@ -58,7 +58,7 @@
|
|
|
58
58
|
},
|
|
59
59
|
"devDependencies": {
|
|
60
60
|
"@clinebot/rpc": "0.0.28",
|
|
61
|
-
"@clinebot/shared": "0.0.
|
|
61
|
+
"@clinebot/shared": "0.0.30"
|
|
62
62
|
},
|
|
63
63
|
"engines": {
|
|
64
64
|
"node": ">=22"
|
|
@@ -30,7 +30,8 @@ async function createGitRepo(): Promise<string> {
|
|
|
30
30
|
return cwd;
|
|
31
31
|
}
|
|
32
32
|
|
|
33
|
-
|
|
33
|
+
// Currently disabled for process?.env?.CLINE_CHECKPOINT
|
|
34
|
+
describe.skip("createCheckpointHooks", () => {
|
|
34
35
|
it("creates one checkpoint at the start of each root run and appends metadata", async () => {
|
|
35
36
|
const cwd = await createGitRepo();
|
|
36
37
|
let metadata: Record<string, unknown> | undefined;
|
|
@@ -344,6 +344,7 @@ export class DefaultSessionManager implements SessionManager {
|
|
|
344
344
|
onConsecutiveMistakeLimitReached:
|
|
345
345
|
configWithProvider.onConsecutiveMistakeLimitReached,
|
|
346
346
|
completionGuard: runtime.completionGuard,
|
|
347
|
+
consumePendingUserMessage: () => this.consumeSteerMessage(sessionId),
|
|
347
348
|
logger: runtime.logger ?? configWithProvider.logger,
|
|
348
349
|
extensionContext: configWithProvider.extensionContext,
|
|
349
350
|
onEvent: (event: AgentEvent) =>
|
|
@@ -964,6 +965,27 @@ export class DefaultSessionManager implements SessionManager {
|
|
|
964
965
|
});
|
|
965
966
|
}
|
|
966
967
|
|
|
968
|
+
/**
|
|
969
|
+
* Consume the first steer-delivery pending prompt for injection into the
|
|
970
|
+
* running agent loop. Called synchronously by the agent between iterations.
|
|
971
|
+
*/
|
|
972
|
+
private consumeSteerMessage(sessionId: string): string | undefined {
|
|
973
|
+
const session = this.sessions.get(sessionId);
|
|
974
|
+
if (!session) {
|
|
975
|
+
return undefined;
|
|
976
|
+
}
|
|
977
|
+
const steerIndex = session.pendingPrompts.findIndex(
|
|
978
|
+
(entry) => entry.delivery === "steer",
|
|
979
|
+
);
|
|
980
|
+
if (steerIndex < 0) {
|
|
981
|
+
return undefined;
|
|
982
|
+
}
|
|
983
|
+
const [steer] = session.pendingPrompts.splice(steerIndex, 1);
|
|
984
|
+
this.emitPendingPrompts(session);
|
|
985
|
+
this.emitPendingPromptSubmitted(session, steer);
|
|
986
|
+
return steer.prompt;
|
|
987
|
+
}
|
|
988
|
+
|
|
967
989
|
private enqueuePendingPrompt(
|
|
968
990
|
sessionId: string,
|
|
969
991
|
entry: {
|
|
@@ -229,8 +229,8 @@ export class OpenTelemetryAdapter implements ITelemetryAdapter {
|
|
|
229
229
|
): TelemetryProperties {
|
|
230
230
|
return {
|
|
231
231
|
...this.commonProperties,
|
|
232
|
-
...properties,
|
|
233
232
|
...this.metadata,
|
|
233
|
+
...properties,
|
|
234
234
|
...(this.distinctId ? { distinct_id: this.distinctId } : {}),
|
|
235
235
|
...(required ? { _required: true } : {}),
|
|
236
236
|
};
|
|
@@ -98,6 +98,197 @@ describe("createOpenTelemetryTelemetryService", () => {
|
|
|
98
98
|
expect(telemetry).toBeInstanceOf(TelemetryService);
|
|
99
99
|
});
|
|
100
100
|
|
|
101
|
+
it("preserves metadata when disabled", () => {
|
|
102
|
+
const metadata = {
|
|
103
|
+
extension_version: "1.0.0",
|
|
104
|
+
cline_type: "kanban",
|
|
105
|
+
platform: "kanban",
|
|
106
|
+
platform_version: "v22.0.0",
|
|
107
|
+
os_type: "darwin",
|
|
108
|
+
os_version: "15.0",
|
|
109
|
+
};
|
|
110
|
+
const { telemetry } = createConfiguredTelemetryService({
|
|
111
|
+
metadata,
|
|
112
|
+
enabled: false,
|
|
113
|
+
});
|
|
114
|
+
const spy = vi.fn();
|
|
115
|
+
(telemetry as any).adapters.push({
|
|
116
|
+
name: "test",
|
|
117
|
+
emit: spy,
|
|
118
|
+
emitRequired: spy,
|
|
119
|
+
isEnabled: () => true,
|
|
120
|
+
recordCounter: vi.fn(),
|
|
121
|
+
recordHistogram: vi.fn(),
|
|
122
|
+
recordGauge: vi.fn(),
|
|
123
|
+
flush: async () => {},
|
|
124
|
+
dispose: async () => {},
|
|
125
|
+
});
|
|
126
|
+
telemetry.captureRequired("test.event", {});
|
|
127
|
+
expect(spy).toHaveBeenCalledWith(
|
|
128
|
+
"test.event",
|
|
129
|
+
expect.objectContaining({
|
|
130
|
+
cline_type: "kanban",
|
|
131
|
+
platform: "kanban",
|
|
132
|
+
}),
|
|
133
|
+
);
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
it("preserves metadata in the enabled (OTEL) path", async () => {
|
|
137
|
+
const metadata = {
|
|
138
|
+
extension_version: "1.0.0",
|
|
139
|
+
cline_type: "kanban",
|
|
140
|
+
platform: "kanban",
|
|
141
|
+
platform_version: "v22.0.0",
|
|
142
|
+
os_type: "darwin",
|
|
143
|
+
os_version: "15.0",
|
|
144
|
+
};
|
|
145
|
+
const { telemetry, provider } = createOpenTelemetryTelemetryService({
|
|
146
|
+
metadata,
|
|
147
|
+
enabled: true,
|
|
148
|
+
logsExporter: "console",
|
|
149
|
+
});
|
|
150
|
+
const spy = vi.fn();
|
|
151
|
+
(telemetry as any).adapters.push({
|
|
152
|
+
name: "test",
|
|
153
|
+
emit: spy,
|
|
154
|
+
emitRequired: spy,
|
|
155
|
+
isEnabled: () => true,
|
|
156
|
+
recordCounter: vi.fn(),
|
|
157
|
+
recordHistogram: vi.fn(),
|
|
158
|
+
recordGauge: vi.fn(),
|
|
159
|
+
flush: async () => {},
|
|
160
|
+
dispose: async () => {},
|
|
161
|
+
});
|
|
162
|
+
telemetry.captureRequired("test.event", {});
|
|
163
|
+
expect(spy).toHaveBeenCalledWith(
|
|
164
|
+
"test.event",
|
|
165
|
+
expect.objectContaining({
|
|
166
|
+
cline_type: "kanban",
|
|
167
|
+
platform: "kanban",
|
|
168
|
+
}),
|
|
169
|
+
);
|
|
170
|
+
await provider.dispose();
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
it("delivers metadata to the OTEL logger without duplication", async () => {
|
|
174
|
+
const otelEmit = vi.fn();
|
|
175
|
+
const provider = new OpenTelemetryProvider({
|
|
176
|
+
enabled: true,
|
|
177
|
+
});
|
|
178
|
+
// Replace the loggerProvider with a mock so we can inspect emit calls
|
|
179
|
+
(provider as any).loggerProvider = {
|
|
180
|
+
getLogger: () => ({ emit: otelEmit }),
|
|
181
|
+
forceFlush: async () => {},
|
|
182
|
+
shutdown: async () => {},
|
|
183
|
+
};
|
|
184
|
+
|
|
185
|
+
const metadata = {
|
|
186
|
+
extension_version: "1.0.0",
|
|
187
|
+
cline_type: "kanban",
|
|
188
|
+
platform: "kanban",
|
|
189
|
+
platform_version: "v22.0.0",
|
|
190
|
+
os_type: "darwin",
|
|
191
|
+
os_version: "15.0",
|
|
192
|
+
};
|
|
193
|
+
|
|
194
|
+
const telemetry = provider.createTelemetryService({ metadata });
|
|
195
|
+
|
|
196
|
+
telemetry.captureRequired("test.otel_event", { custom_prop: "value" });
|
|
197
|
+
|
|
198
|
+
expect(otelEmit).toHaveBeenCalledTimes(1);
|
|
199
|
+
const emittedAttributes = otelEmit.mock.calls[0][0].attributes;
|
|
200
|
+
|
|
201
|
+
// Metadata fields must be present
|
|
202
|
+
expect(emittedAttributes).toMatchObject({
|
|
203
|
+
cline_type: "kanban",
|
|
204
|
+
platform: "kanban",
|
|
205
|
+
extension_version: "1.0.0",
|
|
206
|
+
custom_prop: "value",
|
|
207
|
+
});
|
|
208
|
+
|
|
209
|
+
// Verify no key appears more than once (flattened object can't have
|
|
210
|
+
// duplicate keys, but this guards against nested duplication patterns
|
|
211
|
+
// like metadata appearing under a sub-prefix)
|
|
212
|
+
const keys = Object.keys(emittedAttributes);
|
|
213
|
+
const metadataKeys = Object.keys(metadata);
|
|
214
|
+
for (const mk of metadataKeys) {
|
|
215
|
+
const occurrences = keys.filter((k) => k === mk || k.endsWith(`.${mk}`));
|
|
216
|
+
expect(
|
|
217
|
+
occurrences,
|
|
218
|
+
`metadata key "${mk}" should appear exactly once, found: ${occurrences.join(", ")}`,
|
|
219
|
+
).toHaveLength(1);
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
await provider.dispose();
|
|
223
|
+
});
|
|
224
|
+
|
|
225
|
+
it("propagates updateMetadata to OTEL logger output", async () => {
|
|
226
|
+
const otelEmit = vi.fn();
|
|
227
|
+
const provider = new OpenTelemetryProvider({
|
|
228
|
+
enabled: true,
|
|
229
|
+
});
|
|
230
|
+
(provider as any).loggerProvider = {
|
|
231
|
+
getLogger: () => ({ emit: otelEmit }),
|
|
232
|
+
forceFlush: async () => {},
|
|
233
|
+
shutdown: async () => {},
|
|
234
|
+
};
|
|
235
|
+
|
|
236
|
+
const metadata = {
|
|
237
|
+
extension_version: "1.0.0",
|
|
238
|
+
cline_type: "kanban",
|
|
239
|
+
platform: "kanban",
|
|
240
|
+
platform_version: "v22.0.0",
|
|
241
|
+
os_type: "darwin",
|
|
242
|
+
os_version: "15.0",
|
|
243
|
+
};
|
|
244
|
+
|
|
245
|
+
const telemetry = provider.createTelemetryService({ metadata });
|
|
246
|
+
|
|
247
|
+
// Update metadata after construction
|
|
248
|
+
telemetry.updateMetadata({ cline_type: "kanban-updated" });
|
|
249
|
+
|
|
250
|
+
telemetry.captureRequired("test.updated_event", {});
|
|
251
|
+
|
|
252
|
+
// The OTEL logger should see the updated value
|
|
253
|
+
const emittedAttributes =
|
|
254
|
+
otelEmit.mock.calls[otelEmit.mock.calls.length - 1][0].attributes;
|
|
255
|
+
expect(emittedAttributes.cline_type).toBe("kanban-updated");
|
|
256
|
+
|
|
257
|
+
await provider.dispose();
|
|
258
|
+
});
|
|
259
|
+
|
|
260
|
+
it("preserves logger when disabled", () => {
|
|
261
|
+
const logger: BasicLogger = {
|
|
262
|
+
debug: vi.fn(),
|
|
263
|
+
log: vi.fn(),
|
|
264
|
+
error: vi.fn(),
|
|
265
|
+
};
|
|
266
|
+
const { telemetry } = createConfiguredTelemetryService({
|
|
267
|
+
metadata: {
|
|
268
|
+
extension_version: "1.0.0",
|
|
269
|
+
cline_type: "kanban",
|
|
270
|
+
platform: "kanban",
|
|
271
|
+
platform_version: "v22.0.0",
|
|
272
|
+
os_type: "darwin",
|
|
273
|
+
os_version: "15.0",
|
|
274
|
+
},
|
|
275
|
+
enabled: false,
|
|
276
|
+
logger,
|
|
277
|
+
});
|
|
278
|
+
|
|
279
|
+
telemetry.capture({
|
|
280
|
+
event: "session.started",
|
|
281
|
+
properties: { sessionId: "session-1" },
|
|
282
|
+
});
|
|
283
|
+
|
|
284
|
+
expect(logger.log).toHaveBeenCalledWith(
|
|
285
|
+
"telemetry.event",
|
|
286
|
+
expect.objectContaining({
|
|
287
|
+
event: "session.started",
|
|
288
|
+
}),
|
|
289
|
+
);
|
|
290
|
+
});
|
|
291
|
+
|
|
101
292
|
it("attaches the logger adapter when creating configured telemetry", () => {
|
|
102
293
|
const logger: BasicLogger = {
|
|
103
294
|
debug: vi.fn(),
|
|
@@ -133,10 +133,9 @@ export class OpenTelemetryProvider {
|
|
|
133
133
|
metadata: options.metadata,
|
|
134
134
|
});
|
|
135
135
|
return new TelemetryService({
|
|
136
|
+
...options,
|
|
136
137
|
adapters: [adapter],
|
|
137
138
|
distinctId: resolveCoreDistinctId(options.distinctId),
|
|
138
|
-
commonProperties: options.commonProperties,
|
|
139
|
-
logger: options.logger,
|
|
140
139
|
});
|
|
141
140
|
}
|
|
142
141
|
|
|
@@ -299,6 +298,7 @@ export function createConfiguredTelemetryService(
|
|
|
299
298
|
if (options.enabled !== true) {
|
|
300
299
|
return {
|
|
301
300
|
telemetry: new TelemetryService({
|
|
301
|
+
...options,
|
|
302
302
|
distinctId: resolveCoreDistinctId(options.distinctId),
|
|
303
303
|
}),
|
|
304
304
|
};
|