@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.
- package/README.md +27 -4
- package/dist/internal/private-workerd-sandbox-worker.mjs +243 -273
- package/dist/internal/sandbox-metafile.json +35 -30
- package/dist/local-board-agents.d.ts +46 -0
- package/dist/local-board-agents.js +138 -0
- package/dist/local-board-shipping.d.ts +129 -0
- package/dist/local-board-shipping.js +325 -0
- package/dist/local-extensions.d.ts +4 -2
- package/dist/local-extensions.js +56 -22
- package/dist/local-login.js +33 -25
- package/dist/private-fungi-mcp-connection.js +6 -1
- package/dist/private-invocation-computer.d.ts +6 -2
- package/dist/private-invocation-computer.js +20 -13
- package/dist/private-local-agent-control.d.ts +5 -1
- package/dist/private-local-agent-control.js +11 -1
- package/dist/private-local-auth-rpc.d.ts +5 -0
- package/dist/private-local-auth-rpc.js +46 -4
- package/dist/private-local-client-version.js +3 -1
- package/dist/private-local-computer-processes.d.ts +7 -2
- package/dist/private-local-computer-processes.js +47 -23
- package/dist/private-local-delegation.d.ts +18 -0
- package/dist/private-local-delegation.js +84 -26
- package/dist/private-local-exec-environment.d.ts +5 -2
- package/dist/private-local-exec-environment.js +35 -1
- package/dist/private-local-exec-review.js +14 -7
- package/dist/private-local-exec-rules.d.ts +7 -0
- package/dist/private-local-exec-rules.js +23 -6
- package/dist/private-local-extension-records.d.ts +1 -1
- package/dist/private-local-runtime-host.js +54 -27
- package/dist/private-local-sandbox-policy.js +4 -0
- package/dist/private-local-sandbox.d.ts +3 -1
- package/dist/private-local-sandbox.js +2 -2
- package/dist/private-local-schedule-client.d.ts +12 -0
- package/dist/private-local-schedule-client.js +41 -0
- package/dist/private-local-schedules.d.ts +84 -0
- package/dist/private-local-schedules.js +65 -0
- package/dist/private-local-service-contract.d.ts +4 -0
- package/dist/private-local-service-discovery.d.ts +3 -3
- package/dist/private-local-service-discovery.js +1 -1
- package/dist/private-local-service-listener.js +9 -0
- package/dist/private-local-service-owner.d.ts +130 -0
- package/dist/private-local-service-owner.js +90 -2
- package/dist/private-local-service.d.ts +3 -0
- package/dist/private-local-service.js +9 -1
- package/dist/private-local-session-discovery.d.ts +10 -4
- package/dist/private-local-session-discovery.js +37 -4
- package/dist/private-provider-rejection.d.ts +1 -1
- package/dist/private-provider-rejection.js +28 -8
- package/dist/private-sqlite-owner.d.ts +7 -1
- package/dist/private-sqlite-owner.js +14 -2
- 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 =
|
|
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<
|
|
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<
|
|
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 =
|
|
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
|
|
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 {
|
|
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 {};
|