@noopolis/daimon 0.2.1 → 0.2.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.
Files changed (35) hide show
  1. package/dist/runtime/agentSkillsDirectory.d.ts +25 -0
  2. package/dist/runtime/agentSkillsDirectory.d.ts.map +1 -0
  3. package/dist/runtime/agentSkillsDirectory.js +101 -0
  4. package/dist/runtime/engineBrokerServiceConfig.d.ts +10 -2
  5. package/dist/runtime/engineBrokerServiceConfig.d.ts.map +1 -1
  6. package/dist/runtime/engineBrokerServiceConfig.js +9 -5
  7. package/dist/runtime/grokBrokerProjection.d.ts +17 -1
  8. package/dist/runtime/grokBrokerProjection.d.ts.map +1 -1
  9. package/dist/runtime/grokBrokerProjection.js +7 -3
  10. package/dist/runtime/grokBrokerProxy.d.ts +8 -1
  11. package/dist/runtime/grokBrokerProxy.d.ts.map +1 -1
  12. package/dist/runtime/grokBrokerProxy.js +28 -10
  13. package/dist/runtime/grokBrokerProxyBodyShape.d.ts +24 -0
  14. package/dist/runtime/grokBrokerProxyBodyShape.d.ts.map +1 -0
  15. package/dist/runtime/grokBrokerProxyBodyShape.js +60 -0
  16. package/dist/runtime/grokBrokerProxyRequest.d.ts +29 -0
  17. package/dist/runtime/grokBrokerProxyRequest.d.ts.map +1 -1
  18. package/dist/runtime/grokBrokerProxyRequest.js +68 -13
  19. package/dist/runtime/grokBrokerTurnMeter.d.ts +7 -2
  20. package/dist/runtime/grokBrokerTurnMeter.d.ts.map +1 -1
  21. package/dist/runtime/grokBrokerTurnMeter.js +8 -4
  22. package/dist/runtime/grokWorkerAttestation.d.ts +3 -1
  23. package/dist/runtime/grokWorkerAttestation.d.ts.map +1 -1
  24. package/dist/runtime/grokWorkerAttestation.js +4 -2
  25. package/dist/runtime/grokWorkerHomeAttestation.d.ts +48 -4
  26. package/dist/runtime/grokWorkerHomeAttestation.d.ts.map +1 -1
  27. package/dist/runtime/grokWorkerHomeAttestation.js +95 -6
  28. package/dist/runtime/grokWorkerSkills.d.ts +86 -0
  29. package/dist/runtime/grokWorkerSkills.d.ts.map +1 -0
  30. package/dist/runtime/grokWorkerSkills.js +135 -0
  31. package/dist/runtime/index.d.ts +3 -0
  32. package/dist/runtime/index.d.ts.map +1 -1
  33. package/dist/runtime/index.js +3 -0
  34. package/dist/runtime/productionAgentTools.js +1 -1
  35. package/package.json +1 -1
@@ -0,0 +1,25 @@
1
+ import { type AgentSkill } from "./grokWorkerSkills.js";
2
+ /**
3
+ * What every entry of a skills tree must satisfy beyond being a plain,
4
+ * non-symlink directory or regular file. Attesting a root-provisioned
5
+ * `$GROK_HOME/skills` sets both; reading a compiled workspace leaves them unset.
6
+ */
7
+ export type AgentSkillsTreePolicy = Readonly<{
8
+ ownerUid?: number;
9
+ readOnly?: boolean;
10
+ }>;
11
+ /**
12
+ * Reads a skills tree — `<root>/<name>/SKILL.md` plus any supporting files —
13
+ * into the engine-neutral {@link AgentSkill} input, or `undefined` when `root`
14
+ * does not exist.
15
+ *
16
+ * It is the same reader for both ends of the Grok skills path: provisioning
17
+ * reads the compiled workspace's `.agents/skills` (the tree Codex reads in
18
+ * place), and the broker re-reads the rendered `$GROK_HOME/skills` before every
19
+ * turn under a root-owned, read-only policy. Symlinks, special files, hard
20
+ * links (read-only policy), non-UTF-8 content and anything past the
21
+ * {@link AGENT_SKILLS_BOUNDS} are refused rather than skipped, because a
22
+ * skipped entry would make the attested digest describe less than Grok loads.
23
+ */
24
+ export declare function readAgentSkillsDirectory(root: string, policy?: AgentSkillsTreePolicy): Promise<readonly AgentSkill[] | undefined>;
25
+ //# sourceMappingURL=agentSkillsDirectory.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"agentSkillsDirectory.d.ts","sourceRoot":"","sources":["../../src/runtime/agentSkillsDirectory.ts"],"names":[],"mappings":"AAIA,OAAO,EAAyC,KAAK,UAAU,EAAuB,MAAM,uBAAuB,CAAC;AAEpH;;;;GAIG;AACH,MAAM,MAAM,qBAAqB,GAAG,QAAQ,CAAC;IAAE,QAAQ,CAAC,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,OAAO,CAAA;CAAE,CAAC,CAAC;AAKxF;;;;;;;;;;;;GAYG;AACH,wBAAsB,wBAAwB,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,GAAE,qBAA0B,GAAG,OAAO,CAAC,SAAS,UAAU,EAAE,GAAG,SAAS,CAAC,CAqC3I"}
@@ -0,0 +1,101 @@
1
+ import { constants } from "node:fs";
2
+ import { lstat, open, readdir } from "node:fs/promises";
3
+ import path from "node:path";
4
+ import { AGENT_SKILLS_BOUNDS, parseAgentSkills } from "./grokWorkerSkills.js";
5
+ const invalid = (detail) => new TypeError(`invalid agent skills directory: ${detail}`);
6
+ const decoder = new TextDecoder("utf-8", { fatal: true, ignoreBOM: true });
7
+ /**
8
+ * Reads a skills tree — `<root>/<name>/SKILL.md` plus any supporting files —
9
+ * into the engine-neutral {@link AgentSkill} input, or `undefined` when `root`
10
+ * does not exist.
11
+ *
12
+ * It is the same reader for both ends of the Grok skills path: provisioning
13
+ * reads the compiled workspace's `.agents/skills` (the tree Codex reads in
14
+ * place), and the broker re-reads the rendered `$GROK_HOME/skills` before every
15
+ * turn under a root-owned, read-only policy. Symlinks, special files, hard
16
+ * links (read-only policy), non-UTF-8 content and anything past the
17
+ * {@link AGENT_SKILLS_BOUNDS} are refused rather than skipped, because a
18
+ * skipped entry would make the attested digest describe less than Grok loads.
19
+ */
20
+ export async function readAgentSkillsDirectory(root, policy = {}) {
21
+ let top;
22
+ try {
23
+ top = await lstat(root);
24
+ }
25
+ catch (error) {
26
+ if (error.code === "ENOENT")
27
+ return undefined;
28
+ throw invalid("unreadable root");
29
+ }
30
+ assertEntry(top, "directory", policy, ".");
31
+ const names = await boundedEntries(root, AGENT_SKILLS_BOUNDS.skills);
32
+ const skills = [];
33
+ let budget = AGENT_SKILLS_BOUNDS.totalBytes;
34
+ for (const name of names) {
35
+ const directory = path.join(root, name);
36
+ assertEntry(await lstat(directory), "directory", policy, name);
37
+ const files = [];
38
+ const walk = async (relative, depth) => {
39
+ for (const child of await boundedEntries(path.join(directory, relative), AGENT_SKILLS_BOUNDS.filesPerSkill)) {
40
+ const childRelative = relative === "" ? child : `${relative}/${child}`;
41
+ const absolute = path.join(directory, childRelative);
42
+ const where = `${name}/${childRelative}`;
43
+ const entry = await lstat(absolute);
44
+ if (entry.isDirectory()) {
45
+ if (depth + 1 >= AGENT_SKILLS_BOUNDS.depth)
46
+ throw invalid(`too deep at ${JSON.stringify(where)}`);
47
+ assertEntry(entry, "directory", policy, where);
48
+ await walk(childRelative, depth + 1);
49
+ continue;
50
+ }
51
+ assertEntry(entry, "file", policy, where);
52
+ if (files.length >= AGENT_SKILLS_BOUNDS.filesPerSkill)
53
+ throw invalid(`too many files in ${JSON.stringify(name)}`);
54
+ const content = await readPinnedFile(absolute, where, entry, Math.min(AGENT_SKILLS_BOUNDS.fileBytes, budget));
55
+ budget -= Buffer.byteLength(content, "utf8");
56
+ files.push({ path: childRelative, content });
57
+ }
58
+ };
59
+ await walk("", 0);
60
+ skills.push({ name, files });
61
+ }
62
+ return parseAgentSkills(skills);
63
+ }
64
+ /** `where` is the entry's path relative to the skills root, named in every refusal. */
65
+ function assertEntry(entry, kind, policy, where) {
66
+ if (entry.isSymbolicLink() || (kind === "directory" ? !entry.isDirectory() : !entry.isFile()))
67
+ throw invalid(`expected a plain ${kind} at ${JSON.stringify(where)}`);
68
+ if (policy.ownerUid !== undefined && entry.uid !== policy.ownerUid)
69
+ throw invalid(`unexpected owner of ${JSON.stringify(where)}`);
70
+ if (policy.readOnly === true && ((Number(entry.mode) & 0o7222) !== 0 || (kind === "file" && entry.nlink !== 1)))
71
+ throw invalid(`writable or linked entry ${JSON.stringify(where)}`);
72
+ }
73
+ async function boundedEntries(directory, limit) {
74
+ const entries = await readdir(directory);
75
+ if (entries.length > limit)
76
+ throw invalid(`too many entries in ${JSON.stringify(path.basename(directory))}`);
77
+ return entries.sort();
78
+ }
79
+ /** Opens without following links and requires the inode `lstat` saw, so a swap between the two cannot change the bytes read. */
80
+ async function readPinnedFile(file, where, seen, limit) {
81
+ if (seen.size > limit)
82
+ throw invalid(`file too large ${JSON.stringify(where)}`);
83
+ const handle = await open(file, constants.O_RDONLY | constants.O_NOFOLLOW | constants.O_NONBLOCK);
84
+ try {
85
+ const opened = await handle.stat();
86
+ if (!opened.isFile() || opened.dev !== seen.dev || opened.ino !== seen.ino || opened.uid !== seen.uid || opened.mode !== seen.mode || opened.nlink !== seen.nlink || opened.size > limit)
87
+ throw invalid(`file changed while read ${JSON.stringify(where)}`);
88
+ const bytes = await handle.readFile();
89
+ if (bytes.length > limit)
90
+ throw invalid(`file too large ${JSON.stringify(where)}`);
91
+ try {
92
+ return decoder.decode(bytes);
93
+ }
94
+ catch {
95
+ throw invalid(`file is not UTF-8 ${JSON.stringify(where)}`);
96
+ }
97
+ }
98
+ finally {
99
+ await handle.close().catch(() => undefined);
100
+ }
101
+ }
@@ -16,6 +16,13 @@ export type EngineBrokerServiceRegistration = Readonly<{
16
16
  usageLedgerPath: string;
17
17
  limits: EngineBrokerTurnLimits;
18
18
  model: GrokBrokerModelPolicy;
19
+ /**
20
+ * Digest of the skills root provisioning rendered into `$GROK_HOME/skills`
21
+ * (`grokWorkerSkillsSha256`), attested before every turn. Optional in v2
22
+ * registrations and v3 slot policies; absent means the slot has no skills and
23
+ * the directory must not exist.
24
+ */
25
+ skillsSha256?: string;
19
26
  }>;
