pi-advisor-flow 0.9.2-dev.5 → 0.10.1-dev.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-advisor-flow",
3
- "version": "0.9.2-dev.5",
3
+ "version": "0.10.1-dev.1",
4
4
  "description": "Advanced Executor/Advisor flow for Pi, fully configurable and extendable.",
5
5
  "keywords": [
6
6
  "advisor",
@@ -55,7 +55,7 @@
55
55
  "@earendil-works/pi-coding-agent": "1.0.0",
56
56
  "@earendil-works/pi-tui": "1.0.0",
57
57
  "@oxlint/plugins": "1.86.0",
58
- "@types/node": "^26.6.3",
58
+ "@types/node": "^26.6.4",
59
59
  "@typesafe-ai/sdk": "^0.6.0",
60
60
  "bun-types": "1.4.2",
61
61
  "husky": "^9.1.7",
@@ -2,11 +2,13 @@ import type { ExtensionContext } from "@earendil-works/pi-coding-agent";
2
2
 
3
3
  import {
4
4
  advisorEffortRef,
5
+ advisorFallbackModelRef,
5
6
  advisorRef,
6
7
  executorEffortRef,
7
8
  executorRef,
8
9
  getPersistedModelRefs,
9
10
  setAdvisorEffortRef,
11
+ setAdvisorFallbackModelRef,
10
12
  setAdvisorRef,
11
13
  setExecutorEffortRef,
12
14
  setExecutorRef,
@@ -99,6 +101,7 @@ export const prepareActivationModels = async (
99
101
  const selection = await selectAdvisorModels(ctx, {
100
102
  advisor: advisorOverride || persisted.advisor ? advisorRef : "",
101
103
  advisorEffort: advisorEffortRef,
104
+ advisorFallbackModel: advisorFallbackModelRef,
102
105
  executor:
103
106
  executorOverride || plan.pendingExecutor || persisted.executor
104
107
  ? executorRef
@@ -111,6 +114,7 @@ export const prepareActivationModels = async (
111
114
  return;
112
115
  }
113
116
  setAdvisorRef(selection.advisor);
117
+ setAdvisorFallbackModelRef(selection.advisorFallbackModel);
114
118
  setAdvisorEffortRef(selection.advisorEffort);
115
119
  setExecutorRef(selection.executor);
116
120
  setExecutorEffortRef(selection.executorEffort);
@@ -14,8 +14,10 @@ import { notify } from "./runtime.ts";
14
14
  import type { CommandRuntime, ManualAdvisorProgressState } from "./types.ts";
15
15
 
16
16
  interface ManualResultDetails {
17
+ adviceId?: string;
17
18
  advisor: string;
18
19
  agentRulesBytes?: number;
20
+ followUp?: boolean;
19
21
  preferenceBytes?: number;
20
22
  question?: string;
21
23
  text: string;
@@ -50,35 +52,42 @@ export const startManualConsultation = async (
50
52
  const finishHerdrActivity = runtime.herdrActivity.start();
51
53
  let scoutDetails: ScoutToolDetails | undefined;
52
54
  try {
53
- const { adviceId, agentRulesBytes, markdown, preferenceBytes, usage } =
54
- await runtime.requestAdvisor(
55
- ctx,
56
- question,
57
- controller.signal,
58
- (thinking, text) => {
59
- if (controller.signal.aborted) {
60
- return;
61
- }
55
+ const {
56
+ adviceId,
57
+ agentRulesBytes,
58
+ followUp,
59
+ markdown,
60
+ model,
61
+ preferenceBytes,
62
+ usage,
63
+ } = await runtime.requestAdvisor(
64
+ ctx,
65
+ question,
66
+ controller.signal,
67
+ (thinking, text) => {
68
+ if (controller.signal.aborted) {
69
+ return;
70
+ }
71
+ progress.phase = "active";
72
+ progress.thinking = thinking;
73
+ progress.text = text;
74
+ runtime.requestManualRender(ctx);
75
+ },
76
+ (event) => {
77
+ if (!controller.signal.aborted) {
78
+ runtime.scoutStatus.update(ctx, scoutStatusToken, event);
79
+ scoutDetails = appendScoutLifecycleEntry(
80
+ runtime.pi,
81
+ event,
82
+ scoutDetails
83
+ );
84
+ progress.scout = scoutDetails;
62
85
  progress.phase = "active";
63
- progress.thinking = thinking;
64
- progress.text = text;
65
86
  runtime.requestManualRender(ctx);
66
- },
67
- (event) => {
68
- if (!controller.signal.aborted) {
69
- runtime.scoutStatus.update(ctx, scoutStatusToken, event);
70
- scoutDetails = appendScoutLifecycleEntry(
71
- runtime.pi,
72
- event,
73
- scoutDetails
74
- );
75
- progress.scout = scoutDetails;
76
- progress.phase = "active";
77
- runtime.requestManualRender(ctx);
78
- }
79
- },
80
- gitContext
81
- );
87
+ }
88
+ },
89
+ gitContext
90
+ );
82
91
  if (controller.signal.aborted) {
83
92
  return;
84
93
  }
@@ -87,8 +96,9 @@ export const startManualConsultation = async (
87
96
  runtime.advisorSessionState.recordInvocation({
88
97
  cost: advisorUsageCost(usage),
89
98
  executionEffect: "continued",
99
+ followUp,
90
100
  kind: "markdown",
91
- model: advisorRef,
101
+ model: model ?? advisorRef,
92
102
  trigger: "manual",
93
103
  usage,
94
104
  });
@@ -105,8 +115,10 @@ export const startManualConsultation = async (
105
115
  }
106
116
  runtime.updateAdvisorUsageStatus(ctx);
107
117
  const details: ManualResultDetails = {
108
- advisor: advisorRef,
118
+ adviceId,
119
+ advisor: model ?? advisorRef,
109
120
  agentRulesBytes,
121
+ followUp,
110
122
  preferenceBytes,
111
123
  question,
112
124
  text: markdown,
@@ -1,10 +1,12 @@
1
1
  import {
2
2
  advisorEffortRef,
3
+ advisorFallbackModelRef,
3
4
  advisorRef,
4
5
  executorEffortRef,
5
6
  executorRef,
6
7
  getPersistedModelRefs,
7
8
  setAdvisorEffortRef,
9
+ setAdvisorFallbackModelRef,
8
10
  setAdvisorRef,
9
11
  setExecutorEffortRef,
10
12
  setExecutorRef,
@@ -24,7 +26,7 @@ export const registerModelCommands = (runtime: CommandRuntime) => {
24
26
 
25
27
  runtime.pi.registerCommand("advisor-models", {
26
28
  description:
27
- "Select and persist the Executor and Advisor models with reasoning levels",
29
+ "Select and persist the Executor, Advisor, and optional fallback models with reasoning levels",
28
30
  handler: async (_args, ctx) => {
29
31
  if (!(loadCommandConfig(ctx) && ctx.hasUI)) {
30
32
  return;
@@ -36,6 +38,7 @@ export const registerModelCommands = (runtime: CommandRuntime) => {
36
38
  const selection = await selectAdvisorModels(ctx, {
37
39
  advisor: persisted.advisor ? advisorRef : "",
38
40
  advisorEffort: advisorEffortRef,
41
+ advisorFallbackModel: advisorFallbackModelRef,
39
42
  executor:
40
43
  runtime.pendingExecutorModelRef ??
41
44
  (persisted.executor ? executorRef : ""),
@@ -49,6 +52,7 @@ export const registerModelCommands = (runtime: CommandRuntime) => {
49
52
 
50
53
  setExecutorRef(selection.executor);
51
54
  setAdvisorRef(selection.advisor);
55
+ setAdvisorFallbackModelRef(selection.advisorFallbackModel);
52
56
  setExecutorEffortRef(selection.executorEffort);
53
57
  setAdvisorEffortRef(selection.advisorEffort);
54
58
 
@@ -1,6 +1,7 @@
1
1
  import type { ExtensionContext } from "@earendil-works/pi-coding-agent";
2
2
 
3
3
  import { SearchableModelSelector } from "../ui/model-selector.ts";
4
+ import { FALLBACK_ADVISOR_MODEL_DISABLED } from "../ui/types.ts";
4
5
  import {
5
6
  effortChoices,
6
7
  getAvailableModelRefs,
@@ -10,6 +11,7 @@ import {
10
11
  export interface AdvisorModelSelection {
11
12
  advisor: string;
12
13
  advisorEffort: string | undefined;
14
+ advisorFallbackModel: string | undefined;
13
15
  executor: string;
14
16
  executorEffort: string | undefined;
15
17
  }
@@ -17,6 +19,7 @@ export interface AdvisorModelSelection {
17
19
  export interface AdvisorModelPickerOptions {
18
20
  advisor: string | undefined;
19
21
  advisorEffort: string | undefined;
22
+ advisorFallbackModel: string | undefined;
20
23
  executor: string | undefined;
21
24
  executorEffort: string | undefined;
22
25
  selectAdvisor: boolean;
@@ -46,7 +49,13 @@ export const selectAdvisorModels = async (
46
49
  )
47
50
  ),
48
51
  ];
49
- let { advisor, advisorEffort, executor, executorEffort } = options;
52
+ let {
53
+ advisor,
54
+ advisorEffort,
55
+ advisorFallbackModel,
56
+ executor,
57
+ executorEffort,
58
+ } = options;
50
59
 
51
60
  if (options.selectExecutor) {
52
61
  const selectedExecutor = await ctx.ui.custom<string | undefined>(
@@ -104,8 +113,41 @@ export const selectAdvisorModels = async (
104
113
  advisorEffort = selectedEffort(selectedAdvisorEffort);
105
114
  }
106
115
 
116
+ const selectedFallback = await ctx.ui.custom<string | undefined>(
117
+ (tui, theme, keybindings, done) =>
118
+ new SearchableModelSelector({
119
+ allOptions: [
120
+ FALLBACK_ADVISOR_MODEL_DISABLED,
121
+ ...new Set([
122
+ ...allOptions,
123
+ ...(refs || !advisorFallbackModel ? [] : [advisorFallbackModel]),
124
+ ]),
125
+ ],
126
+ currentOption: advisorFallbackModel ?? FALLBACK_ADVISOR_MODEL_DISABLED,
127
+ keybindings,
128
+ onCancel: () => done(undefined),
129
+ onSelect: done,
130
+ theme,
131
+ title: "Select Fallback Advisor Model (optional)",
132
+ tui,
133
+ })
134
+ );
135
+ if (selectedFallback === undefined) {
136
+ return undefined;
137
+ }
138
+ advisorFallbackModel =
139
+ selectedFallback === FALLBACK_ADVISOR_MODEL_DISABLED
140
+ ? undefined
141
+ : selectedFallback;
142
+
107
143
  if (!(advisor && executor)) {
108
144
  return undefined;
109
145
  }
110
- return { advisor, advisorEffort, executor, executorEffort };
146
+ return {
147
+ advisor,
148
+ advisorEffort,
149
+ advisorFallbackModel,
150
+ executor,
151
+ executorEffort,
152
+ };
111
153
  };
@@ -77,7 +77,15 @@ class CommandRuntime implements CommandRuntimeContract {
77
77
  undefined,
78
78
  undefined,
79
79
  undefined,
80
- onScout
80
+ onScout,
81
+ undefined,
82
+ undefined,
83
+ (adviceId, payload, replacesAdviceId) =>
84
+ this.advisorSessionState.captureFollowUp(
85
+ adviceId,
86
+ payload,
87
+ replacesAdviceId
88
+ )
81
89
  ));
82
90
  }
83
91
 
@@ -11,6 +11,7 @@ import {
11
11
  setAdvisorCustomInvocationRef,
12
12
  setAdvisorDisableSameModelRef,
13
13
  setAdvisorEffortRef,
14
+ setAdvisorFallbackModelRef,
14
15
  setAdvisorFailureGateRef,
15
16
  setAdvisorFailureModeRef,
16
17
  setAdvisorGitContextMaxCharsRef,
@@ -69,6 +70,7 @@ const applySessionSettings = (settings: AdvisorSettings) => {
69
70
  );
70
71
  setContextMaxCharsRef(settings.contextMaxChars);
71
72
  setAdvisorPlanGateRef(settings.planGate);
73
+ setAdvisorFallbackModelRef(settings.fallbackModel);
72
74
  setAdvisorFailureGateRef(settings.failureGate);
73
75
  setAdvisorCompletionGateRef(settings.completionGate);
74
76
  setAdvisorDisableSameModelRef(settings.disableSameModel ?? true);
@@ -21,7 +21,9 @@ export type ManualConsult = (
21
21
  adviceId?: string;
22
22
  agentRulesBytes?: number;
23
23
  draftBytes?: number;
24
+ followUp?: boolean;
24
25
  markdown: string;
26
+ model?: string;
25
27
  preferenceBytes?: number;
26
28
  thinkingText: string;
27
29
  usage?: unknown;
@@ -30,6 +30,7 @@ import {
30
30
  setAdvisorPlanGateRef,
31
31
  setAdvisorRedactSecretsRef,
32
32
  setAdvisorRef,
33
+ setAdvisorFallbackModelRef,
33
34
  setAdvisorScoutEnabledRef,
34
35
  setAdvisorScoutTimeoutMsRef,
35
36
  setAdvisorSessionSummaryRef,
@@ -69,6 +70,7 @@ import {
69
70
  export const resetDefaults = () => {
70
71
  setExecutorRef("");
71
72
  setAdvisorRef("");
73
+ setAdvisorFallbackModelRef(undefined);
72
74
  setAdvisorAgentsMdContextRef(true);
73
75
  setPersistedModelRefs(undefined, undefined);
74
76
  setExecutorEffortRef(undefined);
@@ -139,6 +141,10 @@ const applyNonEmptyStringConfig = (
139
141
  export const applyConfig = (config: AdvisorConfig) => {
140
142
  applyNonEmptyStringConfig(config.executor, setExecutorRef);
141
143
  applyNonEmptyStringConfig(config.advisor, setAdvisorRef);
144
+ applyNonEmptyStringConfig(
145
+ config.advisorFallbackModel,
146
+ setAdvisorFallbackModelRef
147
+ );
142
148
  applyNonEmptyStringConfig(config.executorEffort, setExecutorEffortRef);
143
149
  applyNonEmptyStringConfig(config.advisorEffort, setAdvisorEffortRef);
144
150
  applyOptionalConfig(
@@ -10,6 +10,7 @@ import {
10
10
  advisorCustomInvocationRef,
11
11
  advisorDisableSameModelRef,
12
12
  advisorEffortRef,
13
+ advisorFallbackModelRef,
13
14
  advisorFailureGateRef,
14
15
  advisorFailureModeRef,
15
16
  advisorGitContextMaxCharsRef,
@@ -226,6 +227,12 @@ export const CONFIG_SCHEMA = {
226
227
  persisted: true,
227
228
  type: "boolean",
228
229
  },
230
+ advisorFallbackModel: {
231
+ accepted: "a provider/model string",
232
+ current: () => configuredModelRef(advisorFallbackModelRef),
233
+ persisted: true,
234
+ type: "string",
235
+ },
229
236
  advisorGitContext: {
230
237
  accepted: GIT_CONTEXT_LEVELS.join(", "),
231
238
  current: () => advisorGitContextRef,
@@ -25,6 +25,7 @@ import type {
25
25
  // An empty ref means no model has been selected yet.
26
26
  export let executorRef = "";
27
27
  export let advisorRef = "";
28
+ export let advisorFallbackModelRef: string | undefined;
28
29
  export let advisorAgentsMdContextRef = true;
29
30
  let persistedExecutorRef: string | undefined;
30
31
  let persistedAdvisorRef: string | undefined;
@@ -81,6 +82,9 @@ export const setExecutorRef = (ref: string) => {
81
82
  export const setAdvisorRef = (ref: string) => {
82
83
  advisorRef = ref;
83
84
  };
85
+ export const setAdvisorFallbackModelRef = (ref: string | undefined) => {
86
+ advisorFallbackModelRef = ref?.trim() || undefined;
87
+ };
84
88
  export const setAdvisorAgentsMdContextRef = (enabled: boolean) => {
85
89
  advisorAgentsMdContextRef = enabled;
86
90
  };
@@ -250,6 +254,7 @@ export const getAdvisorSettings = () => ({
250
254
  effort: advisorEffortRef,
251
255
  failureGate: advisorFailureGateRef,
252
256
  failureMode: advisorFailureModeRef,
257
+ fallbackModel: advisorFallbackModelRef,
253
258
  gitContext: advisorGitContextRef,
254
259
  gitContextMaxChars: advisorGitContextMaxCharsRef,
255
260
  herdrIntegration: advisorHerdrIntegrationRef,
@@ -60,6 +60,7 @@ export const GATE_FAILURE_MODES: GateFailureMode[] = [
60
60
 
61
61
  export interface AdvisorConfig {
62
62
  advisor?: string;
63
+ advisorFallbackModel?: string;
63
64
  advisorAgentsMdContext?: boolean;
64
65
  advisorAutoLoopGate?: boolean;
65
66
  advisorBlockOnBlocked?: boolean;
package/src/config.ts CHANGED
@@ -8,6 +8,7 @@ export {
8
8
  advisorCompletionGateRef,
9
9
  advisorCustomInvocationRef,
10
10
  advisorEffortRef,
11
+ advisorFallbackModelRef,
11
12
  advisorFailureGateRef,
12
13
  advisorFailureModeRef,
13
14
  advisorGitContextMaxCharsRef,
@@ -42,6 +43,7 @@ export {
42
43
  setAdvisorCompletionGateRef,
43
44
  setAdvisorCustomInvocationRef,
44
45
  setAdvisorEffortRef,
46
+ setAdvisorFallbackModelRef,
45
47
  setAdvisorFailureGateRef,
46
48
  setAdvisorFailureModeRef,
47
49
  setAdvisorGitContextMaxCharsRef,
@@ -0,0 +1,85 @@
1
+ import type { Message } from "@earendil-works/pi-ai/compat";
2
+
3
+ export const FOLLOW_UP_TTL_MS = 5 * 60 * 1000;
4
+ export const FOLLOW_UP_MAX_DEPTH = 3;
5
+ export const FOLLOW_UP_MAX_TOOL_CALLS = 3;
6
+
7
+ export interface AdvisorFollowUpPayload {
8
+ depth: number;
9
+ messages: Message[];
10
+ model: string;
11
+ privacyKey: string;
12
+ redacted: boolean;
13
+ systemPrompt: string;
14
+ }
15
+
16
+ export interface AdvisorFollowUpLookup {
17
+ payload?: AdvisorFollowUpPayload;
18
+ reason?: string;
19
+ }
20
+
21
+ interface StoredFollowUpPayload extends AdvisorFollowUpPayload {
22
+ expiresAt: number;
23
+ toolCallsSince: number;
24
+ }
25
+
26
+ const copy = <Value>(value: Value): Value => structuredClone(value);
27
+
28
+ export class AdvisorFollowUpCache {
29
+ readonly #entries = new Map<string, StoredFollowUpPayload>();
30
+ readonly #now: () => number;
31
+
32
+ constructor(now: () => number = Date.now) {
33
+ this.#now = now;
34
+ }
35
+
36
+ clear() {
37
+ this.#entries.clear();
38
+ }
39
+
40
+ advanceToolCall() {
41
+ const now = this.#now();
42
+ for (const [adviceId, entry] of this.#entries) {
43
+ if (
44
+ now >= entry.expiresAt ||
45
+ entry.toolCallsSince + 1 >= FOLLOW_UP_MAX_TOOL_CALLS
46
+ ) {
47
+ this.#entries.delete(adviceId);
48
+ continue;
49
+ }
50
+ entry.toolCallsSince += 1;
51
+ }
52
+ }
53
+
54
+ capture(
55
+ adviceId: string,
56
+ payload: AdvisorFollowUpPayload,
57
+ replacesAdviceId?: string
58
+ ) {
59
+ if (replacesAdviceId) {
60
+ this.#entries.delete(replacesAdviceId);
61
+ }
62
+ this.#entries.set(adviceId, {
63
+ ...copy(payload),
64
+ expiresAt: this.#now() + FOLLOW_UP_TTL_MS,
65
+ toolCallsSince: 0,
66
+ });
67
+ }
68
+
69
+ get(adviceId: string): AdvisorFollowUpLookup {
70
+ const entry = this.#entries.get(adviceId);
71
+ if (!entry) {
72
+ return { reason: "the adviceId is unknown or no longer cached" };
73
+ }
74
+ if (this.#now() >= entry.expiresAt) {
75
+ this.#entries.delete(adviceId);
76
+ return { reason: "the cached payload has expired" };
77
+ }
78
+ if (entry.depth >= FOLLOW_UP_MAX_DEPTH) {
79
+ return {
80
+ reason: `the follow-up chain is limited to ${FOLLOW_UP_MAX_DEPTH} follow-ups`,
81
+ };
82
+ }
83
+ return { payload: copy(entry) };
84
+ }
85
+ }
@@ -1,5 +1,10 @@
1
1
  import { isRecordOf, isString } from "./content-utils.ts";
2
2
  import type { JsonValue } from "./content-utils.ts";
3
+ import { AdvisorFollowUpCache } from "./follow-up.ts";
4
+ import type {
5
+ AdvisorFollowUpLookup,
6
+ AdvisorFollowUpPayload,
7
+ } from "./follow-up.ts";
3
8
  import { AdvisorJevLedgerState } from "./jev/ledger.ts";
4
9
  import type { AdvisorJevUsage } from "./jev/ledger.ts";
5
10
  import {
@@ -22,6 +27,7 @@ type ExecutionEffect = "continued" | "tool-blocked" | "session-blocked";
22
27
  export interface AdvisorInvocationRecord {
23
28
  cost?: number;
24
29
  decision?: GateDecision;
30
+ followUp?: boolean;
25
31
  executionEffect: ExecutionEffect;
26
32
  failure?: string;
27
33
  kind: "markdown" | "gate";
@@ -179,9 +185,14 @@ export class AdvisorSessionState {
179
185
  #consumedCalls = 0;
180
186
  #callReservations = new Set<string>();
181
187
  readonly #jev = new AdvisorJevLedgerState();
188
+ readonly #followUps: AdvisorFollowUpCache;
182
189
  #sessionTurnOrdinal = 0;
183
190
  #turnsSinceConsultation = 0;
184
191
 
192
+ constructor(now?: () => number) {
193
+ this.#followUps = new AdvisorFollowUpCache(now);
194
+ }
195
+
185
196
  resetTask() {
186
197
  this.#repetition = freshRepetition();
187
198
  this.#blockedReason = undefined;
@@ -190,6 +201,7 @@ export class AdvisorSessionState {
190
201
  this.#consumedCalls = 0;
191
202
  this.#callReservations.clear();
192
203
  this.#jev.reset();
204
+ this.#followUps.clear();
193
205
  this.#sessionTurnOrdinal = 0;
194
206
  this.#turnsSinceConsultation = 0;
195
207
  }
@@ -268,6 +280,26 @@ export class AdvisorSessionState {
268
280
  return this.#consumedCalls;
269
281
  }
270
282
 
283
+ advanceFollowUpToolCall() {
284
+ this.#followUps.advanceToolCall();
285
+ }
286
+
287
+ clearFollowUps() {
288
+ this.#followUps.clear();
289
+ }
290
+
291
+ followUpFor(adviceId: string): AdvisorFollowUpLookup {
292
+ return this.#followUps.get(adviceId);
293
+ }
294
+
295
+ captureFollowUp(
296
+ adviceId: string,
297
+ payload: AdvisorFollowUpPayload,
298
+ replacesAdviceId?: string
299
+ ) {
300
+ this.#followUps.capture(adviceId, payload, replacesAdviceId);
301
+ }
302
+
271
303
  /** Counts one completed executor turn on both turn counters. The ordinal
272
304
  * feeds the filter override window and summary positions and is never reset
273
305
  * by anything; turnsSinceConsultation resets on every consultation. */
@@ -495,6 +527,7 @@ export class AdvisorSessionState {
495
527
  `Budget: ${budget}`,
496
528
  `Usage: ${formatAdvisorUsageTotals(totals)}`,
497
529
  `Markdown advice: ${markdown.length} responses (${this.#ledger.draftConsultations} with drafts)`,
530
+ `Follow-ups: ${markdown.filter((item) => item.followUp).length}`,
498
531
  `Outcome reports: ${this.#ledger.outcomes}`,
499
532
  `Gate decisions: ${this.#decisionsLine()}`,
500
533
  `Loop matching: normalized tool signatures; ${this.#repetition.interventions} gate intervention${this.#repetition.interventions === 1 ? "" : "s"}`,
@@ -0,0 +1,28 @@
1
+ import { Type } from "typebox";
2
+
3
+ export const advisorOutputSchema = Type.Object({
4
+ adviceId: Type.Optional(Type.String()),
5
+ advisor: Type.Optional(Type.String()),
6
+ followUp: Type.Optional(Type.Boolean()),
7
+ jev: Type.Optional(
8
+ Type.Object({
9
+ kind: Type.Union([Type.Literal("screened"), Type.Literal("repeat")]),
10
+ reason: Type.String(),
11
+ skipped: Type.Boolean(),
12
+ })
13
+ ),
14
+ skipReason: Type.Optional(Type.String()),
15
+ text: Type.String({
16
+ description: "Advisor Markdown or consultation skip notice.",
17
+ }),
18
+ usage: Type.Optional(
19
+ Type.Object({
20
+ cacheRead: Type.Optional(Type.Number()),
21
+ cacheWrite: Type.Optional(Type.Number()),
22
+ cost: Type.Optional(Type.Number()),
23
+ input: Type.Optional(Type.Number()),
24
+ output: Type.Optional(Type.Number()),
25
+ totalTokens: Type.Optional(Type.Number()),
26
+ })
27
+ ),
28
+ });