@oscharko-dev/keiko-server 0.2.5 → 0.2.7

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.
@@ -0,0 +1,12 @@
1
+ import type { ContextCompactionRecord } from "@oscharko-dev/keiko-contracts";
2
+ import type { UiHandlerDeps } from "./deps.js";
3
+ export interface ChatCompactionEvidenceInput {
4
+ readonly compaction: ContextCompactionRecord | undefined;
5
+ readonly chatId: string;
6
+ readonly modelId: string;
7
+ readonly messageCount: number;
8
+ readonly startedAt: number;
9
+ readonly finishedAt: number;
10
+ }
11
+ export declare function persistChatCompactionEvidence(deps: UiHandlerDeps, input: ChatCompactionEvidenceInput): void;
12
+ //# sourceMappingURL=chat-compaction-evidence.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"chat-compaction-evidence.d.ts","sourceRoot":"","sources":["../src/chat-compaction-evidence.ts"],"names":[],"mappings":"AAWA,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,+BAA+B,CAAC;AAC7E,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,WAAW,CAAC;AAG/C,MAAM,WAAW,2BAA2B;IAE1C,QAAQ,CAAC,UAAU,EAAE,uBAAuB,GAAG,SAAS,CAAC;IACzD,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IAGzB,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;CAC7B;AAYD,wBAAgB,6BAA6B,CAC3C,IAAI,EAAE,aAAa,EACnB,KAAK,EAAE,2BAA2B,GACjC,IAAI,CA8BN"}
@@ -0,0 +1,46 @@
1
+ // ADR-0057 D3: best-effort wiring of the chat history-compaction record into regulated evidence.
2
+ // buildGatewayMessages drops the ContextCompactionRecord that conversationForGatewayWithCompaction
3
+ // produces on the slow path (> MAX_CONTEXT_MESSAGES filtered turns with an active profile). This
4
+ // module persists it via persistCompactionEvidence AFTER the turn completes, redacted and retained
5
+ // under DEFAULT_RETENTION. The raw chatId NEVER reaches the manifest — only sha256Hex(chatId) and a
6
+ // derived runId. Persistence is strictly best-effort: a malformed chatId, an invalid runId, or a
7
+ // throwing store can NEVER fail the chat send. The fast path (no compaction record) is a no-op.
8
+ import { persistCompactionEvidence } from "@oscharko-dev/keiko-evidence";
9
+ import { resolveCostClass } from "@oscharko-dev/keiko-model-gateway";
10
+ import { sha256Hex } from "@oscharko-dev/keiko-security";
11
+ import { currentRedactionSecrets } from "./deps.js";
12
+ // runId = chat-<sha256Hex(chatId)[:16]>-t<messageCount>. Hex-only + the fixed prefix/suffix are all
13
+ // in the assertValidRunId charset [A-Za-z0-9._-]; max length 5+16+2+20 = 43 << 256.
14
+ function compactionRunId(chatIdHash, messageCount) {
15
+ return `chat-${chatIdHash.slice(0, 16)}-t${String(messageCount)}`;
16
+ }
17
+ // Best-effort persist of one compaction record. EVERYTHING after the fast-path guard — including the
18
+ // chatId hash, runId construction, and persistCompactionEvidence (which itself calls
19
+ // assertValidRunId) — is wrapped so a malformed chatId or a throwing store cannot escape into the
20
+ // send path. The fast path (no record) returns immediately without touching the store.
21
+ export function persistChatCompactionEvidence(deps, input) {
22
+ if (input.compaction === undefined) {
23
+ return;
24
+ }
25
+ const record = input.compaction;
26
+ try {
27
+ const chatIdHash = sha256Hex(input.chatId);
28
+ persistCompactionEvidence({
29
+ runId: compactionRunId(chatIdHash, input.messageCount),
30
+ modelId: input.modelId,
31
+ records: [record],
32
+ startedAt: input.startedAt,
33
+ finishedAt: input.finishedAt,
34
+ chatIdHash,
35
+ }, {
36
+ store: deps.evidenceStore,
37
+ env: deps.env,
38
+ additionalSecrets: currentRedactionSecrets(deps),
39
+ costClassResolver: resolveCostClass,
40
+ });
41
+ }
42
+ catch (error) {
43
+ // eslint-disable-next-line no-console
44
+ console.warn("chat-compaction-evidence: persistence failed (best-effort, send unaffected)", error);
45
+ }
46
+ }
@@ -5,12 +5,19 @@ import { type ConversationAttachment } from "./conversation-validation.js";
5
5
  import type { UiHandlerDeps } from "./deps.js";
6
6
  import type { RouteContext, RouteResult } from "./routes.js";
7
7
  import { type ConversationMemoryRuntimeContext } from "./memory-conversation-context.js";
8
+ import type { ConversationCompactionOutcome } from "./conversation-compaction.js";
9
+ export declare const MAX_CONTEXT_MESSAGES = 24;
8
10
  export interface GatewayConversationMessage {
9
11
  readonly role: "system" | "user" | "assistant";
10
12
  readonly content: string;
11
13
  }
12
14
  export declare function redactErrorMessage(message: string, deps: UiHandlerDeps): string;
13
15
  export declare function desktopChatErrorResult(error: unknown, deps: UiHandlerDeps): RouteResult;
