@op0ai/sdk 0.1.0

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 (71) hide show
  1. package/LICENSE +38 -0
  2. package/README.md +28 -0
  3. package/dist/agent.d.ts +23 -0
  4. package/dist/agent.d.ts.map +1 -0
  5. package/dist/agent.js +72 -0
  6. package/dist/agent.js.map +1 -0
  7. package/dist/agui.d.ts +87 -0
  8. package/dist/agui.d.ts.map +1 -0
  9. package/dist/agui.js +22 -0
  10. package/dist/agui.js.map +1 -0
  11. package/dist/config.d.ts +76 -0
  12. package/dist/config.d.ts.map +1 -0
  13. package/dist/config.js +38 -0
  14. package/dist/config.js.map +1 -0
  15. package/dist/data.d.ts +168 -0
  16. package/dist/data.d.ts.map +1 -0
  17. package/dist/data.js +211 -0
  18. package/dist/data.js.map +1 -0
  19. package/dist/define.d.ts +30 -0
  20. package/dist/define.d.ts.map +1 -0
  21. package/dist/define.js +87 -0
  22. package/dist/define.js.map +1 -0
  23. package/dist/distill.d.ts +68 -0
  24. package/dist/distill.d.ts.map +1 -0
  25. package/dist/distill.js +113 -0
  26. package/dist/distill.js.map +1 -0
  27. package/dist/events.d.ts +39 -0
  28. package/dist/events.d.ts.map +1 -0
  29. package/dist/events.js +25 -0
  30. package/dist/events.js.map +1 -0
  31. package/dist/evlog.d.ts +71 -0
  32. package/dist/evlog.d.ts.map +1 -0
  33. package/dist/evlog.js +124 -0
  34. package/dist/evlog.js.map +1 -0
  35. package/dist/guards.d.ts +61 -0
  36. package/dist/guards.d.ts.map +1 -0
  37. package/dist/guards.js +88 -0
  38. package/dist/guards.js.map +1 -0
  39. package/dist/hash.d.ts +6 -0
  40. package/dist/hash.d.ts.map +1 -0
  41. package/dist/hash.js +18 -0
  42. package/dist/hash.js.map +1 -0
  43. package/dist/index.d.ts +120 -0
  44. package/dist/index.d.ts.map +1 -0
  45. package/dist/index.js +175 -0
  46. package/dist/index.js.map +1 -0
  47. package/dist/platform.d.ts +97 -0
  48. package/dist/platform.d.ts.map +1 -0
  49. package/dist/platform.js +10 -0
  50. package/dist/platform.js.map +1 -0
  51. package/dist/session.d.ts +47 -0
  52. package/dist/session.d.ts.map +1 -0
  53. package/dist/session.js +88 -0
  54. package/dist/session.js.map +1 -0
  55. package/dist/source.d.ts +65 -0
  56. package/dist/source.d.ts.map +1 -0
  57. package/dist/source.js +96 -0
  58. package/dist/source.js.map +1 -0
  59. package/dist/tool.d.ts +43 -0
  60. package/dist/tool.d.ts.map +1 -0
  61. package/dist/tool.js +16 -0
  62. package/dist/tool.js.map +1 -0
  63. package/dist/usectx.d.ts +93 -0
  64. package/dist/usectx.d.ts.map +1 -0
  65. package/dist/usectx.js +220 -0
  66. package/dist/usectx.js.map +1 -0
  67. package/dist/vault.d.ts +21 -0
  68. package/dist/vault.d.ts.map +1 -0
  69. package/dist/vault.js +25 -0
  70. package/dist/vault.js.map +1 -0
  71. package/package.json +75 -0
