@intentius/chant-lexicon-fountain 0.106.1 → 0.106.3
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/dist/integrity.json +2 -2
- package/dist/manifest.json +1 -1
- package/dist/op/run-prompt.d.ts +14 -5
- package/dist/op/run-prompt.d.ts.map +1 -1
- package/dist/op/runtime.d.ts +10 -6
- package/dist/op/runtime.d.ts.map +1 -1
- package/package.json +2 -2
- package/src/op/run-prompt.ts +21 -11
- package/src/op/runtime.test.ts +73 -2
- package/src/op/runtime.ts +35 -29
package/dist/integrity.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"algorithm": "sha256",
|
|
3
3
|
"artifacts": {
|
|
4
|
-
"manifest.json": "
|
|
4
|
+
"manifest.json": "d991df566d8002caaa016de270498899957eae346f481467997be3bd0e339def",
|
|
5
5
|
"meta.json": "3301437a2a56eaf1a5cc8f6eabb18a40767f916e09701b2d9bf20aba4066738c",
|
|
6
6
|
"types/index.d.ts": "cf6b634fe0a8e9e96f1d9d07e3df53838def2683dc93dbe3c7fd971c6d3908ae",
|
|
7
7
|
"rules/ftn001-no-secret-literals.ts": "a417b194cd7039911cca9a0f14c49b042dfa6b1ee48517f7c2e22b16590a8599",
|
|
@@ -23,5 +23,5 @@
|
|
|
23
23
|
"skills/chant-fountain-ops.md": "4e72f0a0c7477a9e98f85ea45e49ba69e8a699f9c13ba0f0cf1cb20a88151373",
|
|
24
24
|
"skills/chant-fountain-locked-sandboxes.md": "7404ece96f2a3bc9105942fd3b6db156a3537db7e34c8cf2faa9ba87f5512567"
|
|
25
25
|
},
|
|
26
|
-
"composite": "
|
|
26
|
+
"composite": "6f4edb83df0d0f55abea6e8fbed09896f4a6a94a73e6f6a81aa21ccb093a916e"
|
|
27
27
|
}
|
package/dist/manifest.json
CHANGED
package/dist/op/run-prompt.d.ts
CHANGED
|
@@ -27,6 +27,14 @@ export interface HostedRunPromptOptions {
|
|
|
27
27
|
env?: string;
|
|
28
28
|
/** `--param name=value`, one flag per entry, in order (#3539). */
|
|
29
29
|
params?: Record<string, unknown>;
|
|
30
|
+
/**
|
|
31
|
+
* `--work <item>` and `--holder <name>` (#3555): the work item the run is
|
|
32
|
+
* for and who holds its lease. The sandbox's run takes the lease.
|
|
33
|
+
*/
|
|
34
|
+
work?: {
|
|
35
|
+
item?: string;
|
|
36
|
+
holder?: string;
|
|
37
|
+
};
|
|
30
38
|
/** Flags after `--on local`, as `[flag, value]`; each value is quoted as needed. */
|
|
31
39
|
flags?: Array<[string, string]>;
|
|
32
40
|
/** Words appended as they are, already spelled for the command line. */
|
|
@@ -35,7 +43,8 @@ export interface HostedRunPromptOptions {
|
|
|
35
43
|
/**
|
|
36
44
|
* The prompt a hosted run is posted as: {@link runPrompt}, then `--env <env>`
|
|
37
45
|
* when the caller named one (#3232), then each `--param` (#3539), then
|
|
38
|
-
* `--on local` (#3225), then any
|
|
46
|
+
* `--work` and `--holder` (#3555), then `--on local` (#3225), then any
|
|
47
|
+
* further flags.
|
|
39
48
|
*
|
|
40
49
|
* `--env` carries the environment the caller asked for into the sandbox,
|
|
41
50
|
* where `chant acp` hands it to the run as `currentOpRun().env`. `--param`
|
|
@@ -47,10 +56,10 @@ export interface HostedRunPromptOptions {
|
|
|
47
56
|
*
|
|
48
57
|
* A turn still matches {@link runPrompt} by prefix (`turnRunsOp`), and a
|
|
49
58
|
* steward's Schedule keeps the bare line. An `--env` value that is not one
|
|
50
|
-
* plain word is refused by name rather than posted
|
|
51
|
-
*
|
|
59
|
+
* plain word is refused by name rather than posted, as is a `--work` or
|
|
60
|
+
* `--holder` value the sandbox's parser would not read as a value (empty, or
|
|
61
|
+
* starting with `-`); a `--param`, `--work`, `--holder` or flag value is
|
|
62
|
+
* quoted ({@link quoteArg}).
|
|
52
63
|
*/
|
|
53
64
|
export declare function hostedRunPrompt(op: string, opts?: HostedRunPromptOptions): string;
|
|
54
|
-
/** The `--env` a posted prompt carries, or undefined. */
|
|
55
|
-
export declare function envOfPrompt(prompt: string | undefined): string | undefined;
|
|
56
65
|
//# sourceMappingURL=run-prompt.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"run-prompt.d.ts","sourceRoot":"","sources":["../../src/op/run-prompt.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH;;;GAGG;AACH,wBAAgB,SAAS,CAAC,EAAE,EAAE,MAAM,GAAG,MAAM,CAE5C;AAeD;;;;;GAKG;AACH,wBAAgB,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAG9C;AAED,4EAA4E;AAC5E,MAAM,WAAW,sBAAsB;IACrC,4DAA4D;IAC5D,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,kEAAkE;IAClE,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACjC,oFAAoF;IACpF,KAAK,CAAC,EAAE,KAAK,CAAC,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;IAChC,wEAAwE;IACxE,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;CAClB;AAED
|
|
1
|
+
{"version":3,"file":"run-prompt.d.ts","sourceRoot":"","sources":["../../src/op/run-prompt.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH;;;GAGG;AACH,wBAAgB,SAAS,CAAC,EAAE,EAAE,MAAM,GAAG,MAAM,CAE5C;AAeD;;;;;GAKG;AACH,wBAAgB,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAG9C;AAED,4EAA4E;AAC5E,MAAM,WAAW,sBAAsB;IACrC,4DAA4D;IAC5D,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,kEAAkE;IAClE,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACjC;;;OAGG;IACH,IAAI,CAAC,EAAE;QAAE,IAAI,CAAC,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IAC1C,oFAAoF;IACpF,KAAK,CAAC,EAAE,KAAK,CAAC,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;IAChC,wEAAwE;IACxE,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;CAClB;AAED;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,eAAe,CAAC,EAAE,EAAE,MAAM,EAAE,IAAI,GAAE,sBAA2B,GAAG,MAAM,CA4BrF"}
|
package/dist/op/runtime.d.ts
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
*
|
|
4
4
|
* `chant run <op> --on fountain` hands the run to a fountain teammate instead
|
|
5
5
|
* of executing it here. This module is the client for that: it posts the
|
|
6
|
-
* command line `chant run <op> [--env <env>] [--param k=v ...] --on local` ({@link hostedRunPrompt})
|
|
6
|
+
* command line `chant run <op> [--env <env>] [--param k=v ...] [--work <id>] [--holder <name>] --on local` ({@link hostedRunPrompt})
|
|
7
7
|
* as a prompt on the steward's thread, tails the
|
|
8
8
|
* conversation's SSE stream, and reports what comes back through the
|
|
9
9
|
* {@link OpRuntimeProvider} contract core defines in
|
|
@@ -63,8 +63,8 @@
|
|
|
63
63
|
import { type ChantConfig } from "@intentius/chant/config";
|
|
64
64
|
import type { OpRunRecord, OpRunState, OpRunStatus, OpRuntimeProvider, StepRecord } from "@intentius/chant/op";
|
|
65
65
|
import { type FountainHttp } from "./activities/fountain-apply.js";
|
|
66
|
-
import { runPrompt, hostedRunPrompt,
|
|
67
|
-
export { runPrompt, hostedRunPrompt,
|
|
66
|
+
import { runPrompt, hostedRunPrompt, quoteArg } from "./run-prompt.js";
|
|
67
|
+
export { runPrompt, hostedRunPrompt, quoteArg };
|
|
68
68
|
/** One server-sent event, already framed. `data` is the raw payload text. */
|
|
69
69
|
export interface FountainSseEvent {
|
|
70
70
|
/** The event id, which becomes the next connection's `Last-Event-ID`. */
|
|
@@ -209,13 +209,17 @@ export declare function runStateOfTurn(conversationStatus?: string, turnState?:
|
|
|
209
209
|
*/
|
|
210
210
|
export declare function createFountainOpRuntime(opts?: FountainOpRuntimeOptions): OpRuntimeProvider;
|
|
211
211
|
/**
|
|
212
|
-
* The `--env` and `--
|
|
213
|
-
* quote-aware split `chant acp` uses. A prompt that does
|
|
214
|
-
* unterminated quote) carries
|
|
212
|
+
* The `--env`, `--param`s, `--work` and `--holder` a posted prompt carries,
|
|
213
|
+
* read with the same quote-aware split `chant acp` uses. A prompt that does
|
|
214
|
+
* not split (an unterminated quote) carries none of them.
|
|
215
215
|
*/
|
|
216
216
|
export declare function flagsOfPrompt(prompt: string | undefined): {
|
|
217
217
|
env?: string;
|
|
218
218
|
params?: Record<string, string>;
|
|
219
|
+
work?: {
|
|
220
|
+
item?: string;
|
|
221
|
+
holder?: string;
|
|
222
|
+
};
|
|
219
223
|
};
|
|
220
224
|
interface TailOptions {
|
|
221
225
|
sse: FountainSse;
|
package/dist/op/runtime.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"runtime.d.ts","sourceRoot":"","sources":["../../src/op/runtime.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6DG;AAEH,OAAO,EAAmB,KAAK,WAAW,EAAE,MAAM,yBAAyB,CAAC;AAC5E,OAAO,KAAK,EAA+C,WAAW,EAAqB,UAAU,EAAE,WAAW,EAAE,iBAAiB,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;AAI/K,OAAO,EAGL,KAAK,YAAY,EAClB,MAAM,6BAA6B,CAAC;AAGrC,OAAO,EAAE,SAAS,EAAE,eAAe,EAAE,
|
|
1
|
+
{"version":3,"file":"runtime.d.ts","sourceRoot":"","sources":["../../src/op/runtime.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@intentius/chant-lexicon-fountain",
|
|
3
|
-
"version": "0.106.
|
|
3
|
+
"version": "0.106.3",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Fountain lexicon for chant — sandboxed agent environments, vaults, and agents as typed estate",
|
|
6
6
|
"license": "Apache-2.0",
|
|
@@ -50,7 +50,7 @@
|
|
|
50
50
|
"bundle": "tsx src/package-cli.ts"
|
|
51
51
|
},
|
|
52
52
|
"peerDependencies": {
|
|
53
|
-
"@intentius/chant": "^0.106.
|
|
53
|
+
"@intentius/chant": "^0.106.3",
|
|
54
54
|
"typescript": "^5.9.3",
|
|
55
55
|
"zod": "^4.3.6"
|
|
56
56
|
},
|
package/src/op/run-prompt.ts
CHANGED
|
@@ -48,6 +48,11 @@ export interface HostedRunPromptOptions {
|
|
|
48
48
|
env?: string;
|
|
49
49
|
/** `--param name=value`, one flag per entry, in order (#3539). */
|
|
50
50
|
params?: Record<string, unknown>;
|
|
51
|
+
/**
|
|
52
|
+
* `--work <item>` and `--holder <name>` (#3555): the work item the run is
|
|
53
|
+
* for and who holds its lease. The sandbox's run takes the lease.
|
|
54
|
+
*/
|
|
55
|
+
work?: { item?: string; holder?: string };
|
|
51
56
|
/** Flags after `--on local`, as `[flag, value]`; each value is quoted as needed. */
|
|
52
57
|
flags?: Array<[string, string]>;
|
|
53
58
|
/** Words appended as they are, already spelled for the command line. */
|
|
@@ -57,7 +62,8 @@ export interface HostedRunPromptOptions {
|
|
|
57
62
|
/**
|
|
58
63
|
* The prompt a hosted run is posted as: {@link runPrompt}, then `--env <env>`
|
|
59
64
|
* when the caller named one (#3232), then each `--param` (#3539), then
|
|
60
|
-
* `--on local` (#3225), then any
|
|
65
|
+
* `--work` and `--holder` (#3555), then `--on local` (#3225), then any
|
|
66
|
+
* further flags.
|
|
61
67
|
*
|
|
62
68
|
* `--env` carries the environment the caller asked for into the sandbox,
|
|
63
69
|
* where `chant acp` hands it to the run as `currentOpRun().env`. `--param`
|
|
@@ -69,8 +75,10 @@ export interface HostedRunPromptOptions {
|
|
|
69
75
|
*
|
|
70
76
|
* A turn still matches {@link runPrompt} by prefix (`turnRunsOp`), and a
|
|
71
77
|
* steward's Schedule keeps the bare line. An `--env` value that is not one
|
|
72
|
-
* plain word is refused by name rather than posted
|
|
73
|
-
*
|
|
78
|
+
* plain word is refused by name rather than posted, as is a `--work` or
|
|
79
|
+
* `--holder` value the sandbox's parser would not read as a value (empty, or
|
|
80
|
+
* starting with `-`); a `--param`, `--work`, `--holder` or flag value is
|
|
81
|
+
* quoted ({@link quoteArg}).
|
|
74
82
|
*/
|
|
75
83
|
export function hostedRunPrompt(op: string, opts: HostedRunPromptOptions = {}): string {
|
|
76
84
|
const parts = [runPrompt(op)];
|
|
@@ -86,6 +94,16 @@ export function hostedRunPrompt(op: string, opts: HostedRunPromptOptions = {}):
|
|
|
86
94
|
for (const [name, value] of Object.entries(opts.params ?? {})) {
|
|
87
95
|
parts.push(`--param ${quoteArg(`${name}=${paramText(value)}`)}`);
|
|
88
96
|
}
|
|
97
|
+
for (const [flag, value] of [["--work", opts.work?.item], ["--holder", opts.work?.holder]] as const) {
|
|
98
|
+
if (value === undefined) continue;
|
|
99
|
+
if (value === "" || value.startsWith("-")) {
|
|
100
|
+
throw new Error(
|
|
101
|
+
`fountain runtime: ${flag} "${value}" cannot be posted to the steward: chant reads a value that is empty ` +
|
|
102
|
+
`or starts with "-" as a missing one`,
|
|
103
|
+
);
|
|
104
|
+
}
|
|
105
|
+
parts.push(`${flag} ${quoteArg(value)}`);
|
|
106
|
+
}
|
|
89
107
|
parts.push("--on local");
|
|
90
108
|
for (const [flag, value] of opts.flags ?? []) parts.push(`${flag} ${quoteArg(value)}`);
|
|
91
109
|
for (const part of opts.extra ?? []) parts.push(part);
|
|
@@ -98,11 +116,3 @@ function paramText(value: unknown): string {
|
|
|
98
116
|
if (value === undefined || value === null) return "";
|
|
99
117
|
return typeof value === "object" ? JSON.stringify(value) : String(value);
|
|
100
118
|
}
|
|
101
|
-
|
|
102
|
-
/** The `--env` a posted prompt carries, or undefined. */
|
|
103
|
-
export function envOfPrompt(prompt: string | undefined): string | undefined {
|
|
104
|
-
const words = (prompt ?? "").trim().split(/\s+/);
|
|
105
|
-
const at = words.indexOf("--env");
|
|
106
|
-
const env = at >= 0 ? words[at + 1] : undefined;
|
|
107
|
-
return env && !env.startsWith("-") ? env : undefined;
|
|
108
|
-
}
|
package/src/op/runtime.test.ts
CHANGED
|
@@ -197,6 +197,13 @@ describe("quoteArg and flagsOfPrompt (#3539)", () => {
|
|
|
197
197
|
});
|
|
198
198
|
expect(flagsOfPrompt("chant run x --env=prod --param=a=1")).toEqual({ env: "prod", params: { a: "1" } });
|
|
199
199
|
expect(flagsOfPrompt("chant run x --on local")).toEqual({});
|
|
200
|
+
// #3555: --work and --holder too.
|
|
201
|
+
expect(flagsOfPrompt('chant run x --work ISSUE-7 --holder "box steward" --on local')).toEqual({
|
|
202
|
+
work: { item: "ISSUE-7", holder: "box steward" },
|
|
203
|
+
});
|
|
204
|
+
expect(flagsOfPrompt("chant run x --work=ISSUE-7")).toEqual({ work: { item: "ISSUE-7" } });
|
|
205
|
+
// An env that is quoted is read as one value, where a whitespace split read half of it.
|
|
206
|
+
expect(flagsOfPrompt('chant run x --env "prod east"')).toEqual({ env: "prod east" });
|
|
200
207
|
expect(flagsOfPrompt('chant run x --env "unterminated')).toEqual({});
|
|
201
208
|
expect(flagsOfPrompt(undefined)).toEqual({});
|
|
202
209
|
});
|
|
@@ -328,13 +335,49 @@ describe("start", () => {
|
|
|
328
335
|
expect(parsed.ok && parsed.command.args.on).toBe("local");
|
|
329
336
|
});
|
|
330
337
|
|
|
331
|
-
|
|
338
|
+
// #3555: `--work` and `--holder` ride on the posted line, so an Op whose
|
|
339
|
+
// work lease leaves the item to the run can run on fountain; the sandbox's
|
|
340
|
+
// parser reads both back.
|
|
341
|
+
it("posts --work and --holder on the command line (#3555)", async () => {
|
|
342
|
+
const { http, calls } = fakeHttp(
|
|
343
|
+
stewardRoutes({
|
|
344
|
+
"POST /api/team/agent-1/messages": { status: 202, json: { data: { conversation_id: "conv-1" } } },
|
|
345
|
+
}),
|
|
346
|
+
);
|
|
347
|
+
const { sse } = fakeSse([
|
|
348
|
+
[
|
|
349
|
+
sseEvent("1", { stream: "stdout", blocks: [{ kind: "text", body: JSON.stringify(RECORD) }] }),
|
|
350
|
+
sseEvent("2", { stream: "stage", stage: "turn", state: "done" }),
|
|
351
|
+
],
|
|
352
|
+
]);
|
|
353
|
+
const runtime = createFountainOpRuntime({
|
|
354
|
+
config: CONFIG, endpoint: "https://fountain.example.com", token: "t", http, sse, now: fakeClock(),
|
|
355
|
+
});
|
|
356
|
+
expect(runtime.carriesWork).toBe(true);
|
|
357
|
+
|
|
358
|
+
const handle = await runtime.start(OP, { env: "staging", work: { item: "ISSUE-7", holder: "box steward" } });
|
|
359
|
+
await handle.result();
|
|
360
|
+
|
|
361
|
+
const post = calls.find((c) => c.method === "POST");
|
|
362
|
+
const prompt = (post?.body as { prompt: string }).prompt;
|
|
363
|
+
expect(prompt).toBe('chant run alb-deploy --env staging --work ISSUE-7 --holder "box steward" --on local');
|
|
364
|
+
|
|
365
|
+
const { parseChantCommandLine } = await import("../acp/command-line");
|
|
366
|
+
const parsed = await parseChantCommandLine(prompt);
|
|
367
|
+
expect(parsed.ok && parsed.command.kind === "op-run" && parsed.command.op).toBe("alb-deploy");
|
|
368
|
+
expect(parsed.ok && parsed.command.args.work).toBe("ISSUE-7");
|
|
369
|
+
expect(parsed.ok && parsed.command.args.holder).toBe("box steward");
|
|
370
|
+
expect(parsed.ok && parsed.command.args.on).toBe("local");
|
|
371
|
+
});
|
|
372
|
+
|
|
373
|
+
it("refuses a --work the sandbox would read as missing, by name, before posting anything (#3555)", async () => {
|
|
332
374
|
const { http, calls } = fakeHttp(stewardRoutes({}));
|
|
333
375
|
const runtime = createFountainOpRuntime({
|
|
334
376
|
config: CONFIG, endpoint: "https://fountain.example.com", token: "t", http, now: fakeClock(),
|
|
335
377
|
});
|
|
336
378
|
|
|
337
|
-
await expect(runtime.start(OP, { work: { item: "
|
|
379
|
+
await expect(runtime.start(OP, { work: { item: "-7" } })).rejects.toThrow(/--work "-7" cannot be posted/);
|
|
380
|
+
await expect(runtime.start(OP, { work: { holder: "" } })).rejects.toThrow(/--holder "" cannot be posted/);
|
|
338
381
|
expect(calls).toEqual([]);
|
|
339
382
|
});
|
|
340
383
|
|
|
@@ -1194,6 +1237,34 @@ describe("resolveGate", () => {
|
|
|
1194
1237
|
});
|
|
1195
1238
|
});
|
|
1196
1239
|
|
|
1240
|
+
// #3555: the gated turn's work item and holder ride on the re-run, so the
|
|
1241
|
+
// re-run of an Op that needs a work item is told it again.
|
|
1242
|
+
it("carries the gated turn's --work and --holder onto the re-run prompt", async () => {
|
|
1243
|
+
const { http, calls } = fakeHttp(
|
|
1244
|
+
stewardRoutes({
|
|
1245
|
+
...routes,
|
|
1246
|
+
"GET /api/conversations/conv-1/turns": {
|
|
1247
|
+
status: 200,
|
|
1248
|
+
json: {
|
|
1249
|
+
data: [{ id: "turn-1", prompt: 'chant run alb-deploy --work ISSUE-7 --holder "box steward" --on local', state: "done" }],
|
|
1250
|
+
},
|
|
1251
|
+
},
|
|
1252
|
+
}),
|
|
1253
|
+
);
|
|
1254
|
+
const runtime = createFountainOpRuntime({
|
|
1255
|
+
config: CONFIG, endpoint: "https://fountain.example.com", token: "t", http, now: fakeClock(),
|
|
1256
|
+
});
|
|
1257
|
+
|
|
1258
|
+
await runtime.resolveGate!("alb-deploy", "release", resolution);
|
|
1259
|
+
|
|
1260
|
+
const post = calls.find((c) => c.path === "/api/conversations/conv-1/prompts");
|
|
1261
|
+
expect(post?.body).toEqual({
|
|
1262
|
+
prompt:
|
|
1263
|
+
'chant run alb-deploy --work ISSUE-7 --holder "box steward" --on local ' +
|
|
1264
|
+
"--approver alex --url https://github.com/o/r/pull/1",
|
|
1265
|
+
});
|
|
1266
|
+
});
|
|
1267
|
+
|
|
1197
1268
|
it("reports conversation_busy rather than retrying the prompt", async () => {
|
|
1198
1269
|
const { http, calls } = fakeHttp(
|
|
1199
1270
|
stewardRoutes({
|
package/src/op/runtime.ts
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
*
|
|
4
4
|
* `chant run <op> --on fountain` hands the run to a fountain teammate instead
|
|
5
5
|
* of executing it here. This module is the client for that: it posts the
|
|
6
|
-
* command line `chant run <op> [--env <env>] [--param k=v ...] --on local` ({@link hostedRunPrompt})
|
|
6
|
+
* command line `chant run <op> [--env <env>] [--param k=v ...] [--work <id>] [--holder <name>] --on local` ({@link hostedRunPrompt})
|
|
7
7
|
* as a prompt on the steward's thread, tails the
|
|
8
8
|
* conversation's SSE stream, and reports what comes back through the
|
|
9
9
|
* {@link OpRuntimeProvider} contract core defines in
|
|
@@ -73,10 +73,10 @@ import {
|
|
|
73
73
|
} from "./activities/fountain-apply";
|
|
74
74
|
import { resolveAgentId } from "./activities/fountain-run";
|
|
75
75
|
import { stewardForOp } from "../composites/steward";
|
|
76
|
-
import { runPrompt, hostedRunPrompt,
|
|
76
|
+
import { runPrompt, hostedRunPrompt, quoteArg } from "./run-prompt";
|
|
77
77
|
import { tokenize } from "../acp/command-line";
|
|
78
78
|
|
|
79
|
-
export { runPrompt, hostedRunPrompt,
|
|
79
|
+
export { runPrompt, hostedRunPrompt, quoteArg };
|
|
80
80
|
|
|
81
81
|
// ── The SSE seam ──────────────────────────────────────────────────────────
|
|
82
82
|
|
|
@@ -745,6 +745,10 @@ export function createFountainOpRuntime(opts: FountainOpRuntimeOptions = {}): Op
|
|
|
745
745
|
|
|
746
746
|
return {
|
|
747
747
|
name: "fountain",
|
|
748
|
+
// `--work` and `--holder` ride on the posted line (#3555), so an Op whose
|
|
749
|
+
// work lease leaves the item to the run can run here; the sandbox's run
|
|
750
|
+
// takes the lease.
|
|
751
|
+
carriesWork: true,
|
|
748
752
|
|
|
749
753
|
async start(op: OpConfig, startOpts: OpRunStartOptions): Promise<OpRunHandle> {
|
|
750
754
|
known.set(op.name, op);
|
|
@@ -753,12 +757,12 @@ export function createFountainOpRuntime(opts: FountainOpRuntimeOptions = {}): Op
|
|
|
753
757
|
selectProfile(startOpts.profile);
|
|
754
758
|
// Before any request: an `--env` the command line cannot carry is
|
|
755
759
|
// refused here, by name, rather than dropped (#3232). `--param` rides
|
|
756
|
-
// on the line (#3539)
|
|
757
|
-
//
|
|
758
|
-
refuseWork(op.name, startOpts.work);
|
|
760
|
+
// on the line (#3539), and so do `--work` and `--holder` (#3555): the
|
|
761
|
+
// run in the sandbox takes the work lease the caller named.
|
|
759
762
|
const prompt = hostedRunPrompt(op.name, {
|
|
760
763
|
env: startOpts.env,
|
|
761
764
|
...(startOpts.params ? { params: startOpts.params } : {}),
|
|
765
|
+
...(startOpts.work ? { work: startOpts.work } : {}),
|
|
762
766
|
});
|
|
763
767
|
const http = await rest();
|
|
764
768
|
const steward = await resolveSteward(op, startOpts.params);
|
|
@@ -928,15 +932,20 @@ export function createFountainOpRuntime(opts: FountainOpRuntimeOptions = {}): Op
|
|
|
928
932
|
// a space stays one value (#3539). The re-run runs in the environment
|
|
929
933
|
// the approver named (`chant run approve ... --env`, #3539), or else the
|
|
930
934
|
// one the gated run did: the `--env` on the op's latest turn (#3232).
|
|
931
|
-
// The gated turn's `--param`s
|
|
932
|
-
// every posted run (#3225).
|
|
935
|
+
// The gated turn's `--param`s, `--work` and `--holder` ride along too
|
|
936
|
+
// (#3555), and `--on local` as on every posted run (#3225).
|
|
933
937
|
const flags: Array<[string, string]> = [];
|
|
934
938
|
if (resolution.resolvedBy) flags.push(["--approver", resolution.resolvedBy]);
|
|
935
939
|
if (resolution.url) flags.push(["--url", resolution.url]);
|
|
936
940
|
const gated = flagsOfPrompt(found?.turns[0]?.prompt);
|
|
937
941
|
const env = resolveOpts?.env !== undefined && resolveOpts.env !== "" ? resolveOpts.env : gated.env;
|
|
938
942
|
const { status, json } = await http("POST", `/api/conversations/${conversationId}/prompts`, {
|
|
939
|
-
prompt: hostedRunPrompt(op, {
|
|
943
|
+
prompt: hostedRunPrompt(op, {
|
|
944
|
+
env,
|
|
945
|
+
...(gated.params ? { params: gated.params } : {}),
|
|
946
|
+
...(gated.work ? { work: gated.work } : {}),
|
|
947
|
+
flags,
|
|
948
|
+
}),
|
|
940
949
|
});
|
|
941
950
|
|
|
942
951
|
if (status === 400 && errorCode(json) === "conversation_busy") {
|
|
@@ -953,25 +962,15 @@ export function createFountainOpRuntime(opts: FountainOpRuntimeOptions = {}): Op
|
|
|
953
962
|
}
|
|
954
963
|
|
|
955
964
|
/**
|
|
956
|
-
*
|
|
957
|
-
*
|
|
958
|
-
*
|
|
959
|
-
* runtime is reached; this covers a caller that holds the provider directly.
|
|
960
|
-
*/
|
|
961
|
-
function refuseWork(op: string, work: OpRunStartOptions["work"]): void {
|
|
962
|
-
if (!work || (work.item === undefined && work.holder === undefined)) return;
|
|
963
|
-
throw new Error(
|
|
964
|
-
`fountain runtime: --work and --holder are not posted to the steward, so the hosted run of "${op}" ` +
|
|
965
|
-
`would not hold the lease named here. Run it with --on local to hold the lease in this process.`,
|
|
966
|
-
);
|
|
967
|
-
}
|
|
968
|
-
|
|
969
|
-
/**
|
|
970
|
-
* The `--env` and `--param`s a posted prompt carries, read with the same
|
|
971
|
-
* quote-aware split `chant acp` uses. A prompt that does not split (an
|
|
972
|
-
* unterminated quote) carries neither.
|
|
965
|
+
* The `--env`, `--param`s, `--work` and `--holder` a posted prompt carries,
|
|
966
|
+
* read with the same quote-aware split `chant acp` uses. A prompt that does
|
|
967
|
+
* not split (an unterminated quote) carries none of them.
|
|
973
968
|
*/
|
|
974
|
-
export function flagsOfPrompt(prompt: string | undefined): {
|
|
969
|
+
export function flagsOfPrompt(prompt: string | undefined): {
|
|
970
|
+
env?: string;
|
|
971
|
+
params?: Record<string, string>;
|
|
972
|
+
work?: { item?: string; holder?: string };
|
|
973
|
+
} {
|
|
975
974
|
let words: string[];
|
|
976
975
|
try {
|
|
977
976
|
words = tokenize(prompt ?? "");
|
|
@@ -979,6 +978,7 @@ export function flagsOfPrompt(prompt: string | undefined): { env?: string; param
|
|
|
979
978
|
return {};
|
|
980
979
|
}
|
|
981
980
|
let env: string | undefined;
|
|
981
|
+
const work: { item?: string; holder?: string } = {};
|
|
982
982
|
const params: Record<string, string> = {};
|
|
983
983
|
let any = false;
|
|
984
984
|
for (let i = 0; i < words.length; i++) {
|
|
@@ -989,20 +989,26 @@ export function flagsOfPrompt(prompt: string | undefined): { env?: string; param
|
|
|
989
989
|
if (eq > 0) {
|
|
990
990
|
flag = word.slice(0, eq);
|
|
991
991
|
value = word.slice(eq + 1);
|
|
992
|
-
} else if (word === "--env" || word === "--param") {
|
|
992
|
+
} else if (word === "--env" || word === "--param" || word === "--work" || word === "--holder") {
|
|
993
993
|
value = words[i + 1];
|
|
994
994
|
if (value === undefined || value.startsWith("-")) continue;
|
|
995
995
|
i++;
|
|
996
996
|
}
|
|
997
997
|
if (value === undefined) continue;
|
|
998
998
|
if (flag === "--env") env = value;
|
|
999
|
+
else if (flag === "--work") work.item = value;
|
|
1000
|
+
else if (flag === "--holder") work.holder = value;
|
|
999
1001
|
else if (flag === "--param") {
|
|
1000
1002
|
const at = value.indexOf("=");
|
|
1001
1003
|
params[at === -1 ? value : value.slice(0, at)] = at === -1 ? "" : value.slice(at + 1);
|
|
1002
1004
|
any = true;
|
|
1003
1005
|
}
|
|
1004
1006
|
}
|
|
1005
|
-
return {
|
|
1007
|
+
return {
|
|
1008
|
+
...(env ? { env } : {}),
|
|
1009
|
+
...(any ? { params } : {}),
|
|
1010
|
+
...(work.item !== undefined || work.holder !== undefined ? { work } : {}),
|
|
1011
|
+
};
|
|
1006
1012
|
}
|
|
1007
1013
|
|
|
1008
1014
|
/** Was `--durable-requests` asked for on this invocation? */
|