openmausbot 0.1.86 → 0.1.87
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/README.md +2 -2
- package/dist/assets/{abnfDiagram-O67JEVCF-DpUZNnFD.js → abnfDiagram-O67JEVCF-BTiuL79F.js} +1 -1
- package/dist/assets/{arc-DW7mrI4p.js → arc-C88Blc2v.js} +1 -1
- package/dist/assets/{architectureDiagram-NJMV4G6O-CyeZ5WLZ.js → architectureDiagram-NJMV4G6O-BZsJIZrb.js} +1 -1
- package/dist/assets/{blockDiagram-BEXU5L5S-CBVaNjO7.js → blockDiagram-BEXU5L5S-CVeMe9Ah.js} +1 -1
- package/dist/assets/{c4Diagram-YGBWAQC7-sf8d41uT.js → c4Diagram-YGBWAQC7-Cedk3Bhx.js} +1 -1
- package/dist/assets/channel-taCNRlUU.js +1 -0
- package/dist/assets/{chunk-2Q5K7J3B-BOGvy7Y_.js → chunk-2Q5K7J3B-D-VRqzTB.js} +1 -1
- package/dist/assets/{chunk-3FUC2YCW-BiF9zyiz.js → chunk-3FUC2YCW-BChov01Y.js} +2 -2
- package/dist/assets/{chunk-5VM5RSS4-EFuZ1SBZ.js → chunk-5VM5RSS4-C-TfwvHy.js} +1 -1
- package/dist/assets/{chunk-GTNCS2PH-pJLNSAT7.js → chunk-GTNCS2PH-Db7CGj5Y.js} +1 -1
- package/dist/assets/{chunk-GWA4HPMP-Cb-3_3IT.js → chunk-GWA4HPMP-CNvIfqKT.js} +1 -1
- package/dist/assets/{chunk-JWPE2WC7-DAf5aogn.js → chunk-JWPE2WC7-C_bEp5tq.js} +1 -1
- package/dist/assets/{chunk-NETBCI7D-DR3kiwZz.js → chunk-NETBCI7D-Coi4BOls.js} +1 -1
- package/dist/assets/{chunk-WEXAMYUT-BmU8PeyM.js → chunk-WEXAMYUT-CzylEuL3.js} +1 -1
- package/dist/assets/{chunk-XXDRQBXY-Buh8g8OT.js → chunk-XXDRQBXY-m7p7ztaD.js} +1 -1
- package/dist/assets/{classDiagram-v2-NBCMYWYE-BurfmMWM.js → classDiagram-v2-NBCMYWYE-BMy6o9x2.js} +1 -1
- package/dist/assets/{cose-bilkent-JH36ORCC-KkGZUEkU.js → cose-bilkent-JH36ORCC-BDQ__0_J.js} +1 -1
- package/dist/assets/{cynefin-OW5HDTMX-C3PpV96d.js → cynefin-OW5HDTMX-Djv4ghUb.js} +1 -1
- package/dist/assets/{cynefinDiagram-VND7K2PF-Dt8JwJaI.js → cynefinDiagram-VND7K2PF-BULrlGYH.js} +1 -1
- package/dist/assets/{dagre-6A5THRUB-DkjN8266.js → dagre-6A5THRUB-D-jrT_hU.js} +1 -1
- package/dist/assets/{diagram-22UHCM2B-DUAJS0JV.js → diagram-22UHCM2B-p2UhQmfL.js} +1 -1
- package/dist/assets/{diagram-3UASUU5V-Dw4KhF1a.js → diagram-3UASUU5V-DHvGWZ7q.js} +1 -1
- package/dist/assets/{diagram-ATOU4E4O-Dd1gmQqf.js → diagram-ATOU4E4O-ChhKDot-.js} +1 -1
- package/dist/assets/{diagram-CDSNMT55-CjwGs0Yw.js → diagram-CDSNMT55-B-T7A43R.js} +1 -1
- package/dist/assets/{diagram-MLGK6HIB-BL-69Zrq.js → diagram-MLGK6HIB-CJm8ENNY.js} +1 -1
- package/dist/assets/{diagram-MPIPVDR6-5Zi2N7wo.js → diagram-MPIPVDR6-510tlza8.js} +1 -1
- package/dist/assets/{ebnfDiagram-ZINNZB2B-D9NekWUX.js → ebnfDiagram-ZINNZB2B-PkvRPHel.js} +1 -1
- package/dist/assets/{elk-276RUBZZ-CkaSQjxw.js → elk-276RUBZZ-BVBVPoX3.js} +1 -1
- package/dist/assets/{erDiagram-OPXOYQCR-CdAVBV8-.js → erDiagram-OPXOYQCR-BgYMVm7L.js} +1 -1
- package/dist/assets/{flowDiagram-KWPJA3E3-Bm_lWWH7.js → flowDiagram-KWPJA3E3-BGqsnc_3.js} +1 -1
- package/dist/assets/{ganttDiagram-FUAMR5RP-FZ8ejYWJ.js → ganttDiagram-FUAMR5RP-D_-UeReH.js} +1 -1
- package/dist/assets/{gitGraphDiagram-X574FWY7-Dlv9rizZ.js → gitGraphDiagram-X574FWY7-D0uooONx.js} +1 -1
- package/dist/assets/index-BwdMAQ6p.css +1 -0
- package/dist/assets/index-ByJennr7.js +581 -0
- package/dist/assets/{index-h9FFNwTu.js → index-zRsJERGJ.js} +1 -1
- package/dist/assets/{infoDiagram-VRGFBTTK-BdQ_gW5B.js → infoDiagram-VRGFBTTK-B9e1z77f.js} +1 -1
- package/dist/assets/{ishikawaDiagram-OU5B5YK6-EwssEp2X.js → ishikawaDiagram-OU5B5YK6-BB4vNwvD.js} +1 -1
- package/dist/assets/{journeyDiagram-ZHPQQLJL-BX1G1vOd.js → journeyDiagram-ZHPQQLJL-DpkosOTB.js} +1 -1
- package/dist/assets/{kanban-definition-PNTS6WVX-BB4kAsyC.js → kanban-definition-PNTS6WVX-DbDP4kUu.js} +1 -1
- package/dist/assets/{linear-3su2TG93.js → linear-DBJGPmjT.js} +1 -1
- package/dist/assets/{mermaid.core-CDD1E0s9.js → mermaid.core-DWPRi-ue.js} +5 -5
- package/dist/assets/{mindmap-definition-NLK3R4M7-Ba50MsNK.js → mindmap-definition-NLK3R4M7-nszBbJmx.js} +1 -1
- package/dist/assets/{pegDiagram-GJSIUBJH-DZLkkZlM.js → pegDiagram-GJSIUBJH-CWykJWu_.js} +1 -1
- package/dist/assets/{pieDiagram-5QR66LMP-DaTz77Uq.js → pieDiagram-5QR66LMP-g3Da-Vnv.js} +1 -1
- package/dist/assets/{quadrantDiagram-O4NWA36T-CpaiidpR.js → quadrantDiagram-O4NWA36T-Wzrlpzgh.js} +1 -1
- package/dist/assets/{railroadDiagram-XR7U4H2S-mN4JrMeI.js → railroadDiagram-XR7U4H2S-moHTFMC0.js} +1 -1
- package/dist/assets/{requirementDiagram-PLB6GJNP-BSNDRpg7.js → requirementDiagram-PLB6GJNP-CwfXdFH8.js} +1 -1
- package/dist/assets/{sankeyDiagram-IPEJSGJF-FngzGNug.js → sankeyDiagram-IPEJSGJF-3ZROYvnq.js} +1 -1
- package/dist/assets/{sequenceDiagram-PO4LG4MO-CM4pCC3L.js → sequenceDiagram-PO4LG4MO-E1TBebP8.js} +1 -1
- package/dist/assets/{sizeCapture-INFHLROL-CRICHXkw.js → sizeCapture-INFHLROL-DHKEU5pu.js} +1 -1
- package/dist/assets/{stateDiagram-v2-GCMORJYK-oyTWpxH2.js → stateDiagram-v2-GCMORJYK-BguEj09D.js} +1 -1
- package/dist/assets/{swimlanes-2SLR337P-D9h9mPBq.js → swimlanes-2SLR337P-C7IDYnhd.js} +1 -1
- package/dist/assets/swimlanesDiagram-TC7HE7FX-BpEdoB2e.js +8 -0
- package/dist/assets/{timeline-definition-EJHVYXUP-BJUlN9bW.js → timeline-definition-EJHVYXUP-DCW-F5PW.js} +1 -1
- package/dist/assets/{usecaseDiagram-POWQR4AR-3uBNkbAb.js → usecaseDiagram-POWQR4AR-xBMFtWYV.js} +1 -1
- package/dist/assets/{vennDiagram-UO4OBE2U-D2odalzT.js → vennDiagram-UO4OBE2U-B5nIkuH8.js} +1 -1
- package/dist/assets/{wardleyDiagram-VNRHLVJA-DHgFaB9S.js → wardleyDiagram-VNRHLVJA-DYFAhdbr.js} +1 -1
- package/dist/assets/{xychartDiagram-PMCCYNJV-w5GYM2h0.js → xychartDiagram-PMCCYNJV-DeXwX6Cz.js} +1 -1
- package/dist/index.html +2 -2
- package/dist-server/browser-proxy.js +2 -1
- package/dist-server/container-mcp.js +1029 -164
- package/dist-server/drivers/agents-proxy.js +64 -10
- package/dist-server/drivers/phone-proxy.js +33 -2
- package/dist-server/index.js +14577 -9001
- package/dist-server/local-computer-proxy.js +3 -3
- package/dist-server/local-computer.js +53 -17
- package/dist-server/openmausbot.js +3981 -1165
- package/dist-server/pair-cli.js +3981 -1165
- package/dist-server/permission-proxy.js +29 -9
- package/dist-server/server/attachments.js +55 -0
- package/dist-server/server/auto-approve.js +4 -3
- package/dist-server/server/auto-vm-claims.js +1 -0
- package/dist-server/server/bot-package.js +18 -292
- package/dist-server/server/bot-profile.js +5 -17
- package/dist-server/server/box-create-idempotency.js +3 -3
- package/dist-server/server/box.js +57 -55
- package/dist-server/server/browser-proxy.js +3 -2
- package/dist-server/server/browser-runtime.js +37 -1
- package/dist-server/server/cli.js +2 -2
- package/dist-server/server/command-allowlist.js +154 -0
- package/dist-server/server/computer-wait.js +22 -9
- package/dist-server/server/config.js +59 -9
- package/dist-server/server/connector-verdict.js +141 -0
- package/dist-server/server/container-computer.js +1 -1
- package/dist-server/server/default-model-selection.js +9 -0
- package/dist-server/server/delegations.js +85 -34
- package/dist-server/server/drivers/acp/core.js +57 -15
- package/dist-server/server/drivers/agents-call.js +42 -3
- package/dist-server/server/drivers/agents-catalog.js +34 -4
- package/dist-server/server/drivers/boxagent.js +174 -36
- package/dist-server/server/drivers/builtIn.js +2 -0
- package/dist-server/server/drivers/chat-box-tools.js +90 -0
- package/dist-server/server/drivers/chat-images.js +54 -0
- package/dist-server/server/drivers/chat-mcp-tools.js +38 -8
- package/dist-server/server/drivers/chat-tool-approval.js +70 -18
- package/dist-server/server/drivers/claude.js +7 -2
- package/dist-server/server/drivers/codex-instructions.js +48 -6
- package/dist-server/server/drivers/codex-tool-surface.js +20 -0
- package/dist-server/server/drivers/codex.js +95 -30
- package/dist-server/server/drivers/mistral.js +115 -0
- package/dist-server/server/drivers/openai-chat.js +155 -32
- package/dist-server/server/drivers/openai-compat.js +1 -0
- package/dist-server/server/drivers/permission-command.js +35 -0
- package/dist-server/server/drivers/phone-proxy.js +47 -2
- package/dist-server/server/drivers/pi.js +75 -4
- package/dist-server/server/drivers/prompt-split.js +87 -0
- package/dist-server/server/fleet.js +1 -1
- package/dist-server/server/group-thread-usage.js +103 -0
- package/dist-server/server/harness/registry.js +1 -0
- package/dist-server/server/hosted-models.js +1 -1
- package/dist-server/server/index.js +1385 -322
- package/dist-server/server/local-computer.js +72 -23
- package/dist-server/server/managed-desktop.js +1 -1
- package/dist-server/server/mcp-bridge.js +6 -6
- package/dist-server/server/new-bot-defaults.js +115 -0
- package/dist-server/server/org-library.js +1051 -0
- package/dist-server/server/package-export.js +627 -39
- package/dist-server/server/package-import.js +458 -0
- package/dist-server/server/package-parts.js +87 -0
- package/dist-server/server/peer-approval.js +16 -7
- package/dist-server/server/peer-roster.js +10 -0
- package/dist-server/server/presets.js +492 -0
- package/dist-server/server/provider-key-check.js +3 -2
- package/dist-server/server/published-teams.js +80 -0
- package/dist-server/server/room-handoffs.js +19 -3
- package/dist-server/server/routes/bot-presets.js +25 -0
- package/dist-server/server/routine-requests.js +6 -3
- package/dist-server/server/routines.js +41 -1
- package/dist-server/server/section-context.js +10 -0
- package/dist-server/server/skills.js +34 -61
- package/dist-server/server/steer-queue.js +3 -2
- package/dist-server/server/store.js +259 -15
- package/dist-server/server/surface.js +10 -4
- package/dist-server/server/system-prompt.js +12 -4
- package/dist-server/server/team-backup.js +6 -2
- package/dist-server/server/team-computers.js +10 -0
- package/dist-server/server/team-library.js +6 -2
- package/dist-server/server/team-manifest.js +4 -3
- package/dist-server/server/team-setup-requests.js +16 -5
- package/dist-server/server/thread-events.js +17 -1
- package/dist-server/server/turn-context.js +2 -0
- package/dist-server/server/turn-watchdog.js +12 -3
- package/dist-server/server/usage-ledger.js +12 -1
- package/dist-server/server/vps-computer.js +139 -51
- package/dist-server/server/vps-container-mcp.js +7 -0
- package/dist-server/server/vps-ssh.js +36 -7
- package/dist-server/server/workspace-backup-policy.js +12 -0
- package/dist-server/server/workspace-backup.js +2 -2
- package/dist-server/server/workspace.js +1 -1
- package/dist-server/shared/approval-mode.js +1 -1
- package/dist-server/shared/ask-question.js +188 -12
- package/dist-server/shared/bot-profile.js +15 -0
- package/dist-server/shared/command-allowlist.js +1 -0
- package/dist-server/shared/credential-request.js +1 -1
- package/dist-server/shared/package-format.js +967 -0
- package/dist-server/shared/skill-md.js +64 -0
- package/dist-server/shared/team-backup.js +7 -0
- package/dist-server/shared/wire.js +4 -0
- package/dist-server/vps-container-mcp.js +1129 -170
- package/enterprise/README.md +1 -1
- package/package.json +1 -1
- package/dist/assets/channel-BXFkgus9.js +0 -1
- package/dist/assets/index-BGgqlWPJ.css +0 -1
- package/dist/assets/index-BtHo8ZKl.js +0 -577
- package/dist/assets/swimlanesDiagram-TC7HE7FX-BWrvB2ak.js +0 -8
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
// Preset bots for New bot (server/presets.ts has the store and the rules).
|
|
2
|
+
// GET lists what New bot offers: the organization's presets first, then
|
|
3
|
+
// imported files', never one from a withdrawn release. DELETE removes an
|
|
4
|
+
// imported preset (the person's own click; no confirm step). Organization
|
|
5
|
+
// presets are Admin's to remove. Both are admin-scoped by default in
|
|
6
|
+
// server/request-auth.ts, like the New bot defaults: a preset adds skills
|
|
7
|
+
// and starter notes to the bot made from it.
|
|
8
|
+
import { listBotPresets, ORG_PRESET_REMOVE_MESSAGE, PRESET_UNAVAILABLE_MESSAGE } from "../presets.js";
|
|
9
|
+
import { PASS } from "./table.js";
|
|
10
|
+
export function createBotPresetRoutes(deps) {
|
|
11
|
+
return async ({ res, path, method, json }) => {
|
|
12
|
+
if (path === "/api/bot-presets" && method === "GET") {
|
|
13
|
+
return json(res, 200, { presets: listBotPresets(deps.presets, deps.orgStatuses()) });
|
|
14
|
+
}
|
|
15
|
+
const m = path.match(/^\/api\/bot-presets\/([\w-]+)$/);
|
|
16
|
+
if (!m || method !== "DELETE")
|
|
17
|
+
return PASS;
|
|
18
|
+
const removed = deps.presets.removeFilePreset(m[1]);
|
|
19
|
+
if (removed === "not_found")
|
|
20
|
+
return json(res, 404, { error: PRESET_UNAVAILABLE_MESSAGE });
|
|
21
|
+
if (removed === "organization")
|
|
22
|
+
return json(res, 409, { error: ORG_PRESET_REMOVE_MESSAGE });
|
|
23
|
+
return json(res, 200, { ok: true });
|
|
24
|
+
};
|
|
25
|
+
}
|
|
@@ -923,7 +923,7 @@ export class RoutineRequestService {
|
|
|
923
923
|
if (refusal)
|
|
924
924
|
throw new RoutineRequestError(refusal, 403);
|
|
925
925
|
}
|
|
926
|
-
await this.requireCloudReadiness(operation);
|
|
926
|
+
await this.requireCloudReadiness(operation, botId);
|
|
927
927
|
// The readiness probe is asynchronous. Another request can edit or
|
|
928
928
|
// delete the routine while it is in flight, so re-check the captured
|
|
929
929
|
// revision before rendering and persisting the confirmation snapshot.
|
|
@@ -1006,7 +1006,7 @@ export class RoutineRequestService {
|
|
|
1006
1006
|
}
|
|
1007
1007
|
throw new RoutineRequestError(result.state === "invalid" ? result.error : "The routine change could not be applied", result.state === "invalid" ? result.status : 409);
|
|
1008
1008
|
}
|
|
1009
|
-
async requireCloudReadiness(operation) {
|
|
1009
|
+
async requireCloudReadiness(operation, proposerBotId) {
|
|
1010
1010
|
if (!this.cloudReady || operation.action === "pause" || operation.action === "delete")
|
|
1011
1011
|
return;
|
|
1012
1012
|
const definition = effectiveDefinition(operation, this.routines);
|
|
@@ -1014,7 +1014,10 @@ export class RoutineRequestService {
|
|
|
1014
1014
|
return;
|
|
1015
1015
|
let readiness;
|
|
1016
1016
|
try {
|
|
1017
|
-
|
|
1017
|
+
const targetBotId = operation.action === "create"
|
|
1018
|
+
? operation.forBot?.botId ?? proposerBotId
|
|
1019
|
+
: this.routines.listRoutines().find(routine => routine.id === operation.routineId)?.botId ?? proposerBotId;
|
|
1020
|
+
readiness = await this.cloudReady(targetBotId);
|
|
1018
1021
|
}
|
|
1019
1022
|
catch (error) {
|
|
1020
1023
|
const detail = error instanceof Error ? error.message : String(error);
|
|
@@ -7,7 +7,24 @@ import { writeFileAtomic } from "./atomic.js";
|
|
|
7
7
|
import { redactSecretsInText } from "./redact.js";
|
|
8
8
|
import { normalizeCronSchedule, nextCronRuns } from "../shared/routine-schedule.js";
|
|
9
9
|
import { isRoutineProblemRun } from "../shared/routines.js";
|
|
10
|
+
import { ROUTINE_PARTS } from "./package-parts.js";
|
|
10
11
|
const persistedSourceThreadId = z.string().trim().min(1).optional().catch(undefined);
|
|
12
|
+
const HASH = /^[a-f0-9]{64}$/;
|
|
13
|
+
function loadInstalledPackage(value) {
|
|
14
|
+
if (!value || typeof value !== "object")
|
|
15
|
+
return undefined;
|
|
16
|
+
const stamp = value;
|
|
17
|
+
if (typeof stamp.installId !== "string" || !/^[a-f0-9]{32}$/.test(stamp.installId) || typeof stamp.key !== "string" || !stamp.key || stamp.key.length > 80)
|
|
18
|
+
return undefined;
|
|
19
|
+
const parts = stamp.parts;
|
|
20
|
+
if (!parts || typeof parts !== "object" || !ROUTINE_PARTS.every((part) => HASH.test(String(parts[part]?.r)) && HASH.test(String(parts[part]?.w))))
|
|
21
|
+
return undefined;
|
|
22
|
+
return {
|
|
23
|
+
installId: stamp.installId,
|
|
24
|
+
key: stamp.key,
|
|
25
|
+
parts: Object.fromEntries(ROUTINE_PARTS.map((part) => [part, { r: parts[part].r, w: parts[part].w }])),
|
|
26
|
+
};
|
|
27
|
+
}
|
|
11
28
|
const webhookRunReceiptSchema = z.object({
|
|
12
29
|
webhookId: z.string().min(1).max(200),
|
|
13
30
|
deliveryId: z.string().min(1).max(200),
|
|
@@ -490,9 +507,12 @@ export class RoutineManager {
|
|
|
490
507
|
overlap: routine.overlap === "queue" ? "queue" : undefined,
|
|
491
508
|
skippedRuns: Number.isSafeInteger(routine.skippedRuns) && routine.skippedRuns > 0 ? routine.skippedRuns : undefined,
|
|
492
509
|
lastSkippedAt: Number.isSafeInteger(routine.lastSkippedAt) && routine.lastSkippedAt >= 0 && routine.lastSkippedAt <= MAX_DATE_MS ? routine.lastSkippedAt : undefined,
|
|
510
|
+
installedPackage: loadInstalledPackage(routine.installedPackage),
|
|
493
511
|
};
|
|
494
512
|
if (loaded.timeoutMinutes === undefined)
|
|
495
513
|
delete loaded.timeoutMinutes;
|
|
514
|
+
if (loaded.installedPackage === undefined)
|
|
515
|
+
delete loaded.installedPackage;
|
|
496
516
|
delete loaded.failureStreak;
|
|
497
517
|
return [loaded];
|
|
498
518
|
})
|
|
@@ -582,7 +602,27 @@ export class RoutineManager {
|
|
|
582
602
|
.sort((a, b) => (b.finishedAt ?? b.createdAt) - (a.finishedAt ?? a.createdAt) || b.createdAt - a.createdAt);
|
|
583
603
|
const success = outcomes.findIndex(run => run.status === "completed");
|
|
584
604
|
const failures = success < 0 ? outcomes.length : success;
|
|
585
|
-
|
|
605
|
+
const { installedPackage: _installedPackage, ...visible } = cloneRoutine(routine);
|
|
606
|
+
return { ...visible, ...(failures ? { failureStreak: failures } : {}) };
|
|
607
|
+
}
|
|
608
|
+
/** Routines added from the organization's library, with their stamps. */
|
|
609
|
+
packageStamps() {
|
|
610
|
+
return this.routines.flatMap((routine) => routine.installedPackage
|
|
611
|
+
? [{ routineId: routine.id, botId: routine.botId, enabled: routine.enabled, stamp: structuredClone(routine.installedPackage) }]
|
|
612
|
+
: []);
|
|
613
|
+
}
|
|
614
|
+
/** Record which organization install a just-created routine belongs to. */
|
|
615
|
+
stampInstalledPackage(id, stamp) {
|
|
616
|
+
const clean = loadInstalledPackage(stamp);
|
|
617
|
+
if (!clean)
|
|
618
|
+
throw new Error("Invalid routine package stamp");
|
|
619
|
+
if (!this.routines.some((routine) => routine.id === id))
|
|
620
|
+
return false;
|
|
621
|
+
this.commitMutation(() => {
|
|
622
|
+
const routine = this.routines.find((candidate) => candidate.id === id);
|
|
623
|
+
routine.installedPackage = clean;
|
|
624
|
+
});
|
|
625
|
+
return true;
|
|
586
626
|
}
|
|
587
627
|
/** Whether this computer should stay awake for routines: a run is in
|
|
588
628
|
* flight, or an enabled routine is due within the horizon. The schedule
|
|
@@ -9,6 +9,7 @@ import { join } from "node:path";
|
|
|
9
9
|
import { z } from "zod";
|
|
10
10
|
import { writeFileAtomic } from "./atomic.js";
|
|
11
11
|
import { DATA_DIR } from "./config.js";
|
|
12
|
+
import { renamePublishedTeam } from "./published-teams.js";
|
|
12
13
|
export const SECTION_CONTEXT_MAX_BYTES = 24_000;
|
|
13
14
|
export const SECTION_CONTEXTS_FILE = join(DATA_DIR, "section-contexts.json");
|
|
14
15
|
const sectionContextRecordSchema = z.object({ text: z.string(), updatedAt: z.number().finite() });
|
|
@@ -94,6 +95,15 @@ export function changeEmptySection(name, nextName) {
|
|
|
94
95
|
if (nextName !== null && context)
|
|
95
96
|
data.contexts[nextName] = context;
|
|
96
97
|
saveFile(data);
|
|
98
|
+
// Every team rename and removal funnels through here, so the team's
|
|
99
|
+
// published package id and keys follow it (published-teams.ts). The team
|
|
100
|
+
// itself is already renamed; a failure here costs only key stability.
|
|
101
|
+
try {
|
|
102
|
+
renamePublishedTeam(name, nextName);
|
|
103
|
+
}
|
|
104
|
+
catch (error) {
|
|
105
|
+
console.warn(`[teams] Could not move the shared-package record for this team: ${error.message}`);
|
|
106
|
+
}
|
|
97
107
|
}
|
|
98
108
|
export function readSectionContext(section) {
|
|
99
109
|
const record = loadFile().contexts[sectionContextKey(section)];
|
|
@@ -34,14 +34,11 @@ import { DATA_DIR } from "./config.js";
|
|
|
34
34
|
import { redactSecretsInText } from "./redact.js";
|
|
35
35
|
import { LEARN_SOURCE_PREFIX } from "./skill-learn.js";
|
|
36
36
|
import { workspaceDir } from "./workspace.js";
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
export
|
|
42
|
-
export const DESCRIPTION_MAX = 1024;
|
|
43
|
-
/** One SKILL.md may be at most this large; the spec recommends <5k tokens. */
|
|
44
|
-
export const SKILL_FILE_MAX_BYTES = 256 * 1024;
|
|
37
|
+
import { isSkillName, parseSkillMd, scanSkillText, SKILL_FILE_MAX_BYTES } from "../shared/skill-md.js";
|
|
38
|
+
// SKILL.md parsing and the static scan live in shared/ so the package
|
|
39
|
+
// format (Admin and the renderer included) validates skills with the exact
|
|
40
|
+
// same rules; re-exported here under their historical path.
|
|
41
|
+
export { DESCRIPTION_MAX, isSkillName, parseSkillMd, scanSkillText, SKILL_FILE_MAX_BYTES, SKILL_NAME_MAX, } from "../shared/skill-md.js";
|
|
45
42
|
/** Index budget: name+description lines only, ~100 tokens per skill. */
|
|
46
43
|
export const INDEX_MAX_SKILLS = 30;
|
|
47
44
|
export const INDEX_MAX_BYTES = 4_000;
|
|
@@ -51,58 +48,6 @@ export const STAGED_GIST_MAX = 240;
|
|
|
51
48
|
/** Learned skills are duplicated onto their durable review card. Keep that
|
|
52
49
|
* exact review payload bounded while leaving fetched skill imports unchanged. */
|
|
53
50
|
export const STAGED_SKILL_FILE_MAX_BYTES = 32 * 1024;
|
|
54
|
-
export function isSkillName(name) {
|
|
55
|
-
return name.length >= 1 && name.length <= SKILL_NAME_MAX && SKILL_NAME.test(name);
|
|
56
|
-
}
|
|
57
|
-
/** Minimal frontmatter reader for the two required keys plus the two we
|
|
58
|
-
* display. Deliberately not a YAML engine: values are single-line strings in
|
|
59
|
-
* every skill the spec's own examples show, and a parser that cannot
|
|
60
|
-
* evaluate anchors or tags cannot be surprised by them. */
|
|
61
|
-
export function parseSkillMd(raw) {
|
|
62
|
-
const match = raw.match(/^---\r?\n([\s\S]*?)\r?\n---\r?\n?([\s\S]*)$/);
|
|
63
|
-
if (!match)
|
|
64
|
-
return { error: "SKILL.md has no YAML frontmatter (--- block) at the top" };
|
|
65
|
-
const fields = {};
|
|
66
|
-
for (const line of match[1].split(/\r?\n/)) {
|
|
67
|
-
const kv = line.match(/^([A-Za-z][\w-]*):\s*(.*)$/);
|
|
68
|
-
if (!kv)
|
|
69
|
-
continue;
|
|
70
|
-
fields[kv[1].toLowerCase()] = kv[2].replace(/^["']|["']$/g, "").trim();
|
|
71
|
-
}
|
|
72
|
-
const name = fields.name ?? "";
|
|
73
|
-
const description = fields.description ?? "";
|
|
74
|
-
if (!isSkillName(name)) {
|
|
75
|
-
return { error: `frontmatter name ${JSON.stringify(name)} is not a valid skill name (lowercase, hyphens, max ${SKILL_NAME_MAX})` };
|
|
76
|
-
}
|
|
77
|
-
if (!description || description.length > DESCRIPTION_MAX) {
|
|
78
|
-
return { error: `frontmatter description is required and must be at most ${DESCRIPTION_MAX} characters` };
|
|
79
|
-
}
|
|
80
|
-
return {
|
|
81
|
-
name,
|
|
82
|
-
description,
|
|
83
|
-
license: fields.license || undefined,
|
|
84
|
-
compatibility: fields.compatibility || undefined,
|
|
85
|
-
body: match[2] ?? "",
|
|
86
|
-
};
|
|
87
|
-
}
|
|
88
|
-
/** Static red flags before a human review. Presence is a warning shown in
|
|
89
|
-
* the review screen, never a silent rejection — the reviewer decides. These
|
|
90
|
-
* are the three patterns the public registry audits actually caught. */
|
|
91
|
-
export function scanSkillText(raw) {
|
|
92
|
-
const warnings = [];
|
|
93
|
-
if (/[A-Za-z0-9+/]{120,}={0,2}/.test(raw)) {
|
|
94
|
-
warnings.push("contains a long base64-looking blob — a common wrapper for hidden instructions or payloads");
|
|
95
|
-
}
|
|
96
|
-
if (/\b(curl|wget)\b[^\n]{0,200}\|\s*(ba|z|da)?sh\b/.test(raw)) {
|
|
97
|
-
warnings.push("pipes a download straight into a shell (curl|sh) — never enable without understanding why");
|
|
98
|
-
}
|
|
99
|
-
// zero-width and bidi-control characters hide text from the reviewer while
|
|
100
|
-
// the model still reads it — the invisible-instruction trick
|
|
101
|
-
if (/[\u200B-\u200F\u202A-\u202E\u2060-\u2064\uFEFF]/.test(raw)) {
|
|
102
|
-
warnings.push("contains invisible Unicode characters (zero-width or bidi controls) — text you cannot see");
|
|
103
|
-
}
|
|
104
|
-
return warnings;
|
|
105
|
-
}
|
|
106
51
|
const skillManifestEntrySchema = z.object({
|
|
107
52
|
description: z.string(),
|
|
108
53
|
enabled: z.boolean(),
|
|
@@ -115,6 +60,13 @@ const skillManifestEntrySchema = z.object({
|
|
|
115
60
|
skippedFiles: z.array(z.string()),
|
|
116
61
|
appliedStageId: z.string().optional(),
|
|
117
62
|
storageRevision: z.string().regex(/^[a-f0-9]{64}$/).optional(),
|
|
63
|
+
package: z.object({
|
|
64
|
+
installId: z.string().regex(/^[a-f0-9]{32}$/),
|
|
65
|
+
key: z.string().min(1).max(64),
|
|
66
|
+
release: z.string().min(1).max(40),
|
|
67
|
+
r: z.string().regex(/^[a-f0-9]{64}$/),
|
|
68
|
+
w: z.string().regex(/^[a-f0-9]{64}$/),
|
|
69
|
+
}).optional(),
|
|
118
70
|
});
|
|
119
71
|
const skillManifestSchema = z.record(z.string(), skillManifestEntrySchema);
|
|
120
72
|
const managedLinksSchema = z.array(z.string());
|
|
@@ -498,7 +450,7 @@ function skillContentMatches(botId, name, entry) {
|
|
|
498
450
|
}
|
|
499
451
|
}
|
|
500
452
|
function skillListing(botId, name, entry) {
|
|
501
|
-
const { appliedStageId, storageRevision: _storageRevision, ...visible } = entry;
|
|
453
|
+
const { appliedStageId, storageRevision: _storageRevision, package: _package, ...visible } = entry;
|
|
502
454
|
const intact = skillContentMatches(botId, name, entry);
|
|
503
455
|
return {
|
|
504
456
|
name,
|
|
@@ -563,6 +515,26 @@ export function installSkill(botId, source, files) {
|
|
|
563
515
|
return prepared;
|
|
564
516
|
return installPreparedSkill(botId, source, prepared, { enabled: false });
|
|
565
517
|
}
|
|
518
|
+
/** The organization library's path: a skill the organization's Admin
|
|
519
|
+
* published arrives switched ON (the Admin is the review), with the install
|
|
520
|
+
* stamp that lets the library find it again. `source` is chosen by the
|
|
521
|
+
* caller (`org:<ref>@<release>`), never read from the package. */
|
|
522
|
+
export function installOrgSkill(botId, source, skillMd, stamp) {
|
|
523
|
+
if (!source.startsWith("org:"))
|
|
524
|
+
return { error: "organization skills need an org: source" };
|
|
525
|
+
const prepared = preparedSkillFiles([{ path: "SKILL.md", content: skillMd }]);
|
|
526
|
+
if ("error" in prepared)
|
|
527
|
+
return prepared;
|
|
528
|
+
if (prepared.parsed.name !== stamp.key)
|
|
529
|
+
return { error: `the skill is named "${prepared.parsed.name}", not "${stamp.key}"` };
|
|
530
|
+
return installPreparedSkill(botId, source, prepared, { enabled: true, package: { ...stamp } });
|
|
531
|
+
}
|
|
532
|
+
/** A bot's skills that an organization install added, with their stamps. */
|
|
533
|
+
export function skillPackageStamps(botId) {
|
|
534
|
+
return Object.entries(readManifest(botId)).flatMap(([name, entry]) => entry.package
|
|
535
|
+
? [{ name, enabled: entry.enabled, stamp: { ...entry.package } }]
|
|
536
|
+
: []);
|
|
537
|
+
}
|
|
566
538
|
export function setSkillEnabled(botId, name, enabled) {
|
|
567
539
|
if (!isSkillName(name))
|
|
568
540
|
return { error: "invalid skill name" };
|
|
@@ -936,6 +908,7 @@ function installPreparedSkill(botId, source, prepared, options) {
|
|
|
936
908
|
warnings: prepared.warnings,
|
|
937
909
|
skippedFiles: prepared.skippedFiles,
|
|
938
910
|
appliedStageId: options.appliedStageId,
|
|
911
|
+
...(options.package ? { package: options.package } : {}),
|
|
939
912
|
};
|
|
940
913
|
const root = ensureSkillsRoot(botId);
|
|
941
914
|
if (!root)
|
|
@@ -81,14 +81,15 @@ export function queueSteeredMessage(botId, threadId, text, options = {}) {
|
|
|
81
81
|
changed();
|
|
82
82
|
return { id };
|
|
83
83
|
}
|
|
84
|
-
/** Where a thread stands among this bot's threads waiting for
|
|
84
|
+
/** Where a thread stands among this bot's threads waiting for the bot to
|
|
85
|
+
* become available (a full slot list or an active room turn):
|
|
85
86
|
* 1 for the next to start. The drain visits queues in insertion order, so
|
|
86
87
|
* insertion order is the line. Null when nothing of this bot's is waiting
|
|
87
88
|
* on that thread. */
|
|
88
89
|
export function queuedThreadPosition(botId, threadId) {
|
|
89
90
|
let position = 0;
|
|
90
91
|
for (const [candidate, entry] of queues) {
|
|
91
|
-
if (entry.botId !== botId || !entry.items.some((item) => item.reason === "capacity"))
|
|
92
|
+
if (entry.botId !== botId || !entry.items.some((item) => item.reason === "capacity" || item.reason === "group-turn"))
|
|
92
93
|
continue;
|
|
93
94
|
position += 1;
|
|
94
95
|
if (candidate === threadId)
|