16
+ export declare function usableGatewayMessages(messages: readonly ChatMessage[]): {
17
+ role: "user" | "assistant";
18
+ content: string;
19
+ }[];
20
+ export declare function conversationForGateway(messages: readonly ChatMessage[]): GatewayConversationMessage[];
14
21
  export interface SendDesktopChatRequest {
15
22
  readonly chatId: string;
16
23
  readonly projectPath: string;
@@ -34,7 +41,16 @@ export declare function buildChatPatch(chat: Chat, request: SendDesktopChatReque
34
41
  selectedModel: string;
35
42
  title?: string;
36
43
  };
44
+ export declare function deriveCompactionOutcome(deps: UiHandlerDeps, request: SendDesktopChatRequest): ConversationCompactionOutcome;
37
45
  export declare function buildGatewayMessages(deps: UiHandlerDeps, request: SendDesktopChatRequest, memoryText: string): GatewayConversationMessage[];
46
+ export interface ChatCompactionTurn {
47
+ readonly outcome: ConversationCompactionOutcome;
48
+ readonly request: SendDesktopChatRequest;
49
+ readonly modelId: string;
50
+ readonly messageCount: number;
51
+ readonly startedAt: number;
52
+ }
53
+ export declare function recordChatCompaction(deps: UiHandlerDeps, turn: ChatCompactionTurn): void;
38
54
  export declare function handleCreateDesktopChat(ctx: RouteContext, deps: UiHandlerDeps): Promise<RouteResult>;
39
55
  export interface PreparedDesktopChatSend {
40
56
  readonly request: SendDesktopChatRequest;
@@ -1 +1 @@
1
- {"version":3,"file":"chat-handlers.d.ts","sourceRoot":"","sources":["../src/chat-handlers.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"chat-handlers.d.ts","sourceRoot":"","sources":["../src/chat-handlers.ts"],"names":[],"mappings":"AAgBA,OAAO,KAAK,EAAE,+BAA+B,EAAE,MAAM,+BAA+B,CAAC;AACrF,OAAO,KAAK,EACV,4BAA4B,EAC5B,4BAA4B,EAC7B,MAAM,wCAAwC,CAAC;AAuBhD,OAAO,EAGL,KAAK,IAAI,EACT,KAAK,WAAW,EAEjB,MAAM,kBAAkB,CAAC;AAE1B,OAAO,EAEL,KAAK,sBAAsB,EAC5B,MAAM,8BAA8B,CAAC;AAGtC,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,WAAW,CAAC;AAE/C,OAAO,KAAK,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAS7D,OAAO,EAGL,KAAK,gCAAgC,EACtC,MAAM,kCAAkC,CAAC;AAU1C,OAAO,KAAK,EAAE,6BAA6B,EAAE,MAAM,8BAA8B,CAAC;AAOlF,eAAO,MAAM,oBAAoB,KAAK,CAAC;AAEvC,MAAM,WAAW,0BAA0B;IACzC,QAAQ,CAAC,IAAI,EAAE,QAAQ,GAAG,MAAM,GAAG,WAAW,CAAC;IAC/C,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;CAC1B;AAyKD,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,aAAa,GAAG,MAAM,CAE/E;AAOD,wBAAgB,sBAAsB,CAAC,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,aAAa,GAAG,WAAW,CAWvF;AAkBD,wBAAgB,qBAAqB,CACnC,QAAQ,EAAE,SAAS,WAAW,EAAE,GAC/B;IAAE,IAAI,EAAE,MAAM,GAAG,WAAW,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,EAAE,CAMnD;AAED,wBAAgB,sBAAsB,CACpC,QAAQ,EAAE,SAAS,WAAW,EAAE,GAC/B,0BAA0B,EAAE,CAS9B;AAED,MAAM,WAAW,sBAAsB;IACrC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,OAAO,EAAE,MAAM,GAAG,SAAS,CAAC;IAIrC,QAAQ,CAAC,eAAe,EAAE,SAAS,+BAA+B,EAAE,CAAC;IAIrE,QAAQ,CAAC,WAAW,EAAE,SAAS,sBAAsB,EAAE,CAAC;IACxD,QAAQ,CAAC,MAAM,EAAE,+BAA+B,GAAG,SAAS,CAAC;CAC9D;AAED,UAAU,+BAA+B;IACvC,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;IAC1B,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,CAAC;IAC/B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAC3C;AAiPD,wBAAgB,iBAAiB,CAC/B,IAAI,EAAE,aAAa,EACnB,OAAO,EAAE,sBAAsB,GAC9B,WAAW,CAYb;AAED,wBAAgB,sBAAsB,CACpC,IAAI,EAAE,aAAa,EACnB,OAAO,EAAE,sBAAsB,EAC/B,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,MAAM,GACd,WAAW,CAab;AA8BD,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,OAAO,GAAG,4BAA4B,CAUhF;AA2GD,wBAAsB,iBAAiB,CACrC,OAAO,EAAE,sBAAsB,EAC/B,IAAI,EAAE,aAAa,EACnB,OAAO,EAAE,gCAAgC,GACxC,OAAO,CAAC,4BAA4B,CAAC,CAqCvC;AAiFD,wBAAsB,oBAAoB,CACxC,IAAI,EAAE,aAAa,EACnB,OAAO,EAAE,sBAAsB,EAC/B,aAAa,EAAE,gCAAgC,GAAG,SAAS,EAC3D,OAAO,EAAE,MAAM,EACf,aAAa,EAAE,MAAM,GACpB,OAAO,CAAC,SAAS,4BAA4B,EAAE,CAAC,CAalD;AAID,wBAAgB,cAAc,CAC5B,IAAI,EAAE,IAAI,EACV,OAAO,EAAE,sBAAsB,EAC/B,OAAO,EAAE,MAAM,GACd;IAAE,aAAa,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE,CAI3C;AAUD,wBAAgB,uBAAuB,CACrC,IAAI,EAAE,aAAa,EACnB,OAAO,EAAE,sBAAsB,GAC9B,6BAA6B,CAI/B;AAED,wBAAgB,oBAAoB,CAClC,IAAI,EAAE,aAAa,EACnB,OAAO,EAAE,sBAAsB,EAC/B,UAAU,EAAE,MAAM,GACjB,0BAA0B,EAAE,CAM9B;AAED,MAAM,WAAW,kBAAkB;IACjC,QAAQ,CAAC,OAAO,EAAE,6BAA6B,CAAC;IAChD,QAAQ,CAAC,OAAO,EAAE,sBAAsB,CAAC;IACzC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;CAC5B;AAMD,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,aAAa,EAAE,IAAI,EAAE,kBAAkB,GAAG,IAAI,CASxF;AAmFD,wBAAsB,uBAAuB,CAC3C,GAAG,EAAE,YAAY,EACjB,IAAI,EAAE,aAAa,GAClB,OAAO,CAAC,WAAW,CAAC,CAyBtB;AAiCD,MAAM,WAAW,uBAAuB;IACtC,QAAQ,CAAC,OAAO,EAAE,sBAAsB,CAAC;IACzC,QAAQ,CAAC,IAAI,EAAE,IAAI,CAAC;IACpB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,aAAa,EAAE,gCAAgC,GAAG,SAAS,CAAC;CACtE;AAED,wBAAsB,sBAAsB,CAC1C,GAAG,EAAE,YAAY,EACjB,IAAI,EAAE,aAAa,GAClB,OAAO,CAAC,uBAAuB,GAAG,WAAW,CAAC,CA8BhD;AAED,wBAAsB,qBAAqB,CACzC,GAAG,EAAE,YAAY,EACjB,IAAI,EAAE,aAAa,GAClB,OAAO,CAAC,WAAW,CAAC,CAKtB"}
@@ -24,11 +24,13 @@ import { embedAndStoreMemory } from "./memory-embedding.js";
24
24
  import { recordMemoryAudit } from "./memory-audit-handler.js";
25
25
  import { captureSalientFromTurn } from "./memory-salience.js";
26
26
  import { assertUsableAssistantContent, isLegacyEmptyAssistantPlaceholder, } from "./assistant-response.js";
27
+ import { conversationForGatewayWithCompaction } from "./conversation-compaction.js";
28
+ import { persistChatCompactionEvidence } from "./chat-compaction-evidence.js";
27
29
  const DEFAULT_CHAT_MODEL = "example-chat-model";
28
30
  const DEFAULT_CHAT_TITLE = "New chat";
29
31
  const MAX_BODY_BYTES = 128_000;
30
32
  const MAX_CHAT_INPUT_CHARS = 16_000;
31
- const MAX_CONTEXT_MESSAGES = 24;
33
+ export const MAX_CONTEXT_MESSAGES = 24;
32
34
  class BodyTooLargeError extends Error {
33
35
  constructor() {
34
36
  super("request body too large");
@@ -204,11 +206,17 @@ function messageForGateway(message) {
204
206
  }
205
207
  return { role: message.role, content: message.content };
206
208
  }
207
- function conversationForGateway(messages) {
208
- const usable = messages
209
+ // The map+filter projection shared by conversationForGateway and the PR4-W2 compaction shim
210
+ // (conversation-compaction.ts). Returns the full filtered, ORDER-PRESERVED set of user/assistant
211
+ // turns BEFORE the recent-window slice — so the shim's "post-filter count", kept window, and
212
+ // dropped prefix are derived from the exact same selection the slice operates on.
213
+ export function usableGatewayMessages(messages) {
214
+ return messages
209
215
  .map(messageForGateway)
210
- .filter((message) => message !== null)
211
- .slice(-MAX_CONTEXT_MESSAGES);
216
+ .filter((message) => message !== null);
217
+ }
218
+ export function conversationForGateway(messages) {
219
+ const usable = usableGatewayMessages(messages).slice(-MAX_CONTEXT_MESSAGES);
212
220
  return [
213
221
  {
214
222
  role: "system",
@@ -688,48 +696,90 @@ export function buildChatPatch(chat, request, modelId) {
688
696
  // memory text). Shared by the buffered (persistModelChatTurn) and streaming
689
697
  // (handleSendDesktopChatStream) paths so both send a byte-identical prompt. `memoryText` is
690
698
  // `memory.context.text`.
699
+ // ADR-0057 D3: the full compaction outcome for the latest turn, including the optional
700
+ // ContextCompactionRecord that buildGatewayMessages drops. Both send paths call this to capture the
701
+ // record for best-effort regulated evidence; buildGatewayMessages delegates here for the messages.
702
+ // Reads store.listMessages once — identical history slice the prompt is built from.
703
+ export function deriveCompactionOutcome(deps, request) {
704
+ return conversationForGatewayWithCompaction(deps.store.listMessages(request.chatId), {
705
+ contextProfile: deps.contextProfile,
706
+ });
707
+ }
691
708
  export function buildGatewayMessages(deps, request, memoryText) {
692
- const history = conversationForGateway(deps.store.listMessages(request.chatId));
693
- return applyDocumentContextToLatestUserTurn(history, request, memoryText);
709
+ // PR4-W2 (ADR-0055 D3): route history assembly through the predicate-gated compaction shim. On
710
+ // the fast path (no profile or <= MAX_CONTEXT_MESSAGES filtered turns) the shim returns the EXACT
711
+ // conversationForGateway(...) value, so this is byte-identical to the pre-PR4 path.
712
+ const { messages } = deriveCompactionOutcome(deps, request);
713
+ return applyDocumentContextToLatestUserTurn(messages, request, memoryText);
714
+ }
715
+ // ADR-0057 D3: best-effort persist of the turn's compaction record AFTER the response completes.
716
+ // finishedAt is captured here (post-turn). Shared by the buffered and streaming send paths so the
717
+ // runId + timing are identical. Never throws into the send path (persistChatCompactionEvidence is
718
+ // fully guarded and a no-op on the fast path).
719
+ export function recordChatCompaction(deps, turn) {
720
+ persistChatCompactionEvidence(deps, {
721
+ compaction: turn.outcome.compaction,
722
+ chatId: turn.request.chatId,
723
+ modelId: turn.modelId,
724
+ messageCount: turn.messageCount,
725
+ startedAt: turn.startedAt,
726
+ finishedAt: Date.now(),
727
+ });
694
728
  }
695
729
  async function persistModelChatTurn(deps, request, chat, modelId, memoryContext) {
696
730
  const model = deps.modelPortFactory(modelId);
697
731
  if (model === undefined) {
698
732
  return { status: 400, body: errorBody("NO_MODEL", "No model provider is configured.") };
699
733
  }
734
+ // ADR-0057 D3: pin the pre-user-message count BEFORE createUserMessage stores the turn, so the
735
+ // compaction-evidence runId is collision-free and matches the streaming path's lifecycle moment.
736
+ const messageCountBeforeTurn = deps.store.listMessages(request.chatId).length;
737
+ const startedAt = Date.now();
700
738
  try {
701
739
  const memory = memoryContext === undefined
702
740
  ? emptyMemoryResult(false)
703
741
  : await buildMemoryResult(request, deps, memoryContext);
704
742
  const userMessage = createUserMessage(deps, request);
743
+ const outcome = deriveCompactionOutcome(deps, request);
705
744
  const messages = buildGatewayMessages(deps, request, memory.context.text);
706
- const response = await model.call({
745
+ const response = await model.call({ modelId, messages, stream: false }, new AbortController().signal);
746
+ recordChatCompaction(deps, {
747
+ outcome,
748
+ request,
707
749
  modelId,
708
- messages,
709
- stream: false,
710
- }, new AbortController().signal);
711
- // Issue #631 redact the model's raw content before persisting and before returning it to
712
- // the browser. A model that echoes a secret from its context (e.g. an apiKey injected via
713
- // system prompt) would otherwise surface it un-redacted on the success path, mirroring the
714
- // grounded-QA path (grounded-qa.ts line 549) which already applies deps.redactor here.
715
- const redactedContent = deps.redactor(response.content);
716
- const assistantMessage = createAssistantMessage(deps, request, redactedContent, modelId);
717
- const memoryActions = await collectMemoryActions(deps, request, memoryContext, modelId, redactedContent);
718
- const chatPatch = buildChatPatch(chat, request, modelId);
719
- return {
720
- status: 200,
721
- body: {
722
- chat: deps.store.updateChat(request.chatId, chatPatch),
723
- messages: [userMessage, assistantMessage],
724
- usage: response.usage,
725
- memory: { ...memory, actions: memoryActions },
726
- },
727
- };
750
+ messageCount: messageCountBeforeTurn,
751
+ startedAt,
752
+ });
753
+ return await finalizeBufferedTurn(deps, { request, chat, modelId, memoryContext }, memory, {
754
+ userMessage,
755
+ response,
756
+ });
728
757
  }
729
758
  catch (error) {
730
759
  return desktopChatErrorResult(error, deps);
731
760
  }
732
761
  }
762
+ // Post-response assembly for the buffered send: redacts the model content, persists the assistant
763
+ // message, collects memory actions, and builds the 200 body. Extracted to keep persistModelChatTurn
764
+ // within the function-length budget after the ADR-0057 D3 compaction wiring.
765
+ async function finalizeBufferedTurn(deps, turn, memory, result) {
766
+ const { request, chat, modelId, memoryContext } = turn;
767
+ // Issue #631 — redact the model's raw content before persisting and before returning it to the
768
+ // browser, mirroring the grounded-QA path which already applies deps.redactor here.
769
+ const redactedContent = deps.redactor(result.response.content);
770
+ const assistantMessage = createAssistantMessage(deps, request, redactedContent, modelId);
771
+ const memoryActions = await collectMemoryActions(deps, request, memoryContext, modelId, redactedContent);
772
+ const chatPatch = buildChatPatch(chat, request, modelId);
773
+ return {
774
+ status: 200,
775
+ body: {
776
+ chat: deps.store.updateChat(request.chatId, chatPatch),
777
+ messages: [result.userMessage, assistantMessage],
778
+ usage: result.response.usage,
779
+ memory: { ...memory, actions: memoryActions },
780
+ },
781
+ };
782
+ }
733
783
  export async function handleCreateDesktopChat(ctx, deps) {
734
784
  const body = await readJsonObject(ctx.req);
735
785
  if (isRouteResult(body))
@@ -1 +1 @@
1
- {"version":3,"file":"chat-stream-handlers.d.ts","sourceRoot":"","sources":["../src/chat-stream-handlers.ts"],"names":[],"mappings":"AAYA,OAAO,EAAwB,KAAK,cAAc,EAAE,KAAK,YAAY,EAAE,MAAM,aAAa,CAAC;AAC3F,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,WAAW,CAAC;AAyK/C,wBAAsB,2BAA2B,CAC/C,GAAG,EAAE,YAAY,EACjB,IAAI,EAAE,aAAa,GAClB,OAAO,CAAC,cAAc,CAAC,CAyBzB"}
1
+ {"version":3,"file":"chat-stream-handlers.d.ts","sourceRoot":"","sources":["../src/chat-stream-handlers.ts"],"names":[],"mappings":"AAYA,OAAO,EAAwB,KAAK,cAAc,EAAE,KAAK,YAAY,EAAE,MAAM,aAAa,CAAC;AAC3F,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,WAAW,CAAC;AAuL/C,wBAAsB,2BAA2B,CAC/C,GAAG,EAAE,YAAY,EACjB,IAAI,EAAE,aAAa,GAClB,OAAO,CAAC,cAAc,CAAC,CAyBzB"}
@@ -10,7 +10,7 @@
10
10
  import { SSE_HEADERS } from "./sse.js";
11
11
  import { writeOrDestroy } from "./sse-write.js";
12
12
  import { STREAMING, errorBody } from "./routes.js";
13
- import { buildChatPatch, buildGatewayMessages, buildMemoryResult, collectMemoryActions, createAssistantMessage, createUserMessage, desktopChatErrorResult, emptyMemoryResult, prepareDesktopChatSend, } from "./chat-handlers.js";
13
+ import { buildChatPatch, buildGatewayMessages, buildMemoryResult, collectMemoryActions, createAssistantMessage, createUserMessage, deriveCompactionOutcome, desktopChatErrorResult, emptyMemoryResult, prepareDesktopChatSend, recordChatCompaction, } from "./chat-handlers.js";
14
14
  // One SSE message. JSON.stringify never emits a raw newline inside a string (newlines escape to
15
15
  // `\n`), so a single `data:` line is always valid framing — no manual escaping, mirroring sse.ts.
16
16
  function sseMessage(event, data) {
@@ -93,7 +93,12 @@ function errorEvent(error, deps) {
93
93
  async function streamAndPersist(ctx, deps, prepared, callStream, controller) {
94
94
  const { request, chat, modelId, memoryContext } = prepared;
95
95
  const memory = await resolveMemory(deps, request, memoryContext);
96
+ // ADR-0057 D3: pin the pre-user-message count BEFORE createUserMessage, mirroring the buffered
97
+ // path's lifecycle moment so the compaction-evidence runId is identical across both paths.
98
+ const messageCountBeforeTurn = deps.store.listMessages(request.chatId).length;
99
+ const startedAt = Date.now();
96
100
  const userMessage = createUserMessage(deps, request);
101
+ const outcome = deriveCompactionOutcome(deps, request);
97
102
  const messages = buildGatewayMessages(deps, request, memory.context.text);
98
103
  const stream = callStream({ modelId, messages }, controller.signal);
99
104
  const turn = await streamConversation(ctx, deps, stream, controller);
@@ -102,6 +107,13 @@ async function streamAndPersist(ctx, deps, prepared, callStream, controller) {
102
107
  return;
103
108
  }
104
109
  const payload = await persistStreamedTurn(deps, request, chat, modelId, memory, memoryContext, turn, userMessage);
110
+ recordChatCompaction(deps, {
111
+ outcome,
112
+ request,
113
+ modelId,
114
+ messageCount: messageCountBeforeTurn,
115
+ startedAt,
116
+ });
105
117
  ctx.res.write(sseMessage("done", payload));
106
118
  }
107
119
  export async function handleSendDesktopChatStream(ctx, deps) {
@@ -0,0 +1,12 @@
1
+ import { type ContextCompactionRecord, type ContextProfile } from "@oscharko-dev/keiko-contracts";
2
+ import type { ChatMessage } from "./store/index.js";
3
+ import { type GatewayConversationMessage } from "./chat-handlers.js";
4
+ export interface ConversationCompactionOptions {
5
+ readonly contextProfile?: ContextProfile | undefined;
6
+ }
7
+ export interface ConversationCompactionOutcome {
8
+ readonly messages: GatewayConversationMessage[];
9
+ readonly compaction?: ContextCompactionRecord | undefined;
10
+ }
11
+ export declare function conversationForGatewayWithCompaction(messages: readonly ChatMessage[], opts?: ConversationCompactionOptions): ConversationCompactionOutcome;
12
+ //# sourceMappingURL=conversation-compaction.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"conversation-compaction.d.ts","sourceRoot":"","sources":["../src/conversation-compaction.ts"],"names":[],"mappings":"AAcA,OAAO,EAIL,KAAK,uBAAuB,EAC5B,KAAK,cAAc,EAEpB,MAAM,+BAA+B,CAAC;AAEvC,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AACpD,OAAO,EAIL,KAAK,0BAA0B,EAChC,MAAM,oBAAoB,CAAC;AAE5B,MAAM,WAAW,6BAA6B;IAC5C,QAAQ,CAAC,cAAc,CAAC,EAAE,cAAc,GAAG,SAAS,CAAC;CACtD;AAED,MAAM,WAAW,6BAA6B;IAC5C,QAAQ,CAAC,QAAQ,EAAE,0BAA0B,EAAE,CAAC;IAChD,QAAQ,CAAC,UAAU,CAAC,EAAE,uBAAuB,GAAG,SAAS,CAAC;CAC3D;AAgBD,wBAAgB,oCAAoC,CAClD,QAAQ,EAAE,SAAS,WAAW,EAAE,EAChC,IAAI,GAAE,6BAAkC,GACvC,6BAA6B,CAM/B"}
@@ -0,0 +1,102 @@
1
+ // PR4-W2 chat history-compaction splice (ADR-0055 D3) — the ONE genuine behavioral change in the
2
+ // context-engineering milestone. A PURE, deterministic, offline, no-clock, no-random shim that
3
+ // wraps conversationForGateway (chat-handlers.ts).
4
+ //
5
+ // ACTIVATION PREDICATE (the unchanged-guarantee, ADR-0055 D6):
6
+ // opts.contextProfile === undefined || filtered.length <= MAX_CONTEXT_MESSAGES
7
+ // When true, this returns the EXACT return value of conversationForGateway(messages) — no copy,
8
+ // no spread, no transform — so short sessions and no-profile callers are byte-identical to today.
9
+ //
10
+ // SLOW PATH (profile present AND > MAX_CONTEXT_MESSAGES filtered turns): the older turns that the
11
+ // existing slice HARD-DROPS are folded into a single deterministic, REDACTED, byte-bounded summary
12
+ // segment (role "user", model-agnostic-safe — not a second system turn) placed immediately AFTER
13
+ // the existing system message, accompanied by a validated ContextCompactionRecord. No model call.
14
+ import { CONTEXT_ENGINEERING_SCHEMA_VERSION, estimateTokens, validateContextCompactionRecord, } from "@oscharko-dev/keiko-contracts";
15
+ import { redact } from "@oscharko-dev/keiko-security";
16
+ import { MAX_CONTEXT_MESSAGES, conversationForGateway, usableGatewayMessages, } from "./chat-handlers.js";
17
+ // Per-dropped-message redacted snippet budget (UTF-8 bytes). Bounds a single noisy turn.
18
+ const SNIPPET_BYTE_BUDGET = 200;
19
+ // Total token budget for the whole synthetic summary segment (a few KB at ~3.5 bytes/token).
20
+ const SUMMARY_TOKEN_BUDGET = 1024;
21
+ // Deterministic, offline, predicate-gated wrapper over conversationForGateway. On the fast path it
22
+ // returns the existing function's value verbatim; on the slow path it inserts a redacted summary
23
+ // after the system message so platform instructions remain first.
24
+ export function conversationForGatewayWithCompaction(messages, opts = {}) {
25
+ const filtered = usableGatewayMessages(messages);
26
+ if (opts.contextProfile === undefined || filtered.length <= MAX_CONTEXT_MESSAGES) {
27
+ return { messages: conversationForGateway(messages) };
28
+ }
29
+ return buildCompactedOutcome(messages, filtered);
30
+ }
31
+ function buildCompactedOutcome(messages, filtered) {
32
+ const dropped = toDroppedTurns(filtered.slice(0, filtered.length - MAX_CONTEXT_MESSAGES));
33
+ const summaryContent = buildSummaryContent(dropped);
34
+ const summarySegment = { role: "user", content: summaryContent };
35
+ const record = buildRecord(dropped, summaryContent);
36
+ const gatewayMessages = conversationForGateway(messages);
37
+ const [systemMessage, ...recentMessages] = gatewayMessages;
38
+ return {
39
+ messages: systemMessage === undefined
40
+ ? [summarySegment]
41
+ : [systemMessage, summarySegment, ...recentMessages],
42
+ compaction: record,
43
+ };
44
+ }
45
+ function toDroppedTurns(prefix) {
46
+ return prefix.map((turn, index) => ({
47
+ role: turn.role,
48
+ content: turn.content,
49
+ stableId: `history-msg-${String(index)}`,
50
+ }));
51
+ }
52
+ // Redact + byte-bound a single dropped turn to a one-line snippet. UTF-8-safe truncation.
53
+ function snippetFor(turn) {
54
+ const redacted = redact(turn.content).replace(/\s+/gu, " ").trim();
55
+ const bytes = new TextEncoder().encode(redacted);
56
+ if (bytes.length <= SNIPPET_BYTE_BUDGET) {
57
+ return redacted;
58
+ }
59
+ const slice = bytes.slice(0, SNIPPET_BYTE_BUDGET);
60
+ return new TextDecoder("utf-8", { fatal: false }).decode(slice).trimEnd() + "…";
61
+ }
62
+ const SUMMARY_HEADER = "[Automated summary of earlier conversation turns — older messages were compacted to fit the " +
63
+ "context window. The verbatim recent turns follow below.]";
64
+ function buildSummaryContent(dropped) {
65
+ const lines = [SUMMARY_HEADER, `Dropped ${String(dropped.length)} earlier turn(s):`];
66
+ let omitted = 0;
67
+ for (const turn of dropped) {
68
+ const candidate = `- [${turn.role}] ${snippetFor(turn)}`;
69
+ const next = [...lines, candidate].join("\n");
70
+ if (estimateTokens(next) > SUMMARY_TOKEN_BUDGET) {
71
+ omitted += 1;
72
+ continue;
73
+ }
74
+ lines.push(candidate);
75
+ }
76
+ if (omitted > 0) {
77
+ lines.push(`…and ${String(omitted)} further turn(s) omitted to stay within the summary budget.`);
78
+ }
79
+ return lines.join("\n");
80
+ }
81
+ function sourceSpansFor(dropped) {
82
+ return dropped.map((turn) => ({ kind: "message", stableId: turn.stableId }));
83
+ }
84
+ function buildRecord(dropped, summaryContent) {
85
+ const tokensBefore = dropped.reduce((sum, turn) => sum + estimateTokens(turn.content), 0);
86
+ const record = {
87
+ schemaVersion: CONTEXT_ENGINEERING_SCHEMA_VERSION,
88
+ laneId: "history-summary",
89
+ reason: "history-window",
90
+ itemsBefore: dropped.length,
91
+ itemsAfter: 0,
92
+ tokensBefore,
93
+ tokensAfter: estimateTokens(summaryContent),
94
+ orderedAt: dropped.length,
95
+ sourceSpans: sourceSpansFor(dropped),
96
+ };
97
+ const validation = validateContextCompactionRecord(record);
98
+ if (!validation.ok) {
99
+ throw new Error(`conversation-compaction produced an invalid record: ${validation.reasons.join(", ")}`);
100
+ }
101
+ return record;
102
+ }
package/dist/deps.d.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  import { type EnvSource, type GatewayConfig } from "@oscharko-dev/keiko-model-gateway";
2
2
  import type { ModelPort } from "@oscharko-dev/keiko-harness";
3
+ import { type ContextProfile } from "@oscharko-dev/keiko-contracts";
3
4
  import type { EvidenceStore } from "@oscharko-dev/keiko-evidence";
4
5
  import type { RunRegistry } from "./runs.js";
5
6
  import { type UiStore } from "./store/index.js";
@@ -56,6 +57,7 @@ export interface UiHandlerDeps {
56
57
  readonly relationship?: RelationshipHandlerDeps | undefined;
57
58
  readonly evidenceDir?: string | undefined;
58
59
  readonly localKnowledgeKeyProvider?: KnowledgeStoreKeyProvider | undefined;
60
+ readonly contextProfile?: ContextProfile | undefined;
59
61
  }
60
62
  export interface BuildHandlerDepsOptions {
61
63
  readonly configPath: string | undefined;
@@ -1 +1 @@
1
- {"version":3,"file":"deps.d.ts","sourceRoot":"","sources":["../src/deps.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"deps.d.ts","sourceRoot":"","sources":["../src/deps.ts"],"names":[],"mappings":"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"}
package/dist/deps.js CHANGED
@@ -13,6 +13,7 @@ import { resolveCostClass } from "@oscharko-dev/keiko-model-gateway";
13
13
  import { writeSideFile } from "@oscharko-dev/keiko-evidence";
14
14
  import { deepRedactStrings } from "@oscharko-dev/keiko-evidence";
15
15
  import { keikoApiKeySecretValues } from "@oscharko-dev/keiko-security";
16
+ import { DEFAULT_CONTEXT_PROFILE } from "@oscharko-dev/keiko-contracts";
16
17
  import { nodeWorkspaceFs } from "@oscharko-dev/keiko-workspace/internal/fs";
17
18
  import { createNodeEvidenceStore, resolveEvidenceDir } from "@oscharko-dev/keiko-evidence";
18
19
  import { dirname, join } from "node:path";
@@ -430,7 +431,6 @@ export function buildUiHandlerDeps(options) {
430
431
  const liveRedactor = (value) => deepRedactStrings(value, redactString);
431
432
  const { store: uiStore, relationship } = composePersistence(options.store, resolvedUiDbPath, redactString, options.env);
432
433
  const preferredProjectPath = seedInitialProject(uiStore, resolvedUiDbPath, options.initialProjectPath);
433
- const peripherals = buildPeripherals(options, uiStore, evidenceStore, redactString, liveRedactor);
434
434
  return {
435
435
  config,
436
436
  configPresent,
@@ -450,7 +450,8 @@ export function buildUiHandlerDeps(options) {
450
450
  gatewayModelDiscovery: options.gatewayModelDiscovery,
451
451
  figmaCredentialTester: options.figmaCredentialTester,
452
452
  localKnowledgeKeyProvider: createLocalKnowledgeKeyProvider({ env: options.env }),
453
- ...peripherals,
453
+ contextProfile: DEFAULT_CONTEXT_PROFILE,
454
+ ...buildPeripherals(options, uiStore, evidenceStore, redactString, liveRedactor),
454
455
  consolidationJobs: createConsolidationJobRegistry(),
455
456
  ...(relationship === undefined ? {} : { relationship }),
456
457
  };
@@ -0,0 +1,5 @@
1
+ import type { ConnectedContextPack } from "@oscharko-dev/keiko-contracts/connected-context";
2
+ import { type ContextAssemblyDiagnostics, type ContextProfile } from "@oscharko-dev/keiko-contracts";
3
+ export declare function deriveGroundedContextAssembly(pack: ConnectedContextPack, profile: ContextProfile): ContextAssemblyDiagnostics;
4
+ export declare function attachContextBudgetDiagnostics(pack: ConnectedContextPack, profile: ContextProfile): ConnectedContextPack;
5
+ //# sourceMappingURL=grounded-context-diagnostics.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"grounded-context-diagnostics.d.ts","sourceRoot":"","sources":["../src/grounded-context-diagnostics.ts"],"names":[],"mappings":"AAYA,OAAO,KAAK,EACV,oBAAoB,EAErB,MAAM,iDAAiD,CAAC;AACzD,OAAO,EAEL,KAAK,0BAA0B,EAE/B,KAAK,cAAc,EACpB,MAAM,+BAA+B,CAAC;AAqEvC,wBAAgB,6BAA6B,CAC3C,IAAI,EAAE,oBAAoB,EAC1B,OAAO,EAAE,cAAc,GACtB,0BAA0B,CAE5B;AAOD,wBAAgB,8BAA8B,CAC5C,IAAI,EAAE,oBAAoB,EAC1B,OAAO,EAAE,cAAc,GACtB,oBAAoB,CAMtB"}
@@ -0,0 +1,77 @@
1
+ // PR4-W1 grounded diagnostics observer (ADR-0055 D1). A PURE, no-IO, no-clock function that
2
+ // accepts a fully assembled `ConnectedContextPack` and a `ContextProfile` and returns a pack
3
+ // whose `diagnostics.contextBudget?` is populated with a deterministic `ContextBudget` derived by
4
+ // running the keiko-workflows allocator over lanes mapped from the pack.
5
+ //
6
+ // NON-NEGOTIABLE INVARIANT (AC5): this observer MUST NOT touch any field a prompt builder reads.
7
+ // `buildGroundedGatewayMessages` (grounded-qa.ts) reads only pack.schemaVersion / stableId /
8
+ // scope / query / budget / usage / omitted / files / uncertainty — never pack.diagnostics. The
9
+ // observer therefore changes ONLY the additive optional `diagnostics.contextBudget?` field while
10
+ // preserving the existing `diagnostics.rankedCandidates` (the first-lexical-ring explainability).
11
+ // pack.files, pack.budget, pack.usage and pack.stableId are returned reference-identical.
12
+ import { CONTEXT_ENGINEERING_SCHEMA_VERSION, } from "@oscharko-dev/keiko-contracts";
13
+ import { allocateContext, DEFAULT_CONTEXT_BUDGET, } from "@oscharko-dev/keiko-workflows";
14
+ // The grounded path carries repository evidence excerpts only; the system-contract / user-task /
15
+ // plan / tool / memory / history / verification lanes are not assembled here, so they are passed
16
+ // empty. Diagnostics over the populated repo-evidence lane are the W1 deliverable.
17
+ function repoEvidenceLaneItems(pack) {
18
+ const items = [];
19
+ for (const file of pack.files) {
20
+ for (const excerpt of file.excerpts) {
21
+ items.push({
22
+ id: excerpt.atom.stableId,
23
+ text: excerpt.content,
24
+ score: excerpt.atom.score,
25
+ });
26
+ }
27
+ }
28
+ return items;
29
+ }
30
+ function groundedLanes(pack) {
31
+ return [{ laneId: "repo-evidence", items: repoEvidenceLaneItems(pack) }];
32
+ }
33
+ // Builds a ContextBudget whose `profile` is the supplied profile (so validateContextBudget's
34
+ // profile-identity check holds) while reusing the canonical, tunable lane rows. Pure — no clock,
35
+ // no IO, never mutates the inputs.
36
+ function budgetForProfile(profile) {
37
+ return {
38
+ schemaVersion: CONTEXT_ENGINEERING_SCHEMA_VERSION,
39
+ profile,
40
+ lanes: DEFAULT_CONTEXT_BUDGET.lanes,
41
+ };
42
+ }
43
+ function diagnosticsWithBudget(existing, budget) {
44
+ const rankedCandidates = existing?.rankedCandidates ?? [];
45
+ return { ...existing, rankedCandidates, contextBudget: budget };
46
+ }
47
+ // SHARED lane-derivation + allocation pass (single source of truth for the observer AND the
48
+ // evidence producer below). Pure, no-IO, no-clock: maps the pack's repo-evidence lane, builds the
49
+ // profile budget, and runs the deterministic allocator once. Both the BFF/UI ContextBudget slot
50
+ // and the regulated ContextAssemblyDiagnostics are projected from THIS result — there is no
51
+ // divergent derivation path that could let the two views disagree for the same pack + profile.
52
+ function allocateGroundedContext(pack, profile) {
53
+ const budget = budgetForProfile(profile);
54
+ const result = allocateContext({ profile, budget, lanes: groundedLanes(pack) });
55
+ return { budget, result };
56
+ }
57
+ // EVIDENCE producer (ADR-0056 W3). Returns the rich ContextAssemblyDiagnostics
58
+ // (allocateContext(...).diagnostics) for the pack's repo-evidence lane under the supplied profile.
59
+ // This is the value persisted to EvidenceManifest.contextAssembly? — NOT the ContextBudget that
60
+ // ContextPackDiagnostics.contextBudget? carries. Derives the lane via the same shared helper the
61
+ // observer uses, so the persisted diagnostics describe exactly the budget plan the observer
62
+ // attached. Pure and deterministic.
63
+ export function deriveGroundedContextAssembly(pack, profile) {
64
+ return allocateGroundedContext(pack, profile).result.diagnostics;
65
+ }
66
+ // Runs the deterministic allocator over the pack-derived lanes and attaches the resulting
67
+ // ContextBudget to pack.diagnostics.contextBudget. The allocator pass exercises the budget plan
68
+ // over real lane items (proving the budget is meaningful for this pack); the attached value is the
69
+ // ContextBudget that ContextPackDiagnostics.contextBudget? carries (the only diagnostics slot the
70
+ // pack contract exposes). All other pack fields are returned unchanged.
71
+ export function attachContextBudgetDiagnostics(pack, profile) {
72
+ const { budget } = allocateGroundedContext(pack, profile);
73
+ return {
74
+ ...pack,
75
+ diagnostics: diagnosticsWithBudget(pack.diagnostics, budget),
76
+ };
77
+ }
@@ -1,4 +1,5 @@
1
1
  import { type ConnectedContextPack, type ExplorationBudget, type RetrievalQuery, type SelectedScope } from "@oscharko-dev/keiko-contracts/connected-context";
2
+ import type { ContextProfile } from "@oscharko-dev/keiko-contracts";
2
3
  import { type ClarificationPrompt, type ExplorationPlan, type MicroIndex } from "@oscharko-dev/keiko-workflows";
3
4
  import { DEFAULT_SEARCH_LIMITS, type WorkspaceFs, type WorkspaceInfo } from "@oscharko-dev/keiko-workspace";
4
5
  import { type GroundedAnswerPayload } from "./grounded-answer.js";
@@ -19,6 +20,7 @@ export interface OrchestratorDeps {
19
20
  readonly detectWorkspace?: (root: string, fs: WorkspaceFs) => WorkspaceInfo;
20
21
  readonly recordPlan?: (plan: ExplorationPlan) => void;
21
22
  readonly microIndex?: MicroIndex;
23
+ readonly contextProfile?: ContextProfile | undefined;
22
24
  }
23
25
  export interface OrchestratorOutput {
24
26
  readonly pack: ConnectedContextPack;
@@ -1 +1 @@
1
- {"version":3,"file":"grounded-orchestrator.d.ts","sourceRoot":"","sources":["../src/grounded-orchestrator.ts"],"names":[],"mappings":"AAYA,OAAO,EAGL,KAAK,oBAAoB,EAEzB,KAAK,iBAAiB,EAGtB,KAAK,cAAc,EACnB,KAAK,aAAa,EAEnB,MAAM,iDAAiD,CAAC;AACzD,OAAO,EAUL,KAAK,mBAAmB,EAExB,KAAK,eAAe,EAEpB,KAAK,UAAU,EAIhB,MAAM,+BAA+B,CAAC;AACvC,OAAO,EACL,qBAAqB,EAerB,KAAK,WAAW,EAChB,KAAK,aAAa,EAGnB,MAAM,+BAA+B,CAAC;AAGvC,OAAO,EAAkC,KAAK,qBAAqB,EAAE,MAAM,sBAAsB,CAAC;AASlG,MAAM,WAAW,gBAAgB;IAG/B,MAAM,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,oBAAoB,GAAG,OAAO,CAAC,qBAAqB,CAAC,CAAC;CACtF;AAED,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,KAAK,EAAE,aAAa,CAAC;IAC9B,QAAQ,CAAC,KAAK,EAAE,cAAc,CAAC;IAC/B,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;IAC/B,QAAQ,CAAC,MAAM,CAAC,EAAE,iBAAiB,CAAC;CACrC;AAED,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,QAAQ,EAAE,gBAAgB,CAAC;IACpC,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,MAAM,CAAC;IAC9B,QAAQ,CAAC,MAAM,CAAC,EAAE,WAAW,GAAG,SAAS,CAAC;IAE1C,QAAQ,CAAC,EAAE,CAAC,EAAE,WAAW,CAAC;IAE1B,QAAQ,CAAC,eAAe,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,WAAW,KAAK,aAAa,CAAC;IAE5E,QAAQ,CAAC,UAAU,CAAC,EAAE,CAAC,IAAI,EAAE,eAAe,KAAK,IAAI,CAAC;IAEtD,QAAQ,CAAC,UAAU,CAAC,EAAE,UAAU,CAAC;CAClC;AAED,MAAM,WAAW,kBAAkB;IACjC,QAAQ,CAAC,IAAI,EAAE,oBAAoB,CAAC;IACpC,QAAQ,CAAC,gBAAgB,EAAE,MAAM,CAAC;IAClC,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,IAAI,CAAC,EAAE,eAAe,CAAC;CACjC;AAMD,MAAM,WAAW,mBAAmB;IAClC,QAAQ,CAAC,IAAI,EAAE,oBAAoB,CAAC;IACpC,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,IAAI,EAAE,eAAe,CAAC;CAChC;AAKD,qBAAa,wBAAyB,SAAQ,KAAK;aACd,aAAa,EAAE,mBAAmB;gBAAlC,aAAa,EAAE,mBAAmB;CAItE;AAMD,wBAAgB,wBAAwB,CAAC,KAAK,EAAE,wBAAwB,GAAG,MAAM,CAgBhF;AA25BD,wBAAgB,sBAAsB,CAAC,SAAS,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAM/E;AAw5BD,wBAAsB,4BAA4B,CAChD,KAAK,EAAE,iBAAiB,EACxB,IAAI,EAAE,gBAAgB,GACrB,OAAO,CAAC,mBAAmB,CAAC,CA4C9B;AAED,wBAAsB,sBAAsB,CAC1C,KAAK,EAAE,iBAAiB,EACxB,IAAI,EAAE,gBAAgB,GACrB,OAAO,CAAC,kBAAkB,CAAC,CA8B7B;AAID,OAAO,EAAE,qBAAqB,EAAE,CAAC"}
1
+ {"version":3,"file":"grounded-orchestrator.d.ts","sourceRoot":"","sources":["../src/grounded-orchestrator.ts"],"names":[],"mappings":"AAYA,OAAO,EAGL,KAAK,oBAAoB,EAGzB,KAAK,iBAAiB,EAGtB,KAAK,cAAc,EACnB,KAAK,aAAa,EAEnB,MAAM,iDAAiD,CAAC;AACzD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,+BAA+B,CAAC;AACpE,OAAO,EAUL,KAAK,mBAAmB,EAExB,KAAK,eAAe,EAEpB,KAAK,UAAU,EAIhB,MAAM,+BAA+B,CAAC;AACvC,OAAO,EAEL,qBAAqB,EAiBrB,KAAK,WAAW,EAChB,KAAK,aAAa,EAGnB,MAAM,+BAA+B,CAAC;AAGvC,OAAO,EAAkC,KAAK,qBAAqB,EAAE,MAAM,sBAAsB,CAAC;AAUlG,MAAM,WAAW,gBAAgB;IAG/B,MAAM,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,oBAAoB,GAAG,OAAO,CAAC,qBAAqB,CAAC,CAAC;CACtF;AAED,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,KAAK,EAAE,aAAa,CAAC;IAC9B,QAAQ,CAAC,KAAK,EAAE,cAAc,CAAC;IAC/B,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;IAC/B,QAAQ,CAAC,MAAM,CAAC,EAAE,iBAAiB,CAAC;CACrC;AAED,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,QAAQ,EAAE,gBAAgB,CAAC;IACpC,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,MAAM,CAAC;IAC9B,QAAQ,CAAC,MAAM,CAAC,EAAE,WAAW,GAAG,SAAS,CAAC;IAE1C,QAAQ,CAAC,EAAE,CAAC,EAAE,WAAW,CAAC;IAE1B,QAAQ,CAAC,eAAe,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,WAAW,KAAK,aAAa,CAAC;IAE5E,QAAQ,CAAC,UAAU,CAAC,EAAE,CAAC,IAAI,EAAE,eAAe,KAAK,IAAI,CAAC;IAEtD,QAAQ,CAAC,UAAU,CAAC,EAAE,UAAU,CAAC;IAKjC,QAAQ,CAAC,cAAc,CAAC,EAAE,cAAc,GAAG,SAAS,CAAC;CACtD;AAED,MAAM,WAAW,kBAAkB;IACjC,QAAQ,CAAC,IAAI,EAAE,oBAAoB,CAAC;IACpC,QAAQ,CAAC,gBAAgB,EAAE,MAAM,CAAC;IAClC,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,IAAI,CAAC,EAAE,eAAe,CAAC;CACjC;AAMD,MAAM,WAAW,mBAAmB;IAClC,QAAQ,CAAC,IAAI,EAAE,oBAAoB,CAAC;IACpC,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,IAAI,EAAE,eAAe,CAAC;CAChC;AAKD,qBAAa,wBAAyB,SAAQ,KAAK;aACd,aAAa,EAAE,mBAAmB;gBAAlC,aAAa,EAAE,mBAAmB;CAItE;AAMD,wBAAgB,wBAAwB,CAAC,KAAK,EAAE,wBAAwB,GAAG,MAAM,CAgBhF;AAk8BD,wBAAgB,sBAAsB,CAAC,SAAS,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAM/E;AA09BD,wBAAsB,4BAA4B,CAChD,KAAK,EAAE,iBAAiB,EACxB,IAAI,EAAE,gBAAgB,GACrB,OAAO,CAAC,mBAAmB,CAAC,CA4C9B;AAED,wBAAsB,sBAAsB,CAC1C,KAAK,EAAE,iBAAiB,EACxB,IAAI,EAAE,gBAAgB,GACrB,OAAO,CAAC,kBAAkB,CAAC,CA8B7B;AAID,OAAO,EAAE,qBAAqB,EAAE,CAAC"}