20
27
  /**
21
28
  * `inferenceLedgerPath` (v2, optional) is where evaluator inference grants
@@ -46,8 +53,9 @@ export declare const engineBrokerSealLedgerPathFor: (usageLedgerPath: string) =>
46
53
  * v3 retains seven-field registrations and binds slotPolicies by slot; agent IDs
47
54
  * may repeat, while worker UIDs, slots and ledger streams must be distinct.
48
55
  * v2 requires every registration to declare its usage ledger, limits and model
49
- * (`model.id`/`model.reasoningEffort` from the closed lists); unknown keys at
50
- * any level are refused. v1 is still accepted and receives today's defaults:
56
+ * (`model.id`/`model.reasoningEffort` from the closed lists) and may declare
57
+ * `skillsSha256` (v3: on its slot policy); unknown keys at any level are
58
+ * refused. v1 is still accepted and receives today's defaults:
51
59
  * the container ledger, {@link DEFAULT_GROK_BROKER_TURN_LIMITS}, and
52
60
  * `grok-4.6`/`low`.
53
61
  */
@@ -1 +1 @@
1
- {"version":3,"file":"engineBrokerServiceConfig.d.ts","sourceRoot":"","sources":["../../src/runtime/engineBrokerServiceConfig.ts"],"names":[],"mappings":"AAEA,OAAO,EAAgE,KAAK,sBAAsB,EAAE,MAAM,iCAAiC,CAAC;AAC5I,OAAO,EAAuF,KAAK,qBAAqB,EAAE,MAAM,4BAA4B,CAAC;AAK7J,eAAO,MAAM,wBAAwB,EAAG,0CAAmD,CAAC;AAC5F,eAAO,MAAM,wBAAwB,EAAG,0CAAmD,CAAC;AAC5F,eAAO,MAAM,wBAAwB,EAAG,0CAAmD,CAAC;AAE5F,mHAAmH;AACnH,MAAM,MAAM,+BAA+B,GAAG,QAAQ,CAAC;IACrD,OAAO,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAC;IAAC,WAAW,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,MAAM,CAAC;IAAC,aAAa,EAAE,MAAM,CAAC;IACpI,oJAAoJ;IACpJ,eAAe,EAAE,MAAM,CAAC;IACxB,MAAM,EAAE,sBAAsB,CAAC;IAC/B,KAAK,EAAE,qBAAqB,CAAC;CAC9B,CAAC,CAAC;AACH;;;;;;GAMG;AACH,MAAM,MAAM,yBAAyB,GAAG,QAAQ,CAAC;IAAE,cAAc,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAC;IAAC,aAAa,EAAE,SAAS,+BAA+B,EAAE,CAAC;IAAC,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAAC,cAAc,CAAC,EAAE,MAAM,CAAC;IAAC,eAAe,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC,CAAC;AAc5O,2EAA2E;AAC3E,eAAO,MAAM,gCAAgC,GAAI,iBAAiB,MAAM,KAAG,MAAgF,CAAC;AAE5J;;;;GAIG;AACH,eAAO,MAAM,6BAA6B,GAAI,iBAAiB,MAAM,KAAG,MAA6E,CAAC;AAEtJ;;;;;;;;;;GAUG;AACH,wBAAgB,8BAA8B,CAAC,KAAK,EAAE,OAAO,GAAG,yBAAyB,CAwDxF"}
1
+ {"version":3,"file":"engineBrokerServiceConfig.d.ts","sourceRoot":"","sources":["../../src/runtime/engineBrokerServiceConfig.ts"],"names":[],"mappings":"AAEA,OAAO,EAAgE,KAAK,sBAAsB,EAAE,MAAM,iCAAiC,CAAC;AAC5I,OAAO,EAAuF,KAAK,qBAAqB,EAAE,MAAM,4BAA4B,CAAC;AAK7J,eAAO,MAAM,wBAAwB,EAAG,0CAAmD,CAAC;AAC5F,eAAO,MAAM,wBAAwB,EAAG,0CAAmD,CAAC;AAC5F,eAAO,MAAM,wBAAwB,EAAG,0CAAmD,CAAC;AAE5F,mHAAmH;AACnH,MAAM,MAAM,+BAA+B,GAAG,QAAQ,CAAC;IACrD,OAAO,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAC;IAAC,WAAW,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,MAAM,CAAC;IAAC,aAAa,EAAE,MAAM,CAAC;IACpI,oJAAoJ;IACpJ,eAAe,EAAE,MAAM,CAAC;IACxB,MAAM,EAAE,sBAAsB,CAAC;IAC/B,KAAK,EAAE,qBAAqB,CAAC;IAC7B;;;;;OAKG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB,CAAC,CAAC;AACH;;;;;;GAMG;AACH,MAAM,MAAM,yBAAyB,GAAG,QAAQ,CAAC;IAAE,cAAc,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAC;IAAC,aAAa,EAAE,SAAS,+BAA+B,EAAE,CAAC;IAAC,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAAC,cAAc,CAAC,EAAE,MAAM,CAAC;IAAC,eAAe,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC,CAAC;AAc5O,2EAA2E;AAC3E,eAAO,MAAM,gCAAgC,GAAI,iBAAiB,MAAM,KAAG,MAAgF,CAAC;AAE5J;;;;GAIG;AACH,eAAO,MAAM,6BAA6B,GAAI,iBAAiB,MAAM,KAAG,MAA6E,CAAC;AAEtJ;;;;;;;;;;;GAWG;AACH,wBAAgB,8BAA8B,CAAC,KAAK,EAAE,OAAO,GAAG,yBAAyB,CAyDxF"}
@@ -33,8 +33,9 @@ export const engineBrokerSealLedgerPathFor = (usageLedgerPath) => path.posix.joi
33
33
  * v3 retains seven-field registrations and binds slotPolicies by slot; agent IDs
