@openshain/core 0.3.1 → 0.4.1

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/index.js CHANGED
@@ -1,10 +1,12 @@
1
1
  // @openshain/core: Contracts (provider interfaces), fundamental objects, and the work runtime
2
+ export { AUTHORITY_DIR_NAME, DECISION_KINDS, DECISIONS_DIR_NAME, DELEGATIONS_FILE_NAME, DecisionFileSchema, DelegationsFileSchema, evaluate, loadAuthority, matchGlob, OPEN_AUTHORITY, POLICY_FILE_NAME, PolicyFileSchema, writeDecision, } from "./authority/policy.js";
2
3
  export { CONFIG_FILE_NAME, loadConfig, parseConfig, } from "./config/load.js";
3
4
  export { LANGUAGES } from "./config/schema.js";
4
5
  export { ERROR_CODES, isOpenshainError, OpenshainError } from "./errors.js";
5
6
  export { newEventId, newWorkId, parseEventId, parseWorkId, } from "./ids.js";
6
- export { createRuntime, createToolCaller, createToolRegistry, MAX_TOOL_TEXT_CHARS, } from "./runtime.js";
7
+ export { createRuntime, createToolCaller, createToolRegistry, MAX_TOOL_TEXT_CHARS, REVIEW_DIR_NAME, } from "./runtime.js";
7
8
  export { jsonSchemas } from "./schemas.js";
9
+ export { businessDate, companyTime, hostTimezone, isTimezone } from "./time.js";
8
10
  export { ASK_USER, RUNTIME_PROVIDER_ID } from "./tool/ask-user.js";
9
11
  export { loadToolModule } from "./tool/load-module.js";
10
12
  export { RESERVED_PATHS, resolveWorkspacePath } from "./tool/paths.js";
@@ -15,7 +17,7 @@ export { uuidv7 } from "./uuid.js";
15
17
  export { verifyArtifact } from "./work/artifacts.js";
16
18
  export { EVENTS_FILE_NAME, EventLog } from "./work/event-log.js";
17
19
  export { canonical, EventFileSchema, eventFromFile, eventToFile, isKnownEventType, parsePayloadFile, payloadFileSchemas, TOOL_REJECTION_CODES, } from "./work/events.js";
18
- export { countToolCalls, pendingQuestions, workHistory, } from "./work/history.js";
20
+ export { countToolCalls, pendingApprovals, pendingQuestions, workHistory, } from "./work/history.js";
19
21
  export { acquireLock, LOCK_FILE_NAME } from "./work/lock.js";
20
22
  export { buildProjection } from "./work/projection.js";
21
23
  export { WORK_DIR_NAME, WORK_FILE_NAME, WorkStore, } from "./work/store.js";
package/dist/runtime.d.ts CHANGED
@@ -1,3 +1,4 @@
1
+ import { type Authority } from "./authority/policy.ts";
1
2
  import type { Config, ModelConfig } from "./config/schema.ts";
2
3
  import type { ModelProvider } from "./model/types.ts";
3
4
  import type { HiddenTool } from "./tool/registry.ts";
@@ -21,6 +22,8 @@ export interface ToolSummary {
21
22
  }
22
23
  /** Longer tool output is cut here so that one tool cannot flood the model's context. */
23
24
  export declare const MAX_TOOL_TEXT_CHARS = 50000;
25
+ /** Where a work keeps the review packages a person sends to a reviewer. */
26
+ export declare const REVIEW_DIR_NAME = "review";
24
27
  export interface Runtime {
25
28
  readonly workspaceRoot: string;
26
29
  readonly config: Config;
@@ -41,6 +44,28 @@ export declare function createToolCaller(input: {
41
44
  registry: ToolRegistry;
42
45
  config: Config;
43
46
  workspaceRoot: string;
44
- }): (work: WorkHandle, call: ToolCall) => Promise<ToolResult>;
47
+ /**
48
+ * Who may do what. Pass a function when it can change while the server runs, as it does when
49
+ * a reviewer writes a decision. Omitted: the workspace is open, as one without authority/ is.
50
+ */
51
+ authority?: Authority | (() => Authority);
52
+ }): (work: WorkHandle, call: ToolCall, options?: CallOptions) => Promise<ToolResult>;
53
+ export interface CallOptions {
54
+ /** The approval that lets this call run: the policy is not consulted again. */
55
+ approvedBy?: string;
56
+ }
57
+ /** What a held call answers with: the client shows it to the person and the turn stops there. */
58
+ export interface PendingApprovalResult {
59
+ pending: "approval" | "review";
60
+ approval_id: string;
61
+ rule_id: string;
62
+ approvers?: string[];
63
+ reviewer?: {
64
+ role: string;
65
+ name?: string;
66
+ };
67
+ /** For a review: why the policy asks for one, when a cited decision did not cover the call. */
68
+ why?: string;
69
+ }
45
70
  /** Registers the tool providers the config names: the caller's factories by id, and modules from the workspace. Needs no model. */
