@clinebot/core 0.0.36 → 0.0.37
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/ClineCore.d.ts +312 -3
- package/dist/ClineCore.d.ts.map +1 -1
- package/dist/account/cline-account-service.d.ts.map +1 -1
- package/dist/cron/cron-event-ingress.d.ts +38 -0
- package/dist/cron/cron-event-ingress.d.ts.map +1 -0
- package/dist/cron/cron-materializer.d.ts +36 -0
- package/dist/cron/cron-materializer.d.ts.map +1 -0
- package/dist/cron/cron-reconciler.d.ts +62 -0
- package/dist/cron/cron-reconciler.d.ts.map +1 -0
- package/dist/cron/cron-report-writer.d.ts +41 -0
- package/dist/cron/cron-report-writer.d.ts.map +1 -0
- package/dist/cron/cron-runner.d.ts +43 -0
- package/dist/cron/cron-runner.d.ts.map +1 -0
- package/dist/cron/cron-schema.d.ts +3 -0
- package/dist/cron/cron-schema.d.ts.map +1 -0
- package/dist/cron/cron-service.d.ts +57 -0
- package/dist/cron/cron-service.d.ts.map +1 -0
- package/dist/cron/cron-spec-parser.d.ts +27 -0
- package/dist/cron/cron-spec-parser.d.ts.map +1 -0
- package/dist/cron/cron-watcher.d.ts +23 -0
- package/dist/cron/cron-watcher.d.ts.map +1 -0
- package/dist/cron/scheduler.d.ts +3 -1
- package/dist/cron/scheduler.d.ts.map +1 -1
- package/dist/cron/sqlite-cron-store.d.ts +230 -0
- package/dist/cron/sqlite-cron-store.d.ts.map +1 -0
- package/dist/extensions/plugin/plugin-config-loader.d.ts +7 -1
- package/dist/extensions/plugin/plugin-config-loader.d.ts.map +1 -1
- package/dist/extensions/plugin/plugin-loader.d.ts +10 -6
- package/dist/extensions/plugin/plugin-loader.d.ts.map +1 -1
- package/dist/extensions/plugin/plugin-sandbox.d.ts +7 -1
- package/dist/extensions/plugin/plugin-sandbox.d.ts.map +1 -1
- package/dist/extensions/plugin-sandbox-bootstrap.js +236 -275
- package/dist/extensions/tools/constants.d.ts +1 -0
- package/dist/extensions/tools/constants.d.ts.map +1 -1
- package/dist/extensions/tools/definitions.d.ts +2 -3
- package/dist/extensions/tools/definitions.d.ts.map +1 -1
- package/dist/extensions/tools/executors/editor.d.ts.map +1 -1
- package/dist/extensions/tools/helpers.d.ts +1 -0
- package/dist/extensions/tools/helpers.d.ts.map +1 -1
- package/dist/extensions/tools/index.d.ts +1 -2
- package/dist/extensions/tools/index.d.ts.map +1 -1
- package/dist/extensions/tools/presets.d.ts +1 -1
- package/dist/extensions/tools/schemas.d.ts +25 -3
- package/dist/extensions/tools/schemas.d.ts.map +1 -1
- package/dist/extensions/tools/team/delegated-agent.d.ts +2 -2
- package/dist/extensions/tools/team/delegated-agent.d.ts.map +1 -1
- package/dist/extensions/tools/team/multi-agent.d.ts +7 -3
- package/dist/extensions/tools/team/multi-agent.d.ts.map +1 -1
- package/dist/extensions/tools/team/team-tools.d.ts.map +1 -1
- package/dist/extensions/tools/types.d.ts +0 -5
- package/dist/extensions/tools/types.d.ts.map +1 -1
- package/dist/hooks/hook-bridge.d.ts +118 -0
- package/dist/hooks/hook-bridge.d.ts.map +1 -0
- package/dist/hooks/hook-file-hooks.d.ts +2 -1
- package/dist/hooks/hook-file-hooks.d.ts.map +1 -1
- package/dist/hooks/hook-registry.d.ts +16 -0
- package/dist/hooks/hook-registry.d.ts.map +1 -0
- package/dist/hub/browser-websocket.d.ts.map +1 -1
- package/dist/hub/client.d.ts +7 -1
- package/dist/hub/client.d.ts.map +1 -1
- package/dist/hub/daemon-entry.js +721 -461
- package/dist/hub/daemon.d.ts.map +1 -1
- package/dist/hub/defaults.d.ts +8 -4
- package/dist/hub/defaults.d.ts.map +1 -1
- package/dist/hub/index.js +665 -415
- package/dist/hub/runtime-handlers.d.ts.map +1 -1
- package/dist/hub/server.d.ts +18 -0
- package/dist/hub/server.d.ts.map +1 -1
- package/dist/hub/session-client.d.ts +3 -0
- package/dist/hub/session-client.d.ts.map +1 -1
- package/dist/hub/start-shared-server.d.ts.map +1 -1
- package/dist/hub/ui-client.d.ts +1 -0
- package/dist/hub/ui-client.d.ts.map +1 -1
- package/dist/index.d.ts +9 -7
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +756 -467
- package/dist/llms/cline-recommended-models.d.ts +20 -0
- package/dist/llms/cline-recommended-models.d.ts.map +1 -0
- package/dist/llms/handler-factory.d.ts +16 -0
- package/dist/llms/handler-factory.d.ts.map +1 -0
- package/dist/llms/provider-defaults.d.ts.map +1 -1
- package/dist/llms/provider-settings.d.ts +45 -2
- package/dist/llms/provider-settings.d.ts.map +1 -1
- package/dist/llms/runtime-registry.d.ts.map +1 -1
- package/dist/runtime/agent-config-adapter.d.ts +148 -0
- package/dist/runtime/agent-config-adapter.d.ts.map +1 -0
- package/dist/runtime/agent-runtime-config-builder.d.ts +96 -0
- package/dist/runtime/agent-runtime-config-builder.d.ts.map +1 -0
- package/dist/runtime/history.d.ts +6 -0
- package/dist/runtime/history.d.ts.map +1 -1
- package/dist/runtime/host.d.ts.map +1 -1
- package/dist/runtime/loop-detection.d.ts +59 -0
- package/dist/runtime/loop-detection.d.ts.map +1 -0
- package/dist/runtime/mistake-tracker.d.ts +69 -0
- package/dist/runtime/mistake-tracker.d.ts.map +1 -0
- package/dist/runtime/runtime-builder.d.ts.map +1 -1
- package/dist/runtime/runtime-event-adapter.d.ts +102 -0
- package/dist/runtime/runtime-event-adapter.d.ts.map +1 -0
- package/dist/runtime/runtime-host.d.ts +28 -3
- package/dist/runtime/runtime-host.d.ts.map +1 -1
- package/dist/runtime/session-runtime-orchestrator.d.ts +261 -0
- package/dist/runtime/session-runtime-orchestrator.d.ts.map +1 -0
- package/dist/runtime/session-runtime.d.ts +16 -3
- package/dist/runtime/session-runtime.d.ts.map +1 -1
- package/dist/runtime/user-input-builder.d.ts +24 -0
- package/dist/runtime/user-input-builder.d.ts.map +1 -0
- package/dist/services/index.js +28 -0
- package/dist/services/local-runtime-bootstrap.d.ts.map +1 -1
- package/dist/services/plugin-tools.d.ts.map +1 -1
- package/dist/services/providers/local-provider-registry.d.ts +197 -21
- package/dist/services/providers/local-provider-registry.d.ts.map +1 -1
- package/dist/services/providers/local-provider-service.d.ts +3 -1
- package/dist/services/providers/local-provider-service.d.ts.map +1 -1
- package/dist/services/session-data.d.ts.map +1 -1
- package/dist/services/session-telemetry.d.ts +7 -2
- package/dist/services/session-telemetry.d.ts.map +1 -1
- package/dist/services/storage/file-team-store.d.ts.map +1 -1
- package/dist/services/storage/provider-settings-legacy-migration.d.ts.map +1 -1
- package/dist/services/storage/provider-settings-manager.d.ts +1 -0
- package/dist/services/storage/provider-settings-manager.d.ts.map +1 -1
- package/dist/services/storage/sqlite-team-store.d.ts.map +1 -1
- package/dist/session/conversation-store.d.ts +30 -0
- package/dist/session/conversation-store.d.ts.map +1 -0
- package/dist/session/message-builder.d.ts +65 -0
- package/dist/session/message-builder.d.ts.map +1 -0
- package/dist/session/session-manifest.d.ts +1 -1
- package/dist/transports/hub.d.ts +14 -3
- package/dist/transports/hub.d.ts.map +1 -1
- package/dist/transports/local.d.ts +14 -4
- package/dist/transports/local.d.ts.map +1 -1
- package/dist/transports/remote.d.ts.map +1 -1
- package/dist/types/chat-schema.d.ts +5 -5
- package/dist/types/config.d.ts +9 -0
- package/dist/types/config.d.ts.map +1 -1
- package/dist/types/events.d.ts +7 -6
- package/dist/types/events.d.ts.map +1 -1
- package/dist/types/provider-settings.d.ts +2 -2
- package/dist/types/provider-settings.d.ts.map +1 -1
- package/dist/types/session.d.ts +5 -2
- package/dist/types/session.d.ts.map +1 -1
- package/dist/types.d.ts +4 -4
- package/dist/types.d.ts.map +1 -1
- package/package.json +4 -4
- package/src/ClineCore.ts +691 -6
- package/src/account/cline-account-service.ts +44 -6
- package/src/cron/cron-event-ingress.ts +357 -0
- package/src/cron/cron-materializer.ts +97 -0
- package/src/cron/cron-reconciler.ts +241 -0
- package/src/cron/cron-report-writer.ts +153 -0
- package/src/cron/cron-runner.ts +495 -0
- package/src/cron/cron-schema.ts +127 -0
- package/src/cron/cron-service.ts +163 -0
- package/src/cron/cron-spec-parser.ts +489 -0
- package/src/cron/cron-watcher.ts +102 -0
- package/src/cron/index.ts +10 -0
- package/src/cron/scheduler.ts +141 -6
- package/src/cron/sqlite-cron-store.ts +1286 -0
- package/src/extensions/plugin/plugin-config-loader.ts +21 -1
- package/src/extensions/plugin/plugin-loader.ts +25 -9
- package/src/extensions/plugin/plugin-sandbox-bootstrap.ts +151 -1
- package/src/extensions/plugin/plugin-sandbox.ts +131 -7
- package/src/extensions/tools/constants.ts +2 -0
- package/src/extensions/tools/definitions.ts +31 -22
- package/src/extensions/tools/executors/editor.ts +4 -3
- package/src/extensions/tools/helpers.ts +24 -0
- package/src/extensions/tools/index.ts +1 -2
- package/src/extensions/tools/presets.ts +1 -1
- package/src/extensions/tools/schemas.ts +13 -18
- package/src/extensions/tools/team/delegated-agent.ts +8 -3
- package/src/extensions/tools/team/multi-agent.ts +135 -19
- package/src/extensions/tools/team/team-tools.ts +151 -91
- package/src/extensions/tools/types.ts +0 -6
- package/src/hooks/hook-bridge.ts +489 -0
- package/src/hooks/hook-file-hooks.ts +58 -3
- package/src/hooks/hook-registry.ts +257 -0
- package/src/hub/browser-websocket.ts +26 -4
- package/src/hub/client.ts +72 -13
- package/src/hub/daemon-entry.ts +35 -0
- package/src/hub/daemon.ts +117 -14
- package/src/hub/defaults.ts +39 -12
- package/src/hub/runtime-handlers.ts +4 -3
- package/src/hub/server.ts +506 -77
- package/src/hub/session-client.ts +43 -1
- package/src/hub/start-shared-server.ts +3 -0
- package/src/hub/ui-client.ts +4 -0
- package/src/index.ts +46 -1
- package/src/llms/cline-recommended-models.ts +167 -0
- package/src/llms/handler-factory.ts +56 -0
- package/src/llms/provider-defaults.ts +17 -1
- package/src/llms/provider-settings.ts +48 -1
- package/src/llms/runtime-registry.ts +1 -0
- package/src/runtime/agent-config-adapter.ts +636 -0
- package/src/runtime/agent-runtime-config-builder.ts +205 -0
- package/src/runtime/error-feedback.ts +142 -0
- package/src/runtime/history.ts +137 -0
- package/src/runtime/host.ts +22 -0
- package/src/runtime/loop-detection.ts +162 -0
- package/src/runtime/mistake-tracker.ts +221 -0
- package/src/runtime/runtime-builder.ts +61 -5
- package/src/runtime/runtime-event-adapter.ts +412 -0
- package/src/runtime/runtime-host.ts +45 -1
- package/src/runtime/session-runtime-orchestrator.ts +1253 -0
- package/src/runtime/session-runtime.ts +16 -2
- package/src/runtime/user-input-builder.ts +167 -0
- package/src/services/local-runtime-bootstrap.ts +128 -22
- package/src/services/plugin-tools.ts +1 -0
- package/src/services/providers/local-provider-registry.ts +273 -57
- package/src/services/providers/local-provider-service.ts +67 -7
- package/src/services/session-data.ts +16 -14
- package/src/services/session-telemetry.ts +6 -15
- package/src/services/storage/file-team-store.ts +1 -5
- package/src/services/storage/provider-settings-legacy-migration.ts +8 -47
- package/src/services/storage/provider-settings-manager.ts +16 -1
- package/src/services/storage/sqlite-team-store.ts +1 -5
- package/src/session/conversation-store.ts +77 -0
- package/src/session/message-builder.ts +941 -0
- package/src/transports/hub.ts +458 -33
- package/src/transports/local.ts +296 -65
- package/src/transports/remote.ts +1 -0
- package/src/types/config.ts +9 -0
- package/src/types/events.ts +8 -6
- package/src/types/index.ts +3 -0
- package/src/types/provider-settings.ts +8 -1
- package/src/types/session.ts +5 -2
- package/src/types.ts +15 -1
- package/dist/cron/index.d.ts +0 -6
- package/dist/cron/index.d.ts.map +0 -1
- package/dist/services/telemetry/index.js +0 -28
|
@@ -0,0 +1,257 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Lifecycle-hook registration wiring for the legacy `AgentHooks` and
|
|
3
|
+
* `AgentExtension` surfaces onto a `HookEngine`.
|
|
4
|
+
*
|
|
5
|
+
* @see PLAN.md §3.1 — moved from `packages/agents/src/runtime/hook-registry.ts`.
|
|
6
|
+
* @see PLAN.md §3.3.3 — OLD `config.hooks.*` → NEW placement.
|
|
7
|
+
*
|
|
8
|
+
* Verbatim port: pure wiring with no state. After the migration this is
|
|
9
|
+
* invoked by `HookBridge` and the legacy-agent facade's `addTools`
|
|
10
|
+
* pathway. Lives under `packages/core/src/hooks/` per §3.1.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import type {
|
|
14
|
+
AgentConfig,
|
|
15
|
+
AgentExtension,
|
|
16
|
+
AgentExtensionHookStage,
|
|
17
|
+
AgentHookControl,
|
|
18
|
+
HookEngine,
|
|
19
|
+
HookHandler,
|
|
20
|
+
} from "@clinebot/shared";
|
|
21
|
+
|
|
22
|
+
type LifecycleConfig = Pick<AgentConfig, "hooks" | "extensions">;
|
|
23
|
+
|
|
24
|
+
type ExtensionStageHandlerSpec = {
|
|
25
|
+
handler: (
|
|
26
|
+
extension: AgentExtension,
|
|
27
|
+
event: { payload: unknown },
|
|
28
|
+
) => Promise<AgentHookControl | undefined> | AgentHookControl | undefined;
|
|
29
|
+
name: string;
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
const EXTENSION_STAGE_HANDLERS: Record<
|
|
33
|
+
AgentExtensionHookStage,
|
|
34
|
+
ExtensionStageHandlerSpec
|
|
35
|
+
> = {
|
|
36
|
+
input: {
|
|
37
|
+
name: "onInput",
|
|
38
|
+
handler: (extension, event) =>
|
|
39
|
+
extension.onInput?.(event.payload as never) as
|
|
40
|
+
| AgentHookControl
|
|
41
|
+
| undefined,
|
|
42
|
+
},
|
|
43
|
+
session_start: {
|
|
44
|
+
name: "onSessionStart",
|
|
45
|
+
handler: (extension, event) =>
|
|
46
|
+
extension.onSessionStart?.(event.payload as never) as
|
|
47
|
+
| AgentHookControl
|
|
48
|
+
| undefined,
|
|
49
|
+
},
|
|
50
|
+
run_start: {
|
|
51
|
+
name: "onRunStart",
|
|
52
|
+
handler: (extension, event) =>
|
|
53
|
+
extension.onRunStart?.(event.payload as never) as
|
|
54
|
+
| AgentHookControl
|
|
55
|
+
| undefined,
|
|
56
|
+
},
|
|
57
|
+
iteration_start: {
|
|
58
|
+
name: "onIterationStart",
|
|
59
|
+
handler: (extension, event) =>
|
|
60
|
+
extension.onIterationStart?.(event.payload as never) as
|
|
61
|
+
| AgentHookControl
|
|
62
|
+
| undefined,
|
|
63
|
+
},
|
|
64
|
+
turn_start: {
|
|
65
|
+
name: "onTurnStart",
|
|
66
|
+
handler: (extension, event) =>
|
|
67
|
+
extension.onTurnStart?.(event.payload as never) as
|
|
68
|
+
| AgentHookControl
|
|
69
|
+
| undefined,
|
|
70
|
+
},
|
|
71
|
+
before_agent_start: {
|
|
72
|
+
name: "onBeforeAgentStart",
|
|
73
|
+
handler: (extension, event) =>
|
|
74
|
+
extension.onBeforeAgentStart?.(event.payload as never) as
|
|
75
|
+
| AgentHookControl
|
|
76
|
+
| undefined,
|
|
77
|
+
},
|
|
78
|
+
tool_call_before: {
|
|
79
|
+
name: "onToolCall",
|
|
80
|
+
handler: (extension, event) =>
|
|
81
|
+
extension.onToolCall?.(event.payload as never) as
|
|
82
|
+
| AgentHookControl
|
|
83
|
+
| undefined,
|
|
84
|
+
},
|
|
85
|
+
tool_call_after: {
|
|
86
|
+
name: "onToolResult",
|
|
87
|
+
handler: (extension, event) =>
|
|
88
|
+
extension.onToolResult?.(event.payload as never) as
|
|
89
|
+
| AgentHookControl
|
|
90
|
+
| undefined,
|
|
91
|
+
},
|
|
92
|
+
turn_end: {
|
|
93
|
+
name: "onTurnEnd",
|
|
94
|
+
handler: (extension, event) =>
|
|
95
|
+
extension.onTurnEnd?.(event.payload as never) as
|
|
96
|
+
| AgentHookControl
|
|
97
|
+
| undefined,
|
|
98
|
+
},
|
|
99
|
+
stop_error: {
|
|
100
|
+
name: "onAgentError",
|
|
101
|
+
handler: (extension, event) =>
|
|
102
|
+
extension.onAgentError?.(event.payload as never) as
|
|
103
|
+
| AgentHookControl
|
|
104
|
+
| undefined,
|
|
105
|
+
},
|
|
106
|
+
iteration_end: {
|
|
107
|
+
name: "onIterationEnd",
|
|
108
|
+
handler: async (extension, event) => {
|
|
109
|
+
await extension.onIterationEnd?.(event.payload as never);
|
|
110
|
+
return undefined;
|
|
111
|
+
},
|
|
112
|
+
},
|
|
113
|
+
run_end: {
|
|
114
|
+
name: "onRunEnd",
|
|
115
|
+
handler: async (extension, event) => {
|
|
116
|
+
await extension.onRunEnd?.(event.payload as never);
|
|
117
|
+
return undefined;
|
|
118
|
+
},
|
|
119
|
+
},
|
|
120
|
+
session_shutdown: {
|
|
121
|
+
name: "onSessionShutdown",
|
|
122
|
+
handler: (extension, event) =>
|
|
123
|
+
extension.onSessionShutdown?.(event.payload as never) as
|
|
124
|
+
| AgentHookControl
|
|
125
|
+
| undefined,
|
|
126
|
+
},
|
|
127
|
+
error: {
|
|
128
|
+
name: "onError",
|
|
129
|
+
handler: async (extension, event) => {
|
|
130
|
+
await extension.onError?.(event.payload as never);
|
|
131
|
+
return undefined;
|
|
132
|
+
},
|
|
133
|
+
},
|
|
134
|
+
runtime_event: {
|
|
135
|
+
name: "onRuntimeEvent",
|
|
136
|
+
handler: async (extension, event) => {
|
|
137
|
+
await extension.onRuntimeEvent?.(event.payload as never);
|
|
138
|
+
return undefined;
|
|
139
|
+
},
|
|
140
|
+
},
|
|
141
|
+
};
|
|
142
|
+
|
|
143
|
+
export function registerLifecycleHandlers(
|
|
144
|
+
hookEngine: HookEngine,
|
|
145
|
+
config: LifecycleConfig,
|
|
146
|
+
): void {
|
|
147
|
+
const register = (handler: HookHandler): void => {
|
|
148
|
+
hookEngine.register(handler);
|
|
149
|
+
};
|
|
150
|
+
const hooks = config.hooks;
|
|
151
|
+
if (hooks?.onSessionStart)
|
|
152
|
+
register({
|
|
153
|
+
name: "hooks.onSessionStart",
|
|
154
|
+
stage: "session_start",
|
|
155
|
+
handle: (event) => hooks.onSessionStart?.(event.payload as never),
|
|
156
|
+
});
|
|
157
|
+
if (hooks?.onRunStart)
|
|
158
|
+
register({
|
|
159
|
+
name: "hooks.onRunStart",
|
|
160
|
+
stage: "run_start",
|
|
161
|
+
handle: (event) => hooks.onRunStart?.(event.payload as never),
|
|
162
|
+
});
|
|
163
|
+
if (hooks?.onRunEnd)
|
|
164
|
+
register({
|
|
165
|
+
name: "hooks.onRunEnd",
|
|
166
|
+
stage: "run_end",
|
|
167
|
+
handle: async (event) => {
|
|
168
|
+
await hooks.onRunEnd?.(event.payload as never);
|
|
169
|
+
return undefined;
|
|
170
|
+
},
|
|
171
|
+
});
|
|
172
|
+
if (hooks?.onIterationStart)
|
|
173
|
+
register({
|
|
174
|
+
name: "hooks.onIterationStart",
|
|
175
|
+
stage: "iteration_start",
|
|
176
|
+
handle: (event) => hooks.onIterationStart?.(event.payload as never),
|
|
177
|
+
});
|
|
178
|
+
if (hooks?.onIterationEnd)
|
|
179
|
+
register({
|
|
180
|
+
name: "hooks.onIterationEnd",
|
|
181
|
+
stage: "iteration_end",
|
|
182
|
+
handle: async (event) => {
|
|
183
|
+
await hooks.onIterationEnd?.(event.payload as never);
|
|
184
|
+
return undefined;
|
|
185
|
+
},
|
|
186
|
+
});
|
|
187
|
+
if (hooks?.onTurnStart)
|
|
188
|
+
register({
|
|
189
|
+
name: "hooks.onTurnStart",
|
|
190
|
+
stage: "turn_start",
|
|
191
|
+
handle: (event) => hooks.onTurnStart?.(event.payload as never),
|
|
192
|
+
});
|
|
193
|
+
if (hooks?.onBeforeAgentStart) {
|
|
194
|
+
register({
|
|
195
|
+
name: "hooks.onBeforeAgentStart",
|
|
196
|
+
stage: "before_agent_start",
|
|
197
|
+
handle: (event) => hooks.onBeforeAgentStart?.(event.payload as never),
|
|
198
|
+
});
|
|
199
|
+
}
|
|
200
|
+
if (hooks?.onTurnEnd) {
|
|
201
|
+
register({
|
|
202
|
+
name: "hooks.onTurnEnd",
|
|
203
|
+
stage: "turn_end",
|
|
204
|
+
handle: (event) => hooks.onTurnEnd?.(event.payload as never),
|
|
205
|
+
});
|
|
206
|
+
}
|
|
207
|
+
if (hooks?.onStopError)
|
|
208
|
+
register({
|
|
209
|
+
name: "hooks.onStopError",
|
|
210
|
+
stage: "stop_error",
|
|
211
|
+
handle: (event) => hooks.onStopError?.(event.payload as never),
|
|
212
|
+
});
|
|
213
|
+
if (hooks?.onToolCallStart)
|
|
214
|
+
register({
|
|
215
|
+
name: "hooks.onToolCallStart",
|
|
216
|
+
stage: "tool_call_before",
|
|
217
|
+
handle: (event) => hooks.onToolCallStart?.(event.payload as never),
|
|
218
|
+
});
|
|
219
|
+
if (hooks?.onToolCallEnd)
|
|
220
|
+
register({
|
|
221
|
+
name: "hooks.onToolCallEnd",
|
|
222
|
+
stage: "tool_call_after",
|
|
223
|
+
handle: (event) => hooks.onToolCallEnd?.(event.payload as never),
|
|
224
|
+
});
|
|
225
|
+
if (hooks?.onSessionShutdown)
|
|
226
|
+
register({
|
|
227
|
+
name: "hooks.onSessionShutdown",
|
|
228
|
+
stage: "session_shutdown",
|
|
229
|
+
handle: (event) => hooks.onSessionShutdown?.(event.payload as never),
|
|
230
|
+
});
|
|
231
|
+
if (hooks?.onError)
|
|
232
|
+
register({
|
|
233
|
+
name: "hooks.onError",
|
|
234
|
+
stage: "error",
|
|
235
|
+
handle: async (event) => {
|
|
236
|
+
await hooks.onError?.(event.payload as never);
|
|
237
|
+
return undefined;
|
|
238
|
+
},
|
|
239
|
+
});
|
|
240
|
+
for (const [index, extension] of (config.extensions ?? []).entries()) {
|
|
241
|
+
if (extension.disabled) continue;
|
|
242
|
+
if (!extension.manifest.capabilities.includes("hooks")) continue;
|
|
243
|
+
const order = String(index).padStart(4, "0");
|
|
244
|
+
const extensionName = extension.name || `extension_${order}`;
|
|
245
|
+
const base = `${order}:${extensionName}`;
|
|
246
|
+
const subscribedStages = new Set(extension.manifest.hookStages ?? []);
|
|
247
|
+
for (const stage of subscribedStages) {
|
|
248
|
+
const stageHandler = EXTENSION_STAGE_HANDLERS[stage];
|
|
249
|
+
if (!stageHandler) continue;
|
|
250
|
+
register({
|
|
251
|
+
name: `${base}.${stageHandler.name}`,
|
|
252
|
+
stage,
|
|
253
|
+
handle: (event) => stageHandler.handler(extension, event),
|
|
254
|
+
});
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
}
|
|
@@ -4,6 +4,7 @@ import type {
|
|
|
4
4
|
HubReplyEnvelope,
|
|
5
5
|
HubTransportFrame,
|
|
6
6
|
} from "@clinebot/shared";
|
|
7
|
+
import { safeJsonParse } from "@clinebot/shared";
|
|
7
8
|
import type { HubCommandTransport } from "./transport";
|
|
8
9
|
|
|
9
10
|
export interface BrowserHubSocketLike {
|
|
@@ -29,7 +30,17 @@ export class BrowserWebSocketHubAdapter {
|
|
|
29
30
|
let closed = false;
|
|
30
31
|
|
|
31
32
|
const sendFrame = (frame: HubTransportFrame): void => {
|
|
32
|
-
|
|
33
|
+
try {
|
|
34
|
+
socket.send(JSON.stringify(frame));
|
|
35
|
+
} catch (error) {
|
|
36
|
+
console.error(
|
|
37
|
+
`[hub] failed to send websocket frame: ${
|
|
38
|
+
error instanceof Error
|
|
39
|
+
? error.stack || error.message
|
|
40
|
+
: String(error)
|
|
41
|
+
}`,
|
|
42
|
+
);
|
|
43
|
+
}
|
|
33
44
|
};
|
|
34
45
|
|
|
35
46
|
const onEvent = (envelope: HubEventEnvelope): void => {
|
|
@@ -37,8 +48,8 @@ export class BrowserWebSocketHubAdapter {
|
|
|
37
48
|
};
|
|
38
49
|
|
|
39
50
|
const onMessage = async (event: { data: string }): Promise<void> => {
|
|
40
|
-
const frame = JSON.parse(event.data) as HubTransportFrame;
|
|
41
51
|
try {
|
|
52
|
+
const frame = JSON.parse(event.data) as HubTransportFrame;
|
|
42
53
|
switch (frame.kind) {
|
|
43
54
|
case "command": {
|
|
44
55
|
const reply = await this.transport.command(frame.envelope);
|
|
@@ -90,13 +101,24 @@ export class BrowserWebSocketHubAdapter {
|
|
|
90
101
|
break;
|
|
91
102
|
}
|
|
92
103
|
} catch (error) {
|
|
93
|
-
|
|
104
|
+
const parsed =
|
|
105
|
+
typeof event.data === "string"
|
|
106
|
+
? safeJsonParse<HubTransportFrame>(event.data)
|
|
107
|
+
: undefined;
|
|
108
|
+
if (!parsed || parsed.kind !== "command") {
|
|
109
|
+
console.error(
|
|
110
|
+
`[hub] rejected malformed websocket frame: ${
|
|
111
|
+
error instanceof Error
|
|
112
|
+
? error.stack || error.message
|
|
113
|
+
: String(error)
|
|
114
|
+
}`,
|
|
115
|
+
);
|
|
94
116
|
return;
|
|
95
117
|
}
|
|
96
118
|
sendFrame({
|
|
97
119
|
kind: "reply",
|
|
98
120
|
envelope: {
|
|
99
|
-
...
|
|
121
|
+
...parsed.envelope,
|
|
100
122
|
ok: false,
|
|
101
123
|
error: {
|
|
102
124
|
code: "command_failed",
|
package/src/hub/client.ts
CHANGED
|
@@ -152,6 +152,7 @@ export class NodeHubClient {
|
|
|
152
152
|
private readonly listeners = new Set<SubscriptionEntry>();
|
|
153
153
|
private readonly subscriptionCounts = new Map<string, number>();
|
|
154
154
|
private lastCloseMessage = "Hub connection closed";
|
|
155
|
+
private registered = false;
|
|
155
156
|
|
|
156
157
|
constructor(private readonly options: HubClientOptions) {
|
|
157
158
|
this.clientId =
|
|
@@ -253,6 +254,7 @@ export class NodeHubClient {
|
|
|
253
254
|
cwd: this.options.cwd,
|
|
254
255
|
},
|
|
255
256
|
} satisfies HubClientRegistration);
|
|
257
|
+
this.registered = true;
|
|
256
258
|
for (const key of this.subscriptionCounts.keys()) {
|
|
257
259
|
this.sendSubscriptionFrame(
|
|
258
260
|
"stream.subscribe",
|
|
@@ -281,27 +283,36 @@ export class NodeHubClient {
|
|
|
281
283
|
command: HubCommandEnvelope["command"],
|
|
282
284
|
payload?: Record<string, unknown>,
|
|
283
285
|
sessionId?: string,
|
|
286
|
+
options?: { timeoutMs?: number | null },
|
|
284
287
|
): Promise<HubReplyEnvelope> {
|
|
285
288
|
await this.connect();
|
|
286
289
|
const requestId = createSessionId("hubreq_");
|
|
287
290
|
const reply = new Promise<HubReplyEnvelope>((resolve, reject) => {
|
|
288
|
-
const
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
291
|
+
const timeoutMs = options?.timeoutMs;
|
|
292
|
+
const timeout =
|
|
293
|
+
timeoutMs === null
|
|
294
|
+
? undefined
|
|
295
|
+
: setTimeout(() => {
|
|
296
|
+
if (!this.pendingReplies.delete(requestId)) {
|
|
297
|
+
return;
|
|
298
|
+
}
|
|
299
|
+
reject(
|
|
300
|
+
new Error(
|
|
301
|
+
`Hub command ${command} timed out after ${timeoutMs ?? HUB_COMMAND_TIMEOUT_MS}ms`,
|
|
302
|
+
),
|
|
303
|
+
);
|
|
304
|
+
}, timeoutMs ?? HUB_COMMAND_TIMEOUT_MS);
|
|
298
305
|
this.pendingReplies.set(requestId, {
|
|
299
306
|
resolve: (value) => {
|
|
300
|
-
|
|
307
|
+
if (timeout) {
|
|
308
|
+
clearTimeout(timeout);
|
|
309
|
+
}
|
|
301
310
|
resolve(value);
|
|
302
311
|
},
|
|
303
312
|
reject: (error) => {
|
|
304
|
-
|
|
313
|
+
if (timeout) {
|
|
314
|
+
clearTimeout(timeout);
|
|
315
|
+
}
|
|
305
316
|
reject(error);
|
|
306
317
|
},
|
|
307
318
|
});
|
|
@@ -328,6 +339,7 @@ export class NodeHubClient {
|
|
|
328
339
|
|
|
329
340
|
close(): void {
|
|
330
341
|
const socket = this.socket;
|
|
342
|
+
this.registered = false;
|
|
331
343
|
if (!socket) {
|
|
332
344
|
return;
|
|
333
345
|
}
|
|
@@ -345,6 +357,21 @@ export class NodeHubClient {
|
|
|
345
357
|
}
|
|
346
358
|
}
|
|
347
359
|
|
|
360
|
+
async dispose(): Promise<void> {
|
|
361
|
+
const socket = this.socket;
|
|
362
|
+
if (socket?.readyState === 1 && this.registered) {
|
|
363
|
+
try {
|
|
364
|
+
await this.command("client.unregister", undefined, undefined, {
|
|
365
|
+
timeoutMs: 2_000,
|
|
366
|
+
});
|
|
367
|
+
} catch {
|
|
368
|
+
// Best-effort unregister during shutdown. The websocket adapter also
|
|
369
|
+
// unregisters clients on close, so failure here should not block teardown.
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
this.close();
|
|
373
|
+
}
|
|
374
|
+
|
|
348
375
|
private sendFrame(frame: HubTransportFrame): void {
|
|
349
376
|
if (!this.socket || this.socket.readyState !== 1) {
|
|
350
377
|
throw new Error(
|
|
@@ -486,7 +513,8 @@ async function probeCompatibleHubUrl(
|
|
|
486
513
|
};
|
|
487
514
|
}
|
|
488
515
|
const buildId = resolveHubBuildId();
|
|
489
|
-
|
|
516
|
+
const recordBuildId = record.buildId?.trim();
|
|
517
|
+
if (!recordBuildId || recordBuildId !== buildId) {
|
|
490
518
|
return {
|
|
491
519
|
status: "build_mismatch",
|
|
492
520
|
url: normalized,
|
|
@@ -572,3 +600,34 @@ export async function ensureCompatibleLocalHubUrl(
|
|
|
572
600
|
spawnDetachedHubServer(options.workspaceRoot ?? process.cwd());
|
|
573
601
|
return await waitForCompatibleHubUrl(owner);
|
|
574
602
|
}
|
|
603
|
+
|
|
604
|
+
export async function requestHubShutdown(url: string): Promise<boolean> {
|
|
605
|
+
const parsed = new URL(url);
|
|
606
|
+
if (parsed.protocol === "ws:") {
|
|
607
|
+
parsed.protocol = "http:";
|
|
608
|
+
} else if (parsed.protocol === "wss:") {
|
|
609
|
+
parsed.protocol = "https:";
|
|
610
|
+
}
|
|
611
|
+
parsed.pathname = "/shutdown";
|
|
612
|
+
parsed.search = "";
|
|
613
|
+
parsed.hash = "";
|
|
614
|
+
const response = await fetch(parsed, { method: "POST" });
|
|
615
|
+
return response.ok;
|
|
616
|
+
}
|
|
617
|
+
|
|
618
|
+
export async function stopLocalHubServerGracefully(): Promise<boolean> {
|
|
619
|
+
const owner = resolveSharedHubOwnerContext();
|
|
620
|
+
const discovery = await readHubDiscovery(owner.discoveryPath);
|
|
621
|
+
if (!discovery?.url) {
|
|
622
|
+
return false;
|
|
623
|
+
}
|
|
624
|
+
try {
|
|
625
|
+
const stopped = await requestHubShutdown(discovery.url);
|
|
626
|
+
if (stopped) {
|
|
627
|
+
return true;
|
|
628
|
+
}
|
|
629
|
+
} catch {
|
|
630
|
+
// Fall through so callers can apply a stronger fallback.
|
|
631
|
+
}
|
|
632
|
+
return false;
|
|
633
|
+
}
|
package/src/hub/daemon-entry.ts
CHANGED
|
@@ -1,8 +1,11 @@
|
|
|
1
|
+
import { initVcr } from "@clinebot/shared";
|
|
1
2
|
import { resolveHubEndpointOptions } from "./defaults";
|
|
2
3
|
import { createLocalHubScheduleRuntimeHandlers } from "./runtime-handlers";
|
|
3
4
|
import { startHubWebSocketServer } from "./server";
|
|
4
5
|
import { resolveSharedHubOwnerContext } from "./workspace";
|
|
5
6
|
|
|
7
|
+
initVcr(process.env.CLINE_VCR);
|
|
8
|
+
|
|
6
9
|
function parseArgs(argv: string[]): {
|
|
7
10
|
cwd: string;
|
|
8
11
|
host?: string;
|
|
@@ -60,6 +63,7 @@ async function main(): Promise<void> {
|
|
|
60
63
|
pathname: endpoint.pathname,
|
|
61
64
|
owner: resolveSharedHubOwnerContext(),
|
|
62
65
|
runtimeHandlers: createLocalHubScheduleRuntimeHandlers(),
|
|
66
|
+
cronOptions: { workspaceRoot: options.cwd },
|
|
63
67
|
});
|
|
64
68
|
|
|
65
69
|
const shutdown = async (): Promise<void> => {
|
|
@@ -67,12 +71,43 @@ async function main(): Promise<void> {
|
|
|
67
71
|
process.exit(0);
|
|
68
72
|
};
|
|
69
73
|
|
|
74
|
+
let fatalShutdownStarted = false;
|
|
75
|
+
const shutdownFatal = (label: string, error: unknown): void => {
|
|
76
|
+
if (fatalShutdownStarted) {
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
fatalShutdownStarted = true;
|
|
80
|
+
const message =
|
|
81
|
+
error instanceof Error ? error.stack || error.message : String(error);
|
|
82
|
+
process.stderr.write(`[hub-daemon] ${label}: ${message}\n`);
|
|
83
|
+
void server
|
|
84
|
+
.close()
|
|
85
|
+
.catch((closeError) => {
|
|
86
|
+
const closeMessage =
|
|
87
|
+
closeError instanceof Error
|
|
88
|
+
? closeError.stack || closeError.message
|
|
89
|
+
: String(closeError);
|
|
90
|
+
process.stderr.write(
|
|
91
|
+
`[hub-daemon] shutdown after ${label} failed: ${closeMessage}\n`,
|
|
92
|
+
);
|
|
93
|
+
})
|
|
94
|
+
.finally(() => {
|
|
95
|
+
process.exit(1);
|
|
96
|
+
});
|
|
97
|
+
};
|
|
98
|
+
|
|
70
99
|
process.on("SIGINT", () => {
|
|
71
100
|
void shutdown();
|
|
72
101
|
});
|
|
73
102
|
process.on("SIGTERM", () => {
|
|
74
103
|
void shutdown();
|
|
75
104
|
});
|
|
105
|
+
process.on("uncaughtException", (error) => {
|
|
106
|
+
shutdownFatal("uncaughtException", error);
|
|
107
|
+
});
|
|
108
|
+
process.on("unhandledRejection", (reason) => {
|
|
109
|
+
shutdownFatal("unhandledRejection", reason);
|
|
110
|
+
});
|
|
76
111
|
|
|
77
112
|
await new Promise<void>(() => {
|
|
78
113
|
// keep daemon process alive
|