melchizedek-agents 0.9.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.
Files changed (111) hide show
  1. package/AGENT_SETUP.md +182 -0
  2. package/DOCUMENTATION.md +614 -0
  3. package/LICENSE +21 -0
  4. package/QUICKSTART.md +164 -0
  5. package/README.md +151 -0
  6. package/config/agents/README.md +27 -0
  7. package/config/agents/examples/ares.yaml +74 -0
  8. package/config/agents/examples/cartographers.yaml +115 -0
  9. package/config/agents/examples/claude.yaml +17 -0
  10. package/config/agents/examples/council.yaml +65 -0
  11. package/config/agents/examples/critic.yaml +74 -0
  12. package/config/agents/examples/delegation.yaml +29 -0
  13. package/config/agents/examples/hierarchical.yaml +26 -0
  14. package/config/agents/examples/image_production.yaml +112 -0
  15. package/config/agents/examples/librarian.yaml +75 -0
  16. package/config/agents/examples/model_zoo.yaml +81 -0
  17. package/config/agents/examples/patient_advocate.yaml +107 -0
  18. package/config/agents/examples/scriptorium.yaml +96 -0
  19. package/config/agents/examples/style_council.yaml +83 -0
  20. package/config/agents/examples/syndicate.yaml +34 -0
  21. package/config/agents/examples/tutor.yaml +66 -0
  22. package/config/agents/syndicateSchema.yaml +291 -0
  23. package/dist/lib/config.d.ts +108 -0
  24. package/dist/lib/config.js +110 -0
  25. package/dist/lib/dispatch.d.ts +133 -0
  26. package/dist/lib/dispatch.js +182 -0
  27. package/dist/lib/grounding.d.ts +28 -0
  28. package/dist/lib/grounding.js +61 -0
  29. package/dist/lib/index.d.ts +14 -0
  30. package/dist/lib/index.js +13 -0
  31. package/dist/lib/loadEnv.d.ts +21 -0
  32. package/dist/lib/loadEnv.js +58 -0
  33. package/dist/lib/loadSyndicate.d.ts +177 -0
  34. package/dist/lib/loadSyndicate.js +264 -0
  35. package/dist/lib/memory/supabaseMemoryService.d.ts +118 -0
  36. package/dist/lib/memory/supabaseMemoryService.js +499 -0
  37. package/dist/lib/models/claudeLlm.d.ts +93 -0
  38. package/dist/lib/models/claudeLlm.js +332 -0
  39. package/dist/lib/models/gptLlm.d.ts +110 -0
  40. package/dist/lib/models/gptLlm.js +497 -0
  41. package/dist/lib/models/grokLlm.d.ts +64 -0
  42. package/dist/lib/models/grokLlm.js +86 -0
  43. package/dist/lib/models/ollamaLlm.d.ts +105 -0
  44. package/dist/lib/models/ollamaLlm.js +134 -0
  45. package/dist/lib/models/openAiCompatibleLlm.d.ts +125 -0
  46. package/dist/lib/models/openAiCompatibleLlm.js +331 -0
  47. package/dist/lib/models/providerMap.d.ts +28 -0
  48. package/dist/lib/models/providerMap.js +45 -0
  49. package/dist/lib/models/registry.d.ts +71 -0
  50. package/dist/lib/models/registry.js +159 -0
  51. package/dist/lib/models/schemaNormalize.d.ts +22 -0
  52. package/dist/lib/models/schemaNormalize.js +58 -0
  53. package/dist/lib/observability/supabaseSpanExporter.d.ts +35 -0
  54. package/dist/lib/observability/supabaseSpanExporter.js +115 -0
  55. package/dist/lib/observability/tracer.d.ts +44 -0
  56. package/dist/lib/observability/tracer.js +242 -0
  57. package/dist/lib/persistence/supabaseProvider.d.ts +44 -0
  58. package/dist/lib/persistence/supabaseProvider.js +88 -0
  59. package/dist/lib/session/supabaseSessionService.d.ts +26 -0
  60. package/dist/lib/session/supabaseSessionService.js +193 -0
  61. package/dist/lib/session/transcript.d.ts +193 -0
  62. package/dist/lib/session/transcript.js +309 -0
  63. package/dist/lib/toolRegistry.d.ts +15 -0
  64. package/dist/lib/toolRegistry.js +58 -0
  65. package/dist/lib/tools/collectionsSearchTool.d.ts +55 -0
  66. package/dist/lib/tools/collectionsSearchTool.js +86 -0
  67. package/dist/lib/tools/generateImageTool.d.ts +16 -0
  68. package/dist/lib/tools/generateImageTool.js +119 -0
  69. package/dist/lib/tools/inspectImageTool.d.ts +21 -0
  70. package/dist/lib/tools/inspectImageTool.js +97 -0
  71. package/dist/lib/tools/mcpToolFactory.d.ts +2 -0
  72. package/dist/lib/tools/mcpToolFactory.js +116 -0
  73. package/dist/lib/tools/toolContract.d.ts +78 -0
  74. package/dist/lib/tools/toolContract.js +129 -0
  75. package/dist/lib/tools/webExtractTool.d.ts +70 -0
  76. package/dist/lib/tools/webExtractTool.js +461 -0
  77. package/dist/lib/tools/webSearchTool.d.ts +53 -0
  78. package/dist/lib/tools/webSearchTool.js +126 -0
  79. package/dist/lib/tools/wikiTools.d.ts +112 -0
  80. package/dist/lib/tools/wikiTools.js +697 -0
  81. package/dist/lib/tools/xSearchTool.d.ts +61 -0
  82. package/dist/lib/tools/xSearchTool.js +125 -0
  83. package/dist/lib/wiki/agentRun.d.ts +39 -0
  84. package/dist/lib/wiki/agentRun.js +79 -0
  85. package/dist/lib/wiki/builder.d.ts +71 -0
  86. package/dist/lib/wiki/builder.js +165 -0
  87. package/dist/lib/wiki/dive.d.ts +56 -0
  88. package/dist/lib/wiki/dive.js +208 -0
  89. package/dist/lib/wiki/entities.d.ts +220 -0
  90. package/dist/lib/wiki/entities.js +661 -0
  91. package/dist/lib/wiki/extract.d.ts +97 -0
  92. package/dist/lib/wiki/extract.js +305 -0
  93. package/dist/lib/wiki/fill.d.ts +37 -0
  94. package/dist/lib/wiki/fill.js +175 -0
  95. package/dist/lib/wiki/format.d.ts +132 -0
  96. package/dist/lib/wiki/format.js +177 -0
  97. package/dist/lib/wiki/graph.d.ts +80 -0
  98. package/dist/lib/wiki/graph.js +147 -0
  99. package/dist/lib/wiki/lint.d.ts +58 -0
  100. package/dist/lib/wiki/lint.js +267 -0
  101. package/dist/lib/wiki/markdown.d.ts +127 -0
  102. package/dist/lib/wiki/markdown.js +336 -0
  103. package/dist/lib/wiki/pipeline.d.ts +69 -0
  104. package/dist/lib/wiki/pipeline.js +119 -0
  105. package/dist/lib/wiki/vault.d.ts +84 -0
  106. package/dist/lib/wiki/vault.js +194 -0
  107. package/dist/scripts/a2a_server.d.ts +2 -0
  108. package/dist/scripts/a2a_server.js +997 -0
  109. package/dist/scripts/syndicate_chat.d.ts +2 -0
  110. package/dist/scripts/syndicate_chat.js +485 -0
  111. package/package.json +96 -0
