@nettee/beacon 0.4.6 → 0.4.8
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 +6 -6
- package/dist/daemon/config/profile.d.ts +9 -5
- package/dist/daemon/config/profile.js +63 -26
- package/dist/daemon/dashboard/profiles.js +33 -5
- package/dist/daemon/doctor.js +1 -1
- package/dist/daemon/domain/types.d.ts +1 -1
- package/dist/daemon/run/orchestrator.js +2 -2
- package/dist/daemon/run/profile-runner.js +1 -1
- package/dist/daemon/runtime/pi-doctor.js +1 -1
- package/dist/daemon/runtime/pi-rpc.d.ts +1 -1
- package/dist/daemon/runtime/pi-rpc.js +3 -3
- package/dist/daemon/runtime/system-prompt.d.ts +1 -1
- package/dist/daemon/runtime/system-prompt.js +1 -1
- package/dist/daemon/state/trigger-store.js +20 -3
- package/dist/web/assets/index-BBilzKru.css +2 -0
- package/dist/web/assets/{index-P7RcEvMx.js → index-DSJwEbKP.js} +1 -1
- package/dist/web/beacon-icon.svg +24 -0
- package/dist/web/favicon.svg +9 -0
- package/dist/web/index.html +3 -2
- package/examples/playbook/.beacon-profile/persona.md +5 -0
- package/examples/playbook/.beacon-profile/task.md +5 -0
- package/examples/profiles/example/profile.yaml +1 -1
- package/package.json +1 -1
- package/dist/web/assets/index-keS3Bb4P.css +0 -2
- package/examples/workspace/.beacon-profile/persona.md +0 -5
- package/examples/workspace/.beacon-profile/task.md +0 -5
package/README.md
CHANGED
|
@@ -53,20 +53,20 @@ Copy the files under [`examples`](examples) into a private Beacon home and repla
|
|
|
53
53
|
├── profiles/
|
|
54
54
|
│ └── example/
|
|
55
55
|
│ └── profile.yaml
|
|
56
|
-
└──
|
|
56
|
+
└── playbook/
|
|
57
57
|
└── .beacon-profile/
|
|
58
58
|
├── persona.md
|
|
59
59
|
└── task.md
|
|
60
60
|
```
|
|
61
61
|
|
|
62
|
-
yaml names the
|
|
63
|
-
(identity) and `task.md` (process) as a pair: `{
|
|
62
|
+
yaml names the playbook directory only. Beacon then loads `persona.md`
|
|
63
|
+
(identity) and `task.md` (process) as a pair: `{playbook}/.beacon-profile/`
|
|
64
64
|
first; if that pair is missing, `{profileDir}/`. It never mixes the two
|
|
65
65
|
places, never reads yaml `prompt:` / persona / task paths, and never falls
|
|
66
66
|
back to `prompt.md`. If neither place has a complete pair, Profile load fails
|
|
67
|
-
and lists the missing paths. Both pairs may exist; the
|
|
67
|
+
and lists the missing paths. Both pairs may exist; the playbook pair wins.
|
|
68
68
|
|
|
69
|
-
Beacon prepends an English platform template for this Run:
|
|
69
|
+
Beacon prepends an English platform template for this Run: playbook limit,
|
|
70
70
|
inbound vs Schedule vs event vs manual capabilities, the `reply_text` / `reply_card` /
|
|
71
71
|
`no_reply` / `notify_card` contract, and optional `submit_feedback` (instruction gaps).
|
|
72
72
|
|
|
@@ -336,7 +336,7 @@ as errors with a nonzero exit status; Beacon does not fall back to another confi
|
|
|
336
336
|
An operator can run one Profile without Feishu delivery by piping input to `trigger`; the Final Outcome is printed to stdout:
|
|
337
337
|
|
|
338
338
|
```sh
|
|
339
|
-
printf '%s\n' 'Summarize the
|
|
339
|
+
printf '%s\n' 'Summarize the playbook status.' | \
|
|
340
340
|
beacon trigger --profile example --input -
|
|
341
341
|
```
|
|
342
342
|
|
|
@@ -2,6 +2,8 @@ import { type EventListener } from "../events/cloudevent.js";
|
|
|
2
2
|
import { type ChannelRegistry } from "./channels.js";
|
|
3
3
|
export declare const profilePersonaFile = "persona.md";
|
|
4
4
|
export declare const profileTaskFile = "task.md";
|
|
5
|
+
export declare const playbookProfileDirectoryName = ".beacon-profile";
|
|
6
|
+
/** @deprecated Use playbookProfileDirectoryName. */
|
|
5
7
|
export declare const workspaceProfileDirectoryName = ".beacon-profile";
|
|
6
8
|
export type DeliveryDestination = {
|
|
7
9
|
chatId: string;
|
|
@@ -11,7 +13,7 @@ export type DeliveryDestination = {
|
|
|
11
13
|
/** Resolved Profile skills. Omitted on Profile means Pi auto-discovery (today's behavior). */
|
|
12
14
|
export type ProfileSkills = {
|
|
13
15
|
mode: "explicit";
|
|
14
|
-
/** Absolute paths after ~/$HOME/
|
|
16
|
+
/** Absolute paths after ~/$HOME/playbook resolution. */
|
|
15
17
|
paths: string[];
|
|
16
18
|
};
|
|
17
19
|
export type Profile = {
|
|
@@ -19,7 +21,7 @@ export type Profile = {
|
|
|
19
21
|
directory: string;
|
|
20
22
|
persona: string;
|
|
21
23
|
task: string;
|
|
22
|
-
|
|
24
|
+
playbook: string;
|
|
23
25
|
runtime: "pi";
|
|
24
26
|
model: {
|
|
25
27
|
provider: string;
|
|
@@ -40,8 +42,10 @@ export type LoadProfileOptions = {
|
|
|
40
42
|
channels?: ChannelRegistry;
|
|
41
43
|
channelsPath?: string;
|
|
42
44
|
};
|
|
43
|
-
export declare function
|
|
44
|
-
/**
|
|
45
|
-
export declare function
|
|
45
|
+
export declare function playbookProfileDirectory(playbook: string): string;
|
|
46
|
+
/** @deprecated Use playbookProfileDirectory. */
|
|
47
|
+
export declare function workspaceProfileDirectory(playbook: string): string;
|
|
48
|
+
/** Expand `~` / `$HOME` and resolve relative paths against the Profile playbook. */
|
|
49
|
+
export declare function resolveSkillPath(rawPath: string, playbook: string): string;
|
|
46
50
|
export declare function loadProfile(profileId: string, profilesDirectory?: string, options?: LoadProfileOptions): Promise<Profile>;
|
|
47
51
|
export declare function profileAdminChatId(profile: Profile): string;
|
|
@@ -9,7 +9,9 @@ import { parseStrictYaml } from "./yaml.js";
|
|
|
9
9
|
const profileIdPattern = /^[a-z0-9](?:[a-z0-9_-]{0,62})$/;
|
|
10
10
|
export const profilePersonaFile = "persona.md";
|
|
11
11
|
export const profileTaskFile = "task.md";
|
|
12
|
-
export const
|
|
12
|
+
export const playbookProfileDirectoryName = ".beacon-profile";
|
|
13
|
+
/** @deprecated Use playbookProfileDirectoryName. */
|
|
14
|
+
export const workspaceProfileDirectoryName = playbookProfileDirectoryName;
|
|
13
15
|
/** Admin DM: raw Feishu chat_id only (bot×person DMs are not channel registry entries). */
|
|
14
16
|
const adminDestinationSchema = z
|
|
15
17
|
.object({ chat_id: z.string().trim().min(1) })
|
|
@@ -61,9 +63,12 @@ const skillsSchema = z
|
|
|
61
63
|
paths: z.array(z.string().trim().min(1)).default([]),
|
|
62
64
|
})
|
|
63
65
|
.strict();
|
|
64
|
-
const
|
|
66
|
+
const profileDocumentFieldsSchema = z
|
|
65
67
|
.object({
|
|
66
|
-
|
|
68
|
+
/** Preferred config key for the Profile playbook directory. */
|
|
69
|
+
playbook: z.string().min(1).optional(),
|
|
70
|
+
/** Legacy alias for `playbook`. Dual-read during rename; prefer `playbook`. */
|
|
71
|
+
workspace: z.string().min(1).optional(),
|
|
67
72
|
runtime: z.literal("pi"),
|
|
68
73
|
model: z
|
|
69
74
|
.object({
|
|
@@ -85,6 +90,34 @@ const profileDocumentSchema = z
|
|
|
85
90
|
skills: skillsSchema.optional(),
|
|
86
91
|
})
|
|
87
92
|
.strict();
|
|
93
|
+
const profileDocumentSchema = profileDocumentFieldsSchema
|
|
94
|
+
.superRefine((document, context) => {
|
|
95
|
+
if (!document.playbook && !document.workspace) {
|
|
96
|
+
context.addIssue({
|
|
97
|
+
code: "custom",
|
|
98
|
+
message: "Profile must declare playbook (legacy key: workspace)",
|
|
99
|
+
path: ["playbook"],
|
|
100
|
+
});
|
|
101
|
+
return;
|
|
102
|
+
}
|
|
103
|
+
if (document.playbook &&
|
|
104
|
+
document.workspace &&
|
|
105
|
+
document.playbook !== document.workspace) {
|
|
106
|
+
context.addIssue({
|
|
107
|
+
code: "custom",
|
|
108
|
+
message: "Profile playbook and legacy workspace must match when both are set",
|
|
109
|
+
path: ["playbook"],
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
})
|
|
113
|
+
.transform((document) => {
|
|
114
|
+
const playbook = document.playbook ?? document.workspace;
|
|
115
|
+
if (!playbook) {
|
|
116
|
+
throw new Error("Profile must declare playbook (legacy key: workspace)");
|
|
117
|
+
}
|
|
118
|
+
const { workspace: _legacy, playbook: _preferred, ...rest } = document;
|
|
119
|
+
return { ...rest, playbook };
|
|
120
|
+
});
|
|
88
121
|
function assertProfileId(profileId) {
|
|
89
122
|
if (!profileIdPattern.test(profileId)) {
|
|
90
123
|
throw new Error(`Invalid Profile ID: ${JSON.stringify(profileId)}`);
|
|
@@ -94,8 +127,12 @@ function isWithin(parent, child) {
|
|
|
94
127
|
const path = relative(parent, child);
|
|
95
128
|
return path === "" || (!path.startsWith("..") && !isAbsolute(path));
|
|
96
129
|
}
|
|
97
|
-
export function
|
|
98
|
-
return join(
|
|
130
|
+
export function playbookProfileDirectory(playbook) {
|
|
131
|
+
return join(playbook, playbookProfileDirectoryName);
|
|
132
|
+
}
|
|
133
|
+
/** @deprecated Use playbookProfileDirectory. */
|
|
134
|
+
export function workspaceProfileDirectory(playbook) {
|
|
135
|
+
return playbookProfileDirectory(playbook);
|
|
99
136
|
}
|
|
100
137
|
async function isExistingFile(path) {
|
|
101
138
|
try {
|
|
@@ -162,16 +199,16 @@ async function loadMarkdownPair(directory, boundary) {
|
|
|
162
199
|
task: await loadRequiredMarkdown(canonicalDirectory, profileTaskFile),
|
|
163
200
|
};
|
|
164
201
|
}
|
|
165
|
-
async function loadPersonaAndTask(profileId,
|
|
166
|
-
const
|
|
167
|
-
if (await markdownPairExists(
|
|
168
|
-
return loadMarkdownPair(
|
|
202
|
+
async function loadPersonaAndTask(profileId, playbook, profileDirectory) {
|
|
203
|
+
const playbookDirectory = playbookProfileDirectory(playbook);
|
|
204
|
+
if (await markdownPairExists(playbookDirectory)) {
|
|
205
|
+
return loadMarkdownPair(playbookDirectory, playbook);
|
|
169
206
|
}
|
|
170
207
|
if (await markdownPairExists(profileDirectory)) {
|
|
171
208
|
return loadMarkdownPair(profileDirectory, profileDirectory);
|
|
172
209
|
}
|
|
173
210
|
const missing = [
|
|
174
|
-
...(await missingMarkdownPaths(
|
|
211
|
+
...(await missingMarkdownPaths(playbookDirectory)),
|
|
175
212
|
...(await missingMarkdownPaths(profileDirectory)),
|
|
176
213
|
];
|
|
177
214
|
throw new Error(`Profile ${profileId} has no complete ${profilePersonaFile} + ${profileTaskFile} pair. Missing: ${missing.join(", ")}`);
|
|
@@ -187,8 +224,8 @@ function resolveNotifyDestination(destination, channels, channelsPath) {
|
|
|
187
224
|
chatId: channel.chatId,
|
|
188
225
|
};
|
|
189
226
|
}
|
|
190
|
-
/** Expand `~` / `$HOME` and resolve relative paths against the Profile
|
|
191
|
-
export function resolveSkillPath(rawPath,
|
|
227
|
+
/** Expand `~` / `$HOME` and resolve relative paths against the Profile playbook. */
|
|
228
|
+
export function resolveSkillPath(rawPath, playbook) {
|
|
192
229
|
const trimmed = rawPath.trim();
|
|
193
230
|
const home = homedir();
|
|
194
231
|
if (trimmed === "~")
|
|
@@ -202,7 +239,7 @@ export function resolveSkillPath(rawPath, workspace) {
|
|
|
202
239
|
}
|
|
203
240
|
if (isAbsolute(trimmed))
|
|
204
241
|
return trimmed;
|
|
205
|
-
return resolve(
|
|
242
|
+
return resolve(playbook, trimmed);
|
|
206
243
|
}
|
|
207
244
|
async function assertValidSkillPath(profileId, resolvedPath) {
|
|
208
245
|
let pathStat;
|
|
@@ -227,10 +264,10 @@ async function assertValidSkillPath(profileId, resolvedPath) {
|
|
|
227
264
|
}
|
|
228
265
|
throw new Error(`Profile ${profileId} skill path must be a directory or .md file: ${resolvedPath}`);
|
|
229
266
|
}
|
|
230
|
-
async function resolveProfileSkills(profileId,
|
|
267
|
+
async function resolveProfileSkills(profileId, playbook, skills) {
|
|
231
268
|
const paths = [];
|
|
232
269
|
for (const rawPath of skills.paths) {
|
|
233
|
-
const resolved = resolveSkillPath(rawPath,
|
|
270
|
+
const resolved = resolveSkillPath(rawPath, playbook);
|
|
234
271
|
await assertValidSkillPath(profileId, resolved);
|
|
235
272
|
paths.push(resolved);
|
|
236
273
|
}
|
|
@@ -266,31 +303,31 @@ export async function loadProfile(profileId, profilesDirectory = join(homedir(),
|
|
|
266
303
|
}
|
|
267
304
|
const admin = config.admin ? { chatId: config.admin.chat_id } : undefined;
|
|
268
305
|
const canonicalProfileDirectory = await realpath(profileDirectory);
|
|
269
|
-
const
|
|
270
|
-
? config.
|
|
271
|
-
: resolve(dirname(configPath), config.
|
|
272
|
-
let
|
|
306
|
+
const playbook = isAbsolute(config.playbook)
|
|
307
|
+
? config.playbook
|
|
308
|
+
: resolve(dirname(configPath), config.playbook);
|
|
309
|
+
let playbookStat;
|
|
273
310
|
try {
|
|
274
|
-
|
|
311
|
+
playbookStat = await stat(playbook);
|
|
275
312
|
}
|
|
276
313
|
catch (error) {
|
|
277
|
-
throw new Error(`Cannot access Profile
|
|
314
|
+
throw new Error(`Cannot access Profile playbook at ${playbook}`, {
|
|
278
315
|
cause: error,
|
|
279
316
|
});
|
|
280
317
|
}
|
|
281
|
-
if (!
|
|
282
|
-
throw new Error(`Profile
|
|
318
|
+
if (!playbookStat.isDirectory()) {
|
|
319
|
+
throw new Error(`Profile playbook is not a directory: ${playbook}`);
|
|
283
320
|
}
|
|
284
|
-
const { persona, task } = await loadPersonaAndTask(profileId,
|
|
321
|
+
const { persona, task } = await loadPersonaAndTask(profileId, playbook, canonicalProfileDirectory);
|
|
285
322
|
const skills = config.skills
|
|
286
|
-
? await resolveProfileSkills(profileId,
|
|
323
|
+
? await resolveProfileSkills(profileId, playbook, config.skills)
|
|
287
324
|
: undefined;
|
|
288
325
|
return {
|
|
289
326
|
id: profileId,
|
|
290
327
|
directory: canonicalProfileDirectory,
|
|
291
328
|
persona,
|
|
292
329
|
task,
|
|
293
|
-
|
|
330
|
+
playbook,
|
|
294
331
|
runtime: config.runtime,
|
|
295
332
|
model: config.model,
|
|
296
333
|
...(admin ? { admin } : {}),
|
|
@@ -39,7 +39,8 @@ const skillsSchema = z
|
|
|
39
39
|
.strict();
|
|
40
40
|
const profileDocumentSchema = z
|
|
41
41
|
.object({
|
|
42
|
-
|
|
42
|
+
playbook: z.string().min(1).optional(),
|
|
43
|
+
workspace: z.string().min(1).optional(),
|
|
43
44
|
runtime: z.literal("pi"),
|
|
44
45
|
model: z
|
|
45
46
|
.object({
|
|
@@ -59,7 +60,34 @@ const profileDocumentSchema = z
|
|
|
59
60
|
.optional(),
|
|
60
61
|
skills: skillsSchema.optional(),
|
|
61
62
|
})
|
|
62
|
-
.strict()
|
|
63
|
+
.strict()
|
|
64
|
+
.superRefine((document, context) => {
|
|
65
|
+
if (!document.playbook && !document.workspace) {
|
|
66
|
+
context.addIssue({
|
|
67
|
+
code: "custom",
|
|
68
|
+
message: "Profile must declare playbook (legacy key: workspace)",
|
|
69
|
+
path: ["playbook"],
|
|
70
|
+
});
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
if (document.playbook &&
|
|
74
|
+
document.workspace &&
|
|
75
|
+
document.playbook !== document.workspace) {
|
|
76
|
+
context.addIssue({
|
|
77
|
+
code: "custom",
|
|
78
|
+
message: "Profile playbook and legacy workspace must match when both are set",
|
|
79
|
+
path: ["playbook"],
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
})
|
|
83
|
+
.transform((document) => {
|
|
84
|
+
const playbook = document.playbook ?? document.workspace;
|
|
85
|
+
if (!playbook) {
|
|
86
|
+
throw new Error("Profile must declare playbook (legacy key: workspace)");
|
|
87
|
+
}
|
|
88
|
+
const { workspace: _legacy, playbook: _preferred, ...rest } = document;
|
|
89
|
+
return { ...rest, playbook };
|
|
90
|
+
});
|
|
63
91
|
function toNotifyView(notify) {
|
|
64
92
|
if (!notify)
|
|
65
93
|
return null;
|
|
@@ -75,7 +103,7 @@ function toNotifyView(notify) {
|
|
|
75
103
|
function toDetailView(profileId, document) {
|
|
76
104
|
return {
|
|
77
105
|
id: profileId,
|
|
78
|
-
|
|
106
|
+
playbook: document.playbook,
|
|
79
107
|
runtime: document.runtime,
|
|
80
108
|
model: document.model,
|
|
81
109
|
admin: document.admin ? { chatId: document.admin.chat_id } : null,
|
|
@@ -137,7 +165,7 @@ export async function listProfileViews(profilesDirectory) {
|
|
|
137
165
|
const document = await readProfileYaml(profilesDirectory, id);
|
|
138
166
|
items.push({
|
|
139
167
|
id,
|
|
140
|
-
|
|
168
|
+
playbook: document.playbook,
|
|
141
169
|
runtime: document.runtime,
|
|
142
170
|
model: document.model,
|
|
143
171
|
scheduleCount: document.schedules.length,
|
|
@@ -149,7 +177,7 @@ export async function listProfileViews(profilesDirectory) {
|
|
|
149
177
|
catch (error) {
|
|
150
178
|
items.push({
|
|
151
179
|
id,
|
|
152
|
-
|
|
180
|
+
playbook: "",
|
|
153
181
|
runtime: "pi",
|
|
154
182
|
model: { provider: "", id: "" },
|
|
155
183
|
scheduleCount: 0,
|
package/dist/daemon/doctor.js
CHANGED
|
@@ -18,7 +18,7 @@ export async function runDoctor(configPath) {
|
|
|
18
18
|
await createFeishuGateway(credentials).checkReady();
|
|
19
19
|
const result = await runPiAgent({
|
|
20
20
|
prompt: "Reply with exactly: BEACON_PI_RPC_OK",
|
|
21
|
-
|
|
21
|
+
playbook: profile.playbook,
|
|
22
22
|
provider: profile.model.provider,
|
|
23
23
|
model: profile.model.id,
|
|
24
24
|
systemPrompt: "Follow the user's instruction exactly. Do not use tools.",
|
|
@@ -111,7 +111,7 @@ export type RunRecord = {
|
|
|
111
111
|
finishedAt?: string | undefined;
|
|
112
112
|
provider: string;
|
|
113
113
|
model: string;
|
|
114
|
-
|
|
114
|
+
playbook: string;
|
|
115
115
|
promptDigest: string;
|
|
116
116
|
/** Full `--system-prompt` text sent to Pi. Absent on records written before this field. */
|
|
117
117
|
systemPrompt?: string | undefined;
|
|
@@ -184,7 +184,7 @@ export class RunOrchestrator {
|
|
|
184
184
|
...(state === "failed" ? { finishedAt: timestamp } : {}),
|
|
185
185
|
provider: this.options.profile.model.provider,
|
|
186
186
|
model: this.options.profile.model.id,
|
|
187
|
-
|
|
187
|
+
playbook: this.options.profile.playbook,
|
|
188
188
|
promptDigest: profilePromptDigest(this.options.profile),
|
|
189
189
|
systemPrompt: continued?.systemPrompt ?? this.agentSystemPrompt(trigger),
|
|
190
190
|
...(failure ? { failure } : {}),
|
|
@@ -319,7 +319,7 @@ export class RunOrchestrator {
|
|
|
319
319
|
try {
|
|
320
320
|
const completion = await this.options.runAgent({
|
|
321
321
|
prompt: promptFor(input),
|
|
322
|
-
|
|
322
|
+
playbook: this.options.profile.playbook,
|
|
323
323
|
provider: this.options.profile.model.provider,
|
|
324
324
|
model: this.options.profile.model.id,
|
|
325
325
|
systemPrompt: record.run.systemPrompt,
|
|
@@ -24,7 +24,7 @@ export function createProfileRunner(profile, outcomes, beaconCliPath, runAgent =
|
|
|
24
24
|
const submission = outcomes.openRun();
|
|
25
25
|
await runAgent({
|
|
26
26
|
prompt: formatFeishuTriggerPrompt(trigger),
|
|
27
|
-
|
|
27
|
+
playbook: profile.playbook,
|
|
28
28
|
provider: profile.model.provider,
|
|
29
29
|
model: profile.model.id,
|
|
30
30
|
systemPrompt: buildAgentSystemPrompt(profile, {
|
|
@@ -2,7 +2,7 @@ import { runPiAgent } from "./pi-rpc.js";
|
|
|
2
2
|
export async function runPiDoctor(provider, model) {
|
|
3
3
|
const result = await runPiAgent({
|
|
4
4
|
prompt: "Reply with exactly: BEACON_PI_RPC_OK",
|
|
5
|
-
|
|
5
|
+
playbook: process.cwd(),
|
|
6
6
|
provider,
|
|
7
7
|
model,
|
|
8
8
|
systemPrompt: "Follow the user's instruction exactly. Do not use tools.",
|
|
@@ -239,8 +239,8 @@ export function createCappedNdjsonReader(maxFrameBytes, onFrame) {
|
|
|
239
239
|
export async function runPiAgent(request, options = {}) {
|
|
240
240
|
if (!request.prompt.trim())
|
|
241
241
|
throw new Error("Pi Run prompt must not be empty");
|
|
242
|
-
if (!request.
|
|
243
|
-
throw new Error("Pi Run
|
|
242
|
+
if (!request.playbook.trim())
|
|
243
|
+
throw new Error("Pi Run playbook must not be empty");
|
|
244
244
|
if ((request.provider === undefined) !== (request.model === undefined)) {
|
|
245
245
|
throw new Error("Pi Run provider and model must either both be set or both be omitted");
|
|
246
246
|
}
|
|
@@ -276,7 +276,7 @@ export async function runPiAgent(request, options = {}) {
|
|
|
276
276
|
throw new Error("Pi RPC frame limit must be a positive integer");
|
|
277
277
|
}
|
|
278
278
|
const child = spawn(executable, buildArguments(request), {
|
|
279
|
-
cwd: request.
|
|
279
|
+
cwd: request.playbook,
|
|
280
280
|
env: buildPiEnvironment(request.outcome, options.environment, options.runtimeEnvironment),
|
|
281
281
|
stdio: ["pipe", "pipe", "pipe"],
|
|
282
282
|
});
|
|
@@ -7,4 +7,4 @@ export type AgentSystemPromptTrigger = {
|
|
|
7
7
|
};
|
|
8
8
|
export declare function agentSystemPromptTrigger(input: TriggerInput | undefined, notifyTarget: DeliveryTarget | undefined): AgentSystemPromptTrigger;
|
|
9
9
|
export declare function profilePromptDigest(profile: Pick<Profile, "persona" | "task">): string;
|
|
10
|
-
export declare function buildAgentSystemPrompt(profile: Pick<Profile, "persona" | "task" | "
|
|
10
|
+
export declare function buildAgentSystemPrompt(profile: Pick<Profile, "persona" | "task" | "playbook">, trigger: AgentSystemPromptTrigger): string;
|
|
@@ -22,7 +22,7 @@ export function profilePromptDigest(profile) {
|
|
|
22
22
|
}
|
|
23
23
|
export function buildAgentSystemPrompt(profile, trigger) {
|
|
24
24
|
return [
|
|
25
|
-
`All local file reads, searches, and modifications must stay within the
|
|
25
|
+
`All local file reads, searches, and modifications must stay within the playbook directory and its descendants: ${profile.playbook}`,
|
|
26
26
|
"",
|
|
27
27
|
...thisRunCapabilityInstructions(trigger),
|
|
28
28
|
"",
|
|
@@ -44,7 +44,24 @@ const inputSchema = z.discriminatedUnion("kind", [
|
|
|
44
44
|
.strict(),
|
|
45
45
|
z.object({ kind: z.literal("manual"), text: z.string().min(1) }).strict(),
|
|
46
46
|
]);
|
|
47
|
-
const runSchema = z
|
|
47
|
+
const runSchema = z.preprocess((value) => {
|
|
48
|
+
if (typeof value !== "object" || value === null || Array.isArray(value)) {
|
|
49
|
+
return value;
|
|
50
|
+
}
|
|
51
|
+
const record = value;
|
|
52
|
+
if (typeof record.playbook === "string") {
|
|
53
|
+
if ("workspace" in record) {
|
|
54
|
+
const { workspace: _legacy, ...rest } = record;
|
|
55
|
+
return rest;
|
|
56
|
+
}
|
|
57
|
+
return record;
|
|
58
|
+
}
|
|
59
|
+
if (typeof record.workspace === "string") {
|
|
60
|
+
const { workspace, ...rest } = record;
|
|
61
|
+
return { ...rest, playbook: workspace };
|
|
62
|
+
}
|
|
63
|
+
return record;
|
|
64
|
+
}, z
|
|
48
65
|
.object({
|
|
49
66
|
runId: z.string().min(1),
|
|
50
67
|
sessionId: z
|
|
@@ -62,12 +79,12 @@ const runSchema = z
|
|
|
62
79
|
finishedAt: timestamp.optional(),
|
|
63
80
|
provider: z.string().min(1),
|
|
64
81
|
model: z.string().min(1),
|
|
65
|
-
|
|
82
|
+
playbook: z.string().min(1),
|
|
66
83
|
promptDigest: z.string().regex(/^[a-f0-9]{64}$/),
|
|
67
84
|
systemPrompt: z.string().min(1).optional(),
|
|
68
85
|
failure: failureSchema.optional(),
|
|
69
86
|
})
|
|
70
|
-
.strict();
|
|
87
|
+
.strict());
|
|
71
88
|
const outcomeSchema = z.union([
|
|
72
89
|
z
|
|
73
90
|
.object({
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
/*! tailwindcss v4.3.3 | MIT License | https://tailwindcss.com */
|
|
2
|
+
@layer properties{@supports (((-webkit-hyphens:none)) and (not (margin-trim:inline))) or ((-moz-orient:inline) and (not (color:rgb(from red r g b)))){*,:before,:after,::backdrop{--tw-space-y-reverse:0;--tw-divide-y-reverse:0;--tw-border-style:solid;--tw-font-weight:initial;--tw-tracking:initial;--tw-shadow:0 0 #0000;--tw-shadow-color:initial;--tw-shadow-alpha:100%;--tw-inset-shadow:0 0 #0000;--tw-inset-shadow-color:initial;--tw-inset-shadow-alpha:100%;--tw-ring-color:initial;--tw-ring-shadow:0 0 #0000;--tw-inset-ring-color:initial;--tw-inset-ring-shadow:0 0 #0000;--tw-ring-inset:initial;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-offset-shadow:0 0 #0000;--tw-blur:initial;--tw-brightness:initial;--tw-contrast:initial;--tw-grayscale:initial;--tw-hue-rotate:initial;--tw-invert:initial;--tw-opacity:initial;--tw-saturate:initial;--tw-sepia:initial;--tw-drop-shadow:initial;--tw-drop-shadow-color:initial;--tw-drop-shadow-alpha:100%;--tw-drop-shadow-size:initial;--tw-backdrop-blur:initial;--tw-backdrop-brightness:initial;--tw-backdrop-contrast:initial;--tw-backdrop-grayscale:initial;--tw-backdrop-hue-rotate:initial;--tw-backdrop-invert:initial;--tw-backdrop-opacity:initial;--tw-backdrop-saturate:initial;--tw-backdrop-sepia:initial;--tw-outline-style:solid}}}@layer theme{:root,:host{--font-sans:-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", "Noto Sans", Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";--font-mono:ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;--color-red-100:oklch(93.6% .032 17.717);--color-red-600:oklch(57.7% .245 27.325);--color-red-800:oklch(44.4% .177 26.899);--color-amber-100:oklch(96.2% .059 95.617);--color-amber-600:oklch(66.6% .179 58.318);--color-amber-800:oklch(47.3% .137 46.201);--color-emerald-700:oklch(50.8% .118 165.612);--color-sky-100:oklch(95.1% .026 236.824);--color-sky-200:oklch(90.1% .058 230.902);--color-sky-500:oklch(68.5% .169 237.323);--color-sky-600:oklch(58.8% .158 241.966);--color-sky-700:oklch(50% .134 242.749);--color-sky-800:oklch(44.3% .11 240.79);--color-sky-900:oklch(39.1% .09 240.876);--color-violet-100:oklch(94.3% .029 294.588);--color-violet-800:oklch(43.2% .232 292.759);--color-zinc-50:oklch(98.5% 0 none);--color-zinc-100:oklch(96.7% .001 286.375);--color-zinc-200:oklch(92% .004 286.32);--color-zinc-300:oklch(87.1% .006 286.286);--color-zinc-400:oklch(70.5% .015 286.067);--color-zinc-500:oklch(55.2% .016 285.938);--color-zinc-600:oklch(44.2% .017 285.786);--color-zinc-700:oklch(37% .013 285.805);--color-zinc-800:oklch(27.4% .006 286.033);--color-zinc-900:oklch(21% .006 285.885);--color-white:#fff;--spacing:.25rem;--text-xs:.75rem;--text-xs--line-height:calc(1 / .75);--text-sm:.875rem;--text-sm--line-height:calc(1.25 / .875);--text-xl:1.25rem;--text-xl--line-height:calc(1.75 / 1.25);--text-2xl:1.5rem;--text-2xl--line-height:calc(2 / 1.5);--font-weight-medium:500;--font-weight-semibold:600;--tracking-tight:-.025em;--tracking-normal:0em;--tracking-wide:.025em;--radius-md:.375rem;--radius-lg:.5rem;--default-transition-duration:.15s;--default-transition-timing-function:cubic-bezier(.4, 0, .2, 1);--default-font-family:var(--font-sans);--default-mono-font-family:var(--font-mono)}}@layer base{*,:after,:before,::backdrop{box-sizing:border-box;border:0 solid;margin:0;padding:0}::file-selector-button{box-sizing:border-box;border:0 solid;margin:0;padding:0}html,:host{-webkit-text-size-adjust:100%;tab-size:4;line-height:1.5;font-family:var(--default-font-family,-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", "Noto Sans", Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji");font-feature-settings:var(--default-font-feature-settings,normal);font-variation-settings:var(--default-font-variation-settings,normal);-webkit-tap-highlight-color:transparent}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;-webkit-text-decoration:inherit;-webkit-text-decoration:inherit;-webkit-text-decoration:inherit;-webkit-text-decoration:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,samp,pre{font-family:var(--default-mono-font-family,ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace);font-feature-settings:var(--default-mono-font-feature-settings,normal);font-variation-settings:var(--default-mono-font-variation-settings,normal);font-size:1em}small{font-size:80%}sub,sup{vertical-align:baseline;font-size:75%;line-height:0;position:relative}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}:-moz-focusring:where(:not(iframe)){outline:auto}progress{vertical-align:baseline}summary{display:list-item}ol,ul,menu{list-style:none}img,svg,video,canvas,audio,iframe,embed,object{vertical-align:middle;display:block}img,video{max-width:100%;height:auto}button,input,select,optgroup,textarea{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}::file-selector-button{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}:where(select:is([multiple],[size])) optgroup{font-weight:bolder}:where(select:is([multiple],[size])) optgroup option{padding-inline-start:20px}::file-selector-button{margin-inline-end:4px}::placeholder{opacity:1}@supports (not ((-webkit-appearance:-apple-pay-button))) or (contain-intrinsic-size:1px){::placeholder{color:currentColor}@supports (color:color-mix(in lab, red, red)){::placeholder{color:color-mix(in oklab, currentcolor 50%, transparent)}}}textarea{resize:vertical}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-date-and-time-value{min-height:1lh;text-align:inherit}::-webkit-datetime-edit{display:inline-flex}::-webkit-datetime-edit-fields-wrapper{padding:0}::-webkit-datetime-edit{padding-block:0}::-webkit-datetime-edit-year-field{padding-block:0}::-webkit-datetime-edit-month-field{padding-block:0}::-webkit-datetime-edit-day-field{padding-block:0}::-webkit-datetime-edit-hour-field{padding-block:0}::-webkit-datetime-edit-minute-field{padding-block:0}::-webkit-datetime-edit-second-field{padding-block:0}::-webkit-datetime-edit-millisecond-field{padding-block:0}::-webkit-datetime-edit-meridiem-field{padding-block:0}::-webkit-calendar-picker-indicator{line-height:1}:-moz-ui-invalid{box-shadow:none}button,input:where([type=button],[type=reset],[type=submit]){appearance:button}::file-selector-button{appearance:button}::-webkit-inner-spin-button{height:auto}::-webkit-outer-spin-button{height:auto}[hidden]:where(:not([hidden=until-found])){display:none!important}}@layer components;@layer utilities{.sr-only{clip-path:inset(50%);white-space:nowrap;border-width:0;width:1px;height:1px;margin:-1px;padding:0;position:absolute;overflow:hidden}.absolute{position:absolute}.fixed{position:fixed}.relative{position:relative}.sticky{position:sticky}.top-0{top:0}.top-full{top:100%}.left-0{left:0}.z-10{z-index:10}.z-30{z-index:30}.m-0{margin:0}.mt-0\.5{margin-top:calc(var(--spacing) * .5)}.mt-1{margin-top:var(--spacing)}.mt-2{margin-top:calc(var(--spacing) * 2)}.mt-3{margin-top:calc(var(--spacing) * 3)}.ml-1{margin-left:var(--spacing)}.ml-2{margin-left:calc(var(--spacing) * 2)}.flex{display:flex}.grid{display:grid}.inline-flex{display:inline-flex}.size-10{width:calc(var(--spacing) * 10);height:calc(var(--spacing) * 10)}.h-3{height:calc(var(--spacing) * 3)}.h-3\.5{height:calc(var(--spacing) * 3.5)}.h-7{height:calc(var(--spacing) * 7)}.max-h-72{max-height:calc(var(--spacing) * 72)}.min-h-screen{min-height:100vh}.w-3{width:calc(var(--spacing) * 3)}.w-3\.5{width:calc(var(--spacing) * 3.5)}.w-7{width:calc(var(--spacing) * 7)}.w-10{width:calc(var(--spacing) * 10)}.w-56{width:calc(var(--spacing) * 56)}.w-64{width:calc(var(--spacing) * 64)}.w-full{width:100%}.min-w-0{min-width:0}.min-w-52{min-width:calc(var(--spacing) * 52)}.min-w-\[760px\]{min-width:760px}.min-w-\[960px\]{min-width:960px}.flex-1{flex:1}.shrink-0{flex-shrink:0}.border-collapse{border-collapse:collapse}.rotate-90{rotate:90deg}.cursor-pointer{cursor:pointer}.flex-wrap{flex-wrap:wrap}.items-center{align-items:center}.items-end{align-items:flex-end}.items-start{align-items:flex-start}.justify-between{justify-content:space-between}.justify-center{justify-content:center}.gap-1{gap:var(--spacing)}.gap-2{gap:calc(var(--spacing) * 2)}.gap-3{gap:calc(var(--spacing) * 3)}.gap-4{gap:calc(var(--spacing) * 4)}:where(.space-y-1>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(var(--spacing) * var(--tw-space-y-reverse));margin-block-end:calc(var(--spacing) * calc(1 - var(--tw-space-y-reverse)))}:where(.space-y-4>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing) * 4) * var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing) * 4) * calc(1 - var(--tw-space-y-reverse)))}:where(.space-y-5>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing) * 5) * var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing) * 5) * calc(1 - var(--tw-space-y-reverse)))}:where(.divide-y>:not(:last-child)){--tw-divide-y-reverse:0;border-bottom-style:var(--tw-border-style);border-top-style:var(--tw-border-style);border-top-width:calc(1px * var(--tw-divide-y-reverse));border-bottom-width:calc(1px * calc(1 - var(--tw-divide-y-reverse)))}:where(.divide-zinc-200>:not(:last-child)){border-color:var(--color-zinc-200)}.truncate{text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.overflow-auto{overflow:auto}.overflow-x-auto{overflow-x:auto}.rounded{border-radius:.25rem}.rounded-lg{border-radius:var(--radius-lg)}.rounded-md{border-radius:var(--radius-md)}.border{border-style:var(--tw-border-style);border-width:1px}.border-y{border-block-style:var(--tw-border-style);border-block-width:1px}.border-t{border-top-style:var(--tw-border-style);border-top-width:1px}.border-b{border-bottom-style:var(--tw-border-style);border-bottom-width:1px}.border-dashed{--tw-border-style:dashed;border-style:dashed}.border-zinc-100{border-color:var(--color-zinc-100)}.border-zinc-200{border-color:var(--color-zinc-200)}.border-zinc-300{border-color:var(--color-zinc-300)}.bg-amber-100{background-color:var(--color-amber-100)}.bg-red-100{background-color:var(--color-red-100)}.bg-sky-100{background-color:var(--color-sky-100)}.bg-sky-200{background-color:var(--color-sky-200)}.bg-violet-100{background-color:var(--color-violet-100)}.bg-white{background-color:var(--color-white)}.bg-white\/70{background-color:#ffffffb3}@supports (color:color-mix(in lab, red, red)){.bg-white\/70{background-color:color-mix(in oklab, var(--color-white) 70%, transparent)}}.bg-white\/90{background-color:#ffffffe6}@supports (color:color-mix(in lab, red, red)){.bg-white\/90{background-color:color-mix(in oklab, var(--color-white) 90%, transparent)}}.bg-zinc-50{background-color:var(--color-zinc-50)}.bg-zinc-50\/80{background-color:#fafafacc}@supports (color:color-mix(in lab, red, red)){.bg-zinc-50\/80{background-color:color-mix(in oklab, var(--color-zinc-50) 80%, transparent)}}.bg-zinc-50\/90{background-color:#fafafae6}@supports (color:color-mix(in lab, red, red)){.bg-zinc-50\/90{background-color:color-mix(in oklab, var(--color-zinc-50) 90%, transparent)}}.bg-zinc-100{background-color:var(--color-zinc-100)}.bg-zinc-200{background-color:var(--color-zinc-200)}.p-0\.5{padding:calc(var(--spacing) * .5)}.px-1{padding-inline:var(--spacing)}.px-1\.5{padding-inline:calc(var(--spacing) * 1.5)}.px-2{padding-inline:calc(var(--spacing) * 2)}.px-2\.5{padding-inline:calc(var(--spacing) * 2.5)}.px-3{padding-inline:calc(var(--spacing) * 3)}.px-4{padding-inline:calc(var(--spacing) * 4)}.px-5{padding-inline:calc(var(--spacing) * 5)}.px-6{padding-inline:calc(var(--spacing) * 6)}.py-0\.5{padding-block:calc(var(--spacing) * .5)}.py-1{padding-block:var(--spacing)}.py-1\.5{padding-block:calc(var(--spacing) * 1.5)}.py-2{padding-block:calc(var(--spacing) * 2)}.py-3{padding-block:calc(var(--spacing) * 3)}.py-4{padding-block:calc(var(--spacing) * 4)}.py-8{padding-block:calc(var(--spacing) * 8)}.py-10{padding-block:calc(var(--spacing) * 10)}.pt-0{padding-top:0}.pt-3{padding-top:calc(var(--spacing) * 3)}.pb-3{padding-bottom:calc(var(--spacing) * 3)}.pl-8{padding-left:calc(var(--spacing) * 8)}.text-center{text-align:center}.text-left{text-align:left}.font-mono{font-family:var(--font-mono)}.text-2xl{font-size:var(--text-2xl);line-height:var(--tw-leading,var(--text-2xl--line-height))}.text-sm{font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height))}.text-xl{font-size:var(--text-xl);line-height:var(--tw-leading,var(--text-xl--line-height))}.text-xs{font-size:var(--text-xs);line-height:var(--tw-leading,var(--text-xs--line-height))}.text-\[10px\]{font-size:10px}.text-\[11px\]{font-size:11px}.font-medium{--tw-font-weight:var(--font-weight-medium);font-weight:var(--font-weight-medium)}.font-semibold{--tw-font-weight:var(--font-weight-semibold);font-weight:var(--font-weight-semibold)}.tracking-normal{--tw-tracking:var(--tracking-normal);letter-spacing:var(--tracking-normal)}.tracking-tight{--tw-tracking:var(--tracking-tight);letter-spacing:var(--tracking-tight)}.tracking-wide{--tw-tracking:var(--tracking-wide);letter-spacing:var(--tracking-wide)}.break-all{word-break:break-all}.whitespace-nowrap{white-space:nowrap}.whitespace-pre-wrap{white-space:pre-wrap}.text-amber-600{color:var(--color-amber-600)}.text-amber-800{color:var(--color-amber-800)}.text-emerald-700{color:var(--color-emerald-700)}.text-red-600{color:var(--color-red-600)}.text-red-800{color:var(--color-red-800)}.text-sky-600{color:var(--color-sky-600)}.text-sky-700{color:var(--color-sky-700)}.text-sky-800{color:var(--color-sky-800)}.text-sky-900{color:var(--color-sky-900)}.text-violet-800{color:var(--color-violet-800)}.text-zinc-400{color:var(--color-zinc-400)}.text-zinc-500{color:var(--color-zinc-500)}.text-zinc-600{color:var(--color-zinc-600)}.text-zinc-700{color:var(--color-zinc-700)}.text-zinc-800{color:var(--color-zinc-800)}.text-zinc-900{color:var(--color-zinc-900)}.normal-case{text-transform:none}.uppercase{text-transform:uppercase}.opacity-80{opacity:.8}.shadow-sm{--tw-shadow:0 1px 3px 0 var(--tw-shadow-color,#0000001a), 0 1px 2px -1px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.shadow-xl{--tw-shadow:0 20px 25px -5px var(--tw-shadow-color,#0000001a), 0 8px 10px -6px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.ring-sky-500\/40{--tw-ring-color:#00a5ef66}@supports (color:color-mix(in lab, red, red)){.ring-sky-500\/40{--tw-ring-color:color-mix(in oklab, var(--color-sky-500) 40%, transparent)}}.filter{filter:var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,)}.backdrop-blur{--tw-backdrop-blur:blur(8px);-webkit-backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,);backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,)}.transition-transform{transition-property:transform,translate,scale,rotate;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.outline-none{--tw-outline-style:none;outline-style:none}.select-text{-webkit-user-select:text;user-select:text}.placeholder\:text-zinc-400::placeholder{color:var(--color-zinc-400)}.first\:border-t-0:first-child{border-top-style:var(--tw-border-style);border-top-width:0}.first\:pt-0:first-child{padding-top:0}@media (hover:hover){.hover\:border-zinc-300:hover{border-color:var(--color-zinc-300)}.hover\:bg-white:hover{background-color:var(--color-white)}.hover\:bg-zinc-50:hover{background-color:var(--color-zinc-50)}.hover\:bg-zinc-100:hover{background-color:var(--color-zinc-100)}.hover\:text-sky-800:hover{color:var(--color-sky-800)}.hover\:text-sky-900:hover{color:var(--color-sky-900)}.hover\:text-zinc-600:hover{color:var(--color-zinc-600)}.hover\:text-zinc-800:hover{color:var(--color-zinc-800)}.hover\:text-zinc-900:hover{color:var(--color-zinc-900)}}.focus\:ring-2:focus{--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.focus-visible\:outline-2:focus-visible{outline-style:var(--tw-outline-style);outline-width:2px}.focus-visible\:outline-offset-4:focus-visible{outline-offset:4px}.focus-visible\:outline-sky-600:focus-visible{outline-color:var(--color-sky-600)}@media (width>=40rem){.sm\:col-span-2{grid-column:span 2/span 2}.sm\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}}@media (width>=48rem){.md\:sticky{position:sticky}.md\:top-0{top:0}.md\:flex{display:flex}.md\:h-screen{height:100vh}.md\:w-56{width:calc(var(--spacing) * 56)}.md\:flex-1{flex:1}.md\:flex-col{flex-direction:column}.md\:gap-0\.5{gap:calc(var(--spacing) * .5)}.md\:overflow-visible{overflow:visible}.md\:border-r{border-right-style:var(--tw-border-style);border-right-width:1px}.md\:border-b-0{border-bottom-style:var(--tw-border-style);border-bottom-width:0}.md\:px-3{padding-inline:calc(var(--spacing) * 3)}.md\:px-5{padding-inline:calc(var(--spacing) * 5)}.md\:pt-5{padding-top:calc(var(--spacing) * 5)}.md\:pb-3{padding-bottom:calc(var(--spacing) * 3)}.md\:pb-4{padding-bottom:calc(var(--spacing) * 4)}}@media (width>=64rem){.lg\:grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}}}@property --tw-space-y-reverse{syntax:"*";inherits:false;initial-value:0}@property --tw-divide-y-reverse{syntax:"*";inherits:false;initial-value:0}@property --tw-border-style{syntax:"*";inherits:false;initial-value:solid}@property --tw-font-weight{syntax:"*";inherits:false}@property --tw-tracking{syntax:"*";inherits:false}@property --tw-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-shadow-color{syntax:"*";inherits:false}@property --tw-shadow-alpha{syntax:"<percentage>";inherits:false;initial-value:100%}@property --tw-inset-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-inset-shadow-color{syntax:"*";inherits:false}@property --tw-inset-shadow-alpha{syntax:"<percentage>";inherits:false;initial-value:100%}@property --tw-ring-color{syntax:"*";inherits:false}@property --tw-ring-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-inset-ring-color{syntax:"*";inherits:false}@property --tw-inset-ring-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-ring-inset{syntax:"*";inherits:false}@property --tw-ring-offset-width{syntax:"<length>";inherits:false;initial-value:0}@property --tw-ring-offset-color{syntax:"*";inherits:false;initial-value:#fff}@property --tw-ring-offset-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-blur{syntax:"*";inherits:false}@property --tw-brightness{syntax:"*";inherits:false}@property --tw-contrast{syntax:"*";inherits:false}@property --tw-grayscale{syntax:"*";inherits:false}@property --tw-hue-rotate{syntax:"*";inherits:false}@property --tw-invert{syntax:"*";inherits:false}@property --tw-opacity{syntax:"*";inherits:false}@property --tw-saturate{syntax:"*";inherits:false}@property --tw-sepia{syntax:"*";inherits:false}@property --tw-drop-shadow{syntax:"*";inherits:false}@property --tw-drop-shadow-color{syntax:"*";inherits:false}@property --tw-drop-shadow-alpha{syntax:"<percentage>";inherits:false;initial-value:100%}@property --tw-drop-shadow-size{syntax:"*";inherits:false}@property --tw-backdrop-blur{syntax:"*";inherits:false}@property --tw-backdrop-brightness{syntax:"*";inherits:false}@property --tw-backdrop-contrast{syntax:"*";inherits:false}@property --tw-backdrop-grayscale{syntax:"*";inherits:false}@property --tw-backdrop-hue-rotate{syntax:"*";inherits:false}@property --tw-backdrop-invert{syntax:"*";inherits:false}@property --tw-backdrop-opacity{syntax:"*";inherits:false}@property --tw-backdrop-saturate{syntax:"*";inherits:false}@property --tw-backdrop-sepia{syntax:"*";inherits:false}@property --tw-outline-style{syntax:"*";inherits:false;initial-value:solid}
|