34
34
  * may repeat, while worker UIDs, slots and ledger streams must be distinct.
35
35
  * v2 requires every registration to declare its usage ledger, limits and model
36
- * (`model.id`/`model.reasoningEffort` from the closed lists); unknown keys at
37
- * any level are refused. v1 is still accepted and receives today's defaults:
36
+ * (`model.id`/`model.reasoningEffort` from the closed lists) and may declare
37
+ * `skillsSha256` (v3: on its slot policy); unknown keys at any level are
38
+ * refused. v1 is still accepted and receives today's defaults:
38
39
  * the container ledger, {@link DEFAULT_GROK_BROKER_TURN_LIMITS}, and
39
40
  * `grok-4.6`/`low`.
40
41
  */
@@ -66,7 +67,7 @@ export function parseEngineBrokerServiceConfig(value) {
66
67
  for (const policy of value.slotPolicies) {
67
68
  if (!plain(policy))
68
69
  throw invalid();
69
- exact(policy, ["slot", "usageLedgerPath", "limits", "model"]);
70
+ exact(policy, ["slot", "usageLedgerPath", "limits", "model", ...withSkills(policy)]);
70
71
  if (!Number.isSafeInteger(policy.slot) || policy.slot < 0 || policies.has(policy.slot))
71
72
  throw invalid();
72
73
  policies.set(policy.slot, policy);
@@ -77,7 +78,7 @@ export function parseEngineBrokerServiceConfig(value) {
77
78
  const registrations = value.registrations.map((entry) => {
78
79
  if (!plain(entry))
79
80
  throw invalid();
80
- exact(entry, v2 && !v3 ? V2_REGISTRATION : V1_REGISTRATION);
81
+ exact(entry, v2 && !v3 ? [...V2_REGISTRATION, ...withSkills(entry)] : V1_REGISTRATION);
81
82
  const { agentId, slot, workerUid, workspace, profilePath, eventsPath, profileSha256 } = entry;
82
83
  if (typeof agentId !== "string" || !agentId.trim() || (!v3 && seen.has(agentId)) || !Number.isSafeInteger(slot) || slot < 0 || slots.has(slot) || !Number.isSafeInteger(workerUid) || workerUid < 2200 || workers.has(workerUid) || !absolute(workspace) || !absolute(profilePath) || !absolute(eventsPath) || typeof profileSha256 !== "string" || !/^[a-f0-9]{64}$/u.test(profileSha256) || eventsPath !== grokWorkerEventsPathFor(profilePath) || !profilePath.endsWith("/sandbox.toml"))
83
84
  throw invalid();
@@ -104,7 +105,9 @@ export function parseEngineBrokerServiceConfig(value) {
104
105
  catch {
105
106
  throw invalid();
106
107
  }
107
- return { ...base, usageLedgerPath, limits, model: parseServiceModel(policy.model) };
108
+ if (Object.hasOwn(policy, "skillsSha256") && (typeof policy.skillsSha256 !== "string" || !/^[a-f0-9]{64}$/u.test(policy.skillsSha256)))
109
+ throw invalid();
110
+ return { ...base, usageLedgerPath, limits, model: parseServiceModel(policy.model), ...(Object.hasOwn(policy, "skillsSha256") ? { skillsSha256: policy.skillsSha256 } : {}) };
108
111
  });
109
112
  const base = { credentialHome: value.credentialHome, turnStore: value.turnStore, registrations,
110
113
  ...(value.runRequestStore === undefined ? {} : { runRequestStore: value.runRequestStore }),
@@ -119,6 +122,7 @@ export function parseEngineBrokerServiceConfig(value) {
119
122
  throw invalid();
120
123
  return { ...base, inferenceLedgerPath };
121
124
  }
125
+ const withSkills = (value) => Object.hasOwn(value, "skillsSha256") ? ["skillsSha256"] : [];
122
126
  const ledgerPath = (item) => absolute(item) && item.endsWith(".jsonl") && path.posix.normalize(item) === item;
123
127
  function parseServiceModel(value) {
124
128
  if (!plain(value))
@@ -2,6 +2,7 @@ import { GROK_BROKER_TASK_CLOCK, type GrokWorkerConfigVariant } from "../contrac
2
2
  import { type EngineBrokerServiceRegistration } from "./engineBrokerServiceConfig.js";
3
3
  import { type EngineBrokerTurnLimits } from "./engineBrokerTurnAccounting.js";
4
4
  import type { GrokBrokerModel, GrokBrokerReasoningEffort } from "./grokBrokerModelPolicy.js";
5
+ import { type AgentSkill } from "./grokWorkerSkills.js";
5
6
  import { GROK_WORKER_SANDBOX_PROFILE } from "./grokWorkerSandboxProfile.js";
6
7
  export declare const GROK_BROKER_PROJECTION_VERSION: "noopolis.daimon.grok-broker-projection.v3";
7
8
  /**
@@ -27,6 +28,13 @@ export type OrganizationGrokBrokerProjection = Readonly<{
27
28
  timestampFilePath: string;
28
29
  }>;
29
30
  systemPromptSha256: string;
31
+ /**
32
+ * Digest of the declared skills rendered into `$GROK_HOME/skills`
33
+ * (`grokWorkerSkillsSha256`). Present only when the agent declares at least
34
+ * one skill, so a skill-less projection is byte-identical to one computed
35
+ * before skills existed.
36
+ */
37
+ skillsSha256?: string;
30
38
  grokCliVersion: string;
31
39
  grokExecutableSha256: string;
32
40
  nativeAbiVersion: number;
@@ -78,6 +86,13 @@ export type OrganizationGrokBrokerProjectionOptions = Readonly<{
78
86
  seccompProfileSha256: string;
79
87
  /** When the caller already holds a rendered profile digest, it must equal Daimon's. */
80
88
  profileSha256?: string;
89
+ /**
90
+ * The agent's declared skills: the compiled workspace `.agents/skills` tree
91
+ * every engine is given (`readAgentSkillsDirectory`), which root provisioning
92
+ * renders into `$GROK_HOME/skills` (`renderGrokWorkerSkillsTree`). Omitted or
93
+ * empty means no skills.
94
+ */
95
+ skills?: readonly AgentSkill[];
81
96
  }>;
82
97
  /**
83
98
  * Resolve the public Grok broker projection for one agent.
@@ -98,7 +113,8 @@ export type OrganizationGrokBrokerProjectionOptions = Readonly<{
98
113
  * this agent (realm, bootstrap, peers, and the mask covering the acceptance
99
114
  * store) plus the caller's evaluator paths, sorted and deduplicated exactly as
100
115
  * the profile renderer does. A supplied `profileSha256` that differs is
101
- * refused.
116
+ * refused. Declared `skills` add `skillsSha256`, which the implied
117
+ * registration carries and the broker attests against `$GROK_HOME/skills`.
102
118
  */
103
119
  export declare function resolveOrganizationGrokBrokerProjection(config: unknown, agentId: string, options: OrganizationGrokBrokerProjectionOptions): OrganizationGrokBrokerProjection;
104
120
  /** sha256 over the projection's canonical JSON; what a slot preflight receipt binds. */
@@ -1 +1 @@
1
- {"version":3,"file":"grokBrokerProjection.d.ts","sourceRoot":"","sources":["../../src/runtime/grokBrokerProjection.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"grokBrokerProjection.d.ts","sourceRoot":"","sources":["../../src/runtime/grokBrokerProjection.ts"],"names":[],"mappings":"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"}
@@ -9,6 +9,7 @@ import { grokSandboxProtectedPaths } from "./engineDispatcher.js";
9
9
  import { parseEngineBrokerServiceConfig } from "./engineBrokerServiceConfig.js";
10
10
  import { parseEngineBrokerTurnLimits } from "./engineBrokerTurnAccounting.js";
11
11
  import { grokBrokerWorkerConfigPins, grokBrokerWorkerConfigSha256 } from "./grokBrokerWorkerConfig.js";
12
+ import { grokWorkerSkillsSha256, parseAgentSkills } from "./grokWorkerSkills.js";
12
13
  import { GROK_WORKER_SANDBOX_PROFILE, grokWorkerEventsPathFor, renderGrokWorkerSandboxProfile, grokWorkerSandboxProfileSha256 } from "./grokWorkerSandboxProfile.js";
13
14
  import { parseOrganizationRuntimeConfig } from "./organizationRuntime.js";
14
15
  export const GROK_BROKER_PROJECTION_VERSION = GROK_ENGINE_BROKER.projectionVersion;
@@ -31,7 +32,8 @@ export const GROK_BROKER_PROJECTION_VERSION = GROK_ENGINE_BROKER.projectionVersi
31
32
  * this agent (realm, bootstrap, peers, and the mask covering the acceptance
32
33
  * store) plus the caller's evaluator paths, sorted and deduplicated exactly as
33
34
  * the profile renderer does. A supplied `profileSha256` that differs is
34
- * refused.
35
+ * refused. Declared `skills` add `skillsSha256`, which the implied
36
+ * registration carries and the broker attests against `$GROK_HOME/skills`.
35
37
  */
36
38
  export function resolveOrganizationGrokBrokerProjection(config, agentId, options) {
37
39
  const parsed = parseOrganizationRuntimeConfig(config);
@@ -64,12 +66,13 @@ export function resolveOrganizationGrokBrokerProjection(config, agentId, options
64
66
  const artifact = GROK_ENGINE_BROKER.grokCliArtifacts[options.architecture];
65
67
  if (artifact === undefined)
66
68
  throw new Error("Grok broker projection requires a pinned architecture");
69
+ const skills = parseAgentSkills(options.skills ?? []);
67
70
  const profilePath = path.posix.join(options.workerHomePath, ".grok", "sandbox.toml");
68
71
  const projection = {
69
72
  version: GROK_BROKER_PROJECTION_VERSION, agentId, workspacePath: agent.workspacePath, runtimeHomePath: agent.runtimeHomePath,
70
73
  workerUid: options.workerUid, slot: options.slot, profilePath, profileSha256, denyPaths, workerConfigSha256, workerConfigVariant,
71
74
  ...(workerConfigVariant === "clocked" ? { taskClock: { ...GROK_BROKER_TASK_CLOCK, directoryPath: path.posix.dirname(brokerTaskClockFileFor(options.slot)), timestampFilePath: brokerTaskClockFileFor(options.slot) } } : {}),
72
- systemPromptSha256: GROK_ENGINE_BROKER.worker.systemPromptSha256, grokCliVersion: GROK_ENGINE_BROKER.grokCliVersion, grokExecutableSha256: artifact.sha256,
75
+ systemPromptSha256: GROK_ENGINE_BROKER.worker.systemPromptSha256, ...(skills.length === 0 ? {} : { skillsSha256: grokWorkerSkillsSha256(skills) }), grokCliVersion: GROK_ENGINE_BROKER.grokCliVersion, grokExecutableSha256: artifact.sha256,
73
76
  nativeAbiVersion: GROK_ENGINE_BROKER.nativeAbiVersion, model, reasoningEffort, limits: parseEngineBrokerTurnLimits(options.limits), usageLedgerPath: options.usageLedgerPath, seccompProfileSha256: options.seccompProfileSha256,
74
77
  attestation: { platform: "linux/landlock", enforced: true, restrictNetwork: true, profileName: GROK_WORKER_SANDBOX_PROFILE, sandboxRuntime: "bubblewrap", eventsPath: grokWorkerEventsPathFor(profilePath) }
75
78
  };
@@ -84,7 +87,8 @@ export function grokBrokerServiceRegistrationFor(projection) {
84
87
  const registration = {
85
88
  agentId: projection.agentId, slot: projection.slot, workerUid: projection.workerUid, workspace: projection.workspacePath,
86
89
  profilePath: projection.profilePath, eventsPath: projection.attestation.eventsPath, profileSha256: projection.profileSha256,
87
- usageLedgerPath: projection.usageLedgerPath, limits: projection.limits, model: { id: projection.model, reasoningEffort: projection.reasoningEffort }
90
+ usageLedgerPath: projection.usageLedgerPath, limits: projection.limits, model: { id: projection.model, reasoningEffort: projection.reasoningEffort },
91
+ ...(projection.skillsSha256 === undefined ? {} : { skillsSha256: projection.skillsSha256 })
88
92
  };
89
93
  parseEngineBrokerServiceConfig({ version: "noopolis.daimon.engine-broker-service.v2", credentialHome: GROK_ENGINE_BROKER.credentialHomePath, turnStore: GROK_ENGINE_BROKER.turnStorePath, registrations: [registration] });
90
94
  return registration;
@@ -64,6 +64,13 @@ export declare function createGrokBrokerProxyHandler(authority: GrokBrokerCreden
64
64
  */
65
65
  export declare class GrokBrokerProxyRefusal extends Error {
66
66
  readonly reason: string;
67
- constructor(reason: string);
67
+ readonly check?: string | undefined;
68
+ readonly detail?: string | undefined;
69
+ /**
70
+ * `check` names which body-gate check failed and `detail` is the refused
71
+ * body's content-free shape ({@link grokBrokerProxyBodyShape}); both are for
72
+ * the broker's stderr line, and `check` also rides the 400 body.
73
+ */
74
+ constructor(reason: string, check?: string | undefined, detail?: string | undefined);
68
75
  }
69
76
  //# sourceMappingURL=grokBrokerProxy.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"grokBrokerProxy.d.ts","sourceRoot":"","sources":["../../src/runtime/grokBrokerProxy.ts"],"names":[],"mappings":"AAAA,OAAO,EAAsB,KAAK,mBAAmB,EAAE,MAAM,0BAA0B,CAAC;AAExF,OAAO,EAAgB,KAAK,eAAe,EAAE,KAAK,cAAc,EAAE,MAAM,WAAW,CAAC;AAIpF,OAAO,EAAE,wBAAwB,EAAE,MAAM,+BAA+B,CAAC;AAEzE,OAAO,EAAgE,KAAK,qBAAqB,EAAE,MAAM,4BAA4B,CAAC;AACtI,OAAO,EAAE,+BAA+B,EAAE,MAAM,6BAA6B,CAAC;AAE9E,OAAO,EAA2E,KAAK,mBAAmB,EAAE,MAAM,0BAA0B,CAAC;AAG7I,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,0BAA0B,CAAC;AACpE,OAAO,EAAkC,KAAK,uBAAuB,EAAE,MAAM,8BAA8B,CAAC;AAE5G,2FAA2F;AAC3F,MAAM,MAAM,mBAAmB,GAAG,QAAQ,CAAC;IACzC,MAAM,EAAE,qBAAqB,CAAC;IAC9B,KAAK,EAAE,mBAAmB,CAAC;IAC3B,KAAK,CAAC,EAAE,uBAAuB,CAAC;CACjC,CAAC,CAAC;AAEH,MAAM,MAAM,6BAA6B,GAAG,QAAQ,CAAC;IAAE,WAAW,CAAC,YAAY,EAAE,OAAO,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IAAC,qBAAqB,CAAC,CAAC,mBAAmB,EAAC,MAAM,GAAE,OAAO,CAAC,MAAM,CAAC,CAAC;IAAC,YAAY,CAAC,mBAAmB,CAAC,EAAC,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAAC,OAAO,CAAC,IAAI,OAAO,CAAA;CAAE,CAAC,CAAC;AACjQ,MAAM,MAAM,kBAAkB,GAAG,CAAC,OAAO,EAAE,UAAU,CAAC,OAAO,+BAA+B,CAAC,EAAE,MAAM,CAAC,EAAE,WAAW,KAAK,OAAO,CAAC,QAAQ,CAAC;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;IAAC,IAAI,EAAE,UAAU,CAAA;CAAE,CAAC,CAAC,CAAC;AAE3N;;;;;;;;;;GAUG;AACH,wBAAsB,oBAAoB,CACxC,SAAS,EAAE,6BAA6B,EAAE,QAAQ,GAAE,kBAAoC,EACxF,MAAM,GAAE,qBAAwD,EAAE,UAAU,SAAS,EACrF,MAAM,CAAC,EAAE,mBAAmB,EAAE,SAAS,CAAC,EAAE,mBAAmB;;;;mCAoC5B,MAAM,SAAS,MAAM,OAAO,CAAC,IAAI,CAAC;iCACpC,MAAM;yBACd,MAAM,QAAQ,mBAAmB;uBAMnC,MAAM;qBACR,eAAe,YAAY,cAAc;GA5B7D;AAED,iFAAiF;AACjF,wBAAgB,4BAA4B,CAC1C,SAAS,EAAE,6BAA6B,EAAE,QAAQ,GAAE,kBAAoC,EACxF,MAAM,GAAE,qBAAwD,EAAE,MAAM,CAAC,EAAE,mBAAmB,EAAE,SAAS,CAAC,EAAE,mBAAmB;;mCAc9F,MAAM,SAAS,MAAM,OAAO,CAAC,IAAI,CAAC;iCACpC,MAAM;yBACd,MAAM,QAAQ,mBAAmB;uBAMnC,MAAM;qBACR,eAAe,YAAY,cAAc;EAI7D;AAED;;;;;;;;GAQG;AACH,qBAAa,sBAAuB,SAAQ,KAAK;IACnC,QAAQ,CAAC,MAAM,EAAE,MAAM;gBAAd,MAAM,EAAE,MAAM;CACpC"}
1
+ {"version":3,"file":"grokBrokerProxy.d.ts","sourceRoot":"","sources":["../../src/runtime/grokBrokerProxy.ts"],"names":[],"mappings":"AAAA,OAAO,EAAsB,KAAK,mBAAmB,EAAE,MAAM,0BAA0B,CAAC;AAExF,OAAO,EAAgB,KAAK,eAAe,EAAE,KAAK,cAAc,EAAE,MAAM,WAAW,CAAC;AAIpF,OAAO,EAAE,wBAAwB,EAAE,MAAM,+BAA+B,CAAC;AAEzE,OAAO,EAAgE,KAAK,qBAAqB,EAAE,MAAM,4BAA4B,CAAC;AACtI,OAAO,EAAE,+BAA+B,EAAmC,MAAM,6BAA6B,CAAC;AAG/G,OAAO,EAA2E,KAAK,mBAAmB,EAAE,MAAM,0BAA0B,CAAC;AAG7I,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,0BAA0B,CAAC;AACpE,OAAO,EAAkC,KAAK,uBAAuB,EAAE,MAAM,8BAA8B,CAAC;AAE5G,2FAA2F;AAC3F,MAAM,MAAM,mBAAmB,GAAG,QAAQ,CAAC;IACzC,MAAM,EAAE,qBAAqB,CAAC;IAC9B,KAAK,EAAE,mBAAmB,CAAC;IAC3B,KAAK,CAAC,EAAE,uBAAuB,CAAC;CACjC,CAAC,CAAC;AAEH,MAAM,MAAM,6BAA6B,GAAG,QAAQ,CAAC;IAAE,WAAW,CAAC,YAAY,EAAE,OAAO,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IAAC,qBAAqB,CAAC,CAAC,mBAAmB,EAAC,MAAM,GAAE,OAAO,CAAC,MAAM,CAAC,CAAC;IAAC,YAAY,CAAC,mBAAmB,CAAC,EAAC,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAAC,OAAO,CAAC,IAAI,OAAO,CAAA;CAAE,CAAC,CAAC;AACjQ,MAAM,MAAM,kBAAkB,GAAG,CAAC,OAAO,EAAE,UAAU,CAAC,OAAO,+BAA+B,CAAC,EAAE,MAAM,CAAC,EAAE,WAAW,KAAK,OAAO,CAAC,QAAQ,CAAC;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;IAAC,IAAI,EAAE,UAAU,CAAA;CAAE,CAAC,CAAC,CAAC;AAE3N;;;;;;;;;;GAUG;AACH,wBAAsB,oBAAoB,CACxC,SAAS,EAAE,6BAA6B,EAAE,QAAQ,GAAE,kBAAoC,EACxF,MAAM,GAAE,qBAAwD,EAAE,UAAU,SAAS,EACrF,MAAM,CAAC,EAAE,mBAAmB,EAAE,SAAS,CAAC,EAAE,mBAAmB;;;;mCAoC5B,MAAM,SAAS,MAAM,OAAO,CAAC,IAAI,CAAC;iCACpC,MAAM;yBACd,MAAM,QAAQ,mBAAmB;uBAMnC,MAAM;qBACR,eAAe,YAAY,cAAc;GA5B7D;AAED,iFAAiF;AACjF,wBAAgB,4BAA4B,CAC1C,SAAS,EAAE,6BAA6B,EAAE,QAAQ,GAAE,kBAAoC,EACxF,MAAM,GAAE,qBAAwD,EAAE,MAAM,CAAC,EAAE,mBAAmB,EAAE,SAAS,CAAC,EAAE,mBAAmB;;mCAc9F,MAAM,SAAS,MAAM,OAAO,CAAC,IAAI,CAAC;iCACpC,MAAM;yBACd,MAAM,QAAQ,mBAAmB;uBAMnC,MAAM;qBACR,eAAe,YAAY,cAAc;EAI7D;AAED;;;;;;;;GAQG;AACH,qBAAa,sBAAuB,SAAQ,KAAK;IAMnC,QAAQ,CAAC,MAAM,EAAE,MAAM;IAAE,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM;IAAE,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM;IALtF;;;;OAIG;gBACkB,MAAM,EAAE,MAAM,EAAW,KAAK,CAAC,EAAE,MAAM,YAAA,EAAW,MAAM,CAAC,EAAE,MAAM,YAAA;CACvF"}
@@ -6,7 +6,8 @@ import { CLI_ENGINE_MAX_DIAGNOSTIC_BYTES } from "../pi/cliChildOutput.js";
6
6
  import { EngineBrokerCapabilities } from "./engineBrokerCapabilities.js";
7
7
  import { ENGINE_BROKER_AUTH_STALE } from "./engineBrokerProtocol.js";
8
8
  import { DEFAULT_GROK_BROKER_MODEL_POLICY, parseGrokBrokerModelPolicy } from "./grokBrokerModelPolicy.js";
9
- import { authorizeGrokBrokerProxyRequest } from "./grokBrokerProxyRequest.js";
9
+ import { authorizeGrokBrokerProxyRequest, GrokBrokerProxyRequestRejection } from "./grokBrokerProxyRequest.js";
10
+ import { formatGrokBrokerProxyBodyShape, grokBrokerProxyBodyShape } from "./grokBrokerProxyBodyShape.js";
10
11
  import { GROK_SESSION_TITLE_SINK_KEY } from "./grokBrokerWorkerConfig.js";
11
12
  import { parseGrokFinalReply, parseGrokResponseToolNames, parseGrokUpstreamUsage } from "./grokBrokerTurnMeter.js";
12
13
  import { GROK_ENGINE_BROKER } from "../contracts/runtimeContractManifest.js";
@@ -77,9 +78,18 @@ export function createGrokBrokerProxyHandler(authority, upstream = defaultUpstre
77
78
  */
78
79
  export class GrokBrokerProxyRefusal extends Error {
79
80
  reason;
80
- constructor(reason) {
81
+ check;
82
+ detail;
83
+ /**
84
+ * `check` names which body-gate check failed and `detail` is the refused
85
+ * body's content-free shape ({@link grokBrokerProxyBodyShape}); both are for
86
+ * the broker's stderr line, and `check` also rides the 400 body.
87
+ */
88
+ constructor(reason, check, detail) {
81
89
  super(`grok broker proxy refused: ${reason}`);
82
90
  this.reason = reason;
91
+ this.check = check;
92
+ this.detail = detail;
83
93
  }
84
94
  }
85
95
  async function serve(request, response, authority, upstream, capabilities, guards, turns, fallback, grants) {
@@ -137,7 +147,7 @@ async function serve(request, response, authority, upstream, capabilities, guard
137
147
  token = "";
138
148
  // The spend gate runs after the body is proven a real lean worker request
139
149
  // (a refused session-title body never counts) and before any upstream call.
140
- const admission = turn.meter.admit();
150
+ const admission = turn.meter.admit({ keepFinal: prepared.compaction === true });
141
151
  if ("refused" in admission) {
142
152
  response.writeHead(429, { "content-type": "application/json", "cache-control": "no-store" });
143
153
  response.end(JSON.stringify({ error: "turn limit reached", limit: admission.refused }));
@@ -167,8 +177,9 @@ async function serve(request, response, authority, upstream, capabilities, guard
167
177
  // A successful answer that called nothing and stopped is the turn's final
168
178
  // reply: the broker ends a worker that finished but did not exit on it
169
179
  // (`grokEngineBrokerTurnEnd.ts`), and reports it when the launcher refused
170
- // the worker's stdout. Observation only, exactly like the tool names.
171
- settle?.(usageOrEstimate(result.body, result.headers["content-type"]), toolCallsOrNothing(result.body, result.headers["content-type"]), result.status === 200 ? finalReplyOrNothing(result.body, result.headers["content-type"]) : undefined);
180
+ // the worker's stdout. Observation only, exactly like the tool names. A
181
+ // compaction summary also calls nothing and stops, and is never the reply.
182
+ settle?.(usageOrEstimate(result.body, result.headers["content-type"]), toolCallsOrNothing(result.body, result.headers["content-type"]), result.status === 200 && prepared.compaction !== true ? finalReplyOrNothing(result.body, result.headers["content-type"]) : undefined);
172
183
  response.writeHead(result.status, { "content-type": result.headers["content-type"] ?? "application/json", "cache-control": "no-store" });
173
184
  response.end(result.body);
174
185
  }
@@ -191,7 +202,8 @@ async function serve(request, response, authority, upstream, capabilities, guard
191
202
  // A named refusal is its own account; anything else used to reach the log as
192
203
  // the bare word `broker_unavailable`, which names nothing — so it carries the
193
204
  // fault's own class and message, and nothing else, beside it.
194
- const named = refused ? refusal : `${refusal} (${brokerFaultCause(error, secrets)})`;
205
+ const named = !refused ? `${refusal} (${brokerFaultCause(error, secrets)})`
206
+ : error instanceof GrokBrokerProxyRefusal && error.check !== undefined ? `${refusal} check=${error.check}${error.detail === undefined ? "" : ` ${error.detail}`}` : refusal;
195
207
  if (!titleSink && !expectedWorkerProbe(request))
196
208
  process.stderr.write(`[grok-proxy] refused: ${named}\n`);
197
209
  // Grok's own session-title call is refused by design, and keeps the transient
@@ -206,7 +218,7 @@ async function serve(request, response, authority, upstream, capabilities, guard
206
218
  }
207
219
  if (refused) {
208
220
  response.writeHead(400, { "content-type": "application/json", "cache-control": "no-store" });
209
- response.end(JSON.stringify({ error: "broker refused this request", reason: refusal }));
221
+ response.end(JSON.stringify({ error: "broker refused this request", reason: refusal, ...(error instanceof GrokBrokerProxyRefusal && error.check !== undefined ? { check: error.check } : {}) }));
210
222
  return;
211
223
  }
212
224
  response.writeHead(503, { "content-type": "application/json", "cache-control": "no-store" });
@@ -307,13 +319,19 @@ const finalReplyOrNothing = (body, contentType) => {
307
319
  return undefined;
308
320
  }
309
321
  };
310
- /** The body gate, refused non-retryably: a rejected body is a policy miss, never a transient fault. */
322
+ /**
323
+ * The body gate, refused non-retryably: a rejected body is a policy miss, never
324
+ * a transient fault. The refusal names the failed check and the body's
325
+ * content-free shape, because a bare `request_body_rejected` sent a live
326
+ * training pilot to a stub harness to learn which of a dozen checks fired.
327
+ */
311
328
  function authorizeRequestOrRefuse(...args) {
312
329
  try {
313
330
  return authorizeGrokBrokerProxyRequest(...args);
314
331
  }
315
- catch {
316
- throw new GrokBrokerProxyRefusal("request_body_rejected");
332
+ catch (error) {
333
+ const check = error instanceof GrokBrokerProxyRequestRejection ? error.check : "credential";
334
+ throw new GrokBrokerProxyRefusal("request_body_rejected", check, formatGrokBrokerProxyBodyShape(grokBrokerProxyBodyShape(args[0].body)));
317
335
  }
318
336
  }
319
337
  async function readBody(request) { const chunks = []; let bytes = 0; for await (const chunk of request) {
@@ -0,0 +1,24 @@
1
+ /**
2
+ * A content-free description of one refused worker body, for the broker's own
3
+ * stderr: byte length, top-level member names, offered tool names, message
4
+ * count and roles, and `tool_choice`. Never a message, a parameter schema, a
5
+ * value of any other member, a header or a token.
6
+ *
7
+ * Every name comes from a body the worker wrote, so each is admitted only as a
8
+ * plain short identifier (anything else is `<invalid>`) and every list is
9
+ * bounded with a `<truncated>` last entry. A body that is not a JSON object
10
+ * reports its byte length alone.
11
+ */
12
+ export type GrokBrokerProxyBodyShape = Readonly<{
13
+ bytes: number;
14
+ keys?: readonly string[];
15
+ tools?: readonly string[] | "absent" | "invalid";
16
+ messages?: number | "invalid";
17
+ roles?: readonly string[];
18
+ tool_choice?: string;
19
+ }>;
20
+ export declare const GROK_PROXY_BODY_SHAPE_LIST_MAX = 32;
21
+ export declare function grokBrokerProxyBodyShape(body: Uint8Array): GrokBrokerProxyBodyShape;
22
+ /** One line, `key=value` pairs, for the `[grok-proxy] refused:` log. */
23
+ export declare const formatGrokBrokerProxyBodyShape: (shape: GrokBrokerProxyBodyShape) => string;
24
+ //# sourceMappingURL=grokBrokerProxyBodyShape.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"grokBrokerProxyBodyShape.d.ts","sourceRoot":"","sources":["../../src/runtime/grokBrokerProxyBodyShape.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AACH,MAAM,MAAM,wBAAwB,GAAG,QAAQ,CAAC;IAC9C,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IACzB,KAAK,CAAC,EAAE,SAAS,MAAM,EAAE,GAAG,QAAQ,GAAG,SAAS,CAAC;IACjD,QAAQ,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC9B,KAAK,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAC1B,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB,CAAC,CAAC;AAGH,eAAO,MAAM,8BAA8B,KAAK,CAAC;AAqBjD,wBAAgB,wBAAwB,CAAC,IAAI,EAAE,UAAU,GAAG,wBAAwB,CAmBnF;AAED,wEAAwE;AACxE,eAAO,MAAM,8BAA8B,GAAI,OAAO,wBAAwB,KAAG,MAOtE,CAAC"}
@@ -0,0 +1,60 @@
1
+ const NAME = /^[A-Za-z_][A-Za-z0-9_.-]{0,63}$/u;
2
+ export const GROK_PROXY_BODY_SHAPE_LIST_MAX = 32;
3
+ const safeName = (value) => typeof value === "string" && NAME.test(value) ? value : "<invalid>";
4
+ const bounded = (values) => values.length <= GROK_PROXY_BODY_SHAPE_LIST_MAX ? values : [...values.slice(0, GROK_PROXY_BODY_SHAPE_LIST_MAX - 1), "<truncated>"];
5
+ const isObject = (value) => value !== null && typeof value === "object" && !Array.isArray(value);
6
+ /** Roles in order, with consecutive repeats collapsed to `role*n` so a long history stays short. */
7
+ const roleRuns = (messages) => {
8
+ const runs = [];
9
+ let last;
10
+ let count = 0;
11
+ const flush = () => { if (last !== undefined)
12
+ runs.push(count > 1 ? `${last}*${count}` : last); };
13
+ for (const message of messages) {
14
+ const role = isObject(message) ? safeName(message.role) : "<invalid>";
15
+ if (role === last) {
16
+ count++;
17
+ continue;
18
+ }
19
+ flush();
20
+ last = role;
21
+ count = 1;
22
+ }
23
+ flush();
24
+ return bounded(runs);
25
+ };
26
+ export function grokBrokerProxyBodyShape(body) {
27
+ let parsed;
28
+ try {
29
+ parsed = JSON.parse(new TextDecoder("utf-8", { fatal: false }).decode(body));
30
+ }
31
+ catch {
32
+ return { bytes: body.byteLength };
33
+ }
34
+ if (!isObject(parsed))
35
+ return { bytes: body.byteLength };
36
+ const tools = parsed.tools === undefined ? "absent" : Array.isArray(parsed.tools)
37
+ ? bounded(parsed.tools.map((tool) => isObject(tool) && isObject(tool.function) ? safeName(tool.function.name) : "<invalid>"))
38
+ : "invalid";
39
+ const choice = parsed.tool_choice;
40
+ return {
41
+ bytes: body.byteLength,
42
+ keys: bounded(Object.keys(parsed).map(safeName).sort()),
43
+ tools,
44
+ messages: Array.isArray(parsed.messages) ? parsed.messages.length : "invalid",
45
+ ...(Array.isArray(parsed.messages) ? { roles: roleRuns(parsed.messages) } : {}),
46
+ ...(choice === undefined ? {} : {
47
+ tool_choice: typeof choice === "string" ? safeName(choice)
48
+ : isObject(choice) ? `object:${isObject(choice.function) ? safeName(choice.function.name) : safeName(choice.type)}` : "<invalid>"
49
+ })
50
+ };
51
+ }
52
+ /** One line, `key=value` pairs, for the `[grok-proxy] refused:` log. */
53
+ export const formatGrokBrokerProxyBodyShape = (shape) => [
54
+ `bytes=${shape.bytes}`,
55
+ ...(shape.keys === undefined ? [] : [`keys=${shape.keys.join(",")}`]),
56
+ ...(shape.tools === undefined ? [] : [`tools=${Array.isArray(shape.tools) ? shape.tools.join(",") : shape.tools}`]),
57
+ ...(shape.messages === undefined ? [] : [`messages=${shape.messages}`]),
58
+ ...(shape.roles === undefined ? [] : [`roles=${shape.roles.join(",")}`]),
59
+ ...(shape.tool_choice === undefined ? [] : [`tool_choice=${shape.tool_choice}`])
60
+ ].join(" ");
@@ -1,5 +1,15 @@
1
1
  import type { EngineBrokerCapabilities } from "./engineBrokerCapabilities.js";
2
2
  import { type GrokBrokerModelPolicy } from "./grokBrokerModelPolicy.js";
3
+ /**
4
+ * Which body-gate check refused a request: a closed, content-free code, so a
5
+ * refusal on the broker's stderr names its cause without a stub harness.
6
+ */
7
+ export type GrokBrokerProxyRejectionCheck = "route" | "body_size" | "bearer" | "scope" | "client_version" | "json" | "envelope" | "model" | "reasoning_effort" | "tools" | "members" | "stream_options";
8
+ /** A body-gate refusal. The message is the historic one; `check` names the failed check. */
9
+ export declare class GrokBrokerProxyRequestRejection extends Error {
10
+ readonly check: GrokBrokerProxyRejectionCheck;
11
+ constructor(check: GrokBrokerProxyRejectionCheck);
12
+ }
3
13
  export type GrokBrokerProxyInput = Readonly<{
4
14
  method: string;
5
15
  pathname: string;
@@ -8,10 +18,12 @@ export type GrokBrokerProxyInput = Readonly<{
8
18
  agentId?: string;
9
19
  turnId?: string;
10
20
  }>;
21
+ /** `compaction` marks a normalized auto-compaction summary request: its answer is a summary, never the turn's final reply. */
11
22
  export type GrokBrokerUpstreamRequest = Readonly<{
12
23
  url: "https://cli-chat-proxy.grok.com/v1/chat/completions";
13
24
  headers: Readonly<Record<string, string>>;
14
25
  body: Uint8Array;
26
+ compaction?: true;
15
27
  }>;
16
28
  /**
17
29
  * Authorizes one worker request and rebuilds it for the provider.
@@ -24,7 +36,24 @@ export type GrokBrokerUpstreamRequest = Readonly<{
24
36
  * - the offered tool names are exactly the lean visible set. Grok 1.0.34 turns
25
37
  * an unmappable `--tools` entry into its full 19-tool set, and its per-turn
26
38
  * `session_title` request carries a single forced tool; both are refused.
39
+ *
40
+ * One other shape is admitted, and only by normalizing it into the lean one:
41
+ * Grok 1.0.34's auto-compaction summary request ({@link normalizeGrokCompactionRequest}).
27
42
  */
28
43
  export declare function authorizeGrokBrokerProxyRequest(input: GrokBrokerProxyInput, capabilities: EngineBrokerCapabilities, bearer: string, policy?: GrokBrokerModelPolicy): GrokBrokerUpstreamRequest;
44
+ /**
45
+ * Rewrites, in place, exactly the captured compaction shape into the lean
46
+ * worker shape, and reports whether it did. The two extra members are each
47
+ * the provider's default value (`tool_choice: "auto"` with tools offered,
48
+ * `temperature: 1`), so removing them changes nothing the model is asked; the
49
+ * missing effort is set to the declared one, so the request cannot fall back
50
+ * to a provider default effort. Every other check — model, tools, stream
51
+ * options — then runs on the result exactly as for any worker request, and
52
+ * the forwarded body is indistinguishable from an ordinary lean request: the
53
+ * spend envelope is unchanged. Any other value of either member (a forced
54
+ * `tool_choice`, another temperature) or any other member set is left as is,
55
+ * and refused by the ordinary checks.
56
+ */
57
+ export declare function normalizeGrokCompactionRequest(parsed: Record<string, unknown>, reasoningEffort: string): boolean;
29
58
  export declare function exactLeanTools(tools: unknown): boolean;
30
59
  //# sourceMappingURL=grokBrokerProxyRequest.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"grokBrokerProxyRequest.d.ts","sourceRoot":"","sources":["../../src/runtime/grokBrokerProxyRequest.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,+BAA+B,CAAC;AAC9E,OAAO,EAAgE,KAAK,qBAAqB,EAAE,MAAM,4BAA4B,CAAC;AAGtI,MAAM,MAAM,oBAAoB,GAAG,QAAQ,CAAC;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAAC,CAAC;IAAC,IAAI,EAAE,UAAU,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC,CAAC;AAC9L,MAAM,MAAM,yBAAyB,GAAG,QAAQ,CAAC;IAAE,GAAG,EAAE,qDAAqD,CAAC;IAAC,OAAO,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;IAAC,IAAI,EAAE,UAAU,CAAA;CAAE,CAAC,CAAC;AAE9K;;;;;;;;;;;GAWG;AACH,wBAAgB,+BAA+B,CAAC,KAAK,EAAE,oBAAoB,EAAE,YAAY,EAAE,wBAAwB,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAE,qBAAwD,GAAG,yBAAyB,CAmChO;AASD,wBAAgB,cAAc,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAStD"}
1
+ {"version":3,"file":"grokBrokerProxyRequest.d.ts","sourceRoot":"","sources":["../../src/runtime/grokBrokerProxyRequest.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,+BAA+B,CAAC;AAC9E,OAAO,EAAgE,KAAK,qBAAqB,EAAE,MAAM,4BAA4B,CAAC;AAItI;;;GAGG;AACH,MAAM,MAAM,6BAA6B,GAAG,OAAO,GAAG,WAAW,GAAG,QAAQ,GAAG,OAAO,GAAG,gBAAgB,GAAG,MAAM,GAAG,UAAU,GAAG,OAAO,GAAG,kBAAkB,GAAG,OAAO,GAAG,SAAS,GAAG,gBAAgB,CAAC;AAExM,4FAA4F;AAC5F,qBAAa,+BAAgC,SAAQ,KAAK;IAC5C,QAAQ,CAAC,KAAK,EAAE,6BAA6B;gBAApC,KAAK,EAAE,6BAA6B;CAC1D;AAED,MAAM,MAAM,oBAAoB,GAAG,QAAQ,CAAC;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAAC,CAAC;IAAC,IAAI,EAAE,UAAU,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC,CAAC;AAC9L,8HAA8H;AAC9H,MAAM,MAAM,yBAAyB,GAAG,QAAQ,CAAC;IAAE,GAAG,EAAE,qDAAqD,CAAC;IAAC,OAAO,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;IAAC,IAAI,EAAE,UAAU,CAAC;IAAC,UAAU,CAAC,EAAE,IAAI,CAAA;CAAE,CAAC,CAAC;AAEjM;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,+BAA+B,CAAC,KAAK,EAAE,oBAAoB,EAAE,YAAY,EAAE,wBAAwB,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAE,qBAAwD,GAAG,yBAAyB,CAwChO;AAcD;;;;;;;;;;;;GAYG;AACH,wBAAgB,8BAA8B,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,eAAe,EAAE,MAAM,GAAG,OAAO,CAOhH;AASD,wBAAgB,cAAc,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAStD"}