Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/workflows/cowork-guided-packet-compile-fixture.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ name: guided-packet-compile-fixture
# The first steps refuse unless BUILDER_HEAD is an ancestor of the pushed commit and the whole diff is that one line.
on:
push:
branches: [cowork/guided-packet-compile-fixture-4]
branches: [cowork/guided-packet-compile-fixture-5]
permissions:
contents: read
env:
Expand All @@ -22,7 +22,7 @@ env:
DEPENDENCY_LIST_SHA256: f8255bc2b622416d260ff522134beca24b5c0c503700343012a6d2a7e29cdfbe
jobs:
compile-only:
if: github.repository == 'yanlingLabs/winter-runtime-sdk' && github.event_name == 'push' && github.ref == 'refs/heads/cowork/guided-packet-compile-fixture-4' && github.event.created == true && github.run_attempt == '1'
if: github.repository == 'yanlingLabs/winter-runtime-sdk' && github.event_name == 'push' && github.ref == 'refs/heads/cowork/guided-packet-compile-fixture-5' && github.event.created == true && github.run_attempt == '1'
runs-on: ubuntu-24.04-arm
timeout-minutes: 30
steps:
Expand Down
2 changes: 1 addition & 1 deletion scripts/guest-guided-build/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -293,7 +293,7 @@ pruned-edge and tsconfig-`paths` rules, which the reader builder refuses. It is
## The run

`.github/workflows/cowork-guided-packet-compile-fixture.yml` fires only on the **first creation** of the branch
`cowork/guided-packet-compile-fixture-4` (`created == true`, `run_attempt == '1'`). It has no pull request,
`cowork/guided-packet-compile-fixture-5` (`created == true`, `run_attempt == '1'`). It has no pull request,
dispatch, schedule or tag trigger, no `paths` filter, no secrets, `contents: read`, no sudo, and it runs on
`ubuntu-24.04-arm`. It curls the pinned Bun zip and the 20 pinned tarballs (each checked by a strict checksum),
runs `build.py` (which runs `bun build --compile` four times, `cc` six times, plus the semantic parser runs) and
Expand Down
2 changes: 1 addition & 1 deletion scripts/guest-guided-build/guided-compile-inputs.pin.json
Original file line number Diff line number Diff line change
Expand Up @@ -234,7 +234,7 @@
]
},
"trigger": {
"branch": "cowork/guided-packet-compile-fixture-4",
"branch": "cowork/guided-packet-compile-fixture-5",
"workflow": "cowork-guided-packet-compile-fixture.yml",
"sourceWorkflow": "cowork-guided-build-source.yml"
},
Expand Down
2 changes: 1 addition & 1 deletion scripts/guest-guided-build/test_guided_build.py
Original file line number Diff line number Diff line change
Expand Up @@ -427,7 +427,7 @@ def test_retention_is_capped_at_seven_days_and_the_limit_is_stated(self):
self.assertEqual(pins.MAX_PACKET_BYTES,1024**3)
def test_trigger_branch_is_new_and_not_a_reader_or_hosted_name(self):
for used in ('cowork/instruction-reader-compile-fixture','cowork/reader-hosted-dependency-probe','cowork/reader-hosted-fixture-stderr','cowork/guided-packet-build'):self.assertNotEqual(pins.TRIGGER_BRANCH,used)
self.assertEqual(pins.TRIGGER_BRANCH,'cowork/guided-packet-compile-fixture-4')
self.assertEqual(pins.TRIGGER_BRANCH,'cowork/guided-packet-compile-fixture-5')

