Skip to content

Commit 17a02bf

Browse files
committed
Sweep fixes: guarded sandbox writes, canonical token grammars
writeSessionSandboxFile degrades on failure instead of throwing — the CLI invocation it follows already ran, possibly a mutation, and an escaping error reported that success as failure and invited a repeating retry (sim-cli maps the new error outcome to the inline fallback). The deps and lint agent commands now use the executor's own createReferencePattern/createEnvVarPattern with trimmed keys, so what they classify is exactly what the runtime resolves — three private regex re-inventions deleted. Claude-Session: https://claude.ai/code/session_01CgaxNAaeD3taGdghbXn17w
1 parent e3e8b02 commit 17a02bf

4 files changed

Lines changed: 34 additions & 10 deletions

File tree

apps/sim/lib/execution/remote-sandbox/session-files.ts

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,10 @@ export async function readSessionSandboxFile(
5858
}
5959
}
6060

61-
export type SessionFileWrite = { outcome: 'written'; path: string } | { outcome: 'no-session' }
61+
export type SessionFileWrite =
62+
| { outcome: 'written'; path: string }
63+
| { outcome: 'no-session' }
64+
| { outcome: 'error'; detail: string }
6265

6366
export async function writeSessionSandboxFile(
6467
sessionKey: string,
@@ -79,6 +82,18 @@ export async function writeSessionSandboxFile(
7982
}
8083
if (!sandbox) return { outcome: 'no-session' }
8184
const resolved = resolveSessionPath(path)
82-
await sandbox.writeFile(resolved, content)
85+
try {
86+
await sandbox.writeFile(resolved, content)
87+
} catch (error) {
88+
// A failed write must degrade, never throw: the CLI invocation it follows
89+
// already ran — possibly a mutation — and an escaping error here would
90+
// report that successful call as failed and invite a repeating retry.
91+
logger.warn('Session sandbox file write failed', {
92+
sessionKey,
93+
path: resolved,
94+
error: getErrorMessage(error),
95+
})
96+
return { outcome: 'error', detail: getErrorMessage(error) }
97+
}
8398
return { outcome: 'written', path: resolved }
8499
}

apps/sim/lib/mothership/tools/handlers/agent-cli/commands/deps.ts

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import {
55
agentCliOk,
66
} from '@/lib/mothership/tools/handlers/agent-cli/types'
77
import { normalizeName, SPECIAL_REFERENCE_PREFIXES } from '@/executor/constants'
8+
import { createEnvVarPattern, createReferencePattern } from '@/executor/utils/reference-validation'
89

910
/**
1011
* `workflow deps <workflowId> <blockId>` — everything one block consumes, so the
@@ -15,8 +16,10 @@ import { normalizeName, SPECIAL_REFERENCE_PREFIXES } from '@/executor/constants'
1516
* never re-invent resolution semantics.
1617
*/
1718

18-
const TEMPLATE_REF = /<([^<>]+)>/g
19-
const ENV_REF = /\{\{\s*([A-Za-z0-9_-]+)\s*\}\}/g
19+
// The executor's own token grammars — this command must classify exactly what
20+
// the runtime resolves, never a private re-invention of the syntax.
21+
const TEMPLATE_REF = createReferencePattern()
22+
const ENV_REF = createEnvVarPattern()
2023

2124
interface DepView {
2225
token: string
@@ -93,7 +96,8 @@ export const workflowDepsCommand: AgentCliCommand = {
9396
}
9497
}
9598
for (const match of leaf.matchAll(ENV_REF)) {
96-
if (match[1]) envs.add(match[1])
99+
const key = match[1]?.trim()
100+
if (key) envs.add(key)
97101
}
98102
}
99103

apps/sim/lib/mothership/tools/handlers/agent-cli/commands/lint.ts

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import {
88
} from '@/lib/mothership/tools/handlers/agent-cli/types'
99
import { formatWorkflowLintMessage, hasWorkflowLintIssues } from '@/lib/workflows/editing/lint'
1010
import { buildWorkflowLintReport } from '@/lib/workflows/editing/lint-report'
11+
import { createEnvVarPattern } from '@/executor/utils/reference-validation'
1112

1213
/**
1314
* The Go copilot served this as the virtual `workflows/{path}/lint.json` VFS
@@ -42,15 +43,17 @@ export const workflowLintCommand: AgentCliCommand = {
4243
},
4344
}
4445

45-
const ENV_TOKEN = /\{\{\s*([A-Za-z0-9_]+)\s*\}\}/g
46+
// The executor's own env-token grammar (keys trimmed to match its resolution).
47+
const ENV_TOKEN = createEnvVarPattern()
4648

4749
function envTokenNames(value: unknown, out: Map<string, Set<string>>, blockName: string): void {
4850
if (typeof value === 'string') {
4951
for (const match of value.matchAll(ENV_TOKEN)) {
50-
if (!match[1]) continue
51-
const blocks = out.get(match[1]) ?? new Set<string>()
52+
const key = match[1]?.trim()
53+
if (!key) continue
54+
const blocks = out.get(key) ?? new Set<string>()
5255
blocks.add(blockName)
53-
out.set(match[1], blocks)
56+
out.set(key, blocks)
5457
}
5558
} else if (Array.isArray(value)) {
5659
for (const item of value) envTokenNames(item, out, blockName)

apps/sim/lib/mothership/tools/handlers/sim-cli.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -121,9 +121,11 @@ export async function executeSimCli(
121121
const written = await writeSessionSandboxFile(sessionKey, outputFile, result.stdout)
122122
if (written.outcome === 'written') {
123123
result.stdout = `[stdout written to ${outputFile} on your machine: ${result.stdout.length} chars. Read or process it with run_code, or pass it back as @${outputFile}.]`
124-
} else {
124+
} else if (written.outcome === 'no-session') {
125125
result.stdout +=
126126
'\n[outputFile not written: your machine is not booted yet — run any run_code first. Output returned inline instead]'
127+
} else {
128+
result.stdout += '\n[outputFile write failed — output returned inline instead]'
127129
}
128130
}
129131
}

0 commit comments

Comments
 (0)