pi-fold 0.1.0 → 0.1.1
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/README.md +63 -4
- package/extensions/active-context.ts +179 -12
- package/extensions/lib/advisory.ts +92 -6
- package/extensions/lib/canonical.ts +8 -0
- package/extensions/lib/folding.ts +88 -9
- package/extensions/lib/persistence.ts +43 -2
- package/extensions/lib/policy.ts +66 -3
- package/extensions/lib/surfacing.ts +332 -0
- package/extensions/lib/tool-surface.ts +6 -3
- package/package.json +2 -2
|
@@ -9,6 +9,7 @@ import {
|
|
|
9
9
|
stableStringify,
|
|
10
10
|
} from "../json.ts";
|
|
11
11
|
import {
|
|
12
|
+
boundedUtf8,
|
|
12
13
|
bytes,
|
|
13
14
|
clone,
|
|
14
15
|
messageRole,
|
|
@@ -41,6 +42,7 @@ import {
|
|
|
41
42
|
validateFoldForest,
|
|
42
43
|
} from "./persistence.ts";
|
|
43
44
|
import {
|
|
45
|
+
ACTIVE_CONTEXT_POLICY,
|
|
44
46
|
activeContextBrand,
|
|
45
47
|
activeContextSource,
|
|
46
48
|
CONSOLIDATION_WIDTH_THRESHOLD,
|
|
@@ -849,14 +851,8 @@ export function foldStatusRow(fold: ActiveFold, state: ActiveContextState, snaps
|
|
|
849
851
|
};
|
|
850
852
|
}
|
|
851
853
|
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
state: ActiveContextState,
|
|
855
|
-
offset = 0,
|
|
856
|
-
limit = 40,
|
|
857
|
-
maximumChapterSourceRefs = Number.MAX_SAFE_INTEGER,
|
|
858
|
-
): Record<string, unknown> {
|
|
859
|
-
const roots = orderedRoots(state, snapshot).map((item) => item.fold.id);
|
|
854
|
+
/** Every fold, nested ones included, in transcript order with each parent before its children. */
|
|
855
|
+
export function orderedFoldTree(state: ActiveContextState, snapshot: ActiveContextSnapshot): ActiveFold[] {
|
|
860
856
|
const byId = foldMap(state);
|
|
861
857
|
const ordered: ActiveFold[] = [];
|
|
862
858
|
const seen = new Set<string>();
|
|
@@ -867,7 +863,42 @@ export function activeContextStatus(
|
|
|
867
863
|
ordered.push(fold);
|
|
868
864
|
for (const child of childFoldIds(fold)) visit(child);
|
|
869
865
|
};
|
|
870
|
-
for (const root of
|
|
866
|
+
for (const root of orderedRoots(state, snapshot)) visit(root.fold.id);
|
|
867
|
+
return ordered;
|
|
868
|
+
}
|
|
869
|
+
|
|
870
|
+
export function foldDepth(state: ActiveContextState, fold: ActiveFold): number {
|
|
871
|
+
const byId = foldMap(state);
|
|
872
|
+
let depth = 0;
|
|
873
|
+
for (let parentId = fold.parentId; parentId; parentId = byId.get(parentId)?.parentId ?? null) depth += 1;
|
|
874
|
+
return depth;
|
|
875
|
+
}
|
|
876
|
+
|
|
877
|
+
export function foldTreeDetail(
|
|
878
|
+
snapshot: ActiveContextSnapshot,
|
|
879
|
+
state: ActiveContextState,
|
|
880
|
+
): Array<Record<string, unknown>> {
|
|
881
|
+
return orderedFoldTree(state, snapshot).map((fold) => ({
|
|
882
|
+
id: fold.id,
|
|
883
|
+
kind: fold.kind,
|
|
884
|
+
depth: foldDepth(state, fold),
|
|
885
|
+
parentId: fold.parentId,
|
|
886
|
+
brief: fold.brief,
|
|
887
|
+
sourceCount: flattenFoldRefs(fold, state).length,
|
|
888
|
+
state: state.expanded.includes(fold.id) ? "expanded" : "folded",
|
|
889
|
+
peekable: true,
|
|
890
|
+
}));
|
|
891
|
+
}
|
|
892
|
+
|
|
893
|
+
export function activeContextStatus(
|
|
894
|
+
snapshot: ActiveContextSnapshot,
|
|
895
|
+
state: ActiveContextState,
|
|
896
|
+
offset = 0,
|
|
897
|
+
limit = 40,
|
|
898
|
+
maximumChapterSourceRefs = Number.MAX_SAFE_INTEGER,
|
|
899
|
+
): Record<string, unknown> {
|
|
900
|
+
const roots = orderedRoots(state, snapshot).map((item) => item.fold.id);
|
|
901
|
+
const ordered = orderedFoldTree(state, snapshot);
|
|
871
902
|
const selected = ordered.slice(offset, offset + limit);
|
|
872
903
|
const protectedKeys = explicitProtectedKeys(state);
|
|
873
904
|
const objects = snapshot.mapped.flatMap((item) => item.ref ? [{
|
|
@@ -1010,3 +1041,51 @@ export function recoverFoldMessages(input: {
|
|
|
1010
1041
|
return clone(message);
|
|
1011
1042
|
});
|
|
1012
1043
|
}
|
|
1044
|
+
|
|
1045
|
+
/**
|
|
1046
|
+
* Ephemeral point read of one fold's exact source at any depth. It recovers the same
|
|
1047
|
+
* SHA-256-verified messages expansion restores, bounded for the reply, and changes no
|
|
1048
|
+
* projection: the fold stays collapsed and the durable state is untouched.
|
|
1049
|
+
*/
|
|
1050
|
+
export function peekFoldSource(input: {
|
|
1051
|
+
foldId: string;
|
|
1052
|
+
state: ActiveContextState;
|
|
1053
|
+
entries: Array<Record<string, unknown>>;
|
|
1054
|
+
sessionId: string;
|
|
1055
|
+
maximumBytes?: number;
|
|
1056
|
+
projectEntry?: (entry: Record<string, unknown>) => unknown[];
|
|
1057
|
+
}): Record<string, unknown> {
|
|
1058
|
+
const fold = input.state.folds.find((item) => item.id === input.foldId);
|
|
1059
|
+
if (!fold) throw new Error(`Unknown active-context fold ${input.foldId}`);
|
|
1060
|
+
const messages = recoverFoldMessages({
|
|
1061
|
+
foldId: input.foldId,
|
|
1062
|
+
state: input.state,
|
|
1063
|
+
entries: input.entries,
|
|
1064
|
+
sessionId: input.sessionId,
|
|
1065
|
+
projectEntry: input.projectEntry,
|
|
1066
|
+
});
|
|
1067
|
+
const source = stableStringify(messages);
|
|
1068
|
+
const sourceBytes = bytes(source);
|
|
1069
|
+
const returned = boundedUtf8(source, input.maximumBytes ?? ACTIVE_CONTEXT_POLICY.maxChapterChars);
|
|
1070
|
+
const returnedBytes = bytes(returned);
|
|
1071
|
+
const truncated = returnedBytes < sourceBytes;
|
|
1072
|
+
return {
|
|
1073
|
+
version: 1,
|
|
1074
|
+
action: "peek",
|
|
1075
|
+
id: fold.id,
|
|
1076
|
+
kind: fold.kind,
|
|
1077
|
+
parentId: fold.parentId,
|
|
1078
|
+
depth: foldDepth(input.state, fold),
|
|
1079
|
+
brief: fold.brief,
|
|
1080
|
+
sourceCount: messages.length,
|
|
1081
|
+
sourceSha256: fold.sourceSha256,
|
|
1082
|
+
sourceBytes,
|
|
1083
|
+
returnedBytes,
|
|
1084
|
+
truncated,
|
|
1085
|
+
note: truncated
|
|
1086
|
+
? `Truncated: this reply carries the first ${returnedBytes} of ${sourceBytes} exact source bytes; ` +
|
|
1087
|
+
`expand ${fold.id} to restore the complete source in place.`
|
|
1088
|
+
: "Complete exact source; the fold stayed collapsed and no projection changed.",
|
|
1089
|
+
source: returned,
|
|
1090
|
+
};
|
|
1091
|
+
}
|
|
@@ -22,6 +22,7 @@ import {
|
|
|
22
22
|
EXPAND_LEASE_GENERATIONS,
|
|
23
23
|
MAX_ADVISORY_DELIVERIES_PER_MILESTONE,
|
|
24
24
|
MAX_EXPAND_LEASES,
|
|
25
|
+
SURFACING_MAX_LOG_RECORDS,
|
|
25
26
|
} from "./policy.ts";
|
|
26
27
|
import type {
|
|
27
28
|
ActiveContextCheckpointV2,
|
|
@@ -36,6 +37,7 @@ import type {
|
|
|
36
37
|
FoldRecordEntry,
|
|
37
38
|
FoldRecordRef,
|
|
38
39
|
PreparedFold,
|
|
40
|
+
SurfacingRecord,
|
|
39
41
|
} from "./policy.ts";
|
|
40
42
|
|
|
41
43
|
export const ACTIVE_FOLD_KEYS = [
|
|
@@ -49,6 +51,8 @@ export const PREPARED_FOLD_KEYS = [
|
|
|
49
51
|
export const ACTIVE_STATE_KEYS = ["version", "sessionId", "revision", "folds", "expanded", "protected"] as const;
|
|
50
52
|
export const FOLD_RECORD_REF_KEYS = ["id", "sha256"] as const;
|
|
51
53
|
export const FOLD_RECORD_ENTRY_KEYS = ["version", "sessionId", "foldId", "recordSha256", "fold"] as const;
|
|
54
|
+
export const SURFACING_RECORD_KEYS = ["source", "id", "score", "ordinal", "outcome"] as const;
|
|
55
|
+
export const SURFACING_OUTCOMES = Object.freeze(["shown", "accept", "reject"] as const);
|
|
52
56
|
export const STATE_CHECKPOINT_V2_KEYS = [
|
|
53
57
|
"version", "kind", "sessionId", "revision", "foldRefs", "expanded", "protected", "prepared", "stateSha256",
|
|
54
58
|
] as const;
|
|
@@ -67,6 +71,24 @@ export function validTokensSinceToolFold(value: unknown): value is number {
|
|
|
67
71
|
return Number.isSafeInteger(value) && Number(value) >= 0;
|
|
68
72
|
}
|
|
69
73
|
|
|
74
|
+
export function validSurfacingRecord(value: unknown): value is SurfacingRecord {
|
|
75
|
+
if (!exactRecord(value, SURFACING_RECORD_KEYS)) return false;
|
|
76
|
+
const score = ownValue(value, "score");
|
|
77
|
+
return typeof ownValue(value, "source") === "string" && Boolean(ownValue(value, "source")) &&
|
|
78
|
+
typeof ownValue(value, "id") === "string" && Boolean(ownValue(value, "id")) &&
|
|
79
|
+
typeof score === "number" && Number.isFinite(score) && Number(score) >= 0 && Number(score) <= 1 &&
|
|
80
|
+
Number.isSafeInteger(ownValue(value, "ordinal")) && Number(ownValue(value, "ordinal")) >= 0 &&
|
|
81
|
+
SURFACING_OUTCOMES.includes(ownValue(value, "outcome") as SurfacingRecord["outcome"]);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
export function parseSurfacingLog(value: unknown): SurfacingRecord[] {
|
|
85
|
+
const records = denseOwnArrayValues(value);
|
|
86
|
+
if (!records || records.length > SURFACING_MAX_LOG_RECORDS || !records.every(validSurfacingRecord)) {
|
|
87
|
+
throw new Error("Invalid active-context surfacing log");
|
|
88
|
+
}
|
|
89
|
+
return clone(records) as SurfacingRecord[];
|
|
90
|
+
}
|
|
91
|
+
|
|
70
92
|
export function parseLeases(value: unknown, foldIds?: ReadonlySet<string>): Record<string, number> {
|
|
71
93
|
if (!isPlainRecord(value)) throw new Error("Invalid active-context expand leases");
|
|
72
94
|
const entries = Object.entries(value);
|
|
@@ -235,11 +257,13 @@ export function parseActiveContextState(
|
|
|
235
257
|
const hasTokensSinceToolFold = recordLike &&
|
|
236
258
|
Object.prototype.hasOwnProperty.call(value, "tokensSinceToolFold");
|
|
237
259
|
const hasLeases = recordLike && Object.prototype.hasOwnProperty.call(value, "leases");
|
|
260
|
+
const hasSurfacing = recordLike && Object.prototype.hasOwnProperty.call(value, "surfacing");
|
|
238
261
|
const extraKeys = [
|
|
239
262
|
...(hasPrepared ? ["prepared"] : []),
|
|
240
263
|
...(hasAdvisory ? ["advisory"] : []),
|
|
241
264
|
...(hasTokensSinceToolFold ? ["tokensSinceToolFold"] : []),
|
|
242
265
|
...(hasLeases ? ["leases"] : []),
|
|
266
|
+
...(hasSurfacing ? ["surfacing"] : []),
|
|
243
267
|
];
|
|
244
268
|
if (!exactRecord(value, [...ACTIVE_STATE_KEYS, ...extraKeys])) throw new Error("Invalid active-context state keys");
|
|
245
269
|
const folds = denseOwnArrayValues(ownValue(value, "folds"));
|
|
@@ -265,8 +289,9 @@ export function parseActiveContextState(
|
|
|
265
289
|
throw new Error("Invalid active-context tool-fold cadence");
|
|
266
290
|
}
|
|
267
291
|
const leases = hasLeases ? parseLeases(ownValue(value, "leases"), ids) : {};
|
|
292
|
+
const surfacing = hasSurfacing ? parseSurfacingLog(ownValue(value, "surfacing")) : [];
|
|
268
293
|
const source = clone(value) as unknown as ActiveContextState;
|
|
269
|
-
// Provenance normalization is presentation-only
|
|
294
|
+
// Provenance normalization is presentation-only; never mutate a durable
|
|
270
295
|
// content-addressed fold record: changing its bytes causes the next re-persist
|
|
271
296
|
// to report a conflicting durable fold and suspend automatic management.
|
|
272
297
|
return {
|
|
@@ -280,6 +305,9 @@ export function parseActiveContextState(
|
|
|
280
305
|
? Number(ownValue(value, "tokensSinceToolFold"))
|
|
281
306
|
: 0,
|
|
282
307
|
leases,
|
|
308
|
+
// Omitted when empty so every state written before surfacing existed keeps its
|
|
309
|
+
// exact digest; the replay digest is order- and presence-sensitive by design.
|
|
310
|
+
...(surfacing.length ? { surfacing } : {}),
|
|
283
311
|
...(hasAdvisory
|
|
284
312
|
? { advisory: clone(source.advisory!) }
|
|
285
313
|
: defaultAdvisory ? { advisory: { highWater: 0, delivered: {} } } : {}),
|
|
@@ -355,6 +383,7 @@ export function sameStateProjection(left: ActiveContextState, right: ActiveConte
|
|
|
355
383
|
const normalizedState = { ...clone(value), revision: 0 } as Partial<ActiveContextState>;
|
|
356
384
|
if (normalizedState.tokensSinceToolFold === 0) delete normalizedState.tokensSinceToolFold;
|
|
357
385
|
if (normalizedState.leases && Object.keys(normalizedState.leases).length === 0) delete normalizedState.leases;
|
|
386
|
+
if (normalizedState.surfacing && normalizedState.surfacing.length === 0) delete normalizedState.surfacing;
|
|
358
387
|
return normalizedState;
|
|
359
388
|
};
|
|
360
389
|
return stableStringify(normalized(left)) === stableStringify(normalized(right));
|
|
@@ -395,6 +424,7 @@ export function validateV2ProjectionFields(
|
|
|
395
424
|
advisoryValue: unknown,
|
|
396
425
|
tokensSinceToolFoldValue: unknown,
|
|
397
426
|
leasesValue: unknown,
|
|
427
|
+
surfacingValue?: unknown,
|
|
398
428
|
): {
|
|
399
429
|
expanded: string[];
|
|
400
430
|
protected: EvidenceRef[];
|
|
@@ -402,6 +432,7 @@ export function validateV2ProjectionFields(
|
|
|
402
432
|
advisory?: NonNullable<ActiveContextState["advisory"]>;
|
|
403
433
|
tokensSinceToolFold: number;
|
|
404
434
|
leases: Record<string, number>;
|
|
435
|
+
surfacing: SurfacingRecord[];
|
|
405
436
|
} {
|
|
406
437
|
const expanded = denseOwnArrayValues(expandedValue);
|
|
407
438
|
const protectedRefs = denseOwnArrayValues(protectedValue);
|
|
@@ -419,6 +450,7 @@ export function validateV2ProjectionFields(
|
|
|
419
450
|
throw new Error("Invalid active-context v2 tool-fold cadence");
|
|
420
451
|
}
|
|
421
452
|
const leases = leasesValue === undefined ? {} : parseLeases(leasesValue);
|
|
453
|
+
const surfacing = surfacingValue === undefined ? [] : parseSurfacingLog(surfacingValue);
|
|
422
454
|
return {
|
|
423
455
|
expanded: clone(expanded) as string[],
|
|
424
456
|
protected: clone(protectedRefs) as EvidenceRef[],
|
|
@@ -428,6 +460,7 @@ export function validateV2ProjectionFields(
|
|
|
428
460
|
}),
|
|
429
461
|
tokensSinceToolFold: tokensSinceToolFoldValue === undefined ? 0 : Number(tokensSinceToolFoldValue),
|
|
430
462
|
leases,
|
|
463
|
+
surfacing,
|
|
431
464
|
};
|
|
432
465
|
}
|
|
433
466
|
|
|
@@ -439,10 +472,13 @@ export function parseActiveContextStateV2(value: unknown, sessionId: string): Ac
|
|
|
439
472
|
Object.prototype.hasOwnProperty.call(value, "tokensSinceToolFold"));
|
|
440
473
|
const hasLeases = Boolean(value && typeof value === "object" &&
|
|
441
474
|
Object.prototype.hasOwnProperty.call(value, "leases"));
|
|
475
|
+
const hasSurfacing = Boolean(value && typeof value === "object" &&
|
|
476
|
+
Object.prototype.hasOwnProperty.call(value, "surfacing"));
|
|
442
477
|
const optionalKeys = [
|
|
443
478
|
...(hasAdvisory ? ["advisory"] : []),
|
|
444
479
|
...(hasTokensSinceToolFold ? ["tokensSinceToolFold"] : []),
|
|
445
480
|
...(hasLeases ? ["leases"] : []),
|
|
481
|
+
...(hasSurfacing ? ["surfacing"] : []),
|
|
446
482
|
];
|
|
447
483
|
const checkpoint = kind === "checkpoint" &&
|
|
448
484
|
exactRecord(value, [...STATE_CHECKPOINT_V2_KEYS, ...optionalKeys]);
|
|
@@ -457,6 +493,7 @@ export function parseActiveContextStateV2(value: unknown, sessionId: string): Ac
|
|
|
457
493
|
validateV2ProjectionFields(
|
|
458
494
|
ownValue(value, "expanded"), ownValue(value, "protected"), ownValue(value, "prepared"),
|
|
459
495
|
ownValue(value, "advisory"), ownValue(value, "tokensSinceToolFold"), ownValue(value, "leases"),
|
|
496
|
+
ownValue(value, "surfacing"),
|
|
460
497
|
);
|
|
461
498
|
if (checkpoint) {
|
|
462
499
|
const refs = denseOwnArrayValues(ownValue(value, "foldRefs"));
|
|
@@ -521,7 +558,7 @@ export function stateFromFoldRefs(
|
|
|
521
558
|
wire: Pick<
|
|
522
559
|
ActiveContextCheckpointV2,
|
|
523
560
|
"sessionId" | "revision" | "expanded" | "protected" | "prepared" | "advisory" |
|
|
524
|
-
"tokensSinceToolFold" | "leases"
|
|
561
|
+
"tokensSinceToolFold" | "leases" | "surfacing"
|
|
525
562
|
>,
|
|
526
563
|
refs: FoldRecordRef[],
|
|
527
564
|
records: Map<string, FoldRecordEntry>,
|
|
@@ -541,6 +578,7 @@ export function stateFromFoldRefs(
|
|
|
541
578
|
protected: clone(wire.protected),
|
|
542
579
|
tokensSinceToolFold: wire.tokensSinceToolFold ?? 0,
|
|
543
580
|
leases: clone(wire.leases ?? {}),
|
|
581
|
+
...(wire.surfacing?.length ? { surfacing: clone(wire.surfacing) } : {}),
|
|
544
582
|
...(wire.advisory === undefined ? {} : { advisory: clone(wire.advisory) }),
|
|
545
583
|
...(wire.prepared === null || wire.prepared === undefined ? {} : { prepared: clone(wire.prepared) }),
|
|
546
584
|
};
|
|
@@ -690,6 +728,7 @@ export function makeStateCheckpoint(state: ActiveContextState): ActiveContextChe
|
|
|
690
728
|
prepared: state.prepared ? clone(state.prepared) : null,
|
|
691
729
|
tokensSinceToolFold: state.tokensSinceToolFold,
|
|
692
730
|
leases: clone(state.leases),
|
|
731
|
+
...(state.surfacing?.length ? { surfacing: clone(state.surfacing) } : {}),
|
|
693
732
|
...(state.advisory ? { advisory: clone(state.advisory) } : {}),
|
|
694
733
|
stateSha256: semanticStateSha256(state),
|
|
695
734
|
}, state.sessionId) as ActiveContextCheckpointV2;
|
|
@@ -724,6 +763,7 @@ export function makeStateDelta(previous: ActiveContextState, next: ActiveContext
|
|
|
724
763
|
prepared: next.prepared ? clone(next.prepared) : null,
|
|
725
764
|
tokensSinceToolFold: next.tokensSinceToolFold,
|
|
726
765
|
leases: clone(next.leases),
|
|
766
|
+
...(next.surfacing?.length ? { surfacing: clone(next.surfacing) } : {}),
|
|
727
767
|
...(next.advisory ? { advisory: clone(next.advisory) } : {}),
|
|
728
768
|
stateSha256: semanticStateSha256(next),
|
|
729
769
|
}, next.sessionId) as ActiveContextDeltaV2;
|
|
@@ -738,6 +778,7 @@ export function protectionSha256(state: Pick<ActiveContextState, "protected">):
|
|
|
738
778
|
}
|
|
739
779
|
|
|
740
780
|
export const ADVISORY_BUDGETS: Readonly<Record<AdvisoryMilestone, number>> = Object.freeze({
|
|
781
|
+
orientation: 1,
|
|
741
782
|
notice: 2,
|
|
742
783
|
tools: 2,
|
|
743
784
|
chapters: 2,
|
package/extensions/lib/policy.ts
CHANGED
|
@@ -8,6 +8,9 @@ export const DEFAULT_ACTIVE_CONTEXT_COMMAND_NAMES = Object.freeze({
|
|
|
8
8
|
status: "context",
|
|
9
9
|
fold: "fold-context",
|
|
10
10
|
});
|
|
11
|
+
export const GUIDANCE_PROFILES = Object.freeze(["pressure", "curation", "minimal"] as const);
|
|
12
|
+
export type GuidanceProfile = typeof GUIDANCE_PROFILES[number];
|
|
13
|
+
export const DEFAULT_GUIDANCE_PROFILE: GuidanceProfile = "pressure";
|
|
11
14
|
|
|
12
15
|
export function entryTypeNamespace(entryTypePrefix: string): string {
|
|
13
16
|
const suffix = "-active-context";
|
|
@@ -37,7 +40,7 @@ export const NATIVE_COMPACTION_DECISION_ENTRY = `${DEFAULT_ENTRY_NAMESPACE}-nati
|
|
|
37
40
|
export const PROVIDER_CONTEXT_MEASUREMENT_ENTRY = `${DEFAULT_ENTRY_NAMESPACE}-provider-context-measurement`;
|
|
38
41
|
export const ACTIVE_CONTEXT_STATUS_KEY = DEFAULT_ACTIVE_CONTEXT_ENTRY_TYPE_PREFIX;
|
|
39
42
|
export const ACTIVE_CONTEXT_TOOL_ACTIONS = Object.freeze([
|
|
40
|
-
"status", "fold", "expand", "refold", "protect", "unprotect",
|
|
43
|
+
"status", "peek", "fold", "expand", "refold", "protect", "unprotect",
|
|
41
44
|
] as const);
|
|
42
45
|
export type ActiveContextToolAction = typeof ACTIVE_CONTEXT_TOOL_ACTIONS[number];
|
|
43
46
|
export const USER_RESCUE_MAX_SOURCE_CHARS = 512_000;
|
|
@@ -49,8 +52,27 @@ export const MAX_EXPAND_LEASES = 64;
|
|
|
49
52
|
export const CONSOLIDATION_WIDTH_THRESHOLD = 10;
|
|
50
53
|
export const MAX_ADVISORY_DELIVERIES_PER_MILESTONE = 16;
|
|
51
54
|
|
|
55
|
+
// Ephemeral surfacing structure. These stay INTERNAL constants rather than public
|
|
56
|
+
// options: they are the knobs the surfacing experiment is meant to settle, and an
|
|
57
|
+
// option surface fixed before the accept/reject data exists would freeze a guess.
|
|
58
|
+
export const DEFAULT_SURFACING_ENABLED = true;
|
|
59
|
+
export const SURFACING_TOP_K = 3;
|
|
60
|
+
export const SURFACING_MIN_SCORE = 0.30;
|
|
61
|
+
export const SURFACING_CHAR_BUDGET = 1_000;
|
|
62
|
+
export const SURFACING_COOLDOWN_ORDINALS = 8;
|
|
63
|
+
export const SURFACING_OUTCOME_WINDOW_ORDINALS = 12;
|
|
64
|
+
export const SURFACING_RECENT_TASK_SPANS = 6;
|
|
65
|
+
export const SURFACING_MAX_TASK_CHARS = 12_000;
|
|
66
|
+
export const SURFACING_MAX_LOG_RECORDS = 128;
|
|
67
|
+
export const SURFACING_LEXICAL_WEIGHT = 0.80;
|
|
68
|
+
export const SURFACING_RECENCY_WEIGHT = 0.14;
|
|
69
|
+
export const SURFACING_DEPTH_WEIGHT = 0.06;
|
|
70
|
+
export const SURFACING_MAX_DEPTH = 4;
|
|
71
|
+
export const SURFACING_MAX_TEXT_CHARS = 220;
|
|
72
|
+
export const SURFACING_SOURCE_ID = "fold-brief";
|
|
73
|
+
|
|
52
74
|
// Conservative LOWER bound on UTF-8 bytes per provider token, used only to cap
|
|
53
|
-
// the protected byte tail as a share of a small window
|
|
75
|
+
// the protected byte tail as a share of a small window; never to estimate usage.
|
|
54
76
|
export const BYTES_PER_TOKEN_FLOOR = 2;
|
|
55
77
|
|
|
56
78
|
export const ACTIVE_CONTEXT_POLICY = Object.freeze({
|
|
@@ -120,6 +142,44 @@ export interface PreparedFold {
|
|
|
120
142
|
fold: ActiveFold;
|
|
121
143
|
}
|
|
122
144
|
|
|
145
|
+
export type SurfacingOutcome = "shown" | "accept" | "reject";
|
|
146
|
+
|
|
147
|
+
/** One durable surfacing observation: what was shown, how it scored, and what the agent did. */
|
|
148
|
+
export interface SurfacingRecord {
|
|
149
|
+
source: string;
|
|
150
|
+
id: string;
|
|
151
|
+
score: number;
|
|
152
|
+
ordinal: number;
|
|
153
|
+
outcome: SurfacingOutcome;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
/** One item a suggestion source offers for the shared carrier. */
|
|
157
|
+
export interface SurfacingCandidate {
|
|
158
|
+
source: string;
|
|
159
|
+
id: string;
|
|
160
|
+
text: string;
|
|
161
|
+
route: string;
|
|
162
|
+
alternateRoute?: string;
|
|
163
|
+
/** Transcript position used for recency; higher is more recent. Never a wall clock. */
|
|
164
|
+
position?: number;
|
|
165
|
+
depth?: number;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
export interface SurfacingSuggestion extends SurfacingCandidate {
|
|
169
|
+
score: number;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
export interface SuggestionSourceInput {
|
|
173
|
+
state: ActiveContextState;
|
|
174
|
+
snapshot: ActiveContextSnapshot;
|
|
175
|
+
toolName: string;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
export interface SuggestionSource {
|
|
179
|
+
id: string;
|
|
180
|
+
candidates: (input: SuggestionSourceInput) => SurfacingCandidate[];
|
|
181
|
+
}
|
|
182
|
+
|
|
123
183
|
export interface ActiveContextState {
|
|
124
184
|
version: 1;
|
|
125
185
|
sessionId: string;
|
|
@@ -129,6 +189,7 @@ export interface ActiveContextState {
|
|
|
129
189
|
protected: EvidenceRef[];
|
|
130
190
|
tokensSinceToolFold: number;
|
|
131
191
|
leases: Record<string, number>;
|
|
192
|
+
surfacing?: SurfacingRecord[];
|
|
132
193
|
prepared?: PreparedFold;
|
|
133
194
|
advisory?: {
|
|
134
195
|
highWater: number;
|
|
@@ -161,6 +222,7 @@ export interface ActiveContextCheckpointV2 {
|
|
|
161
222
|
prepared: PreparedFold | null;
|
|
162
223
|
tokensSinceToolFold?: number;
|
|
163
224
|
leases?: Record<string, number>;
|
|
225
|
+
surfacing?: SurfacingRecord[];
|
|
164
226
|
advisory?: NonNullable<ActiveContextState["advisory"]>;
|
|
165
227
|
stateSha256: string;
|
|
166
228
|
}
|
|
@@ -179,6 +241,7 @@ export interface ActiveContextDeltaV2 {
|
|
|
179
241
|
prepared: PreparedFold | null;
|
|
180
242
|
tokensSinceToolFold?: number;
|
|
181
243
|
leases?: Record<string, number>;
|
|
244
|
+
surfacing?: SurfacingRecord[];
|
|
182
245
|
advisory?: NonNullable<ActiveContextState["advisory"]>;
|
|
183
246
|
stateSha256: string;
|
|
184
247
|
}
|
|
@@ -230,4 +293,4 @@ export const READ_ONLY_TOOLS_DEFAULT = new Set([
|
|
|
230
293
|
"read", "grep", "find", "ls",
|
|
231
294
|
]);
|
|
232
295
|
|
|
233
|
-
export type AdvisoryMilestone = "notice" | "tools" | "chapters" | "urgent";
|
|
296
|
+
export type AdvisoryMilestone = "orientation" | "notice" | "tools" | "chapters" | "urgent";
|