class NoExecutionTests(unittest.TestCase):
SOURCES=('build.py','graph.py','acquire.py','pins.py')
Expand Down
52 changes: 52 additions & 0 deletions scripts/guest-guided-provider/real-request-layout.fixture.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
{
"source": {
"note": "buildRequestMessages output (renderUserContext with one claudeMd entry, then the prompt), generated from the pinned agent-sdk source; regenerate only when the pin moves",
"repository": "yanlingLabs/winter-agent-sdk",
"commit": "9c980c326638489e0183d7fcddcf1a93553be0ba",
"path": "packages/runtime/src/context/request-layout.ts",
"sha256": "8a664c059c43016f188f34bde83ad1fb3681025e10fb9d86f4541fa36d4fd605"
},
"dispatch1": [
{
"role": "user",
"content": [
{
"type": "text",
"text": "<system-reminder>\nAs you answer the user's questions, you can use the following context:\n# claudeMd\nGuided hosted run guidance, first version.\nThe shared guidance marker is ALPHA.\n\n\n IMPORTANT: this context may or may not be relevant to your tasks. You should not respond to this context unless it is highly relevant to your task.\n</system-reminder>\n\n"
},
{
"type": "text",
"text": "Answer with the first scripted reply."
}
]
}
],
"dispatch2": [
{
"role": "user",
"content": [
{
"type": "text",
"text": "<system-reminder>\nAs you answer the user's questions, you can use the following context:\n# claudeMd\nGuided hosted run guidance, second version.\nThe shared guidance marker is BRAVO.\n\n\n IMPORTANT: this context may or may not be relevant to your tasks. You should not respond to this context unless it is highly relevant to your task.\n</system-reminder>\n\n"
},
{
"type": "text",
"text": "Answer with the first scripted reply."
}
]
},
{
"role": "assistant",
"content": [
{
"type": "text",
"text": "first deterministic answer"
}
]
},
{
"role": "user",
"content": "Answer with the second scripted reply."
}
]
}
69 changes: 69 additions & 0 deletions scripts/guest-guided-provider/stub.test.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { readFileSync } from "node:fs";
import { expect, test } from "bun:test";
import { createGuidedFixturePort, encodeFrame, LIMITS, SELECTION, STANDIN_SELECTION, sha256 } from "./stub.ts";

Expand Down Expand Up @@ -223,3 +224,71 @@ test("the selection argument is explicit and exact: undefined is the default; a
expect(() => createGuidedFixturePort(bytes, hash, SELECTION)).not.toThrow();
for (const bad of [{ ...SELECTION }, { ...STANDIN_SELECTION }, null, "winter-standin", {}, 0] as any[]) expect(() => createGuidedFixturePort(bytes, hash, bad)).toThrow("fixture refused");
});

// The engine merges the meta userContext message into the following user turn (request-layout.ts buildRequestMessages), so a real first message has no `isMeta`.
const merged = (second = false) => ({ ...turn(second), messages: [{ role: "user", content: [{ type: "text", text: `<system-reminder>\n# claudeMd\nSHARED:${second ? "new" : "old"}\nSOURCE:root\nSOURCE:cwd\n\n</system-reminder>\n` }, { type: "text", text: "question" }] }] });
const withTurn = (value: unknown, ordinal = 1, id = "invocation-one") => ({ ...request("begin", 0, ordinal, id), turn: value });
const refusedAt = async (value: unknown) => { const p = port(); try { await p.exchange(encodeFrame(withTurn(value))); } catch (error: any) { return error.site; } return undefined; };

