@fungi.computer/truffle 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.
Files changed (51) hide show
  1. package/README.md +27 -4
  2. package/dist/internal/private-workerd-sandbox-worker.mjs +243 -273
  3. package/dist/internal/sandbox-metafile.json +35 -30
  4. package/dist/local-board-agents.d.ts +46 -0
  5. package/dist/local-board-agents.js +138 -0
  6. package/dist/local-board-shipping.d.ts +129 -0
  7. package/dist/local-board-shipping.js +325 -0
  8. package/dist/local-extensions.d.ts +4 -2
  9. package/dist/local-extensions.js +56 -22
  10. package/dist/local-login.js +33 -25
  11. package/dist/private-fungi-mcp-connection.js +6 -1
  12. package/dist/private-invocation-computer.d.ts +6 -2
  13. package/dist/private-invocation-computer.js +20 -13
  14. package/dist/private-local-agent-control.d.ts +5 -1
  15. package/dist/private-local-agent-control.js +11 -1
  16. package/dist/private-local-auth-rpc.d.ts +5 -0
  17. package/dist/private-local-auth-rpc.js +46 -4
  18. package/dist/private-local-client-version.js +3 -1
  19. package/dist/private-local-computer-processes.d.ts +7 -2
  20. package/dist/private-local-computer-processes.js +47 -23
  21. package/dist/private-local-delegation.d.ts +18 -0
  22. package/dist/private-local-delegation.js +84 -26
  23. package/dist/private-local-exec-environment.d.ts +5 -2
  24. package/dist/private-local-exec-environment.js +35 -1
  25. package/dist/private-local-exec-review.js +14 -7
  26. package/dist/private-local-exec-rules.d.ts +7 -0
  27. package/dist/private-local-exec-rules.js +23 -6
  28. package/dist/private-local-extension-records.d.ts +1 -1
  29. package/dist/private-local-runtime-host.js +54 -27
  30. package/dist/private-local-sandbox-policy.js +4 -0
  31. package/dist/private-local-sandbox.d.ts +3 -1
  32. package/dist/private-local-sandbox.js +2 -2
  33. package/dist/private-local-schedule-client.d.ts +12 -0
  34. package/dist/private-local-schedule-client.js +41 -0
  35. package/dist/private-local-schedules.d.ts +84 -0
  36. package/dist/private-local-schedules.js +65 -0
  37. package/dist/private-local-service-contract.d.ts +4 -0
  38. package/dist/private-local-service-discovery.d.ts +3 -3
  39. package/dist/private-local-service-discovery.js +1 -1
  40. package/dist/private-local-service-listener.js +9 -0
  41. package/dist/private-local-service-owner.d.ts +130 -0
  42. package/dist/private-local-service-owner.js +90 -2
  43. package/dist/private-local-service.d.ts +3 -0
  44. package/dist/private-local-service.js +9 -1
  45. package/dist/private-local-session-discovery.d.ts +10 -4
  46. package/dist/private-local-session-discovery.js +37 -4
  47. package/dist/private-provider-rejection.d.ts +1 -1
  48. package/dist/private-provider-rejection.js +28 -8
  49. package/dist/private-sqlite-owner.d.ts +7 -1
  50. package/dist/private-sqlite-owner.js +14 -2
  51. package/package.json +29 -16
