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.
@@ -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
- export function activeContextStatus(
853
- snapshot: ActiveContextSnapshot,
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 roots) visit(root);
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 — never mutate a durable
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,
@@ -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 — never to estimate usage.
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";