46
71
  export declare function createToolRegistry(workspaceRoot: string, config: Config, tools: RuntimeProviders["tools"]): Promise<ToolRegistry>;
package/dist/runtime.js CHANGED
@@ -1,11 +1,18 @@
1
+ import { mkdir, writeFile } from "node:fs/promises";
2
+ import { join } from "node:path";
3
+ import { evaluate, loadAuthority, OPEN_AUTHORITY, } from "./authority/policy.js";
1
4
  import { loadConfig } from "./config/load.js";
2
5
  import { isOpenshainError, OpenshainError } from "./errors.js";
6
+ import { businessDate } from "./time.js";
3
7
  import { loadToolModule } from "./tool/load-module.js";
4
8
  import { ToolRegistry } from "./tool/registry.js";
9
+ import { uuidv7 } from "./uuid.js";
5
10
  import { TOOL_REJECTION_CODES } from "./work/events.js";
6
- import { WorkStore } from "./work/store.js";
11
+ import { WORK_DIR_NAME, WorkStore } from "./work/store.js";
7
12
  /** Longer tool output is cut here so that one tool cannot flood the model's context. */
8
13
  export const MAX_TOOL_TEXT_CHARS = 50_000;
14
+ /** Where a work keeps the review packages a person sends to a reviewer. */
15
+ export const REVIEW_DIR_NAME = "review";
9
16
  /** Builds a runtime for one workspace from its config and the providers the caller knows. */
