@clinebot/core 0.0.3 → 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/README.md +7 -7
- package/dist/agents/agent-config-parser.d.ts +1 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.node.js +113 -93
- 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 +13 -13
- package/src/agents/agent-config-parser.ts +1 -1
- package/src/index.ts +28 -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/index.browser.d.ts +0 -1
- package/dist/index.browser.js +0 -220
- package/dist/index.js +0 -220
- package/src/index.browser.ts +0 -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
|
@@ -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
|
|
package/dist/index.browser.d.ts
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export * from "./index";
|