@@ -0,0 +1,84 @@
1
+ import * as Schema from "effect/Schema";
2
+ import { z } from "zod";
3
+ import type { ShiitakeAgent } from "@fungi.computer/shiitake";
4
+ /** Private service ingress, never a Team/member or machine-link request. */
5
+ export declare const localScheduleListInputSchema: z.ZodObject<{
6
+ after: z.ZodOptional<z.ZodObject<{
7
+ agentId: z.ZodString;
8
+ kind: z.ZodUnion<readonly [z.ZodLiteral<"almanac">, z.ZodLiteral<"loop">]>;
9
+ after: z.ZodOptional<z.ZodString>;
10
+ }, z.core.$strict>>;
11
+ limit: z.ZodDefault<z.ZodNumber>;
12
+ invocationRoot: z.ZodString;
13
+ }, z.core.$strict>;
14
+ /** Private person-only mutation, distinct from the finite list request. */
15
+ export declare const localScheduleControlInputSchema: z.ZodObject<{
16
+ invocationRoot: z.ZodString;
17
+ target: z.ZodDiscriminatedUnion<[z.ZodObject<{
18
+ kind: z.ZodLiteral<"almanac">;
19
+ agentId: z.ZodString;
20
+ scheduleId: z.ZodString & z.ZodType<string, string, z.core.$ZodTypeInternals<string, string>>;
21
+ }, z.core.$strict>, z.ZodObject<{
22
+ kind: z.ZodLiteral<"loop">;
23
+ agentId: z.ZodString;
24
+ sessionId: z.ZodString & z.ZodType<string, string, z.core.$ZodTypeInternals<string, string>>;
25
+ loopId: z.ZodString & z.ZodType<string, string, z.core.$ZodTypeInternals<string, string>>;
26
+ }, z.core.$strict>], "kind">;
27
+ }, z.core.$strict>;
28
+ /** Local directory pages reuse the schedule owner's decoder and retained state. */
29
+ export declare const localSchedulePageSchema: Schema.Struct<{
30
+ readonly items: Schema.$Array<Schema.Union<readonly [Schema.Struct<{
31
+ readonly agentId: Schema.String;
32
+ readonly kind: Schema.Literal<"almanac">;
33
+ readonly schedule: Schema.Struct<{
34
+ readonly id: Schema.String;
35
+ readonly sessionId: Schema.String;
36
+ readonly prompt: Schema.String;
37
+ readonly cron: Schema.NullOr<Schema.Struct<{
38
+ readonly expression: Schema.String;
39
+ readonly timeZone: Schema.String;
40
+ }>>;
41
+ readonly nextDueAt: Schema.Int;
42
+ readonly state: Schema.Literals<readonly ["active", "paused", "cancelled", "finished"]>;
43
+ readonly lastCommandId: Schema.NullOr<Schema.String>;
44
+ readonly lastError: Schema.NullOr<Schema.String>;
45
+ readonly attempt: Schema.Int;
46
+ }>;
47
+ }>, Schema.Struct<{
48
+ readonly agentId: Schema.String;
49
+ readonly kind: Schema.Literal<"loop">;
50
+ readonly schedule: Schema.Struct<{
51
+ readonly sessionId: Schema.String;
52
+ readonly loopId: Schema.String;
53
+ readonly origin: Schema.Literals<readonly ["human", "model"]>;
54
+ readonly expiresAt: Schema.NullOr<Schema.Int>;
55
+ readonly prompt: Schema.String;
56
+ readonly mode: Schema.Literals<readonly ["self_paced", "fixed_interval"]>;
57
+ readonly cron: Schema.NullOr<Schema.Struct<{
58
+ readonly expression: Schema.String;
59
+ readonly timeZone: Schema.String;
60
+ }>>;
61
+ readonly state: Schema.Literals<readonly ["active", "paused", "finished", "failed", "cancelled"]>;
62
+ readonly commandId: Schema.String;
63
+ readonly reason: Schema.NullOr<Schema.String>;
64
+ readonly nextDueAt: Schema.NullOr<Schema.Int>;
65
+ readonly lastError: Schema.NullOr<Schema.String>;
66
+ readonly consecutiveFailures: Schema.Int;
67
+ }>;
68
+ }>]>>;
69
+ readonly unavailable: Schema.$Array<Schema.Struct<{
70
+ readonly agentId: Schema.String;
71
+ readonly reason: Schema.Literal<"owner_unavailable">;
72
+ }>>;
73
+ readonly next: Schema.NullOr<Schema.Struct<{
74
+ readonly agentId: Schema.String;
75
+ readonly kind: Schema.Literals<readonly ["almanac", "loop"]>;
76
+ readonly after: Schema.optionalKey<Schema.String>;
77
+ }>>;
78
+ }>;
79
+ export type LocalSchedulePage = typeof localSchedulePageSchema.Type;
80
+ /** One bounded read, borrowing existing admitted Agent owners one at a time. */
81
+ export declare function readLocalSchedulePage(agentIds: readonly string[], open: (agentId: string, signal: AbortSignal) => Promise<{
82
+ owner: Pick<ShiitakeAgent, "extensions">;
83
+ release: () => void;
84
+ }>, input: z.output<typeof localScheduleListInputSchema>, signal: AbortSignal): Promise<LocalSchedulePage>;
@@ -0,0 +1,65 @@
1
+ import * as Schema from "effect/Schema";
2
+ import { z } from "zod";
3
+ import { agentScheduleListInputSchema, agentSchedulePageSchema, agentScheduleTargetSchema, readAgentSchedulePage, } from "@fungi.computer/shiitake-extensions/schedules";
4
+ import { logEvent } from "./log-event.js";
5
+ /** Private service ingress, never a Team/member or machine-link request. */
6
+ export const localScheduleListInputSchema = z.strictObject({
7
+ invocationRoot: z.string().min(1),
8
+ ...agentScheduleListInputSchema.shape,
9
+ });
10
+ /** Private person-only mutation, distinct from the finite list request. */
11
+ export const localScheduleControlInputSchema = z.strictObject({
12
+ invocationRoot: z.string().min(1),
13
+ target: agentScheduleTargetSchema,
14
+ });
15
+ /** Local directory pages reuse the schedule owner's decoder and retained state. */
16
+ export const localSchedulePageSchema = Schema.Struct({
17
+ items: agentSchedulePageSchema.fields.items,
18
+ unavailable: Schema.Array(Schema.Struct({
19
+ agentId: Schema.String,
20
+ reason: Schema.Literal("owner_unavailable"),
21
+ })).check(Schema.isMaxLength(8)),
22
+ next: agentSchedulePageSchema.fields.next,
23
+ });
24
+ /** One bounded read, borrowing existing admitted Agent owners one at a time. */
25
+ export async function readLocalSchedulePage(agentIds, open, input, signal) {
26
+ signal.throwIfAborted();
27
+ const candidates = agentIds
28
+ .filter((agentId) => input.after === undefined ? true : agentId >= input.after.agentId)
29
+ .sort();
30
+ const items = [];
31
+ const unavailable = [];
32
+ // Bound runtime fan-out even when many retained Agents have no schedules.
33
+ const owners = candidates.slice(0, 8);
34
+ for (const agentId of owners) {
35
+ signal.throwIfAborted();
36
+ try {
37
+ const { owner, release } = await open(agentId, signal);
38
+ try {
39
+ const limit = input.limit - items.length;
40
+ const pageInput = input.after?.agentId === agentId
41
+ ? { limit, after: input.after }
42
+ : { limit };
43
+ const page = await readAgentSchedulePage(agentId, owner, pageInput);
44
+ signal.throwIfAborted();
45
+ items.push(...page.items);
46
+ if (page.next !== null)
47
+ return { items, unavailable, next: page.next };
48
+ }
49
+ finally {
50
+ release();
51
+ }
52
+ }
53
+ catch (cause) {
54
+ signal.throwIfAborted();
55
+ logEvent("schedule-directory.owner-unavailable", cause);
56
+ unavailable.push({ agentId, reason: "owner_unavailable" });
57
+ }
58
+ }
59
+ const unread = candidates.at(owners.length);
60
+ return {
61
+ items,
62
+ unavailable,
63
+ next: unread === undefined ? null : { agentId: unread, kind: "almanac" },
64
+ };
65
+ }
@@ -10,6 +10,7 @@ import type { ServiceDiagnosticsStatus } from "./private-local-service-log.js";
10
10
  import type { LocalSandboxStatus } from "./private-local-sandbox.js";
