@oscharko-dev/keiko-workflows 0.2.6 → 0.2.8
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/.tsbuildinfo +1 -1
- package/dist/context-budget/allocator.d.ts +29 -0
- package/dist/context-budget/allocator.d.ts.map +1 -0
- package/dist/context-budget/allocator.js +185 -0
- package/dist/context-budget/compaction-helpers.d.ts +44 -0
- package/dist/context-budget/compaction-helpers.d.ts.map +1 -0
- package/dist/context-budget/compaction-helpers.js +203 -0
- package/dist/context-budget/compaction.d.ts +14 -0
- package/dist/context-budget/compaction.d.ts.map +1 -0
- package/dist/context-budget/compaction.js +97 -0
- package/dist/context-budget/defaults.d.ts +3 -0
- package/dist/context-budget/defaults.d.ts.map +1 -0
- package/dist/context-budget/defaults.js +75 -0
- package/dist/context-budget/index.d.ts +8 -0
- package/dist/context-budget/index.d.ts.map +1 -0
- package/dist/context-budget/index.js +6 -0
- package/dist/context-budget/rehydration.d.ts +11 -0
- package/dist/context-budget/rehydration.d.ts.map +1 -0
- package/dist/context-budget/rehydration.js +76 -0
- package/dist/contextpack/assemble.d.ts +2 -1
- package/dist/contextpack/assemble.d.ts.map +1 -1
- package/dist/contextpack/assemble.js +19 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -0
- package/dist/observations/command.d.ts +6 -0
- package/dist/observations/command.d.ts.map +1 -0
- package/dist/observations/command.js +67 -0
- package/dist/observations/index.d.ts +5 -0
- package/dist/observations/index.d.ts.map +1 -0
- package/dist/observations/index.js +6 -0
- package/dist/observations/search.d.ts +9 -0
- package/dist/observations/search.d.ts.map +1 -0
- package/dist/observations/search.js +43 -0
- package/dist/observations/shared.d.ts +18 -0
- package/dist/observations/shared.d.ts.map +1 -0
- package/dist/observations/shared.js +63 -0
- package/dist/observations/test.d.ts +6 -0
- package/dist/observations/test.d.ts.map +1 -0
- package/dist/observations/test.js +47 -0
- package/dist/planner/anchors.d.ts.map +1 -1
- package/dist/planner/anchors.js +3 -1
- package/dist/planner/intent.d.ts.map +1 -1
- package/dist/planner/intent.js +6 -0
- package/dist/ranking/rank.d.ts.map +1 -1
- package/dist/ranking/rank.js +5 -3
- package/dist/ranking/scoring.d.ts +4 -0
- package/dist/ranking/scoring.d.ts.map +1 -1
- package/dist/ranking/scoring.js +29 -1
- package/dist/ranking/signals.d.ts +5 -1
- package/dist/ranking/signals.d.ts.map +1 -1
- package/dist/ranking/signals.js +23 -3
- package/package.json +10 -10
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export type { AllocateContextInput, AllocateContextResult, AllocatedContextLane, ContextLaneInput, ContextLaneItemInput, } from "./allocator.js";
|
|
2
|
+
export { allocateContext } from "./allocator.js";
|
|
3
|
+
export { DEFAULT_CONTEXT_BUDGET } from "./defaults.js";
|
|
4
|
+
export type { BuildCompactionInput, CompactionDigest } from "./compaction.js";
|
|
5
|
+
export { buildCompactionRecords } from "./compaction.js";
|
|
6
|
+
export type { RehydrationResult } from "./rehydration.js";
|
|
7
|
+
export { rehydrateProvenanceRef, rehydrateHandle } from "./rehydration.js";
|
|
8
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/context-budget/index.ts"],"names":[],"mappings":"AAGA,YAAY,EACV,oBAAoB,EACpB,qBAAqB,EACrB,oBAAoB,EACpB,gBAAgB,EAChB,oBAAoB,GACrB,MAAM,gBAAgB,CAAC;AACxB,OAAO,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AAEjD,OAAO,EAAE,sBAAsB,EAAE,MAAM,eAAe,CAAC;AAEvD,YAAY,EAAE,oBAAoB,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AAC9E,OAAO,EAAE,sBAAsB,EAAE,MAAM,iBAAiB,CAAC;AAEzD,YAAY,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AAC1D,OAAO,EAAE,sBAAsB,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
// Public sub-barrel for the deterministic context-budget allocator (ADR-0052 D6). External
|
|
2
|
+
// consumers import the allocator surface and the default budget plan through this module.
|
|
3
|
+
export { allocateContext } from "./allocator.js";
|
|
4
|
+
export { DEFAULT_CONTEXT_BUDGET } from "./defaults.js";
|
|
5
|
+
export { buildCompactionRecords } from "./compaction.js";
|
|
6
|
+
export { rehydrateProvenanceRef, rehydrateHandle } from "./rehydration.js";
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { ContextProvenanceRef, ContextRehydrationHandle } from "@oscharko-dev/keiko-contracts";
|
|
2
|
+
import { type SearchScope, type WorkspaceFs } from "@oscharko-dev/keiko-workspace";
|
|
3
|
+
export interface RehydrationResult {
|
|
4
|
+
readonly resolved: boolean;
|
|
5
|
+
readonly invalidated?: boolean | undefined;
|
|
6
|
+
readonly content?: string | undefined;
|
|
7
|
+
readonly reason?: string | undefined;
|
|
8
|
+
}
|
|
9
|
+
export declare function rehydrateProvenanceRef(ref: ContextProvenanceRef, scope: SearchScope, fs: WorkspaceFs, maxBytes?: number): Promise<RehydrationResult>;
|
|
10
|
+
export declare function rehydrateHandle(handle: ContextRehydrationHandle, scope: SearchScope, fs: WorkspaceFs, maxBytes?: number): Promise<RehydrationResult>;
|
|
11
|
+
//# sourceMappingURL=rehydration.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rehydration.d.ts","sourceRoot":"","sources":["../../src/context-budget/rehydration.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,oBAAoB,EAAE,wBAAwB,EAAE,MAAM,+BAA+B,CAAC;AACpG,OAAO,EAGL,KAAK,WAAW,EAChB,KAAK,WAAW,EACjB,MAAM,+BAA+B,CAAC;AAEvC,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC;IAC3B,QAAQ,CAAC,WAAW,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;IAC3C,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACtC,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CACtC;AAuDD,wBAAsB,sBAAsB,CAC1C,GAAG,EAAE,oBAAoB,EACzB,KAAK,EAAE,WAAW,EAClB,EAAE,EAAE,WAAW,EACf,QAAQ,CAAC,EAAE,MAAM,GAChB,OAAO,CAAC,iBAAiB,CAAC,CAuB5B;AAKD,wBAAsB,eAAe,CACnC,MAAM,EAAE,wBAAwB,EAChC,KAAK,EAAE,WAAW,EAClB,EAAE,EAAE,WAAW,EACf,QAAQ,CAAC,EAAE,MAAM,GAChB,OAAO,CAAC,iBAAiB,CAAC,CAa5B"}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
// Bounded, deny-checked, observable rehydration of compacted provenance (ADR-0053 D5). The ONLY IO
|
|
2
|
+
// path in the context-budget module: every repo-file read goes through readExcerpt, which itself
|
|
3
|
+
// runs isDenied + containment + binary/size gates and throws on a denied/binary/too-large path. We
|
|
4
|
+
// CATCH those throws and degrade to {resolved:false, reason} so a stale or denied source never
|
|
5
|
+
// crashes the caller. The excerpt-content hash is recomputed on read so a line-range change is
|
|
6
|
+
// detected precisely against the handle's contentHash.
|
|
7
|
+
import { hashExcerptContent, readExcerpt, } from "@oscharko-dev/keiko-workspace";
|
|
8
|
+
// Independent of whatever the caller passes: rehydration is always bounded. 256 KiB is generous for
|
|
9
|
+
// a single excerpt window yet far under readExcerpt's 2 MiB file cap. A caller passing Infinity or
|
|
10
|
+
// an oversized value cannot widen this — Math.min clamps it.
|
|
11
|
+
const DEFAULT_REHYDRATION_MAX_BYTES = 262_144;
|
|
12
|
+
const MAX_REHYDRATION_MAX_BYTES = 2_097_152;
|
|
13
|
+
function boundMaxBytes(maxBytes) {
|
|
14
|
+
const requested = maxBytes !== undefined && Number.isFinite(maxBytes) && maxBytes >= 0
|
|
15
|
+
? Math.floor(maxBytes)
|
|
16
|
+
: DEFAULT_REHYDRATION_MAX_BYTES;
|
|
17
|
+
return Math.min(requested, MAX_REHYDRATION_MAX_BYTES);
|
|
18
|
+
}
|
|
19
|
+
function deferred(kind) {
|
|
20
|
+
return { resolved: false, reason: `kind ${kind} rehydration deferred (PR3/PR4)` };
|
|
21
|
+
}
|
|
22
|
+
async function readRepoFile(target, scope, fs, maxBytes) {
|
|
23
|
+
try {
|
|
24
|
+
const result = await readExcerpt(scope, {
|
|
25
|
+
scopePath: target.scopePath,
|
|
26
|
+
startLine: target.startLine,
|
|
27
|
+
endLine: target.endLine,
|
|
28
|
+
maxBytes: boundMaxBytes(maxBytes),
|
|
29
|
+
}, { fs });
|
|
30
|
+
const newHash = hashExcerptContent(result.content);
|
|
31
|
+
const invalidated = target.contentHash !== undefined && newHash !== target.contentHash;
|
|
32
|
+
return { resolved: true, invalidated, content: result.content };
|
|
33
|
+
}
|
|
34
|
+
catch (error) {
|
|
35
|
+
return { resolved: false, reason: error instanceof Error ? error.message : String(error) };
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
// Resolves a single ContextProvenanceRef. A notPersistedReason short-circuits WITHOUT any read.
|
|
39
|
+
// kind 'repo-file' with scopePath + lineRange reads via readExcerpt (deny/bounds gated). All other
|
|
40
|
+
// kinds are deferred to a later PR with no IO.
|
|
41
|
+
export async function rehydrateProvenanceRef(ref, scope, fs, maxBytes) {
|
|
42
|
+
if (ref.notPersistedReason !== undefined) {
|
|
43
|
+
return { resolved: false, reason: ref.notPersistedReason };
|
|
44
|
+
}
|
|
45
|
+
if (ref.kind !== "repo-file") {
|
|
46
|
+
return deferred(ref.kind);
|
|
47
|
+
}
|
|
48
|
+
const scopePath = ref.scopePath;
|
|
49
|
+
const lineRange = ref.lineRange;
|
|
50
|
+
if (scopePath === undefined || lineRange === undefined) {
|
|
51
|
+
return { resolved: false, reason: "repo-file ref missing scopePath or lineRange" };
|
|
52
|
+
}
|
|
53
|
+
return readRepoFile({
|
|
54
|
+
scopePath,
|
|
55
|
+
startLine: lineRange.startLine,
|
|
56
|
+
endLine: lineRange.endLine,
|
|
57
|
+
contentHash: ref.contentHash,
|
|
58
|
+
}, scope, fs, maxBytes);
|
|
59
|
+
}
|
|
60
|
+
// Resolves a lane-level handle. A handle that carries kind + scopePath + lineRange is resolved by
|
|
61
|
+
// constructing the equivalent repo-file ref; otherwise it is lane-level (an opaque eviction-set
|
|
62
|
+
// pointer) and not directly rehydratable.
|
|
63
|
+
export async function rehydrateHandle(handle, scope, fs, maxBytes) {
|
|
64
|
+
const { kind, scopePath, lineRange } = handle;
|
|
65
|
+
if (kind === undefined || scopePath === undefined || lineRange === undefined) {
|
|
66
|
+
return { resolved: false, reason: "handle not directly rehydratable (lane-level)" };
|
|
67
|
+
}
|
|
68
|
+
const ref = {
|
|
69
|
+
kind,
|
|
70
|
+
stableId: handle.handleId,
|
|
71
|
+
scopePath,
|
|
72
|
+
lineRange,
|
|
73
|
+
...(handle.contentHash !== undefined ? { contentHash: handle.contentHash } : {}),
|
|
74
|
+
};
|
|
75
|
+
return rehydrateProvenanceRef(ref, scope, fs, maxBytes);
|
|
76
|
+
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type CandidateFile, type ConnectedContextPack, type EvidenceAtom, type ExplorationBudget, type ExplorationUsage, type OmittedContextEntry, type RetrievalQuery, type SelectedScope, type UncertaintyMarker } from "@oscharko-dev/keiko-contracts/connected-context";
|
|
1
|
+
import { type CandidateFile, type ConnectedContextPack, type ContextPackDiagnostics, type EvidenceAtom, type ExplorationBudget, type ExplorationUsage, type OmittedContextEntry, type RetrievalQuery, type SelectedScope, type UncertaintyMarker } from "@oscharko-dev/keiko-contracts/connected-context";
|
|
2
2
|
import { type MicroIndex } from "./microIndex.js";
|
|
3
3
|
import { type RerankerSeam } from "./reranker.js";
|
|
4
4
|
export interface AssembleInput {
|
|
@@ -12,6 +12,7 @@ export interface AssembleInput {
|
|
|
12
12
|
readonly cacheIdentity?: readonly string[] | undefined;
|
|
13
13
|
readonly initialUsage?: ExplorationUsage;
|
|
14
14
|
readonly initialUncertainty?: readonly UncertaintyMarker[];
|
|
15
|
+
readonly diagnostics?: ContextPackDiagnostics | undefined;
|
|
15
16
|
}
|
|
16
17
|
export interface ExcerptWindow {
|
|
17
18
|
readonly startLine: number;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"assemble.d.ts","sourceRoot":"","sources":["../../src/contextpack/assemble.ts"],"names":[],"mappings":"AAQA,OAAO,EAEL,KAAK,aAAa,EAClB,KAAK,oBAAoB,
|
|
1
|
+
{"version":3,"file":"assemble.d.ts","sourceRoot":"","sources":["../../src/contextpack/assemble.ts"],"names":[],"mappings":"AAQA,OAAO,EAEL,KAAK,aAAa,EAClB,KAAK,oBAAoB,EACzB,KAAK,sBAAsB,EAI3B,KAAK,YAAY,EACjB,KAAK,iBAAiB,EACtB,KAAK,gBAAgB,EACrB,KAAK,mBAAmB,EACxB,KAAK,cAAc,EACnB,KAAK,aAAa,EAClB,KAAK,iBAAiB,EACvB,MAAM,iDAAiD,CAAC;AAIzD,OAAO,EAAgB,KAAK,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAChE,OAAO,EAAoB,KAAK,YAAY,EAAE,MAAM,eAAe,CAAC;AAIpE,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,KAAK,EAAE,aAAa,CAAC;IAC9B,QAAQ,CAAC,KAAK,EAAE,cAAc,CAAC;IAC/B,QAAQ,CAAC,MAAM,EAAE,iBAAiB,CAAC;IACnC,QAAQ,CAAC,KAAK,EAAE,SAAS,YAAY,EAAE,CAAC;IACxC,QAAQ,CAAC,MAAM,EAAE,SAAS,aAAa,EAAE,CAAC;IAC1C,QAAQ,CAAC,kBAAkB,EAAE,SAAS,mBAAmB,EAAE,CAAC;IAC5D,QAAQ,CAAC,QAAQ,EAAE,WAAW,CAAC,MAAM,EAAE,aAAa,CAAC,CAAC;IACtD,QAAQ,CAAC,aAAa,CAAC,EAAE,SAAS,MAAM,EAAE,GAAG,SAAS,CAAC;IACvD,QAAQ,CAAC,YAAY,CAAC,EAAE,gBAAgB,CAAC;IACzC,QAAQ,CAAC,kBAAkB,CAAC,EAAE,SAAS,iBAAiB,EAAE,CAAC;IAE3D,QAAQ,CAAC,WAAW,CAAC,EAAE,sBAAsB,GAAG,SAAS,CAAC;CAC3D;AAED,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;CAC1B;AAED,MAAM,MAAM,aAAa,GAAG,MAAM,GAAG,aAAa,GAAG,SAAS,aAAa,EAAE,CAAC;AAE9E,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,kBAAkB,CAAC,EAAE,MAAM,CAAC;IACrC,QAAQ,CAAC,aAAa,CAAC,EAAE,WAAW,CAAC,MAAM,CAAC,CAAC;IAC7C,QAAQ,CAAC,QAAQ,CAAC,EAAE,YAAY,CAAC;IACjC,QAAQ,CAAC,UAAU,CAAC,EAAE,UAAU,CAAC;IACjC,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,MAAM,CAAC;CAC/B;AAED,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,IAAI,EAAE,oBAAoB,CAAC;IACpC,QAAQ,CAAC,SAAS,EAAE,OAAO,CAAC;CAC7B;AA8gBD,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,aAAa,EAAE,OAAO,CAAC,EAAE,eAAe,GAAG,MAAM,CAQ3F;AAED,wBAAsB,mBAAmB,CACvC,KAAK,EAAE,aAAa,EACpB,OAAO,CAAC,EAAE,eAAe,GACxB,OAAO,CAAC,cAAc,CAAC,CAoCzB"}
|
|
@@ -273,6 +273,7 @@ function buildPack(input, plan, nowMs) {
|
|
|
273
273
|
uncertainty: plan.uncertainty,
|
|
274
274
|
emittedAtMs: nowMs,
|
|
275
275
|
ledgerRef: undefined,
|
|
276
|
+
...(input.diagnostics !== undefined ? { diagnostics: input.diagnostics } : {}),
|
|
276
277
|
};
|
|
277
278
|
}
|
|
278
279
|
// ─── Public facade ────────────────────────────────────────────────────────────
|
|
@@ -354,6 +355,23 @@ function cacheOmitted(entry) {
|
|
|
354
355
|
reason: entry.reason,
|
|
355
356
|
};
|
|
356
357
|
}
|
|
358
|
+
// Diagnostics are carried verbatim onto the pack, so two runs that differ ONLY in ranking
|
|
359
|
+
// explanation must not collide on a cached pack. Returns undefined when absent (serializes away,
|
|
360
|
+
// keeping legacy cache keys byte-identical).
|
|
361
|
+
function cacheDiagnostics(diagnostics) {
|
|
362
|
+
if (diagnostics === undefined) {
|
|
363
|
+
return undefined;
|
|
364
|
+
}
|
|
365
|
+
return {
|
|
366
|
+
rankedCandidates: diagnostics.rankedCandidates.map((entry) => ({
|
|
367
|
+
scopePath: entry.scopePath,
|
|
368
|
+
bucket: entry.bucket,
|
|
369
|
+
score: entry.score,
|
|
370
|
+
ecosystem: entry.ecosystem,
|
|
371
|
+
signals: entry.signals.map((signal) => ({ name: signal.name, value: signal.value })),
|
|
372
|
+
})),
|
|
373
|
+
};
|
|
374
|
+
}
|
|
357
375
|
function cacheExcerptWindow(window) {
|
|
358
376
|
return {
|
|
359
377
|
startLine: window.startLine,
|
|
@@ -387,6 +405,7 @@ function buildCacheAtomIds(input, resolved) {
|
|
|
387
405
|
: undefined,
|
|
388
406
|
ranked: input.ranked.map(cacheCandidate),
|
|
389
407
|
omittedFromRanking: input.omittedFromRanking.map(cacheOmitted),
|
|
408
|
+
diagnostics: cacheDiagnostics(input.diagnostics),
|
|
390
409
|
excerpts: cacheExcerptIdentity(input),
|
|
391
410
|
maxBytesPerExcerpt: resolved.maxBytesPerExcerpt,
|
|
392
411
|
editablePaths: [...resolved.editablePaths].sort(),
|
package/dist/index.d.ts
CHANGED
|
@@ -6,4 +6,6 @@ export * from "./ranking/index.js";
|
|
|
6
6
|
export * from "./contextpack/index.js";
|
|
7
7
|
export * from "./qualityIntelligence/index.js";
|
|
8
8
|
export * from "./promptEnhancer/index.js";
|
|
9
|
+
export * from "./context-budget/index.js";
|
|
10
|
+
export * from "./observations/index.js";
|
|
9
11
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAUA,YAAY,EAAE,kBAAkB,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAC;AAE7E,cAAc,uBAAuB,CAAC;AACtC,cAAc,8BAA8B,CAAC;AAG7C,cAAc,oBAAoB,CAAC;AAGnC,cAAc,oBAAoB,CAAC;AAGnC,cAAc,wBAAwB,CAAC;AAIvC,cAAc,gCAAgC,CAAC;AAG/C,cAAc,2BAA2B,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAUA,YAAY,EAAE,kBAAkB,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAC;AAE7E,cAAc,uBAAuB,CAAC;AACtC,cAAc,8BAA8B,CAAC;AAG7C,cAAc,oBAAoB,CAAC;AAGnC,cAAc,oBAAoB,CAAC;AAGnC,cAAc,wBAAwB,CAAC;AAIvC,cAAc,gCAAgC,CAAC;AAG/C,cAAc,2BAA2B,CAAC;AAG1C,cAAc,2BAA2B,CAAC;AAG1C,cAAc,yBAAyB,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -11,3 +11,7 @@ export * from "./contextpack/index.js";
|
|
|
11
11
|
export * from "./qualityIntelligence/index.js";
|
|
12
12
|
// ─── Prompt Enhancer workflow execution (Epic #1307, Issue #1314) ─────────────
|
|
13
13
|
export * from "./promptEnhancer/index.js";
|
|
14
|
+
// ─── Deterministic context-budget allocator (ADR-0052, context-engineering milestone) ──
|
|
15
|
+
export * from "./context-budget/index.js";
|
|
16
|
+
// ─── Tool-observation shapers (ADR-0054, PR3-W3, context-engineering milestone) ──
|
|
17
|
+
export * from "./observations/index.js";
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { type CommandResult, type ShapedCommandObservation } from "@oscharko-dev/keiko-contracts";
|
|
2
|
+
export interface ShapeCommandOptions {
|
|
3
|
+
readonly observationId?: string | undefined;
|
|
4
|
+
}
|
|
5
|
+
export declare function shapeCommandObservation(result: CommandResult, opts?: ShapeCommandOptions): ShapedCommandObservation;
|
|
6
|
+
//# sourceMappingURL=command.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"command.d.ts","sourceRoot":"","sources":["../../src/observations/command.ts"],"names":[],"mappings":"AAOA,OAAO,EAEL,KAAK,aAAa,EAClB,KAAK,wBAAwB,EAE9B,MAAM,+BAA+B,CAAC;AAWvC,MAAM,WAAW,mBAAmB;IAClC,QAAQ,CAAC,aAAa,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CAC7C;AA0CD,wBAAgB,uBAAuB,CACrC,MAAM,EAAE,aAAa,EACrB,IAAI,CAAC,EAAE,mBAAmB,GACzB,wBAAwB,CA6B1B"}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
// Pure shaper: CommandResult -> ShapedCommandObservation (ADR-0054, PR3-W3, D4). No IO, no clock,
|
|
2
|
+
// no randomness. Total — never throws on empty streams. The exit-code/duration/timedOut/truncated
|
|
3
|
+
// signals are preserved verbatim; each non-empty stream contributes a redacted, byte-bounded
|
|
4
|
+
// excerpt whose combined bytes stay within MAX_OBSERVATION_EXCERPT_BYTES. Injection signals are
|
|
5
|
+
// recorded content-free over the already-redacted excerpt text. A rehydration handle is attached
|
|
6
|
+
// ONLY when the output was truncated (there is omitted content worth re-fetching in PR5).
|
|
7
|
+
import { MAX_OBSERVATION_EXCERPT_BYTES, } from "@oscharko-dev/keiko-contracts";
|
|
8
|
+
import { boundExcerpt, buildToolRehydrationHandle, estimateTokens, injectionSignalsFor, makeObservationId, utf8ByteLength, } from "./shared.js";
|
|
9
|
+
const TRUNCATED_NOT_PERSISTED = "tool output not persisted in PR3 (artifact store wired in PR5)";
|
|
10
|
+
// Builds the bounded excerpts, allocating the shared MAX_OBSERVATION_EXCERPT_BYTES budget across the
|
|
11
|
+
// non-empty streams in order (stdout first, then stderr from the remaining budget) so the SUM of
|
|
12
|
+
// excerpt bytes never exceeds the cap. Each excerpt is redacted before clamping (boundExcerpt).
|
|
13
|
+
function buildExcerpts(sources) {
|
|
14
|
+
const excerpts = [];
|
|
15
|
+
let remaining = MAX_OBSERVATION_EXCERPT_BYTES;
|
|
16
|
+
for (const source of sources) {
|
|
17
|
+
if (source.text.length === 0 || remaining <= 0) {
|
|
18
|
+
continue;
|
|
19
|
+
}
|
|
20
|
+
const text = boundExcerpt(source.text, remaining);
|
|
21
|
+
if (text.length === 0) {
|
|
22
|
+
continue;
|
|
23
|
+
}
|
|
24
|
+
const bytes = utf8ByteLength(text);
|
|
25
|
+
excerpts.push({ stream: source.stream, bytes, text });
|
|
26
|
+
remaining -= bytes;
|
|
27
|
+
}
|
|
28
|
+
return excerpts;
|
|
29
|
+
}
|
|
30
|
+
function injectionFor(excerpts) {
|
|
31
|
+
const combined = excerpts.map((excerpt) => excerpt.text).join("\n");
|
|
32
|
+
const summary = injectionSignalsFor(combined);
|
|
33
|
+
return {
|
|
34
|
+
injectionSignalCount: summary.count,
|
|
35
|
+
hasCriticalInjectionSignal: summary.critical,
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
export function shapeCommandObservation(result, opts) {
|
|
39
|
+
const observationId = opts?.observationId ?? makeObservationId("");
|
|
40
|
+
const excerpts = buildExcerpts([
|
|
41
|
+
{ stream: "stdout", text: result.stdout },
|
|
42
|
+
{ stream: "stderr", text: result.stderr },
|
|
43
|
+
]);
|
|
44
|
+
const injection = injectionFor(excerpts);
|
|
45
|
+
return {
|
|
46
|
+
kind: "command",
|
|
47
|
+
observationId,
|
|
48
|
+
exitCode: result.exitCode,
|
|
49
|
+
durationMs: result.durationMs,
|
|
50
|
+
timedOut: result.timedOut,
|
|
51
|
+
truncated: result.truncated,
|
|
52
|
+
...(result.omittedByteCount !== undefined ? { omittedByteCount: result.omittedByteCount } : {}),
|
|
53
|
+
excerpts,
|
|
54
|
+
injectionSignalCount: injection.injectionSignalCount,
|
|
55
|
+
hasCriticalInjectionSignal: injection.hasCriticalInjectionSignal,
|
|
56
|
+
...(result.truncated
|
|
57
|
+
? {
|
|
58
|
+
rehydration: buildToolRehydrationHandle({
|
|
59
|
+
artifactSeed: `${observationId}:full`,
|
|
60
|
+
itemCount: 1,
|
|
61
|
+
approxTokens: estimateTokens(excerpts.map((excerpt) => excerpt.text).join("\n")),
|
|
62
|
+
notPersistedReason: TRUNCATED_NOT_PERSISTED,
|
|
63
|
+
}),
|
|
64
|
+
}
|
|
65
|
+
: {}),
|
|
66
|
+
};
|
|
67
|
+
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export { shapeCommandObservation, type ShapeCommandOptions } from "./command.js";
|
|
2
|
+
export { shapeTestObservation, type ShapeTestOptions } from "./test.js";
|
|
3
|
+
export { shapeSearchObservation, type ShapeSearchOptions } from "./search.js";
|
|
4
|
+
export { makeObservationId, injectionSignalsFor, boundExcerpt, buildToolRehydrationHandle, type InjectionSummary, type RehydrationHandleInput, } from "./shared.js";
|
|
5
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/observations/index.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,uBAAuB,EAAE,KAAK,mBAAmB,EAAE,MAAM,cAAc,CAAC;AACjF,OAAO,EAAE,oBAAoB,EAAE,KAAK,gBAAgB,EAAE,MAAM,WAAW,CAAC;AACxE,OAAO,EAAE,sBAAsB,EAAE,KAAK,kBAAkB,EAAE,MAAM,aAAa,CAAC;AAC9E,OAAO,EACL,iBAAiB,EACjB,mBAAmB,EACnB,YAAY,EACZ,0BAA0B,EAC1B,KAAK,gBAAgB,EACrB,KAAK,sBAAsB,GAC5B,MAAM,aAAa,CAAC"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
// Barrel for the tool-observation shapers (ADR-0054, PR3-W3). Re-exports the three pure shapers and
|
|
2
|
+
// the shared helpers worth exposing to the PR4 lane assembler. No live browser shaper in PR3 (D8).
|
|
3
|
+
export { shapeCommandObservation } from "./command.js";
|
|
4
|
+
export { shapeTestObservation } from "./test.js";
|
|
5
|
+
export { shapeSearchObservation } from "./search.js";
|
|
6
|
+
export { makeObservationId, injectionSignalsFor, boundExcerpt, buildToolRehydrationHandle, } from "./shared.js";
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { type ShapedSearchObservation } from "@oscharko-dev/keiko-contracts";
|
|
2
|
+
import type { SearchResult } from "@oscharko-dev/keiko-workspace";
|
|
3
|
+
export interface ShapeSearchOptions {
|
|
4
|
+
readonly observationId?: string | undefined;
|
|
5
|
+
readonly query?: string | undefined;
|
|
6
|
+
readonly searchKind?: string | undefined;
|
|
7
|
+
}
|
|
8
|
+
export declare function shapeSearchObservation(result: SearchResult, opts?: ShapeSearchOptions): ShapedSearchObservation;
|
|
9
|
+
//# sourceMappingURL=search.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"search.d.ts","sourceRoot":"","sources":["../../src/observations/search.ts"],"names":[],"mappings":"AAWA,OAAO,EAGL,KAAK,uBAAuB,EAE7B,MAAM,+BAA+B,CAAC;AAEvC,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,+BAA+B,CAAC;AAIlE,MAAM,WAAW,kBAAkB;IACjC,QAAQ,CAAC,aAAa,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC5C,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACpC,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CAC1C;AAiBD,wBAAgB,sBAAsB,CACpC,MAAM,EAAE,YAAY,EACpB,IAAI,CAAC,EAAE,kBAAkB,GACxB,uBAAuB,CAkBzB"}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
// Pure shaper: SearchResult -> ShapedSearchObservation (ADR-0054, PR3-W3, D4). No IO, no clock, no
|
|
2
|
+
// randomness. Total — never throws on empty atoms. Preserves the top-scoring file ranges so the
|
|
3
|
+
// PR4 lane assembler can cite relevant matches without including any raw atom text. scopePath is
|
|
4
|
+
// allowed here: this is the internal tool-observations lane, not the path-free browser summary.
|
|
5
|
+
//
|
|
6
|
+
// SOURCE NOTE: SearchResult (keiko-workspace/repoSearch.ts) carries the atoms + truncation flag but
|
|
7
|
+
// NOT the original query string or the search kind (those are inputs to searchText/findFiles, not
|
|
8
|
+
// outputs). The caller supplies them via opts so the shaper stays a pure projection. The query is
|
|
9
|
+
// the untrusted input (user-typed or model-generated): it is redacted and byte-bounded, and the
|
|
10
|
+
// injection summary is computed over the redacted query, content-free.
|
|
11
|
+
import { MAX_OBSERVATION_QUERY_BYTES, MAX_TOP_RANGES, } from "@oscharko-dev/keiko-contracts";
|
|
12
|
+
import { boundExcerpt, injectionSignalsFor, makeObservationId } from "./shared.js";
|
|
13
|
+
const DEFAULT_SEARCH_KIND = "text";
|
|
14
|
+
function toRange(atom) {
|
|
15
|
+
const startLine = atom.lineRange?.startLine ?? 0;
|
|
16
|
+
const endLine = atom.lineRange?.endLine ?? 0;
|
|
17
|
+
return { scopePath: atom.scopePath, startLine, endLine, score: atom.score };
|
|
18
|
+
}
|
|
19
|
+
// Top MAX_TOP_RANGES atoms by descending score. Sorts a shallow copy (the source array is readonly
|
|
20
|
+
// and must not be mutated). Ties keep their relative input order via a stable comparator.
|
|
21
|
+
function topRanges(atoms) {
|
|
22
|
+
const sorted = [...atoms].sort((a, b) => b.score - a.score);
|
|
23
|
+
return sorted.slice(0, MAX_TOP_RANGES).map(toRange);
|
|
24
|
+
}
|
|
25
|
+
export function shapeSearchObservation(result, opts) {
|
|
26
|
+
const observationId = opts?.observationId ?? makeObservationId("");
|
|
27
|
+
const query = boundExcerpt(opts?.query ?? "", MAX_OBSERVATION_QUERY_BYTES);
|
|
28
|
+
const ranges = topRanges(result.atoms);
|
|
29
|
+
const candidateCount = result.atoms.length;
|
|
30
|
+
const injection = injectionSignalsFor(query);
|
|
31
|
+
return {
|
|
32
|
+
kind: "search",
|
|
33
|
+
observationId,
|
|
34
|
+
query,
|
|
35
|
+
searchKind: opts?.searchKind ?? DEFAULT_SEARCH_KIND,
|
|
36
|
+
candidateCount,
|
|
37
|
+
topRanges: ranges,
|
|
38
|
+
omittedCount: candidateCount - ranges.length,
|
|
39
|
+
truncated: result.truncated,
|
|
40
|
+
injectionSignalCount: injection.count,
|
|
41
|
+
hasCriticalInjectionSignal: injection.critical,
|
|
42
|
+
};
|
|
43
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { estimateTokens, type ContextToolRehydrationHandle } from "@oscharko-dev/keiko-contracts";
|
|
2
|
+
export interface InjectionSummary {
|
|
3
|
+
readonly count: number;
|
|
4
|
+
readonly critical: boolean;
|
|
5
|
+
}
|
|
6
|
+
export declare function makeObservationId(seed: string): string;
|
|
7
|
+
export declare function injectionSignalsFor(text: string): InjectionSummary;
|
|
8
|
+
export declare function boundExcerpt(text: string, maxBytes: number): string;
|
|
9
|
+
export declare function utf8ByteLength(text: string): number;
|
|
10
|
+
export interface RehydrationHandleInput {
|
|
11
|
+
readonly artifactSeed: string;
|
|
12
|
+
readonly itemCount: number;
|
|
13
|
+
readonly approxTokens: number;
|
|
14
|
+
readonly notPersistedReason?: string | undefined;
|
|
15
|
+
}
|
|
16
|
+
export declare function buildToolRehydrationHandle(input: RehydrationHandleInput): ContextToolRehydrationHandle;
|
|
17
|
+
export { estimateTokens };
|
|
18
|
+
//# sourceMappingURL=shared.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"shared.d.ts","sourceRoot":"","sources":["../../src/observations/shared.ts"],"names":[],"mappings":"AAOA,OAAO,EAEL,cAAc,EAEd,KAAK,4BAA4B,EAClC,MAAM,+BAA+B,CAAC;AAavC,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC;CAC5B;AAID,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAEtD;AAKD,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,GAAG,gBAAgB,CAOlE;AAKD,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAWnE;AAGD,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAEnD;AAED,MAAM,WAAW,sBAAsB;IACrC,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,kBAAkB,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CAClD;AAKD,wBAAgB,0BAA0B,CACxC,KAAK,EAAE,sBAAsB,GAC5B,4BAA4B,CAY9B;AAED,OAAO,EAAE,cAAc,EAAE,CAAC"}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
// Shared, pure helpers for the tool-observation shapers (ADR-0054, PR3-W3). No IO, no clock, no
|
|
2
|
+
// randomness. Every untrusted free-text string is scrubbed via redact() from keiko-security BEFORE
|
|
3
|
+
// it is clamped or measured (the defense-in-depth boundary established by the PR2 compaction
|
|
4
|
+
// helpers). Injection detection is CONTENT-FREE by construction: detectPromptInjectionSignals
|
|
5
|
+
// returns only {code, severity, matchCount}; we store an aggregate count and a boolean and NEVER
|
|
6
|
+
// the matched text. No new package edge: contracts/security/workspace are already dependencies.
|
|
7
|
+
import { CONTEXT_ENGINEERING_SCHEMA_VERSION, estimateTokens, } from "@oscharko-dev/keiko-contracts";
|
|
8
|
+
import { hashExcerptContent } from "@oscharko-dev/keiko-workspace";
|
|
9
|
+
import { detectPromptInjectionSignals, hasCriticalInjectionSignal, redact, } from "@oscharko-dev/keiko-security";
|
|
10
|
+
// The lane every shaped tool observation belongs to (ADR-0052 taxonomy).
|
|
11
|
+
const TOOL_OBSERVATIONS_LANE = "tool-observations";
|
|
12
|
+
// Stable, reproducible observation id: SHA-256 hex of the seed (typically the toolCallId). Total —
|
|
13
|
+
// hashExcerptContent never throws; an empty seed yields the well-defined hash of zero bytes.
|
|
14
|
+
export function makeObservationId(seed) {
|
|
15
|
+
return hashExcerptContent(seed);
|
|
16
|
+
}
|
|
17
|
+
// CONTENT-FREE injection signal summary over untrusted text. Sums each signal's matchCount for the
|
|
18
|
+
// aggregate count and derives the critical flag from the authoritative severity helper. The matched
|
|
19
|
+
// text is never inspected by the caller — only the returned counts/boolean are persisted.
|
|
20
|
+
export function injectionSignalsFor(text) {
|
|
21
|
+
const signals = detectPromptInjectionSignals(text);
|
|
22
|
+
let count = 0;
|
|
23
|
+
for (const signal of signals) {
|
|
24
|
+
count += signal.matchCount;
|
|
25
|
+
}
|
|
26
|
+
return { count, critical: hasCriticalInjectionSignal(signals) };
|
|
27
|
+
}
|
|
28
|
+
// Redact-then-clamp: redact() runs FIRST so a secret can never survive into the bounded excerpt,
|
|
29
|
+
// then the redacted text is clamped to maxBytes of UTF-8 without splitting a multibyte character
|
|
30
|
+
// (the repoSearch clampToBytes pattern: encode, subarray, lossy-decode, trim the replacement char).
|
|
31
|
+
export function boundExcerpt(text, maxBytes) {
|
|
32
|
+
const redacted = redact(text);
|
|
33
|
+
if (maxBytes <= 0) {
|
|
34
|
+
return "";
|
|
35
|
+
}
|
|
36
|
+
const encoded = new TextEncoder().encode(redacted);
|
|
37
|
+
if (encoded.length <= maxBytes) {
|
|
38
|
+
return redacted;
|
|
39
|
+
}
|
|
40
|
+
const buffer = encoded.subarray(0, maxBytes);
|
|
41
|
+
return new TextDecoder("utf-8", { fatal: false }).decode(buffer).replace(/�+$/u, "");
|
|
42
|
+
}
|
|
43
|
+
// UTF-8 byte length of a string (the bytes field on a ShapedStreamExcerpt).
|
|
44
|
+
export function utf8ByteLength(text) {
|
|
45
|
+
return new TextEncoder().encode(text).length;
|
|
46
|
+
}
|
|
47
|
+
// Builds the content-free ContextToolRehydrationHandle. artifactId = hashExcerptContent(artifactSeed)
|
|
48
|
+
// — deterministic and stable. notPersistedReason is included only when supplied (exactOptional:
|
|
49
|
+
// conditional spread, never assigned undefined). approxTokens is passed through unchanged.
|
|
50
|
+
export function buildToolRehydrationHandle(input) {
|
|
51
|
+
return {
|
|
52
|
+
schemaVersion: CONTEXT_ENGINEERING_SCHEMA_VERSION,
|
|
53
|
+
laneId: TOOL_OBSERVATIONS_LANE,
|
|
54
|
+
kind: "tool-result",
|
|
55
|
+
artifactId: makeObservationId(input.artifactSeed),
|
|
56
|
+
itemCount: input.itemCount,
|
|
57
|
+
approxTokens: input.approxTokens,
|
|
58
|
+
...(input.notPersistedReason !== undefined
|
|
59
|
+
? { notPersistedReason: input.notPersistedReason }
|
|
60
|
+
: {}),
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
export { estimateTokens };
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { type ShapedTestObservation, type VerificationResult } from "@oscharko-dev/keiko-contracts";
|
|
2
|
+
export interface ShapeTestOptions {
|
|
3
|
+
readonly observationId?: string | undefined;
|
|
4
|
+
}
|
|
5
|
+
export declare function shapeTestObservation(result: VerificationResult, opts?: ShapeTestOptions): ShapedTestObservation;
|
|
6
|
+
//# sourceMappingURL=test.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"test.d.ts","sourceRoot":"","sources":["../../src/observations/test.ts"],"names":[],"mappings":"AAYA,OAAO,EAEL,KAAK,qBAAqB,EAC1B,KAAK,kBAAkB,EACxB,MAAM,+BAA+B,CAAC;AAUvC,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,aAAa,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CAC7C;AAQD,wBAAgB,oBAAoB,CAClC,MAAM,EAAE,kBAAkB,EAC1B,IAAI,CAAC,EAAE,gBAAgB,GACtB,qBAAqB,CA6BvB"}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
// Pure shaper: VerificationResult -> ShapedTestObservation (ADR-0054, PR3-W3, D4). No IO, no clock,
|
|
2
|
+
// no randomness. Total — never throws.
|
|
3
|
+
//
|
|
4
|
+
// HONEST SOURCE NOTE: the live VerificationResult (keiko-contracts/verification.ts) carries NO
|
|
5
|
+
// per-test structured data — no pass/fail/skip counts, no failing test names, no stack frames. Its
|
|
6
|
+
// outputSummary is a redacted DIGEST string (e.g. "command output captured (N bytes) and omitted
|
|
7
|
+
// from summary" — see keiko-verification/src/orchestrator.ts outputDigest), not a parseable test
|
|
8
|
+
// log, and the per-status counts on VerificationReport count STEPS, not individual tests. Per the
|
|
9
|
+
// PR3 constraint we do NOT invent counts or names: counts default to zeros and the name/frame arrays
|
|
10
|
+
// stay empty until a producer supplies genuinely structured data. The reliably-present fields we DO
|
|
11
|
+
// project: kind, status, exitCode, durationMs, truncated, and the redacted outputSummary.
|
|
12
|
+
import {} from "@oscharko-dev/keiko-contracts";
|
|
13
|
+
import { boundExcerpt, buildToolRehydrationHandle, estimateTokens, injectionSignalsFor, makeObservationId, } from "./shared.js";
|
|
14
|
+
const TRUNCATED_NOT_PERSISTED = "tool output not persisted in PR3 (artifact store wired in PR5)";
|
|
15
|
+
// VerificationResult has no per-test breakdown; the only honest count is all-zero. A future producer
|
|
16
|
+
// with structured test data populates this via a richer overload, not by inventing numbers here.
|
|
17
|
+
const EMPTY_COUNTS = { passed: 0, failed: 0, skipped: 0 };
|
|
18
|
+
export function shapeTestObservation(result, opts) {
|
|
19
|
+
const observationId = opts?.observationId ?? makeObservationId("");
|
|
20
|
+
const outputSummary = boundExcerpt(result.outputSummary, Number.MAX_SAFE_INTEGER);
|
|
21
|
+
const injection = injectionSignalsFor(outputSummary);
|
|
22
|
+
return {
|
|
23
|
+
kind: "test",
|
|
24
|
+
observationId,
|
|
25
|
+
verificationKind: result.kind,
|
|
26
|
+
status: result.status,
|
|
27
|
+
exitCode: result.exitCode,
|
|
28
|
+
durationMs: result.durationMs,
|
|
29
|
+
truncated: result.truncated,
|
|
30
|
+
counts: EMPTY_COUNTS,
|
|
31
|
+
failingTestNames: [],
|
|
32
|
+
stackFrameExcerpts: [],
|
|
33
|
+
outputSummary,
|
|
34
|
+
injectionSignalCount: injection.count,
|
|
35
|
+
hasCriticalInjectionSignal: injection.critical,
|
|
36
|
+
...(result.truncated
|
|
37
|
+
? {
|
|
38
|
+
rehydration: buildToolRehydrationHandle({
|
|
39
|
+
artifactSeed: `${observationId}:full`,
|
|
40
|
+
itemCount: 1,
|
|
41
|
+
approxTokens: estimateTokens(outputSummary),
|
|
42
|
+
notPersistedReason: TRUNCATED_NOT_PERSISTED,
|
|
43
|
+
}),
|
|
44
|
+
}
|
|
45
|
+
: {}),
|
|
46
|
+
};
|
|
47
|
+
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"anchors.d.ts","sourceRoot":"","sources":["../../src/planner/anchors.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"anchors.d.ts","sourceRoot":"","sources":["../../src/planner/anchors.ts"],"names":[],"mappings":"AAgMA,MAAM,MAAM,gBAAgB,GAAG,SAAS,GAAG,YAAY,GAAG,MAAM,GAAG,QAAQ,CAAC;AAE5E,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,IAAI,EAAE,gBAAgB,CAAC;CACjC;AAED,MAAM,WAAW,qBAAqB;IACpC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;CAC7B;AAED,MAAM,WAAW,sBAAsB;IACrC,QAAQ,CAAC,OAAO,EAAE,SAAS,YAAY,EAAE,CAAC;IAC1C,QAAQ,CAAC,SAAS,EAAE,OAAO,CAAC;IAC5B,QAAQ,CAAC,gBAAgB,EAAE,MAAM,CAAC;CACnC;AAgHD,wBAAgB,cAAc,CAAC,KAAK,EAAE,qBAAqB,GAAG,sBAAsB,CAqBnF"}
|
package/dist/planner/anchors.js
CHANGED
|
@@ -158,6 +158,7 @@ const QUOTED_DOUBLE_RE = /"([^"\n]+)"/g;
|
|
|
158
158
|
const QUOTED_SINGLE_RE = /'([^'\n]+)'/g;
|
|
159
159
|
const BACKTICK_RE = /`([^`\n]+)`/g;
|
|
160
160
|
const PATH_RE = /(?:[\w.-]+\/)+[\w.-]+\.[A-Za-z]{1,8}/g;
|
|
161
|
+
const API_ROUTE_RE = /(^|[^A-Za-z0-9_.-])((?:\/[A-Za-z0-9_.:{}-]+){2,})/g;
|
|
161
162
|
// Requires a genuine lower/digit -> upper transition so all-caps acronyms and SHOUTING words
|
|
162
163
|
// (WHY, HTTP, BROKEN) are NOT mistaken for code identifiers. A spurious 0.85 identifier anchor
|
|
163
164
|
// would both satisfy the clarification gate for a vague question and seed symbol-file retrieval
|
|
@@ -195,7 +196,7 @@ function collectMatches(source, pattern, kind, weight, out) {
|
|
|
195
196
|
let match = re.exec(source);
|
|
196
197
|
while (match !== null) {
|
|
197
198
|
const full = match[0];
|
|
198
|
-
const captured = match[1] ?? full;
|
|
199
|
+
const captured = match[2] ?? match[1] ?? full;
|
|
199
200
|
pushAnchor(out, captured, kind, weight);
|
|
200
201
|
parts.push(source.slice(cursor, match.index));
|
|
201
202
|
parts.push(" ".repeat(full.length));
|
|
@@ -280,6 +281,7 @@ export function extractAnchors(input) {
|
|
|
280
281
|
let remaining = collectMatches(text, QUOTED_DOUBLE_RE, "quoted", 1, collected);
|
|
281
282
|
remaining = collectMatches(remaining, QUOTED_SINGLE_RE, "quoted", 1, collected);
|
|
282
283
|
remaining = collectMatches(remaining, BACKTICK_RE, "identifier", 0.9, collected);
|
|
284
|
+
remaining = collectMatches(remaining, API_ROUTE_RE, "path", 0.95, collected);
|
|
283
285
|
remaining = collectMatches(remaining, PATH_RE, "path", 0.95, collected);
|
|
284
286
|
remaining = collectMatches(remaining, CAMEL_IDENTIFIER_RE, "identifier", 0.85, collected);
|
|
285
287
|
remaining = collectTechnicalTerms(remaining, collected);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"intent.d.ts","sourceRoot":"","sources":["../../src/planner/intent.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,iDAAiD,CAAC;
|
|
1
|
+
{"version":3,"file":"intent.d.ts","sourceRoot":"","sources":["../../src/planner/intent.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,iDAAiD,CAAC;AAGrF,MAAM,MAAM,eAAe,GACvB,kBAAkB,GAClB,qBAAqB,GACrB,sBAAsB,GACtB,mBAAmB,GACnB,sBAAsB,CAAC;AAE3B,MAAM,WAAW,6BAA6B;IAC5C,QAAQ,CAAC,MAAM,EAAE,eAAe,CAAC;IACjC,QAAQ,CAAC,eAAe,EAAE,SAAS,MAAM,EAAE,CAAC;CAC7C;AA4JD,wBAAgB,uBAAuB,CACrC,SAAS,EAAE,MAAM,EACjB,MAAM,CAAC,EAAE,aAAa,GACrB,6BAA6B,CAqB/B"}
|
package/dist/planner/intent.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
// Deterministic retrieval-intent classification for connected-context planning.
|
|
2
2
|
// This module is intentionally pure: no IO, no clock, no model calls.
|
|
3
|
+
import { ecosystemMetadataIntentPatterns } from "@oscharko-dev/keiko-workspace";
|
|
3
4
|
const BASIC_STOP_WORDS = new Set([
|
|
4
5
|
"the",
|
|
5
6
|
"and",
|
|
@@ -77,6 +78,11 @@ const PROJECT_METADATA_PATTERNS = [
|
|
|
77
78
|
{ term: "nextjs", pattern: /\bnext(?:\.js)?\b/iu },
|
|
78
79
|
{ term: "react", pattern: /\breact\b/iu },
|
|
79
80
|
{ term: "eslint", pattern: /\beslint\b/iu },
|
|
81
|
+
// Polyglot ecosystem routing is sourced from the shared registry so questions like "Which Java
|
|
82
|
+
// version does this project use?" classify as project-metadata instead of generic code search.
|
|
83
|
+
// The established JS/TS terms above stay in place for compatibility; registry duplicates are
|
|
84
|
+
// harmless because matched terms are de-duplicated before classification.
|
|
85
|
+
...ecosystemMetadataIntentPatterns,
|
|
80
86
|
];
|
|
81
87
|
const REPOSITORY_OVERVIEW_PATTERNS = [
|
|
82
88
|
{ term: "architecture", pattern: /\barchitecture\b|\barchitektur\b/iu },
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"rank.d.ts","sourceRoot":"","sources":["../../src/ranking/rank.ts"],"names":[],"mappings":"AAKA,OAAO,EAIL,KAAK,uBAAuB,EAG7B,MAAM,iDAAiD,CAAC;AAEzD,OAAO,EAIL,KAAK,aAAa,EAClB,KAAK,YAAY,EAClB,MAAM,aAAa,CAAC;AACrB,OAAO,
|
|
1
|
+
{"version":3,"file":"rank.d.ts","sourceRoot":"","sources":["../../src/ranking/rank.ts"],"names":[],"mappings":"AAKA,OAAO,EAIL,KAAK,uBAAuB,EAG7B,MAAM,iDAAiD,CAAC;AAEzD,OAAO,EAIL,KAAK,aAAa,EAClB,KAAK,YAAY,EAClB,MAAM,aAAa,CAAC;AACrB,OAAO,EAAkC,KAAK,cAAc,EAAE,MAAM,cAAc,CAAC;AACnF,OAAO,EAIL,KAAK,YAAY,EAClB,MAAM,cAAc,CAAC;AAEtB,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,OAAO,CAAC,EAAE,cAAc,CAAC;IAClC,QAAQ,CAAC,MAAM,CAAC,EAAE,IAAI,CAAC,aAAa,EAAE,OAAO,CAAC,CAAC;IAC/C,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,MAAM,CAAC;CAC/B;AAED,MAAM,WAAW,kBAAkB;IACjC,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,gBAAgB,EAAE,MAAM,CAAC;IAClC,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,aAAa,EAAE,QAAQ,CAAC,MAAM,CAAC,uBAAuB,EAAE,MAAM,CAAC,CAAC,CAAC;IAC1E,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;CAC5B;AAED,MAAM,WAAW,aAAc,SAAQ,YAAY;IACjD,QAAQ,CAAC,WAAW,EAAE,kBAAkB,CAAC;CAC1C;AA+JD,wBAAgB,cAAc,CAAC,KAAK,EAAE,YAAY,EAAE,OAAO,GAAE,cAAmB,GAAG,aAAa,CA2B/F"}
|
package/dist/ranking/rank.js
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
// the returned diagnostics so UI/audit consumers can render zeros without conditionals.
|
|
5
5
|
import { CANDIDATE_OMISSION_REASONS, isValidScopePath, } from "@oscharko-dev/keiko-contracts/connected-context";
|
|
6
6
|
import { DEFAULT_FILTER_OPTIONS, filterCandidates, } from "./filter.js";
|
|
7
|
-
import {
|
|
7
|
+
import { computeScore, weightsForIntent } from "./scoring.js";
|
|
8
8
|
import { DEFAULT_GENERATED_PATTERNS, extractSignals, } from "./signals.js";
|
|
9
9
|
const AUTO_DUPLICATE_CLUSTER_MIN = 3;
|
|
10
10
|
const AUTO_DUPLICATE_BASENAME_EXEMPTIONS = new Set([
|
|
@@ -101,7 +101,7 @@ function deriveDuplicateHints(group, explicit) {
|
|
|
101
101
|
function buildAnnotated(group, input, hints, weights) {
|
|
102
102
|
const annotated = [];
|
|
103
103
|
for (const [scopePath, atomsForPath] of group) {
|
|
104
|
-
const signals = extractSignals(atomsForPath, input.anchors, hints);
|
|
104
|
+
const signals = extractSignals(atomsForPath, input.anchors, hints, input.context);
|
|
105
105
|
const score = computeScore(signals, weights);
|
|
106
106
|
const candidate = {
|
|
107
107
|
scopePath,
|
|
@@ -139,7 +139,9 @@ export function rankCandidates(input, options = {}) {
|
|
|
139
139
|
const startMs = clock();
|
|
140
140
|
const frozenStartMs = () => startMs;
|
|
141
141
|
const hints = resolveHints(input.hints);
|
|
142
|
-
|
|
142
|
+
// Explicit weights still win (existing callers/tests unaffected); otherwise the intent picks the
|
|
143
|
+
// weights — DEFAULT for non-boosted intents and the no-intent path, so behavior is unchanged there.
|
|
144
|
+
const weights = options.weights ?? weightsForIntent(input.context?.retrievalIntent);
|
|
143
145
|
const { valid, invalidPaths } = groupAtomsByPath(input.atoms);
|
|
144
146
|
const rankingHints = { ...hints, duplicateOf: deriveDuplicateHints(valid, hints.duplicateOf) };
|
|
145
147
|
const annotated = buildAnnotated(valid, input, rankingHints, weights);
|
|
@@ -7,7 +7,11 @@ export interface ScoringWeights {
|
|
|
7
7
|
readonly testPairBonus: number;
|
|
8
8
|
readonly stacktracePositionBonus: number;
|
|
9
9
|
readonly generatedPenalty: number;
|
|
10
|
+
readonly canonicalMetadata?: number;
|
|
11
|
+
readonly structuralEdge?: number;
|
|
10
12
|
}
|
|
11
13
|
export declare const DEFAULT_SCORING_WEIGHTS: ScoringWeights;
|
|
14
|
+
export declare function isIntentBoosted(intent: string | undefined): boolean;
|
|
15
|
+
export declare function weightsForIntent(intent: string | undefined): ScoringWeights;
|
|
12
16
|
export declare function computeScore(signals: ExtractedSignals, weights?: ScoringWeights): number;
|
|
13
17
|
//# sourceMappingURL=scoring.d.ts.map
|