pilotswarm-sdk 0.5.20 → 0.5.21
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/api/src/http-api-transport.js +4 -0
- package/api/src/protocol.js +2 -0
- package/dist/blob-store.d.ts +42 -11
- package/dist/blob-store.d.ts.map +1 -1
- package/dist/blob-store.js +163 -53
- package/dist/blob-store.js.map +1 -1
- package/dist/cms-migrations.d.ts.map +1 -1
- package/dist/cms-migrations.js +231 -0
- package/dist/cms-migrations.js.map +1 -1
- package/dist/cms.d.ts +62 -0
- package/dist/cms.d.ts.map +1 -1
- package/dist/cms.js +81 -4
- package/dist/cms.js.map +1 -1
- package/dist/distiller-tools.d.ts +9 -0
- package/dist/distiller-tools.d.ts.map +1 -0
- package/dist/distiller-tools.js +102 -0
- package/dist/distiller-tools.js.map +1 -0
- package/dist/footprint.d.ts +129 -0
- package/dist/footprint.d.ts.map +1 -0
- package/dist/footprint.js +254 -0
- package/dist/footprint.js.map +1 -0
- package/dist/inspect-tools.d.ts.map +1 -1
- package/dist/inspect-tools.js +63 -2
- package/dist/inspect-tools.js.map +1 -1
- package/dist/managed-session.d.ts.map +1 -1
- package/dist/managed-session.js +140 -14
- package/dist/managed-session.js.map +1 -1
- package/dist/management-client.d.ts +32 -0
- package/dist/management-client.d.ts.map +1 -1
- package/dist/management-client.js +93 -0
- package/dist/management-client.js.map +1 -1
- package/dist/orchestration/agents.d.ts +9 -0
- package/dist/orchestration/agents.d.ts.map +1 -1
- package/dist/orchestration/agents.js +15 -0
- package/dist/orchestration/agents.js.map +1 -1
- package/dist/orchestration/index.d.ts +2 -2
- package/dist/orchestration/index.js +1 -1
- package/dist/orchestration/lifecycle.d.ts +1 -0
- package/dist/orchestration/lifecycle.d.ts.map +1 -1
- package/dist/orchestration/lifecycle.js +348 -1
- package/dist/orchestration/lifecycle.js.map +1 -1
- package/dist/orchestration/queue.d.ts.map +1 -1
- package/dist/orchestration/queue.js +29 -1
- package/dist/orchestration/queue.js.map +1 -1
- package/dist/orchestration/runtime.d.ts +1 -1
- package/dist/orchestration/runtime.d.ts.map +1 -1
- package/dist/orchestration/runtime.js +31 -1
- package/dist/orchestration/runtime.js.map +1 -1
- package/dist/orchestration/state.d.ts +27 -0
- package/dist/orchestration/state.d.ts.map +1 -1
- package/dist/orchestration/state.js +10 -0
- package/dist/orchestration/state.js.map +1 -1
- package/dist/orchestration/turn.d.ts.map +1 -1
- package/dist/orchestration/turn.js +42 -0
- package/dist/orchestration/turn.js.map +1 -1
- package/dist/orchestration-registry.d.ts.map +1 -1
- package/dist/orchestration-registry.js +6 -2
- package/dist/orchestration-registry.js.map +1 -1
- package/dist/orchestration-version.d.ts +1 -1
- package/dist/orchestration-version.js +1 -1
- package/dist/orchestration.d.ts +2 -2
- package/dist/orchestration.js +1 -1
- package/dist/orchestration_1_0_46.d.ts +1 -1
- package/dist/orchestration_1_0_47.d.ts +1 -1
- package/dist/orchestration_1_0_48.d.ts +1 -1
- package/dist/orchestration_1_0_49.d.ts +1 -1
- package/dist/orchestration_1_0_50.d.ts +1 -1
- package/dist/orchestration_1_0_66/agents.d.ts +52 -0
- package/dist/orchestration_1_0_66/agents.d.ts.map +1 -0
- package/dist/orchestration_1_0_66/agents.js +825 -0
- package/dist/orchestration_1_0_66/agents.js.map +1 -0
- package/dist/orchestration_1_0_66/index.d.ts +24 -0
- package/dist/orchestration_1_0_66/index.d.ts.map +1 -0
- package/dist/orchestration_1_0_66/index.js +13 -0
- package/dist/orchestration_1_0_66/index.js.map +1 -0
- package/dist/orchestration_1_0_66/lifecycle.d.ts +48 -0
- package/dist/orchestration_1_0_66/lifecycle.d.ts.map +1 -0
- package/dist/orchestration_1_0_66/lifecycle.js +573 -0
- package/dist/orchestration_1_0_66/lifecycle.js.map +1 -0
- package/dist/orchestration_1_0_66/queue.d.ts +7 -0
- package/dist/orchestration_1_0_66/queue.d.ts.map +1 -0
- package/dist/orchestration_1_0_66/queue.js +776 -0
- package/dist/orchestration_1_0_66/queue.js.map +1 -0
- package/dist/orchestration_1_0_66/runtime.d.ts +29 -0
- package/dist/orchestration_1_0_66/runtime.d.ts.map +1 -0
- package/dist/orchestration_1_0_66/runtime.js +198 -0
- package/dist/orchestration_1_0_66/runtime.js.map +1 -0
- package/dist/orchestration_1_0_66/state.d.ts +149 -0
- package/dist/orchestration_1_0_66/state.d.ts.map +1 -0
- package/dist/orchestration_1_0_66/state.js +148 -0
- package/dist/orchestration_1_0_66/state.js.map +1 -0
- package/dist/orchestration_1_0_66/turn.d.ts +34 -0
- package/dist/orchestration_1_0_66/turn.d.ts.map +1 -0
- package/dist/orchestration_1_0_66/turn.js +1169 -0
- package/dist/orchestration_1_0_66/turn.js.map +1 -0
- package/dist/orchestration_1_0_66/utils.d.ts +39 -0
- package/dist/orchestration_1_0_66/utils.d.ts.map +1 -0
- package/dist/orchestration_1_0_66/utils.js +316 -0
- package/dist/orchestration_1_0_66/utils.js.map +1 -0
- package/dist/orchestration_1_0_67/agents.d.ts +52 -0
- package/dist/orchestration_1_0_67/agents.d.ts.map +1 -0
- package/dist/orchestration_1_0_67/agents.js +825 -0
- package/dist/orchestration_1_0_67/agents.js.map +1 -0
- package/dist/orchestration_1_0_67/index.d.ts +28 -0
- package/dist/orchestration_1_0_67/index.d.ts.map +1 -0
- package/dist/orchestration_1_0_67/index.js +13 -0
- package/dist/orchestration_1_0_67/index.js.map +1 -0
- package/dist/orchestration_1_0_67/lifecycle.d.ts +49 -0
- package/dist/orchestration_1_0_67/lifecycle.d.ts.map +1 -0
- package/dist/orchestration_1_0_67/lifecycle.js +807 -0
- package/dist/orchestration_1_0_67/lifecycle.js.map +1 -0
- package/dist/orchestration_1_0_67/queue.d.ts +7 -0
- package/dist/orchestration_1_0_67/queue.d.ts.map +1 -0
- package/dist/orchestration_1_0_67/queue.js +804 -0
- package/dist/orchestration_1_0_67/queue.js.map +1 -0
- package/dist/orchestration_1_0_67/runtime.d.ts +29 -0
- package/dist/orchestration_1_0_67/runtime.d.ts.map +1 -0
- package/dist/orchestration_1_0_67/runtime.js +228 -0
- package/dist/orchestration_1_0_67/runtime.js.map +1 -0
- package/dist/orchestration_1_0_67/state.d.ts +176 -0
- package/dist/orchestration_1_0_67/state.d.ts.map +1 -0
- package/dist/orchestration_1_0_67/state.js +158 -0
- package/dist/orchestration_1_0_67/state.js.map +1 -0
- package/dist/orchestration_1_0_67/turn.d.ts +34 -0
- package/dist/orchestration_1_0_67/turn.d.ts.map +1 -0
- package/dist/orchestration_1_0_67/turn.js +1208 -0
- package/dist/orchestration_1_0_67/turn.js.map +1 -0
- package/dist/orchestration_1_0_67/utils.d.ts +39 -0
- package/dist/orchestration_1_0_67/utils.d.ts.map +1 -0
- package/dist/orchestration_1_0_67/utils.js +316 -0
- package/dist/orchestration_1_0_67/utils.js.map +1 -0
- package/dist/regen-worker.d.ts +167 -0
- package/dist/regen-worker.d.ts.map +1 -0
- package/dist/regen-worker.js +512 -0
- package/dist/regen-worker.js.map +1 -0
- package/dist/resourcemgr-tools.d.ts.map +1 -1
- package/dist/resourcemgr-tools.js +33 -2
- package/dist/resourcemgr-tools.js.map +1 -1
- package/dist/session-lifecycle.d.ts +8 -0
- package/dist/session-lifecycle.d.ts.map +1 -1
- package/dist/session-lifecycle.js +35 -17
- package/dist/session-lifecycle.js.map +1 -1
- package/dist/session-manager.d.ts +21 -0
- package/dist/session-manager.d.ts.map +1 -1
- package/dist/session-manager.js +104 -11
- package/dist/session-manager.js.map +1 -1
- package/dist/session-proxy.d.ts +34 -0
- package/dist/session-proxy.d.ts.map +1 -1
- package/dist/session-proxy.js +407 -5
- package/dist/session-proxy.js.map +1 -1
- package/dist/session-store.d.ts +50 -15
- package/dist/session-store.d.ts.map +1 -1
- package/dist/session-store.js +143 -30
- package/dist/session-store.js.map +1 -1
- package/dist/snapshot-protocol.d.ts +34 -3
- package/dist/snapshot-protocol.d.ts.map +1 -1
- package/dist/snapshot-protocol.js +25 -0
- package/dist/snapshot-protocol.js.map +1 -1
- package/dist/types.d.ts +84 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/dist/web/web-management-client.d.ts +11 -0
- package/dist/web/web-management-client.d.ts.map +1 -1
- package/dist/web/web-management-client.js +8 -0
- package/dist/web/web-management-client.js.map +1 -1
- package/dist/worker.d.ts.map +1 -1
- package/dist/worker.js +7 -0
- package/dist/worker.js.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Distiller Tools — the regen-distiller service session's toolset.
|
|
3
|
+
*
|
|
4
|
+
* One tool: `read_transcript_page`, which pages through the ARCHIVED
|
|
5
|
+
* transcript (`transcript-e<E>-<attemptId>.jsonl`) of the session the
|
|
6
|
+
* distiller serves. The security model is CMS truth, not registration-time
|
|
7
|
+
* gating: the tool is registered on every worker (any pod can host the
|
|
8
|
+
* distiller's turn), but at call time it requires the CALLER's session row
|
|
9
|
+
* to carry `service_kind = "regen-distiller"` and reads only from that
|
|
10
|
+
* row's `service_of` session, only artifacts matching the archive name
|
|
11
|
+
* shape. A spoofed session named "regen-distiller" has no service columns
|
|
12
|
+
* (they are set exclusively by the worker's spawn activity) and gets
|
|
13
|
+
* nothing.
|
|
14
|
+
*
|
|
15
|
+
* @internal
|
|
16
|
+
*/
|
|
17
|
+
import { defineTool } from "@github/copilot-sdk";
|
|
18
|
+
export const REGEN_DISTILLER_SERVICE_KIND = "regen-distiller";
|
|
19
|
+
/** Archive artifact name shape — the ONLY thing the pager will open. */
|
|
20
|
+
const ARCHIVE_NAME_RE = /^transcript-e\d+-[A-Za-z0-9._:-]+\.jsonl$/;
|
|
21
|
+
const DEFAULT_PAGE_SIZE = 40;
|
|
22
|
+
const MAX_PAGE_SIZE = 100;
|
|
23
|
+
/** Per-entry content clip — keeps a page well inside one tool result. */
|
|
24
|
+
const ENTRY_CLIP = 2_000;
|
|
25
|
+
export function createDistillerTools(opts) {
|
|
26
|
+
const { catalog, blobStore } = opts;
|
|
27
|
+
const readTranscriptPage = defineTool("read_transcript_page", {
|
|
28
|
+
description: "Regen-distiller ONLY: read one page of the archived transcript you were spawned to distill. "
|
|
29
|
+
+ "Call with the archive artifact id from your seed prompt and page=1, then keep incrementing "
|
|
30
|
+
+ "page until has_more is false. Other sessions: this tool always refuses.",
|
|
31
|
+
parameters: {
|
|
32
|
+
type: "object",
|
|
33
|
+
properties: {
|
|
34
|
+
artifact: {
|
|
35
|
+
type: "string",
|
|
36
|
+
description: "Archive artifact id (transcript-e<E>-<attemptId>.jsonl) from the seed prompt.",
|
|
37
|
+
},
|
|
38
|
+
page: { type: "number", description: "1-based page number." },
|
|
39
|
+
page_size: { type: "number", description: `Messages per page (default ${DEFAULT_PAGE_SIZE}, max ${MAX_PAGE_SIZE}).` },
|
|
40
|
+
},
|
|
41
|
+
required: ["artifact", "page"],
|
|
42
|
+
},
|
|
43
|
+
handler: async (params, context) => {
|
|
44
|
+
const callerId = context?.durableSessionId;
|
|
45
|
+
if (!callerId)
|
|
46
|
+
return { error: "read_transcript_page: no session context" };
|
|
47
|
+
let caller;
|
|
48
|
+
try {
|
|
49
|
+
caller = await catalog.getSession(callerId);
|
|
50
|
+
}
|
|
51
|
+
catch (err) {
|
|
52
|
+
return { error: `read_transcript_page: ${err?.message || String(err)}` };
|
|
53
|
+
}
|
|
54
|
+
if (!caller || caller.serviceKind !== REGEN_DISTILLER_SERVICE_KIND || !caller.serviceOf) {
|
|
55
|
+
return { error: "read_transcript_page is reserved for the regen-distiller service session." };
|
|
56
|
+
}
|
|
57
|
+
const artifact = String(params.artifact || "").trim();
|
|
58
|
+
if (!ARCHIVE_NAME_RE.test(artifact)) {
|
|
59
|
+
return { error: "read_transcript_page: artifact must be a transcript-e<E>-<attemptId>.jsonl archive id." };
|
|
60
|
+
}
|
|
61
|
+
const pageSize = Math.max(1, Math.min(MAX_PAGE_SIZE, Number(params.page_size) || DEFAULT_PAGE_SIZE));
|
|
62
|
+
const page = Math.max(1, Math.floor(Number(params.page) || 1));
|
|
63
|
+
let body;
|
|
64
|
+
try {
|
|
65
|
+
const download = await blobStore.downloadArtifact(caller.serviceOf, artifact);
|
|
66
|
+
body = download.body;
|
|
67
|
+
}
|
|
68
|
+
catch (err) {
|
|
69
|
+
return { error: `read_transcript_page: archive not readable (${err?.message || String(err)})` };
|
|
70
|
+
}
|
|
71
|
+
const lines = body.toString("utf8").split("\n").filter((l) => l.trim());
|
|
72
|
+
const totalPages = Math.max(1, Math.ceil(lines.length / pageSize));
|
|
73
|
+
const start = (page - 1) * pageSize;
|
|
74
|
+
const entries = lines.slice(start, start + pageSize).map((line) => {
|
|
75
|
+
try {
|
|
76
|
+
const evt = JSON.parse(line);
|
|
77
|
+
const role = evt.eventType === "user.message"
|
|
78
|
+
? "user"
|
|
79
|
+
: evt.eventType === "assistant.message" ? "assistant" : "system";
|
|
80
|
+
const content = String(evt?.data?.content ?? "");
|
|
81
|
+
return {
|
|
82
|
+
seq: evt.seq,
|
|
83
|
+
role,
|
|
84
|
+
content: content.length > ENTRY_CLIP ? content.slice(0, ENTRY_CLIP) + "…" : content,
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
catch {
|
|
88
|
+
return { role: "unparseable", content: line.slice(0, 200) };
|
|
89
|
+
}
|
|
90
|
+
});
|
|
91
|
+
return {
|
|
92
|
+
page,
|
|
93
|
+
total_pages: totalPages,
|
|
94
|
+
total_messages: lines.length,
|
|
95
|
+
has_more: page < totalPages,
|
|
96
|
+
entries,
|
|
97
|
+
};
|
|
98
|
+
},
|
|
99
|
+
});
|
|
100
|
+
return [readTranscriptPage];
|
|
101
|
+
}
|
|
102
|
+
//# sourceMappingURL=distiller-tools.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"distiller-tools.js","sourceRoot":"","sources":["../src/distiller-tools.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Session footprint — the "how degraded is this session" sensor
|
|
3
|
+
* (docs/proposals/session-regen-and-footprint.md §11).
|
|
4
|
+
*
|
|
5
|
+
* Control-plane only by construction: every axis is answered from CMS
|
|
6
|
+
* aggregates, persisted metric summaries, and orchestration runtime stats —
|
|
7
|
+
* computing a footprint never wakes a dehydrated session.
|
|
8
|
+
*
|
|
9
|
+
* The context/compaction counters are DERIVED from persisted SDK transcript
|
|
10
|
+
* events (`session.usage_info`, `session.compaction_start/_complete` — none
|
|
11
|
+
* of which are in the ephemeral filter), using the type-scoped event index.
|
|
12
|
+
* Definitions (per the proposal):
|
|
13
|
+
* - compactionCount completes observed this epoch
|
|
14
|
+
* - compactionGeneration summaries-of-summaries depth. Under infinite
|
|
15
|
+
* sessions the transcript permanently contains a
|
|
16
|
+
* summary after the first compaction, so every
|
|
17
|
+
* subsequent compaction's input includes one:
|
|
18
|
+
* generation = max(0, completes - 1).
|
|
19
|
+
* - failedOrStuck failed completes + starts with no complete
|
|
20
|
+
* - sustained utilization the last SUSTAINED_WINDOW usage readings all
|
|
21
|
+
* above the threshold — never a single reading.
|
|
22
|
+
*/
|
|
23
|
+
import type { SessionCompactionStats, SessionEventStats, SessionMetricSummary } from "./cms.js";
|
|
24
|
+
export declare const FOOTPRINT_UTILIZATION_ELEVATED = 0.7;
|
|
25
|
+
export declare const FOOTPRINT_UTILIZATION_DEGRADED = 0.85;
|
|
26
|
+
export declare const FOOTPRINT_SUSTAINED_WINDOW = 3;
|
|
27
|
+
export declare const FOOTPRINT_GENERATION_DEGRADED = 2;
|
|
28
|
+
export declare const FOOTPRINT_EVENTS_PRUNE_BYTES: number;
|
|
29
|
+
export declare const FOOTPRINT_CACHE_TTL_MS = 15000;
|
|
30
|
+
/** An unmatched compaction start younger than this is RUNNING, not stuck. */
|
|
31
|
+
export declare const FOOTPRINT_STUCK_COMPACTION_MS: number;
|
|
32
|
+
/** Sweep threshold: entries are pruned on write once the cache exceeds this. */
|
|
33
|
+
export declare const FOOTPRINT_CACHE_SWEEP_SIZE = 512;
|
|
34
|
+
export type FootprintLevel = "ok" | "elevated" | "degraded" | "rebuilding";
|
|
35
|
+
export type FootprintRecommendation = "none" | "regenerate" | "prune-events";
|
|
36
|
+
export interface SessionFootprint {
|
|
37
|
+
sessionId: string;
|
|
38
|
+
transcriptEpoch: number;
|
|
39
|
+
regenCount: number;
|
|
40
|
+
epochAgeDays: number | null;
|
|
41
|
+
turnsThisEpoch: number | null;
|
|
42
|
+
context: {
|
|
43
|
+
tokenLimit: number | null;
|
|
44
|
+
currentTokens: number | null;
|
|
45
|
+
utilization: number | null;
|
|
46
|
+
/** True when the last SUSTAINED_WINDOW readings all exceed the degraded threshold. */
|
|
47
|
+
sustainedHighUtilization: boolean;
|
|
48
|
+
compactionCount: number;
|
|
49
|
+
compactionGeneration: number;
|
|
50
|
+
tokensRemovedCumulative: number;
|
|
51
|
+
failedOrStuckCompactions: number;
|
|
52
|
+
};
|
|
53
|
+
transcript: {
|
|
54
|
+
snapshotSizeBytes: number | null;
|
|
55
|
+
rawSizeBytes: number | null;
|
|
56
|
+
};
|
|
57
|
+
events: {
|
|
58
|
+
count: number;
|
|
59
|
+
bytes: number;
|
|
60
|
+
maxSeq: number;
|
|
61
|
+
sinceEpochStart: number;
|
|
62
|
+
};
|
|
63
|
+
facts: {
|
|
64
|
+
count: number;
|
|
65
|
+
bytes: number;
|
|
66
|
+
} | null;
|
|
67
|
+
children: {
|
|
68
|
+
descendantCount: number;
|
|
69
|
+
} | null;
|
|
70
|
+
orchestration: {
|
|
71
|
+
historyEventCount?: number;
|
|
72
|
+
historySizeBytes?: number;
|
|
73
|
+
queuePendingCount?: number;
|
|
74
|
+
orchestrationVersion?: string;
|
|
75
|
+
} | null;
|
|
76
|
+
/** Regen eligibility read model (§10.2). Advisory; the cmd handler is the authority. */
|
|
77
|
+
regenEligibility: {
|
|
78
|
+
eligible: boolean;
|
|
79
|
+
reason?: string;
|
|
80
|
+
};
|
|
81
|
+
assessment: {
|
|
82
|
+
level: FootprintLevel;
|
|
83
|
+
reasons: string[];
|
|
84
|
+
recommendation: FootprintRecommendation;
|
|
85
|
+
};
|
|
86
|
+
computedAt: number;
|
|
87
|
+
}
|
|
88
|
+
/** Structural dependencies — satisfied by ManagementClient's catalog + helpers. */
|
|
89
|
+
export interface FootprintSources {
|
|
90
|
+
getSession(sessionId: string): Promise<{
|
|
91
|
+
createdAt?: number | Date | null;
|
|
92
|
+
currentIteration?: number | null;
|
|
93
|
+
transcriptEpoch?: number | null;
|
|
94
|
+
lastRegeneratedAt?: number | Date | null;
|
|
95
|
+
} | null>;
|
|
96
|
+
getSessionEventStats(sessionId: string, afterSeq?: number): Promise<SessionEventStats>;
|
|
97
|
+
getSessionCompactionStats(sessionId: string, afterSeq?: number): Promise<SessionCompactionStats>;
|
|
98
|
+
/** Reverse-ordered read of typed events (existing getSessionEventsBefore proc). */
|
|
99
|
+
getSessionEventsBefore(sessionId: string, beforeSeq: number, limit?: number, eventTypes?: string[]): Promise<Array<{
|
|
100
|
+
seq: number;
|
|
101
|
+
data?: unknown;
|
|
102
|
+
}>>;
|
|
103
|
+
getSessionMetricSummary(sessionId: string): Promise<SessionMetricSummary | null>;
|
|
104
|
+
getDescendantSessionIds?(sessionId: string): Promise<string[]>;
|
|
105
|
+
getSessionFactsStats?(sessionId: string): Promise<{
|
|
106
|
+
totalCount: number;
|
|
107
|
+
totalBytes: number;
|
|
108
|
+
}>;
|
|
109
|
+
getOrchestrationStats?(sessionId: string): Promise<Record<string, unknown> | null>;
|
|
110
|
+
/**
|
|
111
|
+
* Epoch boundary seq (the session.epoch_committed event) when the session
|
|
112
|
+
* has regenerated. Absent/0 → epoch 0, whole-session axes. Wired in M1.
|
|
113
|
+
*/
|
|
114
|
+
getEpochBoundarySeq?(sessionId: string): Promise<number | null>;
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* Compute a session's footprint. `notFoundOk` is false: callers should have
|
|
118
|
+
* resolved the session first; a missing session throws.
|
|
119
|
+
*/
|
|
120
|
+
export declare function computeSessionFootprint(sources: FootprintSources, sessionId: string): Promise<SessionFootprint>;
|
|
121
|
+
export declare class FootprintCache {
|
|
122
|
+
private readonly ttlMs;
|
|
123
|
+
private readonly entries;
|
|
124
|
+
constructor(ttlMs?: number);
|
|
125
|
+
get(sessionId: string): SessionFootprint | null;
|
|
126
|
+
set(footprint: SessionFootprint): void;
|
|
127
|
+
clear(): void;
|
|
128
|
+
}
|
|
129
|
+
//# sourceMappingURL=footprint.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"footprint.d.ts","sourceRoot":"","sources":["../src/footprint.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,254 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Session footprint — the "how degraded is this session" sensor
|
|
3
|
+
* (docs/proposals/session-regen-and-footprint.md §11).
|
|
4
|
+
*
|
|
5
|
+
* Control-plane only by construction: every axis is answered from CMS
|
|
6
|
+
* aggregates, persisted metric summaries, and orchestration runtime stats —
|
|
7
|
+
* computing a footprint never wakes a dehydrated session.
|
|
8
|
+
*
|
|
9
|
+
* The context/compaction counters are DERIVED from persisted SDK transcript
|
|
10
|
+
* events (`session.usage_info`, `session.compaction_start/_complete` — none
|
|
11
|
+
* of which are in the ephemeral filter), using the type-scoped event index.
|
|
12
|
+
* Definitions (per the proposal):
|
|
13
|
+
* - compactionCount completes observed this epoch
|
|
14
|
+
* - compactionGeneration summaries-of-summaries depth. Under infinite
|
|
15
|
+
* sessions the transcript permanently contains a
|
|
16
|
+
* summary after the first compaction, so every
|
|
17
|
+
* subsequent compaction's input includes one:
|
|
18
|
+
* generation = max(0, completes - 1).
|
|
19
|
+
* - failedOrStuck failed completes + starts with no complete
|
|
20
|
+
* - sustained utilization the last SUSTAINED_WINDOW usage readings all
|
|
21
|
+
* above the threshold — never a single reading.
|
|
22
|
+
*/
|
|
23
|
+
// ── Assessment thresholds (exported for tests) ──────────────────
|
|
24
|
+
export const FOOTPRINT_UTILIZATION_ELEVATED = 0.7;
|
|
25
|
+
export const FOOTPRINT_UTILIZATION_DEGRADED = 0.85;
|
|
26
|
+
export const FOOTPRINT_SUSTAINED_WINDOW = 3;
|
|
27
|
+
export const FOOTPRINT_GENERATION_DEGRADED = 2;
|
|
28
|
+
export const FOOTPRINT_EVENTS_PRUNE_BYTES = 64 * 1024 * 1024;
|
|
29
|
+
export const FOOTPRINT_CACHE_TTL_MS = 15_000;
|
|
30
|
+
/** An unmatched compaction start younger than this is RUNNING, not stuck. */
|
|
31
|
+
export const FOOTPRINT_STUCK_COMPACTION_MS = 10 * 60 * 1000;
|
|
32
|
+
/** Sweep threshold: entries are pruned on write once the cache exceeds this. */
|
|
33
|
+
export const FOOTPRINT_CACHE_SWEEP_SIZE = 512;
|
|
34
|
+
/** Sentinel "after everything" seq for reverse event reads. */
|
|
35
|
+
const MAX_SEQ = Number.MAX_SAFE_INTEGER;
|
|
36
|
+
function toEpochMs(value) {
|
|
37
|
+
if (value == null)
|
|
38
|
+
return null;
|
|
39
|
+
if (value instanceof Date)
|
|
40
|
+
return value.getTime();
|
|
41
|
+
return Number.isFinite(value) ? Number(value) : null;
|
|
42
|
+
}
|
|
43
|
+
function finite(value) {
|
|
44
|
+
return typeof value === "number" && Number.isFinite(value) ? value : null;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Compute a session's footprint. `notFoundOk` is false: callers should have
|
|
48
|
+
* resolved the session first; a missing session throws.
|
|
49
|
+
*/
|
|
50
|
+
export async function computeSessionFootprint(sources, sessionId) {
|
|
51
|
+
const session = await sources.getSession(sessionId);
|
|
52
|
+
if (!session)
|
|
53
|
+
throw new Error(`session not found: ${sessionId}`);
|
|
54
|
+
const transcriptEpoch = finite(session.transcriptEpoch) ?? 0;
|
|
55
|
+
let boundarySeq = 0;
|
|
56
|
+
if (transcriptEpoch > 0) {
|
|
57
|
+
// A regenerated session assessed from WHOLE-SESSION counters would
|
|
58
|
+
// inherit the dead epoch's degradation — refuse loudly rather than
|
|
59
|
+
// silently lie (the caller wires getEpochBoundarySeq; its absence or
|
|
60
|
+
// failure here is a bug, not a fallback).
|
|
61
|
+
if (!sources.getEpochBoundarySeq) {
|
|
62
|
+
throw new Error(`footprint: session ${sessionId} is at epoch ${transcriptEpoch} but no epoch boundary source is wired`);
|
|
63
|
+
}
|
|
64
|
+
const seq = await sources.getEpochBoundarySeq(sessionId);
|
|
65
|
+
if (seq == null || seq <= 0) {
|
|
66
|
+
throw new Error(`footprint: session ${sessionId} is at epoch ${transcriptEpoch} but the epoch boundary seq is unavailable`);
|
|
67
|
+
}
|
|
68
|
+
boundarySeq = seq;
|
|
69
|
+
}
|
|
70
|
+
const afterSeq = boundarySeq > 0 ? boundarySeq : undefined;
|
|
71
|
+
const [eventStatsAll, eventStatsEpoch, compaction, usageEvents, summary] = await Promise.all([
|
|
72
|
+
sources.getSessionEventStats(sessionId),
|
|
73
|
+
afterSeq != null
|
|
74
|
+
? sources.getSessionEventStats(sessionId, afterSeq)
|
|
75
|
+
: Promise.resolve(null),
|
|
76
|
+
sources.getSessionCompactionStats(sessionId, afterSeq),
|
|
77
|
+
sources.getSessionEventsBefore(sessionId, MAX_SEQ, FOOTPRINT_SUSTAINED_WINDOW + 3, [
|
|
78
|
+
"session.usage_info",
|
|
79
|
+
]),
|
|
80
|
+
sources.getSessionMetricSummary(sessionId),
|
|
81
|
+
]);
|
|
82
|
+
// Optional axes degrade independently — a failure in one never sinks the rest.
|
|
83
|
+
const [factsResult, descendantsResult, orchResult] = await Promise.allSettled([
|
|
84
|
+
sources.getSessionFactsStats?.(sessionId) ?? Promise.resolve(null),
|
|
85
|
+
sources.getDescendantSessionIds?.(sessionId) ?? Promise.resolve(null),
|
|
86
|
+
sources.getOrchestrationStats?.(sessionId) ?? Promise.resolve(null),
|
|
87
|
+
]);
|
|
88
|
+
// ── context axis from the most recent usage readings ────────
|
|
89
|
+
const readings = usageEvents
|
|
90
|
+
.slice()
|
|
91
|
+
.sort((a, b) => a.seq - b.seq)
|
|
92
|
+
.map((e) => {
|
|
93
|
+
const data = (e.data ?? {});
|
|
94
|
+
const tokenLimit = finite(data.tokenLimit);
|
|
95
|
+
const currentTokens = finite(data.currentTokens);
|
|
96
|
+
return tokenLimit != null && currentTokens != null && tokenLimit > 0
|
|
97
|
+
? { tokenLimit, currentTokens, utilization: currentTokens / tokenLimit }
|
|
98
|
+
: null;
|
|
99
|
+
})
|
|
100
|
+
.filter((r) => r != null)
|
|
101
|
+
// Collapse consecutive identical readings: usage_info fires more than
|
|
102
|
+
// once per turn, and the sustained window must span distinct states,
|
|
103
|
+
// not one long turn echoing the same number.
|
|
104
|
+
.filter((r, i, all) => i === 0 || r.currentTokens !== all[i - 1].currentTokens);
|
|
105
|
+
const latest = readings.length > 0 ? readings[readings.length - 1] : null;
|
|
106
|
+
const window = readings.slice(-FOOTPRINT_SUSTAINED_WINDOW);
|
|
107
|
+
const sustainedHighUtilization = window.length >= FOOTPRINT_SUSTAINED_WINDOW &&
|
|
108
|
+
window.every((r) => r.utilization > FOOTPRINT_UTILIZATION_DEGRADED);
|
|
109
|
+
// Failed completes inject no summary — they cannot deepen the
|
|
110
|
+
// summaries-of-summaries chain, so depth counts SUCCEEDED compactions only.
|
|
111
|
+
const succeeded = Math.max(0, compaction.completes - compaction.failed);
|
|
112
|
+
const compactionCount = succeeded;
|
|
113
|
+
const compactionGeneration = Math.max(0, succeeded - 1);
|
|
114
|
+
// A single unmatched start is a compaction IN FLIGHT until the stuck
|
|
115
|
+
// timeout passes — without the age gate every live compaction (and,
|
|
116
|
+
// permanently, one crashed worker) would read degraded.
|
|
117
|
+
const unmatchedStarts = Math.max(0, compaction.starts - compaction.completes);
|
|
118
|
+
const newestStartMs = compaction.lastStartAtMs ?? 0;
|
|
119
|
+
const trailingStartIsStuck = unmatchedStarts > 0 &&
|
|
120
|
+
(newestStartMs === 0 || Date.now() - newestStartMs > FOOTPRINT_STUCK_COMPACTION_MS);
|
|
121
|
+
const stuck = trailingStartIsStuck ? unmatchedStarts : Math.max(0, unmatchedStarts - 1);
|
|
122
|
+
const failedOrStuck = compaction.failed + stuck;
|
|
123
|
+
// ── assessment ──────────────────────────────────────────────
|
|
124
|
+
const reasons = [];
|
|
125
|
+
if (compactionGeneration >= FOOTPRINT_GENERATION_DEGRADED) {
|
|
126
|
+
reasons.push(`compactionGeneration >= ${FOOTPRINT_GENERATION_DEGRADED}`);
|
|
127
|
+
}
|
|
128
|
+
if (sustainedHighUtilization) {
|
|
129
|
+
reasons.push(`utilization > ${FOOTPRINT_UTILIZATION_DEGRADED} sustained`);
|
|
130
|
+
}
|
|
131
|
+
if (failedOrStuck >= 1)
|
|
132
|
+
reasons.push("failed or stuck compaction");
|
|
133
|
+
let level;
|
|
134
|
+
if (reasons.length > 0) {
|
|
135
|
+
level = "degraded";
|
|
136
|
+
}
|
|
137
|
+
else if (compactionCount >= 1 ||
|
|
138
|
+
(latest != null && latest.utilization > FOOTPRINT_UTILIZATION_ELEVATED)) {
|
|
139
|
+
level = "elevated";
|
|
140
|
+
if (compactionCount >= 1)
|
|
141
|
+
reasons.push("compaction has occurred");
|
|
142
|
+
if (latest != null && latest.utilization > FOOTPRINT_UTILIZATION_ELEVATED) {
|
|
143
|
+
reasons.push(`utilization > ${FOOTPRINT_UTILIZATION_ELEVATED}`);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
else {
|
|
147
|
+
level = "ok";
|
|
148
|
+
}
|
|
149
|
+
let recommendation = "none";
|
|
150
|
+
if (level === "degraded")
|
|
151
|
+
recommendation = "regenerate";
|
|
152
|
+
else if (eventStatsAll.dataBytes > FOOTPRINT_EVENTS_PRUNE_BYTES)
|
|
153
|
+
recommendation = "prune-events";
|
|
154
|
+
const createdAtMs = toEpochMs(session.createdAt);
|
|
155
|
+
const lastRegenMs = toEpochMs(session.lastRegeneratedAt);
|
|
156
|
+
const epochStartMs = transcriptEpoch > 0 ? lastRegenMs : createdAtMs;
|
|
157
|
+
const epochAgeDays = epochStartMs != null ? (Date.now() - epochStartMs) / (24 * 60 * 60 * 1000) : null;
|
|
158
|
+
const facts = factsResult.status === "fulfilled" && factsResult.value
|
|
159
|
+
? { count: factsResult.value.totalCount, bytes: factsResult.value.totalBytes }
|
|
160
|
+
: null;
|
|
161
|
+
const descendants = descendantsResult.status === "fulfilled" && Array.isArray(descendantsResult.value)
|
|
162
|
+
? { descendantCount: descendantsResult.value.length }
|
|
163
|
+
: null;
|
|
164
|
+
const orchStatsRaw = orchResult.status === "fulfilled" ? orchResult.value : null;
|
|
165
|
+
const orchestration = orchStatsRaw
|
|
166
|
+
? {
|
|
167
|
+
...(finite(orchStatsRaw.historyEventCount) != null
|
|
168
|
+
? { historyEventCount: Number(orchStatsRaw.historyEventCount) }
|
|
169
|
+
: {}),
|
|
170
|
+
...(finite(orchStatsRaw.historySizeBytes) != null
|
|
171
|
+
? { historySizeBytes: Number(orchStatsRaw.historySizeBytes) }
|
|
172
|
+
: {}),
|
|
173
|
+
...(finite(orchStatsRaw.queuePendingCount) != null
|
|
174
|
+
? { queuePendingCount: Number(orchStatsRaw.queuePendingCount) }
|
|
175
|
+
: {}),
|
|
176
|
+
...(typeof orchStatsRaw.orchestrationVersion === "string"
|
|
177
|
+
? { orchestrationVersion: orchStatsRaw.orchestrationVersion }
|
|
178
|
+
: {}),
|
|
179
|
+
}
|
|
180
|
+
: null;
|
|
181
|
+
return {
|
|
182
|
+
sessionId,
|
|
183
|
+
transcriptEpoch,
|
|
184
|
+
regenCount: summary?.regenCount ?? transcriptEpoch,
|
|
185
|
+
epochAgeDays,
|
|
186
|
+
turnsThisEpoch: finite(session.currentIteration),
|
|
187
|
+
context: {
|
|
188
|
+
tokenLimit: latest?.tokenLimit ?? null,
|
|
189
|
+
currentTokens: latest?.currentTokens ?? null,
|
|
190
|
+
utilization: latest != null ? Number(latest.utilization.toFixed(4)) : null,
|
|
191
|
+
sustainedHighUtilization,
|
|
192
|
+
compactionCount,
|
|
193
|
+
compactionGeneration,
|
|
194
|
+
tokensRemovedCumulative: compaction.tokensRemoved,
|
|
195
|
+
failedOrStuckCompactions: failedOrStuck,
|
|
196
|
+
},
|
|
197
|
+
transcript: {
|
|
198
|
+
snapshotSizeBytes: summary?.snapshotSizeBytes ?? null,
|
|
199
|
+
rawSizeBytes: summary?.rawSizeBytes ?? null,
|
|
200
|
+
},
|
|
201
|
+
events: {
|
|
202
|
+
count: eventStatsAll.eventCount,
|
|
203
|
+
bytes: eventStatsAll.dataBytes,
|
|
204
|
+
maxSeq: eventStatsAll.maxSeq,
|
|
205
|
+
sinceEpochStart: (eventStatsEpoch ?? eventStatsAll).eventCount,
|
|
206
|
+
},
|
|
207
|
+
facts,
|
|
208
|
+
children: descendants,
|
|
209
|
+
orchestration,
|
|
210
|
+
// Regeneration ships in M1; until then the read model reports it plainly.
|
|
211
|
+
regenEligibility: { eligible: false, reason: "not_available" },
|
|
212
|
+
assessment: { level, reasons, recommendation },
|
|
213
|
+
computedAt: Date.now(),
|
|
214
|
+
};
|
|
215
|
+
}
|
|
216
|
+
// ── TTL cache ───────────────────────────────────────────────────
|
|
217
|
+
//
|
|
218
|
+
// TTL-only by design: no cross-process invalidation is claimed — checking
|
|
219
|
+
// max seq is itself a query, and every consumer tolerates seconds of
|
|
220
|
+
// staleness. A maintained aggregate row is the upgrade path if this bites.
|
|
221
|
+
export class FootprintCache {
|
|
222
|
+
ttlMs;
|
|
223
|
+
entries = new Map();
|
|
224
|
+
constructor(ttlMs = FOOTPRINT_CACHE_TTL_MS) {
|
|
225
|
+
this.ttlMs = ttlMs;
|
|
226
|
+
}
|
|
227
|
+
get(sessionId) {
|
|
228
|
+
const entry = this.entries.get(sessionId);
|
|
229
|
+
if (!entry)
|
|
230
|
+
return null;
|
|
231
|
+
if (Date.now() - entry.at > this.ttlMs) {
|
|
232
|
+
this.entries.delete(sessionId);
|
|
233
|
+
return null;
|
|
234
|
+
}
|
|
235
|
+
return entry.value;
|
|
236
|
+
}
|
|
237
|
+
set(footprint) {
|
|
238
|
+
if (this.entries.size > FOOTPRINT_CACHE_SWEEP_SIZE) {
|
|
239
|
+
const cutoff = Date.now() - this.ttlMs;
|
|
240
|
+
for (const [key, entry] of this.entries) {
|
|
241
|
+
if (entry.at < cutoff)
|
|
242
|
+
this.entries.delete(key);
|
|
243
|
+
}
|
|
244
|
+
// Pathological churn (fleet-wide pollers): hard reset beats growth.
|
|
245
|
+
if (this.entries.size > FOOTPRINT_CACHE_SWEEP_SIZE * 8)
|
|
246
|
+
this.entries.clear();
|
|
247
|
+
}
|
|
248
|
+
this.entries.set(footprint.sessionId, { at: Date.now(), value: footprint });
|
|
249
|
+
}
|
|
250
|
+
clear() {
|
|
251
|
+
this.entries.clear();
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
//# sourceMappingURL=footprint.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"footprint.js","sourceRoot":"","sources":["../src/footprint.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"inspect-tools.d.ts","sourceRoot":"","sources":["../src/inspect-tools.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAGH,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,qBAAqB,CAAC;AAChD,OAAO,KAAK,EAAE,cAAc,EAAgB,MAAM,UAAU,CAAC;
|
|
1
|
+
{"version":3,"file":"inspect-tools.d.ts","sourceRoot":"","sources":["../src/inspect-tools.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAGH,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,qBAAqB,CAAC;AAChD,OAAO,KAAK,EAAE,cAAc,EAAgB,MAAM,UAAU,CAAC;AA0F7D,MAAM,WAAW,yBAAyB;IACtC,OAAO,EAAE,cAAc,CAAC;IACxB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB;;;;;OAKG;IACH,cAAc,CAAC,EAAE,GAAG,CAAC;IACrB;;;;OAIG;IACH,SAAS,CAAC,EAAE,OAAO,kBAAkB,EAAE,SAAS,CAAC;CACpD;AAED,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,yBAAyB,GAAG,IAAI,CAAC,GAAG,CAAC,EAAE,CAomC/E"}
|
package/dist/inspect-tools.js
CHANGED
|
@@ -20,6 +20,7 @@
|
|
|
20
20
|
* @internal
|
|
21
21
|
*/
|
|
22
22
|
import { defineTool } from "@github/copilot-sdk";
|
|
23
|
+
import { computeSessionFootprint, FootprintCache } from "./footprint.js";
|
|
23
24
|
import { isEnhancedFactStore } from "./facts-store.js";
|
|
24
25
|
import { formatOwnerBucketLabel, formatSessionOwnerLabel, getSessionOwnerKind, matchesOwnerBucketFilters, matchesSessionOwnerFilters } from "./session-owner-utils.js";
|
|
25
26
|
const TUNER_AGENT_ID = "agent-tuner";
|
|
@@ -32,6 +33,8 @@ const SYSTEM_AGENT_IDS = new Set([
|
|
|
32
33
|
]);
|
|
33
34
|
const DEFAULT_LIMIT = 50;
|
|
34
35
|
const MAX_LIMIT = 200;
|
|
36
|
+
/** Shared TTL cache for the context_health tool (per worker process). */
|
|
37
|
+
const contextHealthCache = new FootprintCache();
|
|
35
38
|
const MAX_DATA_BYTES = 4 * 1024;
|
|
36
39
|
const MAX_RESPONSE_BYTES = 64 * 1024;
|
|
37
40
|
function normalizeSessionId(raw) {
|
|
@@ -838,11 +841,68 @@ export function createInspectTools(opts) {
|
|
|
838
841
|
readSessionGraphNodeUsageTool,
|
|
839
842
|
readSessionGraphEdgeSearchUsageTool,
|
|
840
843
|
];
|
|
844
|
+
// context_health is a SELF-scoped footprint sensor: it reads the CALLER's
|
|
845
|
+
// own session (ctx.sessionId) and returns epoch, turns-this-epoch, context
|
|
846
|
+
// utilization, compaction depth, transcript/event sizes and an ok/elevated/
|
|
847
|
+
// degraded assessment. Every agent needs it to detect its own degradation
|
|
848
|
+
// and decide whether to regenerate — so it must ship to ordinary
|
|
849
|
+
// (non-system) sessions, not just the tuner. Defined before the tool-set
|
|
850
|
+
// branches so all return paths can include it.
|
|
851
|
+
const contextHealthTool = defineTool("context_health", {
|
|
852
|
+
description: "Check YOUR OWN context health: transcript epoch, token utilization, compaction depth "
|
|
853
|
+
+ "(summaries-of-summaries), event-log and snapshot sizes, and an overall assessment "
|
|
854
|
+
+ "(ok / elevated / degraded) with a recommendation. This is the regeneration/footprint "
|
|
855
|
+
+ "sensor — call it when asked for regen or context stats, when you have been running a "
|
|
856
|
+
+ "long time, notice yourself losing track of earlier work, or want to decide whether to "
|
|
857
|
+
+ "regenerate your context at a natural boundary. Cheap (control-plane, cached).",
|
|
858
|
+
parameters: { type: "object", properties: {} },
|
|
859
|
+
handler: async (_args, ctx) => {
|
|
860
|
+
const sessionId = ctx?.sessionId;
|
|
861
|
+
if (!sessionId)
|
|
862
|
+
return { error: "context_health: caller session id is required" };
|
|
863
|
+
try {
|
|
864
|
+
const cached = contextHealthCache.get(sessionId);
|
|
865
|
+
const footprint = cached ?? await computeSessionFootprint({
|
|
866
|
+
getSession: (id) => catalog.getSession(id),
|
|
867
|
+
getSessionEventStats: (id, afterSeq) => catalog.getSessionEventStats(id, afterSeq),
|
|
868
|
+
getSessionCompactionStats: (id, afterSeq) => catalog.getSessionCompactionStats(id, afterSeq),
|
|
869
|
+
getSessionEventsBefore: (id, beforeSeq, limit, eventTypes) => catalog.getSessionEventsBefore(id, beforeSeq, limit, eventTypes),
|
|
870
|
+
getSessionMetricSummary: (id) => catalog.getSessionMetricSummary(id),
|
|
871
|
+
getEpochBoundarySeq: async (id) => {
|
|
872
|
+
const rows = await catalog.getSessionEventsBefore(id, Number.MAX_SAFE_INTEGER, 1, ["session.epoch_committed"]);
|
|
873
|
+
return rows.length > 0 ? Number(rows[0].seq) : null;
|
|
874
|
+
},
|
|
875
|
+
}, sessionId);
|
|
876
|
+
if (!cached)
|
|
877
|
+
contextHealthCache.set(footprint);
|
|
878
|
+
const { assessment } = footprint;
|
|
879
|
+
const recommendationText = assessment.level === "degraded"
|
|
880
|
+
? "Consider calling regenerate_context at a natural boundary (finish the current step first)."
|
|
881
|
+
: assessment.level === "elevated"
|
|
882
|
+
? "Context is aging but workable. Re-anchor on facts if you feel unsure about earlier work."
|
|
883
|
+
: "Context is healthy. No action needed.";
|
|
884
|
+
return {
|
|
885
|
+
sessionId: footprint.sessionId,
|
|
886
|
+
transcriptEpoch: footprint.transcriptEpoch,
|
|
887
|
+
epochAgeDays: footprint.epochAgeDays,
|
|
888
|
+
turnsThisEpoch: footprint.turnsThisEpoch,
|
|
889
|
+
context: footprint.context,
|
|
890
|
+
transcript: footprint.transcript,
|
|
891
|
+
events: footprint.events,
|
|
892
|
+
assessment,
|
|
893
|
+
recommendation: recommendationText,
|
|
894
|
+
};
|
|
895
|
+
}
|
|
896
|
+
catch (err) {
|
|
897
|
+
return { error: `context_health: ${err?.message || String(err)}` };
|
|
898
|
+
}
|
|
899
|
+
},
|
|
900
|
+
});
|
|
841
901
|
if (!isSystemAgent) {
|
|
842
|
-
return [readAgentEventsTool, ...lineageRetrievalTools];
|
|
902
|
+
return [readAgentEventsTool, contextHealthTool, ...lineageRetrievalTools];
|
|
843
903
|
}
|
|
844
904
|
if (!isTuner) {
|
|
845
|
-
return [readAgentEventsTool, ...systemReadTools, ...lineageRetrievalTools];
|
|
905
|
+
return [readAgentEventsTool, contextHealthTool, ...systemReadTools, ...lineageRetrievalTools];
|
|
846
906
|
}
|
|
847
907
|
const factsTools = [];
|
|
848
908
|
if (factStore) {
|
|
@@ -966,6 +1026,7 @@ export function createInspectTools(opts) {
|
|
|
966
1026
|
}
|
|
967
1027
|
const tools = [
|
|
968
1028
|
readAgentEventsTool,
|
|
1029
|
+
contextHealthTool,
|
|
969
1030
|
...systemReadTools,
|
|
970
1031
|
readSessionMetricSummaryTool,
|
|
971
1032
|
readSessionTokensByModelTool,
|