openpond-sdk 0.12.8 → 0.12.9

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.
@@ -76,15 +76,46 @@ publication, evidence and feedback, grade/review decisions, batch sealing and
76
76
  iteration reserve/cancel/retry. Reuse released resources and exact revision refs;
77
77
  the SDK does not execute Python-authored graders or invent another scheduler.
78
78
 
79
- `npx tsx examples/training/continual.ts inspect policy-ref.json inspection.json`
80
- reads current admission/budget without reserving an iteration. To execute one
81
- reviewed existing command, use `command command.json response.json`. The input
82
- must contain its stable operation ID and all schema-required scope-independent
83
- release references, expected revisions and limits. A reserve command is an
84
- explicit action, not a side effect of inspection. Retain the resulting iteration
85
- and job references, observe them through the same public reads, and use existing
86
- cancel/retry controls for recovery. Neither example changes recurring policy
87
- settings merely by opening or inspecting them.
79
+ The TS example `examples/training/continual.ts` and native Python example
80
+ `python/openpond-sdk/examples/continual.py` expose the same explicit steps:
81
+
82
+ 1. `inspect policy-ref.json inspection.json` checks the exact released policy.
83
+ Use existing reviewed source/definition/reward/binding resources and approved
84
+ evidence; `command command.json response.json` may publish/review/seal only
85
+ the explicitly supplied typed command. These are writes, and grade/reward
86
+ checks can incur model cost. They are never inferred from opening a policy.
87
+ 2. `reserve reserve-command.json reserved.json` sends one existing
88
+ `reserve_iteration` command with a stable operationId, pinned policy
89
+ `{id, revision, contentHash}` and manual trigger `{kind:"manual", identity}`.
90
+ This is the explicit paid-admission boundary: host workers may dispatch it
91
+ automatically. The policy must already contain the reviewed configuration,
92
+ batch/evidence eligibility and finite budget; the example never enables it,
93
+ changes its limits or accepts/promotes a candidate automatically.
94
+ 3. If the reply is lost, `reconcile reserve-command.json reconciled.json` reads
95
+ that policy's chain and scans at most10 pages of100 reservations for the SAME
96
+ pinned policy/manual trigger. It never issues another reservation. A missing
97
+ result is UNKNOWN, not permission to submit a new operation/trigger.
98
+ 4. `status reserved.json status.json` reloads the retained iteration, lists only
99
+ its dispatch, refreshes the iteration once and checks policy/execution identity
100
+ before reading its training job. Inconsistent snapshots require another read,
101
+ never a mutation. Reinvoke explicitly
102
+ with the latest receipt; there is no unbounded polling. `results status.json
103
+ results.json` requires settled dispatch plus authoritative terminal cleanup
104
+ receipt, then reads retained training outputs. Only cancellation before initial
105
+ submission may return a terminal record without a job. A candidate_ready result is not
106
+ accepted or served, and failed/cancelled terminal records are not success.
107
+ 5. For `cancel` or `retry`, add a new stable `operationId` to a retained response.
108
+ Each reloads current iteration revision; retry additionally requires the SAME
109
+ blocked dispatch. The host handles cancellation by dispatch identity, including
110
+ uncertain initial submission. Read status/results until authoritative terminal
111
+ receipt is observed. Local request abort never claims remote cancellation.
112
+
113
+ Each invocation has a120-second bound, exclusive private output and retains exact
114
+ iteration/policy/dispatch/job references. Reservation reads cannot manufacture
115
+ proof of actual execution. This declares existing manual-trigger continual
116
+ iterations; schedule/approved-count automation and Python-authored graders are
117
+ not demonstrated by this example. One ordinary external consumer must still run
118
+ the reviewed hosted sequence after platform security qualification.
88
119
 
89
120
  ## Proof and release
90
121
 
@@ -95,4 +126,7 @@ lost-reply reconciliation, pagination and real cancellation/recovery remain
95
126
  integration gates. Examples and source/package checks do not establish a real
96
127
  GPU result. Actual hosted GRPO/reward-model execution uses a separately bounded
97
128
  approved workflow and retains exact version/SHA/input/result/cleanup evidence.
98
- No package has been published by these changes.
129
+ The original `openpond-sdk@0.12.8` is published and independently verified.
130
+ The source follow-up prepares coordinated SDK/Ponder0.12.9; it does not claim
131
+ that version is published. Ponder npm and Python first-name publication remain separate publisher/bootstrap
132
+ gates; clean packed consumers do not establish registry ownership.
@@ -1,19 +1,81 @@
1
1
  import { readFile, writeFile } from "node:fs/promises";
