pi-t3-bridge 0.6.0 → 0.7.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/package.json +3 -4
- package/src/amplike/btw.ts +5 -7
- package/src/amplike/handoff.ts +33 -23
- package/src/amplike/package.ts +37 -24
- package/src/amplike/selection.ts +26 -14
- package/src/bridge/accounts.ts +66 -49
- package/src/bridge/ask.ts +73 -93
- package/src/bridge/bash-timeout.ts +15 -8
- package/src/bridge/config.ts +29 -26
- package/src/bridge/plan.ts +19 -11
- package/src/bridge/resume.ts +17 -11
- package/src/bridge/state.ts +54 -34
- package/src/bridge/steer-judge.ts +130 -96
- package/src/bridge/steer-transcript.ts +4 -3
- package/src/bridge/steer.ts +16 -10
- package/src/bridge/t3-json.ts +14 -19
- package/src/bridge/tasks.ts +39 -31
- package/src/bridge/thread.ts +5 -6
- package/src/bridge/transcript.ts +11 -8
- package/src/pstack/brief.ts +4 -3
- package/src/pstack/child.ts +45 -32
- package/src/pstack/models.ts +138 -106
- package/src/pstack/overlay.ts +18 -20
- package/src/pstack/results.ts +4 -7
- package/src/pstack/sandbox.ts +12 -8
- package/pstack/skills/statechart/scripts/node_modules/.package-lock.json +0 -46
- package/pstack/skills/statechart/scripts/node_modules/fast-check/LICENSE +0 -21
- package/pstack/skills/statechart/scripts/node_modules/fast-check/README.md +0 -263
- package/pstack/skills/statechart/scripts/node_modules/fast-check/lib/cjs/fast-check.d.ts +0 -5165
- package/pstack/skills/statechart/scripts/node_modules/fast-check/lib/cjs/fast-check.js +0 -12147
- package/pstack/skills/statechart/scripts/node_modules/fast-check/lib/cjs/package.json +0 -3
- package/pstack/skills/statechart/scripts/node_modules/fast-check/lib/cjs/types57/fast-check.d.ts +0 -5165
- package/pstack/skills/statechart/scripts/node_modules/fast-check/lib/fast-check.d.ts +0 -5165
- package/pstack/skills/statechart/scripts/node_modules/fast-check/lib/fast-check.js +0 -12011
- package/pstack/skills/statechart/scripts/node_modules/fast-check/lib/rolldown-runtime-D7D4PA-g.js +0 -13
- package/pstack/skills/statechart/scripts/node_modules/fast-check/lib/types57/fast-check.d.ts +0 -5165
- package/pstack/skills/statechart/scripts/node_modules/fast-check/package.json +0 -94
- package/pstack/skills/statechart/scripts/node_modules/pure-rand/LICENSE +0 -21
- package/pstack/skills/statechart/scripts/node_modules/pure-rand/README.md +0 -168
- package/pstack/skills/statechart/scripts/node_modules/pure-rand/lib/RandomGenerator-DcXj09Ch.d.ts +0 -14
- package/pstack/skills/statechart/scripts/node_modules/pure-rand/lib/distribution/uniformBigInt.d.ts +0 -15
- package/pstack/skills/statechart/scripts/node_modules/pure-rand/lib/distribution/uniformBigInt.js +0 -38
- package/pstack/skills/statechart/scripts/node_modules/pure-rand/lib/distribution/uniformFloat32.d.ts +0 -15
- package/pstack/skills/statechart/scripts/node_modules/pure-rand/lib/distribution/uniformFloat32.js +0 -18
- package/pstack/skills/statechart/scripts/node_modules/pure-rand/lib/distribution/uniformFloat64.d.ts +0 -15
- package/pstack/skills/statechart/scripts/node_modules/pure-rand/lib/distribution/uniformFloat64.js +0 -22
- package/pstack/skills/statechart/scripts/node_modules/pure-rand/lib/distribution/uniformInt.d.ts +0 -15
- package/pstack/skills/statechart/scripts/node_modules/pure-rand/lib/distribution/uniformInt.js +0 -134
- package/pstack/skills/statechart/scripts/node_modules/pure-rand/lib/esm/RandomGenerator-DcXj09Ch.d.ts +0 -14
- package/pstack/skills/statechart/scripts/node_modules/pure-rand/lib/esm/distribution/uniformBigInt.d.ts +0 -15
- package/pstack/skills/statechart/scripts/node_modules/pure-rand/lib/esm/distribution/uniformBigInt.js +0 -37
- package/pstack/skills/statechart/scripts/node_modules/pure-rand/lib/esm/distribution/uniformFloat32.d.ts +0 -15
- package/pstack/skills/statechart/scripts/node_modules/pure-rand/lib/esm/distribution/uniformFloat32.js +0 -17
- package/pstack/skills/statechart/scripts/node_modules/pure-rand/lib/esm/distribution/uniformFloat64.d.ts +0 -15
- package/pstack/skills/statechart/scripts/node_modules/pure-rand/lib/esm/distribution/uniformFloat64.js +0 -21
- package/pstack/skills/statechart/scripts/node_modules/pure-rand/lib/esm/distribution/uniformInt.d.ts +0 -15
- package/pstack/skills/statechart/scripts/node_modules/pure-rand/lib/esm/distribution/uniformInt.js +0 -133
- package/pstack/skills/statechart/scripts/node_modules/pure-rand/lib/esm/generator/congruential32.d.ts +0 -7
- package/pstack/skills/statechart/scripts/node_modules/pure-rand/lib/esm/generator/congruential32.js +0 -44
- package/pstack/skills/statechart/scripts/node_modules/pure-rand/lib/esm/generator/mersenne.d.ts +0 -7
- package/pstack/skills/statechart/scripts/node_modules/pure-rand/lib/esm/generator/mersenne.js +0 -90
- package/pstack/skills/statechart/scripts/node_modules/pure-rand/lib/esm/generator/xoroshiro128plus.d.ts +0 -7
- package/pstack/skills/statechart/scripts/node_modules/pure-rand/lib/esm/generator/xoroshiro128plus.js +0 -80
- package/pstack/skills/statechart/scripts/node_modules/pure-rand/lib/esm/generator/xorshift128plus.d.ts +0 -7
- package/pstack/skills/statechart/scripts/node_modules/pure-rand/lib/esm/generator/xorshift128plus.js +0 -78
- package/pstack/skills/statechart/scripts/node_modules/pure-rand/lib/esm/package.json +0 -3
- package/pstack/skills/statechart/scripts/node_modules/pure-rand/lib/esm/types/JumpableRandomGenerator.d.ts +0 -16
- package/pstack/skills/statechart/scripts/node_modules/pure-rand/lib/esm/types/JumpableRandomGenerator.js +0 -0
- package/pstack/skills/statechart/scripts/node_modules/pure-rand/lib/esm/types/RandomGenerator.d.ts +0 -2
- package/pstack/skills/statechart/scripts/node_modules/pure-rand/lib/esm/types/RandomGenerator.js +0 -0
- package/pstack/skills/statechart/scripts/node_modules/pure-rand/lib/esm/utils/generateN.d.ts +0 -6
- package/pstack/skills/statechart/scripts/node_modules/pure-rand/lib/esm/utils/generateN.js +0 -8
- package/pstack/skills/statechart/scripts/node_modules/pure-rand/lib/esm/utils/purify.d.ts +0 -12
- package/pstack/skills/statechart/scripts/node_modules/pure-rand/lib/esm/utils/purify.js +0 -9
- package/pstack/skills/statechart/scripts/node_modules/pure-rand/lib/esm/utils/skipN.d.ts +0 -6
- package/pstack/skills/statechart/scripts/node_modules/pure-rand/lib/esm/utils/skipN.js +0 -6
- package/pstack/skills/statechart/scripts/node_modules/pure-rand/lib/generator/congruential32.d.ts +0 -7
- package/pstack/skills/statechart/scripts/node_modules/pure-rand/lib/generator/congruential32.js +0 -46
- package/pstack/skills/statechart/scripts/node_modules/pure-rand/lib/generator/mersenne.d.ts +0 -7
- package/pstack/skills/statechart/scripts/node_modules/pure-rand/lib/generator/mersenne.js +0 -92
- package/pstack/skills/statechart/scripts/node_modules/pure-rand/lib/generator/xoroshiro128plus.d.ts +0 -7
- package/pstack/skills/statechart/scripts/node_modules/pure-rand/lib/generator/xoroshiro128plus.js +0 -82
- package/pstack/skills/statechart/scripts/node_modules/pure-rand/lib/generator/xorshift128plus.d.ts +0 -7
- package/pstack/skills/statechart/scripts/node_modules/pure-rand/lib/generator/xorshift128plus.js +0 -80
- package/pstack/skills/statechart/scripts/node_modules/pure-rand/lib/types/JumpableRandomGenerator.d.ts +0 -16
- package/pstack/skills/statechart/scripts/node_modules/pure-rand/lib/types/JumpableRandomGenerator.js +0 -0
- package/pstack/skills/statechart/scripts/node_modules/pure-rand/lib/types/RandomGenerator.d.ts +0 -2
- package/pstack/skills/statechart/scripts/node_modules/pure-rand/lib/types/RandomGenerator.js +0 -0
- package/pstack/skills/statechart/scripts/node_modules/pure-rand/lib/utils/generateN.d.ts +0 -6
- package/pstack/skills/statechart/scripts/node_modules/pure-rand/lib/utils/generateN.js +0 -9
- package/pstack/skills/statechart/scripts/node_modules/pure-rand/lib/utils/purify.d.ts +0 -12
- package/pstack/skills/statechart/scripts/node_modules/pure-rand/lib/utils/purify.js +0 -10
- package/pstack/skills/statechart/scripts/node_modules/pure-rand/lib/utils/skipN.d.ts +0 -6
- package/pstack/skills/statechart/scripts/node_modules/pure-rand/lib/utils/skipN.js +0 -7
- package/pstack/skills/statechart/scripts/node_modules/pure-rand/package.json +0 -133
package/src/bridge/ask.ts
CHANGED
|
@@ -4,103 +4,79 @@ import type {
|
|
|
4
4
|
ExtensionUIContext,
|
|
5
5
|
ExtensionUIDialogOptions,
|
|
6
6
|
} from "@earendil-works/pi-coding-agent";
|
|
7
|
-
import {
|
|
8
|
-
|
|
9
|
-
|
|
7
|
+
import {
|
|
8
|
+
arr,
|
|
9
|
+
compile,
|
|
10
|
+
int,
|
|
11
|
+
lit,
|
|
12
|
+
obj,
|
|
13
|
+
oneOf,
|
|
14
|
+
opt,
|
|
15
|
+
str,
|
|
16
|
+
union,
|
|
17
|
+
} from "@earendil-works/pi-schema";
|
|
18
|
+
import type { Static } from "@earendil-works/pi-schema";
|
|
10
19
|
|
|
11
20
|
import { registerUnlessTaken } from "./claim.ts";
|
|
12
21
|
import { insideT3 } from "./t3-http.ts";
|
|
13
22
|
|
|
14
23
|
const OWN_ANSWER = "Type my own answer";
|
|
15
24
|
|
|
16
|
-
const ChoiceSchema =
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
},
|
|
26
|
-
{ additionalProperties: false }
|
|
27
|
-
);
|
|
28
|
-
|
|
29
|
-
const QuestionSchema = Type.Object(
|
|
30
|
-
{
|
|
31
|
-
body: Type.Optional(
|
|
32
|
-
Type.String({
|
|
33
|
-
description: "More context, shown under the title.",
|
|
34
|
-
minLength: 1,
|
|
35
|
-
})
|
|
36
|
-
),
|
|
37
|
-
choices: Type.Optional(
|
|
38
|
-
Type.Array(ChoiceSchema, {
|
|
39
|
-
description: "Choices to pick from. Leave out for free text.",
|
|
40
|
-
minItems: 1,
|
|
41
|
-
})
|
|
42
|
-
),
|
|
43
|
-
title: Type.String({ description: "The question.", minLength: 1 }),
|
|
44
|
-
},
|
|
45
|
-
{ additionalProperties: false }
|
|
46
|
-
);
|
|
47
|
-
|
|
48
|
-
const AskInputSchema = Type.Object(
|
|
49
|
-
{
|
|
50
|
-
questions: Type.Array(QuestionSchema, {
|
|
51
|
-
description: "Questions to ask in order, one card each.",
|
|
52
|
-
minItems: 1,
|
|
53
|
-
}),
|
|
54
|
-
timeoutSeconds: Type.Optional(
|
|
55
|
-
Type.Integer({
|
|
56
|
-
description: "Seconds to wait for all answers before giving up.",
|
|
57
|
-
minimum: 10,
|
|
58
|
-
})
|
|
59
|
-
),
|
|
60
|
-
},
|
|
61
|
-
{ additionalProperties: false }
|
|
62
|
-
);
|
|
25
|
+
const ChoiceSchema = obj({
|
|
26
|
+
description: opt(
|
|
27
|
+
str({
|
|
28
|
+
description: "Why to pick it, shown under the question.",
|
|
29
|
+
minLength: 1,
|
|
30
|
+
})
|
|
31
|
+
),
|
|
32
|
+
label: str({ description: "The choice.", minLength: 1 }),
|
|
33
|
+
});
|
|
63
34
|
|
|
64
|
-
const
|
|
65
|
-
|
|
66
|
-
{
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
question: Type.Integer(),
|
|
71
|
-
},
|
|
72
|
-
{ additionalProperties: false }
|
|
35
|
+
const QuestionSchema = obj({
|
|
36
|
+
body: opt(
|
|
37
|
+
str({
|
|
38
|
+
description: "More context, shown under the title.",
|
|
39
|
+
minLength: 1,
|
|
40
|
+
})
|
|
73
41
|
),
|
|
74
|
-
|
|
75
|
-
{
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
},
|
|
80
|
-
{ additionalProperties: false }
|
|
42
|
+
choices: opt(
|
|
43
|
+
arr(ChoiceSchema, {
|
|
44
|
+
description: "Choices to pick from. Leave out for free text.",
|
|
45
|
+
minItems: 1,
|
|
46
|
+
})
|
|
81
47
|
),
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
48
|
+
title: str({ description: "The question.", minLength: 1 }),
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
const AskInputSchema = obj({
|
|
52
|
+
questions: arr(QuestionSchema, {
|
|
53
|
+
description: "Questions to ask in order, one card each.",
|
|
54
|
+
minItems: 1,
|
|
55
|
+
}),
|
|
56
|
+
timeoutSeconds: opt(
|
|
57
|
+
int({
|
|
58
|
+
description: "Seconds to wait for all answers before giving up.",
|
|
59
|
+
minimum: 10,
|
|
60
|
+
})
|
|
85
61
|
),
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
const AnswerSchema = union([
|
|
65
|
+
obj({ index: int(), kind: lit("chose"), label: str(), question: int() }),
|
|
66
|
+
obj({ kind: lit("typed"), question: int(), text: str() }),
|
|
67
|
+
obj({ kind: lit("rephrase"), question: int() }),
|
|
86
68
|
]);
|
|
87
69
|
|
|
88
|
-
const StopSchema =
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
},
|
|
94
|
-
{ additionalProperties: false }
|
|
95
|
-
);
|
|
70
|
+
const StopSchema = obj({
|
|
71
|
+
kind: oneOf(["dismissed", "timed_out"]),
|
|
72
|
+
question: int(),
|
|
73
|
+
unasked: int(),
|
|
74
|
+
});
|
|
96
75
|
|
|
97
|
-
const AskDetailsSchema =
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
},
|
|
102
|
-
{ additionalProperties: false }
|
|
103
|
-
);
|
|
76
|
+
const AskDetailsSchema = obj({
|
|
77
|
+
answers: arr(AnswerSchema),
|
|
78
|
+
stop: opt(StopSchema),
|
|
79
|
+
});
|
|
104
80
|
|
|
105
81
|
type Question = Static<typeof QuestionSchema>;
|
|
106
82
|
|
|
@@ -115,15 +91,19 @@ type AskDetails = Static<typeof AskDetailsSchema>;
|
|
|
115
91
|
type Surface = "t3" | "terminal";
|
|
116
92
|
|
|
117
93
|
// The call as the model sent it, before AskInputSchema rejects extra fields.
|
|
118
|
-
const RawChoiceSchema =
|
|
94
|
+
const RawChoiceSchema = obj({}, { open: true });
|
|
119
95
|
|
|
120
|
-
const RawQuestionSchema =
|
|
121
|
-
choices:
|
|
122
|
-
}
|
|
96
|
+
const RawQuestionSchema = obj(
|
|
97
|
+
{ choices: opt(arr(RawChoiceSchema)) },
|
|
98
|
+
{ open: true }
|
|
99
|
+
);
|
|
123
100
|
|
|
124
|
-
const RawAskSchema =
|
|
125
|
-
questions:
|
|
126
|
-
}
|
|
101
|
+
const RawAskSchema = obj(
|
|
102
|
+
{ questions: opt(arr(RawQuestionSchema)) },
|
|
103
|
+
{ open: true }
|
|
104
|
+
);
|
|
105
|
+
|
|
106
|
+
const isRawAsk = compile(RawAskSchema).check;
|
|
127
107
|
|
|
128
108
|
type RawCall =
|
|
129
109
|
| Static<typeof RawAskSchema>
|
|
@@ -294,7 +274,7 @@ const askTool = defineTool({
|
|
|
294
274
|
annotations: { readOnlyHint: true },
|
|
295
275
|
description:
|
|
296
276
|
'Ask the user and wait for the answer. Each question becomes one card, asked in order. Put your recommendation first and mark it in the label, e.g. "SQLite (recommended)". Put the reason in `description`, which is shown under the question. The user can always type their own answer instead of picking. To let them pick several, say so in `body`. They will type something like "1 and 3" or "all". Leave out `choices` for free text. If a typed answer is a question or pushback, reply to it before you act. If the user dismisses a card, stop and wait for their next message.',
|
|
297
|
-
execute: async (_toolCallId, params, signal, _onUpdate, ctx) => {
|
|
277
|
+
execute: async (_toolCallId, params: AskInput, signal, _onUpdate, ctx) => {
|
|
298
278
|
if (!ctx.hasUI) {
|
|
299
279
|
throw new Error(
|
|
300
280
|
"ask needs an interactive client, such as T3 Code or the Pi terminal."
|
|
@@ -373,7 +353,7 @@ const askTool = defineTool({
|
|
|
373
353
|
outputSchema: AskDetailsSchema,
|
|
374
354
|
parameters: AskInputSchema,
|
|
375
355
|
prepareArguments: (args) => {
|
|
376
|
-
if (
|
|
356
|
+
if (isRawAsk(args)) {
|
|
377
357
|
checkFields(args);
|
|
378
358
|
}
|
|
379
359
|
|
|
@@ -1,6 +1,12 @@
|
|
|
1
1
|
import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
|
|
2
|
-
import {
|
|
3
|
-
|
|
2
|
+
import {
|
|
3
|
+
compile,
|
|
4
|
+
num,
|
|
5
|
+
obj,
|
|
6
|
+
opt,
|
|
7
|
+
str,
|
|
8
|
+
unknown,
|
|
9
|
+
} from "@earendil-works/pi-schema";
|
|
4
10
|
|
|
5
11
|
import { readConfig } from "./config.ts";
|
|
6
12
|
|
|
@@ -13,10 +19,11 @@ const DEFAULT_SECONDS = 300;
|
|
|
13
19
|
export const bashTimeoutSeconds = (): number =>
|
|
14
20
|
readConfig().bashTimeoutSeconds ?? DEFAULT_SECONDS;
|
|
15
21
|
|
|
16
|
-
const
|
|
17
|
-
command:
|
|
18
|
-
|
|
19
|
-
|
|
22
|
+
const isBashInput = compile(
|
|
23
|
+
obj({ command: str(), timeout: opt(unknown()) }, { open: true })
|
|
24
|
+
).check;
|
|
25
|
+
|
|
26
|
+
const isNumber = compile(num()).check;
|
|
20
27
|
|
|
21
28
|
const TIMED_OUT = /Command timed out after \d+ seconds/u;
|
|
22
29
|
|
|
@@ -24,14 +31,14 @@ export const registerBashTimeout = (pi: ExtensionAPI): void => {
|
|
|
24
31
|
pi.on("tool_call", (event) => {
|
|
25
32
|
const { input } = event;
|
|
26
33
|
|
|
27
|
-
if (event.toolName !== "bash" || !
|
|
34
|
+
if (event.toolName !== "bash" || !isBashInput(input)) {
|
|
28
35
|
return;
|
|
29
36
|
}
|
|
30
37
|
|
|
31
38
|
const cap = bashTimeoutSeconds();
|
|
32
39
|
|
|
33
40
|
input.timeout =
|
|
34
|
-
|
|
41
|
+
isNumber(input.timeout) && input.timeout > 0
|
|
35
42
|
? Math.min(input.timeout, cap)
|
|
36
43
|
: cap;
|
|
37
44
|
});
|
package/src/bridge/config.ts
CHANGED
|
@@ -3,24 +3,33 @@ import { homedir } from "node:os";
|
|
|
3
3
|
import path from "node:path";
|
|
4
4
|
|
|
5
5
|
import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
|
|
6
|
-
import {
|
|
7
|
-
|
|
8
|
-
|
|
6
|
+
import {
|
|
7
|
+
compile,
|
|
8
|
+
map,
|
|
9
|
+
num,
|
|
10
|
+
oneOf,
|
|
11
|
+
str,
|
|
12
|
+
unknown,
|
|
13
|
+
} from "@earendil-works/pi-schema";
|
|
14
|
+
import type { Static, Validator } from "@earendil-works/pi-schema";
|
|
9
15
|
|
|
10
16
|
// The bridge's settings, one JSON file per machine. Each key is checked on its
|
|
11
17
|
// own, so one bad value falls back to its default and leaves the rest alone.
|
|
12
18
|
// The file is read on every call, so an edit applies on the next prompt.
|
|
13
19
|
|
|
14
|
-
const SteerSchema =
|
|
15
|
-
Type.Literal("on"),
|
|
16
|
-
Type.Literal("off"),
|
|
17
|
-
Type.Literal("shadow"),
|
|
18
|
-
]);
|
|
20
|
+
const SteerSchema = oneOf(["on", "off", "shadow"]);
|
|
19
21
|
|
|
20
|
-
const ChildSandboxSchema =
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
22
|
+
const ChildSandboxSchema = oneOf(["on", "off"]);
|
|
23
|
+
|
|
24
|
+
const configFile = compile(map(unknown()));
|
|
25
|
+
|
|
26
|
+
const fields = {
|
|
27
|
+
bashTimeoutSeconds: compile(num({ exclusiveMinimum: 0 })),
|
|
28
|
+
childSandbox: compile(ChildSandboxSchema),
|
|
29
|
+
classifierModel: compile(str({ pattern: "^[^/\\s]+/.+$" })),
|
|
30
|
+
mcpUrl: compile(str({ minLength: 1 })),
|
|
31
|
+
steer: compile(SteerSchema),
|
|
32
|
+
};
|
|
24
33
|
|
|
25
34
|
export interface Config {
|
|
26
35
|
readonly bashTimeoutSeconds: number | undefined;
|
|
@@ -60,19 +69,16 @@ const parse = (text: string): Loaded => {
|
|
|
60
69
|
return { config: EMPTY, problems: [`${file} is not valid JSON.`] };
|
|
61
70
|
}
|
|
62
71
|
|
|
63
|
-
if (!
|
|
72
|
+
if (!configFile.check(parsed)) {
|
|
64
73
|
return { config: EMPTY, problems: [`${file} is not a JSON object.`] };
|
|
65
74
|
}
|
|
66
75
|
|
|
67
76
|
const problems: string[] = [];
|
|
68
77
|
|
|
69
|
-
const field = <
|
|
70
|
-
key: string,
|
|
71
|
-
schema: S
|
|
72
|
-
): Static<S> | undefined => {
|
|
78
|
+
const field = <T>(key: string, validator: Validator<T>): T | undefined => {
|
|
73
79
|
const value = parsed[key];
|
|
74
80
|
|
|
75
|
-
if (value === undefined ||
|
|
81
|
+
if (value === undefined || validator.check(value)) {
|
|
76
82
|
return value;
|
|
77
83
|
}
|
|
78
84
|
|
|
@@ -87,15 +93,12 @@ const parse = (text: string): Loaded => {
|
|
|
87
93
|
config: {
|
|
88
94
|
bashTimeoutSeconds: field(
|
|
89
95
|
"bashTimeoutSeconds",
|
|
90
|
-
|
|
91
|
-
),
|
|
92
|
-
childSandbox: field("childSandbox", ChildSandboxSchema),
|
|
93
|
-
classifierModel: field(
|
|
94
|
-
"classifierModel",
|
|
95
|
-
Type.String({ pattern: "^[^/\\s]+/.+$" })
|
|
96
|
+
fields.bashTimeoutSeconds
|
|
96
97
|
),
|
|
97
|
-
|
|
98
|
-
|
|
98
|
+
childSandbox: field("childSandbox", fields.childSandbox),
|
|
99
|
+
classifierModel: field("classifierModel", fields.classifierModel),
|
|
100
|
+
mcpUrl: field("mcpUrl", fields.mcpUrl),
|
|
101
|
+
steer: field("steer", fields.steer),
|
|
99
102
|
},
|
|
100
103
|
problems,
|
|
101
104
|
};
|
package/src/bridge/plan.ts
CHANGED
|
@@ -3,7 +3,8 @@ import type {
|
|
|
3
3
|
ExtensionAPI,
|
|
4
4
|
ExtensionContext,
|
|
5
5
|
} from "@earendil-works/pi-coding-agent";
|
|
6
|
-
import {
|
|
6
|
+
import { obj, opt, str } from "@earendil-works/pi-schema";
|
|
7
|
+
import type { Static } from "@earendil-works/pi-schema";
|
|
7
8
|
|
|
8
9
|
import {
|
|
9
10
|
escapeHtml,
|
|
@@ -117,15 +118,16 @@ const planCard = (title: string, plan: string): PlanCard => {
|
|
|
117
118
|
};
|
|
118
119
|
};
|
|
119
120
|
|
|
120
|
-
const PlanSchema =
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
}
|
|
121
|
+
const PlanSchema = obj(
|
|
122
|
+
{
|
|
123
|
+
plan: str({
|
|
124
|
+
description: "The plan in Markdown, as numbered steps.",
|
|
125
|
+
minLength: 1,
|
|
126
|
+
}),
|
|
127
|
+
title: opt(str({ description: "Short plan title.", minLength: 1 })),
|
|
128
|
+
},
|
|
129
|
+
{ open: true }
|
|
130
|
+
);
|
|
129
131
|
|
|
130
132
|
interface PlanToolDetails {
|
|
131
133
|
readonly choice: PlanChoice | "dismissed";
|
|
@@ -177,7 +179,13 @@ export const registerPlanGate = (pi: ExtensionAPI): void => {
|
|
|
177
179
|
annotations: { readOnlyHint: true },
|
|
178
180
|
description:
|
|
179
181
|
"Ask the user to approve a plan before any file changes. Until the user approves, only read-only tools run. On Revise, change the plan and call this tool again. On Cancel, stop: only read-only tools run until the user's next message.",
|
|
180
|
-
execute: async (
|
|
182
|
+
execute: async (
|
|
183
|
+
toolCallId,
|
|
184
|
+
params: Static<typeof PlanSchema>,
|
|
185
|
+
_signal,
|
|
186
|
+
_onUpdate,
|
|
187
|
+
ctx
|
|
188
|
+
) => {
|
|
181
189
|
if (!ctx.hasUI) {
|
|
182
190
|
throw new Error(
|
|
183
191
|
"Plan approval needs an interactive client, such as T3 Code or the Pi terminal."
|
package/src/bridge/resume.ts
CHANGED
|
@@ -2,9 +2,8 @@ import type {
|
|
|
2
2
|
ExtensionAPI,
|
|
3
3
|
ExtensionContext,
|
|
4
4
|
} from "@earendil-works/pi-coding-agent";
|
|
5
|
-
import {
|
|
6
|
-
import type { Static } from "
|
|
7
|
-
import { Value } from "typebox/value";
|
|
5
|
+
import { compile, obj, oneOf, opt, str } from "@earendil-works/pi-schema";
|
|
6
|
+
import type { Static } from "@earendil-works/pi-schema";
|
|
8
7
|
|
|
9
8
|
import type { Classified } from "./failures.ts";
|
|
10
9
|
import { callT3Tool, insideT3 } from "./t3-http.ts";
|
|
@@ -24,13 +23,20 @@ const AFTER_RESET_MS = 2 * 60_000;
|
|
|
24
23
|
|
|
25
24
|
const MARKER = /\[pi-t3-resume (?<marker>[\w-]+)\]/u;
|
|
26
25
|
|
|
27
|
-
const ResumeSchema =
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
26
|
+
const ResumeSchema = obj(
|
|
27
|
+
{
|
|
28
|
+
marker: str(),
|
|
29
|
+
scheduledTaskId: opt(str()),
|
|
30
|
+
state: oneOf(["scheduled", "done"]),
|
|
31
|
+
},
|
|
32
|
+
{ open: true }
|
|
33
|
+
);
|
|
32
34
|
|
|
33
|
-
const
|
|
35
|
+
const isResume = compile(ResumeSchema).check;
|
|
36
|
+
|
|
37
|
+
const isScheduled = compile(
|
|
38
|
+
obj({ scheduledTaskId: str() }, { open: true })
|
|
39
|
+
).check;
|
|
34
40
|
|
|
35
41
|
const resumes = (ctx: ExtensionContext) =>
|
|
36
42
|
ctx.sessionManager
|
|
@@ -38,7 +44,7 @@ const resumes = (ctx: ExtensionContext) =>
|
|
|
38
44
|
.flatMap((entry) =>
|
|
39
45
|
entry.type === "custom" &&
|
|
40
46
|
entry.customType === RESUME_ENTRY &&
|
|
41
|
-
|
|
47
|
+
isResume(entry.data)
|
|
42
48
|
? [entry.data]
|
|
43
49
|
: []
|
|
44
50
|
);
|
|
@@ -94,7 +100,7 @@ export const scheduleResume = async (
|
|
|
94
100
|
state: "scheduled",
|
|
95
101
|
};
|
|
96
102
|
|
|
97
|
-
if (
|
|
103
|
+
if (isScheduled(result)) {
|
|
98
104
|
scheduled.scheduledTaskId = result.scheduledTaskId;
|
|
99
105
|
}
|
|
100
106
|
|
package/src/bridge/state.ts
CHANGED
|
@@ -9,9 +9,19 @@ import {
|
|
|
9
9
|
import { homedir } from "node:os";
|
|
10
10
|
import path from "node:path";
|
|
11
11
|
|
|
12
|
-
import {
|
|
13
|
-
|
|
14
|
-
|
|
12
|
+
import {
|
|
13
|
+
compile,
|
|
14
|
+
lit,
|
|
15
|
+
map,
|
|
16
|
+
nullable,
|
|
17
|
+
num,
|
|
18
|
+
obj,
|
|
19
|
+
oneOf,
|
|
20
|
+
opt,
|
|
21
|
+
str,
|
|
22
|
+
union,
|
|
23
|
+
} from "@earendil-works/pi-schema";
|
|
24
|
+
import type { Static } from "@earendil-works/pi-schema";
|
|
15
25
|
|
|
16
26
|
// Machine-wide bridge state, shared by every Pi session: which model families
|
|
17
27
|
// are out of usage, and one ledger line per delegated brief.
|
|
@@ -38,24 +48,29 @@ export const moveOldState = (): void => {
|
|
|
38
48
|
}
|
|
39
49
|
};
|
|
40
50
|
|
|
41
|
-
const FamilyRecordSchema =
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
51
|
+
const FamilyRecordSchema = union([
|
|
52
|
+
obj(
|
|
53
|
+
{
|
|
54
|
+
reason: oneOf(["exhausted", "unhealthy"]),
|
|
55
|
+
resetAt: opt(str()),
|
|
56
|
+
since: str(),
|
|
57
|
+
source: oneOf(["delegate", "user"]),
|
|
58
|
+
state: lit("exhausted"),
|
|
59
|
+
},
|
|
60
|
+
{ open: true }
|
|
61
|
+
),
|
|
62
|
+
obj(
|
|
63
|
+
{ childThreadId: str(), since: str(), state: lit("probing") },
|
|
64
|
+
{ open: true }
|
|
65
|
+
),
|
|
54
66
|
]);
|
|
55
67
|
|
|
56
|
-
const AccountsFileSchema =
|
|
57
|
-
families:
|
|
58
|
-
}
|
|
68
|
+
const AccountsFileSchema = obj(
|
|
69
|
+
{ families: map(FamilyRecordSchema) },
|
|
70
|
+
{ open: true }
|
|
71
|
+
);
|
|
72
|
+
|
|
73
|
+
const isAccountsFile = compile(AccountsFileSchema).check;
|
|
59
74
|
|
|
60
75
|
export type FamilyRecord = Static<typeof FamilyRecordSchema>;
|
|
61
76
|
|
|
@@ -67,7 +82,7 @@ export const readFamilies = (): AccountsFile["families"] => {
|
|
|
67
82
|
try {
|
|
68
83
|
const parsed: unknown = JSON.parse(readFileSync(accountsPath(), "utf-8"));
|
|
69
84
|
|
|
70
|
-
return
|
|
85
|
+
return isAccountsFile(parsed) ? parsed.families : {};
|
|
71
86
|
} catch {
|
|
72
87
|
return {};
|
|
73
88
|
}
|
|
@@ -132,19 +147,24 @@ export const appendSteerLog = (line: {
|
|
|
132
147
|
}
|
|
133
148
|
};
|
|
134
149
|
|
|
135
|
-
const LedgerLineSchema =
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
150
|
+
const LedgerLineSchema = obj(
|
|
151
|
+
{
|
|
152
|
+
cacheRead: opt(num()),
|
|
153
|
+
cacheWrite: opt(num()),
|
|
154
|
+
cost: opt(num()),
|
|
155
|
+
input: opt(num()),
|
|
156
|
+
job: opt(nullable(str())),
|
|
157
|
+
minutes: opt(num()),
|
|
158
|
+
model: opt(nullable(str())),
|
|
159
|
+
report: opt(str()),
|
|
160
|
+
status: opt(str()),
|
|
161
|
+
threadId: opt(nullable(str())),
|
|
162
|
+
tokens: opt(num()),
|
|
163
|
+
},
|
|
164
|
+
{ open: true }
|
|
165
|
+
);
|
|
166
|
+
|
|
167
|
+
const isLedgerLine = compile(LedgerLineSchema).check;
|
|
148
168
|
|
|
149
169
|
export type LedgerLine = Static<typeof LedgerLineSchema>;
|
|
150
170
|
|
|
@@ -156,7 +176,7 @@ export const readLedger = (): LedgerLine[] => {
|
|
|
156
176
|
try {
|
|
157
177
|
const parsed: unknown = JSON.parse(line);
|
|
158
178
|
|
|
159
|
-
return
|
|
179
|
+
return isLedgerLine(parsed) ? [parsed] : [];
|
|
160
180
|
} catch {
|
|
161
181
|
return [];
|
|
162
182
|
}
|