Files
resolutionflow/frontend/src/lib/variableResolver.ts
chihlasm ccd06c9ed4 feat: flexible intake — deferred variables + prepared sessions (#103)
* feat: flexible intake — deferred variables + prepared sessions

Remove blocking intake form modal. Variables are now filled inline during
flow execution or pre-filled via prepared sessions. Adds PATCH /sessions/{id}/variables
endpoint, POST /sessions/prepare for session pre-staging, inline variable prompts
in StepDetail, editable Session Variables panel, and "Prepared for You" dashboard section.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: pass treeData directly to startSession to avoid stale state

setTree(treeData) hasn't committed when startSession runs immediately
after, so tree is still null and getStepsFromTree returns []. This
caused the step detail area to render empty on new session start.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* feat: wire PrepareSessionModal entry point in Flow Library

Add "Prepare session" button (clipboard icon) to grid, list, and table
views for procedural/maintenance flows. Clicking fetches tree intake
fields and account members, then opens PrepareSessionModal.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-10 09:49:51 -04:00

102 lines
2.9 KiB
TypeScript

/**
* Frontend-side variable substitution for display during navigation.
*
* - [VAR:name] → replaced with variables[name]
* - [USER_INPUT:prompt] → replaced with variables[prompt]
* - [SAVE_AS:name] → removed from display
*/
export interface UnresolvedVariable {
variableName: string
token: string // e.g. "[VAR:server_name]"
}
export interface ResolveResult {
text: string
unresolvedVariables: UnresolvedVariable[]
}
/**
* Resolve variables in text, replacing tokens with values.
* Missing/empty values are replaced with "N/A".
*/
export function resolveVariables(text: string, variables: Record<string, string>): string {
// Replace [VAR:name] — empty/missing values show "N/A"
let result = text.replace(/\[VAR:([^\]]+)\]/g, (_, name) => {
const key = name.trim()
const value = variables[key]
return value && value.trim() ? value : 'N/A'
})
// Replace [USER_INPUT:prompt]
result = result.replace(/\[USER_INPUT:([^\]]+)\]/g, (_, prompt) => {
const key = prompt.trim()
const value = variables[key]
return value && value.trim() ? value : 'N/A'
})
// Remove [SAVE_AS:name]
result = result.replace(/\[SAVE_AS:[^\]]+\]/g, '')
return result
}
/**
* Resolve variables but also return info about which variables are unresolved.
* Unresolved variables are left as their original tokens in the text (not replaced with N/A).
*/
export function resolveVariablesWithStatus(text: string, variables: Record<string, string>): ResolveResult {
const unresolvedVariables: UnresolvedVariable[] = []
// Replace [VAR:name] — resolved values get substituted, unresolved stay as tokens
let result = text.replace(/\[VAR:([^\]]+)\]/g, (match, name) => {
const key = name.trim()
const value = variables[key]
if (value && value.trim()) {
return value
}
unresolvedVariables.push({ variableName: key, token: match })
return match // Keep original token for inline prompt rendering
})
// Replace [USER_INPUT:prompt]
result = result.replace(/\[USER_INPUT:([^\]]+)\]/g, (match, prompt) => {
const key = prompt.trim()
const value = variables[key]
if (value && value.trim()) {
return value
}
unresolvedVariables.push({ variableName: key, token: match })
return match
})
// Remove [SAVE_AS:name]
result = result.replace(/\[SAVE_AS:[^\]]+\]/g, '')
return { text: result, unresolvedVariables }
}
/**
* Extract all [USER_INPUT:prompt] tokens from text.
* Returns array of prompt strings.
*/
export function extractUserInputPrompts(text: string): string[] {
const prompts: string[] = []
const re = /\[USER_INPUT:([^\]]+)\]/g
let match
while ((match = re.exec(text)) !== null) {
const prompt = match[1].trim()
if (!prompts.includes(prompt)) {
prompts.push(prompt)
}
}
return prompts
}
/**
* Check if text contains any variable tokens.
*/
export function hasVariableTokens(text: string): boolean {
return /\[(USER_INPUT|VAR|SAVE_AS):[^\]]+\]/.test(text)
}