package/dist/index.js ADDED
@@ -0,0 +1,175 @@
1
+ // @op0-ai/cord — the H2–H5 cord runtime + authoring SDK. Still PURE TS, zero deps — it runs in plain Node
2
+ // (the verify path, no npm) AND inside a Worker. The production runtime is Flue (agent = a Durable Object)
3
+ // on Pi on the CF Agents SDK (ADR-1/2/3); this shim implements the SAME authoring surface
4
+ // (defineCord / use / guards / expose / run) plus the H2–H5 run pipeline, so a cord authored here ports to
5
+ // the real runtime unchanged.
6
+ //
7
+ // H2 exposure — `expose:{codemode,mcp,cli,openapi}` is carried into the generated manifest + the
8
+ // Worker's /mcp + /openapi.json. Code-mode is hosted by executor (not self-hosted).
9
+ // H3 vault write — after the gate redacts, the run persists its deliverable via the Vault seam.
10
+ // H4 deepsec gate — scope check (deny off-allow-list) + redaction + a hash-chained audit, per run.
11
+ // H5 cycle-guard — every sub-cord call reserves against a shared RunBudget (cap + maxDepth) so a
12
+ // + publish/fork composition can't recurse/spend without bound; `manifest`/`wellKnown` emit the
13
+ // forkable cord.json + /.well-known/op0.json (ADR-18/25).
14
+ export * from "./hash.js";
15
+ export * from "./vault.js";
16
+ export * from "./guards.js";
17
+ export * from "./platform.js";
18
+ export * from "./agent.js";
19
+ // Governed-CF-Agent surface (2026-06-30 reconciliation): the typed bidirectional Source, the AG-UI event layer
20
+ // (wire + observability), and the typed governed-tool port. The CF binding (define.ts) + thin typed client land next.
21
+ export * from "./source.js";
22
+ export * from "./agui.js";
23
+ export * from "./tool.js";
24
+ export * from "./config.js";
25
+ export * from "./define.js";
26
+ // Vendor adapters (gatewayModel, toAiTools, runCordChat) live in @op0-ai/cord-ai — core stays vendor-free (dep-lint enforced).
27
+ // evlog lane (2026-06-30): wide-events → Analytics Engine + a tamper-evident, DO-persisted audit chain, tied to
28
+ // the run via cordObserver. The ops+audit sibling of the AG-UI timeline (same runId).
29
+ export * from "./evlog.js";
30
+ // host ledger contract (C1.1): the ask-event defined ONCE — host builds it, sink validates it, meter counts it.
31
+ export * from "./events.js";
32
+ // usectx (2026-06-30): the context-acquisition seam — acquire(target) => FileSet, routed via connections
33
+ // (github/executor/http/fs/custom) with in-band redaction + audit. The READ-side Source (sibling to source.ts's write side).
34
+ export * from "./usectx.js";
35
+ // skill-distiller (2026-06-30): sessions → reusable skills (the distillation flywheel). One Session envelope over
36
+ // cord evlog sessions + coding-agent (lapdog/trajectory) sessions; distill(session, note) → SkillDraft → sink.
37
+ export * from "./distill.js";
38
+ // DataDO data-plane (2026-06-30): Convex-style query/mutation/action over a DO's single-writer store + the reactive
39
+ // engine (read-set tracking → recompute only intersecting subscriptions → push full result + version). onloop app data.
40
+ export * from "./data.js";
41
+ import { hashHex, slug, newId } from "./hash.js";
42
+ import { makeAuditChain } from "./guards.js";
43
+ /** Mount an OWNED sub-cord in-process (deterministic trunk). */
44
+ export function use(sc) {
45
+ return { id: sc.id, mount: "local", run: sc.run };
46
+ }
47
+ /** Mount a THIRD-PARTY/registry cord over MCP (the trust boundary; an executor toolkit). */
48
+ export function useRemote(sc) {
49
+ return { id: sc.id, mount: "registry", run: sc.call };
50
+ }
51
+ const now = () => (typeof performance !== "undefined" ? performance.now() : Date.now());
52
+ const errOf = (e) => ({ code: "cord.run", why: e instanceof Error ? e.message : String(e) });
53
+ export function defineCord(def) {
54
+ const guards = def.guards ?? [];
55
+ const expose = def.expose ?? { codemode: true };
56
+ const manifest = {
57
+ id: def.id, version: "0.1.0",
58
+ kind: def.kind ?? "agent-ful", inference: def.inference ?? "managed",
59
+ ...(def.scope !== undefined ? { scope: def.scope } : {}), expose,
60
+ subcords: Object.values(def.subcords ?? {}).map((s) => s.id),
61
+ guards: guards.map((g) => g.name),
62
+ needs: def.needs ?? [],
63
+ };
64
+ const runFull = async (input, opts = {}) => {
65
+ const runId = newId();
66
+ const tenantId = opts.tenantId ?? "local";
67
+ const scope = opts.scope ?? def.scope;
68
+ const budget = { cap: opts.budget?.cap ?? 64, used: 0, depth: 0, maxDepth: opts.budget?.maxDepth ?? 8 };
69
+ const events = [];
70
+ const chain = makeAuditChain();
71
+ const emit = (e) => { events.push(e); opts.onEvent?.(e); for (const g of guards)
72
+ g.onEvent?.(e); };
73
+ const span = (kind, name, ms, ok, error, attrs) => ({
74
+ ts: new Date().toISOString(), runId, tenantId, cordId: def.id, kind, name, durationMs: Math.round(ms), ok,
75
+ ...(scope !== undefined ? { scope } : {}),
76
+ ...(error !== undefined ? { error } : {}),
77
+ ...(attrs !== undefined ? { attrs } : {}),
78
+ });
79
+ // H4 — gate: deepsec authorizes the run scope (throws if off-allow-list), then audit the authorization.
80
+ // Guards run in the author-declared order (def.guards) and any may throw to refuse the run — sequential
81
+ // by design (a later guard should see the run as already vetted by earlier ones, not race them).
82
+ const gev = { cord: def.id, ...(scope !== undefined ? { scope } : {}), input };
83
+ for (const g of guards) {
84
+ // oxlint-disable-next-line no-await-in-loop
85
+ await g.onCall?.(gev);
86
+ }
87
+ chain.append({ actor: "deepsec", action: "run.authorize", scopeId: scope ?? "", payload: { cord: def.id, tenantId } });
88
+ // Resolve host-bound resources (the adapter model). opts.vault is a shorthand for resources.vault. A cord
89
+ // that declares `needs` fails fast here if the host bound none — a missing binding is a deploy bug, not a
90
+ // silent no-op. (Trust boundary: validate, never simplify away — ponytail.)
91
+ const res = { ...opts.resources };
92
+ if (opts.vault && !res.vault)
93
+ res.vault = opts.vault;
94
+ for (const need of def.needs ?? []) {
95
+ if (!res[need])
96
+ throw new Error(`cord ${def.id}: required resource "${need}" not bound by the host`);
97
+ }
98
+ // H5 — wrap each sub-cord with the cycle-guard (budget + maxDepth) and a per-step wide-event.
99
+ const ctx = {};
100
+ for (const key of Object.keys(def.subcords ?? {})) {
101
+ const sc = def.subcords[key];
102
+ if (!sc)
103
+ continue;
104
+ ctx[key] = async (i) => {
105
+ if (budget.depth >= budget.maxDepth)
106
+ throw new Error(`cord: cycle-guard tripped (maxDepth ${budget.maxDepth}) at ${sc.id}`);
107
+ if (budget.used + 1 > budget.cap)
108
+ throw new Error(`cord: run budget exhausted (cap ${budget.cap}) at ${sc.id}`);
109
+ budget.used += 1;
110
+ budget.depth += 1;
111
+ const s0 = now();
112
+ try {
113
+ const out = await sc.run(i);
114
+ emit(span("step", sc.id, now() - s0, true));
115
+ return out;
116
+ }
117
+ catch (e) {
118
+ emit(span("step", sc.id, now() - s0, false, errOf(e)));
119
+ throw e;
120
+ }
121
+ finally {
122
+ budget.depth -= 1;
123
+ }
124
+ };
125
+ }
126
+ // the deterministic trunk
127
+ const t0 = now();
128
+ let output;
129
+ try {
130
+ output = await def.run(input, ctx, res);
131
+ emit(span("turn", def.id, now() - t0, true, undefined, { used: budget.used }));
132
+ }
133
+ catch (e) {
134
+ emit(span("turn", def.id, now() - t0, false, errOf(e)));
135
+ chain.append({ actor: "deepsec", action: "run.error", scopeId: scope ?? "", payload: errOf(e) });
136
+ throw e;
137
+ }
138
+ // H4 — redaction: scrub secrets/PII before the output is persisted or exported.
139
+ let safe = output;
140
+ for (const g of guards)
141
+ if (g.redact)
142
+ safe = g.redact(safe);
143
+ // H3 — vault write: every assess leaves a versioned deliverable.
144
+ let artifact;
145
+ if (res.vault) {
146
+ // ponytail: key by cord id + runId. Was keyed off input.target (deepeval-specific leaking into the
147
+ // generic core) — keying by the cord is correct for every cord.
148
+ const key = `reports/${slug(def.id)}/${runId}.json`;
149
+ artifact = await res.vault.put(key, JSON.stringify(safe, null, 2), { runId, tenantId, cord: def.id });
150
+ chain.append({ actor: "deepsec", action: "artifact.write", scopeId: scope ?? "", payload: { key: artifact.key, version: artifact.version, hash: artifact.hash } });
151
+ }
152
+ chain.append({ actor: "deepsec", action: "run.settle", scopeId: scope ?? "", payload: { runId, ok: true, used: budget.used } });
153
+ return { output: safe, runId, ...(artifact !== undefined ? { artifact } : {}), audit: chain.entries, events, budget };
154
+ };
155
+ return {
156
+ id: def.id,
157
+ expose,
158
+ manifest,
159
+ run: async (input, opts) => (await runFull(input, opts)).output,
160
+ runFull,
161
+ };
162
+ }
163
+ // ---- H5: publish/fork — the forkable manifest + the generated discovery endpoint ----
164
+ /** /.well-known/op0.json (ADR-18) generated from the cord's `expose`. Host is filled at deploy. */
165
+ export const wellKnown = (m, host = "https://host") => ({
166
+ mcp: m.expose.mcp ? [`${host}${m.expose.mcp}`] : [],
167
+ docs: `${host}/llms.txt`,
168
+ ...(m.expose.openapi ? { openapi: `${host}/openapi.json` } : {}),
169
+ ...(m.expose.cli ? { cli: `${host}/cli` } : {}),
170
+ skills: "/.well-known/agent-skills/",
171
+ ...(m.scope ? { auth: { type: "oauth2", scopes: [m.scope] } } : {}),
172
+ });
173
+ /** Content-address the manifest for skillkit publish/fork lineage (signed → forkOf{atCommit} later, ADR-22). */
174
+ export const signManifest = (m) => ({ manifest: m, hash: hashHex(JSON.stringify(m)) });
175
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,97 @@
1
+ import type { Vault } from "./vault.ts";
2
+ /**
3
+ * Durable, strongly-consistent mutable state keyed by string.
4
+ * - CF host → a Durable Object (single-threaded actor → `incr` is atomic). See worker/src/kv.ts.
5
+ * - Node host → a Map (memKv) now; sqlite/Redis when state must outlive the process.
6
+ */
7
+ export interface Kv {
8
+ get(key: string): Promise<string | null>;
9
+ put(key: string, value: string): Promise<void>;
10
+ /** Atomic read-modify-write; returns the new total. Safe because the backing actor serialises writes. */
11
+ incr(key: string, by?: number): Promise<number>;
12
+ }
13
+ export type ToolCall = {
14
+ id: string;
15
+ name: string;
16
+ args: Record<string, unknown>;
17
+ };
18
+ export type Message = {
19
+ role: "system" | "user" | "assistant" | "tool";
20
+ content: string;
21
+ toolCallId?: string;
22
+ toolCalls?: ToolCall[];
23
+ };
24
+ /** A callable the agent may invoke — almost always a thin wrapper over a governed port (governed-tools = ports).
25
+ * `parameters` is the JSON-schema for args (function-calling models need it); fakeInference ignores it. */
26
+ export type ToolSpec = {
27
+ name: string;
28
+ description: string;
29
+ parameters?: Record<string, unknown>;
30
+ run: (args: Record<string, unknown>) => Promise<unknown>;
31
+ };
32
+ /** The model call (chat + tool-calling). Adapters: fakeInference (tests) · AI Gateway / Anthropic (worker). */
33
+ export interface Inference {
34
+ generate(req: {
35
+ messages: Message[];
36
+ tools?: ToolSpec[];
37
+ }): Promise<{
38
+ text?: string;
39
+ toolCalls?: ToolCall[];
40
+ }>;
41
+ }
42
+ /** The agent loop (model → tools → stop). Adapters: minimalHarness (in-house, stack-native) · Flue · Project Think. */
43
+ export interface Harness {
44
+ run(spec: {
45
+ instructions: string;
46
+ input: string;
47
+ tools: ToolSpec[];
48
+ maxSteps?: number;
49
+ }): Promise<{
50
+ output: string;
51
+ steps: number;
52
+ }>;
53
+ }
54
+ /** What a host provides to a run. A cord declares the keys it needs; the runtime validates they're bound. */
55
+ export interface Resources {
56
+ vault?: Vault;
57
+ kv?: Kv;
58
+ inference?: Inference;
59
+ harness?: Harness;
60
+ }
61
+ export type ResourceNeed = keyof Resources;
62
+ /** In-memory Kv — the Node/verify host. ponytail: a Map; swap for sqlite/Redis when state must outlive the run. */
63
+ export declare const memKv: () => Kv;
64
+ /** The full computer, when a cord binds one — NEVER the foundation (the spine runs without it).
65
+ * Adapters: fake/mem (tests) · node-local (dev) · the governed remote path via usectx (fleet cords, R3+)
66
+ * · CF Sandbox / E2B host-side where containers exist. */
67
+ export interface Sandbox {
68
+ exec(cmd: string, opts?: {
69
+ cwd?: string;
70
+ timeoutMs?: number;
71
+ env?: Record<string, string>;
72
+ }): Promise<{
73
+ ok: boolean;
74
+ stdout: string;
75
+ stderr: string;
76
+ }>;
77
+ readFile(path: string): Promise<string | null>;
78
+ writeFile(path: string, content: string): Promise<void>;
79
+ }
80
+ /** A cord run IS a governed loop, declared up front. Goal condition (when to stop), scope (what it may
81
+ * touch), stop rules (success / failure / budget), verifier (who checks). The market's /loop vocabulary,
82
+ * native to the cord. */
83
+ export interface RunSpec {
84
+ /** The verifiable end state — a run without one is a wander, not a loop. */
85
+ goal: string;
86
+ /** What the run may touch (paths, tools, connections) — grants are the hard boundary; this is the declared intent. */
87
+ scope?: string[];
88
+ stop?: {
89
+ /** Hard step ceiling for the loop (default: the harness's own cap). */
90
+ maxSteps?: number;
91
+ /** Spend ceiling (model + tool cost units) — exceeded ⇒ honest halt, ledgered. */
92
+ budget?: number;
93
+ };
94
+ /** Who checks the result: a verifier cord/tool name, or "none" (explicitly unverified). */
95
+ verifier?: string;
96
+ }
97
+ //# sourceMappingURL=platform.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"platform.d.ts","sourceRoot":"","sources":["../src/platform.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,10 @@
1
+ /** In-memory Kv — the Node/verify host. ponytail: a Map; swap for sqlite/Redis when state must outlive the run. */
2
+ export const memKv = () => {
3
+ const m = new Map();
4
+ return {
5
+ async get(k) { return m.get(k) ?? null; },
6
+ async put(k, v) { m.set(k, v); },
7
+ async incr(k, by = 1) { const n = Number(m.get(k) ?? 0) + by; m.set(k, String(n)); return n; },
8
+ };
9
+ };
10
+ //# sourceMappingURL=platform.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"platform.js","sourceRoot":"","sources":["../src/platform.ts"],"names":[],"mappings":"AAwDA,mHAAmH;AACnH,MAAM,CAAC,MAAM,KAAK,GAAG,GAAO,EAAE;IAC5B,MAAM,CAAC,GAAG,IAAI,GAAG,EAAkB,CAAC;IACpC,OAAO;QACL,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,OAAO,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC,CAAC,CAAC;QACzC,KAAK,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;QAChC,KAAK,CAAC,IAAI,CAAC,CAAC,EAAE,EAAE,GAAG,CAAC,IAAI,MAAM,CAAC,GAAG,MAAM,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;KAC/F,CAAC;AACJ,CAAC,CAAC"}
@@ -0,0 +1,47 @@
1
+ import { verifyChain, type WideEvent, type AuditEntry } from "./guards.ts";
2
+ export type SessionMode = "owned" | "attached" | "streamed";
3
+ export type CaptureVector = "hook" | "proxy" | "watcher" | "backfill";
4
+ /** A normalized coding-session event (mirrors lapdog/trajectory capture). */
5
+ export type SessionEvent = {
6
+ ts: string;
7
+ kind: "lifecycle" | "prompt" | "model" | "tool_call" | "tool_result" | "permission";
8
+ name: string;
9
+ vector: CaptureVector;
10
+ attrs?: Record<string, unknown>;
11
+ tokensIn?: number;
12
+ tokensOut?: number;
13
+ costUsd?: number;
14
+ };
15
+ export type SessionStream = {
16
+ mode: SessionMode;
17
+ harness: string;
18
+ runId: string;
19
+ events: SessionEvent[];
20
+ };
21
+ export type Decision = "allow" | "deny" | "approve";
22
+ export type Rule = {
23
+ id: string;
24
+ match: (e: SessionEvent) => boolean;
25
+ decision: Decision;
26
+ why: string;
27
+ };
28
+ export declare const DEFAULT_RULES: Rule[];
29
+ export type Interception = {
30
+ event: SessionEvent;
31
+ rule: Rule;
32
+ decision: Decision;
33
+ enforceable: boolean;
34
+ };
35
+ export declare const intercept: (stream: SessionStream, rules?: Rule[]) => Interception[];
36
+ export type SessionTrace = {
37
+ runId: string;
38
+ harness: string;
39
+ mode: SessionMode;
40
+ events: WideEvent[];
41
+ audit: AuditEntry[];
42
+ interceptions: Interception[];
43
+ };
44
+ export declare const ingestSession: (stream: SessionStream, rules?: Rule[]) => SessionTrace;
45
+ export declare const fromTrajectoryJsonl: (jsonl: string, harness: string, mode?: SessionMode) => SessionStream;
46
+ export { verifyChain };
47
+ //# sourceMappingURL=session.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"session.d.ts","sourceRoot":"","sources":["../src/session.ts"],"names":[],"mappings":"AAmBA,OAAO,EAAkB,WAAW,EAAE,KAAK,SAAS,EAAE,KAAK,UAAU,EAAE,MAAM,aAAa,CAAC;AAG3F,MAAM,MAAM,WAAW,GAAG,OAAO,GAAG,UAAU,GAAG,UAAU,CAAC;AAC5D,MAAM,MAAM,aAAa,GAAG,MAAM,GAAG,OAAO,GAAG,SAAS,GAAG,UAAU,CAAC;AAEtE,6EAA6E;AAC7E,MAAM,MAAM,YAAY,GAAG;IACzB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,WAAW,GAAG,QAAQ,GAAG,OAAO,GAAG,WAAW,GAAG,aAAa,GAAG,YAAY,CAAC;IACpF,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,aAAa,CAAC;IACtB,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAChC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAC;CACzD,CAAC;AAEF,MAAM,MAAM,aAAa,GAAG;IAAE,IAAI,EAAE,WAAW,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,YAAY,EAAE,CAAA;CAAE,CAAC;AAG1G,MAAM,MAAM,QAAQ,GAAG,OAAO,GAAG,MAAM,GAAG,SAAS,CAAC;AACpD,MAAM,MAAM,IAAI,GAAG;IAAE,EAAE,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,CAAC,CAAC,EAAE,YAAY,KAAK,OAAO,CAAC;IAAC,QAAQ,EAAE,QAAQ,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,CAAC;AAExG,eAAO,MAAM,aAAa,EAAE,IAAI,EAI/B,CAAC;AAEF,MAAM,MAAM,YAAY,GAAG;IAAE,KAAK,EAAE,YAAY,CAAC;IAAC,IAAI,EAAE,IAAI,CAAC;IAAC,QAAQ,EAAE,QAAQ,CAAC;IAAC,WAAW,EAAE,OAAO,CAAA;CAAE,CAAC;AAOzG,eAAO,MAAM,SAAS,GAAI,QAAQ,aAAa,EAAE,QAAO,IAAI,EAAkB,KAAG,YAAY,EAQ5F,CAAC;AAGF,MAAM,MAAM,YAAY,GAAG;IACzB,KAAK,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,WAAW,CAAC;IAClD,MAAM,EAAE,SAAS,EAAE,CAAC;IAAC,KAAK,EAAE,UAAU,EAAE,CAAC;IAAC,aAAa,EAAE,YAAY,EAAE,CAAC;CACzE,CAAC;AAEF,eAAO,MAAM,aAAa,GAAI,QAAQ,aAAa,EAAE,QAAO,IAAI,EAAkB,KAAG,YAoBpF,CAAC;AAKF,eAAO,MAAM,mBAAmB,GAAI,OAAO,MAAM,EAAE,SAAS,MAAM,EAAE,OAAM,WAAwB,KAAG,aAcpG,CAAC;AAEF,OAAO,EAAE,WAAW,EAAE,CAAC"}
@@ -0,0 +1,88 @@
1
+ // session.ts — the Session seam (ADR-30): coding-ops over harness-agnostic LIVE sessions.
2
+ //
3
+ // Parallel to the Source seam (source.ts = static repos; this = live coding sessions). Three modes:
4
+ // owned — op0 creates the session in our runtime (WfP/Sandbox): full control + full intercept.
5
+ // attached — op0 drives an EXISTING session via the session-connector (local daemon, ADR-4d): partial.
6
+ // streamed — op0 observes + intercepts a session it does NOT run, via the lapdog/trajectory pattern:
7
+ // HOOKS = observe (fail-open, fire-and-forget) · model-PROXY = in-path = INTERCEPTABLE.
8
+ // This is how we "audit work we're not controlling" AND keep a governance gate (deepsec)
9
+ // on the proxy path even for sessions we didn't launch.
10
+ //
11
+ // Harness-agnostic (Claude Code / Codex / Cursor / Gemini / opencode / Copilot / Factory / Pi): MINE the
12
+ // OSS reference `datadog-labs/trajectory` (per-client hook/proxy/watcher/backfill instrumentation) rather
13
+ // than hand-rolling — the "check for an existing OSS framework first" lesson. *Verify its license before
14
+ // adapting code; otherwise inspiration-only and build our thin capture on the same vectors.* Observability
15
+ // is commoditized by Datadog (lapdog/trajectory) — our moat is the ACT half: govern · intercept · turn the
16
+ // session into a governed onloop run · the flywheel. evlog ingests these traces as just another Source.
17
+ //
18
+ // Pure TS, zero deps (Node + Worker). Reuses the @op0-ai/cord guards so a streamed session yields the SAME
19
+ // evlog WideEvents + tamper-evident audit as an owned cord run.
20
+ import { makeAuditChain, verifyChain } from "./guards.js";
21
+ import { newId } from "./hash.js";
22
+ export const DEFAULT_RULES = [
23
+ { id: "force-push", match: (e) => e.kind === "tool_call" && /\bgit\s+push\b.*--force(?!-with-lease)/.test(e.name), decision: "deny", why: "force-push without --force-with-lease" },
24
+ { id: "rm-rf", match: (e) => e.kind === "tool_call" && /\brm\s+-rf\b/.test(e.name), decision: "approve", why: "destructive delete — needs human approval" },
25
+ { id: "secret-exfil", match: (e) => e.kind === "tool_call" && /(curl|wget)\b.*(\.env|secret|token|key)/i.test(e.name), decision: "approve", why: "possible secret exfiltration" },
26
+ ];
27
+ // enforceable ONLY when the event is actionable: owned/attached (we drive it) or streamed-via-PROXY (in-path).
28
+ // streamed-via-hook is observe-only (fail-open) → we can flag/audit but not block.
29
+ const canEnforce = (mode, vector) => mode === "owned" || mode === "attached" || (mode === "streamed" && vector === "proxy");
30
+ export const intercept = (stream, rules = DEFAULT_RULES) => {
31
+ const out = [];
32
+ for (const e of stream.events) {
33
+ for (const r of rules) {
34
+ if (r.match(e))
35
+ out.push({ event: e, rule: r, decision: r.decision, enforceable: canEnforce(stream.mode, e.vector) });
36
+ }
37
+ }
38
+ return out;
39
+ };
40
+ export const ingestSession = (stream, rules = DEFAULT_RULES) => {
41
+ const chain = makeAuditChain();
42
+ chain.append({ actor: "deepsec", action: "session.capture", scopeId: stream.mode, payload: { harness: stream.harness, mode: stream.mode } });
43
+ const events = stream.events.map((e) => ({
44
+ ts: e.ts, runId: stream.runId, tenantId: "op0", cordId: "@op0/coding-ops",
45
+ kind: e.kind === "tool_call" || e.kind === "tool_result" ? "tool" : e.kind === "model" ? "model" : "step",
46
+ name: e.name, durationMs: 0, ok: true,
47
+ attrs: { vector: e.vector, ...(e.attrs ?? {}) },
48
+ }));
49
+ const interceptions = intercept(stream, rules);
50
+ for (const i of interceptions) {
51
+ chain.append({
52
+ actor: "deepsec",
53
+ action: i.enforceable ? `intercept.${i.decision}` : "intercept.flag",
54
+ scopeId: stream.mode,
55
+ payload: { rule: i.rule.id, why: i.rule.why, event: i.event.name, enforceable: i.enforceable },
56
+ });
57
+ }
58
+ chain.append({ actor: "deepsec", action: "session.settle", scopeId: stream.mode, payload: { runId: stream.runId, events: events.length, interceptions: interceptions.length } });
59
+ return { runId: stream.runId, harness: stream.harness, mode: stream.mode, events, audit: chain.entries, interceptions };
60
+ };
61
+ // --- streamed adapter: parse a lapdog/trajectory-style JSONL capture into a SessionStream ---
62
+ // (lapdog hooks POST one JSON per event; trajectory writes JSONL timelines. This is a minimal normalized
63
+ // subset for the spike; the real adapter maps each harness's hook/proxy schema — mine trajectory's.)
64
+ export const fromTrajectoryJsonl = (jsonl, harness, mode = "streamed") => {
65
+ const events = [];
66
+ for (const line of jsonl.split("\n")) {
67
+ const t = line.trim();
68
+ if (!t)
69
+ continue;
70
+ let o;
71
+ try {
72
+ o = JSON.parse(t);
73
+ }
74
+ catch {
75
+ continue;
76
+ }
77
+ events.push({
78
+ ts: o.ts ?? new Date().toISOString(),
79
+ kind: o.kind ?? "lifecycle",
80
+ name: o.name ?? o.tool ?? o.event ?? "event",
81
+ vector: o.vector ?? "hook",
82
+ attrs: o.attrs, tokensIn: o.tokensIn, tokensOut: o.tokensOut, costUsd: o.costUsd,
83
+ });
84
+ }
85
+ return { mode, harness, runId: newId(), events };
86
+ };
87
+ export { verifyChain };
88
+ //# sourceMappingURL=session.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"session.js","sourceRoot":"","sources":["../src/session.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,65 @@
1
+ /** A blob store — the R2 shape, structural. The CF host binds R2; Node tests bind a fake. */
2
+ export interface Store {
3
+ get(key: string): Promise<{
4
+ text(): Promise<string>;
5
+ } | null>;
6
+ put(key: string, value: string | ArrayBuffer): Promise<unknown>;
7
+ delete(key: string): Promise<void>;
8
+ list(options: {
9
+ prefix: string;
10
+ }): Promise<{
11
+ objects: ReadonlyArray<{
12
+ key: string;
13
+ }>;
14
+ }>;
15
+ }
16
+ /** An external commit target (the by-design Source over HTTPS). Adapter: {@link githubCommitter}. */
17
+ export interface Committer {
18
+ commit(files: ReadonlyArray<FlushedFile>): Promise<CommitResult>;
19
+ }
20
+ export interface FlushedFile {
21
+ readonly path: string;
22
+ readonly text: string;
23
+ }
24
+ export interface CommitResult {
25
+ readonly committed: number;
26
+ readonly total: number;
27
+ readonly target: string;
28
+ }
29
+ /** Result of a flush: what landed in the backing store, plus the external committer's outcome (or "skipped"). */
30
+ export interface FlushResult {
31
+ readonly committed: number;
32
+ readonly paths: readonly string[];
33
+ readonly external: CommitResult | "skipped";
34
+ }
35
+ export interface Source {
36
+ /** Read committed content for a path (null if absent). */
37
+ read(path: string): Promise<string | null>;
38
+ /** Stage an edit; committed on the next {@link Source.flush} (the host debounces flush via its scheduler). */
39
+ stage(path: string, content: string): Promise<void>;
40
+ /** Commit all staged edits idempotently: backing store first, then the external committer (if configured). */
41
+ flush(): Promise<FlushResult>;
42
+ }
43
+ /** Create a bidirectional Source over a backing {@link Store} (+ optional external {@link Committer}). */
44
+ export declare function bidirectionalSource(options: {
45
+ store: Store;
46
+ committer?: Committer;
47
+ /** Namespace every key under this prefix (e.g. a tenant id). Optional. */
48
+ prefix?: string;
49
+ }): Source;
50
+ /** UTF-8 → base64 for the GitHub Contents API (Workers has `btoa`, not `Buffer`; encode bytes so unicode survives). */
51
+ export declare function toBase64(text: string): string;
52
+ /**
53
+ * GitHub-commit Source adapter (HTTPS, Contents API). Idempotent: GET the existing blob sha, then PUT
54
+ * (create or update) under `<base>/<path>`. `fetch` is injectable for tests.
55
+ */
56
+ export declare function githubCommitter(config: {
57
+ token: string;
58
+ owner: string;
59
+ repo: string;
60
+ branch?: string;
61
+ /** Path prefix inside the repo. Defaults to "cord". */
62
+ base?: string;
63
+ fetch?: typeof fetch;
64
+ }): Committer;
65
+ //# sourceMappingURL=source.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"source.d.ts","sourceRoot":"","sources":["../src/source.ts"],"names":[],"mappings":"AAOA,6FAA6F;AAC7F,MAAM,WAAW,KAAK;IACpB,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;QAAE,IAAI,IAAI,OAAO,CAAC,MAAM,CAAC,CAAA;KAAE,GAAG,IAAI,CAAC,CAAC;IAC9D,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,WAAW,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IAChE,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACnC,IAAI,CAAC,OAAO,EAAE;QAAE,MAAM,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC;QAAE,OAAO,EAAE,aAAa,CAAC;YAAE,GAAG,EAAE,MAAM,CAAA;SAAE,CAAC,CAAA;KAAE,CAAC,CAAC;CACzF;AAED,qGAAqG;AACrG,MAAM,WAAW,SAAS;IACxB,MAAM,CAAC,KAAK,EAAE,aAAa,CAAC,WAAW,CAAC,GAAG,OAAO,CAAC,YAAY,CAAC,CAAC;CAClE;AAED,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;CACvB;AAED,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;CACzB;AAED,iHAAiH;AACjH,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,KAAK,EAAE,SAAS,MAAM,EAAE,CAAC;IAClC,QAAQ,CAAC,QAAQ,EAAE,YAAY,GAAG,SAAS,CAAC;CAC7C;AAED,MAAM,WAAW,MAAM;IACrB,0DAA0D;IAC1D,IAAI,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;IAC3C,8GAA8G;IAC9G,KAAK,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACpD,8GAA8G;IAC9G,KAAK,IAAI,OAAO,CAAC,WAAW,CAAC,CAAC;CAC/B;AAID,0GAA0G;AAC1G,wBAAgB,mBAAmB,CAAC,OAAO,EAAE;IAC3C,KAAK,EAAE,KAAK,CAAC;IACb,SAAS,CAAC,EAAE,SAAS,CAAC;IACtB,0EAA0E;IAC1E,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB,GAAG,MAAM,CAkCT;AAED,uHAAuH;AACvH,wBAAgB,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAK7C;AAED;;;GAGG;AACH,wBAAgB,eAAe,CAAC,MAAM,EAAE;IACtC,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,uDAAuD;IACvD,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,OAAO,KAAK,CAAC;CACtB,GAAG,SAAS,CAyCZ"}
package/dist/source.js ADDED
@@ -0,0 +1,96 @@
1
+ // source.ts — the bidirectional Source: a cord's read/write content layer behind ONE flush seam, with
2
+ // interchangeable adapters (an R2-shaped backing store + an optional external committer e.g. GitHub). It is
3
+ // STRUCTURALLY typed — runs in a Worker (R2 bound) AND in Node tests (a Map-backed fake) — which keeps the
4
+ // ports model honest. The debounce itself is the host's durable scheduler (CF Agents `this.schedule`); this
5
+ // module owns staging, the idempotent flush across adapters, and read-back. (Reconciliation of @op0-ai/cord to
6
+ // the governed-CF-Agent reality, ADR-cord-2026-06-30.)
7
+ const PENDING = "pending/";
8
+ /** Create a bidirectional Source over a backing {@link Store} (+ optional external {@link Committer}). */
9
+ export function bidirectionalSource(options) {
10
+ const { store, committer } = options;
11
+ const ns = (key) => (options.prefix ? `${options.prefix}/${key}` : key);
12
+ const pendingPrefix = ns(PENDING);
13
+ return {
14
+ async read(path) {
15
+ const object = await store.get(ns(path));
16
+ return object ? await object.text() : null;
17
+ },
18
+ async stage(path, content) {
19
+ await store.put(ns(PENDING + path), content);
20
+ },
21
+ async flush() {
22
+ const listed = await store.list({ prefix: pendingPrefix });
23
+ // Each pending object is independent (its own key, no cross-object state) — the get→put→delete
24
+ // sequence for ONE object must stay in order, but different objects can flush concurrently.
25
+ const results = await Promise.all(listed.objects.map(async (object) => {
26
+ const staged = await store.get(object.key);
27
+ if (!staged)
28
+ return null;
29
+ const path = object.key.slice(pendingPrefix.length);
30
+ const text = await staged.text();
31
+ await store.put(ns(path), text); // commit to the backing store
32
+ await store.delete(object.key); // clear the staged copy
33
+ return { path, text };
34
+ }));
35
+ const flushed = results.filter((f) => f !== null);
36
+ const external = committer && flushed.length > 0 ? await committer.commit(flushed) : "skipped";
37
+ return { committed: flushed.length, paths: flushed.map((f) => f.path), external };
38
+ },
39
+ };
40
+ }
41
+ /** UTF-8 → base64 for the GitHub Contents API (Workers has `btoa`, not `Buffer`; encode bytes so unicode survives). */
42
+ export function toBase64(text) {
43
+ const bytes = new TextEncoder().encode(text);
44
+ let binary = "";
45
+ for (const byte of bytes)
46
+ binary += String.fromCharCode(byte);
47
+ return btoa(binary);
48
+ }
49
+ /**
50
+ * GitHub-commit Source adapter (HTTPS, Contents API). Idempotent: GET the existing blob sha, then PUT
51
+ * (create or update) under `<base>/<path>`. `fetch` is injectable for tests.
52
+ */
53
+ export function githubCommitter(config) {
54
+ const branch = config.branch ?? "main";
55
+ const base = config.base ?? "cord";
56
+ const doFetch = config.fetch ?? fetch;
57
+ const api = `https://api.github.com/repos/${config.owner}/${config.repo}/contents`;
58
+ const headers = {
59
+ authorization: `Bearer ${config.token}`,
60
+ accept: "application/vnd.github+json",
61
+ "user-agent": "op0-cord",
62
+ "content-type": "application/json",
63
+ };
64
+ return {
65
+ async commit(files) {
66
+ let committed = 0;
67
+ // Sequential by necessity: the GitHub Contents API creates one commit per file PUT against the SAME
68
+ // branch ref. Concurrent PUTs here would race on that ref (each PUT reads then advances branch HEAD),
69
+ // risking "sha is not current" 409/422s — not just a style preference, a correctness requirement.
70
+ for (const file of files) {
71
+ const url = `${api}/${base}/${file.path}`;
72
+ let sha;
73
+ // oxlint-disable-next-line no-await-in-loop
74
+ const head = await doFetch(`${url}?ref=${encodeURIComponent(branch)}`, { headers });
75
+ // oxlint-disable-next-line no-await-in-loop
76
+ if (head.ok)
77
+ sha = (await head.json()).sha;
78
+ // oxlint-disable-next-line no-await-in-loop
79
+ const put = await doFetch(url, {
80
+ method: "PUT",
81
+ headers,
82
+ body: JSON.stringify({
83
+ message: `cord: commit ${file.path}`,
84
+ content: toBase64(file.text),
85
+ branch,
86
+ ...(sha ? { sha } : {}),
87
+ }),
88
+ });
89
+ if (put.ok)
90
+ committed += 1;
91
+ }
92
+ return { committed, total: files.length, target: `${config.owner}/${config.repo}@${branch}` };
93
+ },
94
+ };
95
+ }
96
+ //# sourceMappingURL=source.js.map