fix(mini-apps): reliability fixes + self-contained data loading

From two rounds of copilot build feedback:

- runtime: null-check hallucinated tool names (was crashing the whole run)
- add window.rowboat.fetch + builtin fetch-url: CORS-safe HTTP for UI and
  bg-task agents (no shell needed for plain HTTP)
- mini-app-set-data/install: reject stringified JSON; require object
- manifest dataContract (requiredKeys/nonEmptyArrayKeys) enforced on writes so
  a stray/legacy task can't corrupt an app's data or wipe series
- self-contained data: data.json is a served sibling (dist/), apps fetch it via
  relative URL; removed host-side data injection
- build-mini-app skill: CORS/fetch-url/no-shell/capable-model/dataContract gotchas
This commit is contained in:
Gagan 2026-07-02 01:06:05 +05:30
parent 6d7fd7b013
commit c424976926
11 changed files with 288 additions and 75 deletions

View file

@ -1033,6 +1033,23 @@ const ipcSchemas = {
req: z.object({ id: z.string() }),
res: z.object({ data: z.unknown().nullable() }),
},
// Mini Apps: proxy an HTTP request through main (bypasses browser CORS for the
// sandboxed app origin). GET/POST to http(s) only.
'mini-apps:fetch': {
req: z.object({
url: z.string(),
method: z.string().optional(),
headers: z.record(z.string(), z.string()).optional(),
body: z.string().optional(),
}),
res: z.object({
ok: z.boolean(),
status: z.number(),
statusText: z.string(),
text: z.string(),
error: z.string().optional(),
}),
},
'composio:didConnect': {
req: z.object({
toolkitSlug: z.string(),

View file

@ -26,6 +26,17 @@ export const MiniAppManifest = z.object({
entry: z.string().default('dist/index.html'),
/** Optional associated background-task slug that produces data.json. */
agent: z.string().optional(),
/**
* Optional guard on what may be written to data.json (via mini-app-set-data).
* Prevents a stray/legacy task from corrupting the app with the wrong shape or
* wiping good series with empty ones.
*/
dataContract: z.object({
/** Top-level keys that must be present and non-null. */
requiredKeys: z.array(z.string()).default([]),
/** Top-level keys that must be non-empty arrays. */
nonEmptyArrayKeys: z.array(z.string()).default([]),
}).optional(),
});
export type MiniAppManifest = z.infer<typeof MiniAppManifest>;