@coreplane/switchboard 1.213.0 → 1.214.0
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/assets/config/config.example.yaml +5 -1
- package/dist/assets/deploy/cloudflare/artifactsCopy.ts +180 -0
- package/dist/assets/deploy/cloudflare/ensure-bucket.mjs +71 -0
- package/dist/assets/deploy/cloudflare/package.json +1 -1
- package/dist/assets/deploy/cloudflare/worker.ts +19 -1
- package/dist/assets/deploy/cloudflare/wrangler.template.jsonc +15 -0
- package/dist/assets/deploy/profile.example.json +1 -1
- package/dist/assets/package-lock.json +3 -3
- package/dist/assets/package.json +1 -1
- package/dist/assets/source.json +3 -3
- package/dist/assets/src/agents/registry.ts +39 -14
- package/dist/assets/src/config/profile.ts +8 -5
- package/dist/assets/src/core/runEvents.ts +6 -1
- package/dist/assets/src/core/runFriction.ts +9 -2
- package/dist/assets/src/core/runLedger/types.ts +4 -0
- package/dist/assets/src/core/runRecord.ts +12 -0
- package/dist/assets/src/core/trace/workerTrace.ts +2 -0
- package/dist/assets/src/deploy/profile.ts +11 -0
- package/dist/assets/src/execution/residentText.ts +17 -2
- package/dist/assets/src/providers/types.ts +6 -0
- package/dist/cli.js +1069 -356
- package/package.json +1 -1
|
@@ -145,8 +145,18 @@ export interface RunRecord {
|
|
|
145
145
|
* item 48), stored at the claim so a retried spawn finds its run. Present
|
|
146
146
|
* exactly when `parentInstanceId` is — both or neither, never one alone. */
|
|
147
147
|
idempotencyKey?: string;
|
|
148
|
+
/** Where the run's conversation started (item 52): `channel` — its own
|
|
149
|
+
* thread's history, as for every run a person, a schedule or a coordinator
|
|
150
|
+
* started — or `parent` — a spawned child seeded from its parent's text
|
|
151
|
+
* turns at the spawn (`DispatchOptions.seed`). Absent on records written
|
|
152
|
+
* before it existed. */
|
|
153
|
+
seed?: RunSeed;
|
|
148
154
|
}
|
|
149
155
|
|
|
156
|
+
/** The two places a run's conversation can start (item 52). */
|
|
157
|
+
export const RUN_SEEDS = ["channel", "parent"] as const;
|
|
158
|
+
export type RunSeed = (typeof RUN_SEEDS)[number];
|
|
159
|
+
|
|
150
160
|
/** The profile as the record stores it: the run's effective profile plus the preset it came from. */
|
|
151
161
|
export type RunProfileRecord = RunProfile & { preset: string };
|
|
152
162
|
|
|
@@ -515,6 +525,8 @@ export function isRunRecord(v: unknown): v is RunRecord {
|
|
|
515
525
|
// A parent is named by a run id (item 46): the same shape as the record's own.
|
|
516
526
|
if (r.parentRunId !== undefined && (typeof r.parentRunId !== "string" || !RUN_ID_PATTERN.test(r.parentRunId)))
|
|
517
527
|
return false;
|
|
528
|
+
// Where the conversation started (item 52): one of the two words, or absent.
|
|
529
|
+
if (r.seed !== undefined && !RUN_SEEDS.includes(r.seed as RunSeed)) return false;
|
|
518
530
|
// A coordinator's child (item 48): the instance id in the platform's alphabet
|
|
519
531
|
// and the key `<instance>:<step>` — both or neither; one alone is no tag.
|
|
520
532
|
if ((r.parentInstanceId === undefined) !== (r.idempotencyKey === undefined)) return false;
|
|
@@ -52,6 +52,8 @@ export function shimRoute(pathname: string): string | undefined {
|
|
|
52
52
|
if (pathname === "/costs" || pathname.startsWith("/costs/")) return "costs";
|
|
53
53
|
if (pathname.startsWith("/api/")) return "api";
|
|
54
54
|
if (pathname.startsWith("/admin/")) return "admin";
|
|
55
|
+
// The artifact copy (deploy/cloudflare/artifactsCopy.ts): the shim's own route, a 1 GB stream.
|
|
56
|
+
if (pathname === "/artifacts/copy") return "artifacts";
|
|
55
57
|
// The model proxy's two routes (docs/reference/specs/model-proxy.md): a bounded
|
|
56
58
|
// request per model call, forwarded to the container like everything else.
|
|
57
59
|
if (pathname === "/v1/messages" || pathname === "/v1/chat/completions") return "model-proxy";
|
|
@@ -89,6 +89,17 @@ export const profileSchema = z.object({
|
|
|
89
89
|
/** The Cloudflare Access application in front of the bot's dashboards, when
|
|
90
90
|
* there is one: the team domain the JWT is issued by and the app's AUD. */
|
|
91
91
|
access: z.object({ teamDomain: hostname, aud: z.string().regex(/^[0-9a-f]{64}$/) }).optional(),
|
|
92
|
+
/** The artifact store's bucket (docs/reference/specs/execution.md item 20), when the installation
|
|
93
|
+
* has one: the bot Worker's template binds it as `ARTIFACTS` and `deploy` creates it before the
|
|
94
|
+
* upload. The bot's runtime config (`artifacts.r2.bucket`) must name the same bucket — the two
|
|
95
|
+
* are held equal by `artifacts check`, not by the profile. R2's own bucket-name rules. */
|
|
96
|
+
artifacts: z
|
|
97
|
+
.object({
|
|
98
|
+
bucket: z
|
|
99
|
+
.string()
|
|
100
|
+
.regex(/^[a-z0-9][a-z0-9-]{1,61}[a-z0-9]$/, "an R2 bucket name: 3–63 lowercase letters, digits and hyphens"),
|
|
101
|
+
})
|
|
102
|
+
.optional(),
|
|
92
103
|
});
|
|
93
104
|
|
|
94
105
|
export type DeploymentProfile = z.infer<typeof profileSchema>;
|
|
@@ -19,6 +19,13 @@ import type { ResidentLifecycleState } from "./residentState.js";
|
|
|
19
19
|
* for the runtime-replaced guidance (~120); short enough for a card note. */
|
|
20
20
|
export const RESIDENT_TEXT_CAP = 300;
|
|
21
21
|
|
|
22
|
+
/** Cap for a resident `error`. A refusal is a whole reply, not a card note: the
|
|
23
|
+
* onboard not-in-installation 403 (item 35) carries two causes, the admin's
|
|
24
|
+
* path to the fix and GitHub's own 422 body — ~600 chars, and every word past
|
|
25
|
+
* the first 300 is the one the admin acts on. Still a bound, so a hostile body
|
|
26
|
+
* cannot make a reply unbounded; Slack's 3000-char section stays far away. */
|
|
27
|
+
export const RESIDENT_ERROR_CAP = 1000;
|
|
28
|
+
|
|
22
29
|
/** Strip terminal control sequences, redact credential shapes, cap. The identity
|
|
23
30
|
* on the discriminator literals the bot compares (`runtime-replaced`,
|
|
24
31
|
* `op-refused…`, `disk-pressure:`), pinned by test. */
|
|
@@ -26,6 +33,11 @@ export function residentText(text: string): string {
|
|
|
26
33
|
return redactAndCap(stripAnsi(text), RESIDENT_TEXT_CAP);
|
|
27
34
|
}
|
|
28
35
|
|
|
36
|
+
/** The same strip and redact, at the reply-sized cap — for `error` only. */
|
|
37
|
+
export function residentErrorText(text: string): string {
|
|
38
|
+
return redactAndCap(stripAnsi(text), RESIDENT_ERROR_CAP);
|
|
39
|
+
}
|
|
40
|
+
|
|
29
41
|
const SANITIZED_FIELDS = ["error", "reason", "summary"] as const;
|
|
30
42
|
|
|
31
43
|
/** How deep the sanitizer descends. The deepest resident shape today is
|
|
@@ -36,7 +48,8 @@ const SANITIZE_DEPTH = 4;
|
|
|
36
48
|
/** A parsed resident body with its free-text fields made safe, at every level:
|
|
37
49
|
* `/residents` nests each resident's `state`/`reason` (or an `error`) under
|
|
38
50
|
* `residents[].live`, and `repo list` renders those. In each plain object only
|
|
39
|
-
* `error`, `reason` and `summary` are touched (when strings)
|
|
51
|
+
* `error`, `reason` and `summary` are touched (when strings) — `error` at the
|
|
52
|
+
* reply-sized cap, the other two at the card-sized one; `stderr` is
|
|
40
53
|
* rewritten only when it mirrors `error` (the thread routes' failure shape
|
|
41
54
|
* copies the error into stderr). Every other field — `needs`, `state`,
|
|
42
55
|
* `stdout`, `status`, bindings, numbers — passes through untouched. Arrays are
|
|
@@ -53,7 +66,9 @@ function walk(value: unknown, depth: number): unknown {
|
|
|
53
66
|
for (const [key, v] of Object.entries(src)) {
|
|
54
67
|
out[key] =
|
|
55
68
|
(SANITIZED_FIELDS as readonly string[]).includes(key) && typeof v === "string"
|
|
56
|
-
?
|
|
69
|
+
? key === "error"
|
|
70
|
+
? residentErrorText(v)
|
|
71
|
+
: residentText(v)
|
|
57
72
|
: walk(v, depth - 1);
|
|
58
73
|
}
|
|
59
74
|
if (typeof src.stderr === "string" && src.stderr === src.error && typeof out.error === "string") {
|
|
@@ -88,6 +88,12 @@ export interface CompletionRequest {
|
|
|
88
88
|
system?: string;
|
|
89
89
|
messages: ChatMessage[];
|
|
90
90
|
tools?: ToolDef[];
|
|
91
|
+
/** Force one of `tools`: the model must answer by calling the named tool,
|
|
92
|
+
* so the call's input IS the answer and prose cannot occur (the request
|
|
93
|
+
* router's shape, routing-and-config item 21). Anthropic: `tool_choice:
|
|
94
|
+
* {type: "tool", name}`; Chat Completions: `tool_choice: {type: "function",
|
|
95
|
+
* function: {name}}`. Absent → the model chooses. */
|
|
96
|
+
toolChoice?: { type: "tool"; name: string };
|
|
91
97
|
maxTokens: number;
|
|
92
98
|
/** model effort hint; providers apply it only where the model supports it */
|
|
93
99
|
effort?: Effort;
|