@jonathangu/openclawbrain 0.3.0
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/LICENSE +21 -0
- package/README.md +412 -0
- package/bin/openclawbrain.js +15 -0
- package/docs/END_STATE.md +244 -0
- package/docs/EVIDENCE.md +128 -0
- package/docs/RELEASE_CONTRACT.md +91 -0
- package/docs/agent-tools.md +106 -0
- package/docs/architecture.md +224 -0
- package/docs/configuration.md +178 -0
- package/docs/evidence/2026-03-16/3188b50c4ed30f07dea111e35ce52aabefaced63/brain-teach-session-bound/status.json +87 -0
- package/docs/evidence/2026-03-16/3188b50c4ed30f07dea111e35ce52aabefaced63/brain-teach-session-bound/summary.md +16 -0
- package/docs/evidence/2026-03-16/3188b50c4ed30f07dea111e35ce52aabefaced63/brain-teach-session-bound/trace.json +273 -0
- package/docs/evidence/2026-03-16/3188b50c4ed30f07dea111e35ce52aabefaced63/brain-teach-session-bound/validation-report.json +652 -0
- package/docs/evidence/2026-03-16/4941429588810da5d6f7ef1509f229f83fa08031/channels-status.txt +31 -0
- package/docs/evidence/2026-03-16/4941429588810da5d6f7ef1509f229f83fa08031/config-snapshot.json +66 -0
- package/docs/evidence/2026-03-16/4941429588810da5d6f7ef1509f229f83fa08031/doctor.json +14 -0
- package/docs/evidence/2026-03-16/4941429588810da5d6f7ef1509f229f83fa08031/gateway-probe.txt +34 -0
- package/docs/evidence/2026-03-16/4941429588810da5d6f7ef1509f229f83fa08031/gateway-status.txt +41 -0
- package/docs/evidence/2026-03-16/4941429588810da5d6f7ef1509f229f83fa08031/logs.txt +428 -0
- package/docs/evidence/2026-03-16/4941429588810da5d6f7ef1509f229f83fa08031/status-all.txt +60 -0
- package/docs/evidence/2026-03-16/4941429588810da5d6f7ef1509f229f83fa08031/status.json +223 -0
- package/docs/evidence/2026-03-16/4941429588810da5d6f7ef1509f229f83fa08031/summary.md +13 -0
- package/docs/evidence/2026-03-16/4941429588810da5d6f7ef1509f229f83fa08031/trace.json +4 -0
- package/docs/evidence/2026-03-16/4941429588810da5d6f7ef1509f229f83fa08031/validation-report.json +334 -0
- package/docs/evidence/2026-03-16/7f8dbcb27e741abdeefd5656c210639d0acdd440/channels-status.txt +25 -0
- package/docs/evidence/2026-03-16/7f8dbcb27e741abdeefd5656c210639d0acdd440/config-snapshot.json +91 -0
- package/docs/evidence/2026-03-16/7f8dbcb27e741abdeefd5656c210639d0acdd440/doctor.json +14 -0
- package/docs/evidence/2026-03-16/7f8dbcb27e741abdeefd5656c210639d0acdd440/gateway-probe.txt +36 -0
- package/docs/evidence/2026-03-16/7f8dbcb27e741abdeefd5656c210639d0acdd440/gateway-status.txt +44 -0
- package/docs/evidence/2026-03-16/7f8dbcb27e741abdeefd5656c210639d0acdd440/logs.txt +428 -0
- package/docs/evidence/2026-03-16/7f8dbcb27e741abdeefd5656c210639d0acdd440/short-static-classification/preflight-doctor.json +10 -0
- package/docs/evidence/2026-03-16/7f8dbcb27e741abdeefd5656c210639d0acdd440/short-static-classification/preflight-sdk-probe.json +11 -0
- package/docs/evidence/2026-03-16/7f8dbcb27e741abdeefd5656c210639d0acdd440/short-static-classification/preflight-setup-only.json +12 -0
- package/docs/evidence/2026-03-16/7f8dbcb27e741abdeefd5656c210639d0acdd440/short-static-classification/summary.md +30 -0
- package/docs/evidence/2026-03-16/7f8dbcb27e741abdeefd5656c210639d0acdd440/short-static-classification/validation-report.json +72 -0
- package/docs/evidence/2026-03-16/7f8dbcb27e741abdeefd5656c210639d0acdd440/status-all.txt +63 -0
- package/docs/evidence/2026-03-16/7f8dbcb27e741abdeefd5656c210639d0acdd440/status.json +200 -0
- package/docs/evidence/2026-03-16/7f8dbcb27e741abdeefd5656c210639d0acdd440/summary.md +13 -0
- package/docs/evidence/2026-03-16/7f8dbcb27e741abdeefd5656c210639d0acdd440/trace.json +4 -0
- package/docs/evidence/2026-03-16/7f8dbcb27e741abdeefd5656c210639d0acdd440/validation-report.json +311 -0
- package/docs/evidence/README.md +16 -0
- package/docs/fts5.md +161 -0
- package/docs/tui.md +506 -0
- package/index.ts +1372 -0
- package/openclaw.plugin.json +136 -0
- package/package.json +66 -0
- package/src/assembler.ts +804 -0
- package/src/brain-cli.ts +316 -0
- package/src/brain-core/decay.ts +35 -0
- package/src/brain-core/episode.ts +82 -0
- package/src/brain-core/graph.ts +321 -0
- package/src/brain-core/health.ts +116 -0
- package/src/brain-core/mutator.ts +281 -0
- package/src/brain-core/pack.ts +117 -0
- package/src/brain-core/policy.ts +153 -0
- package/src/brain-core/replay.ts +1 -0
- package/src/brain-core/teacher.ts +105 -0
- package/src/brain-core/trace.ts +40 -0
- package/src/brain-core/traverse.ts +230 -0
- package/src/brain-core/types.ts +405 -0
- package/src/brain-core/update.ts +123 -0
- package/src/brain-harvest/human.ts +46 -0
- package/src/brain-harvest/scanner.ts +98 -0
- package/src/brain-harvest/self.ts +147 -0
- package/src/brain-runtime/assembler-extension.ts +230 -0
- package/src/brain-runtime/evidence-detectors.ts +68 -0
- package/src/brain-runtime/graph-io.ts +72 -0
- package/src/brain-runtime/harvester-extension.ts +98 -0
- package/src/brain-runtime/service.ts +659 -0
- package/src/brain-runtime/tools.ts +109 -0
- package/src/brain-runtime/worker-state.ts +106 -0
- package/src/brain-runtime/worker-supervisor.ts +169 -0
- package/src/brain-store/embedding.ts +179 -0
- package/src/brain-store/init.ts +347 -0
- package/src/brain-store/migrations.ts +188 -0
- package/src/brain-store/store.ts +816 -0
- package/src/brain-worker/child-runner.ts +321 -0
- package/src/brain-worker/jobs.ts +12 -0
- package/src/brain-worker/mutation-job.ts +5 -0
- package/src/brain-worker/promotion-job.ts +5 -0
- package/src/brain-worker/protocol.ts +79 -0
- package/src/brain-worker/teacher-job.ts +5 -0
- package/src/brain-worker/update-job.ts +5 -0
- package/src/brain-worker/worker.ts +422 -0
- package/src/compaction.ts +1332 -0
- package/src/db/config.ts +265 -0
- package/src/db/connection.ts +72 -0
- package/src/db/features.ts +42 -0
- package/src/db/migration.ts +561 -0
- package/src/engine.ts +1995 -0
- package/src/expansion-auth.ts +351 -0
- package/src/expansion-policy.ts +303 -0
- package/src/expansion.ts +383 -0
- package/src/integrity.ts +600 -0
- package/src/large-files.ts +527 -0
- package/src/openclaw-bridge.ts +22 -0
- package/src/retrieval.ts +357 -0
- package/src/store/conversation-store.ts +748 -0
- package/src/store/fts5-sanitize.ts +29 -0
- package/src/store/full-text-fallback.ts +74 -0
- package/src/store/index.ts +29 -0
- package/src/store/summary-store.ts +918 -0
- package/src/summarize.ts +847 -0
- package/src/tools/common.ts +53 -0
- package/src/tools/lcm-conversation-scope.ts +76 -0
- package/src/tools/lcm-describe-tool.ts +234 -0
- package/src/tools/lcm-expand-query-tool.ts +594 -0
- package/src/tools/lcm-expand-tool.delegation.ts +556 -0
- package/src/tools/lcm-expand-tool.ts +448 -0
- package/src/tools/lcm-expansion-recursion-guard.ts +286 -0
- package/src/tools/lcm-grep-tool.ts +200 -0
- package/src/transcript-repair.ts +301 -0
- package/src/types.ts +149 -0
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import type { AnyAgentTool as OpenClawAnyAgentTool } from "openclaw/plugin-sdk";
|
|
2
|
+
|
|
3
|
+
export type AnyAgentTool = OpenClawAnyAgentTool;
|
|
4
|
+
|
|
5
|
+
/** Render structured payloads as deterministic text tool results. */
|
|
6
|
+
export function jsonResult(payload: unknown): {
|
|
7
|
+
content: Array<{ type: "text"; text: string }>;
|
|
8
|
+
details: unknown;
|
|
9
|
+
} {
|
|
10
|
+
return {
|
|
11
|
+
content: [
|
|
12
|
+
{
|
|
13
|
+
type: "text",
|
|
14
|
+
text: JSON.stringify(payload, null, 2),
|
|
15
|
+
},
|
|
16
|
+
],
|
|
17
|
+
details: payload,
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/** Read a string param with optional trimming/required checks. */
|
|
22
|
+
export function readStringParam(
|
|
23
|
+
params: Record<string, unknown>,
|
|
24
|
+
key: string,
|
|
25
|
+
options?: {
|
|
26
|
+
required?: boolean;
|
|
27
|
+
trim?: boolean;
|
|
28
|
+
allowEmpty?: boolean;
|
|
29
|
+
label?: string;
|
|
30
|
+
},
|
|
31
|
+
): string | undefined {
|
|
32
|
+
const raw = params[key];
|
|
33
|
+
if (raw == null) {
|
|
34
|
+
if (options?.required) {
|
|
35
|
+
throw new Error(`${options.label ?? key} is required.`);
|
|
36
|
+
}
|
|
37
|
+
return undefined;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
if (typeof raw !== "string") {
|
|
41
|
+
throw new Error(`${options?.label ?? key} must be a string.`);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const value = options?.trim === false ? raw : raw.trim();
|
|
45
|
+
if (!options?.allowEmpty && value.length === 0) {
|
|
46
|
+
if (options?.required) {
|
|
47
|
+
throw new Error(`${options.label ?? key} is required.`);
|
|
48
|
+
}
|
|
49
|
+
return undefined;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
return value;
|
|
53
|
+
}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import type { LcmContextEngine } from "../engine.js";
|
|
2
|
+
import type { LcmDependencies } from "../types.js";
|
|
3
|
+
|
|
4
|
+
export type LcmConversationScope = {
|
|
5
|
+
conversationId?: number;
|
|
6
|
+
allConversations: boolean;
|
|
7
|
+
};
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Parse an ISO-8601 timestamp tool parameter into a Date.
|
|
11
|
+
*
|
|
12
|
+
* Throws when the value is not a parseable timestamp string.
|
|
13
|
+
*/
|
|
14
|
+
export function parseIsoTimestampParam(
|
|
15
|
+
params: Record<string, unknown>,
|
|
16
|
+
key: string,
|
|
17
|
+
): Date | undefined {
|
|
18
|
+
const raw = params[key];
|
|
19
|
+
if (typeof raw !== "string") {
|
|
20
|
+
return undefined;
|
|
21
|
+
}
|
|
22
|
+
const value = raw.trim();
|
|
23
|
+
if (!value) {
|
|
24
|
+
return undefined;
|
|
25
|
+
}
|
|
26
|
+
const parsed = new Date(value);
|
|
27
|
+
if (Number.isNaN(parsed.getTime())) {
|
|
28
|
+
throw new Error(`${key} must be a valid ISO timestamp.`);
|
|
29
|
+
}
|
|
30
|
+
return parsed;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Resolve LCM conversation scope for tool calls.
|
|
35
|
+
*
|
|
36
|
+
* Priority:
|
|
37
|
+
* 1. Explicit conversationId parameter
|
|
38
|
+
* 2. allConversations=true (cross-conversation mode)
|
|
39
|
+
* 3. Current session's LCM conversation
|
|
40
|
+
*/
|
|
41
|
+
export async function resolveLcmConversationScope(input: {
|
|
42
|
+
lcm: LcmContextEngine;
|
|
43
|
+
params: Record<string, unknown>;
|
|
44
|
+
sessionId?: string;
|
|
45
|
+
sessionKey?: string;
|
|
46
|
+
deps?: Pick<LcmDependencies, "resolveSessionIdFromSessionKey">;
|
|
47
|
+
}): Promise<LcmConversationScope> {
|
|
48
|
+
const { lcm, params } = input;
|
|
49
|
+
|
|
50
|
+
const explicitConversationId =
|
|
51
|
+
typeof params.conversationId === "number" && Number.isFinite(params.conversationId)
|
|
52
|
+
? Math.trunc(params.conversationId)
|
|
53
|
+
: undefined;
|
|
54
|
+
if (explicitConversationId != null) {
|
|
55
|
+
return { conversationId: explicitConversationId, allConversations: false };
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
if (params.allConversations === true) {
|
|
59
|
+
return { conversationId: undefined, allConversations: true };
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
let normalizedSessionId = input.sessionId?.trim();
|
|
63
|
+
if (!normalizedSessionId && input.sessionKey && input.deps) {
|
|
64
|
+
normalizedSessionId = await input.deps.resolveSessionIdFromSessionKey(input.sessionKey.trim());
|
|
65
|
+
}
|
|
66
|
+
if (!normalizedSessionId) {
|
|
67
|
+
return { conversationId: undefined, allConversations: false };
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
const conversation = await lcm.getConversationStore().getConversationBySessionId(normalizedSessionId);
|
|
71
|
+
if (!conversation) {
|
|
72
|
+
return { conversationId: undefined, allConversations: false };
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
return { conversationId: conversation.conversationId, allConversations: false };
|
|
76
|
+
}
|
|
@@ -0,0 +1,234 @@
|
|
|
1
|
+
import { Type } from "@sinclair/typebox";
|
|
2
|
+
import type { LcmContextEngine } from "../engine.js";
|
|
3
|
+
import {
|
|
4
|
+
getRuntimeExpansionAuthManager,
|
|
5
|
+
resolveDelegatedExpansionGrantId,
|
|
6
|
+
} from "../expansion-auth.js";
|
|
7
|
+
import type { LcmDependencies } from "../types.js";
|
|
8
|
+
import type { AnyAgentTool } from "./common.js";
|
|
9
|
+
import { jsonResult } from "./common.js";
|
|
10
|
+
import { resolveLcmConversationScope } from "./lcm-conversation-scope.js";
|
|
11
|
+
import { formatTimestamp } from "../compaction.js";
|
|
12
|
+
|
|
13
|
+
const LcmDescribeSchema = Type.Object({
|
|
14
|
+
id: Type.String({
|
|
15
|
+
description: "The LCM ID to look up. Use sum_xxx for summaries, file_xxx for files.",
|
|
16
|
+
}),
|
|
17
|
+
conversationId: Type.Optional(
|
|
18
|
+
Type.Number({
|
|
19
|
+
description:
|
|
20
|
+
"Conversation ID to scope describe lookups to. If omitted, uses the current session conversation.",
|
|
21
|
+
}),
|
|
22
|
+
),
|
|
23
|
+
allConversations: Type.Optional(
|
|
24
|
+
Type.Boolean({
|
|
25
|
+
description:
|
|
26
|
+
"Set true to explicitly allow lookups across all conversations. Ignored when conversationId is provided.",
|
|
27
|
+
}),
|
|
28
|
+
),
|
|
29
|
+
tokenCap: Type.Optional(
|
|
30
|
+
Type.Number({
|
|
31
|
+
description: "Optional budget cap used for subtree manifest budget-fit annotations.",
|
|
32
|
+
minimum: 1,
|
|
33
|
+
}),
|
|
34
|
+
),
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
function normalizeRequestedTokenCap(value: unknown): number | undefined {
|
|
38
|
+
if (typeof value !== "number" || !Number.isFinite(value)) {
|
|
39
|
+
return undefined;
|
|
40
|
+
}
|
|
41
|
+
return Math.max(1, Math.trunc(value));
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function formatIso(value: Date | null | undefined, timezone?: string): string {
|
|
45
|
+
if (!(value instanceof Date)) return "-";
|
|
46
|
+
if (timezone) {
|
|
47
|
+
return formatTimestamp(value, timezone);
|
|
48
|
+
}
|
|
49
|
+
return value.toISOString();
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export function createLcmDescribeTool(input: {
|
|
53
|
+
deps: LcmDependencies;
|
|
54
|
+
lcm: LcmContextEngine;
|
|
55
|
+
sessionId?: string;
|
|
56
|
+
sessionKey?: string;
|
|
57
|
+
}): AnyAgentTool {
|
|
58
|
+
return {
|
|
59
|
+
name: "lcm_describe",
|
|
60
|
+
label: "LCM Describe",
|
|
61
|
+
description:
|
|
62
|
+
"Look up metadata and content for an LCM item by ID. " +
|
|
63
|
+
"Use this to inspect summaries (sum_xxx) or stored files (file_xxx) " +
|
|
64
|
+
"from compacted conversation history. Returns summary content, lineage, " +
|
|
65
|
+
"token counts, and file exploration results.",
|
|
66
|
+
parameters: LcmDescribeSchema,
|
|
67
|
+
async execute(_toolCallId, params) {
|
|
68
|
+
const retrieval = input.lcm.getRetrieval();
|
|
69
|
+
const timezone = input.lcm.timezone;
|
|
70
|
+
const p = params as Record<string, unknown>;
|
|
71
|
+
const id = (p.id as string).trim();
|
|
72
|
+
const conversationScope = await resolveLcmConversationScope({
|
|
73
|
+
lcm: input.lcm,
|
|
74
|
+
deps: input.deps,
|
|
75
|
+
sessionId: input.sessionId,
|
|
76
|
+
sessionKey: input.sessionKey,
|
|
77
|
+
params: p,
|
|
78
|
+
});
|
|
79
|
+
if (!conversationScope.allConversations && conversationScope.conversationId == null) {
|
|
80
|
+
return jsonResult({
|
|
81
|
+
error:
|
|
82
|
+
"No LCM conversation found for this session. Provide conversationId or set allConversations=true.",
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
const result = await retrieval.describe(id);
|
|
87
|
+
|
|
88
|
+
if (!result) {
|
|
89
|
+
return jsonResult({
|
|
90
|
+
error: `Not found: ${id}`,
|
|
91
|
+
hint: "Check the ID format (sum_xxx for summaries, file_xxx for files).",
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
if (conversationScope.conversationId != null) {
|
|
95
|
+
const itemConversationId =
|
|
96
|
+
result.type === "summary" ? result.summary?.conversationId : result.file?.conversationId;
|
|
97
|
+
if (itemConversationId != null && itemConversationId !== conversationScope.conversationId) {
|
|
98
|
+
return jsonResult({
|
|
99
|
+
error: `Not found in conversation ${conversationScope.conversationId}: ${id}`,
|
|
100
|
+
hint: "Use allConversations=true for cross-conversation lookup.",
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
if (result.type === "summary" && result.summary) {
|
|
106
|
+
const s = result.summary;
|
|
107
|
+
const requestedTokenCap = normalizeRequestedTokenCap((params as Record<string, unknown>).tokenCap);
|
|
108
|
+
const sessionKey =
|
|
109
|
+
(typeof input.sessionKey === "string" ? input.sessionKey : input.sessionId)?.trim() ?? "";
|
|
110
|
+
const delegatedGrantId = input.deps.isSubagentSessionKey(sessionKey)
|
|
111
|
+
? (resolveDelegatedExpansionGrantId(sessionKey) ?? "")
|
|
112
|
+
: "";
|
|
113
|
+
const delegatedRemainingBudget =
|
|
114
|
+
delegatedGrantId !== ""
|
|
115
|
+
? getRuntimeExpansionAuthManager().getRemainingTokenBudget(delegatedGrantId)
|
|
116
|
+
: null;
|
|
117
|
+
const defaultTokenCap = Math.max(1, Math.trunc(input.deps.config.maxExpandTokens));
|
|
118
|
+
const resolvedTokenCap = (() => {
|
|
119
|
+
const base =
|
|
120
|
+
requestedTokenCap ??
|
|
121
|
+
(typeof delegatedRemainingBudget === "number" ? delegatedRemainingBudget : defaultTokenCap);
|
|
122
|
+
if (typeof delegatedRemainingBudget === "number") {
|
|
123
|
+
return Math.max(0, Math.min(base, delegatedRemainingBudget));
|
|
124
|
+
}
|
|
125
|
+
return Math.max(1, base);
|
|
126
|
+
})();
|
|
127
|
+
|
|
128
|
+
const manifestNodes = s.subtree.map((node) => {
|
|
129
|
+
const summariesOnlyCost = Math.max(0, node.tokenCount + node.descendantTokenCount);
|
|
130
|
+
const withMessagesCost = Math.max(0, summariesOnlyCost + node.sourceMessageTokenCount);
|
|
131
|
+
return {
|
|
132
|
+
summaryId: node.summaryId,
|
|
133
|
+
parentSummaryId: node.parentSummaryId,
|
|
134
|
+
depthFromRoot: node.depthFromRoot,
|
|
135
|
+
depth: node.depth,
|
|
136
|
+
kind: node.kind,
|
|
137
|
+
tokenCount: node.tokenCount,
|
|
138
|
+
descendantCount: node.descendantCount,
|
|
139
|
+
descendantTokenCount: node.descendantTokenCount,
|
|
140
|
+
sourceMessageTokenCount: node.sourceMessageTokenCount,
|
|
141
|
+
childCount: node.childCount,
|
|
142
|
+
earliestAt: node.earliestAt,
|
|
143
|
+
latestAt: node.latestAt,
|
|
144
|
+
path: node.path,
|
|
145
|
+
costs: {
|
|
146
|
+
summariesOnly: summariesOnlyCost,
|
|
147
|
+
withMessages: withMessagesCost,
|
|
148
|
+
},
|
|
149
|
+
budgetFit: {
|
|
150
|
+
summariesOnly: summariesOnlyCost <= resolvedTokenCap,
|
|
151
|
+
withMessages: withMessagesCost <= resolvedTokenCap,
|
|
152
|
+
},
|
|
153
|
+
};
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
const lines: string[] = [];
|
|
157
|
+
lines.push(`LCM_SUMMARY ${id}`);
|
|
158
|
+
lines.push(
|
|
159
|
+
`meta conv=${s.conversationId} kind=${s.kind} depth=${s.depth} tok=${s.tokenCount} ` +
|
|
160
|
+
`descTok=${s.descendantTokenCount} srcTok=${s.sourceMessageTokenCount} ` +
|
|
161
|
+
`desc=${s.descendantCount} range=${formatIso(s.earliestAt, timezone)}..${formatIso(s.latestAt, timezone)} ` +
|
|
162
|
+
`budgetCap=${resolvedTokenCap}`,
|
|
163
|
+
);
|
|
164
|
+
if (s.parentIds.length > 0) {
|
|
165
|
+
lines.push(`parents ${s.parentIds.join(" ")}`);
|
|
166
|
+
}
|
|
167
|
+
if (s.childIds.length > 0) {
|
|
168
|
+
lines.push(`children ${s.childIds.join(" ")}`);
|
|
169
|
+
}
|
|
170
|
+
lines.push("manifest");
|
|
171
|
+
for (const node of manifestNodes) {
|
|
172
|
+
lines.push(
|
|
173
|
+
`d${node.depthFromRoot} ${node.summaryId} k=${node.kind} tok=${node.tokenCount} ` +
|
|
174
|
+
`descTok=${node.descendantTokenCount} srcTok=${node.sourceMessageTokenCount} ` +
|
|
175
|
+
`desc=${node.descendantCount} child=${node.childCount} ` +
|
|
176
|
+
`range=${formatIso(node.earliestAt, timezone)}..${formatIso(node.latestAt, timezone)} ` +
|
|
177
|
+
`cost[s=${node.costs.summariesOnly},m=${node.costs.withMessages}] ` +
|
|
178
|
+
`budget[s=${node.budgetFit.summariesOnly ? "in" : "over"},` +
|
|
179
|
+
`m=${node.budgetFit.withMessages ? "in" : "over"}]`,
|
|
180
|
+
);
|
|
181
|
+
}
|
|
182
|
+
lines.push("content");
|
|
183
|
+
lines.push(s.content);
|
|
184
|
+
|
|
185
|
+
return {
|
|
186
|
+
content: [{ type: "text", text: lines.join("\n") }],
|
|
187
|
+
details: {
|
|
188
|
+
...result,
|
|
189
|
+
manifest: {
|
|
190
|
+
tokenCap: resolvedTokenCap,
|
|
191
|
+
budgetSource:
|
|
192
|
+
requestedTokenCap != null
|
|
193
|
+
? "request"
|
|
194
|
+
: typeof delegatedRemainingBudget === "number"
|
|
195
|
+
? "delegated_grant_remaining"
|
|
196
|
+
: "config_default",
|
|
197
|
+
nodes: manifestNodes,
|
|
198
|
+
},
|
|
199
|
+
},
|
|
200
|
+
};
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
if (result.type === "file" && result.file) {
|
|
204
|
+
const f = result.file;
|
|
205
|
+
const lines: string[] = [];
|
|
206
|
+
lines.push(`## LCM File: ${id}`);
|
|
207
|
+
lines.push("");
|
|
208
|
+
lines.push(`**Conversation:** ${f.conversationId}`);
|
|
209
|
+
lines.push(`**Name:** ${f.fileName ?? "(no name)"}`);
|
|
210
|
+
lines.push(`**Type:** ${f.mimeType ?? "unknown"}`);
|
|
211
|
+
if (f.byteSize != null) {
|
|
212
|
+
lines.push(`**Size:** ${f.byteSize.toLocaleString()} bytes`);
|
|
213
|
+
}
|
|
214
|
+
lines.push(`**Created:** ${formatIso(f.createdAt, timezone)}`);
|
|
215
|
+
if (f.explorationSummary) {
|
|
216
|
+
lines.push("");
|
|
217
|
+
lines.push("## Exploration Summary");
|
|
218
|
+
lines.push("");
|
|
219
|
+
lines.push(f.explorationSummary);
|
|
220
|
+
} else {
|
|
221
|
+
lines.push("");
|
|
222
|
+
lines.push("*No exploration summary available.*");
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
return {
|
|
226
|
+
content: [{ type: "text", text: lines.join("\n") }],
|
|
227
|
+
details: result,
|
|
228
|
+
};
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
return jsonResult(result);
|
|
232
|
+
},
|
|
233
|
+
};
|
|
234
|
+
}
|