11
11
  import type { LocalPermissionMode } from "./private-local-permission-mode.js";
12
12
  import type { LocalUiAttachmentLifetime } from "./private-local-ui-attachment.js";
13
+ import type { PrivateLocalScheduleCapability } from "./private-local-schedule-client.js";
13
14
  /** Live health, not the on-disk identity record or a cached connection snapshot. */
14
15
  export type PrivateLocalServiceStatus = Readonly<{
15
16
  /** The client-service wire contract this service speaks. */
@@ -102,6 +103,7 @@ export type PrivateLocalServiceRebind = Readonly<{
102
103
  service: PrivateLocalServiceStatus;
103
104
  }> | Readonly<{
104
105
  kind: "unavailable";
106
+ alive: boolean;
105
107
  }>;
106
108
  export type PrivateLocalSessionChoice = Readonly<{
107
109
  databasePath: string;
@@ -139,6 +141,8 @@ export type PrivateLocalSessionDiscovery = Readonly<{
139
141
  }>;
140
142
  /** Local process management stays outside the shared Session HTTP contract. */
141
143
  export type PrivateLocalServiceManager = Readonly<{
144
+ /** Explicit private person-only capability; never provided by a mesh/App grant. */
145
+ schedules: (invocationRoot: string) => PrivateLocalScheduleCapability;
142
146
  reload: (invocationRoot: string) => Promise<string>;
143
147
  /** Throws `LocalServiceUpdated` when a live service speaks another protocol. */
144
148
  status: () => Promise<PrivateLocalServiceStatus | undefined>;
@@ -4,7 +4,7 @@ export { sandboxStatusSchema } from "./private-local-sandbox.js";
4
4
  * The client-service wire contract. Bump it with any change an older client
5
5
  * cannot read; a running client then asks its window to restart.
6
6
  */
7
- export declare const privateLocalServiceProtocol = 4;
7
+ export declare const privateLocalServiceProtocol = 5;
8
8
  /** This build's identity: the packed CLI's content hash, or `source` for a source run. */
9
9
  export declare function privateLocalServiceVersion(): string;
10
10
  /**
@@ -19,7 +19,7 @@ declare const serviceLifecycleSchema: z.ZodObject<{
19
19
  startedAt: z.ZodString;
20
20
  }, z.core.$loose>;
21
21
  declare const serviceRecordSchema: z.ZodObject<{
22
- protocol: z.ZodLiteral<4>;
22
+ protocol: z.ZodLiteral<5>;
23
23
  baseUrl: z.ZodURL;
24
24
  version: z.ZodString;
25
25
  serviceId: z.ZodString & z.ZodType<import("@fungi.computer/cairn").CairnId<"local-service", "service", 1>, string, z.core.$ZodTypeInternals<import("@fungi.computer/cairn").CairnId<"local-service", "service", 1>, string>>;
@@ -29,7 +29,7 @@ declare const serviceRecordSchema: z.ZodObject<{
29
29
  export type ServiceRecord = z.infer<typeof serviceRecordSchema>;
30
30
  /** Mandatory live diagnostics; never written into the registration pointer. */
31
31
  export declare const liveServiceStatusSchema: z.ZodObject<{
32
- protocol: z.ZodLiteral<4>;
32
+ protocol: z.ZodLiteral<5>;
33
33
  baseUrl: z.ZodURL;
34
34
  version: z.ZodString;
35
35
  serviceId: z.ZodString & z.ZodType<import("@fungi.computer/cairn").CairnId<"local-service", "service", 1>, string, z.core.$ZodTypeInternals<import("@fungi.computer/cairn").CairnId<"local-service", "service", 1>, string>>;
@@ -17,7 +17,7 @@ export { sandboxStatusSchema } from "./private-local-sandbox.js";
17
17
  * The client-service wire contract. Bump it with any change an older client
18
18
  * cannot read; a running client then asks its window to restart.
19
19
  */
20
- export const privateLocalServiceProtocol = 4;
20
+ export const privateLocalServiceProtocol = 5;
21
21
  /** This build's identity: the packed CLI's content hash, or `source` for a source run. */
22
22
  export function privateLocalServiceVersion() {
23
23
  return (z
@@ -1,5 +1,8 @@
1
1
  import { localFailureMetadata, logEvent } from "./log-event.js";
2
2
  import { bodyLimit } from "hono/body-limit";
3
+ import { ShiitakeError } from "@fungi.computer/shiitake";
4
+ import { toShiitakeErrorWire } from "@fungi.computer/shiitake/http";
5
+ import { localScheduleControlInputSchema, localScheduleListInputSchema, } from "./private-local-schedules.js";
3
6
  import { enrollPrivateLocalMesh, localMeshJoinSchema, } from "./private-local-mesh-enrollment.js";
4
7
  import { createLocalMachineEnrollmentAccount, registerFungiAccountOwner, signOutOwnedFungiAccount, } from "./private-fungi-account.js";
5
8
  import { createServiceLogLifecycle, } from "./private-local-service-log.js";
@@ -103,6 +106,9 @@ readRecoveryManifest, root = capturePrivateLocalConfigRoot()) {
103
106
  const app = new Hono();
104
107
  app.onError((error, context) => {
105
108
  diagnostics.failure(localFailureMetadata("service.request", error));
109
+ if (error instanceof ShiitakeError &&
110
+ context.req.path.startsWith(`${prefix}/schedules/`))
111
+ return context.json(toShiitakeErrorWire(error), error.cause === "conflict" ? 409 : 400);
106
112
  if (error instanceof LocalStoreSchemaError)
107
113
  return context.json({
108
114
  code: "store_schema_incompatible",
@@ -308,6 +314,9 @@ readRecoveryManifest, root = capturePrivateLocalConfigRoot()) {
308
314
  archived: input.archived,
309
315
  }));
310
316
  });
317
+ app.post(`${prefix}/schedules/list`, bodyLimit({ maxSize: 32 * 1024 }), async (context) => context.json(await owners.schedules(localScheduleListInputSchema.parse(await context.req.json()), context.req.raw.signal)));
318
+ app.post(`${prefix}/schedules/pause`, bodyLimit({ maxSize: 32 * 1024 }), async (context) => context.json(await owners.pauseSchedule(localScheduleControlInputSchema.parse(await context.req.json()), context.req.raw.signal)));
319
+ app.post(`${prefix}/schedules/delete`, bodyLimit({ maxSize: 32 * 1024 }), async (context) => context.json(await owners.deleteSchedule(localScheduleControlInputSchema.parse(await context.req.json()), context.req.raw.signal)));
311
320
  app.post(`${prefix}/reload`, async (context) => {
312
321
  try {
313
322
  const input = z
@@ -3,10 +3,12 @@ import { type PrivateLocalConfigRoot } from "./private-local-config-root.js";
3
3
  import type { ServiceLogLifecycle } from "./private-local-service-log.js";
4
4
  import type { StopAllResult } from "./private-local-stop-all.js";
5
5
  import { type LocalExtensions } from "./local-extensions.js";
6
+ import { z } from "zod";
6
7
  import type { PrivateLocalServiceSelection, PrivateLocalSessionCreation } from "./private-local-service-contract.js";
7
8
  import { openPrivateLocalRuntimeHost, type PrivateLocalRuntimeHost } from "./private-local-runtime-host.js";
8
9
  import type { LocalUiAttachment } from "./private-local-ui-attachment.js";
9
10
  import { createLocalMeshLinks } from "./private-local-mesh-links.js";
11
+ import { localScheduleControlInputSchema, localScheduleListInputSchema } from "./private-local-schedules.js";
10
12
  export type LocalServiceRuntime = Pick<PrivateLocalRuntimeHost, "databasePath" | "agentId" | "name" | "getSession" | "effectiveModel" | "setModel" | "setArchived" | "setMode" | "sandboxStatus" | "pendingDelegation" | "assertModelReady" | "listSessions" | "createSession" | "deleteSession" | "extensions" | "prepareReload" | "bindSessionPath" | "acceptMail" | "readSettlement" | "idle" | "close">;
11
13
  export declare class LocalSessionPathConflict extends Error {
12
14
  readonly name = "LocalSessionPathConflict";
@@ -127,6 +129,134 @@ serviceNative?: () => NonNullable<Parameters<typeof openPrivateLocalRuntimeHost>
127
129
  } | undefined;
128
130
  }>;
129
131
  sweepIdle: () => Promise<void>;
132
+ schedules: (request: z.input<typeof localScheduleListInputSchema>, signal: AbortSignal) => Promise<{
133
+ readonly items: readonly ({
134
+ readonly agentId: string;
135
+ readonly kind: "almanac";
136
+ readonly schedule: {
137
+ readonly id: string;
138
+ readonly sessionId: string;
139
+ readonly prompt: string;
140
+ readonly cron: {
141
+ readonly expression: string;
142
+ readonly timeZone: string;
143
+ } | null;
144
+ readonly nextDueAt: number;
145
+ readonly state: "cancelled" | "active" | "paused" | "finished";
146
+ readonly lastCommandId: string | null;
147
+ readonly lastError: string | null;
148
+ readonly attempt: number;
149
+ };
150
+ } | {
151
+ readonly agentId: string;
152
+ readonly kind: "loop";
153
+ readonly schedule: {
154
+ readonly sessionId: string;
155
+ readonly loopId: string;
156
+ readonly origin: "human" | "model";
157
+ readonly expiresAt: number | null;
158
+ readonly prompt: string;
159
+ readonly mode: "self_paced" | "fixed_interval";
160
+ readonly cron: {
161
+ readonly expression: string;
162
+ readonly timeZone: string;
163
+ } | null;
164
+ readonly state: "cancelled" | "active" | "failed" | "paused" | "finished";
165
+ readonly commandId: string;
166
+ readonly reason: string | null;
167
+ readonly nextDueAt: number | null;
168
+ readonly lastError: string | null;
169
+ readonly consecutiveFailures: number;
170
+ };
171
+ })[];
172
+ readonly unavailable: readonly {
173
+ readonly agentId: string;
174
+ readonly reason: "owner_unavailable";
175
+ }[];
176
+ readonly next: {
177
+ readonly agentId: string;
178
+ readonly kind: "almanac" | "loop";
179
+ readonly after?: string;
180
+ } | null;
181
+ }>;
182
+ pauseSchedule: (request: z.input<typeof localScheduleControlInputSchema>, signal: AbortSignal) => Promise<{
183
+ readonly agentId: string;
184
+ readonly kind: "almanac";
185
+ readonly schedule: {
186
+ readonly id: string;
187
+ readonly sessionId: string;
188
+ readonly prompt: string;
189
+ readonly cron: {
190
+ readonly expression: string;
191
+ readonly timeZone: string;
192
+ } | null;
193
+ readonly nextDueAt: number;
194
+ readonly state: "cancelled" | "active" | "paused" | "finished";
195
+ readonly lastCommandId: string | null;
196
+ readonly lastError: string | null;
197
+ readonly attempt: number;
198
+ };
199
+ } | {
200
+ readonly agentId: string;
201
+ readonly kind: "loop";
202
+ readonly schedule: {
203
+ readonly sessionId: string;
204
+ readonly loopId: string;
205
+ readonly origin: "human" | "model";
206
+ readonly expiresAt: number | null;
207
+ readonly prompt: string;
208
+ readonly mode: "self_paced" | "fixed_interval";
209
+ readonly cron: {
210
+ readonly expression: string;
211
+ readonly timeZone: string;
212
+ } | null;
213
+ readonly state: "cancelled" | "active" | "failed" | "paused" | "finished";
214
+ readonly commandId: string;
215
+ readonly reason: string | null;
216
+ readonly nextDueAt: number | null;
217
+ readonly lastError: string | null;
218
+ readonly consecutiveFailures: number;
219
+ };
220
+ }>;
221
+ deleteSchedule: (request: z.input<typeof localScheduleControlInputSchema>, signal: AbortSignal) => Promise<{
222
+ readonly agentId: string;
223
+ readonly kind: "almanac";
224
+ readonly schedule: {
225
+ readonly id: string;
226
+ readonly sessionId: string;
227
+ readonly prompt: string;
228
+ readonly cron: {
229
+ readonly expression: string;
230
+ readonly timeZone: string;
231
+ } | null;
232
+ readonly nextDueAt: number;
233
+ readonly state: "cancelled" | "active" | "paused" | "finished";
234
+ readonly lastCommandId: string | null;
235
+ readonly lastError: string | null;
236
+ readonly attempt: number;
237
+ };
238
+ } | {
239
+ readonly agentId: string;
240
+ readonly kind: "loop";
241
+ readonly schedule: {
242
+ readonly sessionId: string;
243
+ readonly loopId: string;
244
+ readonly origin: "human" | "model";
245
+ readonly expiresAt: number | null;
246
+ readonly prompt: string;
247
+ readonly mode: "self_paced" | "fixed_interval";
248
+ readonly cron: {
249
+ readonly expression: string;
250
+ readonly timeZone: string;
251
+ } | null;
252
+ readonly state: "cancelled" | "active" | "failed" | "paused" | "finished";
253
+ readonly commandId: string;
254
+ readonly reason: string | null;
255
+ readonly nextDueAt: number | null;
256
+ readonly lastError: string | null;
257
+ readonly consecutiveFailures: number;
258
+ };
259
+ }>;
130
260
  archive: (request: Readonly<{
131
261
  invocationRoot: string;
132
262
  agentId: string;
@@ -17,13 +17,16 @@ import { z } from "zod";
17
17
  import * as Schema from "effect/Schema";
18
18
  import { selectionSchema } from "./private-local-service-discovery.js";
19
19
  import { createShiitakeHttpRoutes } from "@fungi.computer/shiitake/http";
20
+ import { questionContract } from "@fungi.computer/shiitake-extensions/question";
20
21
  import { listPrivateLocalAgentStores, LocalStoreSchemaError, readPrivateLocalAgentProfile, readPrivateLocalSessionSummaries, isArchivedStore, unreadableStoreReason, } from "./private-local-session-discovery.js";
21
22
  import { createPrivateLocalSessionRequestId, openPrivateLocalRuntimeHost, resolvePrivateLocalCreateStore, } from "./private-local-runtime-host.js";
22
23
  import { parsePrivateLocalExecPath } from "./private-local-exec-path.js";
23
24
  import { createLocalMeshLinks, localMeshLinkKey, } from "./private-local-mesh-links.js";
24
25
  import { agentMailAddressSchema, machineAgentRequestSchema, machineDirectorySchema, } from "@fungi.computer/seam/machine-link-protocol";
25
- import { commandSettlementSchema } from "@fungi.computer/shiitake/fleet";
26
+ import { commandSettlementSchema, settlementReadableBy, } from "@fungi.computer/shiitake/fleet";
26
27
  import { BirdDogMailSchema } from "@fungi.computer/bird-dog/effect";
28
+ import { controlAgentScheduleWithContent } from "@fungi.computer/shiitake-extensions/schedules";
29
+ import { localScheduleControlInputSchema, localScheduleListInputSchema, readLocalSchedulePage, } from "./private-local-schedules.js";
27
30
  export class LocalSessionPathConflict extends Error {
28
31
  name = "LocalSessionPathConflict";
29
32
  constructor() {
@@ -163,7 +166,7 @@ serviceNative, onExecApprovalDecision = () => undefined) {
163
166
  return { status: "accepted", receipt: { sessionId } };
164
167
  }
165
168
  const facts = await admitted(owner, () => owner.readSettlement(operation.sessionId, operation.commandId));
166
- if (facts !== undefined && facts.acceptedSender !== actorAgentId)
169
+ if (facts !== undefined && !settlementReadableBy(facts, actorAgentId))
167
170
  return "refused";
168
171
  return {
169
172
  status: "accepted",
@@ -902,6 +905,62 @@ serviceNative, onExecApprovalDecision = () => undefined) {
902
905
  throw new AgentArchiveRefused("archived", `Agent ${agentId} is archived; unarchive it to work with it`);
903
906
  throw new Error("Agent is outside this Local project");
904
907
  }
908
+ /**
909
+ * Run one sender-authenticated command on a child's question owner. Which
910
+ * Agent the item waits on is decided by the child's own store; this door
911
+ * only states who is calling.
912
+ */
913
+ async function callQuestionAsSender(target, senderAgentId, senderSessionId, command, commandInput, output, signal) {
914
+ ensureRunning();
915
+ signal.throwIfAborted();
916
+ const databasePath = (await targets(signal, "all")).get(target.agentId);
917
+ if (databasePath === undefined)
918
+ return outsideOrArchived(target.agentId, signal);
919
+ const { owner, release } = await admittedOwner(target.agentId, signal);
920
+ try {
921
+ return await admitted(owner, async () => Schema.decodeUnknownSync(output)(await owner
922
+ .getSession(target.sessionId)
923
+ .extension("botanical-session-question")
924
+ .call(command, commandInput, {
925
+ agentId: senderAgentId,
926
+ sessionId: senderSessionId,
927
+ })));
928
+ }
929
+ finally {
930
+ release();
931
+ }
932
+ }
933
+ async function answerAsSender(input, senderAgentId, senderSessionId, signal) {
934
+ const decoded = await callQuestionAsSender(input, senderAgentId, senderSessionId, "answerAgent", {
935
+ askId: input.askId,
936
+ requestId: input.requestId,
937
+ answers: input.answers,
938
+ }, questionContract.commands.answerAgent.output, signal);
939
+ const { result } = decoded;
940
+ if (result.result === "accepted")
941
+ return result.commandId === undefined
942
+ ? { status: "answered", via: result.via }
943
+ : {
944
+ status: "answered",
945
+ via: result.via,
946
+ commandId: result.commandId,
947
+ };
948
+ if (result.result === "closed")
949
+ return { status: "closed", reason: result.status };
950
+ if (result.result === "refused")
951
+ return { status: "refused", reason: result.reason };
952
+ return { status: "not_found" };
953
+ }
954
+ async function escalateAsSender(input, senderAgentId, senderSessionId, signal) {
955
+ const decoded = await callQuestionAsSender(input, senderAgentId, senderSessionId, "escalateAgent", { askId: input.itemId }, questionContract.commands.escalateAgent.output, signal);
956
+ if (decoded.result === "escalated")
957
+ return { status: "escalated", to: decoded.to };
958
+ if (decoded.result === "closed")
959
+ return { status: "closed", reason: decoded.status };
960
+ if (decoded.result === "refused")
961
+ return { status: "refused", reason: decoded.reason };
962
+ return { status: "not_found" };
963
+ }
905
964
  /**
906
965
  * The first command still queued or running on any of an Agent's Sessions,
907
966
  * so an archive can name it rather than cancel it.
@@ -977,7 +1036,10 @@ serviceNative, onExecApprovalDecision = () => undefined) {
977
1036
  }
978
1037
  }
979
1038
  return {
1039
+ targets,
980
1040
  owner: admittedOwner,
1041
+ answer: answerAsSender,
1042
+ escalate: escalateAsSender,
981
1043
  archive: (request, signal) => setArchived(request, true, signal),
982
1044
  unarchive: (request, signal) => setArchived(request, false, signal),
983
1045
  list: async (signal, options) => {
@@ -1307,6 +1369,29 @@ serviceNative, onExecApprovalDecision = () => undefined) {
1307
1369
  scheduleWakes();
1308
1370
  await saveRecovery();
1309
1371
  }
1372
+ /** Private person-only schedule discovery, never the Agent or mesh directory. */
1373
+ async function schedules(request, signal) {
1374
+ ensureRunning();
1375
+ signal.throwIfAborted();
1376
+ const input = localScheduleListInputSchema.parse(request);
1377
+ const root = await realpath(input.invocationRoot);
1378
+ const control = directory(root);
1379
+ const agents = [...(await control.targets(signal, "all")).keys()];
1380
+ return readLocalSchedulePage(agents, (agentId, requestSignal) => control.owner(agentId, requestSignal, true), input, signal);
1381
+ }
1382
+ async function controlSchedule(request, action, signal) {
1383
+ ensureRunning();
1384
+ signal.throwIfAborted();
1385
+ const input = localScheduleControlInputSchema.parse(request);
1386
+ const root = await realpath(input.invocationRoot);
1387
+ const { owner, release } = await directory(root).owner(input.target.agentId, signal, true);
1388
+ try {
1389
+ return await controlAgentScheduleWithContent(owner, input.target, action);
1390
+ }
1391
+ finally {
1392
+ release();
1393
+ }
1394
+ }
1310
1395
  /** A person archives or restores any Agent of one project folder. */
1311
1396
  async function archive(request, signal) {
1312
1397
  const control = directory(await realpath(request.invocationRoot));
@@ -1462,6 +1547,9 @@ serviceNative, onExecApprovalDecision = () => undefined) {
1462
1547
  effectiveModel,
1463
1548
  sandboxStatus,
1464
1549
  sweepIdle,
1550
+ schedules,
1551
+ pauseSchedule: (request, signal) => controlSchedule(request, "pause", signal),
1552
+ deleteSchedule: (request, signal) => controlSchedule(request, "delete", signal),
1465
1553
  archive,
1466
1554
  stopAll,
1467
1555
  restore,
@@ -1,3 +1,6 @@
1
+ export type { PrivateLocalScheduleCapability } from "./private-local-schedule-client.js";
2
+ export type { AgentScheduleRow, AgentScheduleTarget, } from "@fungi.computer/shiitake-extensions/schedules";
3
+ export type { LocalSchedulePage } from "./private-local-schedules.js";
1
4
  export { LocalSandboxUnavailable } from "./private-local-containment.js";
2
5
  export type { StopAllResult } from "./private-local-stop-all.js";
3
6
  export { LocalAgentSelectionAmbiguous, LocalSessionPathConflict, } from "./private-local-service-owner.js";
@@ -1,5 +1,6 @@
1
1
  import { FungiMcpSelectionConflict } from "./private-fungi-mcp-selection.js";
2
2
  import { logEvent } from "./log-event.js";
3
+ import { createPrivateLocalScheduleCapability } from "./private-local-schedule-client.js";
3
4
  import { readBoundedResponse } from "./private-bounded-response.js";
4
5
  import { LocalSandboxUnavailable, sandboxUnavailableReasonSchema, } from "./private-local-containment.js";
5
6
  export { LocalSandboxUnavailable } from "./private-local-containment.js";
@@ -466,7 +467,13 @@ export function createPrivateLocalServiceManager(options = {}) {
466
467
  // stopped it decides when it returns.
467
468
  const ready = await status();
468
469
  if (ready === undefined)
469
- return { kind: "unavailable" };
470
+ return {
471
+ kind: "unavailable",
472
+ // The discovery owner's process-lifetime lease distinguishes an
473
+ // overloaded listener from a stopped service without guessing
474
+ // from the failed HTTP probe or an error message.
475
+ alive: await serviceOwnerIsLive(selectedDirectory),
476
+ };
470
477
  accept(ready);
471
478
  const again = await postSelection("select", {
472
479
  invocationRoot: input.invocationRoot,
@@ -507,6 +514,7 @@ export function createPrivateLocalServiceManager(options = {}) {
507
514
  snapshot: z.unknown(),
508
515
  });
509
516
  return {
517
+ schedules: (invocationRoot) => createPrivateLocalScheduleCapability(invocationRoot, start),
510
518
  status,
511
519
  start,
512
520
  createSession: (creation, request) => openConnection("sessions", creation, request),
@@ -29,24 +29,29 @@ export declare class LocalStoreSchemaError extends Error {
29
29
  readonly name = "LocalStoreSchemaError";
30
30
  constructor(databasePath: string, options?: ErrorOptions);
31
31
  }
32
+ export type PrivateLocalAgentProfile = ReturnType<typeof openAgentProfile>;
32
33
  /**
33
34
  * One Agent store's chosen display name, default model, whether it is
34
35
  * archived and the Agent that created it (absent when a human made it), read
35
- * without opening a host.
36
+ * without opening a host. Directory listings and the sub-agents footer read
37
+ * every store's profile on each call; an unchanged store file answers from
38
+ * memory instead of reopening SQLite (each Bun open/close also leaks native
39
+ * memory).
36
40
  */
37
- export declare function readPrivateLocalAgentProfile(path: string): {
41
+ export declare function readPrivateLocalAgentProfile(path: string): PrivateLocalAgentProfile;
42
+ declare function openAgentProfile(path: string): Readonly<{
38
43
  name: string | undefined;
39
44
  createdBy: string | null;
40
45
  archived: boolean;
41
46
  mode: "read-only" | "workspace" | "full-access";
42
- writableFolders: string[];
47
+ writableFolders: readonly string[];
43
48
  folder: string;
44
49
  model: {
45
50
  readonly providerId: string;
46
51
  readonly modelId: string;
47
52
  readonly reasoning?: "high" | "low" | "max" | "medium" | "minimal" | "xhigh" | undefined;
48
53
  } | null;
49
- };
54
+ }>;
50
55
  export declare function readPrivateLocalSessionSummaries(path: string): Promise<readonly LocalSummary[]>;
51
56
  /**
52
57
  * Discover existing folder sessions without opening or reconciling a host.
@@ -71,3 +76,4 @@ export declare function readPrivateLocalSessionDiscovery(invocationRoot: string,
71
76
  }>;
72
77
  /** Project the service-owned discovery into choices that resume through it. */
73
78
  export declare function projectPrivateLocalSessionDiscovery(discovered: Awaited<ReturnType<typeof readPrivateLocalSessionDiscovery>>, connect: PrivateLocalServiceManager["connect"]): PrivateLocalSessionDiscovery;
79
+ export {};