@fungi.computer/shiitake-extensions 0.0.1 → 0.1.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 +43 -13
- package/dist/almanac/contract.d.ts +94 -0
- package/dist/almanac/contract.js +71 -5
- package/dist/goal/client.d.ts +5 -2
- package/dist/goal/client.js +66 -72
- package/dist/goal/contract.d.ts +181 -64
- package/dist/goal/contract.js +102 -12
- package/dist/goal/host.js +188 -113
- package/dist/home/contract.d.ts +18 -0
- package/dist/home/contract.js +24 -3
- package/dist/home/host.d.ts +3 -0
- package/dist/home/host.js +1 -2
- package/dist/home/routing.js +13 -24
- package/dist/internal/history.d.ts +16 -0
- package/dist/internal/history.js +8 -0
- package/dist/loop/client.d.ts +1 -1
- package/dist/loop/client.js +28 -34
- package/dist/loop/contract.d.ts +113 -84
- package/dist/loop/contract.js +77 -9
- package/dist/loop/host.d.ts +1 -0
- package/dist/loop/host.js +17 -30
- package/dist/plan/client.d.ts +1 -1
- package/dist/plan/contract.d.ts +33 -0
- package/dist/plan/contract.js +28 -3
- package/dist/plan/host.js +12 -7
- package/dist/question/client.d.ts +1 -1
- package/dist/question/contract.d.ts +158 -0
- package/dist/question/contract.js +92 -7
- package/dist/question/host.js +34 -20
- package/dist/roster/conversation-agents.d.ts +31 -0
- package/dist/roster/conversation-agents.js +114 -0
- package/dist/roster.js +1 -0
- package/dist/skills/host.d.ts +1 -0
- package/dist/skills/host.js +22 -17
- package/dist/skills/source.d.ts +34 -0
- package/dist/subagents/client.d.ts +1 -1
- package/dist/subagents/client.js +43 -12
- package/dist/subagents/contract.d.ts +46 -8
- package/dist/subagents/contract.js +38 -8
- package/dist/subagents/host.d.ts +20 -8
- package/dist/subagents/host.js +58 -12
- package/docs/api/README.md +35 -0
- package/docs/api/almanac/contract.md +90 -0
- package/docs/api/almanac/host.md +19 -0
- package/docs/api/goal/client.md +26 -0
- package/docs/api/goal/contract.md +247 -0
- package/docs/api/goal/host.md +29 -0
- package/docs/api/home/contract.md +58 -0
- package/docs/api/home/host.md +55 -0
- package/docs/api/loop/client.md +25 -0
- package/docs/api/loop/contract.md +159 -0
- package/docs/api/loop/host.md +30 -0
- package/docs/api/plan/client.md +26 -0
- package/docs/api/plan/contract.md +163 -0
- package/docs/api/plan/host.md +19 -0
- package/docs/api/question/client.md +25 -0
- package/docs/api/question/contract.md +377 -0
- package/docs/api/question/host.md +19 -0
- package/docs/api/roster/conversation-agents.md +80 -0
- package/docs/api/roster.md +40 -0
- package/docs/api/skills/host.md +39 -0
- package/docs/api/skills/source.md +104 -0
- package/docs/api/subagents/client.md +26 -0
- package/docs/api/subagents/contract.md +131 -0
- package/docs/api/subagents/host.md +115 -0
- package/package.json +14 -8
package/dist/loop/contract.js
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { historySchema } from "../internal/history.js";
|
|
2
1
|
import { ExtensionId } from "@fungi.computer/shiitake/extensions";
|
|
3
2
|
import * as Schema from "effect/Schema";
|
|
4
3
|
import { CatalogStep } from "../internal/catalog.js";
|
|
@@ -11,19 +10,26 @@ const Prompt = Schema.String.pipe(Schema.check(Schema.isMinLength(1)), Schema.ch
|
|
|
11
10
|
const RequestId = Schema.String.pipe(Schema.check(Schema.isMinLength(1)), Schema.check(Schema.isMaxLength(200)));
|
|
12
11
|
/** A named cron cadence evaluated in one IANA time zone. */
|
|
13
12
|
export const LoopCron = Schema.Struct({
|
|
13
|
+
/** Cron expression for the fixed cadence. */
|
|
14
14
|
expression: Schema.String.pipe(Schema.check(Schema.isMinLength(1)), Schema.check(Schema.isMaxLength(256))),
|
|
15
|
+
/** IANA time zone in which the expression is evaluated. */
|
|
15
16
|
timeZone: Schema.String.pipe(Schema.check(Schema.isMinLength(1)), Schema.check(Schema.isMaxLength(128))),
|
|
16
17
|
});
|
|
17
18
|
/** One Session's loop as every client sees it. */
|
|
18
19
|
export const Loop = Schema.Struct({
|
|
20
|
+
/** Identity of the loop whose occurrences are being tracked. */
|
|
19
21
|
loopId: Schema.String,
|
|
22
|
+
/** Whether a person or the model started this loop. */
|
|
20
23
|
origin: Schema.Literals(["human", "model"]),
|
|
24
|
+
/** Configured expiry timestamp, or null when absent. */
|
|
21
25
|
expiresAt: Schema.NullOr(Schema.Int),
|
|
22
26
|
/** Original instruction repeated for every occurrence. */
|
|
23
27
|
prompt: Prompt,
|
|
28
|
+
/** Whether the model or a fixed cron cadence schedules occurrences. */
|
|
24
29
|
mode: Schema.Literals(["self_paced", "fixed_interval"]),
|
|
25
30
|
/** Cadence of a fixed-interval loop; null when the model paces it. */
|
|
26
31
|
cron: Schema.NullOr(LoopCron),
|
|
32
|
+
/** Current loop lifecycle state. */
|
|
27
33
|
state: Schema.Literals([
|
|
28
34
|
"active",
|
|
29
35
|
"paused",
|
|
@@ -41,9 +47,13 @@ export const Loop = Schema.Struct({
|
|
|
41
47
|
lastError: Schema.NullOr(Schema.String),
|
|
42
48
|
});
|
|
43
49
|
const StartResult = Schema.Struct({
|
|
50
|
+
/** Newly started loop. */
|
|
44
51
|
loop: Loop,
|
|
52
|
+
/** Admission receipt for the start command. */
|
|
45
53
|
accepted: Schema.Struct({
|
|
54
|
+
/** Successful admission discriminator. */
|
|
46
55
|
status: Schema.Literal("accepted"),
|
|
56
|
+
/** Identity of the accepted start command. */
|
|
47
57
|
commandId: Schema.String,
|
|
48
58
|
}),
|
|
49
59
|
});
|
|
@@ -54,41 +64,99 @@ const StartResult = Schema.Struct({
|
|
|
54
64
|
*/
|
|
55
65
|
export const LoopCommandInput = Schema.Union([
|
|
56
66
|
Schema.Struct({
|
|
67
|
+
/** Select the loop-starting control. */
|
|
57
68
|
action: Schema.Literal("start"),
|
|
69
|
+
/** Human prompt text, optionally prefixed with an interval. */
|
|
58
70
|
text: Prompt,
|
|
71
|
+
/** Idempotency key for this start command. */
|
|
59
72
|
requestId: RequestId,
|
|
60
73
|
}),
|
|
61
74
|
Schema.Struct({
|
|
75
|
+
/** Select an existing-loop control or open new-loop entry. */
|
|
62
76
|
action: Schema.Literals(["status", "pause", "resume", "stop", "new"]),
|
|
63
77
|
}),
|
|
64
78
|
Schema.Struct({}),
|
|
65
79
|
]);
|
|
66
80
|
/** Loop's contract: Session-scope controls and one projection slot. */
|
|
67
81
|
export const LoopProjection = Schema.Struct({
|
|
68
|
-
|
|
69
|
-
|
|
82
|
+
/** Loop currently visible to Session clients. */
|
|
83
|
+
loop: Loop,
|
|
70
84
|
});
|
|
85
|
+
/** Session-owned loop controls and their visible state. */
|
|
71
86
|
export const loopContract = {
|
|
87
|
+
/** Stable extension identity. */
|
|
72
88
|
id: ExtensionId.make("botanical-session-loop"),
|
|
89
|
+
/** Loop operations admitted by the Session owner. */
|
|
73
90
|
commands: {
|
|
74
|
-
|
|
91
|
+
/** Open or answer the human loop controls. */
|
|
92
|
+
loop: {
|
|
93
|
+
/** The Session owns this loop. */
|
|
94
|
+
scope: "session",
|
|
95
|
+
/** Decode the selected human action. */
|
|
96
|
+
input: LoopCommandInput,
|
|
97
|
+
/** Return the server-owned conversation step. */
|
|
98
|
+
output: CatalogStep,
|
|
99
|
+
},
|
|
100
|
+
/** Start a loop whose next occurrence is chosen by the model. */
|
|
75
101
|
startSelfPaced: {
|
|
102
|
+
/** The Session owns this loop. */
|
|
76
103
|
scope: "session",
|
|
77
|
-
|
|
104
|
+
/** Decode the repeated prompt and command identity. */
|
|
105
|
+
input: Schema.Struct({
|
|
106
|
+
/** Instruction repeated on every occurrence. */
|
|
107
|
+
prompt: Prompt,
|
|
108
|
+
/** Idempotency key for the start command. */
|
|
109
|
+
requestId: RequestId,
|
|
110
|
+
}),
|
|
111
|
+
/** Return the loop and admission receipt. */
|
|
78
112
|
output: StartResult,
|
|
79
113
|
},
|
|
114
|
+
/** Start a loop with a fixed cron cadence. */
|
|
80
115
|
startFixedInterval: {
|
|
116
|
+
/** The Session owns this loop. */
|
|
81
117
|
scope: "session",
|
|
118
|
+
/** Decode the prompt, cadence and command identity. */
|
|
82
119
|
input: Schema.Struct({
|
|
120
|
+
/** Instruction repeated on every occurrence. */
|
|
83
121
|
prompt: Prompt,
|
|
122
|
+
/** Idempotency key for the start command. */
|
|
84
123
|
requestId: RequestId,
|
|
85
124
|
...LoopCron.fields,
|
|
86
125
|
}),
|
|
126
|
+
/** Return the loop and admission receipt. */
|
|
87
127
|
output: StartResult,
|
|
88
128
|
},
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
129
|
+
/** Suspend the current loop. */
|
|
130
|
+
pause: {
|
|
131
|
+
/** The Session owns this loop. */
|
|
132
|
+
scope: "session",
|
|
133
|
+
/** No additional arguments are required. */
|
|
134
|
+
input: Schema.Struct({}),
|
|
135
|
+
/** Return the paused loop. */
|
|
136
|
+
output: Loop,
|
|
137
|
+
},
|
|
138
|
+
/** Resume the current paused loop. */
|
|
139
|
+
resume: {
|
|
140
|
+
/** The Session owns this loop. */
|
|
141
|
+
scope: "session",
|
|
142
|
+
/** No additional arguments are required. */
|
|
143
|
+
input: Schema.Struct({}),
|
|
144
|
+
/** Return the resumed loop. */
|
|
145
|
+
output: Loop,
|
|
146
|
+
},
|
|
147
|
+
/** Stop future occurrences of the current loop. */
|
|
148
|
+
stop: {
|
|
149
|
+
/** The Session owns this loop. */
|
|
150
|
+
scope: "session",
|
|
151
|
+
/** No additional arguments are required. */
|
|
152
|
+
input: Schema.Struct({}),
|
|
153
|
+
/** Return the stopped loop. */
|
|
154
|
+
output: Loop,
|
|
155
|
+
},
|
|
156
|
+
},
|
|
157
|
+
/** Current loop visible to Session clients. */
|
|
158
|
+
projection: {
|
|
159
|
+
/** Decoder for projected loop state. */
|
|
160
|
+
schema: LoopProjection,
|
|
92
161
|
},
|
|
93
|
-
projection: { schema: LoopProjection },
|
|
94
162
|
};
|
package/dist/loop/host.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { type RosteredExtensionV0 } from "@fungi.computer/shiitake/extensions";
|
|
2
2
|
/** The commands this extension advertises to every client. */
|
|
3
3
|
export declare const loopCatalog: import("@fungi.computer/shiitake/extensions").Json;
|
|
4
|
+
/** Durable Session loop implementation with prompts, controls and scheduled occurrences. */
|
|
4
5
|
export declare const loopExtension: RosteredExtensionV0;
|
package/dist/loop/host.js
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { appendHistory, emptyHistory, historySchema, } from "../internal/history.js";
|
|
2
1
|
import { Mycelium } from "@fungi.computer/mycelium";
|
|
3
2
|
import { defineCollection, defineExtension, ExtensionCommandError, ExtensionStop, } from "@fungi.computer/shiitake/extensions";
|
|
4
3
|
import * as Cause from "effect/Cause";
|
|
@@ -12,14 +11,19 @@ import { z } from "zod";
|
|
|
12
11
|
import { ask, catalog, done, freshRequestId } from "../internal/catalog.js";
|
|
13
12
|
import { nextOccurrence } from "../internal/cron.js";
|
|
14
13
|
import { Loop, LoopCommandInput, loopContract } from "./contract.js";
|
|
14
|
+
/** The rules the model follows; the describe page and a live loop's prompt share them. */
|
|
15
|
+
const rules = "You may start loops yourself; one live loop per Session. schedule_next is allowed only in the active self-paced loop's own run, clamped to 60–3600 seconds. Omitting it ends that loop. stop works in any Session run. Human pause/resume/stop and Esc remain user controls.";
|
|
15
16
|
/** Hashed prose and policy: every constant loop reads comes from here. */
|
|
16
17
|
const config = {
|
|
17
18
|
minDelaySeconds: 60,
|
|
18
19
|
maxDelaySeconds: 3_600,
|
|
19
20
|
fixedLifetimeMs: 7 * 24 * 60 * 60 * 1_000,
|
|
20
21
|
promptLimit: 1_024,
|
|
21
|
-
|
|
22
|
-
|
|
22
|
+
idle: "Recurring or scheduled work? forage.describe({path:'loop'}) first.",
|
|
23
|
+
guide: "Session loop tools: loop.get, loop.start, loop.schedule_next, loop.stop. " +
|
|
24
|
+
rules,
|
|
25
|
+
moduleDescription: "Session loop tools: get({}), start({prompt,cadence}), schedule_next({delaySeconds,reason}), stop({reason}). " +
|
|
26
|
+
rules,
|
|
23
27
|
scheduleDescription: "Schedule the original loop prompt after this run settles successfully. delaySeconds is clamped between 60 and 3600. The reason is stored as why now and does not change the loop prompt. Do not call this when the loop should end.",
|
|
24
28
|
};
|
|
25
29
|
/** One Session's durable loop row. */
|
|
@@ -36,7 +40,6 @@ const LoopRow = Schema.Struct({
|
|
|
36
40
|
})),
|
|
37
41
|
});
|
|
38
42
|
const collections = {
|
|
39
|
-
history: defineCollection(Schema.Struct({ sessionId: Schema.String, history: historySchema(Loop) }), { key: (row) => row.sessionId }),
|
|
40
43
|
loops: defineCollection(LoopRow, {
|
|
41
44
|
key: (loop) => loop.sessionId,
|
|
42
45
|
index: (loop) => loop.state,
|
|
@@ -62,19 +65,6 @@ function projected(row) {
|
|
|
62
65
|
}
|
|
63
66
|
function commitLoop(ctx, row) {
|
|
64
67
|
return Effect.gen(function* () {
|
|
65
|
-
const previous = Option.getOrUndefined(yield* ctx.state.loops.get(row.sessionId));
|
|
66
|
-
if (previous?.state !== row.state &&
|
|
67
|
-
(row.state === "active" ||
|
|
68
|
-
row.state === "finished" ||
|
|
69
|
-
row.state === "cancelled" ||
|
|
70
|
-
row.state === "failed")) {
|
|
71
|
-
const history = Option.getOrUndefined(yield* ctx.state.history.get(row.sessionId))
|
|
72
|
-
?.history ?? emptyHistory();
|
|
73
|
-
yield* ctx.state.history.put({
|
|
74
|
-
sessionId: row.sessionId,
|
|
75
|
-
history: appendHistory(history, projected(row), row.commandId || null, yield* Clock.currentTimeMillis),
|
|
76
|
-
});
|
|
77
|
-
}
|
|
78
68
|
yield* ctx.state.loops.put(row);
|
|
79
69
|
});
|
|
80
70
|
}
|
|
@@ -582,6 +572,7 @@ export const loopCatalog = catalog([
|
|
|
582
572
|
},
|
|
583
573
|
},
|
|
584
574
|
]);
|
|
575
|
+
/** Durable Session loop implementation with prompts, controls and scheduled occurrences. */
|
|
585
576
|
export const loopExtension = defineExtension({
|
|
586
577
|
id: loopContract.id,
|
|
587
578
|
version: "3.0.0",
|
|
@@ -592,13 +583,13 @@ export const loopExtension = defineExtension({
|
|
|
592
583
|
prompt: (ctx, { sessionId }) => ctx.state.loops.get(sessionId).pipe(Effect.map((found) => [
|
|
593
584
|
{
|
|
594
585
|
name: "loop",
|
|
595
|
-
text: (
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
586
|
+
text: Option.isSome(found)
|
|
587
|
+
? (ctx.config.guide +
|
|
588
|
+
"\n" +
|
|
589
|
+
loopLine(projected(found.value)) +
|
|
590
|
+
"; nextDueAt=" +
|
|
591
|
+
String(found.value.nextDueAt)).slice(0, ctx.config.promptLimit)
|
|
592
|
+
: ctx.config.idle,
|
|
602
593
|
},
|
|
603
594
|
])),
|
|
604
595
|
catalog: loopCatalog,
|
|
@@ -606,13 +597,9 @@ export const loopExtension = defineExtension({
|
|
|
606
597
|
schema: loopContract.projection.schema,
|
|
607
598
|
read: (ctx, sessionId) => {
|
|
608
599
|
const loop = Option.getOrUndefined(ctx.state.loops.peek(sessionId));
|
|
609
|
-
|
|
610
|
-
return loop === undefined && history === undefined
|
|
600
|
+
return loop === undefined
|
|
611
601
|
? Option.none()
|
|
612
|
-
: Option.some({
|
|
613
|
-
loop: loop === undefined ? null : projected(loop),
|
|
614
|
-
history: history ?? emptyHistory(),
|
|
615
|
-
});
|
|
602
|
+
: Option.some({ loop: projected(loop) });
|
|
616
603
|
},
|
|
617
604
|
},
|
|
618
605
|
hooks: {
|
package/dist/plan/client.d.ts
CHANGED
|
@@ -6,7 +6,7 @@ import { type ClientContributionV1 } from "@fungi.computer/shiitake/client-entry
|
|
|
6
6
|
declare const _default: Readonly<{
|
|
7
7
|
protocol: "shiitake.client/v1";
|
|
8
8
|
id: import("@fungi.computer/shiitake/extensions").ExtensionId;
|
|
9
|
-
|
|
9
|
+
promptPresentation?: "footer";
|
|
10
10
|
attach?: (context: import("@fungi.computer/shiitake/client-entry").ClientExtensionContextV1) => import("effect/Effect").Effect<void, Error, import("effect/Scope").Scope>;
|
|
11
11
|
contribute?: (slot: import("@fungi.computer/shiitake").ExtensionSlotV0 | undefined) => ClientContributionV1;
|
|
12
12
|
}>;
|
package/dist/plan/contract.d.ts
CHANGED
|
@@ -4,31 +4,47 @@ import * as Schema from "effect/Schema";
|
|
|
4
4
|
* steps means no plan: replacing it with an empty checklist clears it.
|
|
5
5
|
*/
|
|
6
6
|
export declare const Plan: Schema.Struct<{
|
|
7
|
+
/** Optional explanation for this checklist replacement. */
|
|
7
8
|
readonly explanation: Schema.optionalKey<Schema.String>;
|
|
9
|
+
/** Ordered checklist; an empty array clears the plan. */
|
|
8
10
|
readonly steps: Schema.$Array<Schema.Struct<{
|
|
11
|
+
/** Work described by this checklist step. */
|
|
9
12
|
readonly step: Schema.String;
|
|
13
|
+
/** Current progress state of the step. */
|
|
10
14
|
readonly status: Schema.Literals<readonly ["pending", "in_progress", "completed"]>;
|
|
11
15
|
}>>;
|
|
12
16
|
}>;
|
|
17
|
+
/** Checklist replacement that permits at most one in-progress step. */
|
|
13
18
|
export declare const PlanInput: Schema.Struct<{
|
|
19
|
+
/** Optional explanation for this checklist replacement. */
|
|
14
20
|
readonly explanation: Schema.optionalKey<Schema.String>;
|
|
21
|
+
/** Ordered checklist; an empty array clears the plan. */
|
|
15
22
|
readonly steps: Schema.$Array<Schema.Struct<{
|
|
23
|
+
/** Work described by this checklist step. */
|
|
16
24
|
readonly step: Schema.String;
|
|
25
|
+
/** Current progress state of the step. */
|
|
17
26
|
readonly status: Schema.Literals<readonly ["pending", "in_progress", "completed"]>;
|
|
18
27
|
}>>;
|
|
19
28
|
}>;
|
|
29
|
+
/** One Session's ordered checklist and optional explanation. */
|
|
20
30
|
export type Plan = Schema.Schema.Type<typeof Plan>;
|
|
21
31
|
/** Open work remains: some step is not completed. Cleared and finished plans are closed. */
|
|
22
32
|
export declare function planIsOpen(plan: Plan): boolean;
|
|
23
33
|
/** The current checklist and every committed replacement, clears included. */
|
|
24
34
|
export declare const PlanProjection: Schema.Struct<{
|
|
35
|
+
/** Latest checklist replacement. */
|
|
25
36
|
readonly plan: Schema.Struct<{
|
|
37
|
+
/** Optional explanation for this checklist replacement. */
|
|
26
38
|
readonly explanation: Schema.optionalKey<Schema.String>;
|
|
39
|
+
/** Ordered checklist; an empty array clears the plan. */
|
|
27
40
|
readonly steps: Schema.$Array<Schema.Struct<{
|
|
41
|
+
/** Work described by this checklist step. */
|
|
28
42
|
readonly step: Schema.String;
|
|
43
|
+
/** Current progress state of the step. */
|
|
29
44
|
readonly status: Schema.Literals<readonly ["pending", "in_progress", "completed"]>;
|
|
30
45
|
}>>;
|
|
31
46
|
}>;
|
|
47
|
+
/** Committed replacements, including explicit clears. */
|
|
32
48
|
readonly history: Schema.Struct<{
|
|
33
49
|
readonly nextSequence: Schema.Int;
|
|
34
50
|
readonly omitted: Schema.Int;
|
|
@@ -55,28 +71,45 @@ export declare const PlanProjection: Schema.Struct<{
|
|
|
55
71
|
}>;
|
|
56
72
|
/** Arguments of the human `/plan` command: a bare call views, `clear` empties it. */
|
|
57
73
|
export declare const PlanCommandInput: Schema.Union<readonly [Schema.Struct<{
|
|
74
|
+
/** Clear the checklist instead of viewing it. */
|
|
58
75
|
readonly action: Schema.Literal<"clear">;
|
|
59
76
|
}>, Schema.Struct<{}>]>;
|
|
77
|
+
/** Session-owned human plan control and checklist projection. */
|
|
60
78
|
export declare const planContract: {
|
|
79
|
+
/** Stable extension identity. */
|
|
61
80
|
id: string & import("effect/Brand").Brand<"ExtensionId">;
|
|
81
|
+
/** Human checklist controls. */
|
|
62
82
|
commands: {
|
|
83
|
+
/** View or clear the current checklist. */
|
|
63
84
|
plan: {
|
|
85
|
+
/** The Session owns the checklist. */
|
|
64
86
|
scope: "session";
|
|
87
|
+
/** Decode the selected human control. */
|
|
65
88
|
input: Schema.Union<readonly [Schema.Struct<{
|
|
89
|
+
/** Clear the checklist instead of viewing it. */
|
|
66
90
|
readonly action: Schema.Literal<"clear">;
|
|
67
91
|
}>, Schema.Struct<{}>]>;
|
|
92
|
+
/** Return the server-owned conversation step. */
|
|
68
93
|
output: Schema.declare<import("@fungi.computer/whistle/catalog").WhistleCatalogStepV1, import("@fungi.computer/whistle/catalog").WhistleCatalogStepV1>;
|
|
69
94
|
};
|
|
70
95
|
};
|
|
96
|
+
/** Current checklist and its replacement history. */
|
|
71
97
|
projection: {
|
|
98
|
+
/** Decoder for visible plan state. */
|
|
72
99
|
schema: Schema.Struct<{
|
|
100
|
+
/** Latest checklist replacement. */
|
|
73
101
|
readonly plan: Schema.Struct<{
|
|
102
|
+
/** Optional explanation for this checklist replacement. */
|
|
74
103
|
readonly explanation: Schema.optionalKey<Schema.String>;
|
|
104
|
+
/** Ordered checklist; an empty array clears the plan. */
|
|
75
105
|
readonly steps: Schema.$Array<Schema.Struct<{
|
|
106
|
+
/** Work described by this checklist step. */
|
|
76
107
|
readonly step: Schema.String;
|
|
108
|
+
/** Current progress state of the step. */
|
|
77
109
|
readonly status: Schema.Literals<readonly ["pending", "in_progress", "completed"]>;
|
|
78
110
|
}>>;
|
|
79
111
|
}>;
|
|
112
|
+
/** Committed replacements, including explicit clears. */
|
|
80
113
|
readonly history: Schema.Struct<{
|
|
81
114
|
readonly nextSequence: Schema.Int;
|
|
82
115
|
readonly omitted: Schema.Int;
|
package/dist/plan/contract.js
CHANGED
|
@@ -7,12 +7,17 @@ import { CatalogStep } from "../internal/catalog.js";
|
|
|
7
7
|
* steps means no plan: replacing it with an empty checklist clears it.
|
|
8
8
|
*/
|
|
9
9
|
export const Plan = Schema.Struct({
|
|
10
|
+
/** Optional explanation for this checklist replacement. */
|
|
10
11
|
explanation: Schema.optionalKey(Schema.String.pipe(Schema.check(Schema.isMaxLength(2_000)))),
|
|
12
|
+
/** Ordered checklist; an empty array clears the plan. */
|
|
11
13
|
steps: Schema.Array(Schema.Struct({
|
|
14
|
+
/** Work described by this checklist step. */
|
|
12
15
|
step: Schema.String.pipe(Schema.check(Schema.isMinLength(1)), Schema.check(Schema.isMaxLength(500))),
|
|
16
|
+
/** Current progress state of the step. */
|
|
13
17
|
status: Schema.Literals(["pending", "in_progress", "completed"]),
|
|
14
18
|
})).pipe(Schema.check(Schema.isMaxLength(50))),
|
|
15
19
|
});
|
|
20
|
+
/** Checklist replacement that permits at most one in-progress step. */
|
|
16
21
|
export const PlanInput = Plan.pipe(Schema.check(Schema.makeFilter((plan) => plan.steps.filter((step) => step.status === "in_progress").length <=
|
|
17
22
|
1 || "At most one plan step may be in progress")));
|
|
18
23
|
/** Open work remains: some step is not completed. Cleared and finished plans are closed. */
|
|
@@ -21,18 +26,38 @@ export function planIsOpen(plan) {
|
|
|
21
26
|
}
|
|
22
27
|
/** The current checklist and every committed replacement, clears included. */
|
|
23
28
|
export const PlanProjection = Schema.Struct({
|
|
29
|
+
/** Latest checklist replacement. */
|
|
24
30
|
plan: Plan,
|
|
31
|
+
/** Committed replacements, including explicit clears. */
|
|
25
32
|
history: historySchema(Plan),
|
|
26
33
|
});
|
|
27
34
|
/** Arguments of the human `/plan` command: a bare call views, `clear` empties it. */
|
|
28
35
|
export const PlanCommandInput = Schema.Union([
|
|
29
|
-
Schema.Struct({
|
|
36
|
+
Schema.Struct({
|
|
37
|
+
/** Clear the checklist instead of viewing it. */
|
|
38
|
+
action: Schema.Literal("clear"),
|
|
39
|
+
}),
|
|
30
40
|
Schema.Struct({}),
|
|
31
41
|
]);
|
|
42
|
+
/** Session-owned human plan control and checklist projection. */
|
|
32
43
|
export const planContract = {
|
|
44
|
+
/** Stable extension identity. */
|
|
33
45
|
id: ExtensionId.make("botanical-session-plan"),
|
|
46
|
+
/** Human checklist controls. */
|
|
34
47
|
commands: {
|
|
35
|
-
|
|
48
|
+
/** View or clear the current checklist. */
|
|
49
|
+
plan: {
|
|
50
|
+
/** The Session owns the checklist. */
|
|
51
|
+
scope: "session",
|
|
52
|
+
/** Decode the selected human control. */
|
|
53
|
+
input: PlanCommandInput,
|
|
54
|
+
/** Return the server-owned conversation step. */
|
|
55
|
+
output: CatalogStep,
|
|
56
|
+
},
|
|
57
|
+
},
|
|
58
|
+
/** Current checklist and its replacement history. */
|
|
59
|
+
projection: {
|
|
60
|
+
/** Decoder for visible plan state. */
|
|
61
|
+
schema: PlanProjection,
|
|
36
62
|
},
|
|
37
|
-
projection: { schema: PlanProjection },
|
|
38
63
|
};
|
package/dist/plan/host.js
CHANGED
|
@@ -8,9 +8,14 @@ import * as Schema from "effect/Schema";
|
|
|
8
8
|
import { z } from "zod";
|
|
9
9
|
import { catalog, done } from "../internal/catalog.js";
|
|
10
10
|
import { Plan, PlanInput, planContract, planIsOpen } from "./contract.js";
|
|
11
|
+
/** The rules the model follows; the describe page and an open plan's prompt share them. */
|
|
12
|
+
const rules = "Use it for multi-step work, with at most one in-progress step; replace the whole plan on each update, and pass steps:[] to clear a plan that no longer applies. A completed checklist is not proof of goal completion.";
|
|
11
13
|
const config = Object.freeze({
|
|
12
14
|
promptLimit: 2_000,
|
|
13
|
-
|
|
15
|
+
idle: "Multi-step work? forage.describe({path:'plan'}) first.",
|
|
16
|
+
moduleDescription: "Session checklist tool: update_plan({explanation?,steps:[{step,status:'pending'|'in_progress'|'completed'}]}). " +
|
|
17
|
+
rules,
|
|
18
|
+
guide: "Session checklist tool: plan.update_plan. " + rules,
|
|
14
19
|
});
|
|
15
20
|
const collections = {
|
|
16
21
|
history: defineCollection(Schema.Struct({ sessionId: Schema.String, history: historySchema(Plan) }), { key: (row) => row.sessionId }),
|
|
@@ -93,17 +98,17 @@ export const planExtension = defineExtension({
|
|
|
93
98
|
prompt: (ctx, { sessionId }) => ctx.state.plans.get(sessionId).pipe(Effect.map((found) => [
|
|
94
99
|
{
|
|
95
100
|
name: "plan",
|
|
96
|
-
text: (
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
+
text: Option.isSome(found) && found.value.plan.steps.length > 0
|
|
102
|
+
? (ctx.config.guide +
|
|
103
|
+
"\n" +
|
|
104
|
+
JSON.stringify(found.value.plan)).slice(0, ctx.config.promptLimit)
|
|
105
|
+
: ctx.config.idle,
|
|
101
106
|
},
|
|
102
107
|
])),
|
|
103
108
|
tools: (ctx, { sessionId, workId }) => Effect.succeed(Option.some(Mycelium.module({
|
|
104
109
|
id: "botanical.session-plan",
|
|
105
110
|
name: "plan",
|
|
106
|
-
description:
|
|
111
|
+
description: ctx.config.moduleDescription,
|
|
107
112
|
operations: {
|
|
108
113
|
update_plan: Mycelium.operation({
|
|
109
114
|
replay: "safe",
|
|
@@ -3,7 +3,7 @@ import { type ClientContributionV1 } from "@fungi.computer/shiitake/client-entry
|
|
|
3
3
|
declare const _default: Readonly<{
|
|
4
4
|
protocol: "shiitake.client/v1";
|
|
5
5
|
id: import("@fungi.computer/shiitake/extensions").ExtensionId;
|
|
6
|
-
|
|
6
|
+
promptPresentation?: "footer";
|
|
7
7
|
attach?: (context: import("@fungi.computer/shiitake/client-entry").ClientExtensionContextV1) => import("effect/Effect").Effect<void, Error, import("effect/Scope").Scope>;
|
|
8
8
|
contribute?: (slot: import("@fungi.computer/shiitake").ExtensionSlotV0 | undefined) => ClientContributionV1;
|
|
9
9
|
}>;
|