@aexol/spectral 0.9.221 → 0.9.223
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/agent/index.d.ts +20 -3
- package/dist/agent/index.d.ts.map +1 -1
- package/dist/agent/index.js +286 -80
- package/dist/extensions/aexol-backend-client.d.ts +85 -0
- package/dist/extensions/aexol-backend-client.d.ts.map +1 -0
- package/dist/extensions/aexol-backend-client.js +159 -0
- package/dist/extensions/aexol-mcp.d.ts +10 -6
- package/dist/extensions/aexol-mcp.d.ts.map +1 -1
- package/dist/extensions/aexol-mcp.js +21 -106
- package/dist/extensions/logic-trace-backend-draft.d.ts +68 -0
- package/dist/extensions/logic-trace-backend-draft.d.ts.map +1 -0
- package/dist/extensions/logic-trace-backend-draft.js +171 -0
- package/dist/extensions/logic-trace-validate.d.ts +33 -0
- package/dist/extensions/logic-trace-validate.d.ts.map +1 -0
- package/dist/extensions/logic-trace-validate.js +73 -0
- package/dist/extensions/logic-trace.d.ts +213 -0
- package/dist/extensions/logic-trace.d.ts.map +1 -0
- package/dist/extensions/logic-trace.js +1098 -0
- package/dist/generated/parser/lexer.d.ts +148 -0
- package/dist/generated/parser/lexer.d.ts.map +1 -0
- package/dist/generated/parser/lexer.js +877 -0
- package/dist/generated/parser/mod.d.ts +26 -0
- package/dist/generated/parser/mod.d.ts.map +1 -0
- package/dist/generated/parser/mod.js +30 -0
- package/dist/generated/parser/parser.d.ts +161 -0
- package/dist/generated/parser/parser.d.ts.map +1 -0
- package/dist/generated/parser/parser.js +754 -0
- package/dist/generated/parser/tokenizer.d.ts +107 -0
- package/dist/generated/parser/tokenizer.d.ts.map +1 -0
- package/dist/generated/parser/tokenizer.js +202 -0
- package/dist/generated/parser/validator.d.ts +45 -0
- package/dist/generated/parser/validator.d.ts.map +1 -0
- package/dist/generated/parser/validator.js +202 -0
- package/dist/generated/zeus/const.d.ts.map +1 -1
- package/dist/generated/zeus/const.js +4 -170
- package/dist/generated/zeus/index.d.ts +8 -873
- package/dist/generated/zeus/index.d.ts.map +1 -1
- package/dist/generated/zeus/index.js +0 -7
- package/dist/mcp/ui-stream-types.d.ts +2 -2
- package/dist/memory/tools/share-project-observation.js +1 -1
- package/dist/memory/tools/write-project-observation.d.ts.map +1 -1
- package/dist/memory/tools/write-project-observation.js +4 -3
- package/dist/sdk/agent-core/agent-loop.d.ts.map +1 -1
- package/dist/sdk/agent-core/agent-loop.js +2 -4
- package/dist/sdk/agent-core/harness/types.d.ts +0 -1
- package/dist/sdk/agent-core/harness/types.d.ts.map +1 -1
- package/dist/sdk/agent-core/index.d.ts +0 -8
- package/dist/sdk/agent-core/index.d.ts.map +1 -1
- package/dist/sdk/agent-core/index.js +0 -9
- package/dist/sdk/ai/index.d.ts +1 -0
- package/dist/sdk/ai/index.d.ts.map +1 -1
- package/dist/sdk/ai/index.js +1 -0
- package/dist/sdk/ai/utils/transient-error.d.ts +28 -0
- package/dist/sdk/ai/utils/transient-error.d.ts.map +1 -0
- package/dist/sdk/ai/utils/transient-error.js +55 -0
- package/dist/sdk/coding-agent/core/agent-session.d.ts.map +1 -1
- package/dist/sdk/coding-agent/core/agent-session.js +7 -3
- package/dist/sdk/coding-agent/core/extensions/index.d.ts +1 -1
- package/dist/sdk/coding-agent/core/extensions/index.d.ts.map +1 -1
- package/dist/sdk/coding-agent/core/extensions/native-extensions.js +2 -2
- package/dist/sdk/coding-agent/core/extensions/types.d.ts +20 -1
- package/dist/sdk/coding-agent/core/extensions/types.d.ts.map +1 -1
- package/dist/sdk/coding-agent/core/tools/tool-definition-wrapper.d.ts +9 -4
- package/dist/sdk/coding-agent/core/tools/tool-definition-wrapper.d.ts.map +1 -1
- package/dist/sdk/coding-agent/core/tools/tool-definition-wrapper.js +15 -2
- package/dist/sdk/coding-agent/index.d.ts +1 -1
- package/dist/sdk/coding-agent/index.d.ts.map +1 -1
- package/dist/server/agent-bridge/headless-ui.d.ts +20 -0
- package/dist/server/agent-bridge/headless-ui.d.ts.map +1 -0
- package/dist/server/agent-bridge/headless-ui.js +85 -0
- package/dist/server/agent-bridge/pricing.d.ts +54 -0
- package/dist/server/agent-bridge/pricing.d.ts.map +1 -0
- package/dist/server/agent-bridge/pricing.js +226 -0
- package/dist/server/agent-bridge/rehydrate.d.ts +35 -0
- package/dist/server/agent-bridge/rehydrate.d.ts.map +1 -0
- package/dist/server/agent-bridge/rehydrate.js +122 -0
- package/dist/server/agent-bridge/synthetic-model.d.ts +27 -0
- package/dist/server/agent-bridge/synthetic-model.d.ts.map +1 -0
- package/dist/server/agent-bridge/synthetic-model.js +92 -0
- package/dist/server/agent-bridge.d.ts +17 -1
- package/dist/server/agent-bridge.d.ts.map +1 -1
- package/dist/server/agent-bridge.js +81 -496
- package/dist/server/error-humanizer.d.ts +11 -3
- package/dist/server/error-humanizer.d.ts.map +1 -1
- package/dist/server/error-humanizer.js +11 -3
- package/dist/server/session-stream/compaction-events.d.ts +13 -0
- package/dist/server/session-stream/compaction-events.d.ts.map +1 -0
- package/dist/server/session-stream/compaction-events.js +32 -0
- package/dist/server/session-stream/history-prune.d.ts +35 -0
- package/dist/server/session-stream/history-prune.d.ts.map +1 -0
- package/dist/server/session-stream/history-prune.js +182 -0
- package/dist/server/session-stream/memory.d.ts +48 -0
- package/dist/server/session-stream/memory.d.ts.map +1 -0
- package/dist/server/session-stream/memory.js +136 -0
- package/dist/server/session-stream/replay-tail.d.ts +4 -0
- package/dist/server/session-stream/replay-tail.d.ts.map +1 -0
- package/dist/server/session-stream/replay-tail.js +39 -0
- package/dist/server/session-stream/turn-events.d.ts +33 -0
- package/dist/server/session-stream/turn-events.d.ts.map +1 -0
- package/dist/server/session-stream/turn-events.js +65 -0
- package/dist/server/session-stream/turn-silence.d.ts +8 -0
- package/dist/server/session-stream/turn-silence.d.ts.map +1 -0
- package/dist/server/session-stream/turn-silence.js +72 -0
- package/dist/server/session-stream.d.ts +137 -36
- package/dist/server/session-stream.d.ts.map +1 -1
- package/dist/server/session-stream.js +18 -509
- package/dist/server/storage/observation-ranking.d.ts +14 -0
- package/dist/server/storage/observation-ranking.d.ts.map +1 -0
- package/dist/server/storage/observation-ranking.js +37 -0
- package/dist/server/storage/preflight.d.ts +20 -0
- package/dist/server/storage/preflight.d.ts.map +1 -0
- package/dist/server/storage/preflight.js +22 -0
- package/dist/server/storage/project-binding.d.ts +5 -0
- package/dist/server/storage/project-binding.d.ts.map +1 -0
- package/dist/server/storage/project-binding.js +32 -0
- package/dist/server/storage/rows.d.ts +104 -0
- package/dist/server/storage/rows.d.ts.map +1 -0
- package/dist/server/storage/rows.js +2 -0
- package/dist/server/storage/schema.d.ts +20 -0
- package/dist/server/storage/schema.d.ts.map +1 -0
- package/dist/server/storage/schema.js +134 -0
- package/dist/server/storage/types.d.ts +66 -0
- package/dist/server/storage/types.d.ts.map +1 -0
- package/dist/server/storage/types.js +1 -0
- package/dist/server/storage.d.ts +7 -94
- package/dist/server/storage.d.ts.map +1 -1
- package/dist/server/storage.js +6 -224
- package/dist/server/wire.d.ts +19 -0
- package/dist/server/wire.d.ts.map +1 -1
- package/package.json +2 -1
- package/dist/sdk/agent-core/harness/agent-harness.d.ts +0 -92
- package/dist/sdk/agent-core/harness/agent-harness.d.ts.map +0 -1
- package/dist/sdk/agent-core/harness/agent-harness.js +0 -899
- package/dist/sdk/agent-core/harness/env/nodejs.d.ts +0 -51
- package/dist/sdk/agent-core/harness/env/nodejs.d.ts.map +0 -1
- package/dist/sdk/agent-core/harness/env/nodejs.js +0 -485
- package/dist/sdk/agent-core/harness/prompt-templates.d.ts +0 -48
- package/dist/sdk/agent-core/harness/prompt-templates.d.ts.map +0 -1
- package/dist/sdk/agent-core/harness/prompt-templates.js +0 -229
- package/dist/sdk/agent-core/harness/session/jsonl-repo.d.ts +0 -26
- package/dist/sdk/agent-core/harness/session/jsonl-repo.d.ts.map +0 -1
- package/dist/sdk/agent-core/harness/session/jsonl-repo.js +0 -100
- package/dist/sdk/agent-core/harness/session/jsonl-storage.d.ts +0 -33
- package/dist/sdk/agent-core/harness/session/jsonl-storage.d.ts.map +0 -1
- package/dist/sdk/agent-core/harness/session/jsonl-storage.js +0 -230
- package/dist/sdk/agent-core/harness/session/memory-repo.d.ts +0 -18
- package/dist/sdk/agent-core/harness/session/memory-repo.d.ts.map +0 -1
- package/dist/sdk/agent-core/harness/session/memory-repo.js +0 -41
- package/dist/sdk/agent-core/harness/session/memory-storage.d.ts +0 -25
- package/dist/sdk/agent-core/harness/session/memory-storage.d.ts.map +0 -1
- package/dist/sdk/agent-core/harness/session/memory-storage.js +0 -113
- package/dist/sdk/agent-core/harness/session/repo-utils.d.ts +0 -11
- package/dist/sdk/agent-core/harness/session/repo-utils.d.ts.map +0 -1
- package/dist/sdk/agent-core/harness/session/repo-utils.js +0 -38
- package/dist/sdk/agent-core/harness/skills.d.ts +0 -44
- package/dist/sdk/agent-core/harness/skills.d.ts.map +0 -1
- package/dist/sdk/agent-core/harness/skills.js +0 -310
- package/dist/sdk/agent-core/harness/utils/shell-output.d.ts +0 -14
- package/dist/sdk/agent-core/harness/utils/shell-output.d.ts.map +0 -1
- package/dist/sdk/agent-core/harness/utils/shell-output.js +0 -125
- package/dist/sdk/agent-core/node.d.ts +0 -3
- package/dist/sdk/agent-core/node.d.ts.map +0 -1
- package/dist/sdk/agent-core/node.js +0 -2
- package/dist/sdk/agent-core/proxy.d.ts +0 -69
- package/dist/sdk/agent-core/proxy.d.ts.map +0 -1
- package/dist/sdk/agent-core/proxy.js +0 -297
|
@@ -12,9 +12,17 @@
|
|
|
12
12
|
* actually happened instead of only a generic fallback.
|
|
13
13
|
*/
|
|
14
14
|
/**
|
|
15
|
-
* Classification
|
|
16
|
-
*
|
|
17
|
-
*
|
|
15
|
+
* Classification table for user-facing message mapping only. Retry
|
|
16
|
+
* decisions live in the shared transient-error classifier
|
|
17
|
+
* (src/sdk/ai/utils/transient-error.ts → isTransientApiError).
|
|
18
|
+
*
|
|
19
|
+
* Documented divergence: this table is intentionally NOT unified with
|
|
20
|
+
* isTransientApiError, which is wider (HTTP 5xx, "temporarily
|
|
21
|
+
* unavailable", internal/server errors, stream endings). Mapping those
|
|
22
|
+
* signals here would change user-facing strings — e.g. a bare
|
|
23
|
+
* "500 Internal Server Error" would surface as "Network error. Please try
|
|
24
|
+
* again." instead of the sanitized underlying cause. Keep this table in
|
|
25
|
+
* sync manually when new transient signals are added for retries.
|
|
18
26
|
*/
|
|
19
27
|
export declare const PROVIDER_ERROR_PATTERNS: RegExp;
|
|
20
28
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"error-humanizer.d.ts","sourceRoot":"","sources":["../../src/server/error-humanizer.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAIH
|
|
1
|
+
{"version":3,"file":"error-humanizer.d.ts","sourceRoot":"","sources":["../../src/server/error-humanizer.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAIH;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,uBAAuB,QACsN,CAAC;AAqC3P;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,qBAAqB,CAAC,GAAG,EAAE,OAAO,GAAG,MAAM,CAkB1D"}
|
|
@@ -13,9 +13,17 @@
|
|
|
13
13
|
*/
|
|
14
14
|
import { formatPaymentRequiredMessage, isPaymentRequiredError } from "../sdk/ai/utils/rate-limit-retry.js";
|
|
15
15
|
/**
|
|
16
|
-
* Classification
|
|
17
|
-
*
|
|
18
|
-
*
|
|
16
|
+
* Classification table for user-facing message mapping only. Retry
|
|
17
|
+
* decisions live in the shared transient-error classifier
|
|
18
|
+
* (src/sdk/ai/utils/transient-error.ts → isTransientApiError).
|
|
19
|
+
*
|
|
20
|
+
* Documented divergence: this table is intentionally NOT unified with
|
|
21
|
+
* isTransientApiError, which is wider (HTTP 5xx, "temporarily
|
|
22
|
+
* unavailable", internal/server errors, stream endings). Mapping those
|
|
23
|
+
* signals here would change user-facing strings — e.g. a bare
|
|
24
|
+
* "500 Internal Server Error" would surface as "Network error. Please try
|
|
25
|
+
* again." instead of the sanitized underlying cause. Keep this table in
|
|
26
|
+
* sync manually when new transient signals are added for retries.
|
|
19
27
|
*/
|
|
20
28
|
export const PROVIDER_ERROR_PATTERNS = /rate.?limit|429|overloaded|529|timeout|network|econnreset|fetch failed|socket connection was closed|connection.?closed|connection.?reset|socket hang up|other side closed|reset before headers|upstream.?connect|http2 request did not get a response/i;
|
|
21
29
|
const RATE_LIMIT_MESSAGE = "Rate limit reached. Please try again in a moment.";
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pure helpers for compaction_end events and CompactionResult handling.
|
|
3
|
+
* Bodies moved verbatim from `session-stream.ts`.
|
|
4
|
+
*/
|
|
5
|
+
import type { CompactionResult } from "../../sdk/coding-agent/index.js";
|
|
6
|
+
import type { ServerEvent } from "../wire.js";
|
|
7
|
+
export declare function formatCompactionTokenStats(event: Extract<ServerEvent, {
|
|
8
|
+
type: "compaction_end";
|
|
9
|
+
}>): string;
|
|
10
|
+
export declare function isSkippedCompactionResult(result: CompactionResult | void): result is CompactionResult & {
|
|
11
|
+
skipped: true;
|
|
12
|
+
};
|
|
13
|
+
//# sourceMappingURL=compaction-events.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"compaction-events.d.ts","sourceRoot":"","sources":["../../../src/server/session-stream/compaction-events.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,iCAAiC,CAAC;AACxE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAE9C,wBAAgB,0BAA0B,CACxC,KAAK,EAAE,OAAO,CAAC,WAAW,EAAE;IAAE,IAAI,EAAE,gBAAgB,CAAA;CAAE,CAAC,GACtD,MAAM,CAiCR;AAED,wBAAgB,yBAAyB,CACvC,MAAM,EAAE,gBAAgB,GAAG,IAAI,GAC9B,MAAM,IAAI,gBAAgB,GAAG;IAAE,OAAO,EAAE,IAAI,CAAA;CAAE,CAIhD"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
export function formatCompactionTokenStats(event) {
|
|
2
|
+
const parts = [];
|
|
3
|
+
if (typeof event.compactionNumber === "number") {
|
|
4
|
+
parts.push(`compaction #${event.compactionNumber.toLocaleString()}`);
|
|
5
|
+
}
|
|
6
|
+
if (typeof event.tokensBefore === "number") {
|
|
7
|
+
parts.push(`from ~${event.tokensBefore.toLocaleString()} tokens`);
|
|
8
|
+
}
|
|
9
|
+
if (typeof event.tokensCompacted === "number") {
|
|
10
|
+
parts.push(`compacted ~${event.tokensCompacted.toLocaleString()} tokens`);
|
|
11
|
+
}
|
|
12
|
+
if (typeof event.tokensRemoved === "number") {
|
|
13
|
+
parts.push(`removed ~${event.tokensRemoved.toLocaleString()} tokens`);
|
|
14
|
+
}
|
|
15
|
+
if (typeof event.summaryTokens === "number") {
|
|
16
|
+
parts.push(`summary +~${event.summaryTokens.toLocaleString()} tokens`);
|
|
17
|
+
}
|
|
18
|
+
if (typeof event.reductionPercent === "number") {
|
|
19
|
+
parts.push(`reduction ~${event.reductionPercent.toLocaleString(undefined, { maximumFractionDigits: 2 })}%`);
|
|
20
|
+
}
|
|
21
|
+
if (event.largestTokenSources?.length) {
|
|
22
|
+
const sources = event.largestTokenSources
|
|
23
|
+
.slice(0, 3)
|
|
24
|
+
.map((bucket) => `${bucket.type}:${bucket.name} ~${bucket.tokens.toLocaleString()}`)
|
|
25
|
+
.join("; ");
|
|
26
|
+
parts.push(`largest sources ${sources}`);
|
|
27
|
+
}
|
|
28
|
+
return parts.length > 0 ? ` (${parts.join(", ")})` : "";
|
|
29
|
+
}
|
|
30
|
+
export function isSkippedCompactionResult(result) {
|
|
31
|
+
return Boolean(result && typeof result === "object" && result.skipped === true);
|
|
32
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Persisted-history pruning after compaction: derive the kept branch
|
|
3
|
+
* message signatures, match them against SQLite history, and delete the
|
|
4
|
+
* pruned remainder after a successful compaction_end. Bodies moved
|
|
5
|
+
* verbatim from `session-stream.ts`.
|
|
6
|
+
*/
|
|
7
|
+
import type { BridgeLike } from "../session-stream.js";
|
|
8
|
+
import type { SessionStore } from "../storage.js";
|
|
9
|
+
import type { ServerEvent, WireMessage } from "../wire.js";
|
|
10
|
+
export type CompactionEndPruneSkipReason = "aborted" | "skipped" | "failed" | "missing_token_reduction";
|
|
11
|
+
export declare function getCompactionEndPruneSkipReason(event: Extract<ServerEvent, {
|
|
12
|
+
type: "compaction_end";
|
|
13
|
+
}>): CompactionEndPruneSkipReason | null;
|
|
14
|
+
export type BranchEntryLike = NonNullable<ReturnType<NonNullable<BridgeLike["getSessionBranch"]>>>[number];
|
|
15
|
+
export type PersistedConversationRole = "user" | "assistant";
|
|
16
|
+
export interface KeptBranchMessageSignature {
|
|
17
|
+
role: PersistedConversationRole;
|
|
18
|
+
content: string;
|
|
19
|
+
timestampMs: number | null;
|
|
20
|
+
entryId: string;
|
|
21
|
+
}
|
|
22
|
+
export interface ExistingConversationMessage {
|
|
23
|
+
message: WireMessage;
|
|
24
|
+
conversationIndex: number;
|
|
25
|
+
}
|
|
26
|
+
export declare function isRecord(value: unknown): value is Record<string, unknown>;
|
|
27
|
+
export declare function contentAsPersistedText(content: unknown): string | null;
|
|
28
|
+
export declare function branchMessageTimestampMs(entry: BranchEntryLike, message: Record<string, unknown>): number | null;
|
|
29
|
+
export declare function branchMessageSignature(entry: BranchEntryLike): KeptBranchMessageSignature | null;
|
|
30
|
+
export declare function branchMessageRole(entry: BranchEntryLike): unknown;
|
|
31
|
+
export declare function getKeptBranchMessageSignatures(entries: BranchEntryLike[], keptEntryIndex: number): KeptBranchMessageSignature[] | null;
|
|
32
|
+
export declare function selectUniqueCandidate(candidates: ExistingConversationMessage[], kept: KeptBranchMessageSignature): ExistingConversationMessage | null;
|
|
33
|
+
export declare function matchKeptPersistedMessageIds(existing: WireMessage[], keptBranchMessages: KeptBranchMessageSignature[]): Set<string> | null;
|
|
34
|
+
export declare function prunePersistedHistoryAfterCompaction(store: SessionStore, sessionId: string, bridge: BridgeLike): void;
|
|
35
|
+
//# sourceMappingURL=history-prune.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"history-prune.d.ts","sourceRoot":"","sources":["../../../src/server/session-stream/history-prune.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAC;AACvD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAClD,OAAO,KAAK,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAE3D,MAAM,MAAM,4BAA4B,GACtC,SAAS,GAAG,SAAS,GAAG,QAAQ,GAAG,yBAAyB,CAAC;AAM/D,wBAAgB,+BAA+B,CAC7C,KAAK,EAAE,OAAO,CAAC,WAAW,EAAE;IAAE,IAAI,EAAE,gBAAgB,CAAA;CAAE,CAAC,GACtD,4BAA4B,GAAG,IAAI,CAOrC;AAED,MAAM,MAAM,eAAe,GAAG,WAAW,CACvC,UAAU,CAAC,WAAW,CAAC,UAAU,CAAC,kBAAkB,CAAC,CAAC,CAAC,CACxD,CAAC,MAAM,CAAC,CAAC;AACV,MAAM,MAAM,yBAAyB,GAAG,MAAM,GAAG,WAAW,CAAC;AAE7D,MAAM,WAAW,0BAA0B;IACzC,IAAI,EAAE,yBAAyB,CAAC;IAChC,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,2BAA2B;IAC1C,OAAO,EAAE,WAAW,CAAC;IACrB,iBAAiB,EAAE,MAAM,CAAC;CAC3B;AAID,wBAAgB,QAAQ,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAEzE;AAED,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,OAAO,GAAG,MAAM,GAAG,IAAI,CAmBtE;AAED,wBAAgB,wBAAwB,CACtC,KAAK,EAAE,eAAe,EACtB,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC/B,MAAM,GAAG,IAAI,CASf;AAED,wBAAgB,sBAAsB,CACpC,KAAK,EAAE,eAAe,GACrB,0BAA0B,GAAG,IAAI,CAYnC;AAED,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,eAAe,GAAG,OAAO,CAIjE;AAED,wBAAgB,8BAA8B,CAC5C,OAAO,EAAE,eAAe,EAAE,EAC1B,cAAc,EAAE,MAAM,GACrB,0BAA0B,EAAE,GAAG,IAAI,CAkBrC;AAED,wBAAgB,qBAAqB,CACnC,UAAU,EAAE,2BAA2B,EAAE,EACzC,IAAI,EAAE,0BAA0B,GAC/B,2BAA2B,GAAG,IAAI,CAUpC;AAED,wBAAgB,4BAA4B,CAC1C,QAAQ,EAAE,WAAW,EAAE,EACvB,kBAAkB,EAAE,0BAA0B,EAAE,GAC/C,GAAG,CAAC,MAAM,CAAC,GAAG,IAAI,CA+BpB;AAED,wBAAgB,oCAAoC,CAClD,KAAK,EAAE,YAAY,EACnB,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,UAAU,GACjB,IAAI,CA4FN"}
|
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
// Persisted-history prune is deliberately stricter than timeline status
|
|
2
|
+
// rendering: only a successful compaction_end proves Pi appended a usable
|
|
3
|
+
// compaction entry. Aborted, skipped, or failed endings may leave the branch
|
|
4
|
+
// unchanged or partially settled, so pruning must skip instead of guessing.
|
|
5
|
+
export function getCompactionEndPruneSkipReason(event) {
|
|
6
|
+
if (event.aborted)
|
|
7
|
+
return "aborted";
|
|
8
|
+
if (event.skipped)
|
|
9
|
+
return "skipped";
|
|
10
|
+
if (event.errorMessage)
|
|
11
|
+
return "failed";
|
|
12
|
+
if (typeof event.tokensRemoved !== "number" || event.tokensRemoved <= 0)
|
|
13
|
+
return "missing_token_reduction";
|
|
14
|
+
return null;
|
|
15
|
+
}
|
|
16
|
+
const BRANCH_MESSAGE_TIMESTAMP_TOLERANCE_MS = 5_000;
|
|
17
|
+
export function isRecord(value) {
|
|
18
|
+
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
19
|
+
}
|
|
20
|
+
export function contentAsPersistedText(content) {
|
|
21
|
+
if (typeof content === "string")
|
|
22
|
+
return content;
|
|
23
|
+
if (!Array.isArray(content))
|
|
24
|
+
return null;
|
|
25
|
+
const textParts = [];
|
|
26
|
+
for (const part of content) {
|
|
27
|
+
if (typeof part === "string") {
|
|
28
|
+
textParts.push(part);
|
|
29
|
+
continue;
|
|
30
|
+
}
|
|
31
|
+
if (isRecord(part) &&
|
|
32
|
+
part.type === "text" &&
|
|
33
|
+
typeof part.text === "string") {
|
|
34
|
+
textParts.push(part.text);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
return textParts.length > 0 ? textParts.join("") : null;
|
|
38
|
+
}
|
|
39
|
+
export function branchMessageTimestampMs(entry, message) {
|
|
40
|
+
if (typeof message.timestamp === "number" &&
|
|
41
|
+
Number.isFinite(message.timestamp))
|
|
42
|
+
return message.timestamp;
|
|
43
|
+
if (typeof entry.timestamp !== "string")
|
|
44
|
+
return null;
|
|
45
|
+
const parsed = Date.parse(entry.timestamp);
|
|
46
|
+
return Number.isFinite(parsed) ? parsed : null;
|
|
47
|
+
}
|
|
48
|
+
export function branchMessageSignature(entry) {
|
|
49
|
+
if (entry.type !== "message" || !isRecord(entry.message))
|
|
50
|
+
return null;
|
|
51
|
+
const role = entry.message.role;
|
|
52
|
+
if (role !== "user" && role !== "assistant")
|
|
53
|
+
return null;
|
|
54
|
+
const content = contentAsPersistedText(entry.message.content);
|
|
55
|
+
if (content === null)
|
|
56
|
+
return null;
|
|
57
|
+
return {
|
|
58
|
+
role,
|
|
59
|
+
content,
|
|
60
|
+
timestampMs: branchMessageTimestampMs(entry, entry.message),
|
|
61
|
+
entryId: entry.id,
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
export function branchMessageRole(entry) {
|
|
65
|
+
return entry.type === "message" && isRecord(entry.message)
|
|
66
|
+
? entry.message.role
|
|
67
|
+
: undefined;
|
|
68
|
+
}
|
|
69
|
+
export function getKeptBranchMessageSignatures(entries, keptEntryIndex) {
|
|
70
|
+
const signatures = [];
|
|
71
|
+
for (let i = keptEntryIndex; i < entries.length; i++) {
|
|
72
|
+
const entry = entries[i];
|
|
73
|
+
if (entry.type !== "message")
|
|
74
|
+
continue;
|
|
75
|
+
const role = branchMessageRole(entry);
|
|
76
|
+
if (role !== "user" && role !== "assistant")
|
|
77
|
+
continue;
|
|
78
|
+
const signature = branchMessageSignature(entry);
|
|
79
|
+
if (!signature) {
|
|
80
|
+
console.log(`[spectral] prune: skipped – reason=unsafe_kept_message, ` +
|
|
81
|
+
`kept branch message ${entry.id} cannot be matched safely`);
|
|
82
|
+
return null;
|
|
83
|
+
}
|
|
84
|
+
signatures.push(signature);
|
|
85
|
+
}
|
|
86
|
+
return signatures;
|
|
87
|
+
}
|
|
88
|
+
export function selectUniqueCandidate(candidates, kept) {
|
|
89
|
+
if (candidates.length === 1)
|
|
90
|
+
return candidates[0];
|
|
91
|
+
if (kept.timestampMs === null)
|
|
92
|
+
return null;
|
|
93
|
+
const keptTimestampMs = kept.timestampMs;
|
|
94
|
+
const timestampMatches = candidates.filter((candidate) => Math.abs(candidate.message.createdAt - keptTimestampMs) <=
|
|
95
|
+
BRANCH_MESSAGE_TIMESTAMP_TOLERANCE_MS);
|
|
96
|
+
return timestampMatches.length === 1 ? timestampMatches[0] : null;
|
|
97
|
+
}
|
|
98
|
+
export function matchKeptPersistedMessageIds(existing, keptBranchMessages) {
|
|
99
|
+
const existingConversationMessages = existing
|
|
100
|
+
.filter((message) => message.role === "user" || message.role === "assistant")
|
|
101
|
+
.map((message, conversationIndex) => ({ message, conversationIndex }));
|
|
102
|
+
const matchedIds = new Set();
|
|
103
|
+
let searchStart = 0;
|
|
104
|
+
for (const kept of keptBranchMessages) {
|
|
105
|
+
const candidates = existingConversationMessages.filter((candidate) => candidate.conversationIndex >= searchStart &&
|
|
106
|
+
!matchedIds.has(candidate.message.id) &&
|
|
107
|
+
candidate.message.role === kept.role &&
|
|
108
|
+
candidate.message.content === kept.content);
|
|
109
|
+
const selected = selectUniqueCandidate(candidates, kept);
|
|
110
|
+
if (!selected) {
|
|
111
|
+
console.log(`[spectral] prune: skipped – reason=ambiguous_kept_mapping, ` +
|
|
112
|
+
`could not stably match kept branch entry ${kept.entryId} ` +
|
|
113
|
+
`(${kept.role}, ${kept.content.length.toLocaleString()} chars) to SQLite history`);
|
|
114
|
+
return null;
|
|
115
|
+
}
|
|
116
|
+
matchedIds.add(selected.message.id);
|
|
117
|
+
searchStart = selected.conversationIndex + 1;
|
|
118
|
+
}
|
|
119
|
+
return matchedIds;
|
|
120
|
+
}
|
|
121
|
+
export function prunePersistedHistoryAfterCompaction(store, sessionId, bridge) {
|
|
122
|
+
if (!bridge.getSessionBranch)
|
|
123
|
+
return;
|
|
124
|
+
try {
|
|
125
|
+
const entries = bridge.getSessionBranch();
|
|
126
|
+
// Pi appends a CompactionEntry (type "compaction") at the end whose
|
|
127
|
+
// firstKeptEntryId marks the first branch entry that survives in LLM context.
|
|
128
|
+
// We scan backwards so we find the most recent compaction (there should be
|
|
129
|
+
// exactly one per compaction pass).
|
|
130
|
+
const lastCompaction = [...entries]
|
|
131
|
+
.reverse()
|
|
132
|
+
.find((entry) => entry.type === "compaction");
|
|
133
|
+
const firstKeptEntryId = lastCompaction?.firstKeptEntryId;
|
|
134
|
+
if (!firstKeptEntryId) {
|
|
135
|
+
console.log(`[spectral] prune: skipped – reason=missing_compaction_entry, ` +
|
|
136
|
+
`no compaction entry (or no firstKeptEntryId) ` +
|
|
137
|
+
`in branch for ${sessionId} (${entries.length} entries)`);
|
|
138
|
+
return;
|
|
139
|
+
}
|
|
140
|
+
const keptEntryIndex = entries.findIndex((entry) => entry.id === firstKeptEntryId);
|
|
141
|
+
if (keptEntryIndex === -1) {
|
|
142
|
+
console.log(`[spectral] prune: skipped – reason=missing_kept_entry, ` +
|
|
143
|
+
`firstKeptEntryId "${firstKeptEntryId}" ` +
|
|
144
|
+
`not found in ${entries.length} branch entries for ${sessionId}`);
|
|
145
|
+
return;
|
|
146
|
+
}
|
|
147
|
+
const keptBranchMessages = getKeptBranchMessageSignatures(entries, keptEntryIndex);
|
|
148
|
+
if (!keptBranchMessages)
|
|
149
|
+
return;
|
|
150
|
+
const existing = store.getMessages(sessionId);
|
|
151
|
+
if (existing.length === 0)
|
|
152
|
+
return;
|
|
153
|
+
if (keptBranchMessages.length === 0) {
|
|
154
|
+
console.log(`[spectral] prune: skipped – reason=no_kept_conversation_messages, ` +
|
|
155
|
+
`no kept persisted user/assistant messages for ${sessionId}`);
|
|
156
|
+
return;
|
|
157
|
+
}
|
|
158
|
+
console.log(`[spectral] prune: ${sessionId} – source=compaction_end, branch has ${entries.length} entries, ` +
|
|
159
|
+
`kept portion starts at index ${keptEntryIndex} (id=${firstKeptEntryId}), ` +
|
|
160
|
+
`${keptBranchMessages.length} user+assistant entries kept, ` +
|
|
161
|
+
`${existing.length} SQLite messages`);
|
|
162
|
+
const matchedKeptIds = matchKeptPersistedMessageIds(existing, keptBranchMessages);
|
|
163
|
+
if (!matchedKeptIds)
|
|
164
|
+
return;
|
|
165
|
+
const deleteIds = existing
|
|
166
|
+
.filter((message) => (message.role === "user" || message.role === "assistant") &&
|
|
167
|
+
!matchedKeptIds.has(message.id))
|
|
168
|
+
.map((message) => message.id);
|
|
169
|
+
if (deleteIds.length === 0) {
|
|
170
|
+
console.log(`[spectral] prune: skipped – reason=already_pruned, ` +
|
|
171
|
+
`SQLite history already matches compacted branch for ${sessionId}`);
|
|
172
|
+
return;
|
|
173
|
+
}
|
|
174
|
+
const pruned = store.deleteMessagesByIdForSession(sessionId, deleteIds);
|
|
175
|
+
console.log(`[spectral] prune: finished – reason=successful_compaction_end, ` +
|
|
176
|
+
`dropped ${pruned} pre-compaction message(s) ` +
|
|
177
|
+
`for ${sessionId}, ${matchedKeptIds.size} user/assistant message(s) kept`);
|
|
178
|
+
}
|
|
179
|
+
catch (err) {
|
|
180
|
+
console.warn(`[spectral] warn: failed to prune persisted history after compaction for ${sessionId}: ${err instanceof Error ? err.message : String(err)}`);
|
|
181
|
+
}
|
|
182
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { type CompactionPolicy } from "../../sdk/coding-agent/core/compaction/policy.js";
|
|
2
|
+
import { type Config } from "../../memory/config.js";
|
|
3
|
+
import type { BridgeLike, SessionStream } from "../session-stream.js";
|
|
4
|
+
import type { SessionStore } from "../storage.js";
|
|
5
|
+
import type { ServerEvent, WireDcpLiteStatus } from "../wire.js";
|
|
6
|
+
export declare function persistObservationalMemorySnapshot(store: SessionStore, sessionId: string, bridge: BridgeLike): void;
|
|
7
|
+
export declare function loadCompactionPolicy(cwd: string): CompactionPolicy;
|
|
8
|
+
export type SessionDcpLiteThresholds = WireDcpLiteStatus["thresholds"];
|
|
9
|
+
export interface SessionDcpLiteStatus extends Omit<WireDcpLiteStatus, "thresholds"> {
|
|
10
|
+
thresholds: SessionDcpLiteThresholds;
|
|
11
|
+
}
|
|
12
|
+
export interface SessionMemoryStatus {
|
|
13
|
+
mode: "active" | "passive";
|
|
14
|
+
phase: "idle" | "observing" | "compacting" | "reflecting" | "pruning";
|
|
15
|
+
inFlight: {
|
|
16
|
+
observer: boolean;
|
|
17
|
+
compaction: boolean;
|
|
18
|
+
reflection: boolean;
|
|
19
|
+
pruner: boolean;
|
|
20
|
+
};
|
|
21
|
+
reflections: {
|
|
22
|
+
count: number;
|
|
23
|
+
tokens: number;
|
|
24
|
+
};
|
|
25
|
+
observations: {
|
|
26
|
+
committedCount: number;
|
|
27
|
+
committedTokens: number;
|
|
28
|
+
pendingCount: number;
|
|
29
|
+
pendingTokens: number;
|
|
30
|
+
};
|
|
31
|
+
thresholds: {
|
|
32
|
+
observation: number;
|
|
33
|
+
compaction: number;
|
|
34
|
+
reflection: number;
|
|
35
|
+
};
|
|
36
|
+
progress: {
|
|
37
|
+
sinceObservationBoundTokens: number;
|
|
38
|
+
sinceLastCompactionTokens: number;
|
|
39
|
+
observationPoolTokens: number;
|
|
40
|
+
};
|
|
41
|
+
dcpLite?: SessionDcpLiteStatus;
|
|
42
|
+
}
|
|
43
|
+
export declare function buildDcpLiteThresholds(config: Config, policy: CompactionPolicy): SessionDcpLiteThresholds;
|
|
44
|
+
export declare function contextPercent(usedTokens: number | null, maxTokens: number | null): number | null;
|
|
45
|
+
export declare function applyDcpLiteRuntimeStatus(status: SessionMemoryStatus, stream: SessionStream | undefined): void;
|
|
46
|
+
export declare function dcpLiteEventFromServerEvent(event: ServerEvent): WireDcpLiteStatus["lastEvent"] | null;
|
|
47
|
+
export declare function defaultMemoryStatus(cwd: string): SessionMemoryStatus;
|
|
48
|
+
//# sourceMappingURL=memory.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"memory.d.ts","sourceRoot":"","sources":["../../../src/server/session-stream/memory.ts"],"names":[],"mappings":"AAMA,OAAO,EAGL,KAAK,gBAAgB,EACtB,MAAM,kDAAkD,CAAC;AAG1D,OAAO,EAAc,KAAK,MAAM,EAAE,MAAM,wBAAwB,CAAC;AAEjE,OAAO,KAAK,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AACtE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAClD,OAAO,KAAK,EAAE,WAAW,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AAEjE,wBAAgB,kCAAkC,CAChD,KAAK,EAAE,YAAY,EACnB,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,UAAU,GACjB,IAAI,CAmCN;AAED,wBAAgB,oBAAoB,CAAC,GAAG,EAAE,MAAM,GAAG,gBAAgB,CAMlE;AAED,MAAM,MAAM,wBAAwB,GAAG,iBAAiB,CAAC,YAAY,CAAC,CAAC;AAEvE,MAAM,WAAW,oBAAqB,SAAQ,IAAI,CAAC,iBAAiB,EAAE,YAAY,CAAC;IACjF,UAAU,EAAE,wBAAwB,CAAC;CACtC;AAED,MAAM,WAAW,mBAAmB;IAClC,IAAI,EAAE,QAAQ,GAAG,SAAS,CAAC;IAC3B,KAAK,EAAE,MAAM,GAAG,WAAW,GAAG,YAAY,GAAG,YAAY,GAAG,SAAS,CAAC;IACtE,QAAQ,EAAE;QACR,QAAQ,EAAE,OAAO,CAAC;QAClB,UAAU,EAAE,OAAO,CAAC;QACpB,UAAU,EAAE,OAAO,CAAC;QACpB,MAAM,EAAE,OAAO,CAAC;KACjB,CAAC;IACF,WAAW,EAAE;QACX,KAAK,EAAE,MAAM,CAAC;QACd,MAAM,EAAE,MAAM,CAAC;KAChB,CAAC;IACF,YAAY,EAAE;QACZ,cAAc,EAAE,MAAM,CAAC;QACvB,eAAe,EAAE,MAAM,CAAC;QACxB,YAAY,EAAE,MAAM,CAAC;QACrB,aAAa,EAAE,MAAM,CAAC;KACvB,CAAC;IACF,UAAU,EAAE;QACV,WAAW,EAAE,MAAM,CAAC;QACpB,UAAU,EAAE,MAAM,CAAC;QACnB,UAAU,EAAE,MAAM,CAAC;KACpB,CAAC;IACF,QAAQ,EAAE;QACR,2BAA2B,EAAE,MAAM,CAAC;QACpC,yBAAyB,EAAE,MAAM,CAAC;QAClC,qBAAqB,EAAE,MAAM,CAAC;KAC/B,CAAC;IACF,OAAO,CAAC,EAAE,oBAAoB,CAAC;CAChC;AAED,wBAAgB,sBAAsB,CACpC,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,gBAAgB,GACvB,wBAAwB,CAa1B;AAED,wBAAgB,cAAc,CAC5B,UAAU,EAAE,MAAM,GAAG,IAAI,EACzB,SAAS,EAAE,MAAM,GAAG,IAAI,GACvB,MAAM,GAAG,IAAI,CAQf;AAED,wBAAgB,yBAAyB,CACvC,MAAM,EAAE,mBAAmB,EAC3B,MAAM,EAAE,aAAa,GAAG,SAAS,GAChC,IAAI,CAYN;AAED,wBAAgB,2BAA2B,CACzC,KAAK,EAAE,WAAW,GACjB,iBAAiB,CAAC,WAAW,CAAC,GAAG,IAAI,CAoBvC;AAED,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,MAAM,GAAG,mBAAmB,CAiCpE"}
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Observational-memory session status / DCP-lite status computation and
|
|
3
|
+
* snapshot persistence helpers. Bodies moved verbatim from
|
|
4
|
+
* `session-stream.ts`.
|
|
5
|
+
*/
|
|
6
|
+
import { SettingsManager } from "../../sdk/coding-agent/index.js";
|
|
7
|
+
import { CONTEXT_LIMIT_RATIO, DEFAULT_COMPACTION_POLICY, } from "../../sdk/coding-agent/core/compaction/policy.js";
|
|
8
|
+
import { getMemoryState, isSourceEntry } from "../../memory/branch.js";
|
|
9
|
+
import { renderSummary } from "../../memory/compaction.js";
|
|
10
|
+
import { loadConfig } from "../../memory/config.js";
|
|
11
|
+
export function persistObservationalMemorySnapshot(store, sessionId, bridge) {
|
|
12
|
+
if (!bridge.getSessionBranch)
|
|
13
|
+
return;
|
|
14
|
+
try {
|
|
15
|
+
const entries = bridge.getSessionBranch();
|
|
16
|
+
const memoryState = getMemoryState(entries);
|
|
17
|
+
const allObservations = [
|
|
18
|
+
...memoryState.committedObs,
|
|
19
|
+
...memoryState.pendingObs,
|
|
20
|
+
];
|
|
21
|
+
const hasMemory = memoryState.reflections.length > 0 || allObservations.length > 0;
|
|
22
|
+
if (!hasMemory) {
|
|
23
|
+
store.deleteSessionMemorySnapshot(sessionId);
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
const summary = renderSummary(memoryState.reflections, allObservations);
|
|
27
|
+
const details = {
|
|
28
|
+
type: "observational-memory",
|
|
29
|
+
version: 4,
|
|
30
|
+
observations: allObservations,
|
|
31
|
+
reflections: memoryState.reflections,
|
|
32
|
+
};
|
|
33
|
+
store.upsertSessionMemorySnapshot(sessionId, {
|
|
34
|
+
summary,
|
|
35
|
+
details,
|
|
36
|
+
coveredSourceCount: entries.filter((entry) => isSourceEntry(entry))
|
|
37
|
+
.length,
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
catch (err) {
|
|
41
|
+
console.warn(`[spectral] warn: failed to persist observational memory snapshot for ${sessionId}: ${err instanceof Error ? err.message : String(err)}`);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
export function loadCompactionPolicy(cwd) {
|
|
45
|
+
try {
|
|
46
|
+
return SettingsManager.create(cwd).getCompactionPolicy();
|
|
47
|
+
}
|
|
48
|
+
catch {
|
|
49
|
+
return DEFAULT_COMPACTION_POLICY;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
export function buildDcpLiteThresholds(config, policy) {
|
|
53
|
+
return {
|
|
54
|
+
postRunCompactionThresholdTokens: Math.max(config.compactionThresholdTokens, policy.autoOlderHistory.minRawTokens),
|
|
55
|
+
autoContextLimitRatio: CONTEXT_LIMIT_RATIO,
|
|
56
|
+
olderHistoryKeepRecentTokens: policy.agentDriven.olderHistoryKeepRecentTokens,
|
|
57
|
+
olderHistoryMinCompactableTokens: policy.agentDriven.olderHistoryMinCompactableTokens,
|
|
58
|
+
cooldownTurns: policy.agentDriven.cooldownTurns,
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
export function contextPercent(usedTokens, maxTokens) {
|
|
62
|
+
if (typeof usedTokens !== "number" ||
|
|
63
|
+
typeof maxTokens !== "number" ||
|
|
64
|
+
maxTokens <= 0)
|
|
65
|
+
return null;
|
|
66
|
+
return Math.max(0, Math.min(100, (usedTokens / maxTokens) * 100));
|
|
67
|
+
}
|
|
68
|
+
export function applyDcpLiteRuntimeStatus(status, stream) {
|
|
69
|
+
if (!status.dcpLite || !stream)
|
|
70
|
+
return;
|
|
71
|
+
const usage = stream.bridge.getContextUsage?.();
|
|
72
|
+
const usedTokens = usage ? usage.tokens : stream.contextWindowUsed;
|
|
73
|
+
const maxTokens = usage ? usage.contextWindow : stream.contextWindowMax;
|
|
74
|
+
const percent = usage?.percent ?? contextPercent(usedTokens, maxTokens);
|
|
75
|
+
if (usage || usedTokens !== null || maxTokens !== null) {
|
|
76
|
+
status.dcpLite.contextWindow = { usedTokens, maxTokens, percent };
|
|
77
|
+
}
|
|
78
|
+
if (stream.lastDcpEvent) {
|
|
79
|
+
status.dcpLite.lastEvent = stream.lastDcpEvent;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
export function dcpLiteEventFromServerEvent(event) {
|
|
83
|
+
if (event.type !== "agent_notification" || !event.dcpLiteTelemetry)
|
|
84
|
+
return null;
|
|
85
|
+
const telemetry = event.dcpLiteTelemetry;
|
|
86
|
+
return {
|
|
87
|
+
kind: telemetry.event,
|
|
88
|
+
message: event.message,
|
|
89
|
+
timestamp: telemetry.timestamp,
|
|
90
|
+
source: telemetry.source,
|
|
91
|
+
mode: telemetry.mode,
|
|
92
|
+
scope: telemetry.scope,
|
|
93
|
+
reason: telemetry.reason,
|
|
94
|
+
satisfiedSignals: telemetry.satisfiedSignals,
|
|
95
|
+
rawTokens: telemetry.rawTokens,
|
|
96
|
+
contextTokens: telemetry.contextTokens,
|
|
97
|
+
contextPercent: telemetry.contextPercent,
|
|
98
|
+
tokensBefore: telemetry.tokensBefore,
|
|
99
|
+
tokensAfter: telemetry.tokensAfter,
|
|
100
|
+
tokensRemoved: telemetry.tokensRemoved,
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
export function defaultMemoryStatus(cwd) {
|
|
104
|
+
const config = loadConfig(cwd);
|
|
105
|
+
const policy = loadCompactionPolicy(cwd);
|
|
106
|
+
return {
|
|
107
|
+
mode: config.passive ? "passive" : "active",
|
|
108
|
+
phase: "idle",
|
|
109
|
+
inFlight: {
|
|
110
|
+
observer: false,
|
|
111
|
+
compaction: false,
|
|
112
|
+
reflection: false,
|
|
113
|
+
pruner: false,
|
|
114
|
+
},
|
|
115
|
+
reflections: { count: 0, tokens: 0 },
|
|
116
|
+
observations: {
|
|
117
|
+
committedCount: 0,
|
|
118
|
+
committedTokens: 0,
|
|
119
|
+
pendingCount: 0,
|
|
120
|
+
pendingTokens: 0,
|
|
121
|
+
},
|
|
122
|
+
thresholds: {
|
|
123
|
+
observation: config.observationThresholdTokens,
|
|
124
|
+
compaction: config.compactionThresholdTokens,
|
|
125
|
+
reflection: config.reflectionThresholdTokens,
|
|
126
|
+
},
|
|
127
|
+
progress: {
|
|
128
|
+
sinceObservationBoundTokens: 0,
|
|
129
|
+
sinceLastCompactionTokens: 0,
|
|
130
|
+
observationPoolTokens: 0,
|
|
131
|
+
},
|
|
132
|
+
dcpLite: {
|
|
133
|
+
thresholds: buildDcpLiteThresholds(config, policy),
|
|
134
|
+
},
|
|
135
|
+
};
|
|
136
|
+
}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { WireMessage } from "../wire.js";
|
|
2
|
+
export declare function estimateStoredMessageReplayTokens(message: WireMessage): number;
|
|
3
|
+
export declare function selectHistoryForBridge(history: WireMessage[] | undefined, hasMemorySnapshot: boolean): WireMessage[] | undefined;
|
|
4
|
+
//# sourceMappingURL=replay-tail.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"replay-tail.d.ts","sourceRoot":"","sources":["../../../src/server/session-stream/replay-tail.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAe9C,wBAAgB,iCAAiC,CAAC,OAAO,EAAE,WAAW,GAAG,MAAM,CAM9E;AAED,wBAAgB,sBAAsB,CACpC,OAAO,EAAE,WAAW,EAAE,GAAG,SAAS,EAClC,iBAAiB,EAAE,OAAO,GACzB,WAAW,EAAE,GAAG,SAAS,CAc3B"}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Fresh-bridge history replay tail selection used on attach/rehydrate.
|
|
3
|
+
* Bodies moved verbatim from `session-stream.ts`.
|
|
4
|
+
*/
|
|
5
|
+
import { estimateStringTokens } from "../../memory/tokens.js";
|
|
6
|
+
import { estimateImagesTokens } from "../image-attachments.js";
|
|
7
|
+
/**
|
|
8
|
+
* Defensive cap for fresh-bridge history replay when we already have a
|
|
9
|
+
* persisted observational-memory snapshot. spectral's own compaction keeps a
|
|
10
|
+
* recent live tail verbatim and summarizes the older prefix; replaying the
|
|
11
|
+
* entire SQLite transcript after restart defeats that and can explode the
|
|
12
|
+
* next prompt. We therefore rehydrate only an approximate recent tail.
|
|
13
|
+
*
|
|
14
|
+
* Keep this conservative — the tail is rehydrated as raw user/assistant
|
|
15
|
+
* messages PLUS spectral reconstructs tool-result messages from events_jsonl,
|
|
16
|
+
* so the actual context inflation is 3-8× the stored token estimate.
|
|
17
|
+
*/
|
|
18
|
+
const REHYDRATION_TAIL_TOKEN_BUDGET = 20_000;
|
|
19
|
+
export function estimateStoredMessageReplayTokens(message) {
|
|
20
|
+
return (estimateStringTokens(message.content) +
|
|
21
|
+
estimateStringTokens(message.events) +
|
|
22
|
+
estimateImagesTokens(message.images));
|
|
23
|
+
}
|
|
24
|
+
export function selectHistoryForBridge(history, hasMemorySnapshot) {
|
|
25
|
+
if (!history || history.length === 0)
|
|
26
|
+
return history;
|
|
27
|
+
if (!hasMemorySnapshot)
|
|
28
|
+
return history;
|
|
29
|
+
let total = 0;
|
|
30
|
+
let start = history.length - 1;
|
|
31
|
+
for (; start >= 0; start--) {
|
|
32
|
+
const nextTotal = total + estimateStoredMessageReplayTokens(history[start]);
|
|
33
|
+
if (total > 0 && nextTotal > REHYDRATION_TAIL_TOKEN_BUDGET)
|
|
34
|
+
break;
|
|
35
|
+
total = nextTotal;
|
|
36
|
+
}
|
|
37
|
+
const selected = history.slice(Math.max(0, start + 1));
|
|
38
|
+
return selected.length > 0 ? selected : [history[history.length - 1]];
|
|
39
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* In-flight turn buffering primitives: the turn event model
|
|
3
|
+
* (`BufferedTurnEvent`, `InProgressTurn`), replayability gating, turn
|
|
4
|
+
* snapshots for reconnect replay, and persist-time credit extraction from
|
|
5
|
+
* the events JSONL. Bodies moved verbatim from `session-stream.ts`.
|
|
6
|
+
*/
|
|
7
|
+
import type { InProgressTurnSnapshot, ReplayableTurnEvent, ServerEvent } from "../wire.js";
|
|
8
|
+
/**
|
|
9
|
+
* Extract the `creditsUsed` value from a `token_usage` event embedded in an
|
|
10
|
+
* events JSONL string. Returns `null` if no `token_usage` event is present
|
|
11
|
+
* (e.g. it hasn't arrived yet at persist time — the race condition this
|
|
12
|
+
* column exists to bridge). Best-effort: malformed lines are skipped.
|
|
13
|
+
*/
|
|
14
|
+
export declare function extractCreditsFromEventsJsonl(eventsJsonl: string): number | null;
|
|
15
|
+
export type BufferedTurnEvent = Exclude<ReplayableTurnEvent, {
|
|
16
|
+
type: "subagent_progress";
|
|
17
|
+
}>;
|
|
18
|
+
export interface InProgressTurn {
|
|
19
|
+
turnId: string;
|
|
20
|
+
startedAt: number;
|
|
21
|
+
events: BufferedTurnEvent[];
|
|
22
|
+
/** Text accumulated from `text_delta` events in this turn. Used by the
|
|
23
|
+
* auto-titler so we don't have to re-read the persisted message. */
|
|
24
|
+
assistantText: string;
|
|
25
|
+
/** Number of leading events already persisted to SQLite. snapshotTurn uses this
|
|
26
|
+
* to avoid sending already-stored events again on reconnect/refresh. */
|
|
27
|
+
persistedEventCount: number;
|
|
28
|
+
cleanupStarted: boolean;
|
|
29
|
+
}
|
|
30
|
+
export declare function isReplayable(event: ServerEvent): event is BufferedTurnEvent;
|
|
31
|
+
export declare function assistantMessageIdsForTurn(turn: InProgressTurn): Set<string>;
|
|
32
|
+
export declare function snapshotTurn(turn: InProgressTurn, includePersistedEvents?: boolean): InProgressTurnSnapshot;
|
|
33
|
+
//# sourceMappingURL=turn-events.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"turn-events.d.ts","sourceRoot":"","sources":["../../../src/server/session-stream/turn-events.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,KAAK,EACV,sBAAsB,EACtB,mBAAmB,EACnB,WAAW,EACZ,MAAM,YAAY,CAAC;AAEpB;;;;;GAKG;AACH,wBAAgB,6BAA6B,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAkBhF;AAED,MAAM,MAAM,iBAAiB,GAAG,OAAO,CACrC,mBAAmB,EACnB;IAAE,IAAI,EAAE,mBAAmB,CAAA;CAAE,CAC9B,CAAC;AAEF,MAAM,WAAW,cAAc;IAC7B,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,iBAAiB,EAAE,CAAC;IAC5B;yEACqE;IACrE,aAAa,EAAE,MAAM,CAAC;IACtB;4EACwE;IACxE,mBAAmB,EAAE,MAAM,CAAC;IAC5B,cAAc,EAAE,OAAO,CAAC;CACzB;AAED,wBAAgB,YAAY,CAAC,KAAK,EAAE,WAAW,GAAG,KAAK,IAAI,iBAAiB,CAoB3E;AAED,wBAAgB,0BAA0B,CAAC,IAAI,EAAE,cAAc,GAAG,GAAG,CAAC,MAAM,CAAC,CAM5E;AACD,wBAAgB,YAAY,CAC1B,IAAI,EAAE,cAAc,EACpB,sBAAsB,UAAQ,GAC7B,sBAAsB,CAWxB"}
|