@clinebot/core 0.0.4 → 0.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 (79) hide show
  1. package/dist/agents/agent-config-parser.d.ts +1 -1
  2. package/dist/index.d.ts +5 -2
  3. package/dist/index.node.d.ts +1 -0
  4. package/dist/index.node.js +134 -107
  5. package/dist/runtime/session-runtime.d.ts +4 -2
  6. package/dist/session/default-session-manager.d.ts +5 -1
  7. package/dist/session/session-host.d.ts +3 -1
  8. package/dist/session/session-manager.d.ts +2 -1
  9. package/dist/session/unified-session-persistence-service.d.ts +4 -0
  10. package/dist/telemetry/ITelemetryAdapter.d.ts +54 -0
  11. package/dist/telemetry/LoggerTelemetryAdapter.d.ts +21 -0
  12. package/dist/telemetry/OpenTelemetryAdapter.d.ts +43 -0
  13. package/dist/telemetry/OpenTelemetryProvider.d.ts +41 -0
  14. package/dist/telemetry/TelemetryService.d.ts +34 -0
  15. package/dist/telemetry/opentelemetry.d.ts +3 -0
  16. package/dist/telemetry/opentelemetry.js +27 -0
  17. package/dist/{default-tools → tools}/schemas.d.ts +6 -0
  18. package/dist/types/config.d.ts +3 -2
  19. package/package.json +17 -3
  20. package/src/agents/agent-config-parser.ts +1 -1
  21. package/src/agents/hooks-config-loader.ts +19 -1
  22. package/src/index.node.ts +3 -0
  23. package/src/index.ts +35 -19
  24. package/src/providers/local-provider-service.ts +25 -7
  25. package/src/runtime/hook-file-hooks.test.ts +47 -0
  26. package/src/runtime/hook-file-hooks.ts +3 -0
  27. package/src/runtime/runtime-builder.test.ts +20 -0
  28. package/src/runtime/runtime-builder.ts +3 -2
  29. package/src/runtime/runtime-parity.test.ts +1 -1
  30. package/src/runtime/session-runtime.ts +4 -2
  31. package/src/session/default-session-manager.test.ts +72 -0
  32. package/src/session/default-session-manager.ts +63 -6
  33. package/src/session/session-host.ts +7 -2
  34. package/src/session/session-manager.ts +2 -1
  35. package/src/session/unified-session-persistence-service.ts +213 -23
  36. package/src/telemetry/ITelemetryAdapter.ts +94 -0
  37. package/src/telemetry/LoggerTelemetryAdapter.test.ts +42 -0
  38. package/src/telemetry/LoggerTelemetryAdapter.ts +114 -0
  39. package/src/telemetry/OpenTelemetryAdapter.test.ts +157 -0
  40. package/src/telemetry/OpenTelemetryAdapter.ts +348 -0
  41. package/src/telemetry/OpenTelemetryProvider.test.ts +113 -0
  42. package/src/telemetry/OpenTelemetryProvider.ts +322 -0
  43. package/src/telemetry/TelemetryService.test.ts +134 -0
  44. package/src/telemetry/TelemetryService.ts +141 -0
  45. package/src/telemetry/opentelemetry.ts +20 -0
  46. package/src/{default-tools → tools}/definitions.ts +35 -28
  47. package/src/{default-tools → tools}/schemas.ts +9 -0
  48. package/src/types/config.ts +3 -1
  49. /package/dist/{default-tools → tools}/constants.d.ts +0 -0
  50. /package/dist/{default-tools → tools}/definitions.d.ts +0 -0
  51. /package/dist/{default-tools → tools}/executors/apply-patch-parser.d.ts +0 -0
  52. /package/dist/{default-tools → tools}/executors/apply-patch.d.ts +0 -0
  53. /package/dist/{default-tools → tools}/executors/bash.d.ts +0 -0
  54. /package/dist/{default-tools → tools}/executors/editor.d.ts +0 -0
  55. /package/dist/{default-tools → tools}/executors/file-read.d.ts +0 -0
  56. /package/dist/{default-tools → tools}/executors/index.d.ts +0 -0
  57. /package/dist/{default-tools → tools}/executors/search.d.ts +0 -0
  58. /package/dist/{default-tools → tools}/executors/web-fetch.d.ts +0 -0
  59. /package/dist/{default-tools → tools}/index.d.ts +0 -0
  60. /package/dist/{default-tools → tools}/model-tool-routing.d.ts +0 -0
  61. /package/dist/{default-tools → tools}/presets.d.ts +0 -0
  62. /package/dist/{default-tools → tools}/types.d.ts +0 -0
  63. /package/src/{default-tools → tools}/constants.ts +0 -0
  64. /package/src/{default-tools → tools}/definitions.test.ts +0 -0
  65. /package/src/{default-tools → tools}/executors/apply-patch-parser.ts +0 -0
  66. /package/src/{default-tools → tools}/executors/apply-patch.ts +0 -0
  67. /package/src/{default-tools → tools}/executors/bash.ts +0 -0
  68. /package/src/{default-tools → tools}/executors/editor.ts +0 -0
  69. /package/src/{default-tools → tools}/executors/file-read.test.ts +0 -0
  70. /package/src/{default-tools → tools}/executors/file-read.ts +0 -0
  71. /package/src/{default-tools → tools}/executors/index.ts +0 -0
  72. /package/src/{default-tools → tools}/executors/search.ts +0 -0
  73. /package/src/{default-tools → tools}/executors/web-fetch.ts +0 -0
  74. /package/src/{default-tools → tools}/index.ts +0 -0
  75. /package/src/{default-tools → tools}/model-tool-routing.test.ts +0 -0
  76. /package/src/{default-tools → tools}/model-tool-routing.ts +0 -0
  77. /package/src/{default-tools → tools}/presets.test.ts +0 -0
  78. /package/src/{default-tools → tools}/presets.ts +0 -0
  79. /package/src/{default-tools → tools}/types.ts +0 -0
