Skip to content

Commit f7ab0ec

Browse files
authored
feat(iii-worker): champion-tier agent DX for sandbox::* (#1699)
1 parent 2011aab commit f7ab0ec

39 files changed

Lines changed: 2590 additions & 412 deletions

.gitignore

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,3 +78,7 @@ graphify-out
7878
docs/plans/
7979
# Local-only personal notes (per-contributor, not tracked)
8080
TODOS.md
81+
82+
# Per-contributor coding-agent state (not tracked)
83+
.serena/
84+
CLAUDE.md

Cargo.lock

Lines changed: 2 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

console/packages/console-frontend/src/api/events/functions.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,10 @@ export interface TriggerInfo {
1818
id: string
1919
trigger_type: string
2020
function_id: string
21-
config: Record<string, unknown>
21+
// `config` may be absent on older / leaner engine summaries — always
22+
// narrow with `?? {}` before reading nested fields. See triggers.tsx
23+
// and flows.ts for the pattern.
24+
config?: Record<string, unknown>
2225
internal?: boolean
2326
}
2427

console/packages/console-frontend/src/api/flows/flows.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -308,7 +308,7 @@ export async function fetchFlows(): Promise<FlowResponse[]> {
308308
for (const trigger of triggersData.triggers || []) {
309309
const funcId = trigger.function_id
310310
const existing = triggersByFunction.get(funcId) ?? []
311-
const config = trigger.config as Record<string, unknown>
311+
const config = (trigger.config ?? {}) as Record<string, unknown>
312312
const normalized: Record<string, unknown> = {
313313
...config,
314314
type: trigger.trigger_type,

console/packages/console-frontend/src/routes/triggers.tsx

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -251,7 +251,7 @@ function TriggersPage() {
251251
const matchesTrigger =
252252
t.id.toLowerCase().includes(query) || t.trigger_type.toLowerCase().includes(query)
253253
const matchesFunction = t.function_id.toLowerCase().includes(query)
254-
const config = t.config as Record<string, unknown>
254+
const config = (t.config ?? {}) as Record<string, unknown>
255255
const matchesConfig =
256256
(config.api_path && String(config.api_path).toLowerCase().includes(query)) ||
257257
(config.topic && String(config.topic).toLowerCase().includes(query)) ||
@@ -392,7 +392,7 @@ function TriggersPage() {
392392
}
393393

394394
const getTriggerSummary = (trigger: TriggerInfo): string => {
395-
const config = trigger.config as Record<string, unknown>
395+
const config = (trigger.config ?? {}) as Record<string, unknown>
396396
switch (trigger.trigger_type) {
397397
case 'http': {
398398
const method = (config.http_method as string) || 'GET'
@@ -418,7 +418,7 @@ function TriggersPage() {
418418
dispatchInvoke({ type: 'CLEAR_RESULT' })
419419

420420
if (trigger.trigger_type === 'http') {
421-
const config = trigger.config as { api_path?: string; http_method?: string }
421+
const config = (trigger.config ?? {}) as { api_path?: string; http_method?: string }
422422
const method = config.http_method || 'GET'
423423
const path = config.api_path || ''
424424
const matches = path.match(/:([a-zA-Z_]+)/g)
@@ -441,7 +441,7 @@ function TriggersPage() {
441441
}
442442

443443
const invokeHttp = async (trigger: TriggerInfo) => {
444-
const config = trigger.config as { api_path?: string; http_method?: string }
444+
const config = (trigger.config ?? {}) as { api_path?: string; http_method?: string }
445445
let path = (config.api_path || '').replace(/^\//, '')
446446
const method = httpMethod || config.http_method || 'GET'
447447

@@ -553,7 +553,7 @@ function TriggersPage() {
553553
}
554554

555555
const invokeEvent = async (trigger: TriggerInfo) => {
556-
const config = trigger.config as { topic?: string }
556+
const config = (trigger.config ?? {}) as { topic?: string }
557557
const topic = config.topic || ''
558558
dispatchInvoke({ type: 'START_INVOKE' })
559559
try {
@@ -891,7 +891,7 @@ function TriggersPage() {
891891
{/* HTTP Trigger Detail */}
892892
{selectedTrigger.trigger_type === 'http' &&
893893
(() => {
894-
const config = selectedTrigger.config as {
894+
const config = (selectedTrigger.config ?? {}) as {
895895
api_path?: string
896896
http_method?: string
897897
}
@@ -1096,7 +1096,7 @@ function TriggersPage() {
10961096
{/* Cron Trigger Detail */}
10971097
{selectedTrigger.trigger_type === 'cron' &&
10981098
(() => {
1099-
const config = selectedTrigger.config as {
1099+
const config = (selectedTrigger.config ?? {}) as {
11001100
expression?: string
11011101
description?: string
11021102
}
@@ -1194,7 +1194,10 @@ function TriggersPage() {
11941194
{/* Event Trigger Detail */}
11951195
{selectedTrigger.trigger_type === 'event' &&
11961196
(() => {
1197-
const config = selectedTrigger.config as { topic?: string; description?: string }
1197+
const config = (selectedTrigger.config ?? {}) as {
1198+
topic?: string
1199+
description?: string
1200+
}
11981201
const topic = config.topic || 'Unknown'
11991202

12001203
return (

crates/iii-shell-proto/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ path = "src/lib.rs"
1414
serde = { version = "1", features = ["derive"] }
1515
serde_json = "1"
1616
thiserror = "2"
17+
schemars = "0.8"
1718

1819
[dev-dependencies]
1920
base64 = "0.22"

crates/iii-shell-proto/src/lib.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ pub mod flags {
6666
/// `mode` is the octal permission string (e.g. `"0644"`); `mtime` is
6767
/// Unix seconds. `is_symlink` reflects the entry itself — FS handlers
6868
/// never follow symlinks unless the op doc-comment says otherwise.
69-
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
69+
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, schemars::JsonSchema)]
7070
pub struct FsEntry {
7171
pub name: String,
7272
pub is_dir: bool,
@@ -79,7 +79,7 @@ pub struct FsEntry {
7979
/// Single grep hit. `line` is 1-based. `content` is already truncated
8080
/// to `max_line_bytes` (with a trailing `…`) if the original line was
8181
/// longer.
82-
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
82+
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, schemars::JsonSchema)]
8383
pub struct FsMatch {
8484
#[serde(alias = "file")]
8585
pub path: String,
@@ -89,7 +89,7 @@ pub struct FsMatch {
8989

9090
/// One `sed` file-level outcome. `success=false` carries the human
9191
/// failure message in `error`; otherwise `error` is absent.
92-
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
92+
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, schemars::JsonSchema)]
9393
pub struct FsSedFileResult {
9494
#[serde(alias = "file")]
9595
pub path: String,
@@ -238,7 +238,7 @@ pub enum FsResult {
238238

239239
/// Metadata frame sent by the supervisor at the start of a
240240
/// `ReadStart` sequence (one per session, before any `FsChunk`).
241-
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
241+
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, schemars::JsonSchema)]
242242
pub struct FsReadMeta {
243243
pub size: u64,
244244
pub mode: String,

crates/iii-worker/Cargo.toml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,10 @@ once_cell = "1"
8585
thiserror = "2"
8686

8787
schemars = "0.8"
88+
# Used by sandbox::exec to shlex-split `cmd` when an agent passes a shell-style
89+
# line (e.g. `cmd: "node script.js"`). Lets the existing argv-based runner stay
90+
# argv-pure while accepting the agent-natural input shape.
91+
shlex = "1"
8892

8993
[dev-dependencies]
9094
tokio = { version = "1", features = ["test-util"] }

crates/iii-worker/src/cli/sandbox.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ const CREATE_TRIGGER_TIMEOUT_MS: u64 = 300_000;
1818
/// Matches the daemon's default exec timeout. If the daemon changes, update here.
1919
/// (See `sandbox_daemon::adapters::DEFAULT_EXEC_TIMEOUT_MS` — private there, so
2020
/// we maintain a mirrored constant on the CLI side.)
21-
const DAEMON_DEFAULT_EXEC_TIMEOUT_MS: u64 = 30_000;
21+
const DAEMON_DEFAULT_EXEC_TIMEOUT_MS: u64 = 300_000;
2222
/// Safety margin so the daemon's deadline fires before the trigger does.
2323
const EXEC_TRIGGER_MARGIN_MS: u64 = 5_000;
2424

crates/iii-worker/src/cli/sandbox_daemon.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
// contributor license agreements. Licensed under the Elastic License 2.0.
33

44
//! CLI entrypoint for `iii-worker sandbox-daemon`. Loads config and
5-
//! delegates to `sandbox_daemon::run`.
5+
//! delegates to `sandbox_daemon::serve`.
66
77
use crate::cli::app::SandboxDaemonArgs;
88
use crate::sandbox_daemon;
@@ -30,7 +30,7 @@ pub async fn run(args: SandboxDaemonArgs) -> i32 {
3030
}
3131
};
3232

33-
match sandbox_daemon::run(cfg, &args.engine).await {
33+
match sandbox_daemon::serve(cfg, &args.engine).await {
3434
Ok(()) => 0,
3535
Err(e) => {
3636
tracing::error!(error = %e, "sandbox-daemon exited with error");

0 commit comments

Comments
 (0)