@@ -84,9 +84,14 @@ export interface WorkspaceFallback {
8484
8585/** What the failure path learns about the workspace, in one read. */
8686export type FallbackLookup =
87- | { state : "current" ; fallbacks : WorkspaceFallback [ ] }
87+ | { state : "current" ; fallbacks : WorkspaceFallback [ ] ; reason : Reason }
8888 | { state : "relinked" | "unreadable" }
8989
90+ /** Why nothing is served, read once with the snapshot so the note describes the
91+ * same snapshot the lookup did: `none` — no snapshot for the session; a disabled
92+ * reason — the snapshot's own; `served` — enabled. */
93+ type Reason = "none" | "served" | NonNullable < Precedence . Precedence [ "disabledReason" ] >
94+
9095/**
9196 * The workspace-served execute tools for the types a FinOps operation supports, if the
9297 * session is bound to a workspace that serves any. Reads the session's precedence
@@ -97,17 +102,18 @@ export type FallbackLookup =
97102 */
98103export async function workspaceFallbacks ( sessionID : string , supportedTypes : readonly string [ ] ) : Promise < FallbackLookup > {
99104 const precedence = Precedence . forSession ( sessionID )
100- if ( ! precedence ?. enabled ) return { state : "current" , fallbacks : [ ] }
105+ if ( ! precedence ) return { state : "current" , fallbacks : [ ] , reason : "none" }
106+ if ( ! precedence . enabled ) return { state : "current" , fallbacks : [ ] , reason : precedence . disabledReason ?? "served" }
101107 const fallbacks = Precedence . servedInventory ( precedence ) . flatMap ( ( { type, served } ) => {
102108 if ( ! supportedTypes . includes ( type ) ) return [ ]
103109 const execute = served . find ( ( row ) => row . capability === "sql_execute" )
104110 return execute
105111 ? [ { workspaceName : precedence . workspaceName , workspaceId : precedence . workspaceId , type, modelKey : execute . modelKey } ]
106112 : [ ]
107113 } )
108- if ( fallbacks . length === 0 ) return { state : "current" , fallbacks }
114+ if ( fallbacks . length === 0 ) return { state : "current" , fallbacks, reason : "served" }
109115 const state = await Precedence . snapshotState ( precedence )
110- return state === "current" ? { state, fallbacks } : { state }
116+ return state === "current" ? { state, fallbacks, reason : "served" } : { state }
111117}
112118
113119/** The sentence appended to a FinOps failure, or nothing when the workspace serves
@@ -126,8 +132,9 @@ export function workspaceFallbackNote(operation: FinopsOperation, fallbacks: Wor
126132 // The snapshot does not carry a BigQuery integration's location, and the engine
127133 // runs what it is given: the placeholder has to be explained, not left to be sent.
128134 const region = fallbacks . some ( ( f ) => f . type === "bigquery" && SOURCE [ operation ] . bigquery )
129- ? " Replace `<location>` with the BigQuery connection's location (for example `region-us`, `region-eu`); " +
130- "if it is unknown, ask the engine for the connection's details first — the view is not reachable unqualified."
135+ ? " Replace `<location>` with the BigQuery connection's location (for example `us`, `eu`, `us-central1`, " +
136+ "giving `region-us.INFORMATION_SCHEMA…`); if it is unknown, ask the engine for the connection's details " +
137+ "first — the view is not reachable unqualified."
131138 : ""
132139 return (
133140 `This tool only uses warehouse connections configured on this machine, and workspace ${ label } ` +
@@ -141,20 +148,19 @@ export function workspaceFallbackNote(operation: FinopsOperation, fallbacks: Wor
141148 * materialised) says nothing: that is the plain local failure. Uncertainty must say so
142149 * (the precedence module's first claim), so the failure is marked `undetermined` and
143150 * says the workspace could not be consulted — mirroring `check()`'s own cases. */
144- function undeterminedNote ( sessionID : string , lookup : FallbackLookup ) : string | undefined {
151+ function undeterminedNote ( lookup : FallbackLookup ) : string | undefined {
145152 if ( lookup . state === "unreadable" ) {
146153 return "The workspace link could not be read while this call ran, so whether the workspace serves this connection type is unknown."
147154 }
148- if ( lookup . state === "relinked ") {
155+ if ( lookup . state !== "current ") {
149156 return "The workspace binding changed while this call ran, so the previous routing decision no longer applies."
150157 }
151- const precedence = Precedence . forSession ( sessionID )
152158 // No snapshot at all is the same unknown `check()` reports: a caller that never
153159 // resolved tools, or an entry evicted between resolution and this call.
154- if ( ! precedence ) {
160+ if ( lookup . reason === "none" ) {
155161 return "No routing decision was available for this call, so whether the linked workspace serves this connection type is unknown."
156162 }
157- const reason = precedence . disabledReason
163+ const reason = lookup . reason
158164 if ( reason === "unattributed" || reason === "binding-unreadable" || reason === "derive-failed" ) {
159165 return (
160166 "Whether the linked workspace serves this connection type could not be determined this turn " +
@@ -185,7 +191,7 @@ export async function withWorkspaceFallback<T extends { metadata: Record<string,
185191 output : `${ result . output } \n\n${ note } ` ,
186192 }
187193 }
188- const undetermined = undeterminedNote ( sessionID , lookup )
194+ const undetermined = undeterminedNote ( lookup )
189195 if ( ! undetermined ) return result
190196 return {
191197 ...result ,
0 commit comments