@@ -0,0 +1,193 @@
1
+ import { BaseSessionService, createSession } from '@google/adk';
2
+ import { randomUUID } from 'crypto';
3
+ import { trimEventForStorage } from "./transcript.js";
4
+ export class SupabaseSessionService extends BaseSessionService {
5
+ supabase;
6
+ constructor(supabaseClient) {
7
+ super();
8
+ this.supabase = supabaseClient;
9
+ }
10
+ getDbId(request) {
11
+ return `${request.appName}:${request.userId}:${request.sessionId}`;
12
+ }
13
+ async createSession(request) {
14
+ const sessionId = request.sessionId || randomUUID();
15
+ const dbId = this.getDbId({ appName: request.appName, userId: request.userId, sessionId });
16
+ const session = createSession({
17
+ id: sessionId,
18
+ appName: request.appName,
19
+ userId: request.userId,
20
+ state: request.state || {},
21
+ events: [],
22
+ lastUpdateTime: Date.now()
23
+ });
24
+ const cleanSession = JSON.parse(JSON.stringify(session));
25
+ const expireAt = new Date(Date.now() + 7 * 24 * 60 * 60 * 1000).toISOString();
26
+ const { error } = await this.supabase
27
+ .from('adk_sessions')
28
+ .upsert({
29
+ id: dbId,
30
+ app_name: cleanSession.appName,
31
+ user_id: cleanSession.userId,
32
+ state: cleanSession.state,
33
+ events: cleanSession.events,
34
+ last_update_time: cleanSession.lastUpdateTime,
35
+ expire_at: expireAt
36
+ });
37
+ if (error) {
38
+ console.error('Error creating session in Supabase:', error);
39
+ throw new Error(`Failed to create session: ${error.message}`);
40
+ }
41
+ return session;
42
+ }
43
+ async getSession(request) {
44
+ const dbId = this.getDbId(request);
45
+ const { data, error } = await this.supabase
46
+ .from('adk_sessions')
47
+ .select('*')
48
+ .eq('id', dbId)
49
+ .single();
50
+ if (error) {
51
+ if (error.code === 'PGRST116') {
52
+ return undefined; // Not found
53
+ }
54
+ console.error('Error getting session from Supabase:', error);
55
+ throw new Error(`Failed to get session: ${error.message}`);
56
+ }
57
+ if (!data)
58
+ return undefined;
59
+ const session = {
60
+ id: request.sessionId,
61
+ appName: data.app_name,
62
+ userId: data.user_id,
63
+ state: data.state || {},
64
+ events: data.events || [],
65
+ lastUpdateTime: data.last_update_time
66
+ };
67
+ // Handle GetSessionConfig (numRecentEvents, afterTimestamp) filters
68
+ if (request.config) {
69
+ if (request.config.afterTimestamp) {
70
+ session.events = session.events.filter((e) => e.timestamp && e.timestamp > request.config.afterTimestamp);
71
+ }
72
+ if (request.config.numRecentEvents) {
73
+ session.events = session.events.slice(-request.config.numRecentEvents);
74
+ }
75
+ }
76
+ return session;
77
+ }
78
+ /**
79
+ * ADK's ListSessions contract carries paging: `limit` with either `page`
80
+ * (1-based, wins) or `offset` (0-based), plus an optional sort by last
81
+ * update, and the response must report where in the set the caller is.
82
+ * The window is pushed down to Postgres via .range(), and `count:
83
+ * 'exact'` returns the TOTAL matching rows alongside the page — so
84
+ * totalItems is the real total, not the slice length. With no `limit`,
85
+ * the whole set is one page, which is what the ADK's own docs specify
86
+ * ("Page size used. Equals `totalItems` when no limit was requested").
87
+ */
88
+ async listSessions(request) {
89
+ const { limit, order } = request;
90
+ const offset = limit !== undefined && request.page !== undefined
91
+ ? (Math.max(1, request.page) - 1) * limit
92
+ : (request.offset ?? 0);
93
+ let query = this.supabase
94
+ .from('adk_sessions')
95
+ .select('id, app_name, user_id, state, last_update_time', { count: 'exact' })
96
+ .eq('app_name', request.appName)
97
+ .eq('user_id', request.userId);
98
+ if (order) {
99
+ query = query.order('last_update_time', { ascending: order === 'asc' });
100
+ }
101
+ if (limit !== undefined) {
102
+ query = query.range(offset, offset + limit - 1);
103
+ }
104
+ else if (offset > 0) {
105
+ // An offset without a limit still means "skip these".
106
+ query = query.range(offset, offset + 999_999);
107
+ }
108
+ const { data, error, count } = await query;
109
+ if (error) {
110
+ console.error('Error listing sessions from Supabase:', error);
111
+ throw new Error(`Failed to list sessions: ${error.message}`);
112
+ }
113
+ const sessions = (data || []).map(row => {
114
+ // Extract the original sessionId from the composite key if possible
115
+ const parts = row.id.split(':');
116
+ const originalSessionId = parts.length >= 3 ? parts.slice(2).join(':') : row.id;
117
+ return {
118
+ id: originalSessionId,
119
+ appName: row.app_name,
120
+ userId: row.user_id,
121
+ state: row.state || {},
122
+ events: [], // ListSessions usually returns omitted events to save bandwidth
123
+ lastUpdateTime: row.last_update_time
124
+ };
125
+ });
126
+ const totalItems = count ?? sessions.length;
127
+ const pageSize = limit ?? totalItems;
128
+ return {
129
+ sessions,
130
+ page: limit ? Math.floor(offset / limit) + 1 : 1,
131
+ limit: pageSize,
132
+ totalItems,
133
+ totalPages: limit ? Math.max(1, Math.ceil(totalItems / limit)) : 1,
134
+ };
135
+ }
136
+ async deleteSession(request) {
137
+ const dbId = this.getDbId(request);
138
+ const { error } = await this.supabase
139
+ .from('adk_sessions')
140
+ .delete()
141
+ .eq('id', dbId);
142
+ if (error) {
143
+ console.error('Error deleting session from Supabase:', error);
144
+ throw new Error(`Failed to delete session: ${error.message}`);
145
+ }
146
+ }
147
+ async appendEvent(request) {
148
+ const { session, event } = request;
149
+ const dbId = this.getDbId({ appName: session.appName, userId: session.userId, sessionId: session.id });
150
+ // Partial events are streaming fragments of an event that is appended
151
+ // whole when it completes. The runner already guards this, but the
152
+ // base contract does too, and a service that persists them would write
153
+ // every token twice.
154
+ if (event.partial)
155
+ return event;
156
+ // Standard ADK internal state merging. This MUST go through the base
157
+ // implementation: it applies `event.actions.stateDelta` to
158
+ // `session.state` and strips `temp:` keys before pushing the event.
159
+ // Re-implementing it as a bare `events.push` (as this did until
160
+ // 2026-08-15) silently drops every state write — `output_key`, any
161
+ // state-injected instruction variable — and leaves `session.state`
162
+ // permanently `{}`, which is exactly what every stored row showed.
163
+ await super.appendEvent({ session, event });
164
+ session.lastUpdateTime = Date.now();
165
+ // Trim on the SERIALIZED COPY only. The live session keeps thought
166
+ // signatures and real tool payloads — the agent's own tool loop reads
167
+ // them back mid-turn — but nothing reads them from the ROW: the
168
+ // projection drops both on the way into a prompt, and the memory
169
+ // service walks `part.text` alone. Across 128 live sessions they were
170
+ // ~90% of every byte stored (73% of it Gemini's `thoughtSignature`),
171
+ // and this method re-uploads the whole array on every event, so those
172
+ // bytes were paid for again on each one. See trimEventForStorage.
173
+ const cleanSession = JSON.parse(JSON.stringify(session));
174
+ cleanSession.events = cleanSession.events.map((e) => trimEventForStorage(e));
175
+ const expireAt = new Date(Date.now() + 7 * 24 * 60 * 60 * 1000).toISOString();
176
+ const { error } = await this.supabase
177
+ .from('adk_sessions')
178
+ .upsert({
179
+ id: dbId,
180
+ app_name: cleanSession.appName,
181
+ user_id: cleanSession.userId,
182
+ state: cleanSession.state,
183
+ events: cleanSession.events,
184
+ last_update_time: cleanSession.lastUpdateTime,
185
+ expire_at: expireAt
186
+ });
187
+ if (error) {
188
+ console.error('Error appending event to session in Supabase:', error);
189
+ throw new Error(`Failed to append event: ${error.message}`);
190
+ }
191
+ return event;
192
+ }
193
+ }
@@ -0,0 +1,193 @@
1
+ /**
2
+ * Cross-agent transcript sharing.
3
+ *
4
+ * ── The problem this solves ───────────────────────────────────────────────
5
+ * Under plan-dispatch every route runs as its OWN root agent (XScout,
6
+ * Conversationalist, FinancialAnalyst) against ONE shared session. Sharing
7
+ * the session is necessary but not sufficient: ADK decides how a stored
8
+ * event is rendered into a prompt by comparing `event.author` against the
9
+ * agent that is running, and every prior event fails that comparison the
10
+ * moment the route changes. `convertForeignEvent` then rewrites each one to
11
+ * `role: "user"` prefixed with "For context:".
12
+ *
13
+ * Replaying a real production session — a four-turn thread that had passed
14
+ * through three routes — through ADK's own content processor showed what the
15
+ * last route actually received: 15 contents, 118,013 bytes, and EVERY ONE of them
16
+ * `role: "user"`. Not a single `role: "model"` turn in the prompt. The
17
+ * history was all there — and unreadable as a conversation, because nothing
18
+ * in it was marked as having been said by the desk. That is the "it doesn't
19
+ * know what I'm referring to" failure, and no amount of session sharing
20
+ * fixes it, because sharing was never the broken part.
21
+ *
22
+ * Two further leaks rode along in the same conversion. `convertForeignEvent`
23
+ * clones any part it does not recognise verbatim, so the previous route's
24
+ * private chain-of-thought ("**My Micron Monday Decision Strategy** Okay, so
25
+ * the question is…", `thought: true`) arrived as USER speech. And raw tool
26
+ * payloads were inlined as text — one `load_memory` result alone was 23,502
27
+ * characters, one `get_company_fundamentals` 8,953 — all of it handed to a
28
+ * 400-character, tool-less flash-lite route that could do nothing with it.
29
+ *
30
+ * ── The fix ───────────────────────────────────────────────────────────────
31
+ * Project the stored transcript before ADK ever sees it: rewrite each past
32
+ * agent turn to the CURRENT agent's name so it survives as a real
33
+ * `role: "model"` turn, label it with the desk that spoke, and drop what no
34
+ * successor can use — thoughts, tool calls, tool results.
35
+ *
36
+ * The projection is a READ-TIME view. The stored session keeps everything,
37
+ * verbatim, because long-term memory ingestion reads the real record.
38
+ */
39
+ import { BaseSessionService } from '@google/adk';
40
+ import type { CreateSessionRequest, GetSessionRequest, ListSessionsRequest, ListSessionsResponse, DeleteSessionRequest, Session, Event } from '@google/adk';
41
+ /** Tuning for {@link projectTranscript}. Defaults are the production values. */
42
+ export interface ProjectionOptions {
43
+ /**
44
+ * Character budget for the projected HISTORY, walked backwards from the
45
+ * newest event. Older events are dropped whole once it is exhausted.
46
+ *
47
+ * A bound is required — the session row is a 7-day append-only log and a
48
+ * busy channel reaches six figures of text — but it is set generously:
49
+ * 40,000 characters of tool-free, thought-free prose is many turns, and
50
+ * the analyst route recovers older material through long-term memory.
51
+ */
52
+ maxHistoryChars?: number;
53
+ /**
54
+ * Ceiling on the NUMBER of past turns kept, whichever binds first with
55
+ * `maxHistoryChars`.
56
+ *
57
+ * A character budget alone is not a bound on prompt shape: a thread of
58
+ * short exchanges fits 43 turns inside 40,000 characters, and a model
59
+ * reading 43 turns of history to answer one question is paying attention
60
+ * tax no budget in bytes describes. Turns and bytes are different costs;
61
+ * both need a ceiling.
62
+ */
63
+ maxHistoryTurns?: number;
64
+ /** Longest single past turn kept intact; the tail is elided. */
65
+ maxTurnChars?: number;
66
+ }
67
+ /**
68
+ * Longest tool result kept in the DURABLE record. See {@link trimEventForStorage}.
69
+ */
70
+ export declare const DEFAULT_MAX_STORED_PAYLOAD_CHARS = 2000;
71
+ /**
72
+ * Render a stored transcript as history the given agent can read.
73
+ *
74
+ * User events pass through untouched. Every agent event becomes a
75
+ * `role: "model"` turn ATTRIBUTED to `forAgent`, which is what keeps ADK
76
+ * from rewriting it into "For context:" user text — with the original
77
+ * author kept as a visible `[Name]` label when it was a different desk, so
78
+ * attribution survives without costing the turn structure.
79
+ *
80
+ * Events that said nothing out loud (a pure tool call, a pure tool result)
81
+ * disappear entirely. Calls and their responses are dropped together, so
82
+ * ADK's function-response pairing never sees a widowed half.
83
+ *
84
+ * Pure and synchronous — see tests/transcript.test.ts.
85
+ */
86
+ export declare function projectTranscript(events: Event[], forAgent: string, options?: ProjectionOptions): Event[];
87
+ /**
88
+ * Strip from an event what no reader of the STORED session will ever use.
89
+ *
90
+ * ── What the durable record is actually made of ───────────────────────────
91
+ * Measured across 128 live sessions, by part field:
92
+ *
93
+ * thoughtSignature 14.14 MB 73.3%
94
+ * functionResponse.response 2.07 MB 10.8%
95
+ * text (the conversation) 1.86 MB 9.6%
96
+ * toolResponse.response 1.06 MB 5.5%
97
+ *
98
+ * Nine-tenths of every byte stored is machinery. `thoughtSignature` is the
99
+ * opaque base64 blob Gemini attaches for thought continuity — a single part
100
+ * of it reached 115 KB — and tool payloads are the rest, the largest being
101
+ * Gemini's grounding `search_suggestions`, an HTML and CSS widget attached
102
+ * to every web-search result.
103
+ *
104
+ * Beware the shape of this data when measuring it: bucketing a part by
105
+ * whichever tool key it carries attributes the whole part to the tool
106
+ * response, when the weight is in a sibling field. That misattribution said
107
+ * "76% is tool results" and predicted a 70% saving from eliding payloads
108
+ * alone; the real saving from that change was 12%.
109
+ *
110
+ * ── Why none of it is needed ──────────────────────────────────────────────
111
+ * The stored row has exactly two readers. {@link projectTranscript} drops
112
+ * thought parts and tool traffic entirely on the way into a prompt, so a
113
+ * stored signature is never replayed to a model. The memory service's
114
+ * `serializeEvents` walks `part.text` alone, so a payload has never
115
+ * contributed one extracted fact. Everything above is written, re-uploaded
116
+ * on every later event in the turn (this service re-upserts the whole
117
+ * array), and re-fetched whole next turn — for nothing. One live session had
118
+ * reached 1.17 MB; a 60-turn conversation uploaded ~288 MB getting there.
119
+ *
120
+ * ── What survives ─────────────────────────────────────────────────────────
121
+ * A trimmed response KEEPS its shape: `id` and `name` are preserved and only
122
+ * the body is replaced, because ADK pairs calls to responses by id
123
+ * (`rearrangeEventsForLatestFunctionResponse` throws on a widowed half), so
124
+ * an elided result must remain a result. The marker records what was dropped
125
+ * and how big it was — what an operator reading the row needs. Small results
126
+ * pass through untouched: they are cheap, and a short one is occasionally
127
+ * the only record of what a number was.
128
+ *
129
+ * ── The one constraint on changing this ───────────────────────────────────
130
+ * Dropping `thoughtSignature` is safe BECAUSE the projection never feeds
131
+ * stored events back to a model. Anything that starts replaying raw stored
132
+ * events into Gemini would need the signature preserved, or lose thought
133
+ * continuity across a resumed session.
134
+ *
135
+ * Pure: returns a new event, never mutates the input. The caller applies it
136
+ * to the SERIALIZED COPY only — the live in-memory session must keep both
137
+ * signature and payloads, or the agent's own tool loop breaks mid-turn.
138
+ */
139
+ export declare function trimEventForStorage(event: Event, maxPayloadChars?: number): Event;
140
+ /**
141
+ * A compact plain-text digest of the exchange, for an agent that must be
142
+ * given history as INPUT rather than as conversation.
143
+ *
144
+ * The dispatch classifier is that agent. It runs in its own lane so its JSON
145
+ * verdicts never pollute the user-facing transcript — and the cost of that
146
+ * isolation, in production, was a router that had never seen a single answer
147
+ * it routed. On 2026-08-15 its lane held four user messages and four of its
148
+ * own verdicts, nothing else; a user's counter-argument to the desk's
149
+ * MU/SNDK/NVDA/AMD call therefore read as a remark with no request behind
150
+ * it, and went to small talk. Its own rules ("follow-ups on prior analysis →
151
+ * FinancialAnalyst") were unusable, because no prior analysis was in view.
152
+ *
153
+ * @param maxTurns newest N exchanges to show
154
+ * @param maxCharsPerTurn per-line budget — the router classifies, it does not
155
+ * read; the shape of an answer is enough
156
+ */
157
+ export declare function renderTranscriptDigest(events: Event[], { maxTurns, maxCharsPerTurn }?: {
158
+ maxTurns?: number;
159
+ maxCharsPerTurn?: number;
160
+ }): string;
161
+ /**
162
+ * A read-time projection over another session service.
163
+ *
164
+ * `getSession` returns the transcript as {@link projectTranscript} renders it
165
+ * for one named agent; every write goes to the REAL session held underneath,
166
+ * so the durable record keeps thoughts, tool calls and full payloads for
167
+ * long-term memory ingestion and for operators reading the row.
168
+ *
169
+ * Both views are kept live during a turn: the runner appends to the
170
+ * projected session it is holding (it re-reads `session.events` before every
171
+ * model call, so the agent's own in-flight tool calls must stay visible and
172
+ * unprojected), while the same event is appended and persisted to the real
173
+ * one. Only history is projected; the current turn never is.
174
+ *
175
+ * Construct one per turn — it is bound to a single agent name.
176
+ */
177
+ export declare class ProjectedSessionService extends BaseSessionService {
178
+ private readonly inner;
179
+ private readonly forAgent;
180
+ private readonly options;
181
+ /** Real sessions handed out by `getSession`, keyed by identity. */
182
+ private readonly stored;
183
+ constructor(inner: BaseSessionService, forAgent: string, options?: ProjectionOptions);
184
+ private key;
185
+ createSession(request: CreateSessionRequest): Promise<Session>;
186
+ listSessions(request: ListSessionsRequest): Promise<ListSessionsResponse>;
187
+ deleteSession(request: DeleteSessionRequest): Promise<void>;
188
+ getSession(request: GetSessionRequest): Promise<Session | undefined>;
189
+ appendEvent(request: {
190
+ session: Session;
191
+ event: Event;
192
+ }): Promise<Event>;
193
+ }