@brainervirus/workit-core 5.0.0 → 6.0.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.
@@ -6,7 +6,6 @@ import {
6
6
  POLICY_VERSION,
7
7
  candidateDigest,
8
8
  candidateSchema,
9
- decisionDigest,
10
9
  failure,
11
10
  scopeCovers as contractScopeCovers,
12
11
  success,
@@ -25,7 +24,6 @@ import {
25
24
  type TaskView,
26
25
  type WorkspaceRecord,
27
26
  } from "./task-contract";
28
- import { verifyDecisionContentAtRoot } from "./authority";
29
27
  import {
30
28
  CHECKS_FILE,
31
29
  checkCommand,
@@ -495,6 +493,69 @@ export const findingVerificationPasses = (
495
493
  evidence.status === "passed" &&
496
494
  (evidence.kind === "check" || evidence.kind === "review") &&
497
495
  ((findingCandidateId ?? null) === null || evidence.candidateId != null);
496
+ /** A decision about a document stays current only while its bytes are unchanged. */
497
+ export const verifyDecisionContentAtRoot = (
498
+ checkoutRoot: string,
499
+ binding: Decision["binding"],
500
+ ): Result<null> => {
501
+ let root: string;
502
+ try {
503
+ root = fs.realpathSync(checkoutRoot);
504
+ } catch {
505
+ return failure("permission_denied", "checkout root is unavailable");
506
+ }
507
+ for (const reference of binding.contentRefs) {
508
+ if (reference.kind !== "file") continue;
509
+ if (!reference.digest)
510
+ return failure("invalid_input", "document references require a byte digest");
511
+ const target = path.resolve(root, reference.path);
512
+ if (target !== root && !target.startsWith(`${root}${path.sep}`))
513
+ return failure(
514
+ "invalid_input",
515
+ "document reference escapes checkout; cite it as an external file:// reference instead",
516
+ {
517
+ fields: [
518
+ { path: "contentRefs.path", reason: `${reference.path} is outside the checkout` },
519
+ ],
520
+ },
521
+ );
522
+ try {
523
+ const below = path.relative(root, target);
524
+ let current = root;
525
+ for (const segment of below.split(path.sep)) {
526
+ if (!segment) continue;
527
+ current = path.join(current, segment);
528
+ const stat = fs.lstatSync(current);
529
+ const real = fs.realpathSync(current);
530
+ if (real !== root && !real.startsWith(`${root}${path.sep}`))
531
+ return failure(
532
+ "invalid_input",
533
+ "document reference escapes checkout; cite it as an external file:// reference instead",
534
+ {
535
+ fields: [
536
+ {
537
+ path: "contentRefs.path",
538
+ reason: `${reference.path} resolves outside the checkout`,
539
+ },
540
+ ],
541
+ },
542
+ );
543
+ if (stat.isSymbolicLink())
544
+ return failure("invalid_input", "document reference uses a symlink");
545
+ if (current === target && !stat.isFile())
546
+ return failure("invalid_input", "document reference is not a regular file");
547
+ if (current !== target && !stat.isDirectory())
548
+ return failure("invalid_input", "document reference ancestor is not a directory");
549
+ }
550
+ if (digestBytes(fs.readFileSync(target)) !== reference.digest)
551
+ return failure("permission_denied", "decision document bytes have changed");
552
+ } catch {
553
+ return failure("invalid_input", "decision document is unavailable");
554
+ }
555
+ }
556
+ return success(null, null, null);
557
+ };
558
+
498
559
  const applicableDecision = (
499
560
  task: TaskRecord,
500
561
  workspace: WorkspaceRecord,
@@ -514,7 +575,6 @@ const applicableDecision = (
514
575
  decision.requirementIds.includes(requirement.id) &&
515
576
  decision.binding.scope &&
516
577
  scopeCovers(decision.binding.scope, requirement.scope) &&
517
- decision.digest === decisionDigest(decision) &&
518
578
  (checkoutRoot
519
579
  ? verifyDecisionContentAtRoot(checkoutRoot, decision.binding).ok
520
580
  : decision.binding.contentRefs.every((reference) => reference.kind !== "file")),
@@ -537,7 +597,6 @@ const applicableRequirementDecision = (
537
597
  data.binding.workspaceId === workspace.id &&
538
598
  data.requirementIds.includes(requirement.id) &&
539
599
  scopeCovers(data.binding.scope, requirement.scope) &&
540
- data.digest === decisionDigest(data) &&
541
600
  (checkoutRoot
542
601
  ? verifyDecisionContentAtRoot(checkoutRoot, data.binding).ok
543
602
  : data.binding.contentRefs.every((reference) => reference.kind !== "file")),
@@ -903,7 +962,6 @@ export function evaluateClosure(
903
962
  decision.purpose === "limitation" &&
904
963
  decision.response === "approved" &&
905
964
  decision.revoked === null &&
906
- decision.digest === decisionDigest(decision) &&
907
965
  verifyDecisionContentAtRoot(view.workspace.root, decision.binding).ok &&
908
966
  decision.binding.taskId === view.task.id &&
909
967
  decision.binding.workspaceId === view.workspace.id &&
@@ -5,8 +5,8 @@
5
5
  //
6
6
  // store.json format marker (a newer format fails closed)
7
7
  // checkouts/<slug>/workspace.json this checkout's workspace record
8
- // checkouts/<slug>/metadata.lock checkout lock: workspace writes, task
9
- // creation and managed external actions
8
+ // checkouts/<slug>/metadata.lock checkout lock: workspace writes and task
9
+ // creation
10
10
  // tasks/<id>/events.jsonl the task's append-only event log
11
11
  // tasks/<id>/snapshot.json rebuildable cache of the reduced state
12
12
  // tasks/<id>/lock task lock: one append at a time
@@ -24,7 +24,6 @@
24
24
  // A 2.x store in `<root>/.workit` is migrated on first use (idempotent, under
25
25
  // the checkout lock, backup kept). The migrated `.workit/workspace.json` is
26
26
  // replaced by a marker that 2.x readers reject with an upgrade message.
27
- import { AsyncLocalStorage } from "node:async_hooks";
28
27
  import { createHash, randomUUID } from "node:crypto";
29
28
  import * as fs from "node:fs";
30
29
  import path from "node:path";
@@ -341,7 +340,6 @@ export const sameDirectoryIdentity = (left: string, right: string): boolean => {
341
340
  }
342
341
  };
343
342
 
344
- const externalActionLockRoots = new AsyncLocalStorage<ReadonlySet<string>>();
345
343
  /** Locks this process holds; waiting on them can only time out. */
346
344
  const heldInProcess = new Map<string, number>();
347
345
  const hold = (lock: string) => heldInProcess.set(lock, (heldInProcess.get(lock) ?? 0) + 1);
@@ -854,7 +852,6 @@ export class TaskStore {
854
852
  const reserved = this.writeWorkspace(nextWorkspace.data);
855
853
  if (!reserved.ok) return reserved;
856
854
  const uncertain = (message: string) => {
857
- this.markUncertain(nextWorkspace.data);
858
855
  return failure("external_outcome_unknown", message, {
859
856
  operation: "coupled_mutation",
860
857
  outcome: "unknown",
@@ -885,92 +882,6 @@ export class TaskStore {
885
882
  return result;
886
883
  }
887
884
 
888
- /** Hold this checkout's lock across a managed effect. */
889
- async withExternalActionLock<T>(
890
- operation: () => Promise<Result<T>>,
891
- reentrant = false,
892
- ): Promise<Result<T>> {
893
- const activeRoots = externalActionLockRoots.getStore();
894
- if (
895
- activeRoots &&
896
- [...activeRoots].some((root) => root === this.root || sameDirectoryIdentity(root, this.root))
897
- )
898
- return operation();
899
- const ready = this.ready(true);
900
- if (!ready.ok) return ready;
901
- const lockPath = this.paths().checkoutLock;
902
- try {
903
- this.initializeStorage();
904
- } catch (error) {
905
- return failure("storage_error", `unable to initialize store: ${String(error)}`, {
906
- path: this.paths().dir,
907
- });
908
- }
909
- let handle: FileLockSyncHandle;
910
- try {
911
- const options = this.lockOptions(lockPath);
912
- handle = this.acquire(lockPath, {
913
- ...options,
914
- payload: () => ({ ...options.payload(), externalAction: true }),
915
- });
916
- } catch (error) {
917
- return this.lockFailure(lockPath, error);
918
- }
919
- hold(lockPath);
920
- let result: Result<T>;
921
- try {
922
- if (!handle.verifyStillHeld())
923
- result = failure("recovery_required", "metadata lock was compromised", { path: lockPath });
924
- else {
925
- try {
926
- const run = () => operation();
927
- result = await (reentrant
928
- ? externalActionLockRoots.run(new Set([...(activeRoots ?? []), this.root]), run)
929
- : run());
930
- } catch {
931
- result = failure("external_outcome_unknown", "external action outcome is unknown", {
932
- outcome: "unknown",
933
- });
934
- }
935
- try {
936
- if (!handle.verifyStillHeld())
937
- result = failure("external_outcome_unknown", "metadata lock was compromised", {
938
- outcome: "unknown",
939
- path: lockPath,
940
- });
941
- } catch {
942
- result = failure("external_outcome_unknown", "metadata lock could not be verified", {
943
- outcome: "unknown",
944
- path: lockPath,
945
- });
946
- }
947
- }
948
- } catch {
949
- result = failure("external_outcome_unknown", "external action lock operation failed", {
950
- outcome: "unknown",
951
- path: lockPath,
952
- });
953
- }
954
- drop(lockPath);
955
- try {
956
- retryTransient(() => handle.release());
957
- } catch (error) {
958
- const released = this.lockFailure(lockPath, error);
959
- const releaseError = released.ok ? "metadata lock release failed" : released.error;
960
- return result.ok
961
- ? failure(
962
- "external_outcome_unknown",
963
- "external action metadata lock release is uncertain",
964
- {
965
- outcome: "unknown",
966
- path: lockPath,
967
- },
968
- )
969
- : failure("recovery_required", `${result.error}; ${releaseError}`, result.details);
970
- }
971
- return result;
972
- }
973
-
974
885
  /**
975
886
  * `workit gc`: compact long task logs into a checkpoint plus their most
976
887
  * recent events (the latest state is never lost), remove unreferenced
@@ -1700,7 +1611,6 @@ export class TaskStore {
1700
1611
  revision: newRevision(),
1701
1612
  root: this.root,
1702
1613
  runtime: { createdWith: runtimeVersion(), updatedWith: runtimeVersion() },
1703
- writer: null,
1704
1614
  };
1705
1615
  }
1706
1616
 
@@ -1747,16 +1657,6 @@ export class TaskStore {
1747
1657
  }
1748
1658
  }
1749
1659
 
1750
- private markUncertain(workspace: WorkspaceRecord) {
1751
- if (!workspace.writer || workspace.writer.state === "uncertain") return;
1752
- this.writeWorkspace({
1753
- ...workspace,
1754
- writer: { ...workspace.writer, state: "uncertain" as const },
1755
- revision: newRevision(),
1756
- runtime: stampNew(workspace),
1757
- });
1758
- }
1759
-
1760
1660
  // -------------------------------------------------------------------------
1761
1661
  // internals: index cache
1762
1662
 
@@ -2388,11 +2288,6 @@ export class TaskStore {
2388
2288
  try {
2389
2289
  lock = parseMetadataLockOrNull(fs.readFileSync(lockPath, "utf8"));
2390
2290
  } catch {}
2391
- if (lock?.externalAction)
2392
- return failure("writer_conflict", "workspace is reserved by a managed external action", {
2393
- outcome: "not_started",
2394
- path: lockPath,
2395
- });
2396
2291
  return failure(
2397
2292
  "busy",
2398
2293
  `workit lock is held by another Workit call${lock ? ` (pid ${lock.pid} on ${lock.host})` : ""}; retry shortly`,
@@ -1,30 +1,17 @@
1
- import {
2
- canonicalJson,
3
- failure,
4
- success,
5
- type Caller,
6
- type Id,
7
- type Owner,
8
- type Provenance,
9
- type Ref,
10
- type Result,
11
- type Revision,
12
- type TaskRecord,
13
- type Worker,
14
- type WorkerReport,
15
- type WorkspaceRecord,
1
+ import type {
2
+ Caller,
3
+ Id,
4
+ Provenance,
5
+ Ref,
6
+ Result,
7
+ Revision,
8
+ Worker,
9
+ WorkerReport,
16
10
  } from "./task-contract";
17
- import { TaskStore } from "./task-store";
18
11
 
19
12
  export type WorkerState = Worker["state"];
20
13
  export type HostSession = Extract<Ref, { kind: "host" }>;
21
14
 
22
- /** The adapter-owned caller identity used by all writer checks. */
23
- export type CallerContext = Caller & {
24
- workerId?: Id | null;
25
- session?: HostSession;
26
- };
27
-
28
15
  export type NativeWorkerObservation = {
29
16
  taskId: Id;
30
17
  workerId: Id;
@@ -44,55 +31,9 @@ export type NativeWorkerVerification = {
44
31
  caller: Caller;
45
32
  };
46
33
 
47
- /**
48
- * Host attestation for a worker that has no session yet: `prepare` claims the launch
49
- * slot, `not_started` claims that no child was ever created for that same reservation.
50
- */
51
- export type WorkerDispatchStage = "prepare" | "not_started";
52
-
53
- export type NativeWorkerDispatchVerification = {
54
- observation: unknown;
55
- expected: {
56
- taskId: Id;
57
- workspaceId: Id;
58
- workerId: Id;
59
- expectedRevision: Revision;
60
- expectedWorkspaceRevision: Revision;
61
- stage: WorkerDispatchStage;
62
- };
63
- caller: Caller;
64
- };
65
-
34
+ /** A host that observes its own child sessions attests their lifecycle. */
66
35
  export type NativeWorkerVerifier = {
67
36
  verifyWorker: (input: NativeWorkerVerification) => Result<Provenance>;
68
- /** Hosts that can observe their own launch surface also attest dispatch stages. */
69
- verifyDispatch?: (input: NativeWorkerDispatchVerification) => Result<Provenance>;
70
- };
71
-
72
- declare const workerDispatch: unique symbol;
73
- /** Opaque in-process launch reservation. Adapters hold it; it is never serialized. */
74
- export type WorkerDispatch = { readonly [workerDispatch]: true };
75
-
76
- export type WorkerDispatchRequest = {
77
- taskId: Id;
78
- workerId: Id;
79
- expectedRevision: Revision;
80
- expectedWorkspaceRevision: Revision;
81
- observation: unknown;
82
- };
83
-
84
- export type WorkerDispatchCommit = WorkerDispatchRequest & {
85
- dispatch: WorkerDispatch;
86
- outcome: "started" | "not_started";
87
- session: HostSession | null;
88
- };
89
-
90
- const same = (left: unknown, right: unknown): boolean => {
91
- try {
92
- return canonicalJson(left) === canonicalJson(right);
93
- } catch {
94
- return false;
95
- }
96
37
  };
97
38
 
98
39
  /** The one shared definition of active or uncertain worker state used by
@@ -103,97 +44,10 @@ export const isUncertainWorker = (state: WorkerState): boolean =>
103
44
 
104
45
  /**
105
46
  * Lifecycle gate table: which worker states block each lead transition.
106
- * Pause freezes, so live workers never block it (writer release stays a
107
- * separate explicit step); resume, revise, and close must reconcile first.
47
+ * Pause freezes, so live workers never block it; resume, revise, and close
48
+ * must reconcile first.
108
49
  */
109
50
  export const workerBlocksTransition = (
110
51
  transition: "pause" | "resume" | "revise" | "close",
111
52
  state: WorkerState,
112
53
  ): boolean => (transition === "pause" ? false : isUncertainWorker(state));
113
-
114
- const callerSession = (caller: CallerContext): HostSession =>
115
- caller.session ?? { kind: "host", host: caller.host, handle: caller.actor };
116
-
117
- const validPath = (value: unknown): value is string =>
118
- typeof value === "string" &&
119
- value.length > 0 &&
120
- !Array.from(value).some((char) => {
121
- const code = char.charCodeAt(0);
122
- return code < 32 || code === 127;
123
- }) &&
124
- !value.split(/[\\/]/).includes("..");
125
-
126
- const callerValue = (value: CallerContext): Caller => ({ host: value.host, actor: value.actor });
127
- const workerIdOf = (value: CallerContext): Id | null => value.workerId ?? null;
128
-
129
- export type ProductWriteInput = {
130
- task: TaskRecord;
131
- workspace: WorkspaceRecord;
132
- caller: CallerContext;
133
- paths: string[];
134
- store?: TaskStore;
135
- };
136
-
137
- export const currentWriterOwnsTask = (
138
- task: TaskRecord,
139
- workspace: WorkspaceRecord,
140
- caller: { host: string; actor: string },
141
- ): boolean => {
142
- const owner = workspace.writer?.owner;
143
- return (
144
- task.status === "active" &&
145
- task.workspaceId === workspace.id &&
146
- owner?.taskId === task.id &&
147
- owner.session.host === caller.host &&
148
- owner.session.handle === caller.actor
149
- );
150
- };
151
-
152
- /** Check the authoritative workspace owner and the caller's role. File paths
153
- * are host-policy territory: only malformed paths are rejected here, never
154
- * out-of-scope ones. */
155
- export function assertProductWriteAllowed(input: ProductWriteInput): Result<Owner> {
156
- if (!Array.isArray(input.paths) || input.paths.some((path) => !validPath(path)))
157
- return failure("invalid_input", "invalid product write path");
158
- if (!input.store) return failure("permission_denied", "write authorization requires core state");
159
- const task = input.store.readTask(input.task.id);
160
- if (!task.ok) return task;
161
- const workspace = input.store.readWorkspace();
162
- if (!workspace.ok) return workspace;
163
- if (!workspace.data) return failure("not_found", "workspace not found");
164
- const workspaceRecord = workspace.data;
165
- if (task.data.status !== "active")
166
- return failure("invalid_transition", "paused or closed tasks cannot own product writes");
167
- if (workspaceRecord.id !== task.data.workspaceId || workspaceRecord.root.length === 0)
168
- return failure("recovery_required", "task and workspace bindings are invalid");
169
- const writer = workspaceRecord.writer;
170
- if (!writer) return failure("permission_denied", "checkout has no writer owner");
171
- if (writer.state === "uncertain")
172
- return failure("recovery_required", "checkout writer requires recovery", {
173
- owner: writer.owner,
174
- });
175
- const caller = callerValue(input.caller);
176
- const workerId = workerIdOf(input.caller);
177
- const owner = writer.owner;
178
- if (
179
- owner.taskId !== task.data.id ||
180
- owner.workerId !== workerId ||
181
- owner.session.host !== caller.host ||
182
- owner.session.handle !== caller.actor
183
- )
184
- return failure("writer_conflict", "checkout is owned by another validated actor", { owner });
185
- const worker =
186
- workerId === null ? null : task.data.workers.find((entry) => entry.id === workerId);
187
- if (workerId !== null && (!worker || worker.data.assignment.role !== "implementer"))
188
- return failure("permission_denied", "only implementers can own product writes");
189
- if (
190
- workerId !== null &&
191
- (!worker?.data.session ||
192
- worker.data.state === "cancelling" ||
193
- worker.data.state === "unknown" ||
194
- worker.data.state === "stopped" ||
195
- !same(worker.data.session, callerSession(input.caller)))
196
- )
197
- return failure("recovery_required", "worker session is not an active writer");
198
- return success(null, workspaceRecord.revision, owner);
199
- }
@@ -117,7 +117,22 @@ const workspaceConfigSchema = z
117
117
  })
118
118
  .passthrough()
119
119
  .optional(),
120
+ /** Legacy (≤4.x) standing approvals; read once as autonomy grants (autonomy.ts). */
120
121
  autoApprove: z.union([z.boolean(), z.array(nonBlank)]).optional(),
122
+ /** Where an unnamed request stops (skills read it); same rules as grants. */
123
+ defaultEndpoint: z.enum(["commit", "pr"]).optional(),
124
+ /** Autonomy grants (D15): only ever set here, by the user. */
125
+ autonomy: z
126
+ .object(
127
+ Object.fromEntries(
128
+ ["push", "pr", "merge", "release", "rerun"].map((kind) => [
129
+ kind,
130
+ z.union([z.boolean(), z.literal("verified")]).optional(),
131
+ ]),
132
+ ),
133
+ )
134
+ .passthrough()
135
+ .optional(),
121
136
  youtrack: z
122
137
  .object({ baseUrl: nonBlank.optional(), link_issues: z.boolean().optional() })
123
138
  .passthrough()
@@ -0,0 +1,92 @@
1
+ // Idempotent YouTrack writes for `workit youtrack note|time|meeting` (S16
2
+ // review M4). Each comment or work item carries a deterministic marker,
3
+ // `<!-- workit-action:<sha256> -->`, derived from the request. Before writing,
4
+ // the issue is read back and a write whose marker is already present is
5
+ // skipped (`already_done`); after a write with an unknown outcome (a timeout
6
+ // after the server applied it), the read-back settles it instead of a retry
7
+ // posting a duplicate.
8
+ import { sha256 } from "./task-contract";
9
+ import { youTrackRequest, youTrackToken } from "./youtrack";
10
+
11
+ export type MarkerWhere = "comments" | "workItems";
12
+
13
+ export const youTrackMarker = (request: Record<string, unknown>): string | null => {
14
+ const creds = youTrackToken();
15
+ if ("error" in creds) return null;
16
+ return `<!-- workit-action:${sha256({ ...request, baseUrl: creds.base })} -->`;
17
+ };
18
+
19
+ export const withMarker = (text: string, marker: string): string =>
20
+ `${text}${text ? "\n\n" : ""}${marker}`;
21
+
22
+ const PAGE = 500;
23
+
24
+ /** true/false when the read-back is conclusive, null when it is not. */
25
+ export async function markerPresent(
26
+ issueId: string,
27
+ marker: string,
28
+ where: MarkerWhere,
29
+ ): Promise<boolean | null> {
30
+ const creds = youTrackToken();
31
+ if ("error" in creds) return null;
32
+ const endpoint =
33
+ where === "comments"
34
+ ? `/api/issues/${encodeURIComponent(issueId)}/comments?fields=id,text,deleted`
35
+ : `/api/issues/${encodeURIComponent(issueId)}/timeTracking/workItems?fields=id,text`;
36
+ const result = await youTrackRequest(`${creds.base}${endpoint}&$top=${PAGE}&$skip=0`, {
37
+ method: "GET",
38
+ token: creds.token,
39
+ });
40
+ if (result.status !== 0) return null;
41
+ try {
42
+ const items = JSON.parse(result.stdout) as unknown;
43
+ if (!Array.isArray(items)) return null;
44
+ const found = items.some(
45
+ (item) =>
46
+ item &&
47
+ typeof item === "object" &&
48
+ (item as { deleted?: unknown }).deleted !== true &&
49
+ typeof (item as { text?: unknown }).text === "string" &&
50
+ (item as { text: string }).text.includes(marker),
51
+ );
52
+ // A full page may hide an older match: only a hit is conclusive then.
53
+ return found ? true : items.length < PAGE ? false : null;
54
+ } catch {
55
+ return null;
56
+ }
57
+ }
58
+
59
+ type WriteResult = { ok: boolean; error?: string | null; data?: unknown };
60
+
61
+ export type OnceOutcome =
62
+ | {
63
+ status: "done" | "already_done" | "settled";
64
+ data?: unknown;
65
+ /** The pre-write read-back could not rule out an earlier copy (>1 page). */
66
+ precheck?: "inconclusive";
67
+ }
68
+ | { status: "failed" | "unknown"; error: string; data?: unknown };
69
+
70
+ /** Write at most once: skip when the marker is there, read back an unknown outcome. */
71
+ export async function writeOnce(
72
+ issueId: string,
73
+ marker: string,
74
+ where: MarkerWhere,
75
+ write: () => Promise<WriteResult>,
76
+ ): Promise<OnceOutcome> {
77
+ const before = await markerPresent(issueId, marker, where);
78
+ if (before === true) return { status: "already_done" };
79
+ const precheck = before === null ? { precheck: "inconclusive" as const } : {};
80
+ const result = await write();
81
+ if (result.ok) return { status: "done", data: result.data, ...precheck };
82
+ const outcome = (result.data as { outcome?: unknown } | undefined)?.outcome;
83
+ if (outcome === "not_applied")
84
+ return { status: "failed", error: result.error ?? "not applied", data: result.data };
85
+ const settled = await markerPresent(issueId, marker, where);
86
+ if (settled === true) return { status: "settled", ...precheck };
87
+ return {
88
+ status: settled === false ? "failed" : "unknown",
89
+ error: result.error ?? "YouTrack write failed",
90
+ data: result.data,
91
+ };
92
+ }
package/src/core.ts CHANGED
@@ -24,8 +24,6 @@ export {
24
24
  candidateDigest,
25
25
  requirementId,
26
26
  rewriteRecordRefs,
27
- BINDING_QUESTION_BUDGET,
28
- workitBindingQuestionIssue,
29
27
  } from "./core/task-contract";
30
28
  export type {
31
29
  OperationFamily,
@@ -74,76 +72,7 @@ export type { MethodId, SelectedMethod } from "./core/methods";
74
72
  export { shellBranchPolicyViolation, shellBranchTarget } from "./core/route-intent";
75
73
  export { classifyBranchDirt } from "./core/branch";
76
74
  export type { BranchDirt } from "./core/branch";
77
- export {
78
- AUTO_CLASSES,
79
- autoApproves,
80
- operationAutoClass,
81
- resolveAutoApproval,
82
- standingApprovalLive,
83
- standingAutoApplies,
84
- standingAutoBinding,
85
- standingReceiptFor,
86
- verifyStandingApproval,
87
- } from "./core/auto-approval";
88
- export type {
89
- AutoApproval,
90
- AutoClass,
91
- StandingAutoBinding,
92
- StandingReceipt,
93
- } from "./core/auto-approval";
94
75
  export { bundleHashOfFile, isEphemeralCachePath, sha256Hex } from "./core/runtime-identity";
95
- export { applicableDecision, reserveAction, settleAction, reconcileAction } from "./core/authority";
96
- export { normalizeChainSteps, chainStepKey } from "./core/authority";
97
- export type { ChainStep } from "./core/authority";
98
- export {
99
- createAuthorizedExternalActionRunner,
100
- runAuthorizedExternalAction,
101
- } from "./core/external-action";
102
- export type {
103
- AuthorizedActionInput,
104
- ExternalActionResult,
105
- ExternalActionRunner,
106
- ExternalActionEffect,
107
- ExternalActionBinding,
108
- NativeExternalActionObservation,
109
- ExternalActionRequest,
110
- ExternalActionOperation,
111
- } from "./core/external-action";
112
- export {
113
- approvedBranchSetupIntent,
114
- approvedChainStep,
115
- approvedExternalAction,
116
- approvedPlanCommit,
117
- branchSetupIntent,
118
- chainStepBinding,
119
- planCommitDescriptor,
120
- planCommitBinding,
121
- commitMessageFromDescriptor,
122
- externalActionState,
123
- priorExternalAction,
124
- priorResolvedDrift,
125
- externalActionRef,
126
- externalActionDescriptor,
127
- externalActionHelp,
128
- externalActionJsonSchema,
129
- externalActionSchema,
130
- planReservationLength,
131
- externalActionRequest,
132
- matchesNativeExternalAction,
133
- nativeExternalActionObservation,
134
- readNativeExternalActionObservation,
135
- } from "./core/external-action";
136
- export type {
137
- ActionReservation,
138
- NativeAuthorityContext,
139
- NativeAuthorityVerifier,
140
- NativeActionVerification,
141
- NativeReconciliationVerification,
142
- NativeDecisionVerification,
143
- ReserveActionInput,
144
- SettleActionInput,
145
- ReconcileActionInput,
146
- } from "./core/authority";
147
76
  export {
148
77
  captureCandidate,
149
78
  checkPin,
@@ -156,20 +85,14 @@ export {
156
85
  export type { CandidateEnvironment, ClosureEvaluation } from "./core/task-evaluation";
157
86
  export { WorkitCore } from "./core/task-engine";
158
87
  export type { OperationContext } from "./core/task-engine";
159
- export { assertProductWriteAllowed } from "./core/workers";
160
88
  export type {
161
- CallerContext,
162
89
  HostSession,
163
- NativeWorkerDispatchVerification,
164
90
  NativeWorkerObservation,
165
91
  NativeWorkerVerification,
166
92
  NativeWorkerVerifier,
167
- ProductWriteInput,
168
- WorkerDispatch,
169
- WorkerDispatchCommit,
170
- WorkerDispatchRequest,
171
- WorkerDispatchStage,
172
93
  } from "./core/workers";
94
+ export { contextReadJsonSchema, parseContextRead, readExternalContext } from "./core/context-read";
95
+ export type { ContextReadPayload } from "./core/context-read";
173
96
 
174
97
  export type Result<T> =
175
98
  | { ok: true; data: T; error: null }