@@ -23,6 +23,12 @@ export declare const ReadFilesInputUnionSchema: z.ZodUnion<readonly [z.ZodObject
23
23
  export declare const SearchCodebaseInputSchema: z.ZodObject<{
24
24
  queries: z.ZodArray<z.ZodString>;
25
25
  }, z.core.$strip>;
26
+ /**
27
+ * Union schema for search_codebase tool input, allowing either a single string, an array of strings, or the full object schema
28
+ */
29
+ export declare const SearchCodebaseUnionInputSchema: z.ZodUnion<readonly [z.ZodObject<{
30
+ queries: z.ZodArray<z.ZodString>;
31
+ }, z.core.$strip>, z.ZodArray<z.ZodString>, z.ZodString]>;
26
32
  /**
27
33
  * Schema for run_commands tool input
28
34
  */
@@ -1,7 +1,7 @@
1
1
  import type { AgentConfig, AgentHooks, ConsecutiveMistakeLimitContext, ConsecutiveMistakeLimitDecision, HookErrorMode, TeamEvent, Tool } from "@clinebot/agents";
2
2
  import type { providers as LlmsProviders } from "@clinebot/llms";
3
- import type { AgentMode, BasicLogger, SessionExecutionConfig, SessionPromptConfig, SessionWorkspaceConfig } from "@clinebot/shared";
4
- import type { ToolRoutingRule } from "../default-tools/model-tool-routing.js";
3
+ import type { AgentMode, BasicLogger, ITelemetryService, SessionExecutionConfig, SessionPromptConfig, SessionWorkspaceConfig } from "@clinebot/shared";
4
+ import type { ToolRoutingRule } from "../tools/model-tool-routing.js";
5
5
  export type CoreAgentMode = AgentMode;
6
6
  export interface CoreModelConfig {
7
7
  providerId: string;
@@ -35,6 +35,7 @@ export interface CoreSessionConfig extends CoreModelConfig, CoreRuntimeFeatures,
35
35
  hooks?: AgentHooks;
36
36
  hookErrorMode?: HookErrorMode;
37
37
  logger?: BasicLogger;
38
+ telemetry?: ITelemetryService;
38
39
  extraTools?: Tool[];
39
40
  pluginPaths?: string[];
40
41
  extensions?: AgentConfig["extensions"];
package/package.json CHANGED
@@ -1,10 +1,19 @@
1
1
  {
2
2
  "name": "@clinebot/core",
3
- "version": "0.0.4",
3
+ "version": "0.0.6",
4
4
  "main": "./dist/index.node.js",
5
5
  "dependencies": {
6
- "@clinebot/agents": "0.0.4",
7
- "@clinebot/llms": "0.0.4",
6
+ "@clinebot/agents": "0.0.6",
7
+ "@clinebot/llms": "0.0.6",
8
+ "@opentelemetry/api": "^1.9.0",
9
+ "@opentelemetry/api-logs": "^0.56.0",
10
+ "@opentelemetry/exporter-logs-otlp-http": "^0.56.0",
11
+ "@opentelemetry/exporter-metrics-otlp-http": "^0.56.0",
12
+ "@opentelemetry/resources": "^1.30.1",
13
+ "@opentelemetry/sdk-logs": "^0.56.0",
14
+ "@opentelemetry/sdk-metrics": "^1.30.1",
15
+ "@opentelemetry/semantic-conventions": "^1.37.0",
16
+ "better-sqlite3": "^11.10.0",
8
17
  "nanoid": "^5.1.7",
9
18
  "simple-git": "^3.32.3",
10
19
  "yaml": "^2.8.2",
@@ -20,6 +29,11 @@
20
29
  "development": "./src/index.node.ts",
21
30
  "types": "./dist/index.node.d.ts",
22
31
  "import": "./dist/index.node.js"
32
+ },
33
+ "./telemetry/opentelemetry": {
34
+ "development": "./src/telemetry/opentelemetry.ts",
35
+ "types": "./dist/telemetry/opentelemetry.d.ts",
36
+ "import": "./dist/telemetry/opentelemetry.js"
23
37
  }
24
38
  },
25
39
  "description": "State-aware orchestration for Cline Agent runtimes",
@@ -1,7 +1,7 @@
1
1
  import type { AgentConfig, Tool } from "@clinebot/agents";
2
2
  import YAML from "yaml";
3
3
  import { z } from "zod";
4
- import { ALL_DEFAULT_TOOL_NAMES, type DefaultToolName } from "../default-tools";
4
+ import { ALL_DEFAULT_TOOL_NAMES, type DefaultToolName } from "../tools";
5
5
 
6
6
  const AgentConfigFrontmatterSchema = z.object({
7
7
  name: z.string().trim().min(1),
@@ -45,10 +45,28 @@ const HOOK_CONFIG_FILE_LOOKUP = new Map<string, HookConfigFileName>(
45
45
  Object.values(HookConfigFileName).map((name) => [name.toLowerCase(), name]),
46
46
  );
47
47
 
48
+ const SUPPORTED_HOOK_FILE_EXTENSIONS = new Set([
49
+ "",
50
+ ".sh",
51
+ ".bash",
52
+ ".zsh",
53
+ ".js",
54
+ ".mjs",
55
+ ".cjs",
56
+ ".ts",
57
+ ".mts",
58
+ ".cts",
59
+ ".py",
60
+ ]);
61
+
48
62
  export function toHookConfigFileName(
49
63
  fileName: string,
50
64
  ): HookConfigFileName | undefined {
51
- const key = basename(fileName, extname(fileName)).trim().toLowerCase();
65
+ const extension = extname(fileName).toLowerCase();
66
+ if (!SUPPORTED_HOOK_FILE_EXTENSIONS.has(extension)) {
67
+ return undefined;
68
+ }
69
+ const key = basename(fileName, extension).trim().toLowerCase();
52
70
  return HOOK_CONFIG_FILE_LOOKUP.get(key);
53
71
  }
54
72
 
package/src/index.node.ts CHANGED
@@ -100,6 +100,9 @@ export {
100
100
  OCI_HEADER_OPC_REQUEST_ID,
101
101
  refreshOcaToken,
102
102
  } from "./auth/oca";
103
+ export async function loadOpenTelemetryAdapter() {
104
+ return import("./telemetry/opentelemetry.js");
105
+ }
103
106
  export { startLocalOAuthServer } from "./auth/server";
104
107
  export type {
105
108
  OAuthCredentials,
package/src/index.ts CHANGED
@@ -19,6 +19,7 @@ export type {
19
19
  BasicLogger,
20
20
  ConnectorHookEvent,
21
21
  HookSessionContext,
22
+ ITelemetryService,
22
23
  RpcAddProviderActionRequest,
23
24
  RpcChatMessage,
24
25
  RpcChatRunTurnRequest,
@@ -38,6 +39,12 @@ export type {
38
39
  RpcSaveProviderSettingsActionRequest,
39
40
  SessionLineage,
40
41
  TeamProgressProjectionEvent,
42
+ TelemetryArray,
43
+ TelemetryMetadata,
44
+ TelemetryObject,
45
+ TelemetryPrimitive,
46
+ TelemetryProperties,
47
+ TelemetryValue,
41
48
  ToolPolicy,
42
49
  } from "@clinebot/shared";
43
50
  export {
@@ -85,25 +92,6 @@ export {
85
92
  ChatSummarySchema,
86
93
  ChatViewStateSchema,
87
94
  } from "./chat/chat-schema";
88
- export {
89
- ALL_DEFAULT_TOOL_NAMES,
90
- type AskQuestionExecutor,
91
- type CreateBuiltinToolsOptions,
92
- type CreateDefaultToolsOptions,
93
- createBuiltinTools,
94
- createDefaultExecutors,
95
- createDefaultTools,
96
- createDefaultToolsWithPreset,
97
- createToolPoliciesWithPreset,
98
- type DefaultExecutorsOptions,
99
- type DefaultToolName,
100
- DefaultToolNames,
101
- type DefaultToolsConfig,
102
- type ToolExecutors,
103
- type ToolPolicyPresetName,
104
- type ToolPresetName,
105
- ToolPresets,
106
- } from "./default-tools";
107
95
  export {
108
96
  hasMcpSettingsFile,
109
97
  InMemoryMcpManager,
@@ -135,6 +123,34 @@ export {
135
123
  buildTeamProgressSummary,
136
124
  toTeamProgressLifecycleEvent,
137
125
  } from "./team";
126
+ export type { ITelemetryAdapter } from "./telemetry/ITelemetryAdapter";
127
+ export {
128
+ LoggerTelemetryAdapter,
129
+ type LoggerTelemetryAdapterOptions,
130
+ } from "./telemetry/LoggerTelemetryAdapter";
131
+ export {
132
+ TelemetryService,
133
+ type TelemetryServiceOptions,
134
+ } from "./telemetry/TelemetryService";
135
+ export {
136
+ ALL_DEFAULT_TOOL_NAMES,
137
+ type AskQuestionExecutor,
138
+ type CreateBuiltinToolsOptions,
139
+ type CreateDefaultToolsOptions,
140
+ createBuiltinTools,
141
+ createDefaultExecutors,
142
+ createDefaultTools,
143
+ createDefaultToolsWithPreset,
144
+ createToolPoliciesWithPreset,
145
+ type DefaultExecutorsOptions,
146
+ type DefaultToolName,
147
+ DefaultToolNames,
148
+ type DefaultToolsConfig,
149
+ type ToolExecutors,
150
+ type ToolPolicyPresetName,
151
+ type ToolPresetName,
152
+ ToolPresets,
153
+ } from "./tools";
138
154
  // Compatibility barrel (legacy imports).
139
155
  export type { RuntimeEnvironment, SessionEvent, StoredMessages } from "./types";
140
156
  export type { SessionStatus } from "./types/common";
@@ -35,6 +35,7 @@ type StoredModelsFile = {
35
35
  name: string;
36
36
  supportsVision?: boolean;
37
37
  supportsAttachments?: boolean;
38
+ supportsReasoning?: boolean;
38
39
  }
39
40
  >;
40
41
  }
@@ -132,6 +133,24 @@ async function writeModelsFile(
132
133
  await writeFile(filePath, `${JSON.stringify(state, null, 2)}\n`, "utf8");
133
134
  }
134
135
 
136
+ function toRpcProviderModel(
137
+ modelId: string,
138
+ info: {
139
+ name?: string;
140
+ capabilities?: string[];
141
+ thinkingConfig?: unknown;
142
+ },
143
+ ): RpcProviderModel {
144
+ return {
145
+ id: modelId,
146
+ name: info.name ?? modelId,
147
+ supportsAttachments: info.capabilities?.includes("files"),
148
+ supportsVision: info.capabilities?.includes("images"),
149
+ supportsReasoning:
150
+ info.capabilities?.includes("reasoning") || info.thinkingConfig != null,
151
+ };
152
+ }
153
+
135
154
  function toProviderCapabilities(
136
155
  capabilities: RpcProviderCapability[] | undefined,
137
156
  ): Array<"reasoning" | "prompt-cache" | "tools"> | undefined {
@@ -352,6 +371,7 @@ export async function addLocalProvider(
352
371
  const modelsState = await readModelsFile(modelsPath);
353
372
  const supportsVision = capabilities?.includes("vision") ?? false;
354
373
  const supportsAttachments = supportsVision;
374
+ const supportsReasoning = capabilities?.includes("reasoning") ?? false;
355
375
  modelsState.providers[providerId] = {
356
376
  provider: {
357
377
  name: providerName,
@@ -368,6 +388,7 @@ export async function addLocalProvider(
368
388
  name: modelId,
369
389
  supportsVision,
370
390
  supportsAttachments,
391
+ supportsReasoning,
371
392
  },
372
393
  ]),
373
394
  ),
@@ -394,12 +415,13 @@ export async function listLocalProviders(
394
415
  const providerItems = await Promise.all(
395
416
  ids.map(async (id): Promise<RpcProviderListItem> => {
396
417
  const info = await models.getProvider(id);
418
+ const providerModels = await getLocalProviderModels(id);
397
419
  const persistedSettings = state.providers[id]?.settings;
398
420
  const providerName = info?.name ?? titleCaseFromId(id);
399
421
  return {
400
422
  id,
401
423
  name: providerName,
402
- models: null,
424
+ models: providerModels.models.length,
403
425
  color: stableColor(id),
404
426
  letter: createLetter(providerName),
405
427
  enabled: Boolean(persistedSettings),
@@ -413,6 +435,7 @@ export async function listLocalProviders(
413
435
  defaultModelId: info?.defaultModelId,
414
436
  authDescription: "This provider uses API keys for authentication.",
415
437
  baseUrlDescription: "The base endpoint to use for provider requests.",
438
+ modelList: providerModels.models,
416
439
  };
417
440
  }),
418
441
  );
@@ -430,12 +453,7 @@ export async function getLocalProviderModels(
430
453
  const modelMap = await models.getModelsForProvider(id);
431
454
  const items = Object.entries(modelMap)
432
455
  .sort(([a], [b]) => a.localeCompare(b))
433
- .map(([modelId, info]) => ({
434
- id: modelId,
435
- name: info.name ?? modelId,
436
- supportsAttachments: info.capabilities?.includes("files"),
437
- supportsVision: info.capabilities?.includes("images"),
438
- }));
456
+ .map(([modelId, info]) => toRpcProviderModel(modelId, info));
439
457
  return {
440
458
  providerId: id,
441
459
  models: items,
@@ -17,6 +17,22 @@ async function createWorkspaceWithHook(
17
17
  }
18
18
 
19
19
  describe("createHookConfigFileHooks", () => {
20
+ it("ignores example hook files", async () => {
21
+ const { workspace } = await createWorkspaceWithHook(
22
+ "PreToolUse.example",
23
+ 'echo \'HOOK_CONTROL\t{"cancel":true,"context":"should-not-run"}\'\n',
24
+ );
25
+ try {
26
+ const hooks = createHookConfigFileHooks({
27
+ cwd: workspace,
28
+ workspacePath: workspace,
29
+ });
30
+ expect(hooks).toBeUndefined();
31
+ } finally {
32
+ await rm(workspace, { recursive: true, force: true });
33
+ }
34
+ });
35
+
20
36
  it("executes extensionless legacy hook files via bash fallback", async () => {
21
37
  const { workspace } = await createWorkspaceWithHook(
22
38
  "PreToolUse",
@@ -103,4 +119,35 @@ describe("createHookConfigFileHooks", () => {
103
119
  await rm(workspace, { recursive: true, force: true });
104
120
  }
105
121
  });
122
+
123
+ it("executes python hook files", async () => {
124
+ const { workspace } = await createWorkspaceWithHook(
125
+ "PreToolUse.py",
126
+ 'print(\'HOOK_CONTROL\\t{"cancel": false, "context": "python-ok"}\')\n',
127
+ );
128
+ try {
129
+ const hooks = createHookConfigFileHooks({
130
+ cwd: workspace,
131
+ workspacePath: workspace,
132
+ });
133
+ expect(hooks?.onToolCallStart).toBeTypeOf("function");
134
+ const control = await hooks?.onToolCallStart?.({
135
+ agentId: "agent_1",
136
+ conversationId: "conv_1",
137
+ parentAgentId: null,
138
+ iteration: 1,
139
+ call: {
140
+ id: "call_1",
141
+ name: "read_file",
142
+ input: { path: "README.md" },
143
+ },
144
+ });
145
+ expect(control).toMatchObject({
146
+ cancel: false,
147
+ context: "python-ok",
148
+ });
149
+ } finally {
150
+ await rm(workspace, { recursive: true, force: true });
151
+ }
152
+ });
106
153
  });
@@ -323,6 +323,9 @@ function inferHookCommand(path: string): string[] {
323
323
  ) {
324
324
  return ["bun", "run", path];
325
325
  }
326
+ if (lowered.endsWith(".py")) {
327
+ return ["python3", path];
328
+ }
326
329
  // Default to bash for legacy hook files with no extension/shebang.
327
330
  return ["/bin/bash", path];
328
331
  }
@@ -3,6 +3,7 @@ import { tmpdir } from "node:os";
3
3
  import { join } from "node:path";
4
4
  import type { Tool } from "@clinebot/agents";
5
5
  import { describe, expect, it } from "vitest";
6
+ import { TelemetryService } from "../telemetry/TelemetryService";
6
7
  import { DefaultRuntimeBuilder } from "./runtime-builder";
7
8
 
8
9
  function makeSpawnTool(): Tool {
@@ -55,6 +56,25 @@ describe("DefaultRuntimeBuilder", () => {
55
56
  expect(runtime.logger).toBe(logger);
56
57
  });
57
58
 
59
+ it("forwards telemetry for downstream runtime consumers", () => {
60
+ const telemetry = new TelemetryService();
61
+ const runtime = new DefaultRuntimeBuilder().build({
62
+ config: {
63
+ providerId: "anthropic",
64
+ modelId: "claude-sonnet-4-6",
65
+ apiKey: "key",
66
+ systemPrompt: "test",
67
+ cwd: process.cwd(),
68
+ enableTools: false,
69
+ enableSpawnAgent: false,
70
+ enableAgentTeams: false,
71
+ telemetry,
72
+ },
73
+ });
74
+
75
+ expect(runtime.telemetry).toBe(telemetry);
76
+ });
77
+
58
78
  it("uses readonly preset in plan mode", () => {
59
79
  const runtime = new DefaultRuntimeBuilder().build({
60
80
  config: {
@@ -14,6 +14,7 @@ import {
14
14
  type SkillConfig,
15
15
  type UserInstructionConfigWatcher,
16
16
  } from "../agents";
17
+ import { SqliteTeamStore } from "../storage/sqlite-team-store";
17
18
  import {
18
19
  createBuiltinTools,
19
20
  DEFAULT_MODEL_TOOL_ROUTING_RULES,
@@ -22,8 +23,7 @@ import {
22
23
  type ToolExecutors,
23
24
  ToolPresets,
24
25
  type ToolRoutingRule,
25
- } from "../default-tools";
26
- import { SqliteTeamStore } from "../storage/sqlite-team-store";
26
+ } from "../tools";
27
27
  import type { CoreAgentMode, CoreSessionConfig } from "../types/config";
28
28
  import type {
29
29
  RuntimeBuilder,
@@ -522,6 +522,7 @@ export class DefaultRuntimeBuilder implements RuntimeBuilder {
522
522
  return {
523
523
  tools,
524
524
  logger: logger ?? config.logger,
525
+ telemetry: input.telemetry ?? config.telemetry,
525
526
  teamRuntime,
526
527
  completionGuard,
527
528
  shutdown: (reason: string) => {
@@ -3,7 +3,7 @@ import { tmpdir } from "node:os";
3
3
  import { join } from "node:path";
4
4
  import type { Tool } from "@clinebot/agents";
5
5
  import { describe, expect, it } from "vitest";
6
- import { createBuiltinTools } from "../default-tools";
6
+ import { createBuiltinTools } from "../tools";
7
7
  import { DefaultRuntimeBuilder } from "./runtime-builder";
8
8
 
9
9
  type LegacyConfig = {
@@ -5,15 +5,16 @@ import type {
5
5
  AgentTeamsRuntime,
6
6
  Tool,
7
7
  } from "@clinebot/agents";
8
- import type { BasicLogger } from "@clinebot/shared";
8
+ import type { BasicLogger, ITelemetryService } from "@clinebot/shared";
9
9
  import type { UserInstructionConfigWatcher } from "../agents";
10
- import type { ToolExecutors } from "../default-tools";
10
+ import type { ToolExecutors } from "../tools";
11
11
  import type { CoreSessionConfig } from "../types/config";
12
12
 
13
13
  export interface BuiltRuntime {
14
14
  tools: Tool[];
15
15
  hooks?: AgentHooks;
16
16
  logger?: BasicLogger;
17
+ telemetry?: ITelemetryService;
17
18
  teamRuntime?: AgentTeamsRuntime;
18
19
  completionGuard?: () => string | undefined;
19
20
  shutdown: (reason: string) => Promise<void> | void;
@@ -29,6 +30,7 @@ export interface RuntimeBuilderInput {
29
30
  userInstructionWatcher?: UserInstructionConfigWatcher;
30
31
  defaultToolExecutors?: Partial<ToolExecutors>;
31
32
  logger?: BasicLogger;
33
+ telemetry?: ITelemetryService;
32
34
  }
33
35
 
34
36
  export interface RuntimeBuilder {
@@ -3,6 +3,7 @@ import { tmpdir } from "node:os";
3
3
  import { join } from "node:path";
4
4
  import type { AgentResult } from "@clinebot/agents";
5
5
  import { describe, expect, it, vi } from "vitest";
6
+ import { TelemetryService } from "../telemetry/TelemetryService";
6
7
  import { SessionSource } from "../types/common";
7
8
  import type { CoreSessionConfig } from "../types/config";
8
9
  import { DefaultSessionManager } from "./default-session-manager";
@@ -70,6 +71,77 @@ function createConfig(
70
71
  }
71
72
 
72
73
  describe("DefaultSessionManager", () => {
74
+ it("emits session lifecycle telemetry when configured", async () => {
75
+ const sessionId = "sess-telemetry";
76
+ const manifest = createManifest(sessionId);
77
+ const adapter = {
78
+ name: "test",
79
+ emit: vi.fn(),
80
+ emitRequired: vi.fn(),
81
+ recordCounter: vi.fn(),
82
+ recordHistogram: vi.fn(),
83
+ recordGauge: vi.fn(),
84
+ isEnabled: vi.fn(() => true),
85
+ flush: vi.fn().mockResolvedValue(undefined),
86
+ dispose: vi.fn().mockResolvedValue(undefined),
87
+ };
88
+ const telemetry = new TelemetryService({
89
+ adapters: [adapter],
90
+ distinctId: distinctId,
91
+ });
92
+ const sessionService = {
93
+ ensureSessionsDir: vi.fn().mockReturnValue("/tmp/sessions"),
94
+ createRootSessionWithArtifacts: vi.fn().mockResolvedValue({
95
+ manifestPath: "/tmp/manifest.json",
96
+ transcriptPath: "/tmp/transcript.log",
97
+ hookPath: "/tmp/hook.log",
98
+ messagesPath: "/tmp/messages.json",
99
+ manifest,
100
+ }),
101
+ persistSessionMessages: vi.fn(),
102
+ updateSessionStatus: vi.fn().mockResolvedValue({
103
+ updated: true,
104
+ endedAt: "2026-01-01T00:00:05.000Z",
105
+ }),
106
+ writeSessionManifest: vi.fn(),
107
+ listSessions: vi.fn().mockResolvedValue([]),
108
+ deleteSession: vi.fn().mockResolvedValue({ deleted: true }),
109
+ };
110
+ const runtimeBuilder = {
111
+ build: vi.fn().mockReturnValue({
112
+ tools: [],
113
+ shutdown: vi.fn(),
114
+ }),
115
+ };
116
+ const agent = {
117
+ run: vi.fn().mockResolvedValue(createResult()),
118
+ continue: vi.fn().mockResolvedValue(createResult()),
119
+ getMessages: vi.fn().mockReturnValue([]),
120
+ abort: vi.fn(),
121
+ shutdown: vi.fn().mockResolvedValue(undefined),
122
+ };
123
+ const manager = new DefaultSessionManager({
124
+ distinctId,
125
+ sessionService: sessionService as never,
126
+ runtimeBuilder: runtimeBuilder as never,
127
+ createAgent: () => agent as never,
128
+ telemetry,
129
+ });
130
+
131
+ await manager.start({
132
+ config: createConfig({ telemetry, sessionId }),
133
+ prompt: "hello",
134
+ });
135
+
136
+ expect(adapter.emit).toHaveBeenCalledWith(
137
+ "session.started",
138
+ expect.objectContaining({
139
+ sessionId,
140
+ distinct_id: distinctId,
141
+ }),
142
+ );
143
+ });
144
+
73
145
  it("runs a non-interactive prompt and persists messages/status", async () => {
74
146
  const sessionId = "sess-1";
75
147
  const manifest = createManifest(sessionId);