@fungi.computer/trellis 0.5.0 → 0.6.1

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 CHANGED
@@ -25,6 +25,13 @@ Workflows, and its steps call the same App methods your agents use.
25
25
  - Arm a published version in **Triggers** to start on a schedule, on an App
26
26
  event such as a Bonsai push or merge, or from a webhook, and have it report
27
27
  its ending to one of your chats.
28
+ - Open **Schedules** from the scope picker without selecting a workflow or
29
+ granting Git access. It shows this installation's Workflow registrations and
30
+ the Almanac reminders and Session Loops of Agents visible to Trellis. Pause
31
+ and Delete call the original owners and retain accepted work and history.
32
+ Agent read and management approval are independent. Missing or refused owners,
33
+ incomplete pages and unavailable private Local coverage are labelled, not
34
+ counted as empty schedules. Publication-context arming stays in Triggers.
28
35
  - Start from **Templates**: Bonsai checks (run `npm test` on each Change and
29
36
  report the result), Build on merge, Review a result, Storyboard (panels from
30
37
  Prints with a review per beat) and Storyboard + Save to git.
@@ -0,0 +1,59 @@
1
+ import { defineAppMethod } from "@fungi.computer/agar/backend-definition";
2
+ import {
3
+ listAgentSchedulesInputSchema,
4
+ agentScheduleTargetSchema,
5
+ agentSchedulesReplySchema,
6
+ agentScheduleControlReplySchema,
7
+ decodeAgentSchedulePage,
8
+ decodeAgentScheduleControl,
9
+ } from "../shared/agent-schedules.js";
10
+ import type { WorkflowMethodContext } from "./methods.js";
11
+
12
+ const granted = (context: WorkflowMethodContext) => {
13
+ if (!context.env.AGENTS)
14
+ throw new Error("Agent schedules are unavailable from this host");
15
+ return context.env.AGENTS;
16
+ };
17
+
18
+ export const agentScheduleMethods = {
19
+ listAgentSchedules: defineAppMethod({
20
+ description:
21
+ "Read one bounded native schedule page for an authorized Agent.",
22
+ effect: "read",
23
+ requires: ["agent.read"],
24
+ input: listAgentSchedulesInputSchema,
25
+ output: agentSchedulesReplySchema,
26
+ handler: async (input, context: WorkflowMethodContext) =>
27
+ decodeAgentSchedulePage(await granted(context).schedules(input)),
28
+ }),
29
+ pauseAgentSchedule: defineAppMethod({
30
+ description:
31
+ "Pause future occurrences of the exact listed Agent schedule, retaining accepted work.",
32
+ effect: "write",
33
+ requires: ["agent.manage"],
34
+ input: agentScheduleTargetSchema,
35
+ output: agentScheduleControlReplySchema,
36
+ handler: async (input, context: WorkflowMethodContext) =>
37
+ decodeAgentScheduleControl(await granted(context).pauseSchedule(input)),
38
+ }),
39
+ resumeAgentSchedule: defineAppMethod({
40
+ description:
41
+ "Resume future occurrences of the exact listed Agent schedule, retaining accepted work.",
42
+ effect: "write",
43
+ requires: ["agent.manage"],
44
+ input: agentScheduleTargetSchema,
45
+ output: agentScheduleControlReplySchema,
46
+ handler: async (input, context: WorkflowMethodContext) =>
47
+ decodeAgentScheduleControl(await granted(context).resumeSchedule(input)),
48
+ }),
49
+ deleteAgentSchedule: defineAppMethod({
50
+ description:
51
+ "Cancel future occurrences of the exact listed Agent schedule, retaining accepted work and history.",
52
+ effect: "write",
53
+ requires: ["agent.manage"],
54
+ input: agentScheduleTargetSchema,
55
+ output: agentScheduleControlReplySchema,
56
+ handler: async (input, context: WorkflowMethodContext) =>
57
+ decodeAgentScheduleControl(await granted(context).deleteSchedule(input)),
58
+ }),
59
+ };
@@ -197,6 +197,7 @@ export const libraryMethods = {
197
197
  "workflow.arm",
198
198
  "media.generation",
199
199
  "computer.writeFile",
200
+ "agent.manage",
200
201
  ],