2
- import { LearningCommandSchema, LearningRevisionRefSchema, OpenPondLearningClient } from "openpond-sdk/learning";
2
+ import { LearningCommandSchema, LearningIterationSchema, LearningRevisionRefSchema, OpenPondLearningClient } from "openpond-sdk/learning";
3
+ import { createTrainingClient } from "openpond-sdk/training";
3
4
 
5
+ // Each invocation performs one explicit transition or one finite read snapshot.
4
6
  const [action, inputPath, outputPath] = process.argv.slice(2);
5
- if (!["inspect", "command"].includes(action ?? "") || !inputPath || !outputPath) throw new Error("Usage: continual.ts <inspect|command> <policy-ref-or-command.json> <new-response.json>");
7
+ if (!["inspect", "command", "reserve", "reconcile", "status", "results", "cancel", "retry"].includes(action ?? "") || !inputPath || !outputPath) throw new Error("Usage: continual.ts <inspect|command|reserve|reconcile|status|results|cancel|retry> <input.json> <new-response.json>");
6
8
  const { OPENPOND_API_URL: baseUrl, OPENPOND_API_KEY: apiKey, OPENPOND_TEAM_ID: scope } = process.env;
7
9
  if (!baseUrl || !apiKey || !scope) throw new Error("Set OPENPOND_API_URL, OPENPOND_API_KEY and OPENPOND_TEAM_ID.");
8
10
  const bytes = await readFile(inputPath);
9
11
  if (bytes.byteLength > 16_777_216) throw new Error("Learning input exceeds the protocol limit.");
10
12
  const input: unknown = JSON.parse(bytes.toString("utf8"));
11
13
  const client = new OpenPondLearningClient({ baseUrl, apiKey, scope });