test("the real merged first message (a reminder block then the prompt, no isMeta) is the current userContext, and the old separate-meta shape still is", async () => {
const p = port();
expect(decode(await p.exchange(encodeFrame(withTurn(merged()))))).toMatchObject({ kind: "accepted" });
await finish(port());
const q = port();
expect(decode(await q.exchange(encodeFrame(withTurn(turn()))))).toMatchObject({ kind: "accepted" });
});
test("a first message that is neither meta nor opens with the reminder marker is not the userContext", async () => {
const plain = { ...turn(), messages: [{ role: "user", content: [{ type: "text", text: "SHARED:old\nSOURCE:root\nSOURCE:cwd" }, { type: "text", text: "question" }] }] };
expect(await refusedAt(plain)).toBe("not_meta");
const late = { ...turn(), messages: [{ role: "user", content: [{ type: "text", text: "question" }, { type: "text", text: "<system-reminder>\nSHARED:old\nSOURCE:root\nSOURCE:cwd\n</system-reminder>" }] }] };
expect(await refusedAt(late)).toBe("not_meta");
const text = { ...turn(), messages: [{ role: "user", content: "SHARED:old\nSOURCE:root\nSOURCE:cwd" }] };
expect(await refusedAt(text)).toBe("not_meta");
expect(await refusedAt({ ...turn(), messages: [{ role: "assistant", content: "<system-reminder>" }, { role: "user", content: "q" }] })).toBe("not_meta");
// the reminder-first shape needs an ARRAY of at least two blocks whose first is one closed reminder
expect(await refusedAt({ ...turn(), messages: [{ role: "user", content: "<system-reminder>\nSHARED:old\nSOURCE:root\nSOURCE:cwd\n</system-reminder>" }] })).toBe("not_meta");
expect(await refusedAt({ ...turn(), messages: [{ role: "user", content: [{ type: "text", text: "<system-reminder>\nSHARED:old\nSOURCE:root\nSOURCE:cwd\n</system-reminder>" }] }] })).toBe("not_meta");
expect(await refusedAt({ ...turn(), messages: [{ role: "user", content: [{ type: "text", text: "<system-reminder>\nSHARED:old\nSOURCE:root\nSOURCE:cwd" }, { type: "text", text: "q" }] }] })).toBe("not_meta");
expect(await refusedAt({ ...turn(), messages: [{ role: "user", content: [{ type: "text", text: "<system-reminder>\nSHARED:old\n</system-reminder>\nSOURCE:root\nSOURCE:cwd\n</system-reminder>" }, { type: "text", text: "q" }] }] })).toBe("not_meta");
});
test("reviewer probes: markers carried by the prompt block, or by one block the user typed, are not the userContext", async () => {
// (a) a reminder block with no markers, then a prompt block that carries them
const a = { ...turn(), messages: [{ role: "user", content: [{ type: "text", text: "<system-reminder>\nnothing here\n</system-reminder>\n" }, { type: "text", text: "SHARED:old\nSOURCE:root\nSOURCE:cwd" }] }] };
expect(await refusedAt(a)).toBe("user_marker");
// (b) one user-typed block that opens with the tag and carries the markers (no second block, no closing tag)
const b = { ...turn(), messages: [{ role: "user", content: [{ type: "text", text: "<system-reminder> SHARED:old\nSOURCE:root\nSOURCE:cwd" }] }] };
expect(await refusedAt(b)).toBe("not_meta");
expect(await refusedAt({ ...turn(), messages: [{ role: "user", content: "<system-reminder> SHARED:old\nSOURCE:root\nSOURCE:cwd" }] })).toBe("not_meta");
// an absent marker that only the prompt block carries is not the userContext's, so it does not refuse a good reminder
const c = merged(); (c.messages[0]!.content as any[])[1].text = "SHARED:new";
const p = port(); expect(decode(await p.exchange(encodeFrame(withTurn(c))))).toMatchObject({ kind: "accepted" });
});
test("the reminder marker does not excuse a missing or stale marker: user, system and absent checks still apply to the merged shape", async () => {
const stale = merged(); (stale.messages[0]!.content[0] as any).text = (stale.messages[0]!.content[0] as any).text.replace("SHARED:old", "SHARED:old\nSHARED:new");
expect(await refusedAt(stale)).toBe("absent_marker");
const missing = merged(); (missing.messages[0]!.content[0] as any).text = "<system-reminder>\nnothing\n</system-reminder>";
expect(await refusedAt(missing)).toBe("user_marker");
expect(await refusedAt({ ...merged(), system: "nothing", systemBlocks: undefined })).toBe("system_marker");
});

