@fungi.computer/shiitake-extensions 0.4.0 → 0.6.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.
@@ -0,0 +1,49 @@
1
+ import * as Schema from "effect/Schema";
2
+ import { Prompt, SessionId } from "../internal/schedule-fields.js";
3
+ /** A named cron cadence evaluated in one IANA time zone. */
4
+ export const LoopCron = Schema.Struct({
5
+ /** Cron expression for the fixed cadence. */
6
+ expression: Schema.String.pipe(Schema.check(Schema.isMinLength(1)), Schema.check(Schema.isMaxLength(256))),
7
+ /** IANA time zone in which the expression is evaluated. */
8
+ timeZone: Schema.String.pipe(Schema.check(Schema.isMinLength(1)), Schema.check(Schema.isMaxLength(128))),
9
+ });
10
+ /** One Session's loop as every client sees it. */
11
+ export const Loop = Schema.Struct({
12
+ /** Identity of the loop whose occurrences are being tracked. */
13
+ loopId: Schema.String,
14
+ /** Whether a person or the model started this loop. */
15
+ origin: Schema.Literals(["human", "model"]),
16
+ /** Configured expiry timestamp, or null when absent. */
17
+ expiresAt: Schema.NullOr(Schema.Int),
18
+ /** Original instruction repeated for every occurrence. */
19
+ prompt: Prompt,
20
+ /** Whether the model or a fixed cron cadence schedules occurrences. */
21
+ mode: Schema.Literals(["self_paced", "fixed_interval"]),
22
+ /** Cadence of a fixed-interval loop; null when the model paces it. */
23
+ cron: Schema.NullOr(LoopCron),
24
+ /** Current loop lifecycle state. */
25
+ state: Schema.Literals([
26
+ "active",
27
+ "paused",
28
+ "finished",
29
+ "failed",
30
+ "cancelled",
31
+ ]),
32
+ /** Latest accepted command associated with this loop. */
33
+ commandId: Schema.String,
34
+ /** The model's stated reason for the next self-paced occurrence. */
35
+ reason: Schema.NullOr(Schema.String),
36
+ /** When the next occurrence is due, if one is scheduled. */
37
+ nextDueAt: Schema.NullOr(Schema.Int),
38
+ /** Visible explanation when loop work failed. */
39
+ lastError: Schema.NullOr(Schema.String),
40
+ /** Consecutive failed work outcomes; successful work resets this to zero. */
41
+ consecutiveFailures: Schema.Int.pipe(Schema.check(Schema.isGreaterThanOrEqualTo(0))),
42
+ });
43
+ /** Native bounded Loop page grammar, also used by shared continuations. */
44
+ export const LoopListInput = Schema.Struct({
45
+ /** Exclusive Session cursor. */
46
+ after: Schema.optionalKey(SessionId),
47
+ /** Maximum number of Loop rows to read. */
48
+ limit: Schema.optionalKey(Schema.Int.pipe(Schema.check(Schema.isBetween({ minimum: 1, maximum: 64 })))),
49
+ });
@@ -6,14 +6,24 @@ import type { SubagentSource } from "../subagents/host.js";
6
6
  /** Host-pack creation admission; attachments/model selection retain their own owners. */
7
7
  export declare function assertOrdinaryAgentCreation(input: Parameters<NonNullable<AgentControlDirectory["create"]>>[0]): void;
8
8
  /** The directory a conversation sends through; opening a recipient only needs its Sessions' extension doors. */