201
202
  input: z.strictObject({}),
202
203
  output: z.strictObject({ more: z.boolean() }),
@@ -7,6 +7,11 @@ import type { BackgroundBinding } from "@fungi.computer/puffball/background-repl
7
7
  import type { GitBinding } from "@fungi.computer/agar/git";
8
8
  import type { WorkflowIndexes } from "./indexes.js";
9
9
  import { scheduleMethods } from "./schedule-methods.js";
10
+ import { agentScheduleMethods } from "./agent-schedule-methods.js";
11
+ import type {
12
+ listAgentSchedulesInputSchema,
13
+ agentScheduleTargetSchema,
14
+ } from "../shared/agent-schedules.js";
10
15
  import { toolFamilyMethods } from "./tool-family.js";
11
16
  import { libraryMethods } from "./library-methods.js";
12
17
  import * as Schema from "effect/Schema";
@@ -34,6 +39,20 @@ const inboxPage = z.object({ items: z.array(z.json()).max(200) });
34
39
  export type WorkflowMethodContext = Readonly<{
35
40
  indexes?: WorkflowIndexes;
36
41
  env: Readonly<{
42
+ AGENTS?: Readonly<{
43
+ schedules(
44
+ input: z.output<typeof listAgentSchedulesInputSchema>,
45
+ ): Promise<typeof Schema.Json.Type>;
46
+ pauseSchedule(
47
+ input: z.output<typeof agentScheduleTargetSchema>,
48
+ ): Promise<typeof Schema.Json.Type>;
49
+ resumeSchedule(
50
+ input: z.output<typeof agentScheduleTargetSchema>,
51
+ ): Promise<typeof Schema.Json.Type>;
52
+ deleteSchedule(
53
+ input: z.output<typeof agentScheduleTargetSchema>,
54
+ ): Promise<typeof Schema.Json.Type>;
55
+ }>;
37
56
  GIT?: GitBinding;
38
57
  /** The member's Inbox, read only to offer chats for run reports. */
39
58
  INBOX?: Readonly<{
@@ -59,6 +78,9 @@ export type WorkflowMethodContext = Readonly<{
59
78
  pauseSchedule(input: {
60
79
  scheduleId: string;
61
80
  }): Promise<typeof Schema.Json.Type>;
81
+ resumeSchedule(input: {
82
+ scheduleId: string;
83
+ }): Promise<typeof Schema.Json.Type>;
62
84
  schedules(input: ScheduleListInput): Promise<typeof Schema.Json.Type>;
63
85
  catalog(input: {
64
86
  installationId?: string | undefined;
@@ -137,6 +159,7 @@ export const appBackend = defineAppBackend<WorkflowMethodContext>({
137
159
  : { error: "not_granted" },
138
160
  }),
139
161
  ...scheduleMethods,
162
+ ...agentScheduleMethods,
140
163
  listChats: defineAppMethod({
141
164
  description:
142
165
  "List your existing chats (Agent Sessions) that a run or trigger may report its result to.",
@@ -3,6 +3,8 @@ import {
3
3
  scheduleArmReplySchema,
4
4
  schedulePauseInputSchema,
5
5
  schedulePauseReplySchema,
6
+ scheduleResumeInputSchema,
7
+ scheduleResumeReplySchema,
6
8
  triggerDisarmReplySchema,
7
9
  schedulesReplySchema,
8
10
  scheduleDisarmInputSchema,
@@ -73,6 +75,17 @@ export const scheduleMethods = {
73
75
  await granted(context).pauseSchedule(input),
74
76
  ),
75
77
  }),
78
+ resume: defineAppMethod({
79
+ description: "Resume future occurrences of a paused Workflow schedule.",
80
+ effect: "write",
81
+ requires: ["workflow.run", "workflow.arm"],
82
+ input: scheduleResumeInputSchema,
83
+ output: scheduleResumeReplySchema,
84
+ handler: async (input, context: WorkflowMethodContext) =>
85
+ scheduleResumeReplySchema.parse(
86
+ await granted(context).resumeSchedule(input),
87
+ ),
88
+ }),
76
89
  disarm: defineAppMethod({
77
90
  description: "Cancel an armed Workflow schedule.",
78
91
  effect: "write",
@@ -0,0 +1,41 @@
1
+ import { defineAppMethod } from "@fungi.computer/agar/backend-definition";
2
+ import { listAgentSchedulesInputSchema, agentScheduleTargetSchema, agentSchedulesReplySchema, agentScheduleControlReplySchema, decodeAgentSchedulePage, decodeAgentScheduleControl, } from "../shared/agent-schedules.js";
3
+ const granted = (context) => {
4
+ if (!context.env.AGENTS)
5
+ throw new Error("Agent schedules are unavailable from this host");
6
+ return context.env.AGENTS;
7
+ };
8
+ export const agentScheduleMethods = {
9
+ listAgentSchedules: defineAppMethod({
10
+ description: "Read one bounded native schedule page for an authorized Agent.",
11
+ effect: "read",
12
+ requires: ["agent.read"],
13
+ input: listAgentSchedulesInputSchema,
14
+ output: agentSchedulesReplySchema,
15
+ handler: async (input, context) => decodeAgentSchedulePage(await granted(context).schedules(input)),
16
+ }),
17
+ pauseAgentSchedule: defineAppMethod({
18
+ description: "Pause future occurrences of the exact listed Agent schedule, retaining accepted work.",
19
+ effect: "write",
20
+ requires: ["agent.manage"],
21
+ input: agentScheduleTargetSchema,
22
+ output: agentScheduleControlReplySchema,
23
+ handler: async (input, context) => decodeAgentScheduleControl(await granted(context).pauseSchedule(input)),
24
+ }),
25
+ resumeAgentSchedule: defineAppMethod({
26
+ description: "Resume future occurrences of the exact listed Agent schedule, retaining accepted work.",
27
+ effect: "write",
28
+ requires: ["agent.manage"],
29
+ input: agentScheduleTargetSchema,
30
+ output: agentScheduleControlReplySchema,
31
+ handler: async (input, context) => decodeAgentScheduleControl(await granted(context).resumeSchedule(input)),
32
+ }),
33
+ deleteAgentSchedule: defineAppMethod({
34
+ description: "Cancel future occurrences of the exact listed Agent schedule, retaining accepted work and history.",
35
+ effect: "write",
36
+ requires: ["agent.manage"],
37
+ input: agentScheduleTargetSchema,
38
+ output: agentScheduleControlReplySchema,
39
+ handler: async (input, context) => decodeAgentScheduleControl(await granted(context).deleteSchedule(input)),
40
+ }),
41
+ };
@@ -145,6 +145,7 @@ export const libraryMethods = {
145
145
  "workflow.arm",
146
146
  "media.generation",
147
147
  "computer.writeFile",
148
+ "agent.manage",
148
149
  ],
149
150
  input: z.strictObject({}),
150
151
  output: z.strictObject({ more: z.boolean() }),
@@ -3,6 +3,7 @@ import * as z from "zod";
3
3
  import { memberInboxItemSchema } from "@fungi.computer/agar/inbox";
4
4
  import { catalogReplySchema as catalogReply } from "./catalog.js";
5
5
  import { scheduleMethods } from "./schedule-methods.js";
6
+ import { agentScheduleMethods } from "./agent-schedule-methods.js";
6
7
  import { toolFamilyMethods } from "./tool-family.js";
7
8
  import { libraryMethods } from "./library-methods.js";
8
9
  import * as Schema from "effect/Schema";
@@ -65,6 +66,7 @@ export const appBackend = defineAppBackend({
65
66
  : { error: "not_granted" },
66
67
  }),
67
68
  ...scheduleMethods,
69
+ ...agentScheduleMethods,
68
70
  listChats: defineAppMethod({
69
71
  description: "List your existing chats (Agent Sessions) that a run or trigger may report its result to.",
70
72
  effect: "read",
@@ -1,4 +1,4 @@
1
- import { triggerTargetSchema, scheduleArmReplySchema, schedulePauseInputSchema, schedulePauseReplySchema, triggerDisarmReplySchema, schedulesReplySchema, scheduleDisarmInputSchema, scheduleListInputSchema, eventDisarmInputSchema, triggerListInputSchema, eventArmReplySchema, eventTriggersReplySchema, } from "@fungi.computer/workflows-wire";
1
+ import { triggerTargetSchema, scheduleArmReplySchema, schedulePauseInputSchema, schedulePauseReplySchema, scheduleResumeInputSchema, scheduleResumeReplySchema, triggerDisarmReplySchema, schedulesReplySchema, scheduleDisarmInputSchema, scheduleListInputSchema, eventDisarmInputSchema, triggerListInputSchema, eventArmReplySchema, eventTriggersReplySchema, } from "@fungi.computer/workflows-wire";
2
2
  import { defineAppMethod } from "@fungi.computer/agar/backend-definition";
3
3
  import { createWorkflowLibrary } from "./library.js";
4
4
  import { readWorkflowCatalog } from "./catalog.js";
@@ -49,6 +49,14 @@ export const scheduleMethods = {
49
49
  output: schedulePauseReplySchema,
50
50
  handler: async (input, context) => schedulePauseReplySchema.parse(await granted(context).pauseSchedule(input)),
51
51
  }),
52
+ resume: defineAppMethod({
53
+ description: "Resume future occurrences of a paused Workflow schedule.",
54
+ effect: "write",
55
+ requires: ["workflow.run", "workflow.arm"],
56
+ input: scheduleResumeInputSchema,
57
+ output: scheduleResumeReplySchema,
58
+ handler: async (input, context) => scheduleResumeReplySchema.parse(await granted(context).resumeSchedule(input)),
59
+ }),
52
60
  disarm: defineAppMethod({
53
61
  description: "Cancel an armed Workflow schedule.",
54
62
  effect: "write",
@@ -0,0 +1,12 @@
1
+ import * as S from "effect/Schema";
2
+ import * as z from "zod";
3
+ import { agentScheduleListInputSchema, agentScheduleTargetSchema, agentSchedulePageSchema, agentScheduleControlReceiptSchema, } from "@fungi.computer/shiitake-extensions/schedules";
4
+ /** App-method arguments retain the native owner's page and identity grammar. */
5
+ export const listAgentSchedulesInputSchema = agentScheduleListInputSchema.extend({
6
+ agentId: agentScheduleTargetSchema.options[0].shape.agentId,
7
+ });
8
+ export { agentScheduleTargetSchema };
9
+ export const agentSchedulesReplySchema = z.fromJSONSchema(S.toJsonSchemaDocument(agentSchedulePageSchema).schema);
10
+ export const agentScheduleControlReplySchema = z.fromJSONSchema(S.toJsonSchemaDocument(agentScheduleControlReceiptSchema).schema);
11
+ export const decodeAgentSchedulePage = S.decodeUnknownSync(agentSchedulePageSchema, { onExcessProperty: "error" });
12
+ export const decodeAgentScheduleControl = S.decodeUnknownSync(agentScheduleControlReceiptSchema, { onExcessProperty: "error" });
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@fungi.computer/trellis",
3
3
  "description": "Trellis: build and run workflows of App methods, an App for Oyster",
4
- "version": "0.5.0",
4
+ "version": "0.6.1",
5
5
  "author": "Levi <levi@technomancy.dev>",
6
6
  "type": "module",
7
7
  "exports": {
@@ -14,40 +14,43 @@
14
14
  "./run-timeline": "./src/runs/public.ts"
15
15
  },
16
16
  "dependencies": {
17
- "@fungi.computer/puffball": "0.3.0",
18
17
  "@cfworker/json-schema": "4.1.1",
19
- "@fungi.computer/agar": "0.6.0",
20
- "@fungi.computer/caps": "0.5.0",
18
+ "@fungi.computer/agar": "0.6.1",
19
+ "@fungi.computer/braid": "0.4.0",
20
+ "@fungi.computer/cairn": "0.1.0",
21
+ "@fungi.computer/caps": "0.6.0",
22
+ "@fungi.computer/maosaico": "0.1.0",
23
+ "@fungi.computer/puffball": "0.4.0",
24
+ "@fungi.computer/shiitake-extensions": "0.6.0",
25
+ "@fungi.computer/sigils": "0.2.2",
21
26
  "@fungi.computer/stipe": "0.1.0",
27
+ "@fungi.computer/watchdog": "0.1.0",
28
+ "@fungi.computer/workflows-wire": "0.4.0",
22
29
  "@xyflow/react": "12.12.0",
23
- "react": "18.3.1",
24
- "react-dom": "18.3.1",
25
- "@fungi.computer/braid": "0.3.0",
30
+ "cronstrue": "3.38.0",
26
31
  "effect": "4.0.2",
27
- "@fungi.computer/sigils": "0.2.0",
28
32
  "lucide-react": "0.460.0",
29
- "zod": "4.4.3",
30
- "@fungi.computer/workflows-wire": "0.3.0",
31
- "@fungi.computer/cairn": "0.1.0",
32
- "@fungi.computer/watchdog": "0.1.0",
33
- "@fungi.computer/maosaico": "0.1.0"
33
+ "react": "18.3.1",
34
+ "react-dom": "18.3.1",
35
+ "zod": "4.4.3"
34
36
  },
35
37
  "devDependencies": {
36
38
  "@cloudflare/workers-types": "5.20260828.1",
39
+ "@fungi.computer/sprite": "0.2.0",
37
40
  "@ladle/react": "5.1.1",
38
41
  "@tailwindcss/vite": "4.2.1",
42
+ "@types/node": "24.13.3",
39
43
  "@types/react": "18.3.3",
40
44
  "@types/react-dom": "18.3.0",
41
45
  "@vitejs/plugin-react": "6.1.1",
46
+ "eslint": "10.9.1",
47
+ "jsdom": "29.0.1",
42
48
  "playwright": "1.62.1",
49
+ "tsx": "4.23.12",
43
50
  "typescript": "5.9.3",
44
51
  "vite": "8.2.2",
45
52
  "vite-plugin-singlefile": "2.3.3",
46
- "vitest": "4.1.11",
47
- "eslint": "10.9.1",
48
- "tsx": "4.23.12",
49
- "jsdom": "29.0.1",
50
- "@types/node": "24.13.3"
53
+ "vitest": "4.1.11"
51
54
  },
52
55
  "license": "MIT",
53
56
  "repository": {
@@ -94,6 +97,8 @@
94
97
  "workflow.arm",
95
98
  "git.repository",
96
99
  "agent.read",
100
+ "agent.list",
101
+ "agent.manage",
97
102
  "apps.open"
98
103
  ],
99
104
  "handlers": [],
@@ -119,6 +124,7 @@
119
124
  "answerReview",
120
125
  "armSchedule",
121
126
  "pauseSchedule",
127
+ "resumeSchedule",
122
128
  "disarmSchedule",
123
129
  "schedules",
124
130
  "armEventTrigger",
@@ -132,6 +138,15 @@
132
138
  "operations": [
133
139
  "page"
134
140
  ]
141
+ },
142
+ {
143
+ "name": "AGENTS",
144
+ "operations": [
145
+ "schedules",
146
+ "pauseSchedule",
147
+ "resumeSchedule",
148
+ "deleteSchedule"
149
+ ]
135
150
  }
136
151
  ]
137
152
  }
@@ -170,14 +185,15 @@
170
185
  "typecheck": "tsc -p tsconfig.json --noEmit && tsc -p backend/tsconfig.json --noEmit",
171
186
  "lint": "eslint src test backend --max-warnings=0",
172
187
  "rebuild-copies": "node --import tsx scripts/rebuild-copies.mjs",
173
- "test": "node --import tsx --test test/*.test.ts test/storyboard-method-pins.test.mjs && vitest run --config vitest.config.mjs test/main.test.tsx test/inspector.test.tsx test/use-draft.test.tsx test/review-dock.test.tsx test/runs.test.tsx test/runs-list.test.tsx test/timeline-items.test.tsx test/version-history.test.tsx test/portable-share.test.tsx test/review-output.test.tsx test/publish-controls.test.tsx",
188
+ "test": "node --import tsx --test test/*.test.ts test/storyboard-method-pins.test.mjs test/schedule-pages.test.mjs && vitest run --config vitest.config.mjs test/main.test.tsx test/inspector.test.tsx test/use-draft.test.tsx test/review-dock.test.tsx test/runs.test.tsx test/runs-list.test.tsx test/timeline-items.test.tsx test/version-history.test.tsx test/portable-share.test.tsx test/review-output.test.tsx test/publish-controls.test.tsx test/schedules.test.tsx",
174
189
  "test:readiness:browser": "node test/editor-w4.browser.mjs && node test/readiness.browser.mjs",
175
190
  "test:browser": "node test/workflows.browser.mjs",
191
+ "test:schedules:browser": "node test/schedules.browser.mjs",
176
192
  "test:storyboard:browser": "node test/storyboard.browser.mjs",
177
193
  "test:timeline:browser": "node test/run-timeline.browser.mjs",
178
194
  "format:check": "node ../../tooling/repository/scripts/format-package-check.mjs",
179
195
  "lint:anti-slop": "node ../../tooling/repository/scripts/lint-package-anti-slop.mjs",
180
- "test:nightly": "WORKFLOW_EVIDENCE=.botanical/workflows bash ../../.agents/skills/orchestrate-multi-lane-work/scripts/run-proof.sh node test/workflows.browser.mjs && pnpm run test:history:browser",
196
+ "test:nightly": "WORKFLOW_EVIDENCE=.botanical/workflows bash ../../tooling/proof-runner/run-proof.sh node test/workflows.browser.mjs && pnpm run test:history:browser",
181
197
  "test:editor:browser": "node test/editor.browser.mjs && node test/editor-w4.browser.mjs",
182
198
  "test:generic:browser": "node test/workflows-generic.browser.mjs",
183
199
  "test:nested:browser": "node test/nested-fields.browser.mjs",
@@ -0,0 +1,30 @@
1
+ import * as S from "effect/Schema";
2
+ import * as z from "zod";
3
+ import {
4
+ agentScheduleListInputSchema,
5
+ agentScheduleTargetSchema,
6
+ agentSchedulePageSchema,
7
+ agentScheduleControlReceiptSchema,
8
+ } from "@fungi.computer/shiitake-extensions/schedules";
9
+
10
+ /** App-method arguments retain the native owner's page and identity grammar. */
11
+ export const listAgentSchedulesInputSchema =
12
+ agentScheduleListInputSchema.extend({
13
+ agentId: agentScheduleTargetSchema.options[0].shape.agentId,
14
+ });
15
+ export { agentScheduleTargetSchema };
16
+
17
+ export const agentSchedulesReplySchema = z.fromJSONSchema(
18
+ S.toJsonSchemaDocument(agentSchedulePageSchema).schema,
19
+ );
20
+ export const agentScheduleControlReplySchema = z.fromJSONSchema(
21
+ S.toJsonSchemaDocument(agentScheduleControlReceiptSchema).schema,
22
+ );
23
+ export const decodeAgentSchedulePage = S.decodeUnknownSync(
24
+ agentSchedulePageSchema,
25
+ { onExcessProperty: "error" },
26
+ );
27
+ export const decodeAgentScheduleControl = S.decodeUnknownSync(
28
+ agentScheduleControlReceiptSchema,
29
+ { onExcessProperty: "error" },
30
+ );