@clinebot/core 0.0.4 → 0.0.5
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/agents/agent-config-parser.d.ts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.node.js +63 -63
- package/dist/runtime/session-runtime.d.ts +1 -1
- package/dist/session/default-session-manager.d.ts +1 -1
- package/dist/session/session-host.d.ts +1 -1
- package/dist/session/session-manager.d.ts +1 -1
- package/dist/session/unified-session-persistence-service.d.ts +4 -0
- package/dist/types/config.d.ts +1 -1
- package/package.json +4 -3
- package/src/agents/agent-config-parser.ts +1 -1
- package/src/index.ts +19 -19
- package/src/providers/local-provider-service.ts +25 -7
- package/src/runtime/runtime-builder.ts +2 -2
- package/src/runtime/runtime-parity.test.ts +1 -1
- package/src/runtime/session-runtime.ts +1 -1
- package/src/session/default-session-manager.ts +4 -5
- package/src/session/session-host.ts +1 -1
- package/src/session/session-manager.ts +1 -1
- package/src/session/unified-session-persistence-service.ts +213 -23
- package/src/types/config.ts +1 -1
- /package/dist/{default-tools → tools}/constants.d.ts +0 -0
- /package/dist/{default-tools → tools}/definitions.d.ts +0 -0
- /package/dist/{default-tools → tools}/executors/apply-patch-parser.d.ts +0 -0
- /package/dist/{default-tools → tools}/executors/apply-patch.d.ts +0 -0
- /package/dist/{default-tools → tools}/executors/bash.d.ts +0 -0
- /package/dist/{default-tools → tools}/executors/editor.d.ts +0 -0
- /package/dist/{default-tools → tools}/executors/file-read.d.ts +0 -0
- /package/dist/{default-tools → tools}/executors/index.d.ts +0 -0
- /package/dist/{default-tools → tools}/executors/search.d.ts +0 -0
- /package/dist/{default-tools → tools}/executors/web-fetch.d.ts +0 -0
- /package/dist/{default-tools → tools}/index.d.ts +0 -0
- /package/dist/{default-tools → tools}/model-tool-routing.d.ts +0 -0
- /package/dist/{default-tools → tools}/presets.d.ts +0 -0
- /package/dist/{default-tools → tools}/schemas.d.ts +0 -0
- /package/dist/{default-tools → tools}/types.d.ts +0 -0
- /package/src/{default-tools → tools}/constants.ts +0 -0
- /package/src/{default-tools → tools}/definitions.test.ts +0 -0
- /package/src/{default-tools → tools}/definitions.ts +0 -0
- /package/src/{default-tools → tools}/executors/apply-patch-parser.ts +0 -0
- /package/src/{default-tools → tools}/executors/apply-patch.ts +0 -0
- /package/src/{default-tools → tools}/executors/bash.ts +0 -0
- /package/src/{default-tools → tools}/executors/editor.ts +0 -0
- /package/src/{default-tools → tools}/executors/file-read.test.ts +0 -0
- /package/src/{default-tools → tools}/executors/file-read.ts +0 -0
- /package/src/{default-tools → tools}/executors/index.ts +0 -0
- /package/src/{default-tools → tools}/executors/search.ts +0 -0
- /package/src/{default-tools → tools}/executors/web-fetch.ts +0 -0
- /package/src/{default-tools → tools}/index.ts +0 -0
- /package/src/{default-tools → tools}/model-tool-routing.test.ts +0 -0
- /package/src/{default-tools → tools}/model-tool-routing.ts +0 -0
- /package/src/{default-tools → tools}/presets.test.ts +0 -0
- /package/src/{default-tools → tools}/presets.ts +0 -0
- /package/src/{default-tools → tools}/schemas.ts +0 -0
- /package/src/{default-tools → tools}/types.ts +0 -0
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { AgentConfig, AgentHooks, AgentResult, AgentTeamsRuntime, Tool } from "@clinebot/agents";
|
|
2
2
|
import type { BasicLogger } from "@clinebot/shared";
|
|
3
3
|
import type { UserInstructionConfigWatcher } from "../agents";
|
|
4
|
-
import type { ToolExecutors } from "../
|
|
4
|
+
import type { ToolExecutors } from "../tools";
|
|
5
5
|
import type { CoreSessionConfig } from "../types/config";
|
|
6
6
|
export interface BuiltRuntime {
|
|
7
7
|
tools: Tool[];
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { Agent, type AgentConfig, type AgentResult, type ToolApprovalRequest, type ToolApprovalResult } from "@clinebot/agents";
|
|
2
2
|
import type { providers as LlmsProviders } from "@clinebot/llms";
|
|
3
|
-
import { type ToolExecutors } from "../default-tools";
|
|
4
3
|
import type { RuntimeBuilder } from "../runtime/session-runtime";
|
|
5
4
|
import { ProviderSettingsManager } from "../storage/provider-settings-manager";
|
|
5
|
+
import { type ToolExecutors } from "../tools";
|
|
6
6
|
import type { CoreSessionEvent } from "../types/events";
|
|
7
7
|
import type { SessionRecord } from "../types/sessions";
|
|
8
8
|
import type { RpcCoreSessionService } from "./rpc-session-service";
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { AgentConfig, ToolApprovalRequest, ToolApprovalResult } from "@clinebot/agents";
|
|
2
|
-
import type { ToolExecutors } from "../
|
|
2
|
+
import type { ToolExecutors } from "../tools";
|
|
3
3
|
import { RpcCoreSessionService } from "./rpc-session-service";
|
|
4
4
|
import type { SessionManager } from "./session-manager";
|
|
5
5
|
import { CoreSessionService } from "./session-service";
|
|
@@ -15,7 +15,7 @@ export interface StartSessionInput {
|
|
|
15
15
|
userFiles?: string[];
|
|
16
16
|
userInstructionWatcher?: import("../agents").UserInstructionConfigWatcher;
|
|
17
17
|
onTeamRestored?: () => void;
|
|
18
|
-
defaultToolExecutors?: Partial<import("../
|
|
18
|
+
defaultToolExecutors?: Partial<import("../tools").ToolExecutors>;
|
|
19
19
|
toolPolicies?: import("@clinebot/agents").AgentConfig["toolPolicies"];
|
|
20
20
|
requestToolApproval?: (request: import("@clinebot/agents").ToolApprovalRequest) => Promise<import("@clinebot/agents").ToolApprovalResult>;
|
|
21
21
|
}
|
|
@@ -12,6 +12,7 @@ export interface PersistedSessionUpdateInput {
|
|
|
12
12
|
exitCode?: number | null;
|
|
13
13
|
prompt?: string | null;
|
|
14
14
|
metadataJson?: string | null;
|
|
15
|
+
title?: string | null;
|
|
15
16
|
parentSessionId?: string | null;
|
|
16
17
|
parentAgentId?: string | null;
|
|
17
18
|
agentId?: string | null;
|
|
@@ -55,6 +56,8 @@ export declare class UnifiedSessionPersistenceService {
|
|
|
55
56
|
private activeTeamTaskSessionId;
|
|
56
57
|
private subagentArtifactPaths;
|
|
57
58
|
private writeSessionManifestFile;
|
|
59
|
+
private readSessionManifestFile;
|
|
60
|
+
private applyResolvedTitleToRow;
|
|
58
61
|
private createRootSessionId;
|
|
59
62
|
createRootSessionWithArtifacts(input: CreateRootSessionWithArtifactsInput): Promise<RootSessionArtifacts>;
|
|
60
63
|
writeSessionManifest(manifestPath: string, manifest: SessionManifest): void;
|
|
@@ -66,6 +69,7 @@ export declare class UnifiedSessionPersistenceService {
|
|
|
66
69
|
sessionId: string;
|
|
67
70
|
prompt?: string | null;
|
|
68
71
|
metadata?: Record<string, unknown> | null;
|
|
72
|
+
title?: string | null;
|
|
69
73
|
}): Promise<{
|
|
70
74
|
updated: boolean;
|
|
71
75
|
}>;
|
package/dist/types/config.d.ts
CHANGED
|
@@ -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
3
|
import type { AgentMode, BasicLogger, SessionExecutionConfig, SessionPromptConfig, SessionWorkspaceConfig } from "@clinebot/shared";
|
|
4
|
-
import type { ToolRoutingRule } from "../
|
|
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;
|
package/package.json
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@clinebot/core",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.5",
|
|
4
4
|
"main": "./dist/index.node.js",
|
|
5
5
|
"dependencies": {
|
|
6
|
-
"@clinebot/agents": "0.0.
|
|
7
|
-
"@clinebot/llms": "0.0.
|
|
6
|
+
"@clinebot/agents": "0.0.5",
|
|
7
|
+
"@clinebot/llms": "0.0.5",
|
|
8
|
+
"better-sqlite3": "^11.10.0",
|
|
8
9
|
"nanoid": "^5.1.7",
|
|
9
10
|
"simple-git": "^3.32.3",
|
|
10
11
|
"yaml": "^2.8.2",
|
|
@@ -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 "../
|
|
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),
|
package/src/index.ts
CHANGED
|
@@ -85,25 +85,6 @@ export {
|
|
|
85
85
|
ChatSummarySchema,
|
|
86
86
|
ChatViewStateSchema,
|
|
87
87
|
} 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
88
|
export {
|
|
108
89
|
hasMcpSettingsFile,
|
|
109
90
|
InMemoryMcpManager,
|
|
@@ -135,6 +116,25 @@ export {
|
|
|
135
116
|
buildTeamProgressSummary,
|
|
136
117
|
toTeamProgressLifecycleEvent,
|
|
137
118
|
} from "./team";
|
|
119
|
+
export {
|
|
120
|
+
ALL_DEFAULT_TOOL_NAMES,
|
|
121
|
+
type AskQuestionExecutor,
|
|
122
|
+
type CreateBuiltinToolsOptions,
|
|
123
|
+
type CreateDefaultToolsOptions,
|
|
124
|
+
createBuiltinTools,
|
|
125
|
+
createDefaultExecutors,
|
|
126
|
+
createDefaultTools,
|
|
127
|
+
createDefaultToolsWithPreset,
|
|
128
|
+
createToolPoliciesWithPreset,
|
|
129
|
+
type DefaultExecutorsOptions,
|
|
130
|
+
type DefaultToolName,
|
|
131
|
+
DefaultToolNames,
|
|
132
|
+
type DefaultToolsConfig,
|
|
133
|
+
type ToolExecutors,
|
|
134
|
+
type ToolPolicyPresetName,
|
|
135
|
+
type ToolPresetName,
|
|
136
|
+
ToolPresets,
|
|
137
|
+
} from "./tools";
|
|
138
138
|
// Compatibility barrel (legacy imports).
|
|
139
139
|
export type { RuntimeEnvironment, SessionEvent, StoredMessages } from "./types";
|
|
140
140
|
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:
|
|
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,
|
|
@@ -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 "../
|
|
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,
|
|
@@ -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 "../
|
|
6
|
+
import { createBuiltinTools } from "../tools";
|
|
7
7
|
import { DefaultRuntimeBuilder } from "./runtime-builder";
|
|
8
8
|
|
|
9
9
|
type LegacyConfig = {
|
|
@@ -7,7 +7,7 @@ import type {
|
|
|
7
7
|
} from "@clinebot/agents";
|
|
8
8
|
import type { BasicLogger } from "@clinebot/shared";
|
|
9
9
|
import type { UserInstructionConfigWatcher } from "../agents";
|
|
10
|
-
import type { ToolExecutors } from "../
|
|
10
|
+
import type { ToolExecutors } from "../tools";
|
|
11
11
|
import type { CoreSessionConfig } from "../types/config";
|
|
12
12
|
|
|
13
13
|
export interface BuiltRuntime {
|
|
@@ -18,11 +18,6 @@ import { formatUserInputBlock, normalizeUserInput } from "@clinebot/shared";
|
|
|
18
18
|
import { setHomeDirIfUnset } from "@clinebot/shared/storage";
|
|
19
19
|
import { nanoid } from "nanoid";
|
|
20
20
|
import { resolveAndLoadAgentPlugins } from "../agents/plugin-config-loader";
|
|
21
|
-
import {
|
|
22
|
-
createBuiltinTools,
|
|
23
|
-
type ToolExecutors,
|
|
24
|
-
ToolPresets,
|
|
25
|
-
} from "../default-tools";
|
|
26
21
|
import { enrichPromptWithMentions } from "../input";
|
|
27
22
|
import {
|
|
28
23
|
createHookAuditHooks,
|
|
@@ -36,6 +31,7 @@ import {
|
|
|
36
31
|
buildTeamProgressSummary,
|
|
37
32
|
toTeamProgressLifecycleEvent,
|
|
38
33
|
} from "../team";
|
|
34
|
+
import { createBuiltinTools, type ToolExecutors, ToolPresets } from "../tools";
|
|
39
35
|
import { SessionSource, type SessionStatus } from "../types/common";
|
|
40
36
|
import type { CoreSessionConfig } from "../types/config";
|
|
41
37
|
import type { CoreSessionEvent } from "../types/events";
|
|
@@ -565,6 +561,9 @@ export class DefaultSessionManager implements SessionManager {
|
|
|
565
561
|
if (!prompt) {
|
|
566
562
|
throw new Error("prompt cannot be empty");
|
|
567
563
|
}
|
|
564
|
+
if (!session.artifacts && !session.pendingPrompt) {
|
|
565
|
+
session.pendingPrompt = prompt;
|
|
566
|
+
}
|
|
568
567
|
await this.ensureSessionPersisted(session);
|
|
569
568
|
await this.syncOAuthCredentials(session);
|
|
570
569
|
|
|
@@ -9,8 +9,8 @@ import type {
|
|
|
9
9
|
import { getRpcServerDefaultAddress, getRpcServerHealth } from "@clinebot/rpc";
|
|
10
10
|
import { resolveSessionDataDir } from "@clinebot/shared/storage";
|
|
11
11
|
import { nanoid } from "nanoid";
|
|
12
|
-
import type { ToolExecutors } from "../default-tools";
|
|
13
12
|
import { SqliteSessionStore } from "../storage/sqlite-session-store";
|
|
13
|
+
import type { ToolExecutors } from "../tools";
|
|
14
14
|
import { DefaultSessionManager } from "./default-session-manager";
|
|
15
15
|
import { RpcCoreSessionService } from "./rpc-session-service";
|
|
16
16
|
import type { SessionManager } from "./session-manager";
|
|
@@ -16,7 +16,7 @@ export interface StartSessionInput {
|
|
|
16
16
|
userFiles?: string[];
|
|
17
17
|
userInstructionWatcher?: import("../agents").UserInstructionConfigWatcher;
|
|
18
18
|
onTeamRestored?: () => void;
|
|
19
|
-
defaultToolExecutors?: Partial<import("../
|
|
19
|
+
defaultToolExecutors?: Partial<import("../tools").ToolExecutors>;
|
|
20
20
|
toolPolicies?: import("@clinebot/agents").AgentConfig["toolPolicies"];
|
|
21
21
|
requestToolApproval?: (
|
|
22
22
|
request: import("@clinebot/agents").ToolApprovalRequest,
|
|
@@ -10,7 +10,7 @@ import type {
|
|
|
10
10
|
SubAgentStartContext,
|
|
11
11
|
} from "@clinebot/agents";
|
|
12
12
|
import type { providers as LlmsProviders } from "@clinebot/llms";
|
|
13
|
-
import { resolveRootSessionId } from "@clinebot/shared";
|
|
13
|
+
import { normalizeUserInput, resolveRootSessionId } from "@clinebot/shared";
|
|
14
14
|
import { nanoid } from "nanoid";
|
|
15
15
|
import { z } from "zod";
|
|
16
16
|
import type { SessionStatus } from "../types/common";
|
|
@@ -48,6 +48,58 @@ function stringifyMetadataJson(
|
|
|
48
48
|
return JSON.stringify(metadata);
|
|
49
49
|
}
|
|
50
50
|
|
|
51
|
+
function normalizeSessionTitle(title?: string | null): string | undefined {
|
|
52
|
+
const trimmed = title?.trim();
|
|
53
|
+
return trimmed ? trimmed.slice(0, 120) : undefined;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function deriveSessionTitleFromPrompt(
|
|
57
|
+
prompt?: string | null,
|
|
58
|
+
): string | undefined {
|
|
59
|
+
const normalizedPrompt = normalizeUserInput(prompt ?? "").trim();
|
|
60
|
+
if (!normalizedPrompt) {
|
|
61
|
+
return undefined;
|
|
62
|
+
}
|
|
63
|
+
const firstLine = normalizedPrompt.split("\n")[0]?.trim();
|
|
64
|
+
return normalizeSessionTitle(firstLine);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function normalizeMetadataForStorage(
|
|
68
|
+
metadata: Record<string, unknown> | null | undefined,
|
|
69
|
+
): Record<string, unknown> | undefined {
|
|
70
|
+
if (!metadata) {
|
|
71
|
+
return undefined;
|
|
72
|
+
}
|
|
73
|
+
const next = { ...metadata };
|
|
74
|
+
if (typeof next.title === "string") {
|
|
75
|
+
const normalizedTitle = normalizeSessionTitle(next.title);
|
|
76
|
+
if (normalizedTitle) {
|
|
77
|
+
next.title = normalizedTitle;
|
|
78
|
+
} else {
|
|
79
|
+
delete next.title;
|
|
80
|
+
}
|
|
81
|
+
} else {
|
|
82
|
+
delete next.title;
|
|
83
|
+
}
|
|
84
|
+
return Object.keys(next).length > 0 ? next : undefined;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
function metadataWithResolvedTitle(input: {
|
|
88
|
+
metadata?: Record<string, unknown> | null;
|
|
89
|
+
title?: string | null;
|
|
90
|
+
prompt?: string | null;
|
|
91
|
+
}): Record<string, unknown> | undefined {
|
|
92
|
+
const next = { ...(normalizeMetadataForStorage(input.metadata) ?? {}) };
|
|
93
|
+
const resolvedTitle =
|
|
94
|
+
input.title !== undefined
|
|
95
|
+
? normalizeSessionTitle(input.title)
|
|
96
|
+
: deriveSessionTitleFromPrompt(input.prompt);
|
|
97
|
+
if (resolvedTitle) {
|
|
98
|
+
next.title = resolvedTitle;
|
|
99
|
+
}
|
|
100
|
+
return Object.keys(next).length > 0 ? next : undefined;
|
|
101
|
+
}
|
|
102
|
+
|
|
51
103
|
export interface PersistedSessionUpdateInput {
|
|
52
104
|
sessionId: string;
|
|
53
105
|
expectedStatusLock?: number;
|
|
@@ -56,6 +108,7 @@ export interface PersistedSessionUpdateInput {
|
|
|
56
108
|
exitCode?: number | null;
|
|
57
109
|
prompt?: string | null;
|
|
58
110
|
metadataJson?: string | null;
|
|
111
|
+
title?: string | null;
|
|
59
112
|
parentSessionId?: string | null;
|
|
60
113
|
parentAgentId?: string | null;
|
|
61
114
|
agentId?: string | null;
|
|
@@ -173,6 +226,63 @@ export class UnifiedSessionPersistenceService {
|
|
|
173
226
|
);
|
|
174
227
|
}
|
|
175
228
|
|
|
229
|
+
private readSessionManifestFile(sessionId: string): {
|
|
230
|
+
path: string;
|
|
231
|
+
manifest?: SessionManifest;
|
|
232
|
+
} {
|
|
233
|
+
const manifestPath = this.sessionManifestPath(sessionId, false);
|
|
234
|
+
if (!existsSync(manifestPath)) {
|
|
235
|
+
return { path: manifestPath };
|
|
236
|
+
}
|
|
237
|
+
try {
|
|
238
|
+
const manifest = SessionManifestSchema.parse(
|
|
239
|
+
JSON.parse(readFileSync(manifestPath, "utf8")) as SessionManifest,
|
|
240
|
+
);
|
|
241
|
+
return { path: manifestPath, manifest };
|
|
242
|
+
} catch {
|
|
243
|
+
return { path: manifestPath };
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
private applyResolvedTitleToRow(row: SessionRowShape): SessionRowShape {
|
|
248
|
+
const existingMetadata =
|
|
249
|
+
typeof row.metadata_json === "string" &&
|
|
250
|
+
row.metadata_json.trim().length > 0
|
|
251
|
+
? (() => {
|
|
252
|
+
try {
|
|
253
|
+
const parsed = JSON.parse(row.metadata_json) as unknown;
|
|
254
|
+
if (
|
|
255
|
+
parsed &&
|
|
256
|
+
typeof parsed === "object" &&
|
|
257
|
+
!Array.isArray(parsed)
|
|
258
|
+
) {
|
|
259
|
+
return parsed as Record<string, unknown>;
|
|
260
|
+
}
|
|
261
|
+
} catch {
|
|
262
|
+
// Ignore malformed metadata payloads.
|
|
263
|
+
}
|
|
264
|
+
return undefined;
|
|
265
|
+
})()
|
|
266
|
+
: undefined;
|
|
267
|
+
const sanitizedMetadata = normalizeMetadataForStorage(existingMetadata);
|
|
268
|
+
const { manifest } = this.readSessionManifestFile(row.session_id);
|
|
269
|
+
const manifestTitle = normalizeSessionTitle(
|
|
270
|
+
typeof manifest?.metadata?.title === "string"
|
|
271
|
+
? (manifest.metadata.title as string)
|
|
272
|
+
: undefined,
|
|
273
|
+
);
|
|
274
|
+
const resolvedMetadata = manifestTitle
|
|
275
|
+
? {
|
|
276
|
+
...(sanitizedMetadata ?? {}),
|
|
277
|
+
title: manifestTitle,
|
|
278
|
+
}
|
|
279
|
+
: sanitizedMetadata;
|
|
280
|
+
return {
|
|
281
|
+
...row,
|
|
282
|
+
metadata_json: stringifyMetadataJson(resolvedMetadata),
|
|
283
|
+
};
|
|
284
|
+
}
|
|
285
|
+
|
|
176
286
|
private createRootSessionId(): string {
|
|
177
287
|
return `${Date.now()}_${nanoid(5)}`;
|
|
178
288
|
}
|
|
@@ -207,9 +317,13 @@ export class UnifiedSessionPersistenceService {
|
|
|
207
317
|
enable_spawn: input.enableSpawn,
|
|
208
318
|
enable_teams: input.enableTeams,
|
|
209
319
|
prompt: input.prompt?.trim() || undefined,
|
|
210
|
-
metadata:
|
|
320
|
+
metadata: metadataWithResolvedTitle({
|
|
321
|
+
metadata: input.metadata,
|
|
322
|
+
prompt: input.prompt,
|
|
323
|
+
}),
|
|
211
324
|
messages_path: messagesPath,
|
|
212
325
|
});
|
|
326
|
+
const storedMetadata = normalizeMetadataForStorage(manifest.metadata);
|
|
213
327
|
|
|
214
328
|
await this.adapter.upsertSession({
|
|
215
329
|
session_id: sessionId,
|
|
@@ -235,7 +349,7 @@ export class UnifiedSessionPersistenceService {
|
|
|
235
349
|
conversation_id: null,
|
|
236
350
|
is_subagent: 0,
|
|
237
351
|
prompt: manifest.prompt ?? null,
|
|
238
|
-
metadata_json: stringifyMetadataJson(
|
|
352
|
+
metadata_json: stringifyMetadataJson(storedMetadata),
|
|
239
353
|
transcript_path: transcriptPath,
|
|
240
354
|
hook_path: hookPath,
|
|
241
355
|
messages_path: messagesPath,
|
|
@@ -293,40 +407,86 @@ export class UnifiedSessionPersistenceService {
|
|
|
293
407
|
sessionId: string;
|
|
294
408
|
prompt?: string | null;
|
|
295
409
|
metadata?: Record<string, unknown> | null;
|
|
410
|
+
title?: string | null;
|
|
296
411
|
}): Promise<{ updated: boolean }> {
|
|
297
412
|
for (let attempt = 0; attempt < 4; attempt++) {
|
|
298
413
|
const row = await this.adapter.getSession(input.sessionId);
|
|
299
414
|
if (!row || typeof row.status_lock !== "number") {
|
|
300
415
|
return { updated: false };
|
|
301
416
|
}
|
|
417
|
+
const sanitizedMetadata =
|
|
418
|
+
input.metadata === undefined
|
|
419
|
+
? undefined
|
|
420
|
+
: normalizeMetadataForStorage(input.metadata);
|
|
421
|
+
const existingMetadata = (() => {
|
|
422
|
+
const raw = row.metadata_json?.trim();
|
|
423
|
+
if (!raw) {
|
|
424
|
+
return undefined;
|
|
425
|
+
}
|
|
426
|
+
try {
|
|
427
|
+
const parsed = JSON.parse(raw) as unknown;
|
|
428
|
+
if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
|
|
429
|
+
return normalizeMetadataForStorage(
|
|
430
|
+
parsed as Record<string, unknown>,
|
|
431
|
+
);
|
|
432
|
+
}
|
|
433
|
+
} catch {
|
|
434
|
+
// Ignore malformed metadata payloads.
|
|
435
|
+
}
|
|
436
|
+
return undefined;
|
|
437
|
+
})();
|
|
438
|
+
const existingTitle = normalizeSessionTitle(
|
|
439
|
+
typeof existingMetadata?.title === "string"
|
|
440
|
+
? (existingMetadata.title as string)
|
|
441
|
+
: undefined,
|
|
442
|
+
);
|
|
443
|
+
const nextTitle =
|
|
444
|
+
input.title !== undefined
|
|
445
|
+
? normalizeSessionTitle(input.title)
|
|
446
|
+
: input.prompt !== undefined
|
|
447
|
+
? deriveSessionTitleFromPrompt(input.prompt)
|
|
448
|
+
: existingTitle;
|
|
449
|
+
const nextMetadata =
|
|
450
|
+
input.metadata !== undefined
|
|
451
|
+
? { ...(sanitizedMetadata ?? {}) }
|
|
452
|
+
: { ...(existingMetadata ?? {}) };
|
|
453
|
+
if (nextTitle) {
|
|
454
|
+
nextMetadata.title = nextTitle;
|
|
455
|
+
} else {
|
|
456
|
+
delete nextMetadata.title;
|
|
457
|
+
}
|
|
302
458
|
const changed = await this.adapter.updateSession({
|
|
303
459
|
sessionId: input.sessionId,
|
|
304
460
|
prompt: input.prompt,
|
|
305
461
|
metadataJson:
|
|
306
|
-
input.metadata === undefined
|
|
462
|
+
input.metadata === undefined &&
|
|
463
|
+
input.prompt === undefined &&
|
|
464
|
+
input.title === undefined
|
|
307
465
|
? undefined
|
|
308
|
-
: stringifyMetadataJson(
|
|
466
|
+
: stringifyMetadataJson(nextMetadata),
|
|
467
|
+
title: nextTitle,
|
|
309
468
|
expectedStatusLock: row.status_lock,
|
|
310
469
|
});
|
|
311
470
|
if (!changed.updated) {
|
|
312
471
|
continue;
|
|
313
472
|
}
|
|
314
|
-
const manifestPath = this.
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
} catch {
|
|
328
|
-
// Ignore malformed manifests and keep backend session state as source of truth.
|
|
473
|
+
const { path: manifestPath, manifest } = this.readSessionManifestFile(
|
|
474
|
+
input.sessionId,
|
|
475
|
+
);
|
|
476
|
+
if (manifest) {
|
|
477
|
+
if (input.prompt !== undefined) {
|
|
478
|
+
manifest.prompt = input.prompt ?? undefined;
|
|
479
|
+
}
|
|
480
|
+
const nextMetadata =
|
|
481
|
+
input.metadata !== undefined
|
|
482
|
+
? { ...(normalizeMetadataForStorage(input.metadata) ?? {}) }
|
|
483
|
+
: { ...(normalizeMetadataForStorage(manifest.metadata) ?? {}) };
|
|
484
|
+
if (nextTitle) {
|
|
485
|
+
nextMetadata.title = nextTitle;
|
|
329
486
|
}
|
|
487
|
+
manifest.metadata =
|
|
488
|
+
Object.keys(nextMetadata).length > 0 ? nextMetadata : undefined;
|
|
489
|
+
this.writeSessionManifestFile(manifestPath, manifest);
|
|
330
490
|
}
|
|
331
491
|
return { updated: true };
|
|
332
492
|
}
|
|
@@ -422,7 +582,9 @@ export class UnifiedSessionPersistenceService {
|
|
|
422
582
|
conversation_id: input.conversationId,
|
|
423
583
|
is_subagent: 1,
|
|
424
584
|
prompt,
|
|
425
|
-
metadata_json:
|
|
585
|
+
metadata_json: stringifyMetadataJson(
|
|
586
|
+
metadataWithResolvedTitle({ prompt }),
|
|
587
|
+
),
|
|
426
588
|
transcript_path: artifactPaths.transcriptPath,
|
|
427
589
|
hook_path: artifactPaths.hookPath,
|
|
428
590
|
messages_path: artifactPaths.messagesPath,
|
|
@@ -444,6 +606,30 @@ export class UnifiedSessionPersistenceService {
|
|
|
444
606
|
agentId: input.agentId,
|
|
445
607
|
conversationId: input.conversationId,
|
|
446
608
|
prompt: existing.prompt ?? prompt ?? null,
|
|
609
|
+
metadataJson: stringifyMetadataJson(
|
|
610
|
+
metadataWithResolvedTitle({
|
|
611
|
+
metadata: (() => {
|
|
612
|
+
const raw = existing.metadata_json?.trim();
|
|
613
|
+
if (!raw) {
|
|
614
|
+
return undefined;
|
|
615
|
+
}
|
|
616
|
+
try {
|
|
617
|
+
const parsed = JSON.parse(raw) as unknown;
|
|
618
|
+
if (
|
|
619
|
+
parsed &&
|
|
620
|
+
typeof parsed === "object" &&
|
|
621
|
+
!Array.isArray(parsed)
|
|
622
|
+
) {
|
|
623
|
+
return parsed as Record<string, unknown>;
|
|
624
|
+
}
|
|
625
|
+
} catch {
|
|
626
|
+
// Ignore malformed metadata payloads.
|
|
627
|
+
}
|
|
628
|
+
return undefined;
|
|
629
|
+
})(),
|
|
630
|
+
prompt: existing.prompt ?? prompt ?? null,
|
|
631
|
+
}),
|
|
632
|
+
),
|
|
447
633
|
expectedStatusLock: existing.status_lock,
|
|
448
634
|
});
|
|
449
635
|
return sessionId;
|
|
@@ -601,7 +787,9 @@ export class UnifiedSessionPersistenceService {
|
|
|
601
787
|
conversation_id: null,
|
|
602
788
|
is_subagent: 1,
|
|
603
789
|
prompt: message || `Team task for ${agentId}`,
|
|
604
|
-
metadata_json:
|
|
790
|
+
metadata_json: stringifyMetadataJson(
|
|
791
|
+
metadataWithResolvedTitle({ prompt: message }),
|
|
792
|
+
),
|
|
605
793
|
transcript_path: transcriptPath,
|
|
606
794
|
hook_path: hookPath,
|
|
607
795
|
messages_path: messagesPath,
|
|
@@ -750,7 +938,9 @@ export class UnifiedSessionPersistenceService {
|
|
|
750
938
|
}
|
|
751
939
|
rows = await this.adapter.listSessions({ limit: scanLimit });
|
|
752
940
|
}
|
|
753
|
-
return rows
|
|
941
|
+
return rows
|
|
942
|
+
.slice(0, requestedLimit)
|
|
943
|
+
.map((row) => this.applyResolvedTitleToRow(row));
|
|
754
944
|
}
|
|
755
945
|
|
|
756
946
|
async deleteSession(sessionId: string): Promise<{ deleted: boolean }> {
|
package/src/types/config.ts
CHANGED
|
@@ -15,7 +15,7 @@ import type {
|
|
|
15
15
|
SessionPromptConfig,
|
|
16
16
|
SessionWorkspaceConfig,
|
|
17
17
|
} from "@clinebot/shared";
|
|
18
|
-
import type { ToolRoutingRule } from "../
|
|
18
|
+
import type { ToolRoutingRule } from "../tools/model-tool-routing.js";
|
|
19
19
|
|
|
20
20
|
export type CoreAgentMode = AgentMode;
|
|
21
21
|
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|