9
- type SendingDirectory = Omit<AgentControlDirectory, "open"> & Readonly<{
9
+ export type SendingDirectory = Omit<AgentControlDirectory, "open"> & Readonly<{
10
+ /** Open the recipient's Session extension door for goal adoption. */
10
11
  open: (agentId: string, signal: AbortSignal) => Promise<Readonly<{
12
+ /** Recipient Agent with a Session extension door. */
11
13
  agent: Readonly<{
14
+ /** Address a recipient Session without loading its conversation. */
12
15
  session: (sessionId: string) => Pick<ShiitakeSession, "extension">;
13
16
  }>;
17
+ /** Release the acquired recipient without stopping its work. */
14
18
  release: () => Promise<void>;
15
19
  }>>;
16
20
  }>;
21
+ /** Adopt the first accepted turn once, even when the sender was offline at delivery. */
22
+ export declare function adoptDelegatedGoal(session: Pick<ShiitakeSession, "extension">, input: Readonly<{
23
+ text: string;
24
+ goal: string | false | undefined;
25
+ commandId: string;
26
+ }>): Promise<void>;
17
27
  /**
18
28
  * This host's own record of the Agents each conversation created or sent
19
29
  * accepted work to, written as its Agent-control directory accepts them
@@ -35,4 +45,3 @@ export declare function oysterAgentExtensions(source: SubagentSource): Readonly<
35
45
  roster: readonly import("@fungi.computer/shiitake").RosteredExtensionV0[];
36
46
  grants: readonly import("@fungi.computer/shiitake").ExtensionGrantV0[];
37
47
  }>;
38
- export {};
@@ -37,6 +37,18 @@ function laneObjective(text, goal) {
37
37
  .map((line) => line.trim())
38
38
  .find((line) => line !== "") ?? text.trim()).slice(0, 4_000);
39
39
  }
40
+ /** Adopt the first accepted turn once, even when the sender was offline at delivery. */
41
+ export async function adoptDelegatedGoal(session, input) {
42
+ if (input.goal === false)
43
+ return;
44
+ await bindSessionExtension(session, goalContract).call("set", {
45
+ objective: laneObjective(input.text, input.goal),
46
+ expectedRevision: null,
47
+ requestId: `lane-goal:${input.commandId}`,
48
+ // The delegated prompt is already admitted; never enqueue it twice.
49
+ adoptCommandId: input.commandId,
50
+ });
51
+ }
40
52
  /**
41
53
  * This host's own record of the Agents each conversation created or sent
42
54
  * accepted work to, written as its Agent-control directory accepts them
@@ -99,12 +111,10 @@ export function createConversationAgents(storage) {
99
111
  sessionId !== undefined) {
100
112
  const opened = await directory.open(task.agentId, signal);
101
113
  try {
102
- await bindSessionExtension(opened.agent.session(sessionId), goalContract).call("set", {
103
- objective: laneObjective(task.text, task.goal),
104
- expectedRevision: null,
105
- requestId: `lane-goal:${accepted.commandId}`,
106
- // The send is the lane's first turn; never repeat it.
107
- adoptCommandId: accepted.commandId,
114
+ await adoptDelegatedGoal(opened.agent.session(sessionId), {
115
+ text: task.text,
116
+ goal: task.goal,
117
+ commandId: accepted.commandId,
108
118
  });
109
119
  }
110
120
  finally {
@@ -0,0 +1,16 @@
1
+ import type { ShiitakeAgent } from "@fungi.computer/shiitake";
2
+ import type * as z from "zod";
3
+ import { agentScheduleListInputSchema, type AgentScheduleRow, type AgentSchedulePage, type AgentScheduleTarget, type AgentScheduleControlReceipt } from "../schedules.js";
4
+ /**
5
+ * Read one already-admitted Agent, without scanning Sessions or draining pages.
6
+ * The caller owns Agent, home and resource authorization and the owner's lifetime.
7
+ */
8
+ export declare function readAgentSchedulePage(agentId: string, owner: Pick<ShiitakeAgent, "extensions">, input?: z.input<typeof agentScheduleListInputSchema>): Promise<AgentSchedulePage>;
9
+ /**
10
+ * Explicit content-returning control for callers admitted to both read and manage.
11
+ * In particular, private Local retains its native-row reply. The supplied owner
12
+ * must already be admitted for target.agentId; this helper grants no authority.
13
+ */
14
+ export declare function controlAgentScheduleWithContent(owner: Pick<ShiitakeAgent, "extensions">, input: AgentScheduleTarget, action: "pause" | "resume" | "delete"): Promise<AgentScheduleRow>;
15
+ /** Control an already-admitted owner; manage-only callers receive no read content. */
16
+ export declare function controlAgentSchedule(owner: Pick<ShiitakeAgent, "extensions">, target: AgentScheduleTarget, action: "pause" | "resume" | "delete"): Promise<AgentScheduleControlReceipt>;
@@ -0,0 +1,81 @@
1
+ import { bindAgentExtension } from "@fungi.computer/shiitake/client";
2
+ import { almanacContract } from "../almanac/contract.js";
3
+ import { loopContract } from "../loop/contract.js";
4
+ import { agentScheduleListInputSchema, agentScheduleTargetSchema, } from "../schedules.js";
5
+ /**
6
+ * Read one already-admitted Agent, without scanning Sessions or draining pages.
7
+ * The caller owns Agent, home and resource authorization and the owner's lifetime.
8
+ */
9
+ export async function readAgentSchedulePage(agentId, owner, input = {}) {
10
+ const { after, limit } = agentScheduleListInputSchema.parse(input);
11
+ if (after !== undefined && after.agentId !== agentId)
12
+ throw new Error("Schedule continuation belongs to another Agent");
13
+ const items = [];
14
+ if (after?.kind !== "loop") {
15
+ const nativeInput = after?.after === undefined ? { limit } : { limit, after: after.after };
16
+ const rows = await bindAgentExtension(owner, almanacContract).call("list", nativeInput);
17
+ for (const schedule of rows)
18
+ items.push({ agentId, kind: "almanac", schedule });
19
+ const last = rows.at(-1);
20
+ if (items.length === limit && last !== undefined)
21
+ return {
22
+ items,
23
+ next: { agentId, kind: "almanac", after: last.id },
24
+ };
25
+ }
26
+ const remaining = limit - items.length;
27
+ const cursor = after?.kind === "loop" ? after.after : undefined;
28
+ const nativeInput = cursor === undefined
29
+ ? { limit: remaining }
30
+ : { limit: remaining, after: cursor };
31
+ const rows = await bindAgentExtension(owner, loopContract).call("list", nativeInput);
32
+ for (const schedule of rows)
33
+ items.push({ agentId, kind: "loop", schedule });
34
+ const last = rows.at(-1);
35
+ return {
36
+ items,
37
+ next: items.length === limit && last !== undefined
38
+ ? { agentId, kind: "loop", after: last.sessionId }
39
+ : null,
40
+ };
41
+ }
42
+ /**
43
+ * Explicit content-returning control for callers admitted to both read and manage.
44
+ * In particular, private Local retains its native-row reply. The supplied owner
45
+ * must already be admitted for target.agentId; this helper grants no authority.
46
+ */
47
+ export async function controlAgentScheduleWithContent(owner, input, action) {
48
+ const target = agentScheduleTargetSchema.parse(input);
49
+ if (target.kind === "almanac") {
50
+ const schedule = await bindAgentExtension(owner, almanacContract).call(action === "delete" ? "cancel" : action, { scheduleId: target.scheduleId });
51
+ return { agentId: target.agentId, kind: "almanac", schedule };
52
+ }
53
+ const loop = await bindAgentExtension(owner, loopContract).call(action === "pause"
54
+ ? "pauseLoop"
55
+ : action === "resume"
56
+ ? "resumeLoop"
57
+ : "stopLoop", { sessionId: target.sessionId, loopId: target.loopId });
58
+ return {
59
+ agentId: target.agentId,
60
+ kind: "loop",
61
+ schedule: { ...loop, sessionId: target.sessionId },
62
+ };
63
+ }
64
+ /** Control an already-admitted owner; manage-only callers receive no read content. */
65
+ export async function controlAgentSchedule(owner, target, action) {
66
+ const row = await controlAgentScheduleWithContent(owner, target, action);
67
+ return row.kind === "almanac"
68
+ ? {
69
+ agentId: row.agentId,
70
+ kind: "almanac",
71
+ scheduleId: row.schedule.id,
72
+ state: row.schedule.state,
73
+ }
74
+ : {
75
+ agentId: row.agentId,
76
+ kind: "loop",
77
+ sessionId: row.schedule.sessionId,
78
+ loopId: row.schedule.loopId,
79
+ state: row.schedule.state,
80
+ };
81
+ }
@@ -0,0 +1,164 @@
1
+ import * as Schema from "effect/Schema";
2
+ import * as z from "zod";
3
+ /** Exclusive native key within one Agent's Almanac-then-Loop traversal. */
4
+ export declare const agentScheduleCursorSchema: z.ZodObject<{
5
+ agentId: z.ZodString;
6
+ kind: z.ZodUnion<readonly [z.ZodLiteral<"almanac">, z.ZodLiteral<"loop">]>;
7
+ after: z.ZodOptional<z.ZodString>;
8
+ }, z.core.$strict>;
9
+ /** One bounded read; a full native page conservatively carries continuation. */
10
+ export declare const agentScheduleListInputSchema: z.ZodObject<{
11
+ after: z.ZodOptional<z.ZodObject<{
12
+ agentId: z.ZodString;
13
+ kind: z.ZodUnion<readonly [z.ZodLiteral<"almanac">, z.ZodLiteral<"loop">]>;
14
+ after: z.ZodOptional<z.ZodString>;
15
+ }, z.core.$strict>>;
16
+ limit: z.ZodDefault<z.ZodNumber>;
17
+ }, z.core.$strict>;
18
+ /** Content-bearing read projection, decoded by the retained native contracts. */
19
+ export declare const agentScheduleRowSchema: Schema.Union<readonly [Schema.Struct<{
20
+ /** Agent that owns this Almanac schedule. */
21
+ readonly agentId: Schema.String;
22
+ /** Identifies the native Almanac schedule representation. */
23
+ readonly kind: Schema.Literal<"almanac">;
24
+ /** Retained Almanac row, including its instruction and lifecycle state. */
25
+ readonly schedule: Schema.Struct<{
26
+ readonly id: Schema.String;
27
+ readonly sessionId: Schema.String;
28
+ readonly prompt: Schema.String;
29
+ readonly cron: Schema.NullOr<Schema.Struct<{
30
+ readonly expression: Schema.String;
31
+ readonly timeZone: Schema.String;
32
+ }>>;
33
+ readonly nextDueAt: Schema.Int;
34
+ readonly state: Schema.Literals<readonly ["active", "paused", "cancelled", "finished"]>;
35
+ readonly lastCommandId: Schema.NullOr<Schema.String>;
36
+ readonly lastError: Schema.NullOr<Schema.String>;
37
+ readonly attempt: Schema.Int;
38
+ }>;
39
+ }>, Schema.Struct<{
40
+ /** Agent that owns this Session's Loop. */
41
+ readonly agentId: Schema.String;
42
+ /** Identifies the native Loop representation. */
43
+ readonly kind: Schema.Literal<"loop">;
44
+ /** Retained Loop row with the Session identity used to address its owner. */
45
+ readonly schedule: Schema.Struct<{
46
+ /** Session containing this exact Loop. */
47
+ readonly sessionId: Schema.String;
48
+ readonly loopId: Schema.String;
49
+ readonly origin: Schema.Literals<readonly ["human", "model"]>;
50
+ readonly expiresAt: Schema.NullOr<Schema.Int>;
51
+ readonly prompt: Schema.String;
52
+ readonly mode: Schema.Literals<readonly ["self_paced", "fixed_interval"]>;
53
+ readonly cron: Schema.NullOr<Schema.Struct<{
54
+ readonly expression: Schema.String;
55
+ readonly timeZone: Schema.String;
56
+ }>>;
57
+ readonly state: Schema.Literals<readonly ["active", "paused", "finished", "failed", "cancelled"]>;
58
+ readonly commandId: Schema.String;
59
+ readonly reason: Schema.NullOr<Schema.String>;
60
+ readonly nextDueAt: Schema.NullOr<Schema.Int>;
61
+ readonly lastError: Schema.NullOr<Schema.String>;
62
+ readonly consecutiveFailures: Schema.Int;
63
+ }>;
64
+ }>]>;
65
+ /** One Agent's retained schedule, including its native content. */
66
+ export type AgentScheduleRow = typeof agentScheduleRowSchema.Type;
67
+ /** Exact listed identity; a Loop target never selects its later replacement. */
68
+ export declare const agentScheduleTargetSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
69
+ kind: z.ZodLiteral<"almanac">;
70
+ agentId: z.ZodString;
71
+ scheduleId: z.ZodString & z.ZodType<string, string, z.core.$ZodTypeInternals<string, string>>;
72
+ }, z.core.$strict>, z.ZodObject<{
73
+ kind: z.ZodLiteral<"loop">;
74
+ agentId: z.ZodString;
75
+ sessionId: z.ZodString & z.ZodType<string, string, z.core.$ZodTypeInternals<string, string>>;
76
+ loopId: z.ZodString & z.ZodType<string, string, z.core.$ZodTypeInternals<string, string>>;
77
+ }, z.core.$strict>], "kind">;
78
+ /** Identity to admit before acquiring the corresponding Agent owner. */
79
+ export type AgentScheduleTarget = z.output<typeof agentScheduleTargetSchema>;
80
+ /** A bounded content-bearing page; owner failures reject rather than look empty. */
81
+ export declare const agentSchedulePageSchema: Schema.Struct<{
82
+ /** At most 64 retained rows, ordered Almanac first and then Loop. */
83
+ readonly items: Schema.$Array<Schema.Union<readonly [Schema.Struct<{
84
+ /** Agent that owns this Almanac schedule. */
85
+ readonly agentId: Schema.String;
86
+ /** Identifies the native Almanac schedule representation. */
87
+ readonly kind: Schema.Literal<"almanac">;
88
+ /** Retained Almanac row, including its instruction and lifecycle state. */
89
+ readonly schedule: Schema.Struct<{
90
+ readonly id: Schema.String;
91
+ readonly sessionId: Schema.String;
92
+ readonly prompt: Schema.String;
93
+ readonly cron: Schema.NullOr<Schema.Struct<{
94
+ readonly expression: Schema.String;
95
+ readonly timeZone: Schema.String;
96
+ }>>;
97
+ readonly nextDueAt: Schema.Int;
98
+ readonly state: Schema.Literals<readonly ["active", "paused", "cancelled", "finished"]>;
99
+ readonly lastCommandId: Schema.NullOr<Schema.String>;
100
+ readonly lastError: Schema.NullOr<Schema.String>;
101
+ readonly attempt: Schema.Int;
102
+ }>;
103
+ }>, Schema.Struct<{
104
+ /** Agent that owns this Session's Loop. */
105
+ readonly agentId: Schema.String;
106
+ /** Identifies the native Loop representation. */
107
+ readonly kind: Schema.Literal<"loop">;
108
+ /** Retained Loop row with the Session identity used to address its owner. */
109
+ readonly schedule: Schema.Struct<{
110
+ /** Session containing this exact Loop. */
111
+ readonly sessionId: Schema.String;
112
+ readonly loopId: Schema.String;
113
+ readonly origin: Schema.Literals<readonly ["human", "model"]>;
114
+ readonly expiresAt: Schema.NullOr<Schema.Int>;
115
+ readonly prompt: Schema.String;
116
+ readonly mode: Schema.Literals<readonly ["self_paced", "fixed_interval"]>;
117
+ readonly cron: Schema.NullOr<Schema.Struct<{
118
+ readonly expression: Schema.String;
119
+ readonly timeZone: Schema.String;
120
+ }>>;
121
+ readonly state: Schema.Literals<readonly ["active", "paused", "finished", "failed", "cancelled"]>;
122
+ readonly commandId: Schema.String;
123
+ readonly reason: Schema.NullOr<Schema.String>;
124
+ readonly nextDueAt: Schema.NullOr<Schema.Int>;
125
+ readonly lastError: Schema.NullOr<Schema.String>;
126
+ readonly consecutiveFailures: Schema.Int;
127
+ }>;
128
+ }>]>>;
129
+ /** Exclusive continuation on a full page, or null when both kinds are exhausted. */
130
+ readonly next: Schema.NullOr<Schema.Struct<{
131
+ /** Agent whose traversal the next request must continue. */
132
+ readonly agentId: Schema.String;
133
+ /** Native owner to continue within that Agent. */
134
+ readonly kind: Schema.Literals<readonly ["almanac", "loop"]>;
135
+ /** Exclusive schedule ID or Session ID; absent starts the native kind. */
136
+ readonly after: Schema.optionalKey<Schema.String>;
137
+ }>>;
138
+ }>;
139
+ /** One Agent's page, with no directory fan-out or availability policy. */
140
+ export type AgentSchedulePage = typeof agentSchedulePageSchema.Type;
141
+ /** Manage-only result: exact target and native state, never prompt or history. */
142
+ export declare const agentScheduleControlReceiptSchema: Schema.Union<readonly [Schema.Struct<{
143
+ /** Agent whose Almanac owner completed the transition. */
144
+ readonly agentId: Schema.String;
145
+ /** Identifies the controlled native Almanac schedule. */
146
+ readonly kind: Schema.Literal<"almanac">;
147
+ /** Identity of the schedule that was paused or cancelled. */
148
+ readonly scheduleId: Schema.String;
149
+ /** Native lifecycle state returned after the transition. */
150
+ readonly state: Schema.Literals<readonly ["active", "paused", "cancelled", "finished"]>;
151
+ }>, Schema.Struct<{
152
+ /** Agent whose Loop owner completed the transition. */
153
+ readonly agentId: Schema.String;
154
+ /** Identifies the controlled native Loop. */
155
+ readonly kind: Schema.Literal<"loop">;
156
+ /** Session containing the controlled Loop. */
157
+ readonly sessionId: Schema.String;
158
+ /** Exact Loop identity checked by the native transition. */
159
+ readonly loopId: Schema.String;
160
+ /** Native lifecycle state returned after the transition. */
161
+ readonly state: Schema.Literals<readonly ["active", "paused", "finished", "failed", "cancelled"]>;
162
+ }>]>;
163
+ /** Content-free acknowledgement of the native owner's completed transition. */
164
+ export type AgentScheduleControlReceipt = typeof agentScheduleControlReceiptSchema.Type;
@@ -0,0 +1,107 @@
1
+ import * as Schema from "effect/Schema";
2
+ import * as z from "zod";
3
+ import { Schedule, ScheduleListInput } from "./almanac/schema.js";
4
+ import { Loop, LoopListInput } from "./loop/schema.js";
5
+ import { SessionId as LoopSessionId } from "./internal/schedule-fields.js";
6
+ const identity = z.string().min(1).max(256);
7
+ /** Exclusive native key within one Agent's Almanac-then-Loop traversal. */
8
+ export const agentScheduleCursorSchema = z
9
+ .strictObject({
10
+ /** Agent whose retained schedules this continuation traverses. */
11
+ agentId: identity,
12
+ /** Native owner to continue; Almanac precedes Loop in a fresh traversal. */
13
+ kind: z.union([z.literal("almanac"), z.literal("loop")]),
14
+ /** Exclusive Almanac schedule ID or Loop Session ID; absent starts the kind. */
15
+ after: identity.optional(),
16
+ })
17
+ .refine(({ kind, after }) => Schema.is(kind === "almanac" ? ScheduleListInput : LoopListInput)(after === undefined ? {} : { after }), { message: "Invalid schedule continuation key" });
18
+ /** One bounded read; a full native page conservatively carries continuation. */
19
+ export const agentScheduleListInputSchema = z.strictObject({
20
+ /** Native continuation from the previous page of this Agent's schedules. */
21
+ after: agentScheduleCursorSchema.optional(),
22
+ /** Shared row budget across both native kinds; defaults to and cannot exceed 64. */
23
+ limit: z.number().int().min(1).max(64).default(64),
24
+ });
25
+ /** Content-bearing read projection, decoded by the retained native contracts. */
26
+ export const agentScheduleRowSchema = Schema.Union([
27
+ Schema.Struct({
28
+ /** Agent that owns this Almanac schedule. */
29
+ agentId: Schema.String,
30
+ /** Identifies the native Almanac schedule representation. */
31
+ kind: Schema.Literal("almanac"),
32
+ /** Retained Almanac row, including its instruction and lifecycle state. */
33
+ schedule: Schedule,
34
+ }),
35
+ Schema.Struct({
36
+ /** Agent that owns this Session's Loop. */
37
+ agentId: Schema.String,
38
+ /** Identifies the native Loop representation. */
39
+ kind: Schema.Literal("loop"),
40
+ /** Retained Loop row with the Session identity used to address its owner. */
41
+ schedule: Schema.Struct({
42
+ ...Loop.fields,
43
+ /** Session containing this exact Loop. */
44
+ sessionId: LoopSessionId,
45
+ }),
46
+ }),
47
+ ]);
48
+ /** Exact listed identity; a Loop target never selects its later replacement. */
49
+ export const agentScheduleTargetSchema = z.discriminatedUnion("kind", [
50
+ z.strictObject({
51
+ /** Selects the native Almanac control. */
52
+ kind: z.literal("almanac"),
53
+ /** Agent whose owner the caller must admit before controlling this target. */
54
+ agentId: identity,
55
+ /** Exact native Almanac schedule to pause or cancel. */
56
+ scheduleId: identity.refine(Schema.is(Schedule.fields.id)),
57
+ }),
58
+ z.strictObject({
59
+ /** Selects an identified native Loop control. */
60
+ kind: z.literal("loop"),
61
+ /** Agent whose owner the caller must admit before controlling this target. */
62
+ agentId: identity,
63
+ /** Session containing the listed Loop. */
64
+ sessionId: identity.refine(Schema.is(LoopSessionId)),
65
+ /** Exact listed Loop identity; a replacement must not be controlled instead. */
66
+ loopId: identity.refine(Schema.is(Loop.fields.loopId)),
67
+ }),
68
+ ]);
69
+ /** A bounded content-bearing page; owner failures reject rather than look empty. */
70
+ export const agentSchedulePageSchema = Schema.Struct({
71
+ /** At most 64 retained rows, ordered Almanac first and then Loop. */
72
+ items: Schema.Array(agentScheduleRowSchema).check(Schema.isMaxLength(64)),
73
+ /** Exclusive continuation on a full page, or null when both kinds are exhausted. */
74
+ next: Schema.NullOr(Schema.Struct({
75
+ /** Agent whose traversal the next request must continue. */
76
+ agentId: Schema.String,
77
+ /** Native owner to continue within that Agent. */
78
+ kind: Schema.Literals(["almanac", "loop"]),
79
+ /** Exclusive schedule ID or Session ID; absent starts the native kind. */
80
+ after: Schema.optionalKey(Schema.String),
81
+ })),
82
+ });
83
+ /** Manage-only result: exact target and native state, never prompt or history. */
84
+ export const agentScheduleControlReceiptSchema = Schema.Union([
85
+ Schema.Struct({
86
+ /** Agent whose Almanac owner completed the transition. */
87
+ agentId: Schema.String,
88
+ /** Identifies the controlled native Almanac schedule. */
89
+ kind: Schema.Literal("almanac"),
90
+ /** Identity of the schedule that was paused or cancelled. */
91
+ scheduleId: Schedule.fields.id,
92
+ /** Native lifecycle state returned after the transition. */
93
+ state: Schedule.fields.state,
94
+ }),
95
+ Schema.Struct({
96
+ /** Agent whose Loop owner completed the transition. */
97
+ agentId: Schema.String,
98
+ /** Identifies the controlled native Loop. */
99
+ kind: Schema.Literal("loop"),
100
+ /** Session containing the controlled Loop. */
101
+ sessionId: LoopSessionId,
102
+ /** Exact Loop identity checked by the native transition. */
103
+ loopId: Loop.fields.loopId,
104
+ /** Native lifecycle state returned after the transition. */
105
+ state: Loop.fields.state,
106
+ }),
107
+ ]);
@@ -28,6 +28,8 @@
28
28
  | [question/host](question/host.md) | - |
29
29
  | [roster](roster.md) | - |
30
30
  | [roster/conversation-agents](roster/conversation-agents.md) | - |
31
+ | [schedules](schedules.md) | - |
32
+ | [schedules/client](schedules/client.md) | - |
31
33
  | [skills/host](skills/host.md) | - |
32
34
  | [skills/source](skills/source.md) | - |
33
35
  | [subagents/client](subagents/client.md) | - |