10
17
  export async function createRuntime(options) {
11
18
  const { workspaceRoot, providers } = options;
@@ -25,6 +32,7 @@ export async function createRuntime(options) {
25
32
  throw new OpenshainError("config", `model ${description.provider}/${description.model} cannot call tools; openshain needs a model with tool support`);
26
33
  }
27
34
  const registry = await createToolRegistry(workspaceRoot, config, providers.tools);
35
+ const authority = await loadAuthority(workspaceRoot);
28
36
  const works = new WorkStore(workspaceRoot);
29
37
  return {
30
38
  workspaceRoot,
@@ -34,13 +42,15 @@ export async function createRuntime(options) {
34
42
  tools: {
35
43
  list: () => registry.list().map(({ definition, providerId }) => ({ definition, providerId })),
36
44
  hidden: () => registry.hiddenTools(),
37
- call: createToolCaller({ registry, config, workspaceRoot }),
45
+ call: createToolCaller({ registry, config, workspaceRoot, authority }),
38
46
  },
39
47
  };
40
48
  }
41
49
  /** The tool call pipeline on its own: authorize, validate, run, record. For callers that need no model, such as the MCP server. */
42
50
  export function createToolCaller(input) {
43
- return (work, call) => callTool({ ...input, work, call });
51
+ const given = input.authority;
52
+ const current = typeof given === "function" ? given : () => given ?? OPEN_AUTHORITY;
53
+ return (work, call, options) => callTool({ ...input, authority: current(), work, call, ...options });
44
54
  }
45
55
  /** Registers the tool providers the config names: the caller's factories by id, and modules from the workspace. Needs no model. */
46
56
  export async function createToolRegistry(workspaceRoot, config, tools) {
@@ -61,8 +71,8 @@ export async function createToolRegistry(workspaceRoot, config, tools) {
61
71
  return registry;
62
72
  }
63
73
  /**
64
- * The one place that allows or refuses a call before it runs. At this stage it knows only
65
- * the allow lists; a later authority engine plugs in here.
74
+ * The one place that allows or refuses a call before it runs by name alone. What the workspace
75
+ * allows a call to do is decided after this, by the policy in `authority/`.
66
76
  */
67
77
  function authorize(registry, call) {
68
78
  const tool = registry.get(call.name);
@@ -77,7 +87,7 @@ function authorize(registry, call) {
77
87
  : { ok: false, code: "unknown_tool", reason: `unknown tool "${call.name}"` };
78
88
  }
79
89
  async function callTool(input) {
80
- const { registry, config, workspaceRoot, work, call } = input;
90
+ const { registry, config, workspaceRoot, authority, work, call } = input;
81
91
  const reject = async (code, reason) => {
82
92
  await work.append({
83
93
  type: "tool.rejected",
@@ -93,6 +103,70 @@ async function callTool(input) {
93
103
  if (!validation.ok) {
94
104
  return reject("schema_mismatch", `input does not match the schema of ${call.name}: ${validation.reason}`);
95
105
  }
106
+ // The policy judges after the allow list, unless a person already approved this very call.
107
+ if (input.approvedBy === undefined) {
108
+ const path = pathOf(call.input);
109
+ const judged = evaluate(authority, {
110
+ tool: call.name,
111
+ effect: tool.definition.effect,
112
+ ...(path !== undefined && { path }),
113
+ principal: config.principal.id,
114
+ profession: config.profession.id,
115
+ workType: (await work.current()).type,
116
+ businessDate: businessDate(config.company.timezone),
117
+ });
118
+ if (judged.kind === "deny")
119
+ return reject("denied", judged.reason);
120
+ if (judged.kind === "approval_required" || judged.kind === "review_required") {
121
+ const review = judged.kind === "review_required";
122
+ const approvalId = `apr_${uuidv7()}`;
123
+ const approvers = judged.rule.approvers ?? [config.principal.id];
124
+ const declared = judged.rule.reviewer;
125
+ const reviewer = declared
126
+ ? { role: declared.role, ...(declared.name !== undefined && { name: declared.name }) }
127
+ : undefined;
128
+ await work.append({
129
+ type: "approval.requested",
130
+ payload: {
131
+ approvalId,
132
+ call: { callId: call.id, name: call.name, input: call.input },
133
+ ruleId: judged.rule.id,
134
+ kind: review ? "review" : "approval",
135
+ ...(review ? reviewer && { reviewer } : { approvers }),
136
+ },
137
+ });
138
+ if (review) {
139
+ const built = await reviewPackage(work, {
140
+ approvalId,
141
+ call,
142
+ rule: judged.rule,
143
+ principal: config.principal.id,
144
+ });
145
+ await work.append({ type: "review.requested", payload: { approvalId, package: built } });
146
+ // A copy the person can send to the reviewer, next to the work's own record.
147
+ const dir = join(workspaceRoot, WORK_DIR_NAME, work.id, REVIEW_DIR_NAME);
148
+ await mkdir(dir, { recursive: true });
149
+ await writeFile(join(dir, `${approvalId}.json`), `${JSON.stringify(built, null, 2)}\n`, {
150
+ flag: "wx",
151
+ });
152
+ }
153
+ await work.transition("waiting_approval", `rule ${judged.rule.id} needs ${review ? "a review" : "approval"}`);
154
+ const held = {
155
+ pending: review ? "review" : "approval",
156
+ approval_id: approvalId,
157
+ rule_id: judged.rule.id,
158
+ ...(review ? reviewer && { reviewer } : { approvers }),
159
+ ...(review && judged.why !== undefined && { why: judged.why }),
160
+ };
161
+ return { content: [{ type: "json", value: held }] };
162
+ }
163
+ if (judged.kind === "allow" && judged.decision) {
164
+ await work.append({
165
+ type: "decision.applied",
166
+ payload: { callId: call.id, decisionId: judged.decision.id },
167
+ });
168
+ }
169
+ }
96
170
  await work.append({
97
171
  type: "tool.called",
98
172
  payload: { callId: call.id, provider: tool.providerId, name: call.name, input: call.input },
@@ -130,6 +204,67 @@ async function callTool(input) {
130
204
  });
131
205
  return result;
132
206
  }
207
+ /**
208
+ * What the reviewer is asked to decide on, from the work's own record: the call, the tool calls
209
+ * that came before it, and the agent's last words as the proposal. Sources and company rules stay
210
+ * empty until knowledge is in.
211
+ */
212
+ async function reviewPackage(work, input) {
213
+ const events = await work.events();
214
+ const facts = [];
215
+ let proposal = "";
216
+ for (const event of events) {
217
+ if (event.type === "tool.called") {
218
+ const { name, input: called } = event.payload;
219
+ const path = pathOf(called);
220
+ facts.push(path === undefined ? name : `${name} ${path}`);
221
+ }
222
+ else if (event.type === "model.completed") {
223
+ const text = event.payload.content
224
+ .filter((part) => part.type === "text")
225
+ .map((part) => part.text)
226
+ .join("\n")
227
+ .trim();
228
+ if (text !== "")
229
+ proposal = text;
230
+ }
231
+ }
232
+ const current = await work.current();
233
+ return {
234
+ approvalId: input.approvalId,
235
+ workId: work.id,
236
+ action: {
237
+ name: input.rule.match.action?.toString() ?? input.call.name,
238
+ tool: input.call.name,
239
+ input: input.call.input,
240
+ },
241
+ facts,
242
+ sources: [],
243
+ companyRules: [],
244
+ proposal,
245
+ question: input.rule.reason ??
246
+ `${current.objective} のために ${input.call.name} を実行してよいか、判断をお願いします。`,
247
+ requestedBy: input.principal,
248
+ requestedAt: new Date().toISOString(),
249
+ };
250
+ }
251
+ /** The path a call names, normalized to a workspace-relative posix path, when its input has one. */
252
+ function pathOf(input) {
253
+ const path = input?.path;
254
+ if (typeof path !== "string" || path === "")
255
+ return undefined;
256
+ const segments = [];
257
+ for (const segment of path.replaceAll("\\", "/").split("/")) {
258
+ if (segment === "" || segment === ".")
259
+ continue;
260
+ if (segment === "..")
261
+ segments.pop();
262
+ else
263
+ segments.push(segment);
264
+ }
265
+ return segments.join("/");
266
+ }
267
+ /** Today's date on this machine's clock, YYYY-MM-DD. */
133
268
  function isRejectionCode(code) {
134
269
  return TOOL_REJECTION_CODES.includes(code);
135
270
  }
package/dist/schemas.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import type { JsonSchema } from "./tool/types.ts";
2
- export type SchemaName = "config.v1" | "events.v1" | "work.v1";
2
+ export type SchemaName = "config.v1" | "events.v1" | "work.v1" | "authority-policy.v1" | "authority-delegations.v1";
3
3
  /**
4
4
  * The JSON Schemas (draft 2020-12) of the files openshain reads and writes, derived from the zod
5
5
  * schemas that validate them. `spec/schemas/` holds this output; `bun run schemas` regenerates it.
package/dist/schemas.js CHANGED
@@ -1,4 +1,5 @@
1
1
  import { z } from "zod";
2
+ import { DelegationsFileSchema, PolicyFileSchema } from "./authority/policy.js";
2
3
  import { ConfigFileSchema } from "./config/schema.js";
3
4
  import { EventFileSchema, payloadFileSchemas } from "./work/events.js";
4
5
  import { WorkFileSchema } from "./work/work.js";
@@ -13,6 +14,8 @@ export function jsonSchemas() {
13
14
  "config.v1": describe(ConfigFileSchema, "openshain.yaml", "The company workspace manifest as written on disk."),
14
15
  "events.v1": eventsSchema(),
15
16
  "work.v1": describe(WorkFileSchema, "work.json", "The state of a work as projected from its event log. Never the source of truth."),
17
+ "authority-policy.v1": describe(PolicyFileSchema, "authority/policy.yaml", "The rules that judge tool calls: the first matching rule decides, else the default."),
18
+ "authority-delegations.v1": describe(DelegationsFileSchema, "authority/delegations.yaml", "Who the agent may act for, as which profession, and when."),
16
19
  };
17
20
  }
18
21
  /**
package/dist/time.d.ts ADDED
@@ -0,0 +1,14 @@
1
+ /**
2
+ * The company's clock. Every date the runtime judges against — a decision's effective days, a
3
+ * delegation's validity — is a date in the company's timezone, not in the host's. A workspace
4
+ * carried between a laptop in Tokyo and a container in UTC has to answer the same question the
5
+ * same way, so the timezone is part of the configuration rather than the environment.
6
+ */
7
+ /** True when the name is a timezone this runtime knows (`Asia/Tokyo`, `UTC`, ...). */
8
+ export declare function isTimezone(name: string): boolean;
9
+ /** The timezone of the machine, used when the configuration names none. */
10
+ export declare function hostTimezone(): string;
11
+ /** The business date (`YYYY-MM-DD`) in the company's timezone. */
12
+ export declare function businessDate(timezone: string, at?: Date): string;
13
+ /** The moment as the company reads it: `2026-09-09T14:03:11+09:00`. */
14
+ export declare function companyTime(timezone: string, at?: Date): string;
package/dist/time.js ADDED
@@ -0,0 +1,50 @@
1
+ /**
2
+ * The company's clock. Every date the runtime judges against — a decision's effective days, a
3
+ * delegation's validity — is a date in the company's timezone, not in the host's. A workspace
4
+ * carried between a laptop in Tokyo and a container in UTC has to answer the same question the
5
+ * same way, so the timezone is part of the configuration rather than the environment.
6
+ */
7
+ /** True when the name is a timezone this runtime knows (`Asia/Tokyo`, `UTC`, ...). */
8
+ export function isTimezone(name) {
9
+ try {
10
+ new Intl.DateTimeFormat("en-US", { timeZone: name });
11
+ return true;
12
+ }
13
+ catch {
14
+ return false;
15
+ }
16
+ }
17
+ /** The timezone of the machine, used when the configuration names none. */
18
+ export function hostTimezone() {
19
+ const name = Intl.DateTimeFormat().resolvedOptions().timeZone;
20
+ return name && isTimezone(name) ? name : "UTC";
21
+ }
22
+ /** The business date (`YYYY-MM-DD`) in the company's timezone. */
23
+ export function businessDate(timezone, at = new Date()) {
24
+ // en-CA writes a date as YYYY-MM-DD.
25
+ return new Intl.DateTimeFormat("en-CA", {
26
+ timeZone: timezone,
27
+ year: "numeric",
28
+ month: "2-digit",
29
+ day: "2-digit",
30
+ }).format(at);
31
+ }
32
+ /** The moment as the company reads it: `2026-09-09T14:03:11+09:00`. */
33
+ export function companyTime(timezone, at = new Date()) {
34
+ const parts = new Intl.DateTimeFormat("en-CA", {
35
+ timeZone: timezone,
36
+ hour12: false,
37
+ year: "numeric",
38
+ month: "2-digit",
39
+ day: "2-digit",
40
+ hour: "2-digit",
41
+ minute: "2-digit",
42
+ second: "2-digit",
43
+ timeZoneName: "longOffset",
44
+ }).formatToParts(at);
45
+ const of = (type) => parts.find((part) => part.type === type)?.value ?? "";
46
+ // "GMT+09:00" for a zone with an offset, "GMT" for UTC itself.
47
+ const zone = of("timeZoneName").replace("GMT", "");
48
+ const hour = of("hour") === "24" ? "00" : of("hour");
49
+ return `${of("year")}-${of("month")}-${of("day")}T${hour}:${of("minute")}:${of("second")}${zone === "" ? "+00:00" : zone}`;
50
+ }
@@ -1,5 +1,5 @@
1
1
  /** Paths the runtime keeps for itself. Tools may not read or write them. */
2
- export declare const RESERVED_PATHS: readonly ["openshain.yaml", "work"];
2
+ export declare const RESERVED_PATHS: readonly ["openshain.yaml", "work", "principals", "authority"];
3
3
  /**
4
4
  * Turns a tool-supplied relative path into an absolute path inside the workspace.
5
5
  *
@@ -2,7 +2,7 @@ import { lstat, readlink, realpath } from "node:fs/promises";
2
2
  import { dirname, isAbsolute, join, normalize, relative, resolve, sep } from "node:path";
3
3
  import { OpenshainError } from "../errors.js";
4
4
  /** Paths the runtime keeps for itself. Tools may not read or write them. */
5
- export const RESERVED_PATHS = ["openshain.yaml", "work"];
5
+ export const RESERVED_PATHS = ["openshain.yaml", "work", "principals", "authority"];
6
6
  const MAX_SYMLINK_HOPS = 32;
7
7
  /**
8
8
  * Turns a tool-supplied relative path into an absolute path inside the workspace.
@@ -12,6 +12,10 @@ export const RESERVED_TOOL_NAMES = [
12
12
  "work_fail",
13
13
  "work_answer",
14
14
  "work_record",
15
+ "context",
16
+ "approval_list",
17
+ "approval_decide",
18
+ "review_decide",
15
19
  "work_run",
16
20
  "work_show",
17
21
  ];
@@ -43,7 +43,7 @@ export interface ModelUsage {
43
43
  /** The part of outputTokens spent on reasoning. */
44
44
  reasoningTokens?: number;
45
45
  }
46
- export declare const TOOL_REJECTION_CODES: readonly ["schema_mismatch", "unknown_tool", "not_allowed", "reserved_path", "outside_workspace", "invalid_path", "limit_reached"];
46
+ export declare const TOOL_REJECTION_CODES: readonly ["schema_mismatch", "unknown_tool", "not_allowed", "reserved_path", "outside_workspace", "invalid_path", "limit_reached", "denied", "rejected_by_person"];
47
47
  export type ToolRejectionCode = (typeof TOOL_REJECTION_CODES)[number];
48
48
  export interface EventPayloads {
49
49
  "work.created": {
@@ -106,6 +106,45 @@ export interface EventPayloads {
106
106
  callId: string;
107
107
  answer: string;
108
108
  };
109
+ /** A tool call the policy holds for a person's or a reviewer's approval. The work waits. */
110
+ "approval.requested": {
111
+ approvalId: string;
112
+ call: {
113
+ callId: string;
114
+ name: string;
115
+ input: unknown;
116
+ };
117
+ ruleId: string;
118
+ kind: "approval" | "review";
119
+ approvers?: string[];
120
+ reviewer?: {
121
+ role: string;
122
+ name?: string;
123
+ };
124
+ };
125
+ /** The package handed to the reviewer: the call, what the work established, and the question. */
126
+ "review.requested": {
127
+ approvalId: string;
128
+ package: ReviewPackage;
129
+ };
130
+ /** The reviewer's answer. A decision id when they wrote one; absent when they refused. */
131
+ "review.decided": {
132
+ approvalId: string;
133
+ decisionId?: string;
134
+ };
135
+ /** A call that ran because an approved decision covers it. */
136
+ "decision.applied": {
137
+ callId: string;
138
+ decisionId: string;
139
+ };
140
+ /** The answer to an approval: approve runs the call, reject refuses it, modify runs it with the reviewer's input. */
141
+ "approval.decided": {
142
+ approvalId: string;
143
+ decision: "approve" | "reject" | "modify";
144
+ by: string;
145
+ comment?: string;
146
+ modifiedInput?: unknown;
147
+ };
109
148
  /** What the person said in a session. Becomes a user message in the projection. */
110
149
  "human.message": {
111
150
  text: string;
@@ -141,6 +180,33 @@ export interface EventPayloads {
141
180
  detail: string;
142
181
  };
143
182
  }
183
+ /** What a reviewer is asked to decide on, built from the work's own record. */
184
+ export interface ReviewPackage {
185
+ approvalId: string;
186
+ workId: string;
187
+ action: {
188
+ name: string;
189
+ tool: string;
190
+ input: unknown;
191
+ };
192
+ /** What the work established before this call: the tool calls it made. */
193
+ facts: string[];
194
+ /** Sources and company rules the work cited. Empty until knowledge is in. */
195
+ sources: {
196
+ id: string;
197
+ locator?: string;
198
+ version?: string;
199
+ }[];
200
+ companyRules: {
201
+ id: string;
202
+ statement: string;
203
+ }[];
204
+ /** What the agent proposes, in its own words. */
205
+ proposal: string;
206
+ question: string;
207
+ requestedBy: string;
208
+ requestedAt: string;
209
+ }
144
210
  export type EventType = keyof EventPayloads;
145
211
  interface Envelope {
146
212
  v: 1;
@@ -238,10 +304,12 @@ export declare const payloadFileSchemas: {
238
304
  call_id: z.ZodString;
239
305
  name: z.ZodString;
240
306
  code: z.ZodEnum<{
307
+ denied: "denied";
241
308
  invalid_path: "invalid_path";
242
309
  limit_reached: "limit_reached";
243
310
  not_allowed: "not_allowed";
244
311
  outside_workspace: "outside_workspace";
312
+ rejected_by_person: "rejected_by_person";
245
313
  reserved_path: "reserved_path";
246
314
  schema_mismatch: "schema_mismatch";
247
315
  unknown_tool: "unknown_tool";
@@ -256,6 +324,69 @@ export declare const payloadFileSchemas: {
256
324
  call_id: z.ZodString;
257
325
  answer: z.ZodString;
258
326
  }, z.core.$loose>;
327
+ "approval.requested": z.ZodObject<{
328
+ approval_id: z.ZodString;
329
+ call: z.ZodObject<{
330
+ call_id: z.ZodString;
331
+ name: z.ZodString;
332
+ input: z.ZodUnknown;
333
+ }, z.core.$loose>;
334
+ rule_id: z.ZodString;
335
+ kind: z.ZodEnum<{
336
+ approval: "approval";
337
+ review: "review";
338
+ }>;
339
+ approvers: z.ZodOptional<z.ZodArray<z.ZodString>>;
340
+ reviewer: z.ZodOptional<z.ZodObject<{
341
+ role: z.ZodString;
342
+ name: z.ZodOptional<z.ZodString>;
343
+ }, z.core.$loose>>;
344
+ }, z.core.$loose>;
345
+ "review.requested": z.ZodObject<{
346
+ approval_id: z.ZodString;
347
+ package: z.ZodObject<{
348
+ approval_id: z.ZodString;
349
+ work_id: z.ZodString;
350
+ action: z.ZodObject<{
351
+ name: z.ZodString;
352
+ tool: z.ZodString;
353
+ input: z.ZodUnknown;
354
+ }, z.core.$loose>;
355
+ facts: z.ZodArray<z.ZodString>;
356
+ sources: z.ZodArray<z.ZodObject<{
357
+ id: z.ZodString;
358
+ locator: z.ZodOptional<z.ZodString>;
359
+ version: z.ZodOptional<z.ZodString>;
360
+ }, z.core.$loose>>;
361
+ company_rules: z.ZodArray<z.ZodObject<{
362
+ id: z.ZodString;
363
+ statement: z.ZodString;
364
+ }, z.core.$loose>>;
365
+ proposal: z.ZodString;
366
+ question: z.ZodString;
367
+ requested_by: z.ZodString;
368
+ requested_at: z.ZodISODateTime;
369
+ }, z.core.$loose>;
370
+ }, z.core.$loose>;
371
+ "review.decided": z.ZodObject<{
372
+ approval_id: z.ZodString;
373
+ decision_id: z.ZodOptional<z.ZodString>;
374
+ }, z.core.$loose>;
375
+ "decision.applied": z.ZodObject<{
376
+ call_id: z.ZodString;
377
+ decision_id: z.ZodString;
378
+ }, z.core.$loose>;
379
+ "approval.decided": z.ZodObject<{
380
+ approval_id: z.ZodString;
381
+ decision: z.ZodEnum<{
382
+ approve: "approve";
383
+ modify: "modify";
384
+ reject: "reject";
385
+ }>;
386
+ by: z.ZodString;
387
+ comment: z.ZodOptional<z.ZodString>;
388
+ modified_input: z.ZodOptional<z.ZodUnknown>;
389
+ }, z.core.$loose>;
259
390
  "human.message": z.ZodObject<{
260
391
  text: z.ZodString;
261
392
  }, z.core.$loose>;