// The cross-check: the REAL engine layout, vendored with its source pinned, run through the stub for both dispatches of the hosted fixture.
const layout = JSON.parse(readFileSync(new URL("./real-request-layout.fixture.json", import.meta.url), "utf8"));
test("the vendored fixture names the pinned agent-sdk source it was generated from", () => {
expect(layout.source).toMatchObject({ repository: "yanlingLabs/winter-agent-sdk", commit: "9c980c326638489e0183d7fcddcf1a93553be0ba", path: "packages/runtime/src/context/request-layout.ts", sha256: "8a664c059c43016f188f34bde83ad1fb3681025e10fb9d86f4541fa36d4fd605" });
expect(layout.dispatch1[0].isMeta).toBeUndefined();
expect(layout.dispatch1).toHaveLength(1); expect(layout.dispatch1[0].content).toHaveLength(2);
expect(layout.dispatch2[0].isMeta).toBeUndefined(); expect(layout.dispatch2.map((m: any) => m.role)).toEqual(["user", "assistant", "user"]);
});
test("the stub accepts the real engine's request layout for both hosted dispatches, with the real guidance markers", async () => {
const markers = (version: string, mark: string) => [`Guided hosted run guidance, ${version} version.`, `The shared guidance marker is ${mark}.`];
const hosted = { version: 1, scope: { ...scope }, selection: SELECTION, steps: [
{ system: ["Cowork current host context"], user: markers("first", "ALPHA"), absentSystem: [], absentUser: markers("second", "BRAVO"), response: "first deterministic answer" },
{ system: ["Cowork current host context"], user: markers("second", "BRAVO"), absentSystem: [], absentUser: markers("first", "ALPHA"), response: "second deterministic answer" }] };
const p = port(hosted);
for (const [ordinal, key] of [[1, "dispatch1"], [2, "dispatch2"]] as const) {
const id = `invocation-${ordinal}`, t = { model: SELECTION.providerModelId, system: "Cowork current host context\n\nbase", messages: layout[key] };
expect(decode(await p.exchange(encodeFrame(withTurn(t, ordinal, id))))).toMatchObject({ kind: "accepted" });
for (const sequence of [1, 2]) await p.exchange(encodeFrame(request("next", sequence, ordinal, id)));
}
expect(p.receipt()).toMatchObject({ receivedBegins: 2, acceptedBegins: 2, completed: 2 });
});
23 changes: 20 additions & 3 deletions scripts/guest-guided-provider/stub.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,16 @@ function content(value: unknown): string {
if (!Array.isArray(value)) refuse();
return value.map(item => { const block = object(item); keys(block, ["type", "text"]); if (block.type !== "text" || typeof block.text !== "string") refuse(); return block.text; }).join("\n");
}
const REMINDER_OPEN = "<system-reminder>", REMINDER_CLOSE = "</system-reminder>";
/** The merged layout's userContext: message content that is an array of at least two text blocks whose FIRST is one closed system-reminder
* (the prompt follows in its own block). Returns that first block's text, or undefined for any other content. */
function reminderBlock(value: unknown): string | undefined {
if (!Array.isArray(value) || value.length < 2) return undefined;
const first = value[0] as ObjectValue | null;
if (!first || typeof first !== "object" || first.type !== "text" || typeof first.text !== "string") return undefined;
const body = first.text.trimEnd();
return first.text.startsWith(REMINDER_OPEN) && body.endsWith(REMINDER_CLOSE) && body.indexOf(REMINDER_CLOSE) === body.length - REMINDER_CLOSE.length ? first.text : undefined;
}
function optionalTurnFields(turn: ObjectValue): void {
for (const key of ["cacheKey"]) if (turn[key] !== undefined) text(turn[key], LIMITS.requestBytes);
if (turn.effort !== undefined && !(typeof turn.effort === "number" && Number.isFinite(turn.effort)) && !(typeof turn.effort === "string" && ["low", "medium", "high", "xhigh", "max"].includes(turn.effort))) refuse();
Expand Down Expand Up @@ -98,9 +108,16 @@ function validateTurn(value: unknown, step: Step, selection: StubSelection): Obj
const current = object(turn.messages[0]);
// Match current userContext only; stale markers in historical user/assistant text
// cannot satisfy the next-dispatch guidance oracle.
if (current.role !== "user" || (selection !== STANDIN_SELECTION && current.isMeta !== true)) refuse("not_meta");
ordered(turn.system, step.system, "system_marker"); ordered(content(current.content), step.user, "user_marker");
absent(turn.system, step.absentSystem); absent(content(current.content), step.absentUser);
// The engine merges the meta userContext message into the user turn that follows it (request-layout.ts buildRequestMessages: consecutive
// user-role messages become one, which drops `isMeta`), so the real first message is `[closed <system-reminder> block, prompt block]` with no
// flag. The separate `isMeta` message is the other accepted shape. In the merged shape the markers are matched against the reminder block
// ONLY: a marker in the prompt block, or in a block the user typed, is not the userContext.
if (current.role !== "user") refuse("not_meta");
let userText: string;
if (selection === STANDIN_SELECTION || current.isMeta === true) userText = content(current.content);
else { const block = reminderBlock(current.content); if (block === undefined) return refuse("not_meta"); userText = block; }
ordered(turn.system, step.system, "system_marker"); ordered(userText, step.user, "user_marker");
absent(turn.system, step.absentSystem); absent(userText, step.absentUser);
return frozen(turn);
}
export interface CapturedTurn { readonly invocationId: string; readonly ordinal: number; readonly sha256: string; readonly turn: Readonly<ObjectValue> }
Expand Down
Loading
Loading