@rubytech/create-realagent-code 0.1.29 → 0.1.31
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/__tests__/init-logging.test.js +85 -0
- package/dist/index.js +21 -10
- package/dist/init-logging.js +28 -0
- package/package.json +1 -1
- package/payload/platform/lib/persistent-components/dist/index.d.ts +11 -12
- package/payload/platform/lib/persistent-components/dist/index.d.ts.map +1 -1
- package/payload/platform/lib/persistent-components/dist/index.js +11 -12
- package/payload/platform/lib/persistent-components/dist/index.js.map +1 -1
- package/payload/platform/lib/persistent-components/src/index.ts +11 -12
- package/payload/platform/neo4j/schema.cypher +6 -3
- package/payload/platform/plugins/admin/.claude-plugin/plugin.json +1 -1
- package/payload/platform/plugins/admin/PLUGIN.md +1 -3
- package/payload/platform/plugins/admin/hooks/onboarding-skill-drift.sh +57 -54
- package/payload/platform/plugins/admin/mcp/dist/index.js +15 -50
- package/payload/platform/plugins/admin/mcp/dist/index.js.map +1 -1
- package/payload/platform/plugins/admin/skills/access-manager/references/operations.md +7 -7
- package/payload/platform/plugins/admin/skills/file-presentation/SKILL.md +16 -33
- package/payload/platform/plugins/admin/skills/plugin-management/SKILL.md +1 -1
- package/payload/platform/plugins/admin/skills/public-agent-manager/SKILL.md +2 -2
- package/payload/platform/plugins/admin/skills/stream-log-review/references/analysis-patterns.md +2 -2
- package/payload/platform/plugins/anthropic/skills/get-api-key/SKILL.md +2 -4
- package/payload/platform/plugins/brochures/PLUGIN.md +1 -1
- package/payload/platform/plugins/buyers/PLUGIN.md +1 -2
- package/payload/platform/plugins/deep-research/skills/book-mirror/SKILL.md +1 -1
- package/payload/platform/plugins/deep-research/skills/strategic-reading/SKILL.md +1 -1
- package/payload/platform/plugins/docs/references/plugins-guide.md +2 -2
- package/payload/platform/plugins/email/references/email-reference.md +3 -7
- package/payload/platform/plugins/estate-business/PLUGIN.md +1 -2
- package/payload/platform/plugins/estate-coaching/PLUGIN.md +1 -2
- package/payload/platform/plugins/estate-onboarding/PLUGIN.md +1 -2
- package/payload/platform/plugins/estate-sales/PLUGIN.md +1 -2
- package/payload/platform/plugins/estate-teaching/PLUGIN.md +1 -2
- package/payload/platform/plugins/leads/PLUGIN.md +1 -2
- package/payload/platform/plugins/listings/PLUGIN.md +1 -2
- package/payload/platform/plugins/loop/PLUGIN.md +1 -1
- package/payload/platform/plugins/scheduling/PLUGIN.md +1 -1
- package/payload/platform/plugins/teaching/PLUGIN.md +2 -1
- package/payload/platform/plugins/vendors/PLUGIN.md +1 -2
- package/payload/platform/plugins/whatsapp/mcp/dist/index.js +1 -1
- package/payload/platform/plugins/whatsapp/mcp/dist/index.js.map +1 -1
- package/payload/platform/plugins/whatsapp/skills/connect-whatsapp/SKILL.md +1 -1
- package/payload/platform/plugins/whatsapp/skills/manage-whatsapp-config/SKILL.md +2 -8
- package/payload/platform/plugins/writer-craft/PLUGIN.md +2 -1
- package/payload/platform/scripts/component-knowledgedoc-backfill.ts +1 -1
- package/payload/platform/services/claude-session-manager/dist/http-server.d.ts +1 -1
- package/payload/platform/services/claude-session-manager/dist/http-server.d.ts.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/http-server.js +45 -31
- package/payload/platform/services/claude-session-manager/dist/http-server.js.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/index.js +11 -0
- package/payload/platform/services/claude-session-manager/dist/index.js.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/jsonl-observer.d.ts +30 -0
- package/payload/platform/services/claude-session-manager/dist/jsonl-observer.d.ts.map +1 -0
- package/payload/platform/services/claude-session-manager/dist/jsonl-observer.js +175 -0
- package/payload/platform/services/claude-session-manager/dist/jsonl-observer.js.map +1 -0
- package/payload/platform/services/claude-session-manager/dist/jsonl-path.d.ts +4 -2
- package/payload/platform/services/claude-session-manager/dist/jsonl-path.d.ts.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/jsonl-path.js +15 -22
- package/payload/platform/services/claude-session-manager/dist/jsonl-path.js.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/pty-spawner.d.ts +13 -0
- package/payload/platform/services/claude-session-manager/dist/pty-spawner.d.ts.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/pty-spawner.js +26 -4
- package/payload/platform/services/claude-session-manager/dist/pty-spawner.js.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/session-store.d.ts +9 -0
- package/payload/platform/services/claude-session-manager/dist/session-store.d.ts.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/session-store.js.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/system-prompt.d.ts +10 -7
- package/payload/platform/services/claude-session-manager/dist/system-prompt.d.ts.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/system-prompt.js +51 -28
- package/payload/platform/services/claude-session-manager/dist/system-prompt.js.map +1 -1
- package/payload/platform/services/claude-session-manager/scripts/onboarding-advance.sh +61 -0
- package/payload/platform/services/claude-session-manager/scripts/onboarding-prompts/step-0.md +5 -0
- package/payload/platform/services/claude-session-manager/scripts/onboarding-prompts/step-1.md +3 -0
- package/payload/platform/services/claude-session-manager/scripts/onboarding-prompts/step-2.md +3 -0
- package/payload/platform/services/claude-session-manager/scripts/onboarding-prompts/step-3.md +8 -0
- package/payload/platform/services/claude-session-manager/scripts/onboarding-prompts/step-4.md +9 -0
- package/payload/platform/services/claude-session-manager/scripts/onboarding-prompts/step-5.md +3 -0
- package/payload/platform/services/claude-session-manager/scripts/onboarding-prompts/step-6.md +5 -0
- package/payload/platform/services/claude-session-manager/scripts/onboarding-prompts/step-7.md +5 -0
- package/payload/platform/services/claude-session-manager/scripts/onboarding-prompts/step-8.md +5 -0
- package/payload/platform/services/claude-session-manager/scripts/onboarding-prompts/step-9.md +8 -0
- package/payload/platform/services/claude-session-manager/scripts/onboarding-prompts/step-complete.md +3 -0
- package/payload/platform/services/claude-session-manager/scripts/onboarding-prompts/step-unreachable.md +3 -0
- package/payload/platform/templates/agents/public/IDENTITY.md +1 -1
- package/payload/platform/templates/specialists/agents/content-producer.md +3 -3
- package/payload/platform/templates/specialists/agents/personal-assistant.md +2 -2
- package/payload/premium-plugins/real-agent/BUNDLE.md +5 -5
- package/payload/premium-plugins/real-agent/agents/compliance.md +1 -1
- package/payload/premium-plugins/real-agent/agents/negotiator.md +1 -1
- package/payload/premium-plugins/real-agent/agents/valuer.md +1 -1
- package/payload/premium-plugins/real-agent/plugins/brochures/PLUGIN.md +1 -1
- package/payload/premium-plugins/real-agent/plugins/buyers/PLUGIN.md +1 -2
- package/payload/premium-plugins/real-agent/plugins/estate-business/PLUGIN.md +1 -2
- package/payload/premium-plugins/real-agent/plugins/estate-coaching/PLUGIN.md +1 -2
- package/payload/premium-plugins/real-agent/plugins/estate-onboarding/PLUGIN.md +1 -2
- package/payload/premium-plugins/real-agent/plugins/estate-sales/PLUGIN.md +1 -2
- package/payload/premium-plugins/real-agent/plugins/estate-teaching/PLUGIN.md +1 -2
- package/payload/premium-plugins/real-agent/plugins/leads/PLUGIN.md +1 -2
- package/payload/premium-plugins/real-agent/plugins/listings/PLUGIN.md +1 -2
- package/payload/premium-plugins/real-agent/plugins/loop/PLUGIN.md +1 -1
- package/payload/premium-plugins/real-agent/plugins/vendors/PLUGIN.md +1 -2
- package/payload/premium-plugins/teaching/PLUGIN.md +2 -1
- package/payload/premium-plugins/writer-craft/PLUGIN.md +2 -1
- package/payload/server/public/assets/{admin-CDvF5de6.js → admin-Bk2eXMFD.js} +24 -24
- package/payload/server/public/assets/{data-K_kS__sL.js → data-ll_OwVNL.js} +1 -1
- package/payload/server/public/assets/{graph-DeEigyO_.js → graph-DJ2VWioQ.js} +1 -1
- package/payload/server/public/assets/graph-labels-qhU8xZDH.js +1 -0
- package/payload/server/public/assets/{page-qSH972X0.js → page-Dk73ZO1F.js} +1 -1
- package/payload/server/public/assets/{page-B_rpjIRr.js → page-DsYsdBUK.js} +1 -1
- package/payload/server/public/data.html +3 -3
- package/payload/server/public/graph.html +3 -3
- package/payload/server/public/index.html +4 -4
- package/payload/server/server.js +121 -46
- package/payload/platform/plugins/admin/references/contextual-ui.md +0 -107
- package/payload/platform/scripts/__tests__/admin-persist-audit.test.ts +0 -182
- package/payload/server/public/assets/graph-labels-C7I5QvNv.js +0 -1
package/payload/server/server.js
CHANGED
|
@@ -618,7 +618,7 @@ var serveStatic = (options = { root: "" }) => {
|
|
|
618
618
|
|
|
619
619
|
// server/index.ts
|
|
620
620
|
import { readFileSync as readFileSync19, existsSync as existsSync22, watchFile } from "fs";
|
|
621
|
-
import { resolve as resolve23, join as
|
|
621
|
+
import { resolve as resolve23, join as join14, basename as basename5 } from "path";
|
|
622
622
|
import { homedir as homedir5 } from "os";
|
|
623
623
|
|
|
624
624
|
// app/lib/agent-slug-pattern.ts
|
|
@@ -8024,7 +8024,7 @@ app14.post("/:id/resume", async (c) => {
|
|
|
8024
8024
|
await storeComponentArtefact(accountId, c2.artefactAttachmentId, bytesPick.mimeType, bytesPick.content, filename);
|
|
8025
8025
|
clearHealPending(accountId, c2.artefactAttachmentId);
|
|
8026
8026
|
streamLogPath.write(
|
|
8027
|
-
`[${(/* @__PURE__ */ new Date()).toISOString()}] [
|
|
8027
|
+
`[${(/* @__PURE__ */ new Date()).toISOString()}] [component-persist] heal componentName=${c2.name} attachmentId=${c2.artefactAttachmentId.slice(0, 8)} mimeType=${bytesPick.mimeType} bytes=${bytesPick.content.length} outcome=ok
|
|
8028
8028
|
`
|
|
8029
8029
|
);
|
|
8030
8030
|
return {
|
|
@@ -8037,7 +8037,7 @@ app14.post("/:id/resume", async (c) => {
|
|
|
8037
8037
|
clearHealPending(accountId, c2.artefactAttachmentId);
|
|
8038
8038
|
const reason2 = writeErr instanceof Error ? writeErr.message : String(writeErr);
|
|
8039
8039
|
streamLogPath.write(
|
|
8040
|
-
`[${(/* @__PURE__ */ new Date()).toISOString()}] [
|
|
8040
|
+
`[${(/* @__PURE__ */ new Date()).toISOString()}] [component-persist] heal componentName=${c2.name} attachmentId=${c2.artefactAttachmentId.slice(0, 8)} outcome=disk-fail reason=${JSON.stringify(reason2.slice(0, 200))}
|
|
8041
8041
|
`
|
|
8042
8042
|
);
|
|
8043
8043
|
return base;
|
|
@@ -8217,19 +8217,72 @@ async function buildOnboardingPromptBlock(accountId) {
|
|
|
8217
8217
|
return { step };
|
|
8218
8218
|
}
|
|
8219
8219
|
|
|
8220
|
-
// server/
|
|
8220
|
+
// server/lib/onboarding-advance.ts
|
|
8221
|
+
import { execFile as execFile2 } from "child_process";
|
|
8222
|
+
import { join as join11, resolve as resolvePath2 } from "path";
|
|
8221
8223
|
var TAG19 = "[claude-session-manager:wrapper]";
|
|
8224
|
+
function onboardingAdvanceScriptPath() {
|
|
8225
|
+
const platformRoot = process.env.MAXY_PLATFORM_ROOT ?? resolvePath2(process.cwd(), "..");
|
|
8226
|
+
return join11(platformRoot, "services", "claude-session-manager", "scripts", "onboarding-advance.sh");
|
|
8227
|
+
}
|
|
8228
|
+
function runOnboardingAdvance(accountId, currentStep) {
|
|
8229
|
+
const scriptPath = onboardingAdvanceScriptPath();
|
|
8230
|
+
const stepArg = currentStep === null ? "null" : String(currentStep);
|
|
8231
|
+
return new Promise((resolve24) => {
|
|
8232
|
+
execFile2(
|
|
8233
|
+
scriptPath,
|
|
8234
|
+
[accountId, stepArg],
|
|
8235
|
+
{ maxBuffer: 64 * 1024, timeout: 5e3 },
|
|
8236
|
+
(err, stdout, stderr) => {
|
|
8237
|
+
if (err) {
|
|
8238
|
+
console.error(`${TAG19} [onboarding-state] route-advance script-failed step=${stepArg} sender=${accountId}: ${err.message}`);
|
|
8239
|
+
resolve24(null);
|
|
8240
|
+
return;
|
|
8241
|
+
}
|
|
8242
|
+
if (stderr && stderr.trim()) {
|
|
8243
|
+
process.stderr.write(stderr.endsWith("\n") ? stderr : `${stderr}
|
|
8244
|
+
`);
|
|
8245
|
+
}
|
|
8246
|
+
resolve24(stdout);
|
|
8247
|
+
}
|
|
8248
|
+
);
|
|
8249
|
+
});
|
|
8250
|
+
}
|
|
8251
|
+
|
|
8252
|
+
// server/routes/admin/claude-sessions.ts
|
|
8253
|
+
var TAG20 = "[claude-session-manager:wrapper]";
|
|
8222
8254
|
function managerBase2() {
|
|
8223
8255
|
const port2 = Number(process.env.CLAUDE_SESSION_MANAGER_PORT ?? "19400");
|
|
8224
8256
|
return `http://127.0.0.1:${port2}`;
|
|
8225
8257
|
}
|
|
8258
|
+
async function resolveConversationIdForBanner(cacheKey, accountId, userId) {
|
|
8259
|
+
const bound = getConversationIdForSession(cacheKey);
|
|
8260
|
+
if (bound) return bound;
|
|
8261
|
+
const ensured = await ensureConversation(
|
|
8262
|
+
accountId,
|
|
8263
|
+
"admin",
|
|
8264
|
+
cacheKey,
|
|
8265
|
+
void 0,
|
|
8266
|
+
void 0,
|
|
8267
|
+
userId
|
|
8268
|
+
).catch((err) => {
|
|
8269
|
+
console.error(`${TAG20} ensureConversation failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
8270
|
+
return null;
|
|
8271
|
+
});
|
|
8272
|
+
if (ensured?.conversationId) {
|
|
8273
|
+
setConversationIdForSession(cacheKey, ensured.conversationId);
|
|
8274
|
+
return ensured.conversationId;
|
|
8275
|
+
}
|
|
8276
|
+
return null;
|
|
8277
|
+
}
|
|
8226
8278
|
var app15 = new Hono();
|
|
8227
8279
|
app15.use("*", requireAdminSession);
|
|
8228
8280
|
app15.post("/", async (c) => {
|
|
8229
8281
|
const cacheKey = c.get("cacheKey") ?? "";
|
|
8230
8282
|
const senderId = getAccountIdForSession(cacheKey) ?? "";
|
|
8283
|
+
const userId = getUserIdForSession(cacheKey);
|
|
8231
8284
|
if (!senderId) {
|
|
8232
|
-
console.error(`${
|
|
8285
|
+
console.error(`${TAG20} reject reason=no-account-id`);
|
|
8233
8286
|
return c.json({ error: "admin-account-not-resolved" }, 500);
|
|
8234
8287
|
}
|
|
8235
8288
|
let body = {};
|
|
@@ -8239,37 +8292,59 @@ app15.post("/", async (c) => {
|
|
|
8239
8292
|
}
|
|
8240
8293
|
const channel = typeof body.channel === "string" ? body.channel : "browser";
|
|
8241
8294
|
const initialMessage = typeof body.initialMessage === "string" && body.initialMessage.trim() ? body.initialMessage : null;
|
|
8295
|
+
const onboardingAdvance = body.onboardingAdvance === true;
|
|
8242
8296
|
const permissionMode = typeof body.permissionMode === "string" ? body.permissionMode : void 0;
|
|
8243
8297
|
let onboarding;
|
|
8298
|
+
let resolvedStep = 9;
|
|
8244
8299
|
try {
|
|
8245
8300
|
const outcome = await buildOnboardingPromptBlock(senderId);
|
|
8246
8301
|
onboarding = { step: outcome.step };
|
|
8302
|
+
resolvedStep = outcome.step;
|
|
8247
8303
|
} catch (err) {
|
|
8248
|
-
console.error(`${
|
|
8304
|
+
console.error(`${TAG20} onboarding-resolve failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
8305
|
+
}
|
|
8306
|
+
let scriptOutput = null;
|
|
8307
|
+
if (onboardingAdvance && resolvedStep !== 9 && onboarding) {
|
|
8308
|
+
scriptOutput = await runOnboardingAdvance(senderId, resolvedStep);
|
|
8309
|
+
if (scriptOutput && scriptOutput.trim()) {
|
|
8310
|
+
onboarding = { step: resolvedStep, resumePrompt: scriptOutput };
|
|
8311
|
+
}
|
|
8249
8312
|
}
|
|
8250
8313
|
const upstream = await fetch(`${managerBase2()}/spawn`, {
|
|
8251
8314
|
method: "POST",
|
|
8252
8315
|
headers: { "content-type": "application/json" },
|
|
8253
8316
|
body: JSON.stringify({ senderId, role: "admin", channel, accountId: senderId, onboarding, permissionMode })
|
|
8254
8317
|
}).catch((err) => {
|
|
8255
|
-
console.error(`${
|
|
8318
|
+
console.error(`${TAG20} fetch-failed op=spawn message=${err instanceof Error ? err.message : String(err)}`);
|
|
8256
8319
|
return null;
|
|
8257
8320
|
});
|
|
8258
8321
|
if (!upstream) return c.json({ error: "manager-unreachable" }, 503);
|
|
8259
8322
|
let spawnedSessionId = null;
|
|
8260
|
-
if (upstream.ok
|
|
8323
|
+
if (upstream.ok) {
|
|
8261
8324
|
const buffered = await upstream.clone().json().catch(() => null);
|
|
8262
8325
|
if (buffered && typeof buffered.sessionId === "string" && buffered.sessionId) {
|
|
8263
8326
|
spawnedSessionId = buffered.sessionId;
|
|
8264
8327
|
}
|
|
8265
8328
|
}
|
|
8266
|
-
if (
|
|
8329
|
+
if (onboardingAdvance && spawnedSessionId && scriptOutput && scriptOutput.trim()) {
|
|
8330
|
+
const conversationId = await resolveConversationIdForBanner(cacheKey, senderId, userId);
|
|
8331
|
+
if (conversationId) {
|
|
8332
|
+
try {
|
|
8333
|
+
await persistMessage(conversationId, "assistant", scriptOutput.trimEnd(), senderId);
|
|
8334
|
+
} catch (err) {
|
|
8335
|
+
console.error(`${TAG20} persist initial assistant message failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
8336
|
+
}
|
|
8337
|
+
} else {
|
|
8338
|
+
console.error(`${TAG20} banner-flow no conversationId resolved \u2014 script output not persisted, only system prompt carries it`);
|
|
8339
|
+
}
|
|
8340
|
+
}
|
|
8341
|
+
if (!onboardingAdvance && initialMessage && spawnedSessionId) {
|
|
8267
8342
|
fetch(`${managerBase2()}/${encodeURIComponent(spawnedSessionId)}/input`, {
|
|
8268
8343
|
method: "POST",
|
|
8269
8344
|
headers: { "content-type": "application/json" },
|
|
8270
8345
|
body: JSON.stringify({ text: initialMessage })
|
|
8271
8346
|
}).catch((err) => {
|
|
8272
|
-
console.error(`${
|
|
8347
|
+
console.error(`${TAG20} fetch-failed op=initial-input message=${err instanceof Error ? err.message : String(err)}`);
|
|
8273
8348
|
});
|
|
8274
8349
|
}
|
|
8275
8350
|
return new Response(upstream.body, { status: upstream.status, headers: upstream.headers });
|
|
@@ -8281,7 +8356,7 @@ app15.get("/", async (c) => {
|
|
|
8281
8356
|
const upstream = await fetch(
|
|
8282
8357
|
`${managerBase2()}/list?senderId=${encodeURIComponent(senderId)}`
|
|
8283
8358
|
).catch((err) => {
|
|
8284
|
-
console.error(`${
|
|
8359
|
+
console.error(`${TAG20} fetch-failed op=list message=${err instanceof Error ? err.message : String(err)}`);
|
|
8285
8360
|
return null;
|
|
8286
8361
|
});
|
|
8287
8362
|
if (!upstream) return c.json({ error: "manager-unreachable" }, 503);
|
|
@@ -8311,7 +8386,7 @@ app15.post("/resume", async (c) => {
|
|
|
8311
8386
|
claudeSessionId: body.claudeSessionId
|
|
8312
8387
|
})
|
|
8313
8388
|
}).catch((err) => {
|
|
8314
|
-
console.error(`${
|
|
8389
|
+
console.error(`${TAG20} fetch-failed op=resume message=${err instanceof Error ? err.message : String(err)}`);
|
|
8315
8390
|
return null;
|
|
8316
8391
|
});
|
|
8317
8392
|
if (!upstream) return c.json({ error: "manager-unreachable" }, 503);
|
|
@@ -8324,7 +8399,7 @@ app15.delete("/:sessionId", async (c) => {
|
|
|
8324
8399
|
`${managerBase2()}/${encodeURIComponent(sessionId)}${purge}`,
|
|
8325
8400
|
{ method: "DELETE" }
|
|
8326
8401
|
).catch((err) => {
|
|
8327
|
-
console.error(`${
|
|
8402
|
+
console.error(`${TAG20} fetch-failed op=delete message=${err instanceof Error ? err.message : String(err)}`);
|
|
8328
8403
|
return null;
|
|
8329
8404
|
});
|
|
8330
8405
|
if (!upstream) return c.json({ error: "manager-unreachable" }, 503);
|
|
@@ -8341,7 +8416,7 @@ app15.post("/:sessionId/archive", async (c) => {
|
|
|
8341
8416
|
body
|
|
8342
8417
|
}
|
|
8343
8418
|
).catch((err) => {
|
|
8344
|
-
console.error(`${
|
|
8419
|
+
console.error(`${TAG20} fetch-failed op=archive message=${err instanceof Error ? err.message : String(err)}`);
|
|
8345
8420
|
return null;
|
|
8346
8421
|
});
|
|
8347
8422
|
if (!upstream) return c.json({ error: "manager-unreachable" }, 503);
|
|
@@ -8352,7 +8427,7 @@ app15.get("/:sessionId/meta", async (c) => {
|
|
|
8352
8427
|
const upstream = await fetch(
|
|
8353
8428
|
`${managerBase2()}/${encodeURIComponent(sessionId)}/meta`
|
|
8354
8429
|
).catch((err) => {
|
|
8355
|
-
console.error(`${
|
|
8430
|
+
console.error(`${TAG20} fetch-failed op=meta message=${err instanceof Error ? err.message : String(err)}`);
|
|
8356
8431
|
return null;
|
|
8357
8432
|
});
|
|
8358
8433
|
if (!upstream) return c.json({ error: "manager-unreachable" }, 503);
|
|
@@ -8369,7 +8444,7 @@ app15.patch("/:sessionId", async (c) => {
|
|
|
8369
8444
|
body
|
|
8370
8445
|
}
|
|
8371
8446
|
).catch((err) => {
|
|
8372
|
-
console.error(`${
|
|
8447
|
+
console.error(`${TAG20} fetch-failed op=patch message=${err instanceof Error ? err.message : String(err)}`);
|
|
8373
8448
|
return null;
|
|
8374
8449
|
});
|
|
8375
8450
|
if (!upstream) return c.json({ error: "manager-unreachable" }, 503);
|
|
@@ -8386,7 +8461,7 @@ app15.post("/:sessionId/input", async (c) => {
|
|
|
8386
8461
|
body: rawBody
|
|
8387
8462
|
}
|
|
8388
8463
|
).catch((err) => {
|
|
8389
|
-
console.error(`${
|
|
8464
|
+
console.error(`${TAG20} fetch-failed op=input message=${err instanceof Error ? err.message : String(err)}`);
|
|
8390
8465
|
return null;
|
|
8391
8466
|
});
|
|
8392
8467
|
if (!upstream) return c.json({ error: "manager-unreachable" }, 503);
|
|
@@ -8398,7 +8473,7 @@ app15.get("/:sessionId/log", async (c) => {
|
|
|
8398
8473
|
const upstream = await fetch(
|
|
8399
8474
|
`${managerBase2()}/${encodeURIComponent(sessionId)}/log${follow}`
|
|
8400
8475
|
).catch((err) => {
|
|
8401
|
-
console.error(`${
|
|
8476
|
+
console.error(`${TAG20} fetch-failed op=log message=${err instanceof Error ? err.message : String(err)}`);
|
|
8402
8477
|
return null;
|
|
8403
8478
|
});
|
|
8404
8479
|
if (!upstream) return c.json({ error: "manager-unreachable" }, 503);
|
|
@@ -8514,13 +8589,13 @@ async function cdpNavigateNewTab(url, opts = {}) {
|
|
|
8514
8589
|
// server/routes/admin/device-browser.ts
|
|
8515
8590
|
var app18 = new Hono();
|
|
8516
8591
|
app18.post("/navigate", async (c) => {
|
|
8517
|
-
const
|
|
8592
|
+
const TAG22 = "[device-url:click]";
|
|
8518
8593
|
let body;
|
|
8519
8594
|
try {
|
|
8520
8595
|
body = await c.req.json();
|
|
8521
8596
|
} catch (err) {
|
|
8522
8597
|
const detail = err instanceof Error ? err.message : String(err);
|
|
8523
|
-
console.error(`${
|
|
8598
|
+
console.error(`${TAG22} reject reason=body-not-json detail=${detail} browser=fallback navigateResult=error`);
|
|
8524
8599
|
return c.json(
|
|
8525
8600
|
{ ok: false, navigateResult: "error", browser: "fallback", detail: "Request body was not valid JSON" },
|
|
8526
8601
|
400
|
|
@@ -8530,7 +8605,7 @@ app18.post("/navigate", async (c) => {
|
|
|
8530
8605
|
const intent = typeof body.intent === "string" ? body.intent : "";
|
|
8531
8606
|
const hostname2 = typeof body.hostname === "string" ? body.hostname : "";
|
|
8532
8607
|
if (!url) {
|
|
8533
|
-
console.error(`${
|
|
8608
|
+
console.error(`${TAG22} reject reason=missing-url intent=${JSON.stringify(intent)} browser=fallback navigateResult=error`);
|
|
8534
8609
|
return c.json(
|
|
8535
8610
|
{ ok: false, navigateResult: "error", browser: "fallback", detail: "url field is required" },
|
|
8536
8611
|
400
|
|
@@ -8540,7 +8615,7 @@ app18.post("/navigate", async (c) => {
|
|
|
8540
8615
|
try {
|
|
8541
8616
|
parsed = new URL(url);
|
|
8542
8617
|
} catch {
|
|
8543
|
-
console.error(`${
|
|
8618
|
+
console.error(`${TAG22} reject reason=url-malformed intent=${JSON.stringify(intent)} url=${url} browser=fallback navigateResult=error`);
|
|
8544
8619
|
return c.json(
|
|
8545
8620
|
{ ok: false, navigateResult: "error", browser: "fallback", detail: "url is not a valid URL" },
|
|
8546
8621
|
400
|
|
@@ -8548,7 +8623,7 @@ app18.post("/navigate", async (c) => {
|
|
|
8548
8623
|
}
|
|
8549
8624
|
if (parsed.protocol !== "http:" && parsed.protocol !== "https:") {
|
|
8550
8625
|
console.error(
|
|
8551
|
-
`${
|
|
8626
|
+
`${TAG22} reject reason=scheme-not-allowed scheme=${parsed.protocol} intent=${JSON.stringify(intent)} browser=fallback navigateResult=error`
|
|
8552
8627
|
);
|
|
8553
8628
|
return c.json(
|
|
8554
8629
|
{
|
|
@@ -8564,7 +8639,7 @@ app18.post("/navigate", async (c) => {
|
|
|
8564
8639
|
const cdpOk = await ensureCdp(transport);
|
|
8565
8640
|
if (!cdpOk) {
|
|
8566
8641
|
console.error(
|
|
8567
|
-
`${
|
|
8642
|
+
`${TAG22} intent=${JSON.stringify(intent)} browser=fallback navigateResult=cdp-unreachable hostname=${JSON.stringify(hostname2)}`
|
|
8568
8643
|
);
|
|
8569
8644
|
return c.json(
|
|
8570
8645
|
{
|
|
@@ -8580,7 +8655,7 @@ app18.post("/navigate", async (c) => {
|
|
|
8580
8655
|
const browser = outcome.result === "ok" ? "vnc" : "fallback";
|
|
8581
8656
|
const detailStr = outcome.detail ? ` detail=${JSON.stringify(outcome.detail.length > 230 ? outcome.detail.slice(0, 227) + "..." : outcome.detail)}` : "";
|
|
8582
8657
|
console.error(
|
|
8583
|
-
`${
|
|
8658
|
+
`${TAG22} intent=${JSON.stringify(intent)} browser=${browser} navigateResult=${outcome.result} hostname=${JSON.stringify(hostname2)} targetId=${outcome.targetId ?? "none"}${detailStr}`
|
|
8584
8659
|
);
|
|
8585
8660
|
if (outcome.result !== "ok") {
|
|
8586
8661
|
return c.json(
|
|
@@ -8611,18 +8686,18 @@ var ALLOWED_EVENTS2 = /* @__PURE__ */ new Set([
|
|
|
8611
8686
|
]);
|
|
8612
8687
|
var app19 = new Hono();
|
|
8613
8688
|
app19.post("/", async (c) => {
|
|
8614
|
-
const
|
|
8689
|
+
const TAG22 = "[admin:events]";
|
|
8615
8690
|
let body;
|
|
8616
8691
|
try {
|
|
8617
8692
|
body = await c.req.json();
|
|
8618
8693
|
} catch (err) {
|
|
8619
8694
|
const detail = err instanceof Error ? err.message : String(err);
|
|
8620
|
-
console.error(`${
|
|
8695
|
+
console.error(`${TAG22} reject reason=body-not-json detail=${detail}`);
|
|
8621
8696
|
return c.json({ ok: false, detail: "Request body was not valid JSON" }, 400);
|
|
8622
8697
|
}
|
|
8623
8698
|
const event = typeof body.event === "string" ? body.event : "";
|
|
8624
8699
|
if (!ALLOWED_EVENTS2.has(event)) {
|
|
8625
|
-
console.error(`${
|
|
8700
|
+
console.error(`${TAG22} reject reason=event-not-allowed event=${JSON.stringify(event)}`);
|
|
8626
8701
|
return c.json({ ok: false, detail: `Event "${event}" is not allowed` }, 400);
|
|
8627
8702
|
}
|
|
8628
8703
|
const rawFields = body.fields && typeof body.fields === "object" ? body.fields : {};
|
|
@@ -9514,7 +9589,7 @@ var cloudflare_default = app20;
|
|
|
9514
9589
|
import { createReadStream as createReadStream3 } from "fs";
|
|
9515
9590
|
import { readdir as readdir2, readFile as readFile4, stat as stat4, mkdir as mkdir3, writeFile as writeFile4, unlink as unlink2 } from "fs/promises";
|
|
9516
9591
|
import { realpathSync as realpathSync4 } from "fs";
|
|
9517
|
-
import { basename as basename4, dirname as dirname6, join as
|
|
9592
|
+
import { basename as basename4, dirname as dirname6, join as join12, resolve as resolve16, sep as sep3 } from "path";
|
|
9518
9593
|
import { Readable as Readable2 } from "stream";
|
|
9519
9594
|
|
|
9520
9595
|
// app/lib/data-path.ts
|
|
@@ -9872,7 +9947,7 @@ async function cascadeDeleteDocument(params) {
|
|
|
9872
9947
|
var UUID_RE4 = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
9873
9948
|
async function readMeta(absDir, baseName) {
|
|
9874
9949
|
try {
|
|
9875
|
-
const raw = await readFile4(
|
|
9950
|
+
const raw = await readFile4(join12(absDir, `${baseName}.meta.json`), "utf8");
|
|
9876
9951
|
const parsed = JSON.parse(raw);
|
|
9877
9952
|
if (typeof parsed?.filename === "string") {
|
|
9878
9953
|
return { filename: parsed.filename, mimeType: typeof parsed.mimeType === "string" ? parsed.mimeType : void 0 };
|
|
@@ -9910,7 +9985,7 @@ async function readAccountNames() {
|
|
|
9910
9985
|
}
|
|
9911
9986
|
async function enrich(absolute, entry, accountNames) {
|
|
9912
9987
|
if (entry.kind === "directory" && UUID_RE4.test(entry.name)) {
|
|
9913
|
-
const meta = await readMeta(
|
|
9988
|
+
const meta = await readMeta(join12(absolute, entry.name), entry.name);
|
|
9914
9989
|
if (meta?.filename) {
|
|
9915
9990
|
entry.displayName = meta.filename;
|
|
9916
9991
|
entry.mimeType = meta.mimeType;
|
|
@@ -9969,7 +10044,7 @@ app21.get("/", requireAdminSession, async (c) => {
|
|
|
9969
10044
|
continue;
|
|
9970
10045
|
}
|
|
9971
10046
|
try {
|
|
9972
|
-
const entryPath =
|
|
10047
|
+
const entryPath = join12(absolute, name);
|
|
9973
10048
|
const s = await stat4(entryPath);
|
|
9974
10049
|
entries.push({
|
|
9975
10050
|
name,
|
|
@@ -10142,7 +10217,7 @@ app21.delete("/", requireAdminSession, async (c) => {
|
|
|
10142
10217
|
}
|
|
10143
10218
|
const dot = base.lastIndexOf(".");
|
|
10144
10219
|
const stem = dot === -1 ? base : base.slice(0, dot);
|
|
10145
|
-
const sidecarPath = UUID_RE4.test(stem) && base !== `${stem}.meta.json` ?
|
|
10220
|
+
const sidecarPath = UUID_RE4.test(stem) && base !== `${stem}.meta.json` ? join12(dirname6(absolute), `${stem}.meta.json`) : null;
|
|
10146
10221
|
await unlink2(absolute);
|
|
10147
10222
|
if (sidecarPath) {
|
|
10148
10223
|
try {
|
|
@@ -12193,10 +12268,10 @@ var sidebar_artefact_content_default = app31;
|
|
|
12193
12268
|
|
|
12194
12269
|
// server/routes/admin/health.ts
|
|
12195
12270
|
import { existsSync as existsSync19, readFileSync as readFileSync16 } from "fs";
|
|
12196
|
-
import { resolve as resolve20, join as
|
|
12271
|
+
import { resolve as resolve20, join as join13 } from "path";
|
|
12197
12272
|
var PLATFORM_ROOT7 = process.env.MAXY_PLATFORM_ROOT ?? resolve20(process.cwd(), "..");
|
|
12198
12273
|
var brandHostname = "maxy";
|
|
12199
|
-
var brandJsonPath =
|
|
12274
|
+
var brandJsonPath = join13(PLATFORM_ROOT7, "config", "brand.json");
|
|
12200
12275
|
if (existsSync19(brandJsonPath)) {
|
|
12201
12276
|
try {
|
|
12202
12277
|
const brand = JSON.parse(readFileSync16(brandJsonPath, "utf-8"));
|
|
@@ -12511,7 +12586,7 @@ function startGraphHealthTimer() {
|
|
|
12511
12586
|
}
|
|
12512
12587
|
|
|
12513
12588
|
// app/lib/whatsapp/inbound/claude-bridge.ts
|
|
12514
|
-
var
|
|
12589
|
+
var TAG21 = "[whatsapp-adaptor]";
|
|
12515
12590
|
function whatsappTurnTimeoutMs() {
|
|
12516
12591
|
return Number(process.env.WHATSAPP_PTY_TURN_TIMEOUT_MS ?? String(5 * 6e4));
|
|
12517
12592
|
}
|
|
@@ -12532,7 +12607,7 @@ async function dispatchToClaude(input) {
|
|
|
12532
12607
|
await input.reply(result.turnText);
|
|
12533
12608
|
} catch (err) {
|
|
12534
12609
|
const m = err instanceof Error ? err.message : String(err);
|
|
12535
|
-
console.error(`${
|
|
12610
|
+
console.error(`${TAG21} reject reason=reply-failed senderId=${input.senderId} message=${m}`);
|
|
12536
12611
|
}
|
|
12537
12612
|
}
|
|
12538
12613
|
function startReaper2() {
|
|
@@ -12780,7 +12855,7 @@ function clientFrom(c) {
|
|
|
12780
12855
|
);
|
|
12781
12856
|
}
|
|
12782
12857
|
var PLATFORM_ROOT8 = process.env.MAXY_PLATFORM_ROOT || "";
|
|
12783
|
-
var BRAND_JSON_PATH = PLATFORM_ROOT8 ?
|
|
12858
|
+
var BRAND_JSON_PATH = PLATFORM_ROOT8 ? join14(PLATFORM_ROOT8, "config", "brand.json") : "";
|
|
12784
12859
|
var BRAND = { productName: "Maxy", hostname: "maxy", configDir: ".maxy", domain: "getmaxy.com" };
|
|
12785
12860
|
if (BRAND_JSON_PATH && !existsSync22(BRAND_JSON_PATH)) {
|
|
12786
12861
|
console.error(`[brand] WARNING: brand.json not found at ${BRAND_JSON_PATH} \u2014 using Maxy defaults`);
|
|
@@ -12806,7 +12881,7 @@ var brandLoginOpts = {
|
|
|
12806
12881
|
bodyFont: BRAND.defaultFonts?.body,
|
|
12807
12882
|
logoContainsName: !!BRAND.logoContainsName
|
|
12808
12883
|
};
|
|
12809
|
-
var ALIAS_DOMAINS_PATH2 =
|
|
12884
|
+
var ALIAS_DOMAINS_PATH2 = join14(homedir5(), BRAND.configDir, "alias-domains.json");
|
|
12810
12885
|
function loadAliasDomains() {
|
|
12811
12886
|
try {
|
|
12812
12887
|
if (!existsSync22(ALIAS_DOMAINS_PATH2)) return null;
|
|
@@ -13252,7 +13327,7 @@ var brandScript = `<script>window.__BRAND__=${JSON.stringify({
|
|
|
13252
13327
|
function readInstalledVersion() {
|
|
13253
13328
|
try {
|
|
13254
13329
|
if (!PLATFORM_ROOT8) return "unknown";
|
|
13255
|
-
const versionFile =
|
|
13330
|
+
const versionFile = join14(PLATFORM_ROOT8, "config", `.${BRAND.hostname}-version`);
|
|
13256
13331
|
if (!existsSync22(versionFile)) return "unknown";
|
|
13257
13332
|
const content = readFileSync19(versionFile, "utf-8").trim();
|
|
13258
13333
|
return content || "unknown";
|
|
@@ -13311,14 +13386,14 @@ ${clientErrorReporterScript}
|
|
|
13311
13386
|
}
|
|
13312
13387
|
var brandedHtmlCache = /* @__PURE__ */ new Map();
|
|
13313
13388
|
function loadBrandingCache(agentSlug) {
|
|
13314
|
-
const configDir2 =
|
|
13389
|
+
const configDir2 = join14(homedir5(), BRAND.configDir);
|
|
13315
13390
|
try {
|
|
13316
|
-
const accountJsonPath =
|
|
13391
|
+
const accountJsonPath = join14(configDir2, "account.json");
|
|
13317
13392
|
if (!existsSync22(accountJsonPath)) return null;
|
|
13318
13393
|
const account = JSON.parse(readFileSync19(accountJsonPath, "utf-8"));
|
|
13319
13394
|
const accountId = account.accountId;
|
|
13320
13395
|
if (!accountId) return null;
|
|
13321
|
-
const cachePath =
|
|
13396
|
+
const cachePath = join14(configDir2, "branding-cache", accountId, `${agentSlug}.json`);
|
|
13322
13397
|
if (!existsSync22(cachePath)) return null;
|
|
13323
13398
|
return JSON.parse(readFileSync19(cachePath, "utf-8"));
|
|
13324
13399
|
} catch {
|
|
@@ -13327,8 +13402,8 @@ function loadBrandingCache(agentSlug) {
|
|
|
13327
13402
|
}
|
|
13328
13403
|
function resolveDefaultSlug() {
|
|
13329
13404
|
try {
|
|
13330
|
-
const configDir2 =
|
|
13331
|
-
const accountJsonPath =
|
|
13405
|
+
const configDir2 = join14(homedir5(), BRAND.configDir);
|
|
13406
|
+
const accountJsonPath = join14(configDir2, "account.json");
|
|
13332
13407
|
if (!existsSync22(accountJsonPath)) return null;
|
|
13333
13408
|
const account = JSON.parse(readFileSync19(accountJsonPath, "utf-8"));
|
|
13334
13409
|
return account.defaultAgent || null;
|
|
@@ -13650,7 +13725,7 @@ if (bootAccountConfig?.whatsapp) {
|
|
|
13650
13725
|
}
|
|
13651
13726
|
init({
|
|
13652
13727
|
configDir: configDirForWhatsApp,
|
|
13653
|
-
platformRoot: resolve23(process.env.MAXY_PLATFORM_ROOT ??
|
|
13728
|
+
platformRoot: resolve23(process.env.MAXY_PLATFORM_ROOT ?? join14(__dirname, "..")),
|
|
13654
13729
|
accountConfig: bootAccountConfig,
|
|
13655
13730
|
onMessage: async (msg) => {
|
|
13656
13731
|
if (process.env.WHATSAPP_PTY_BRIDGE_ENABLED === "true" && msg.text && !msg.isOwnerMirror) {
|
|
@@ -1,107 +0,0 @@
|
|
|
1
|
-
# Contextual UI Toolkit
|
|
2
|
-
|
|
3
|
-
A suite of UI primitives rendered inline in the conversation via `render-component`. Use these when structured interaction improves clarity, reduces ambiguity, or prevents errors — not for every exchange.
|
|
4
|
-
|
|
5
|
-
## Component Catalogue
|
|
6
|
-
|
|
7
|
-
| Name | Purpose | Data key fields |
|
|
8
|
-
|------|---------|-----------------|
|
|
9
|
-
| `single-select` | Choose one option from a list | `options[].{value, label, description?}`, `submitMessage?` |
|
|
10
|
-
| `multi-select` | Choose multiple options with checkboxes | `options[].{value, label, description?, badge?, defaultSelected?}`, `submitMessage?`, `skipLabel?` |
|
|
11
|
-
| `confirm` | Approve or reject a consequential action | `title`, `description?`, `items?[].{label, value}`, `confirmMessage?`, `rejectMessage?` |
|
|
12
|
-
| `info-card` | Display structured entity data (read-only) | `title`, `subtitle?`, `fields[].{label, value, type?}`, `actions?[].{label, message, variant}` |
|
|
13
|
-
| `action-list` | List of items with per-item actions | `items[].{id, label, description?, status?, actions[]}`, `doneMessage?` |
|
|
14
|
-
| `form` | Collect 2+ pieces of structured input | `fields[].{name, label, type, description?, placeholder?, required?, options?[].{value, label, description?}}`, `submitMessage?` |
|
|
15
|
-
| `progress` | Multi-step progress tracker (read-only) | `steps[].{label, status, description?}`, `completedMessage?` |
|
|
16
|
-
| `browser-viewer` | VNC iframe for browser automation | `title` |
|
|
17
|
-
| `document-editor` | Inline markdown editor — prose content | `title`, `content` (markdown), `filePath?`, `readOnly?` |
|
|
18
|
-
| `rich-content-editor` | Rich HTML editor — visual/branded content with images | `title`, `content` (HTML fragment), `filePath?`, `readOnly?`, `brandName?`, `brandLogo?` |
|
|
19
|
-
| `grid-editor` | Spreadsheet grid — tabular data editing | `title`, `columns[]`, `rows[][]`, `filePath?`, `brandName?`, `brandLogo?` |
|
|
20
|
-
|
|
21
|
-
Editors that accept `filePath` display an **Approve** button in the header bar. Omitting `filePath` renders the editor without an approval action — the user can view and edit but cannot submit content for writing. Always include `filePath` when the content requires user approval before being saved to disk.
|
|
22
|
-
|
|
23
|
-
## Editor Selection
|
|
24
|
-
|
|
25
|
-
Three editor modes serve different content types. Choose based on what the user is working with:
|
|
26
|
-
|
|
27
|
-
| Content type | Editor | Examples |
|
|
28
|
-
|---|---|---|
|
|
29
|
-
| Prose — headings, paragraphs, lists | `document-editor` | Knowledge base articles, FAQs, procedures, meeting notes, SOUL files |
|
|
30
|
-
| Visual/branded — HTML with images and layout | `rich-content-editor` | Product cards, blog posts, invoices with logos, marketing materials |
|
|
31
|
-
| Tabular — columns and rows of values | `grid-editor` | Price lists, contact details, inventory, service catalogues |
|
|
32
|
-
|
|
33
|
-
**Decision rule:** If the content has images or needs branded visual layout → `rich-content-editor`. If the content is a table of values → `grid-editor`. Everything else → `document-editor`.
|
|
34
|
-
|
|
35
|
-
All three editors support PDF export. Include `brandName` and `brandLogo` (from the business profile in the graph) when the output may be exported as a branded PDF.
|
|
36
|
-
|
|
37
|
-
## When to Use UI vs. Text
|
|
38
|
-
|
|
39
|
-
**Use a component when:**
|
|
40
|
-
- The user is choosing from 2+ discrete options → `single-select` or `multi-select`
|
|
41
|
-
- You need approval before a consequential or irreversible action → `confirm`
|
|
42
|
-
- You're presenting a structured entity (contact, task, invoice, status) → `info-card`
|
|
43
|
-
- A list has distinct per-item actions → `action-list`
|
|
44
|
-
- You need 2+ pieces of information at once → `form`
|
|
45
|
-
- A multi-step process benefits from visible progress → `progress`
|
|
46
|
-
|
|
47
|
-
**Use an editor when:**
|
|
48
|
-
- The user asks to see, review, or edit a document → `document-editor`
|
|
49
|
-
- The user needs visual/branded content (product card, invoice, brochure) → `rich-content-editor`
|
|
50
|
-
- The user asks about tabular data (prices, contacts, inventory) → `grid-editor`
|
|
51
|
-
- "Show me the FAQ" → `document-editor` with the FAQ content
|
|
52
|
-
- "Create a product card for the new range" → `rich-content-editor` with generated HTML
|
|
53
|
-
- "Show me the price list" → `grid-editor` with columns and rows
|
|
54
|
-
|
|
55
|
-
**Use plain text when:**
|
|
56
|
-
- Simple yes/no — ask in text
|
|
57
|
-
- Open-ended input — ask in text
|
|
58
|
-
- Informational response with no structure — just respond
|
|
59
|
-
- Single piece of information needed — ask in text
|
|
60
|
-
- The interaction is fast and unambiguous without UI
|
|
61
|
-
|
|
62
|
-
Components add value when they reduce ambiguity, prevent errors, or present structure that text cannot. Not every interaction needs a widget.
|
|
63
|
-
|
|
64
|
-
## How to Use
|
|
65
|
-
|
|
66
|
-
Call `render-component` with `name` (component name) and `data` (structured object matching the component's schema). Then wait for the user's response before continuing — the response arrives as a regular message.
|
|
67
|
-
|
|
68
|
-
### Form field types
|
|
69
|
-
|
|
70
|
-
The `form` component supports these field types:
|
|
71
|
-
|
|
72
|
-
| Type | Renders as | Value type | Notes |
|
|
73
|
-
|------|-----------|------------|-------|
|
|
74
|
-
| `text` | Text input | string | Default for short text |
|
|
75
|
-
| `textarea` | Multi-line text | string | For longer input |
|
|
76
|
-
| `select` | Dropdown | string | Requires `options[]` with `{value, label, description?}` — descriptions are appended to the option label |
|
|
77
|
-
| `checkbox` | Toggle checkbox | boolean | Uses the shared Checkbox component. Label appears inline next to the checkbox, not above it |
|
|
78
|
-
| `number` | Number input | string | |
|
|
79
|
-
| `date` | Date input | string | |
|
|
80
|
-
|
|
81
|
-
All field types accept optional `description` (help text below the label) and `defaultValue` (pre-populated value — used if the user doesn't interact with the field) properties.
|
|
82
|
-
|
|
83
|
-
For `checkbox` fields, the submitted JSON value is `true` or `false` (boolean), not a string. For all other types, the value is a string.
|
|
84
|
-
|
|
85
|
-
### Submit message templates
|
|
86
|
-
|
|
87
|
-
Most components accept a `submitMessage` field that controls the format of the message sent back:
|
|
88
|
-
- `single-select`: `"Selected: {{value}}"` — `{{value}}` and `{{label}}` are replaced
|
|
89
|
-
- `multi-select`: `"Selected: {{values}}"` — `{{values}}` is comma-separated selected values
|
|
90
|
-
- `form`: `"Form submitted: {{json}}"` — `{{json}}` is a JSON string of `{name: value}` pairs. Per-field placeholders `{{fieldName}}` are also supported (e.g. `"model={{model}}, live={{liveMemory}}"`). `{{values}}` is an alias for `{{json}}`
|
|
91
|
-
- `confirm`: `confirmMessage` / `rejectMessage` — literal strings
|
|
92
|
-
- `action-list`: each item action has its own `message` field
|
|
93
|
-
- `info-card`: each action has its own `message` field
|
|
94
|
-
|
|
95
|
-
When no template is provided, sensible defaults are used.
|
|
96
|
-
|
|
97
|
-
### Backwards-compatible aliases
|
|
98
|
-
|
|
99
|
-
These legacy names still work: `output-style` → `single-select`, `thinking-view` → `single-select`, `plugin-selector` → `multi-select`.
|
|
100
|
-
|
|
101
|
-
## Anti-patterns
|
|
102
|
-
|
|
103
|
-
- Rendering a `single-select` for a yes/no question — just ask in text
|
|
104
|
-
- Rendering a `form` for a single field — just ask in text
|
|
105
|
-
- Chaining multiple components in one response — render one, wait for the response, then decide if another is needed
|
|
106
|
-
- Using `info-card` for data the user already knows — only show structured display when it adds value
|
|
107
|
-
- Rendering `progress` once and never updating it — progress is for multi-step processes where you re-render with updated step statuses
|