12
- const options = { signal: AbortSignal.timeout(120_000) };
13
- // The same typed command covers existing publish/review/seal/reserve/cancel
14
- // operations. Preserve operationId and pinned releases on uncertain responses.
15
- const result = action === "inspect"
16
- ? await client.inspectPolicy(LearningRevisionRefSchema.parse(input), options)
17
- : await client.command(LearningCommandSchema.parse(input), options);
14
+ const signal = AbortSignal.timeout(120_000);
15
+ const options = { signal };
16
+ const training = createTrainingClient({ baseUrl, headers: { Authorization: `Bearer ${apiKey}`, "X-OpenPond-Team-Id": scope }, fetch: (url, init) => fetch(url, { ...init, signal: init?.signal ? AbortSignal.any([signal, init.signal]) : signal, redirect: "error" }) });
17
+ let result: unknown;
18
+ if (action === "inspect") result = await client.inspectPolicy(LearningRevisionRefSchema.parse(input), options);
19
+ else if (action === "reconcile") {
20
+ const command = LearningCommandSchema.parse(input);
21
+ if (command.action !== "reserve_iteration" || command.trigger.kind !== "manual") throw new Error("Reconcile the original manual reservation command.");
22
+ const triggerIdentity = command.trigger.identity;
23
+ const inspection = await client.inspectPolicy(command.policy, options);
24
+ if (!inspection.chain) throw new Error("No chain observed; do not create another reservation automatically.");
25
+ let afterId: string | undefined;
26
+ let resultFound = false;
27
+ const seen = new Set<string>();
28
+ for (let pageIndex = 0; pageIndex < 10; pageIndex++) {
29
+ const page = await client.list("reservation", { parentId: inspection.chain.id, limit: 100, ...(afterId ? { afterId } : {}) }, options);
30
+ const reservation = page.items.find((item) => item.policy.id === command.policy.id && item.policy.revision === command.policy.revision && item.policy.contentHash === command.policy.contentHash && item.trigger.kind === "manual" && item.trigger.identity === triggerIdentity);
31
+ if (reservation) {
32
+ const iteration = await client.get("iteration", reservation.iterationId, undefined, options);
33
+ result = { reservation, iteration }; resultFound = true; break;
34
+ }
35
+ if (!page.nextCursor) break;
36
+ if (seen.has(page.nextCursor)) throw new Error("Repeated reservation cursor.");
37
+ seen.add(page.nextCursor); afterId = page.nextCursor;
38
+ }
39
+ if (!resultFound) throw new Error("Reservation not observed within finite scan; preserve original command and reconcile with operator before any replay.");
40
+ } else if (action === "command" || action === "reserve") {
41
+ const command = LearningCommandSchema.parse(input);
42
+ if ((action === "reserve") !== (command.action === "reserve_iteration")) throw new Error("Use explicit reserve for reserve_iteration; command is only for reviewed resource/evidence transitions.");
43
+ if (command.action === "reserve_iteration" && command.trigger.kind !== "manual") throw new Error("This example declares manual-trigger continual iterations only.");
44
+ // Reservation may be dispatched automatically by the host and incur compute.
45
+ // The original policy/trigger/operationId is the lost-reply reconciliation key.
46
+ const operation = await client.command(command, options);
47
+ const iteration = operation.resources.find((resource) => resource.schemaVersion === "openpond.learningIteration.v1");
48
+ result = { operation, ...(iteration ? { iteration } : {}) };
49
+ } else {
50
+ if (!input || typeof input !== "object" || !("iteration" in input)) throw new Error("Use a retained response containing iteration; never invent a replacement reservation.");
51
+ const retained = LearningIterationSchema.parse(input.iteration);
52
+ let iteration = await client.get("iteration", retained.id, undefined, options);
53
+ if (iteration.id !== retained.id || (iteration.policy.id !== retained.policy.id || iteration.policy.revision !== retained.policy.revision || iteration.policy.contentHash !== retained.policy.contentHash) || iteration.dispatchId !== retained.dispatchId) throw new Error("Iteration binding changed.");
54
+ if (action === "cancel" || action === "retry") {
55
+ if (!("operationId" in input) || typeof input.operationId !== "string") throw new Error("Add a new stable control operationId to the retained response before dispatch.");
56
+ if (action === "retry") {
57
+ const dispatch = await client.get("dispatch", iteration.dispatchId, undefined, options);
58
+ if (dispatch.iterationId !== iteration.id || dispatch.state !== "blocked") throw new Error("Only the same blocked dispatch can be retried.");
59
+ }
60
+ const command = LearningCommandSchema.parse({ action: action === "cancel" ? "cancel_iteration" : "retry_iteration_dispatch", operationId: input.operationId, iterationId: iteration.id, expectedRevision: iteration.revision });
61
+ const operation = await client.command(command, options);
62
+ result = { operation, iteration: operation.resources.find((resource) => resource.schemaVersion === "openpond.learningIteration.v1") ?? iteration };
63
+ } else {
64
+ // Empty list is legitimate before the worker creates this dispatch.
65
+ const page = await client.list("dispatch", { parentId: iteration.id, limit: 2 }, options);
66
+ if (page.nextCursor || page.items.length > 1 || page.items.some((item) => item.id !== iteration.dispatchId)) throw new Error("Ambiguous dispatch identity.");
67
+ const dispatch = page.items[0] ?? null;
68
+ // Separate HTTP reads can straddle the worker's atomic commit. Refresh once.
69
+ iteration = await client.get("iteration", retained.id, undefined, options);
70
+ if (iteration.policy.id !== retained.policy.id || iteration.policy.revision !== retained.policy.revision || iteration.policy.contentHash !== retained.policy.contentHash || iteration.dispatchId !== retained.dispatchId) throw new Error("Iteration binding changed.");
71
+ if (dispatch && (dispatch.iterationId !== iteration.id || dispatch.policy.id !== iteration.policy.id || dispatch.policy.revision !== iteration.policy.revision || dispatch.policy.contentHash !== iteration.policy.contentHash || dispatch.execution?.id !== iteration.trainingJob?.id || dispatch.execution?.contentHash !== iteration.trainingJob?.contentHash)) throw new Error("Snapshot changed; read status again without mutation.");
72
+ if (!dispatch && iteration.trainingJob) throw new Error("Snapshot changed; read status again.");
73
+ const job = iteration.trainingJob ? await training.getJob(iteration.trainingJob.id) : null;
74
+ if (job && (job.id !== iteration.trainingJob?.id || job.submissionHash !== iteration.trainingJob?.contentHash)) throw new Error("Training execution content identity mismatch.");
75
+ if (action === "results" && (!dispatch || dispatch.state !== "settled" || !dispatch.terminalReceipt)) throw new Error("No settled authoritative execution/cleanup receipt yet; retain status and inspect again.");
76
+ if (action === "results" && !job && (iteration.status !== "cancelled" || dispatch?.execution)) throw new Error("Terminal job not observed.");
77
+ result = { iteration, dispatch, job, ...(action === "results" && job ? { outputs: await training.outputs(job.id) } : {}) };
78
+ }
79
+ }
18
80
  await writeFile(outputPath, JSON.stringify(result, null, 2) + "\n", { flag: "wx", mode: 0o600 });
19
- console.log("Saved the retained learning response. Inspection does not reserve an iteration; command execution uses its explicit existing approval/budget contract.");
81
+ console.log("Saved one retained transition/snapshot. Timeout does not cancel remote work; reconcile the same iteration before any explicit retry.");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "openpond-sdk",
3
- "version": "0.12.8",
3
+ "version": "0.12.9",
4
4
  "description": "Server-side TypeScript SDK for OpenPond sandboxes and agentic work",
5
5
  "license": "MIT",
6
6
  "type": "module",