@clinebot/core 0.0.35 → 0.0.36
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/README.md +1 -2
- package/dist/ClineCore.d.ts +53 -39
- package/dist/ClineCore.d.ts.map +1 -1
- package/dist/account/index.d.ts +1 -1
- package/dist/account/index.d.ts.map +1 -1
- package/dist/account/rpc.d.ts +6 -6
- package/dist/account/rpc.d.ts.map +1 -1
- package/dist/cron/index.d.ts +6 -0
- package/dist/cron/index.d.ts.map +1 -0
- package/dist/cron/resource-limiter.d.ts +9 -0
- package/dist/cron/resource-limiter.d.ts.map +1 -0
- package/dist/cron/schedule-command-service.d.ts +10 -0
- package/dist/cron/schedule-command-service.d.ts.map +1 -0
- package/dist/cron/schedule-service.d.ts +100 -0
- package/dist/cron/schedule-service.d.ts.map +1 -0
- package/dist/cron/scheduler.d.ts +66 -0
- package/dist/cron/scheduler.d.ts.map +1 -0
- package/dist/cron/sqlite-schedule-store.d.ts +52 -0
- package/dist/cron/sqlite-schedule-store.d.ts.map +1 -0
- package/dist/extensions/config/agent-config-loader.d.ts +4 -3
- package/dist/extensions/config/agent-config-loader.d.ts.map +1 -1
- package/dist/extensions/config/runtime-commands.d.ts +1 -0
- package/dist/extensions/config/runtime-commands.d.ts.map +1 -1
- package/dist/extensions/config/user-instruction-config-loader.d.ts +1 -0
- package/dist/extensions/config/user-instruction-config-loader.d.ts.map +1 -1
- package/dist/extensions/context/agentic-compaction.d.ts +2 -2
- package/dist/extensions/context/agentic-compaction.d.ts.map +1 -1
- package/dist/extensions/context/compaction-shared.d.ts +5 -4
- package/dist/extensions/context/compaction-shared.d.ts.map +1 -1
- package/dist/extensions/context/compaction.d.ts.map +1 -1
- package/dist/extensions/plugin/plugin-config-loader.d.ts +9 -2
- package/dist/extensions/plugin/plugin-config-loader.d.ts.map +1 -1
- package/dist/extensions/plugin/plugin-loader.d.ts +5 -3
- package/dist/extensions/plugin/plugin-loader.d.ts.map +1 -1
- package/dist/extensions/plugin/plugin-module-import.d.ts.map +1 -1
- package/dist/extensions/plugin/plugin-sandbox.d.ts +15 -2
- package/dist/extensions/plugin/plugin-sandbox.d.ts.map +1 -1
- package/dist/extensions/plugin/plugin-targeting.d.ts +7 -0
- package/dist/extensions/plugin/plugin-targeting.d.ts.map +1 -0
- package/dist/extensions/plugin-sandbox-bootstrap.js +211 -211
- package/dist/extensions/tools/definitions.d.ts +1 -1
- package/dist/extensions/tools/definitions.d.ts.map +1 -1
- package/dist/extensions/tools/executors/apply-patch.d.ts +3 -1
- package/dist/extensions/tools/executors/apply-patch.d.ts.map +1 -1
- package/dist/extensions/tools/executors/search.d.ts +1 -1
- package/dist/extensions/tools/executors/search.d.ts.map +1 -1
- package/dist/extensions/tools/index.d.ts +2 -0
- package/dist/extensions/tools/index.d.ts.map +1 -1
- package/dist/extensions/tools/presets.d.ts +26 -43
- package/dist/extensions/tools/presets.d.ts.map +1 -1
- package/dist/extensions/tools/runtime.d.ts +25 -0
- package/dist/extensions/tools/runtime.d.ts.map +1 -0
- package/dist/extensions/tools/schemas.d.ts.map +1 -1
- package/dist/extensions/tools/team/team-tools.d.ts +1 -0
- package/dist/extensions/tools/team/team-tools.d.ts.map +1 -1
- package/dist/hooks/hook-file-hooks.d.ts +4 -1
- package/dist/hooks/hook-file-hooks.d.ts.map +1 -1
- package/dist/hooks/index.d.ts +0 -1
- package/dist/hooks/index.d.ts.map +1 -1
- package/dist/hooks/subprocess.d.ts +8 -1
- package/dist/hooks/subprocess.d.ts.map +1 -1
- package/dist/hub/browser-websocket.d.ts +18 -0
- package/dist/hub/browser-websocket.d.ts.map +1 -0
- package/dist/hub/client.d.ts +45 -0
- package/dist/hub/client.d.ts.map +1 -0
- package/dist/hub/connect.d.ts +15 -0
- package/dist/hub/connect.d.ts.map +1 -0
- package/dist/hub/daemon-entry.d.ts +2 -0
- package/dist/hub/daemon-entry.d.ts.map +1 -0
- package/dist/hub/daemon-entry.js +1045 -0
- package/dist/hub/daemon.d.ts +5 -0
- package/dist/hub/daemon.d.ts.map +1 -0
- package/dist/hub/defaults.d.ts +13 -0
- package/dist/hub/defaults.d.ts.map +1 -0
- package/dist/hub/discovery.d.ts +29 -0
- package/dist/hub/discovery.d.ts.map +1 -0
- package/dist/hub/index.d.ts +15 -0
- package/dist/hub/index.d.ts.map +1 -0
- package/dist/hub/index.js +1044 -0
- package/dist/hub/native-transport.d.ts +17 -0
- package/dist/hub/native-transport.d.ts.map +1 -0
- package/dist/hub/runtime-handlers.d.ts +11 -0
- package/dist/hub/runtime-handlers.d.ts.map +1 -0
- package/dist/hub/server.d.ts +86 -0
- package/dist/hub/server.d.ts.map +1 -0
- package/dist/hub/session-client.d.ts +87 -0
- package/dist/hub/session-client.d.ts.map +1 -0
- package/dist/hub/start-shared-server.d.ts +19 -0
- package/dist/hub/start-shared-server.d.ts.map +1 -0
- package/dist/hub/transport.d.ts +8 -0
- package/dist/hub/transport.d.ts.map +1 -0
- package/dist/hub/ui-client.d.ts +44 -0
- package/dist/hub/ui-client.d.ts.map +1 -0
- package/dist/hub/workspace.d.ts +4 -0
- package/dist/hub/workspace.d.ts.map +1 -0
- package/dist/index.d.ts +26 -15
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +498 -476
- package/dist/llms/configured-provider-registry.d.ts +28 -0
- package/dist/llms/configured-provider-registry.d.ts.map +1 -0
- package/dist/llms/provider-defaults.d.ts +27 -0
- package/dist/llms/provider-defaults.d.ts.map +1 -0
- package/dist/llms/provider-settings.d.ts +202 -0
- package/dist/llms/provider-settings.d.ts.map +1 -0
- package/dist/llms/runtime-config.d.ts +4 -0
- package/dist/llms/runtime-config.d.ts.map +1 -0
- package/dist/llms/runtime-registry.d.ts +20 -0
- package/dist/llms/runtime-registry.d.ts.map +1 -0
- package/dist/llms/runtime-types.d.ts +85 -0
- package/dist/llms/runtime-types.d.ts.map +1 -0
- package/dist/runtime/host.d.ts +1 -2
- package/dist/runtime/host.d.ts.map +1 -1
- package/dist/runtime/rules.d.ts +1 -0
- package/dist/runtime/rules.d.ts.map +1 -1
- package/dist/runtime/runtime-builder.d.ts.map +1 -1
- package/dist/runtime/runtime-host.d.ts +22 -24
- package/dist/runtime/runtime-host.d.ts.map +1 -1
- package/dist/runtime/runtime-oauth-token-manager.d.ts.map +1 -1
- package/dist/runtime/session-runtime.d.ts +1 -19
- package/dist/runtime/session-runtime.d.ts.map +1 -1
- package/dist/services/global-settings.d.ts +12 -0
- package/dist/services/global-settings.d.ts.map +1 -0
- package/dist/services/local-runtime-bootstrap.d.ts +9 -3
- package/dist/services/local-runtime-bootstrap.d.ts.map +1 -1
- package/dist/services/plugin-tools.d.ts +16 -0
- package/dist/services/plugin-tools.d.ts.map +1 -0
- package/dist/services/providers/local-provider-registry.d.ts +4 -4
- package/dist/services/providers/local-provider-registry.d.ts.map +1 -1
- package/dist/services/providers/local-provider-service.d.ts +13 -13
- package/dist/services/providers/local-provider-service.d.ts.map +1 -1
- package/dist/services/session-data.d.ts +1 -1
- package/dist/services/session-data.d.ts.map +1 -1
- package/dist/services/storage/provider-settings-legacy-migration.d.ts +1 -1
- package/dist/services/storage/provider-settings-legacy-migration.d.ts.map +1 -1
- package/dist/services/telemetry/index.js +28 -15
- package/dist/services/workspace-manifest.d.ts +11 -0
- package/dist/services/workspace-manifest.d.ts.map +1 -1
- package/dist/session/persistence-service.d.ts +11 -23
- package/dist/session/persistence-service.d.ts.map +1 -1
- package/dist/session/session-manifest-store.d.ts +22 -0
- package/dist/session/session-manifest-store.d.ts.map +1 -0
- package/dist/session/session-row.d.ts +93 -0
- package/dist/session/session-row.d.ts.map +1 -0
- package/dist/session/session-service.d.ts +2 -102
- package/dist/session/session-service.d.ts.map +1 -1
- package/dist/session/subagent-session-manager.d.ts +36 -0
- package/dist/session/subagent-session-manager.d.ts.map +1 -0
- package/dist/session/team-persistence-store.d.ts +24 -0
- package/dist/session/team-persistence-store.d.ts.map +1 -0
- package/dist/transports/hub.d.ts +47 -0
- package/dist/transports/hub.d.ts.map +1 -0
- package/dist/transports/local.d.ts +10 -6
- package/dist/transports/local.d.ts.map +1 -1
- package/dist/transports/remote.d.ts +10 -0
- package/dist/transports/remote.d.ts.map +1 -0
- package/dist/transports/runtime-host-support.d.ts +3 -2
- package/dist/transports/runtime-host-support.d.ts.map +1 -1
- package/dist/types/chat-schema.d.ts +10 -12
- package/dist/types/chat-schema.d.ts.map +1 -1
- package/dist/types/config.d.ts +8 -7
- package/dist/types/config.d.ts.map +1 -1
- package/dist/types/provider-settings.d.ts +4 -5
- package/dist/types/provider-settings.d.ts.map +1 -1
- package/dist/types/session.d.ts +2 -1
- package/dist/types/session.d.ts.map +1 -1
- package/dist/types.d.ts +8 -1
- package/dist/types.d.ts.map +1 -1
- package/package.json +20 -6
- package/src/ClineCore.ts +68 -40
- package/src/account/index.ts +3 -3
- package/src/account/rpc.ts +12 -12
- package/src/cron/index.ts +5 -0
- package/src/cron/resource-limiter.ts +46 -0
- package/src/cron/schedule-command-service.ts +193 -0
- package/src/cron/schedule-service.ts +703 -0
- package/src/cron/scheduler.ts +637 -0
- package/src/cron/sqlite-schedule-store.ts +708 -0
- package/src/extensions/config/agent-config-loader.ts +17 -7
- package/src/extensions/config/runtime-commands.ts +6 -0
- package/src/extensions/config/user-instruction-config-loader.ts +1 -0
- package/src/extensions/context/agentic-compaction.ts +3 -3
- package/src/extensions/context/basic-compaction.ts +2 -2
- package/src/extensions/context/compaction-shared.ts +5 -4
- package/src/extensions/context/compaction.ts +3 -3
- package/src/extensions/plugin/plugin-config-loader.ts +17 -2
- package/src/extensions/plugin/plugin-loader.ts +48 -4
- package/src/extensions/plugin/plugin-module-import.ts +0 -2
- package/src/extensions/plugin/plugin-sandbox-bootstrap.ts +93 -39
- package/src/extensions/plugin/plugin-sandbox.ts +47 -27
- package/src/extensions/plugin/plugin-targeting.ts +32 -0
- package/src/extensions/tools/definitions.ts +30 -49
- package/src/extensions/tools/executors/apply-patch.ts +69 -80
- package/src/extensions/tools/executors/search.ts +195 -3
- package/src/extensions/tools/index.ts +10 -0
- package/src/extensions/tools/presets.ts +31 -46
- package/src/extensions/tools/runtime.ts +261 -0
- package/src/extensions/tools/schemas.ts +4 -2
- package/src/extensions/tools/team/team-tools.ts +21 -0
- package/src/hooks/hook-file-hooks.ts +8 -2
- package/src/hooks/index.ts +0 -7
- package/src/hooks/subprocess-runner.ts +1 -1
- package/src/hooks/subprocess.ts +9 -0
- package/src/hub/browser-websocket.ts +137 -0
- package/src/hub/client.ts +574 -0
- package/src/hub/connect.ts +156 -0
- package/src/hub/daemon-entry.ts +87 -0
- package/src/hub/daemon.ts +181 -0
- package/src/hub/defaults.ts +43 -0
- package/src/hub/discovery.ts +247 -0
- package/src/hub/index.ts +14 -0
- package/src/hub/native-transport.ts +31 -0
- package/src/hub/runtime-handlers.ts +140 -0
- package/src/hub/server.ts +1888 -0
- package/src/hub/session-client.ts +460 -0
- package/src/hub/start-shared-server.ts +58 -0
- package/src/hub/transport.ts +14 -0
- package/src/hub/ui-client.ts +122 -0
- package/src/hub/workspace.ts +19 -0
- package/src/index.ts +124 -68
- package/src/llms/configured-provider-registry.ts +193 -0
- package/src/llms/provider-defaults.ts +637 -0
- package/src/llms/provider-settings.ts +263 -0
- package/src/llms/runtime-config.ts +43 -0
- package/src/llms/runtime-registry.ts +171 -0
- package/src/llms/runtime-types.ts +121 -0
- package/src/runtime/host.ts +107 -269
- package/src/runtime/index.ts +1 -0
- package/src/runtime/rules.ts +12 -0
- package/src/runtime/runtime-builder.ts +24 -8
- package/src/runtime/runtime-host.ts +89 -61
- package/src/runtime/runtime-oauth-token-manager.ts +11 -15
- package/src/runtime/session-runtime.ts +0 -24
- package/src/services/global-settings.ts +122 -0
- package/src/services/local-runtime-bootstrap.ts +51 -13
- package/src/services/plugin-tools.ts +85 -0
- package/src/services/providers/local-provider-registry.ts +6 -6
- package/src/services/providers/local-provider-service.ts +42 -37
- package/src/services/session-data.ts +15 -9
- package/src/services/storage/provider-settings-legacy-migration.ts +6 -4
- package/src/services/storage/provider-settings-manager.ts +1 -1
- package/src/services/workspace-manifest.ts +18 -0
- package/src/session/file-session-service.ts +1 -1
- package/src/session/index.ts +6 -27
- package/src/session/persistence-service.ts +119 -504
- package/src/session/session-manifest-store.ts +158 -0
- package/src/session/session-row.ts +199 -0
- package/src/session/session-service.ts +17 -376
- package/src/session/session-team-coordination.ts +1 -1
- package/src/session/subagent-session-manager.ts +397 -0
- package/src/session/team-persistence-store.ts +176 -0
- package/src/transports/hub.ts +656 -0
- package/src/transports/local.ts +135 -40
- package/src/transports/remote.ts +26 -0
- package/src/transports/runtime-host-support.ts +63 -9
- package/src/types/chat-schema.ts +4 -5
- package/src/types/config.ts +8 -7
- package/src/types/provider-settings.ts +11 -7
- package/src/types/session.ts +2 -4
- package/src/types.ts +27 -1
- package/dist/hooks/persistent.d.ts +0 -64
- package/dist/hooks/persistent.d.ts.map +0 -1
- package/dist/runtime/rpc-runtime-ensure.d.ts +0 -65
- package/dist/runtime/rpc-runtime-ensure.d.ts.map +0 -1
- package/dist/runtime/rpc-spawn-lease.d.ts +0 -8
- package/dist/runtime/rpc-spawn-lease.d.ts.map +0 -1
- package/dist/session/rpc-session-service.d.ts +0 -16
- package/dist/session/rpc-session-service.d.ts.map +0 -1
- package/dist/session/sqlite-rpc-session-backend.d.ts +0 -31
- package/dist/session/sqlite-rpc-session-backend.d.ts.map +0 -1
- package/dist/transports/rpc.d.ts +0 -51
- package/dist/transports/rpc.d.ts.map +0 -1
- package/src/ClineCore.test.ts +0 -226
- package/src/account/cline-account-service.test.ts +0 -185
- package/src/account/featurebase-token.test.ts +0 -175
- package/src/account/rpc.test.ts +0 -63
- package/src/auth/bounded-ttl-cache.test.ts +0 -38
- package/src/auth/client.test.ts +0 -69
- package/src/auth/cline.test.ts +0 -267
- package/src/auth/codex.test.ts +0 -170
- package/src/auth/oca.test.ts +0 -340
- package/src/auth/server.test.ts +0 -287
- package/src/auth/utils.test.ts +0 -128
- package/src/extensions/config/agent-config-loader.test.ts +0 -236
- package/src/extensions/config/hooks-config-loader.test.ts +0 -20
- package/src/extensions/config/runtime-commands.test.ts +0 -115
- package/src/extensions/config/unified-config-file-watcher.test.ts +0 -196
- package/src/extensions/config/user-instruction-config-loader.test.ts +0 -246
- package/src/extensions/context/compaction.test.ts +0 -483
- package/src/extensions/mcp/config-loader.test.ts +0 -238
- package/src/extensions/mcp/manager.test.ts +0 -105
- package/src/extensions/plugin/plugin-config-loader.test.ts +0 -184
- package/src/extensions/plugin/plugin-loader.test.ts +0 -292
- package/src/extensions/plugin/plugin-sandbox.test.ts +0 -423
- package/src/extensions/tools/definitions.test.ts +0 -780
- package/src/extensions/tools/executors/bash.test.ts +0 -87
- package/src/extensions/tools/executors/editor.test.ts +0 -35
- package/src/extensions/tools/executors/file-read.test.ts +0 -125
- package/src/extensions/tools/model-tool-routing.test.ts +0 -86
- package/src/extensions/tools/presets.test.ts +0 -70
- package/src/extensions/tools/team/multi-agent.lifecycle.test.ts +0 -455
- package/src/extensions/tools/team/spawn-agent-tool.test.ts +0 -381
- package/src/extensions/tools/team/team-tools.test.ts +0 -918
- package/src/hooks/checkpoint-hooks.test.ts +0 -168
- package/src/hooks/hook-file-hooks.test.ts +0 -311
- package/src/hooks/persistent.ts +0 -661
- package/src/runtime/history.test.ts +0 -114
- package/src/runtime/host.test.ts +0 -230
- package/src/runtime/rpc-runtime-ensure.test.ts +0 -123
- package/src/runtime/rpc-runtime-ensure.ts +0 -659
- package/src/runtime/rpc-spawn-lease.test.ts +0 -81
- package/src/runtime/rpc-spawn-lease.ts +0 -156
- package/src/runtime/runtime-builder.team-persistence.test.ts +0 -245
- package/src/runtime/runtime-builder.test.ts +0 -615
- package/src/runtime/runtime-oauth-token-manager.test.ts +0 -137
- package/src/runtime/runtime-parity.test.ts +0 -143
- package/src/services/providers/local-provider-service.test.ts +0 -1062
- package/src/services/session-data.test.ts +0 -160
- package/src/services/storage/provider-settings-legacy-migration.test.ts +0 -424
- package/src/services/storage/provider-settings-manager.test.ts +0 -191
- package/src/services/telemetry/OpenTelemetryAdapter.test.ts +0 -157
- package/src/services/telemetry/OpenTelemetryProvider.test.ts +0 -326
- package/src/services/telemetry/TelemetryLoggerSink.test.ts +0 -42
- package/src/services/telemetry/TelemetryService.test.ts +0 -134
- package/src/services/telemetry/distinct-id.test.ts +0 -57
- package/src/services/workspace/file-indexer.d.ts +0 -11
- package/src/services/workspace/file-indexer.test.ts +0 -156
- package/src/services/workspace/mention-enricher.test.ts +0 -106
- package/src/session/persistence-service.test.ts +0 -300
- package/src/session/rpc-session-service.ts +0 -114
- package/src/session/session-service.team-persistence.test.ts +0 -48
- package/src/session/sqlite-rpc-session-backend.ts +0 -301
- package/src/transports/local.e2e.test.ts +0 -380
- package/src/transports/local.test.ts +0 -2559
- package/src/transports/rpc.test.ts +0 -82
- package/src/transports/rpc.ts +0 -665
|
@@ -1,85 +1,42 @@
|
|
|
1
|
-
import {
|
|
2
|
-
appendFileSync,
|
|
3
|
-
existsSync,
|
|
4
|
-
mkdirSync,
|
|
5
|
-
readFileSync,
|
|
6
|
-
writeFileSync,
|
|
7
|
-
} from "node:fs";
|
|
8
|
-
import { dirname, join } from "node:path";
|
|
1
|
+
import { dirname } from "node:path";
|
|
9
2
|
import type * as LlmsProviders from "@clinebot/llms";
|
|
10
3
|
import type { AgentResult } from "@clinebot/shared";
|
|
11
|
-
import { resolveRootSessionId } from "@clinebot/shared";
|
|
12
|
-
import { ensureHookLogDir } from "@clinebot/shared/storage";
|
|
13
4
|
import { nanoid } from "nanoid";
|
|
14
|
-
import { z } from "zod";
|
|
15
5
|
import type {
|
|
16
6
|
SubAgentEndContext,
|
|
17
7
|
SubAgentStartContext,
|
|
18
8
|
} from "../extensions/tools/team";
|
|
19
9
|
import type { HookEventPayload } from "../hooks";
|
|
20
10
|
import { deleteCheckpointRefs } from "../hooks/checkpoint-hooks";
|
|
21
|
-
import {
|
|
22
|
-
nowIso,
|
|
23
|
-
SessionArtifacts,
|
|
24
|
-
unlinkIfExists,
|
|
25
|
-
} from "../services/session-artifacts";
|
|
11
|
+
import { nowIso, unlinkIfExists } from "../services/session-artifacts";
|
|
26
12
|
import {
|
|
27
13
|
buildManifestFromRow,
|
|
28
|
-
buildMessagesFilePayload,
|
|
29
14
|
deriveTitleFromPrompt,
|
|
30
15
|
normalizeStoredMessagesForPersistence,
|
|
31
16
|
normalizeTitle,
|
|
32
|
-
resolveMessagesFileContext,
|
|
33
17
|
resolveMetadataWithTitle,
|
|
34
18
|
sanitizeMetadata,
|
|
35
19
|
withLatestAssistantTurnMetadata,
|
|
36
20
|
withOccRetry,
|
|
37
|
-
writeEmptyMessagesFile,
|
|
38
21
|
} from "../services/session-data";
|
|
39
|
-
import {
|
|
22
|
+
import type { SessionStatus } from "../types/common";
|
|
40
23
|
import type {
|
|
41
24
|
PersistedSessionUpdateInput,
|
|
42
25
|
SessionMessagesArtifactUploader,
|
|
43
26
|
SessionPersistenceAdapter,
|
|
44
27
|
StoredMessageWithMetadata,
|
|
45
28
|
} from "../types/session";
|
|
46
|
-
import {
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
makeTeamTaskSubSessionId,
|
|
50
|
-
} from "./session-graph";
|
|
51
|
-
import {
|
|
52
|
-
type SessionManifest,
|
|
53
|
-
SessionManifestSchema,
|
|
54
|
-
} from "./session-manifest";
|
|
55
|
-
import type {
|
|
56
|
-
CreateRootSessionWithArtifactsInput,
|
|
57
|
-
RootSessionArtifacts,
|
|
58
|
-
SessionRow,
|
|
59
|
-
UpsertSubagentInput,
|
|
60
|
-
} from "./session-service";
|
|
29
|
+
import { SessionManifestStore } from "./session-manifest-store";
|
|
30
|
+
import type { SessionRow } from "./session-row";
|
|
31
|
+
import { SubagentSessionManager } from "./subagent-session-manager";
|
|
61
32
|
|
|
62
33
|
export type { PersistedSessionUpdateInput, SessionPersistenceAdapter };
|
|
63
34
|
|
|
64
|
-
const SUBSESSION_SOURCE = SessionSource.SUBAGENT;
|
|
65
35
|
const OCC_MAX_RETRIES = 4;
|
|
66
36
|
|
|
67
|
-
const SpawnAgentInputSchema = z.looseObject({
|
|
68
|
-
task: z.string().optional(),
|
|
69
|
-
systemPrompt: z.string().optional(),
|
|
70
|
-
});
|
|
71
|
-
|
|
72
|
-
// ── Service ───────────────────────────────────────────────────────────
|
|
73
|
-
|
|
74
37
|
export class UnifiedSessionPersistenceService {
|
|
75
|
-
private readonly
|
|
76
|
-
private readonly
|
|
77
|
-
private readonly teamTaskLastProgressLineBySession = new Map<
|
|
78
|
-
string,
|
|
79
|
-
string
|
|
80
|
-
>();
|
|
81
|
-
protected readonly artifacts: SessionArtifacts;
|
|
82
|
-
private readonly messagesArtifactUploader?: SessionMessagesArtifactUploader;
|
|
38
|
+
private readonly manifestStore: SessionManifestStore;
|
|
39
|
+
private readonly subagents: SubagentSessionManager;
|
|
83
40
|
private static readonly STALE_REASON = "failed_external_process_exit";
|
|
84
41
|
private static readonly STALE_SOURCE = "stale_session_reconciler";
|
|
85
42
|
private static readonly TEAM_HEARTBEAT_LOG_INTERVAL_MS = 30_000;
|
|
@@ -90,23 +47,16 @@ export class UnifiedSessionPersistenceService {
|
|
|
90
47
|
messagesArtifactUploader?: SessionMessagesArtifactUploader;
|
|
91
48
|
} = {},
|
|
92
49
|
) {
|
|
93
|
-
this.
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
path: string,
|
|
104
|
-
startedAt: string,
|
|
105
|
-
): void {
|
|
106
|
-
writeEmptyMessagesFile(
|
|
107
|
-
path,
|
|
108
|
-
startedAt,
|
|
109
|
-
resolveMessagesFileContext(sessionId),
|
|
50
|
+
this.manifestStore = new SessionManifestStore(
|
|
51
|
+
adapter,
|
|
52
|
+
options.messagesArtifactUploader,
|
|
53
|
+
);
|
|
54
|
+
this.subagents = new SubagentSessionManager(
|
|
55
|
+
adapter,
|
|
56
|
+
this.manifestStore,
|
|
57
|
+
(messages, result, previousMessages) =>
|
|
58
|
+
this.toPersistedMessages(messages, result, previousMessages),
|
|
59
|
+
UnifiedSessionPersistenceService.TEAM_HEARTBEAT_LOG_INTERVAL_MS,
|
|
110
60
|
);
|
|
111
61
|
}
|
|
112
62
|
|
|
@@ -127,99 +77,45 @@ export class UnifiedSessionPersistenceService {
|
|
|
127
77
|
);
|
|
128
78
|
}
|
|
129
79
|
|
|
130
|
-
|
|
80
|
+
ensureSessionsDir(): string {
|
|
81
|
+
return this.manifestStore.ensureSessionsDir();
|
|
82
|
+
}
|
|
131
83
|
|
|
132
|
-
|
|
84
|
+
writeSessionManifest(
|
|
133
85
|
manifestPath: string,
|
|
134
|
-
manifest: SessionManifest,
|
|
86
|
+
manifest: import("./session-manifest").SessionManifest,
|
|
135
87
|
): void {
|
|
136
|
-
|
|
137
|
-
writeFileSync(
|
|
138
|
-
manifestPath,
|
|
139
|
-
`${JSON.stringify(SessionManifestSchema.parse(manifest), null, 2)}\n`,
|
|
140
|
-
"utf8",
|
|
141
|
-
);
|
|
88
|
+
this.manifestStore.writeSessionManifest(manifestPath, manifest);
|
|
142
89
|
}
|
|
143
90
|
|
|
144
|
-
|
|
145
|
-
this.writeManifestFile(manifestPath, manifest);
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
readSessionManifest(sessionId: string): SessionManifest | undefined {
|
|
149
|
-
return this.readManifestFile(sessionId).manifest;
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
private readManifestFile(sessionId: string): {
|
|
153
|
-
path: string;
|
|
154
|
-
manifest?: SessionManifest;
|
|
155
|
-
} {
|
|
156
|
-
const manifestPath = this.artifacts.sessionManifestPath(sessionId, false);
|
|
157
|
-
if (!existsSync(manifestPath)) return { path: manifestPath };
|
|
158
|
-
try {
|
|
159
|
-
return {
|
|
160
|
-
path: manifestPath,
|
|
161
|
-
manifest: SessionManifestSchema.parse(
|
|
162
|
-
JSON.parse(readFileSync(manifestPath, "utf8")) as SessionManifest,
|
|
163
|
-
),
|
|
164
|
-
};
|
|
165
|
-
} catch {
|
|
166
|
-
return { path: manifestPath };
|
|
167
|
-
}
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
// ── Path resolution ───────────────────────────────────────────────
|
|
171
|
-
|
|
172
|
-
private async resolveArtifactPath(
|
|
91
|
+
readSessionManifest(
|
|
173
92
|
sessionId: string,
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
): Promise<string> {
|
|
177
|
-
const row = await this.adapter.getSession(sessionId);
|
|
178
|
-
const value = row?.[kind];
|
|
179
|
-
return typeof value === "string" && value.trim().length > 0
|
|
180
|
-
? value
|
|
181
|
-
: fallback(sessionId);
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
// ── Team task queue ───────────────────────────────────────────────
|
|
185
|
-
|
|
186
|
-
private teamTaskQueueKey(rootSessionId: string, agentId: string): string {
|
|
187
|
-
return `${rootSessionId}::${agentId}`;
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
private activeTeamTaskSessionId(
|
|
191
|
-
rootSessionId: string,
|
|
192
|
-
parentAgentId: string,
|
|
193
|
-
): string | undefined {
|
|
194
|
-
const queue = this.teamTaskSessionsByAgent.get(
|
|
195
|
-
this.teamTaskQueueKey(rootSessionId, parentAgentId),
|
|
196
|
-
);
|
|
197
|
-
return queue?.at(-1);
|
|
93
|
+
): import("./session-manifest").SessionManifest | undefined {
|
|
94
|
+
return this.manifestStore.readSessionManifest(sessionId);
|
|
198
95
|
}
|
|
199
96
|
|
|
200
|
-
// ── Root session ──────────────────────────────────────────────────
|
|
201
|
-
|
|
202
97
|
async createRootSessionWithArtifacts(
|
|
203
|
-
input: CreateRootSessionWithArtifactsInput,
|
|
204
|
-
): Promise<RootSessionArtifacts> {
|
|
98
|
+
input: import("./session-row").CreateRootSessionWithArtifactsInput,
|
|
99
|
+
): Promise<import("./session-row").RootSessionArtifacts> {
|
|
205
100
|
const startedAt = input.startedAt ?? nowIso();
|
|
206
101
|
const providedId = input.sessionId.trim();
|
|
207
102
|
const sessionId =
|
|
208
103
|
providedId.length > 0 ? providedId : `${Date.now()}_${nanoid(5)}`;
|
|
209
|
-
const messagesPath =
|
|
210
|
-
|
|
211
|
-
|
|
104
|
+
const messagesPath =
|
|
105
|
+
this.manifestStore.artifacts.sessionMessagesPath(sessionId);
|
|
106
|
+
const manifestPath =
|
|
107
|
+
this.manifestStore.artifacts.sessionManifestPath(sessionId);
|
|
212
108
|
const metadata = resolveMetadataWithTitle({
|
|
213
109
|
metadata: input.metadata,
|
|
214
110
|
prompt: input.prompt,
|
|
215
111
|
});
|
|
216
|
-
const manifest =
|
|
217
|
-
version: 1,
|
|
112
|
+
const manifest = {
|
|
113
|
+
version: 1 as const,
|
|
218
114
|
session_id: sessionId,
|
|
219
115
|
source: input.source,
|
|
220
116
|
pid: input.pid,
|
|
221
117
|
started_at: startedAt,
|
|
222
|
-
status: "running",
|
|
118
|
+
status: "running" as const,
|
|
223
119
|
interactive: input.interactive,
|
|
224
120
|
provider: input.provider,
|
|
225
121
|
model: input.model,
|
|
@@ -232,7 +128,7 @@ export class UnifiedSessionPersistenceService {
|
|
|
232
128
|
prompt: input.prompt?.trim() || undefined,
|
|
233
129
|
metadata,
|
|
234
130
|
messages_path: messagesPath,
|
|
235
|
-
}
|
|
131
|
+
};
|
|
236
132
|
|
|
237
133
|
await this.adapter.upsertSession({
|
|
238
134
|
sessionId,
|
|
@@ -264,13 +160,15 @@ export class UnifiedSessionPersistenceService {
|
|
|
264
160
|
updatedAt: nowIso(),
|
|
265
161
|
});
|
|
266
162
|
|
|
267
|
-
this.initializeMessagesFile(
|
|
268
|
-
|
|
163
|
+
this.manifestStore.initializeMessagesFile(
|
|
164
|
+
sessionId,
|
|
165
|
+
messagesPath,
|
|
166
|
+
startedAt,
|
|
167
|
+
);
|
|
168
|
+
this.manifestStore.writeSessionManifest(manifestPath, manifest);
|
|
269
169
|
return { manifestPath, messagesPath, manifest };
|
|
270
170
|
}
|
|
271
171
|
|
|
272
|
-
// ── Session status updates ────────────────────────────────────────
|
|
273
|
-
|
|
274
172
|
async updateSessionStatus(
|
|
275
173
|
sessionId: string,
|
|
276
174
|
status: SessionStatus,
|
|
@@ -293,7 +191,10 @@ export class UnifiedSessionPersistenceService {
|
|
|
293
191
|
);
|
|
294
192
|
if (result.updated) {
|
|
295
193
|
if (status === "cancelled") {
|
|
296
|
-
await this.applyStatusToRunningChildSessions(
|
|
194
|
+
await this.subagents.applyStatusToRunningChildSessions(
|
|
195
|
+
sessionId,
|
|
196
|
+
"cancelled",
|
|
197
|
+
);
|
|
297
198
|
}
|
|
298
199
|
return { updated: true, endedAt };
|
|
299
200
|
}
|
|
@@ -352,9 +253,8 @@ export class UnifiedSessionPersistenceService {
|
|
|
352
253
|
});
|
|
353
254
|
if (!changed.updated) continue;
|
|
354
255
|
|
|
355
|
-
const { path: manifestPath, manifest } =
|
|
356
|
-
input.sessionId
|
|
357
|
-
);
|
|
256
|
+
const { path: manifestPath, manifest } =
|
|
257
|
+
this.manifestStore.readManifestFile(input.sessionId);
|
|
358
258
|
if (manifest) {
|
|
359
259
|
if (input.prompt !== undefined) {
|
|
360
260
|
manifest.prompt = input.prompt ?? undefined;
|
|
@@ -366,411 +266,130 @@ export class UnifiedSessionPersistenceService {
|
|
|
366
266
|
if (nextTitle) manifestMeta.title = nextTitle;
|
|
367
267
|
manifest.metadata =
|
|
368
268
|
Object.keys(manifestMeta).length > 0 ? manifestMeta : undefined;
|
|
369
|
-
this.
|
|
269
|
+
this.manifestStore.writeSessionManifest(manifestPath, manifest);
|
|
370
270
|
}
|
|
371
271
|
return { updated: true };
|
|
372
272
|
}
|
|
373
273
|
return { updated: false };
|
|
374
274
|
}
|
|
375
275
|
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
async queueSpawnRequest(event: HookEventPayload): Promise<void> {
|
|
379
|
-
if (event.hookName !== "tool_call" || event.parent_agent_id !== null)
|
|
380
|
-
return;
|
|
381
|
-
if (event.tool_call?.name !== "spawn_agent") return;
|
|
382
|
-
|
|
383
|
-
const rootSessionId = resolveRootSessionId(event.sessionContext);
|
|
384
|
-
if (!rootSessionId) return;
|
|
385
|
-
|
|
386
|
-
const parsed = SpawnAgentInputSchema.safeParse(event.tool_call.input);
|
|
387
|
-
await this.adapter.enqueueSpawnRequest({
|
|
388
|
-
rootSessionId,
|
|
389
|
-
parentAgentId: event.agent_id,
|
|
390
|
-
task: parsed.success ? parsed.data.task : undefined,
|
|
391
|
-
systemPrompt: parsed.success ? parsed.data.systemPrompt : undefined,
|
|
392
|
-
});
|
|
393
|
-
}
|
|
394
|
-
|
|
395
|
-
// ── Subagent sessions ─────────────────────────────────────────────
|
|
396
|
-
|
|
397
|
-
private buildSubsessionRow(
|
|
398
|
-
root: SessionRow,
|
|
399
|
-
opts: {
|
|
400
|
-
sessionId: string;
|
|
401
|
-
parentSessionId: string;
|
|
402
|
-
parentAgentId: string;
|
|
403
|
-
agentId: string;
|
|
404
|
-
conversationId?: string | null;
|
|
405
|
-
prompt: string;
|
|
406
|
-
startedAt: string;
|
|
407
|
-
messagesPath: string;
|
|
408
|
-
},
|
|
409
|
-
): SessionRow {
|
|
410
|
-
return {
|
|
411
|
-
sessionId: opts.sessionId,
|
|
412
|
-
source: SUBSESSION_SOURCE,
|
|
413
|
-
pid: process.ppid,
|
|
414
|
-
startedAt: opts.startedAt,
|
|
415
|
-
endedAt: null,
|
|
416
|
-
exitCode: null,
|
|
417
|
-
status: "running",
|
|
418
|
-
statusLock: 0,
|
|
419
|
-
interactive: false,
|
|
420
|
-
provider: root.provider,
|
|
421
|
-
model: root.model,
|
|
422
|
-
cwd: root.cwd,
|
|
423
|
-
workspaceRoot: root.workspaceRoot,
|
|
424
|
-
teamName: root.teamName ?? null,
|
|
425
|
-
enableTools: root.enableTools,
|
|
426
|
-
enableSpawn: root.enableSpawn,
|
|
427
|
-
enableTeams: root.enableTeams,
|
|
428
|
-
parentSessionId: opts.parentSessionId,
|
|
429
|
-
parentAgentId: opts.parentAgentId,
|
|
430
|
-
agentId: opts.agentId,
|
|
431
|
-
conversationId: opts.conversationId ?? null,
|
|
432
|
-
isSubagent: true,
|
|
433
|
-
prompt: opts.prompt,
|
|
434
|
-
metadata: resolveMetadataWithTitle({ prompt: opts.prompt }),
|
|
435
|
-
hookPath: "",
|
|
436
|
-
messagesPath: opts.messagesPath,
|
|
437
|
-
updatedAt: opts.startedAt,
|
|
438
|
-
};
|
|
276
|
+
queueSpawnRequest(event: HookEventPayload): Promise<void> {
|
|
277
|
+
return this.subagents.queueSpawnRequest(event);
|
|
439
278
|
}
|
|
440
279
|
|
|
441
|
-
|
|
442
|
-
input: UpsertSubagentInput,
|
|
280
|
+
upsertSubagentSession(
|
|
281
|
+
input: import("./session-row").UpsertSubagentInput,
|
|
443
282
|
): Promise<string | undefined> {
|
|
444
|
-
|
|
445
|
-
if (!rootSessionId) return undefined;
|
|
446
|
-
|
|
447
|
-
const root = await this.adapter.getSession(rootSessionId);
|
|
448
|
-
if (!root) return undefined;
|
|
449
|
-
|
|
450
|
-
const sessionId = makeSubSessionId(rootSessionId, input.agentId);
|
|
451
|
-
const existing = await this.adapter.getSession(sessionId);
|
|
452
|
-
const startedAt = nowIso();
|
|
453
|
-
const artifactPaths = this.artifacts.subagentArtifactPaths(
|
|
454
|
-
sessionId,
|
|
455
|
-
input.agentId,
|
|
456
|
-
this.activeTeamTaskSessionId(rootSessionId, input.parentAgentId),
|
|
457
|
-
);
|
|
458
|
-
|
|
459
|
-
let prompt = input.prompt ?? existing?.prompt ?? undefined;
|
|
460
|
-
if (!prompt) {
|
|
461
|
-
prompt =
|
|
462
|
-
(await this.adapter.claimSpawnRequest(
|
|
463
|
-
rootSessionId,
|
|
464
|
-
input.parentAgentId,
|
|
465
|
-
)) ?? `Subagent run by ${input.parentAgentId}`;
|
|
466
|
-
}
|
|
467
|
-
|
|
468
|
-
if (!existing) {
|
|
469
|
-
await this.adapter.upsertSession(
|
|
470
|
-
this.buildSubsessionRow(root, {
|
|
471
|
-
sessionId,
|
|
472
|
-
parentSessionId: rootSessionId,
|
|
473
|
-
parentAgentId: input.parentAgentId,
|
|
474
|
-
agentId: input.agentId,
|
|
475
|
-
conversationId: input.conversationId,
|
|
476
|
-
prompt,
|
|
477
|
-
startedAt,
|
|
478
|
-
...artifactPaths,
|
|
479
|
-
}),
|
|
480
|
-
);
|
|
481
|
-
this.initializeMessagesFile(
|
|
482
|
-
sessionId,
|
|
483
|
-
artifactPaths.messagesPath,
|
|
484
|
-
startedAt,
|
|
485
|
-
);
|
|
486
|
-
return sessionId;
|
|
487
|
-
}
|
|
488
|
-
|
|
489
|
-
await this.adapter.updateSession({
|
|
490
|
-
sessionId,
|
|
491
|
-
setRunning: true,
|
|
492
|
-
parentSessionId: rootSessionId,
|
|
493
|
-
parentAgentId: input.parentAgentId,
|
|
494
|
-
agentId: input.agentId,
|
|
495
|
-
conversationId: input.conversationId,
|
|
496
|
-
prompt: existing.prompt ?? prompt ?? null,
|
|
497
|
-
metadata: resolveMetadataWithTitle({
|
|
498
|
-
metadata: existing.metadata ?? undefined,
|
|
499
|
-
prompt: existing.prompt ?? prompt ?? null,
|
|
500
|
-
}),
|
|
501
|
-
expectedStatusLock: existing.statusLock,
|
|
502
|
-
});
|
|
503
|
-
return sessionId;
|
|
283
|
+
return this.subagents.upsertSubagentSession(input);
|
|
504
284
|
}
|
|
505
285
|
|
|
506
|
-
|
|
286
|
+
upsertSubagentSessionFromHook(
|
|
507
287
|
event: HookEventPayload,
|
|
508
288
|
): Promise<string | undefined> {
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
const rootSessionId = resolveRootSessionId(event.sessionContext);
|
|
512
|
-
if (!rootSessionId) return undefined;
|
|
513
|
-
|
|
514
|
-
if (event.hookName === "session_shutdown") {
|
|
515
|
-
const sessionId = makeSubSessionId(rootSessionId, event.agent_id);
|
|
516
|
-
const existing = await this.adapter.getSession(sessionId);
|
|
517
|
-
return existing ? sessionId : undefined;
|
|
518
|
-
}
|
|
519
|
-
return await this.upsertSubagentSession({
|
|
520
|
-
agentId: event.agent_id,
|
|
521
|
-
parentAgentId: event.parent_agent_id,
|
|
522
|
-
conversationId: event.taskId,
|
|
523
|
-
rootSessionId,
|
|
524
|
-
});
|
|
289
|
+
return this.subagents.upsertSubagentSessionFromHook(event);
|
|
525
290
|
}
|
|
526
291
|
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
async appendSubagentHookAudit(
|
|
530
|
-
subSessionId: string,
|
|
292
|
+
appendSubagentHookAudit(
|
|
293
|
+
_subSessionId: string,
|
|
531
294
|
event: HookEventPayload,
|
|
532
295
|
): Promise<void> {
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
const logPath = envPath ?? join(ensureHookLogDir(), "hooks.jsonl");
|
|
536
|
-
appendFileSync(
|
|
537
|
-
logPath,
|
|
538
|
-
`${JSON.stringify({ ts: nowIso(), ...event })}\n`,
|
|
539
|
-
"utf8",
|
|
540
|
-
);
|
|
296
|
+
this.subagents.appendSubagentHookAudit(event);
|
|
297
|
+
return Promise.resolve();
|
|
541
298
|
}
|
|
542
299
|
|
|
543
|
-
|
|
300
|
+
persistSessionMessages(
|
|
544
301
|
sessionId: string,
|
|
545
302
|
messages: LlmsProviders.Message[],
|
|
546
303
|
systemPrompt?: string,
|
|
547
304
|
): Promise<void> {
|
|
548
|
-
const path = await this.resolveArtifactPath(
|
|
549
|
-
sessionId,
|
|
550
|
-
"messagesPath",
|
|
551
|
-
(id) => this.artifacts.sessionMessagesPath(id),
|
|
552
|
-
);
|
|
553
305
|
const normalizedMessages = normalizeStoredMessagesForPersistence(
|
|
554
306
|
messages as LlmsProviders.MessageWithMetadata[],
|
|
555
307
|
);
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
messages: normalizedMessages,
|
|
308
|
+
return this.manifestStore.persistSessionMessages(
|
|
309
|
+
sessionId,
|
|
310
|
+
normalizedMessages,
|
|
560
311
|
systemPrompt,
|
|
561
|
-
|
|
562
|
-
const contents = `${JSON.stringify(payload, null, 2)}\n`;
|
|
563
|
-
mkdirSync(dirname(path), { recursive: true });
|
|
564
|
-
writeFileSync(path, contents, "utf8");
|
|
565
|
-
if (!this.messagesArtifactUploader) {
|
|
566
|
-
return;
|
|
567
|
-
}
|
|
568
|
-
try {
|
|
569
|
-
const row = await this.adapter.getSession(sessionId);
|
|
570
|
-
await this.messagesArtifactUploader.uploadMessagesFile({
|
|
571
|
-
sessionId,
|
|
572
|
-
path,
|
|
573
|
-
contents,
|
|
574
|
-
row,
|
|
575
|
-
});
|
|
576
|
-
} catch (error) {
|
|
577
|
-
console.warn(
|
|
578
|
-
`Failed to upload persisted session messages for ${sessionId}`,
|
|
579
|
-
error,
|
|
580
|
-
);
|
|
581
|
-
}
|
|
312
|
+
);
|
|
582
313
|
}
|
|
583
314
|
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
async applySubagentStatus(
|
|
315
|
+
applySubagentStatus(
|
|
587
316
|
subSessionId: string,
|
|
588
317
|
event: HookEventPayload,
|
|
589
318
|
): Promise<void> {
|
|
590
|
-
|
|
591
|
-
subSessionId,
|
|
592
|
-
deriveSubsessionStatus(event),
|
|
593
|
-
);
|
|
319
|
+
return this.subagents.applySubagentStatus(subSessionId, event);
|
|
594
320
|
}
|
|
595
321
|
|
|
596
|
-
|
|
322
|
+
applySubagentStatusBySessionId(
|
|
597
323
|
subSessionId: string,
|
|
598
324
|
status: SessionStatus,
|
|
599
325
|
): Promise<void> {
|
|
600
|
-
|
|
601
|
-
if (!row) return;
|
|
602
|
-
|
|
603
|
-
const endedAt = status === "running" ? null : nowIso();
|
|
604
|
-
const exitCode = status === "running" ? null : status === "failed" ? 1 : 0;
|
|
605
|
-
await this.adapter.updateSession({
|
|
606
|
-
sessionId: subSessionId,
|
|
607
|
-
status,
|
|
608
|
-
endedAt,
|
|
609
|
-
exitCode,
|
|
610
|
-
expectedStatusLock: row.statusLock,
|
|
611
|
-
});
|
|
326
|
+
return this.subagents.applySubagentStatusBySessionId(subSessionId, status);
|
|
612
327
|
}
|
|
613
328
|
|
|
614
|
-
|
|
329
|
+
applyStatusToRunningChildSessions(
|
|
615
330
|
parentSessionId: string,
|
|
616
331
|
status: Exclude<SessionStatus, "running">,
|
|
617
332
|
): Promise<void> {
|
|
618
|
-
|
|
619
|
-
const rows = await this.adapter.listSessions({
|
|
620
|
-
limit: 2000,
|
|
333
|
+
return this.subagents.applyStatusToRunningChildSessions(
|
|
621
334
|
parentSessionId,
|
|
622
|
-
status
|
|
623
|
-
|
|
624
|
-
for (const row of rows) {
|
|
625
|
-
await this.applySubagentStatusBySessionId(row.sessionId, status);
|
|
626
|
-
}
|
|
335
|
+
status,
|
|
336
|
+
);
|
|
627
337
|
}
|
|
628
338
|
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
async onTeamTaskStart(
|
|
339
|
+
onTeamTaskStart(
|
|
632
340
|
rootSessionId: string,
|
|
633
341
|
agentId: string,
|
|
634
342
|
message: string,
|
|
635
343
|
): Promise<void> {
|
|
636
|
-
|
|
637
|
-
if (!root) return;
|
|
638
|
-
|
|
639
|
-
const sessionId = makeTeamTaskSubSessionId(rootSessionId, agentId);
|
|
640
|
-
const startedAt = nowIso();
|
|
641
|
-
const { messagesPath } = this.artifacts.subagentArtifactPaths(
|
|
642
|
-
sessionId,
|
|
643
|
-
agentId,
|
|
644
|
-
);
|
|
645
|
-
|
|
646
|
-
await this.adapter.upsertSession(
|
|
647
|
-
this.buildSubsessionRow(root, {
|
|
648
|
-
sessionId,
|
|
649
|
-
parentSessionId: rootSessionId,
|
|
650
|
-
parentAgentId: "lead",
|
|
651
|
-
agentId,
|
|
652
|
-
prompt: message || `Team task for ${agentId}`,
|
|
653
|
-
startedAt,
|
|
654
|
-
messagesPath,
|
|
655
|
-
}),
|
|
656
|
-
);
|
|
657
|
-
this.initializeMessagesFile(sessionId, messagesPath, startedAt);
|
|
658
|
-
|
|
659
|
-
const key = this.teamTaskQueueKey(rootSessionId, agentId);
|
|
660
|
-
const queue = this.teamTaskSessionsByAgent.get(key) ?? [];
|
|
661
|
-
queue.push(sessionId);
|
|
662
|
-
this.teamTaskSessionsByAgent.set(key, queue);
|
|
344
|
+
return this.subagents.onTeamTaskStart(rootSessionId, agentId, message);
|
|
663
345
|
}
|
|
664
346
|
|
|
665
|
-
|
|
347
|
+
onTeamTaskEnd(
|
|
666
348
|
rootSessionId: string,
|
|
667
349
|
agentId: string,
|
|
668
350
|
status: SessionStatus,
|
|
669
|
-
|
|
351
|
+
summary?: string,
|
|
670
352
|
result?: AgentResult,
|
|
671
353
|
messages?: LlmsProviders.Message[],
|
|
672
354
|
): Promise<void> {
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
if (queue.length === 0) this.teamTaskSessionsByAgent.delete(key);
|
|
679
|
-
if (!sessionId) return;
|
|
680
|
-
|
|
681
|
-
const teammateMessages = result?.messages ?? messages;
|
|
682
|
-
const persistedMessages = this.toPersistedMessages(
|
|
683
|
-
teammateMessages,
|
|
355
|
+
return this.subagents.onTeamTaskEnd(
|
|
356
|
+
rootSessionId,
|
|
357
|
+
agentId,
|
|
358
|
+
status,
|
|
359
|
+
summary,
|
|
684
360
|
result,
|
|
685
361
|
messages,
|
|
686
362
|
);
|
|
687
|
-
if (persistedMessages) {
|
|
688
|
-
await this.persistSessionMessages(sessionId, persistedMessages);
|
|
689
|
-
}
|
|
690
|
-
await this.applySubagentStatusBySessionId(sessionId, status);
|
|
691
|
-
this.teamTaskLastHeartbeatBySession.delete(sessionId);
|
|
692
|
-
this.teamTaskLastProgressLineBySession.delete(sessionId);
|
|
693
363
|
}
|
|
694
364
|
|
|
695
|
-
|
|
365
|
+
onTeamTaskProgress(
|
|
696
366
|
rootSessionId: string,
|
|
697
367
|
agentId: string,
|
|
698
368
|
progress: string,
|
|
699
369
|
options?: { kind?: "heartbeat" | "progress" | "text" },
|
|
700
370
|
): Promise<void> {
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
const kind = options?.kind ?? "progress";
|
|
709
|
-
if (kind === "heartbeat") {
|
|
710
|
-
const now = Date.now();
|
|
711
|
-
const last = this.teamTaskLastHeartbeatBySession.get(sessionId) ?? 0;
|
|
712
|
-
if (
|
|
713
|
-
now - last <
|
|
714
|
-
UnifiedSessionPersistenceService.TEAM_HEARTBEAT_LOG_INTERVAL_MS
|
|
715
|
-
) {
|
|
716
|
-
return;
|
|
717
|
-
}
|
|
718
|
-
this.teamTaskLastHeartbeatBySession.set(sessionId, now);
|
|
719
|
-
}
|
|
720
|
-
|
|
721
|
-
const line =
|
|
722
|
-
kind === "heartbeat"
|
|
723
|
-
? "[progress] heartbeat"
|
|
724
|
-
: kind === "text"
|
|
725
|
-
? `[progress] text: ${trimmed}`
|
|
726
|
-
: `[progress] ${trimmed}`;
|
|
727
|
-
if (this.teamTaskLastProgressLineBySession.get(sessionId) === line) return;
|
|
728
|
-
this.teamTaskLastProgressLineBySession.set(sessionId, line);
|
|
371
|
+
return this.subagents.onTeamTaskProgress(
|
|
372
|
+
rootSessionId,
|
|
373
|
+
agentId,
|
|
374
|
+
progress,
|
|
375
|
+
options,
|
|
376
|
+
);
|
|
729
377
|
}
|
|
730
378
|
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
async handleSubAgentStart(
|
|
379
|
+
handleSubAgentStart(
|
|
734
380
|
rootSessionId: string,
|
|
735
381
|
context: SubAgentStartContext,
|
|
736
382
|
): Promise<void> {
|
|
737
|
-
|
|
738
|
-
agentId: context.subAgentId,
|
|
739
|
-
parentAgentId: context.parentAgentId,
|
|
740
|
-
conversationId: context.conversationId,
|
|
741
|
-
prompt: context.input.task,
|
|
742
|
-
rootSessionId,
|
|
743
|
-
});
|
|
744
|
-
if (!subSessionId) return;
|
|
745
|
-
await this.applySubagentStatusBySessionId(subSessionId, "running");
|
|
383
|
+
return this.subagents.handleSubAgentStart(rootSessionId, context);
|
|
746
384
|
}
|
|
747
385
|
|
|
748
|
-
|
|
386
|
+
handleSubAgentEnd(
|
|
749
387
|
rootSessionId: string,
|
|
750
388
|
context: SubAgentEndContext,
|
|
751
389
|
): Promise<void> {
|
|
752
|
-
|
|
753
|
-
agentId: context.subAgentId,
|
|
754
|
-
parentAgentId: context.parentAgentId,
|
|
755
|
-
conversationId: context.conversationId,
|
|
756
|
-
prompt: context.input.task,
|
|
757
|
-
rootSessionId,
|
|
758
|
-
});
|
|
759
|
-
if (!subSessionId) return;
|
|
760
|
-
|
|
761
|
-
if (context.error) {
|
|
762
|
-
await this.applySubagentStatusBySessionId(subSessionId, "failed");
|
|
763
|
-
return;
|
|
764
|
-
}
|
|
765
|
-
const reason = context.result?.finishReason ?? "completed";
|
|
766
|
-
await this.applySubagentStatusBySessionId(
|
|
767
|
-
subSessionId,
|
|
768
|
-
reason === "aborted" ? "cancelled" : "completed",
|
|
769
|
-
);
|
|
390
|
+
return this.subagents.handleSubAgentEnd(rootSessionId, context);
|
|
770
391
|
}
|
|
771
392
|
|
|
772
|
-
// ── Stale session reconciliation ──────────────────────────────────
|
|
773
|
-
|
|
774
393
|
private isPidAlive(pid: number): boolean {
|
|
775
394
|
if (!Number.isFinite(pid) || pid <= 0) return false;
|
|
776
395
|
try {
|
|
@@ -817,7 +436,10 @@ export class UnifiedSessionPersistenceService {
|
|
|
817
436
|
});
|
|
818
437
|
if (!changed.updated) continue;
|
|
819
438
|
|
|
820
|
-
await this.applyStatusToRunningChildSessions(
|
|
439
|
+
await this.subagents.applyStatusToRunningChildSessions(
|
|
440
|
+
latest.sessionId,
|
|
441
|
+
"failed",
|
|
442
|
+
);
|
|
821
443
|
|
|
822
444
|
const manifest = buildManifestFromRow(latest, {
|
|
823
445
|
status: "failed",
|
|
@@ -825,25 +447,17 @@ export class UnifiedSessionPersistenceService {
|
|
|
825
447
|
exitCode: 1,
|
|
826
448
|
metadata: nextMetadata,
|
|
827
449
|
});
|
|
828
|
-
const { path: manifestPath } = this.readManifestFile(
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
reason,
|
|
840
|
-
sessionId: latest.sessionId,
|
|
841
|
-
pid: latest.pid,
|
|
842
|
-
source: UnifiedSessionPersistenceService.STALE_SOURCE,
|
|
843
|
-
})}\n`,
|
|
844
|
-
"utf8",
|
|
845
|
-
);
|
|
846
|
-
}
|
|
450
|
+
const { path: manifestPath } = this.manifestStore.readManifestFile(
|
|
451
|
+
latest.sessionId,
|
|
452
|
+
);
|
|
453
|
+
this.manifestStore.writeSessionManifest(manifestPath, manifest);
|
|
454
|
+
this.manifestStore.appendStaleSessionHookLog(
|
|
455
|
+
detectedAt,
|
|
456
|
+
latest.sessionId,
|
|
457
|
+
latest.pid,
|
|
458
|
+
reason,
|
|
459
|
+
UnifiedSessionPersistenceService.STALE_SOURCE,
|
|
460
|
+
);
|
|
847
461
|
return {
|
|
848
462
|
...latest,
|
|
849
463
|
status: "failed",
|
|
@@ -857,8 +471,6 @@ export class UnifiedSessionPersistenceService {
|
|
|
857
471
|
return await this.adapter.getSession(row.sessionId);
|
|
858
472
|
}
|
|
859
473
|
|
|
860
|
-
// ── List / reconcile / delete ─────────────────────────────────────
|
|
861
|
-
|
|
862
474
|
async listSessions(limit = 200): Promise<SessionRow[]> {
|
|
863
475
|
const requestedLimit = Math.max(1, Math.floor(limit));
|
|
864
476
|
const scanLimit = Math.min(requestedLimit * 5, 2000);
|
|
@@ -867,7 +479,7 @@ export class UnifiedSessionPersistenceService {
|
|
|
867
479
|
const rows = await this.adapter.listSessions({ limit: scanLimit });
|
|
868
480
|
return rows.slice(0, requestedLimit).map((row) => {
|
|
869
481
|
const meta = sanitizeMetadata(row.metadata ?? undefined);
|
|
870
|
-
const
|
|
482
|
+
const manifest = this.manifestStore.readSessionManifest(row.sessionId);
|
|
871
483
|
const manifestTitle = normalizeTitle(
|
|
872
484
|
typeof manifest?.metadata?.title === "string"
|
|
873
485
|
? (manifest.metadata.title as string)
|
|
@@ -913,9 +525,12 @@ export class UnifiedSessionPersistenceService {
|
|
|
913
525
|
await deleteCheckpointRefs(child.cwd, child.sessionId);
|
|
914
526
|
unlinkIfExists(child.messagesPath);
|
|
915
527
|
unlinkIfExists(
|
|
916
|
-
this.artifacts.sessionManifestPath(
|
|
528
|
+
this.manifestStore.artifacts.sessionManifestPath(
|
|
529
|
+
child.sessionId,
|
|
530
|
+
false,
|
|
531
|
+
),
|
|
917
532
|
);
|
|
918
|
-
this.artifacts.removeSessionDirIfEmpty(child.sessionId);
|
|
533
|
+
this.manifestStore.artifacts.removeSessionDirIfEmpty(child.sessionId);
|
|
919
534
|
}),
|
|
920
535
|
);
|
|
921
536
|
}
|
|
@@ -923,12 +538,12 @@ export class UnifiedSessionPersistenceService {
|
|
|
923
538
|
await deleteCheckpointRefs(row.cwd, id);
|
|
924
539
|
|
|
925
540
|
unlinkIfExists(row.messagesPath);
|
|
926
|
-
unlinkIfExists(this.artifacts.sessionManifestPath(id, false));
|
|
541
|
+
unlinkIfExists(this.manifestStore.artifacts.sessionManifestPath(id, false));
|
|
927
542
|
if (row.isSubagent) {
|
|
928
|
-
this.artifacts.removeSessionDirIfEmpty(id);
|
|
543
|
+
this.manifestStore.artifacts.removeSessionDirIfEmpty(id);
|
|
929
544
|
} else {
|
|
930
545
|
const candidateDirs = new Set<string>([
|
|
931
|
-
this.artifacts.sessionArtifactsDir(id),
|
|
546
|
+
this.manifestStore.artifacts.sessionArtifactsDir(id),
|
|
932
547
|
]);
|
|
933
548
|
for (const path of [row.messagesPath]) {
|
|
934
549
|
if (typeof path === "string" && path.trim().length > 0) {
|
|
@@ -936,7 +551,7 @@ export class UnifiedSessionPersistenceService {
|
|
|
936
551
|
}
|
|
937
552
|
}
|
|
938
553
|
for (const dir of candidateDirs) {
|
|
939
|
-
this.artifacts.removeDir(dir);
|
|
554
|
+
this.manifestStore.artifacts.removeDir(dir);
|
|
940
555
|
}
|
|
941
556
|
}
|
|
942
557
|
return { deleted: true };
|