Expose canonical host chains and validate automation ancestry
This commit is contained in:
@@ -32,8 +32,10 @@ External script effects can repeat after a crash.
|
||||
## Credentials
|
||||
|
||||
The conversation that creates an automation remains its execution conversation.
|
||||
Every script launch uses that conversation's T44 chain.
|
||||
The chain includes ownership, fork ancestry, and the optional branch starter.
|
||||
Every script launch uses `host.chain(creator)`.
|
||||
The host delegates to `conversationChains` in `host/js/environment.ts`.
|
||||
The host appends the branch starter's chain after the ownership or fork ancestry.
|
||||
The host removes duplicate ids without changing their order.
|
||||
Subscribe and unsubscribe change notification recipients only.
|
||||
They never change the execution conversation.
|
||||
Built-in automations without a creator use an empty chain.
|
||||
|
||||
@@ -8,7 +8,7 @@ import {
|
||||
import type { ConversationId, Cursor, Tx } from '@earendil-works/pi-durable'
|
||||
type ExtensionHost = any
|
||||
import { SubagentsApi } from '../subagents/api.ts'
|
||||
import { discover } from '../subagents/definitions.ts'
|
||||
import { ModelsApi } from '../models/api.ts'
|
||||
import { nextAt } from './schedule.ts'
|
||||
import type { Automation, Run } from './types.ts'
|
||||
import { RUN_TASK } from './types.ts'
|
||||
@@ -26,7 +26,7 @@ const terminal = {
|
||||
}
|
||||
async function definition(host: ExtensionHost, name: string) {
|
||||
const found = (
|
||||
await discover(host)
|
||||
await host.need(ModelsApi).discover('/src/sausage')
|
||||
).definitions.find((d) => d.name === name)
|
||||
if (!found) throw new Error(`Unknown agent: ${name}`)
|
||||
return found
|
||||
|
||||
@@ -6,7 +6,6 @@ import { SubagentsApi } from '../subagents/api.ts'
|
||||
import { scheduler } from './scheduler.ts'
|
||||
import { triggers, TRIGGER_EVENTS } from './triggers.ts'
|
||||
import { parseSchedule } from './schedule.ts'
|
||||
import { executionChain } from './chain.ts'
|
||||
import { fireText, doneText, trimLog } from './types.ts'
|
||||
import type { Args, Automation, Subscriber } from './types.ts'
|
||||
export type { Automation, Run } from './types.ts'
|
||||
@@ -62,7 +61,7 @@ export default function automations(host:any) {
|
||||
execute:async(task,runtime,ctx)=>{
|
||||
const a = (await host.harness().snapshot(Automations,ctx))!.automations[task.input.id]
|
||||
if (!a?.enabled || !a.script || a.scheduler!==task.id) return runtime.commit(()=>terminal,ctx)
|
||||
const conversations = await executionChain(host,a.executionConversation)
|
||||
const conversations = a.executionConversation === undefined ? [] : await host.chain(a.executionConversation)
|
||||
let queue = Promise.resolve(), log='', done=false, sequence=0
|
||||
const pending:Record<string,string> = {stdout:'',stderr:''}
|
||||
function line(text:string,stream:string) {
|
||||
@@ -79,7 +78,7 @@ export default function automations(host:any) {
|
||||
receipts[key]=true
|
||||
},ctx))
|
||||
}
|
||||
const result=await host.exec(['sh',path(a.id)],{cwd:directory(a.id),conversations,timeout:3600,signal:runtime.signal,onOutput:(text:string,stream:string)=>{
|
||||
const result=await host.exec(['sh',path(a.id)],{cwd:directory(a.id),conversations,timeout:3600,signal:runtime.signal,task:task.id,onOutput:(text:string,stream:string)=>{
|
||||
pending[stream]+=text
|
||||
const lines=pending[stream].split('\n'); pending[stream]=lines.pop()!
|
||||
for(const text of lines) line(text.replace(/\r$/,''),stream)
|
||||
|
||||
+502
-434
File diff suppressed because it is too large
Load Diff
@@ -16,10 +16,10 @@ const loader=await loadExtensions(harness,registry,storage,models,context)
|
||||
assert(!loader.state().error,loader.state().error)
|
||||
globalThis.automationBase=`file:///run/sausage/extensions/${loader.state().good}/extensions`
|
||||
const {Automations}=await import(/* @vite-ignore */ `${globalThis.automationBase}/automations/server.ts`)
|
||||
const call=async(args:any)=>{
|
||||
const call=async(args:any,conversation=root)=>{
|
||||
faux.setResponses([fauxAssistantMessage([fauxToolCall('automation',args)],{stopReason:'toolUse'}),fauxAssistantMessage('done')])
|
||||
await (await root.submit({type:'input',content:'Configure automation'},context)).wait(context)
|
||||
const result=(await root.context(context)).entries.filter((entry:any)=>entry.kind==='pi.tool-result').at(-1)
|
||||
await (await conversation.submit({type:'input',content:'Configure automation'},context)).wait(context)
|
||||
const result=(await conversation.context(context)).entries.filter((entry:any)=>entry.kind==='pi.tool-result').at(-1)
|
||||
assert(result && !result.model?.[0]?.isError,JSON.stringify(result))
|
||||
}
|
||||
harness.resume()
|
||||
@@ -55,17 +55,27 @@ if(globalThis.automationMode==='prepare') {
|
||||
await loader.close();await harness.close(context);await storage.close()
|
||||
} else if(globalThis.automationMode==='grant') {
|
||||
for(const path of ['/src/sausage/automations','/src/sausage/automations/A1']) await fs.mkdir(path)
|
||||
await fs.write('/src/sausage/automations/A1/script.sh',`curl http://127.0.0.1:${globalThis.automationPort}/token\nprintf '\\nFIRE delivered\\n'\nprintf 'FIRE ignored\\n' >&2\nprintf 'DONE\\n'`)
|
||||
await fs.write('/src/sausage/automations/A1/script.sh',`curl http://host.sausage/whoami\nprintf '\\n'\ncurl http://127.0.0.1:${globalThis.automationPort}/token\nprintf '\\nFIRE delivered\\n'\nprintf 'FIRE ignored\\n' >&2\nprintf 'DONE\\n'`)
|
||||
const subscriber=await harness.createConversation({ownership:{kind:'ownerless'}},context)
|
||||
await subscriber.configure({model:{provider:'faux',modelId:'faux-1'}},context)
|
||||
await call({action:'create',name:'creator grant',ask:'Fetch and fire',subscribers:[subscriber.id]})
|
||||
const {Started}=await import(/* @vite-ignore */ `${globalThis.automationBase}/messages/addresses.ts`)
|
||||
const entry=await harness.commit((tx:any)=>tx.appendEntry(subscriber.id,{kind:'automation.origin',data:{}}),context)
|
||||
const creatorRecord=await harness.commit(async(tx:any)=>{
|
||||
const fork=await tx.forkConversation(subscriber.id,entry.id,{ownership:{kind:'ownerless'}})
|
||||
;(await tx.doc(Started,fork.id)).by=root.id
|
||||
return fork
|
||||
},context)
|
||||
const creator=await harness.conversation(creatorRecord.id,context)
|
||||
await call({action:'create',name:'creator grant',ask:'Fetch and fire',subscribers:[subscriber.id]},creator)
|
||||
await until(async()=>(await harness.snapshot(Automations,context))?.automations.A1?.script?.state==='done')
|
||||
await until(async()=>(await subscriber.context(context)).entries.some((entry:any)=>entry.model?.some((m:any)=>typeof m.content==='string'&&m.content.includes('[automation A1 fired]'))))
|
||||
await harness.waitForIdle(context)
|
||||
const ledger=await harness.snapshot(Automations,context)
|
||||
assert(ledger.automations.A1.fires===1,'Only stdout fires')
|
||||
assert(ledger.automations.A1.executionConversation===root.id,'The creator owns execution')
|
||||
assert((await fs.read('/src/sausage/automations/A1/log')).includes('accepted'),'The creator grant reaches curl')
|
||||
assert(ledger.automations.A1.executionConversation===creator.id,'The creator owns execution')
|
||||
const log=await fs.read('/src/sausage/automations/A1/log')
|
||||
assert(log.includes('accepted'),'The creator chain reaches the starter grant')
|
||||
assert(JSON.stringify(JSON.parse(log.split('\n')[0]).conversations)===JSON.stringify([String(creator.id),String(subscriber.id),String(root.id)]),'The host appends the starter after fork ancestry')
|
||||
const entries=(await root.context(context)).entries.filter((entry:any)=>entry.model?.some((m:any)=>m.role==='user'&&typeof m.content==='string'&&m.content.includes('[automation A1 fired]')))
|
||||
assert(entries.length===0,'The creator is not a subscriber')
|
||||
const delivered=(await subscriber.context(context)).entries.filter((entry:any)=>entry.model?.some((m:any)=>m.role==='user'&&typeof m.content==='string'&&m.content.includes('[automation A1 fired]')))
|
||||
@@ -74,7 +84,7 @@ if(globalThis.automationMode==='prepare') {
|
||||
await call({action:'subscribe',id:'A1',to:`#${replacement.id}`})
|
||||
await call({action:'unsubscribe',id:'A1',to:`#${subscriber.id}`})
|
||||
const changed=(await harness.snapshot(Automations,context)).automations.A1
|
||||
assert(changed.executionConversation===root.id,'Subscription changes retain the creator')
|
||||
assert(changed.executionConversation===creator.id,'Subscription changes retain the creator')
|
||||
assert(JSON.stringify(changed.subscribers)===JSON.stringify([replacement.id]),'Subscription ids normalize')
|
||||
await loader.close();await harness.close(context);await storage.close()
|
||||
} else {
|
||||
|
||||
@@ -54213,6 +54213,7 @@ function registerModules() {
|
||||
async function loadExtensions(harness2, registry2, storage2, models2, context, builtins = []) {
|
||||
registerModules();
|
||||
const eventService = eventLog(() => harness2);
|
||||
const chain2 = conversationChains(harness2, context);
|
||||
const repo = "/src/sausage";
|
||||
let providers = /* @__PURE__ */ new Map();
|
||||
const resources = /* @__PURE__ */ new Map();
|
||||
@@ -54264,6 +54265,7 @@ async function loadExtensions(harness2, registry2, storage2, models2, context, b
|
||||
fs,
|
||||
exec: (command, options) => exec(command, { ...options, extension: name }),
|
||||
git,
|
||||
chain: chain2,
|
||||
implement: (api, value2) => {
|
||||
staged.set(api.name, value2);
|
||||
},
|
||||
|
||||
+109
-70
@@ -8946,27 +8946,27 @@ var REPLACE_TAKE = { kind: "replace" };
|
||||
var PendingOps = class {
|
||||
#state = EMPTY;
|
||||
/** Queue the ops of one commit. */
|
||||
push(ops2) {
|
||||
if (ops2.length === 0)
|
||||
push(ops3) {
|
||||
if (ops3.length === 0)
|
||||
return;
|
||||
const state = this.#state;
|
||||
if (ops2[0][0] === "r") {
|
||||
this.#state = { kind: "batch", ops: ops2 };
|
||||
if (ops3[0][0] === "r") {
|
||||
this.#state = { kind: "batch", ops: ops3 };
|
||||
return;
|
||||
}
|
||||
switch (state.kind) {
|
||||
case "empty":
|
||||
this.#state = { kind: "batch", ops: ops2 };
|
||||
this.#state = { kind: "batch", ops: ops3 };
|
||||
return;
|
||||
case "batch": {
|
||||
const folding = { kind: "folding", ops: [], leaves: /* @__PURE__ */ new Map() };
|
||||
this.#state = folding;
|
||||
if (this.#fold(state.ops, folding))
|
||||
this.#fold(ops2, folding);
|
||||
this.#fold(ops3, folding);
|
||||
return;
|
||||
}
|
||||
case "folding":
|
||||
this.#fold(ops2, state);
|
||||
this.#fold(ops3, state);
|
||||
return;
|
||||
case "replace":
|
||||
return;
|
||||
@@ -8997,8 +8997,8 @@ var PendingOps = class {
|
||||
}
|
||||
}
|
||||
/** Fold `ops` into `folding`; false once the queue overflowed into `replace`. */
|
||||
#fold(ops2, folding) {
|
||||
for (const op2 of ops2) {
|
||||
#fold(ops3, folding) {
|
||||
for (const op2 of ops3) {
|
||||
switch (op2[0]) {
|
||||
case "r":
|
||||
folding.ops.length = 0;
|
||||
@@ -9267,12 +9267,12 @@ var PathError = class extends Error {
|
||||
this.name = "PathError";
|
||||
}
|
||||
};
|
||||
function apply(target, ops2) {
|
||||
return applyOps(target, ops2);
|
||||
function apply(target, ops3) {
|
||||
return applyOps(target, ops3);
|
||||
}
|
||||
function applyOps(target, ops2) {
|
||||
function applyOps(target, ops3) {
|
||||
let root = target;
|
||||
for (const op2 of ops2) {
|
||||
for (const op2 of ops3) {
|
||||
assertValidOp(op2);
|
||||
if (op2[0] === "r") {
|
||||
root = op2[1];
|
||||
@@ -9341,14 +9341,14 @@ function applyOps(target, ops2) {
|
||||
}
|
||||
return root;
|
||||
}
|
||||
function applyImmutable(target, ops2) {
|
||||
return applyImmutableBatches(target, [ops2]);
|
||||
function applyImmutable(target, ops3) {
|
||||
return applyImmutableBatches(target, [ops3]);
|
||||
}
|
||||
function applyImmutableBatches(target, batches) {
|
||||
let root = target;
|
||||
const owned = /* @__PURE__ */ new WeakSet();
|
||||
for (const ops2 of batches) {
|
||||
for (const op2 of ops2) {
|
||||
for (const ops3 of batches) {
|
||||
for (const op2 of ops3) {
|
||||
assertValidOp(op2);
|
||||
if (op2[0] === "r") {
|
||||
root = op2[1];
|
||||
@@ -9462,10 +9462,10 @@ function encoder() {
|
||||
let nextId = 0;
|
||||
let previous;
|
||||
return {
|
||||
encode(ops2) {
|
||||
encode(ops3) {
|
||||
previous = void 0;
|
||||
const out = [];
|
||||
for (const op2 of ops2) {
|
||||
for (const op2 of ops3) {
|
||||
if (op2[0] === "r") {
|
||||
out.push(op2);
|
||||
seen.clear();
|
||||
@@ -9715,9 +9715,9 @@ var Source = class {
|
||||
return [];
|
||||
}
|
||||
};
|
||||
function unbox(ops2, value2) {
|
||||
function unbox(ops3, value2) {
|
||||
const result = [];
|
||||
for (const op2 of ops2) {
|
||||
for (const op2 of ops3) {
|
||||
if (op2[0] === "r" || op2[1].length === 0)
|
||||
return [["r", value2]];
|
||||
const path = op2[1].slice(1);
|
||||
@@ -9811,8 +9811,8 @@ var Cell = class extends Source {
|
||||
throw notReady(`state ${this.key} (its init has not run)`);
|
||||
const before = this.value;
|
||||
const diff = options.diff ?? sharesStructure(before, value2);
|
||||
const ops2 = diff ? diffRevisions(before, value2) : void 0;
|
||||
this.#publish(this.#tracker.prepareReplace({ v: value2 }), "ops", true, ops2);
|
||||
const ops3 = diff ? diffRevisions(before, value2) : void 0;
|
||||
this.#publish(this.#tracker.prepareReplace({ v: value2 }), "ops", true, ops3);
|
||||
}
|
||||
/**
|
||||
* Installs a migrated value and its schema (reload commit). The caller validated the value and the derivations
|
||||
@@ -9823,7 +9823,7 @@ var Cell = class extends Source {
|
||||
this.#publish(this.#tracker.prepareReplace({ v: value2 }), "reset", false);
|
||||
}
|
||||
/** Adopts this revision and every derived one, then queues all notifications (cell first, then derivations). */
|
||||
#publish(prepared, type, validate2 = true, ops2) {
|
||||
#publish(prepared, type, validate2 = true, ops3) {
|
||||
if (prepared.ops.length === 0 && type === "ops") {
|
||||
this.#tracker.adopt(prepared);
|
||||
return;
|
||||
@@ -9840,7 +9840,7 @@ var Cell = class extends Source {
|
||||
}
|
||||
holdPublications(() => {
|
||||
this.#tracker.adopt(prepared);
|
||||
const published = ops2 !== void 0 && ops2.length > 0 ? ops2 : unbox(prepared.ops, value2);
|
||||
const published = ops3 !== void 0 && ops3.length > 0 ? ops3 : unbox(prepared.ops, value2);
|
||||
this.fanout.emit(type === "reset" ? { type, value: value2 } : { type, ops: published, value: value2 });
|
||||
for (const [derived, next] of candidates)
|
||||
derived.install(next);
|
||||
@@ -9953,9 +9953,9 @@ var Derived = class extends Source {
|
||||
install(next) {
|
||||
const before = this.#value;
|
||||
this.#value = next;
|
||||
const ops2 = before === void 0 || next === void 0 ? [["r", next ?? null]] : diffRevisions(before, next);
|
||||
if (ops2.length > 0)
|
||||
this.fanout.emit({ type: "ops", ops: ops2, value: next });
|
||||
const ops3 = before === void 0 || next === void 0 ? [["r", next ?? null]] : diffRevisions(before, next);
|
||||
if (ops3.length > 0)
|
||||
this.fanout.emit({ type: "ops", ops: ops3, value: next });
|
||||
}
|
||||
};
|
||||
var isSource = (value2) => value2 instanceof Source;
|
||||
@@ -14008,10 +14008,10 @@ var RemoteBinding = class {
|
||||
const target = pending?.values.get(name) ?? this.members.get(name);
|
||||
if (target === void 0)
|
||||
return;
|
||||
const ops2 = target.decoder.decode(frame.ops ?? []);
|
||||
target.value = applyImmutable(target.value, ops2);
|
||||
const ops3 = target.decoder.decode(frame.ops ?? []);
|
||||
target.value = applyImmutable(target.value, ops3);
|
||||
if (target === this.members.get(name))
|
||||
this.#deliver(name, { type: "ops", ops: ops2, value: target.value });
|
||||
this.#deliver(name, { type: "ops", ops: ops3, value: target.value });
|
||||
return;
|
||||
}
|
||||
const fresh = { value: frame.value, decoder: decoder() };
|
||||
@@ -15835,8 +15835,8 @@ var Watch = class extends WatchLifecycle {
|
||||
this.#retired = false;
|
||||
if (lone !== void 0)
|
||||
return lone;
|
||||
const ops2 = batch.kind === "ops" ? batch.ops : [["r", value2]];
|
||||
return { kind: "value", value: value2, ops: ops2, retired };
|
||||
const ops3 = batch.kind === "ops" ? batch.ops : [["r", value2]];
|
||||
return { kind: "value", value: value2, ops: ops3, retired };
|
||||
}
|
||||
/** The pending failure, or the value after one as a root replacement (no ops were kept since the failure). */
|
||||
#nextAfterFailure() {
|
||||
@@ -17601,7 +17601,7 @@ function describe(plan, record2) {
|
||||
const change = plan.change;
|
||||
if (change === void 0 || !publishes(plan))
|
||||
return void 0;
|
||||
const ops2 = change.loaded === void 0 ? EMPTY_OPERATIONS : change.prepared.ops;
|
||||
const ops3 = change.loaded === void 0 ? EMPTY_OPERATIONS : change.prepared.ops;
|
||||
return {
|
||||
type: "document",
|
||||
kind: "change",
|
||||
@@ -17609,11 +17609,11 @@ function describe(plan, record2) {
|
||||
conversationId,
|
||||
version: change.version,
|
||||
value: change.prepared.value,
|
||||
ops: ops2
|
||||
ops: ops3
|
||||
};
|
||||
}
|
||||
function storeHeld(hold, ops2, value2, version) {
|
||||
hold.pending.push(ops2);
|
||||
function storeHeld(hold, ops3, value2, version) {
|
||||
hold.pending.push(ops3);
|
||||
const taken = hold.pending.take();
|
||||
if (taken.kind === "replace")
|
||||
return { content: { version, kind: "base", value: value2 }, ops: [["r", value2]] };
|
||||
@@ -18694,7 +18694,7 @@ var SessionImpl = class {
|
||||
return { kind: "adoption", error };
|
||||
}
|
||||
const deadline = this.#clock() + withinMs;
|
||||
for (const { loaded, ops: ops2, base } of held2) {
|
||||
for (const { loaded, ops: ops3, base } of held2) {
|
||||
const id = loaded.record.id;
|
||||
let hold = this.#holds.get(id);
|
||||
if (hold === void 0) {
|
||||
@@ -18702,7 +18702,7 @@ var SessionImpl = class {
|
||||
hold = { loaded, pending: new PendingOps(), base: false, deadline, unpin };
|
||||
this.#holds.set(id, hold);
|
||||
}
|
||||
hold.pending.push(ops2);
|
||||
hold.pending.push(ops3);
|
||||
if (base)
|
||||
hold.base = true;
|
||||
if (deadline < hold.deadline)
|
||||
@@ -26917,14 +26917,14 @@ var TaskGraphView = class {
|
||||
const mount = this.#mount;
|
||||
if (mount === void 0)
|
||||
return;
|
||||
const ops2 = [];
|
||||
const ops3 = [];
|
||||
for (const [key, record2] of mount.records) {
|
||||
const blocked = this.#blocking.blocked(record2);
|
||||
if (blocked === mount.value.tasks[key]?.blocked)
|
||||
continue;
|
||||
ops2.push(blocked === void 0 ? ["d", ["tasks", key, "blocked"]] : ["s", ["tasks", key, "blocked"], blocked]);
|
||||
ops3.push(blocked === void 0 ? ["d", ["tasks", key, "blocked"]] : ["s", ["tasks", key, "blocked"], blocked]);
|
||||
}
|
||||
publish(mount, ops2, BACKGROUND_CONTEXT, (error) => this.#session.report(error));
|
||||
publish(mount, ops3, BACKGROUND_CONTEXT, (error) => this.#session.report(error));
|
||||
}).catch(() => {
|
||||
});
|
||||
}
|
||||
@@ -26948,7 +26948,7 @@ var TaskGraphView = class {
|
||||
}
|
||||
};
|
||||
function advance(mount, publication, context, blocked, report2) {
|
||||
const ops2 = [];
|
||||
const ops3 = [];
|
||||
const changed = /* @__PURE__ */ new Map();
|
||||
const node = (key) => changed.has(key) ? changed.get(key) : mount.value.tasks[key];
|
||||
for (const change of publication.changes) {
|
||||
@@ -26961,7 +26961,7 @@ function advance(mount, publication, context, blocked, report2) {
|
||||
mount.records.delete(key);
|
||||
if (previous === void 0)
|
||||
continue;
|
||||
ops2.push(["d", ["tasks", key]]);
|
||||
ops3.push(["d", ["tasks", key]]);
|
||||
changed.set(key, void 0);
|
||||
continue;
|
||||
}
|
||||
@@ -26970,7 +26970,7 @@ function advance(mount, publication, context, blocked, report2) {
|
||||
const next = nodeOf2(record2, previous?.conversations ?? [], blocked(subject));
|
||||
if (previous !== void 0 && JSON.stringify(next) === JSON.stringify(previous))
|
||||
continue;
|
||||
ops2.push(["s", ["tasks", key], next]);
|
||||
ops3.push(["s", ["tasks", key], next]);
|
||||
changed.set(key, next);
|
||||
}
|
||||
const created = /* @__PURE__ */ new Map();
|
||||
@@ -26983,16 +26983,16 @@ function advance(mount, publication, context, blocked, report2) {
|
||||
}
|
||||
for (const [key, ids] of created) {
|
||||
const conversations = [...node(key).conversations, ...ids].sort((a, b) => a - b);
|
||||
ops2.push(["s", ["tasks", key, "conversations"], conversations]);
|
||||
ops3.push(["s", ["tasks", key, "conversations"], conversations]);
|
||||
}
|
||||
publish(mount, ops2, context, report2);
|
||||
publish(mount, ops3, context, report2);
|
||||
}
|
||||
function publish(mount, ops2, context, report2) {
|
||||
if (ops2.length === 0)
|
||||
function publish(mount, ops3, context, report2) {
|
||||
if (ops3.length === 0)
|
||||
return;
|
||||
mount.value = applyImmutable(mount.value, ops2);
|
||||
mount.value = applyImmutable(mount.value, ops3);
|
||||
const frameContext = withoutAbortSignal(context);
|
||||
const call = { kind: "value", value: mount.value, ops: ops2, retired: false };
|
||||
const call = { kind: "value", value: mount.value, ops: ops3, retired: false };
|
||||
for (const observer of [...mount.observers])
|
||||
contained(() => observer.advance(call, frameContext), report2);
|
||||
}
|
||||
@@ -27218,19 +27218,19 @@ function advance2(id, mount, publication, context, report2) {
|
||||
const previous = windowed.value;
|
||||
let entries2 = previous.entries;
|
||||
let before = previous.before;
|
||||
let ops2 = docOps;
|
||||
let ops3 = docOps;
|
||||
if (appended.length > 0) {
|
||||
ops2 = [...docOps, ["p", ["entries"], entries2.length, 0, inserted]];
|
||||
ops3 = [...docOps, ["p", ["entries"], entries2.length, 0, inserted]];
|
||||
entries2 = entries2.concat(appended);
|
||||
if (entries2.length > windowed.max) {
|
||||
const drop = entries2.length - windowed.min;
|
||||
before = entries2[drop - 1].id;
|
||||
entries2 = entries2.slice(drop);
|
||||
ops2.push(["p", ["entries"], 0, drop, []], ["s", ["before"], before]);
|
||||
ops3.push(["p", ["entries"], 0, drop, []], ["s", ["before"], before]);
|
||||
}
|
||||
}
|
||||
windowed.value = view(mount.conversation, entries2, before, mount.docs);
|
||||
const call = { kind: "value", value: windowed.value, ops: ops2, retired: false };
|
||||
const call = { kind: "value", value: windowed.value, ops: ops3, retired: false };
|
||||
contained(() => windowed.observer.advance(call, frameContext), report2);
|
||||
}
|
||||
}
|
||||
@@ -31368,14 +31368,51 @@ function eventLog(harness2) {
|
||||
}
|
||||
}
|
||||
|
||||
// extensions.ts
|
||||
// environment.ts
|
||||
var CodingTools2 = { ...CodingTools, tools: CodingTools.tools.map((tool) => ({
|
||||
...tool,
|
||||
execute: (args, api, context) => tool.execute(args, { ...api, env: api.env.forTool(api.callId, tool.name) }, context)
|
||||
})) };
|
||||
var Started = defineDoc({
|
||||
kind: "board.started",
|
||||
version: 1,
|
||||
checkpointWhen: () => false,
|
||||
scope: "conversation",
|
||||
history: "latest",
|
||||
fork: "initial",
|
||||
initial: () => ({})
|
||||
});
|
||||
function conversationChains(harness2, context) {
|
||||
const cache = /* @__PURE__ */ new Map();
|
||||
const chain = (id) => {
|
||||
let result = cache.get(id);
|
||||
if (!result) {
|
||||
result = (async () => {
|
||||
const record2 = await harness2.commit((tx) => tx.conversation(id), context);
|
||||
const up = record2?.owner?.conversationId ?? record2?.parent?.conversationId;
|
||||
const starter = (await harness2.snapshot(Started, id, context))?.by;
|
||||
return [.../* @__PURE__ */ new Set([
|
||||
String(id),
|
||||
...up === void 0 ? [] : await chain(up),
|
||||
...starter === void 0 ? [] : await chain(starter)
|
||||
])];
|
||||
})();
|
||||
cache.set(id, result);
|
||||
}
|
||||
return result;
|
||||
};
|
||||
return chain;
|
||||
}
|
||||
var ops = Deno.core.ops;
|
||||
|
||||
// extensions.ts
|
||||
var ops2 = Deno.core.ops;
|
||||
var encoder4 = new TextEncoder();
|
||||
var decoder4 = new TextDecoder();
|
||||
var ExtensionState = defineDoc({ kind: "sausage.extensions", version: 1, scope: "session", checkpointWhen: () => false, initial: () => ({ good: null, current: null, error: null }) });
|
||||
var Metadata = defineDoc({ kind: "sausage.metadata", version: 1, scope: "conversation", history: "latest", fork: "current", initial: () => ({ label: "", title: "" }), checkpointWhen: () => false });
|
||||
async function file(kind, path, bytes2 = []) {
|
||||
const result = await ops.op_file(kind, path, bytes2);
|
||||
const result = await ops2.op_file(kind, path, bytes2);
|
||||
if (!result.ok) throw Object.assign(new Error(result.error.message), result.error);
|
||||
return result.value;
|
||||
}
|
||||
@@ -31398,14 +31435,14 @@ async function exec(command, options) {
|
||||
program: `extension:${options.extension}`,
|
||||
conversations: options.conversations ?? []
|
||||
} : void 0;
|
||||
const rid = ops.op_exec(command, options.cwd, options.timeout, void 0, options.env, options.conversations, tag);
|
||||
const rid = ops2.op_exec(command, options.cwd, options.timeout, void 0, options.env, options.conversations, tag);
|
||||
const abort = () => Deno.core.close(rid);
|
||||
options.signal?.addEventListener("abort", abort, { once: true });
|
||||
const decoders = { stdout: new TextDecoder(), stderr: new TextDecoder() };
|
||||
const result = { code: 0, stdout: "", stderr: "" };
|
||||
try {
|
||||
for (; ; ) {
|
||||
const chunk = await ops.op_exec_next(rid);
|
||||
const chunk = await ops2.op_exec_next(rid);
|
||||
if (!chunk.stream) {
|
||||
if (chunk.error) throw new Error(chunk.error);
|
||||
result.code = chunk.exitCode;
|
||||
@@ -31432,11 +31469,12 @@ async function git(cwd, ...args) {
|
||||
function registerModules() {
|
||||
const modules = { "@earendil-works/sausage-events": events_exports, "@earendil-works/pi-schema": dist_exports, "@earendil-works/pi-durable": dist_exports3, "@earendil-works/pi-durable/tools": tools_exports, "@earendil-works/pi-ai": dist_exports4, "@earendil-works/pi-ai/models": models_exports, "@earendil-works/chord": dist_exports2, "@earendil-works/chord/context": context_exports };
|
||||
globalThis.__sausage = { modules };
|
||||
ops.op_modules(Object.fromEntries(Object.entries(modules).map(([name, value2]) => [name, Object.keys(value2)])));
|
||||
ops2.op_modules(Object.fromEntries(Object.entries(modules).map(([name, value2]) => [name, Object.keys(value2)])));
|
||||
}
|
||||
async function loadExtensions(harness2, registry2, storage2, models2, context, builtins = []) {
|
||||
registerModules();
|
||||
const eventService = eventLog(() => harness2);
|
||||
const chain = conversationChains(harness2, context);
|
||||
const repo = "/src/sausage";
|
||||
let providers = /* @__PURE__ */ new Map();
|
||||
const resources = /* @__PURE__ */ new Map();
|
||||
@@ -31447,7 +31485,7 @@ async function loadExtensions(harness2, registry2, storage2, models2, context, b
|
||||
let queue2 = Promise.resolve();
|
||||
const publish2 = async () => {
|
||||
await harness2.commit(async (tx) => Object.assign(await tx.doc(ExtensionState), state), context);
|
||||
ops.op_extension_status(JSON.stringify(state));
|
||||
ops2.op_extension_status(JSON.stringify(state));
|
||||
await fs.write("/src/sausage/.git/sausage-last-good", state.good ?? "");
|
||||
};
|
||||
const dispose = async (entries2) => {
|
||||
@@ -31455,7 +31493,7 @@ async function loadExtensions(harness2, registry2, storage2, models2, context, b
|
||||
try {
|
||||
await cleanup();
|
||||
} catch (error) {
|
||||
ops.op_output(`Extension cleanup: ${String(error)}
|
||||
ops2.op_output(`Extension cleanup: ${String(error)}
|
||||
`);
|
||||
}
|
||||
}
|
||||
@@ -31488,6 +31526,7 @@ async function loadExtensions(harness2, registry2, storage2, models2, context, b
|
||||
fs,
|
||||
exec: (command, options) => exec(command, { ...options, extension: name }),
|
||||
git,
|
||||
chain,
|
||||
implement: (api, value2) => {
|
||||
staged.set(api.name, value2);
|
||||
},
|
||||
@@ -31510,12 +31549,12 @@ async function loadExtensions(harness2, registry2, storage2, models2, context, b
|
||||
onClose: (cb) => close.push(cb),
|
||||
notice: (message) => {
|
||||
state.notice = `${name}: ${message}`;
|
||||
ops.op_extension_status(JSON.stringify(state));
|
||||
ops.op_output(`${name}: ${message}
|
||||
ops2.op_extension_status(JSON.stringify(state));
|
||||
ops2.op_output(`${name}: ${message}
|
||||
`);
|
||||
if (!notices.has(message)) {
|
||||
notices.add(message);
|
||||
void harness2.commit((tx) => eventService.emit(tx, NoticeEvent, { extension: name, message, level: "warning" }), context).catch((error) => ops.op_output(`Recording a notice failed: ${String(error)}
|
||||
void harness2.commit((tx) => eventService.emit(tx, NoticeEvent, { extension: name, message, level: "warning" }), context).catch((error) => ops2.op_output(`Recording a notice failed: ${String(error)}
|
||||
`));
|
||||
}
|
||||
}
|
||||
@@ -31594,7 +31633,7 @@ async function loadExtensions(harness2, registry2, storage2, models2, context, b
|
||||
tip = await git(repo, "rev-parse", "refs/heads/main");
|
||||
} catch (error) {
|
||||
state.error = String(error);
|
||||
ops.op_extension_status(JSON.stringify(state));
|
||||
ops2.op_extension_status(JSON.stringify(state));
|
||||
return;
|
||||
}
|
||||
try {
|
||||
@@ -31603,8 +31642,8 @@ async function loadExtensions(harness2, registry2, storage2, models2, context, b
|
||||
if (error.code === "not_found") settings = {};
|
||||
else {
|
||||
state.notice = `Settings: ${String(error)}`;
|
||||
ops.op_extension_status(JSON.stringify(state));
|
||||
ops.op_output(`${state.notice}
|
||||
ops2.op_extension_status(JSON.stringify(state));
|
||||
ops2.op_output(`${state.notice}
|
||||
`);
|
||||
}
|
||||
}
|
||||
@@ -31620,11 +31659,11 @@ Last good: ${fallback}` : failed;
|
||||
await publish2();
|
||||
};
|
||||
const enqueue2 = () => queue2 = queue2.then(reload);
|
||||
const rid = ops.op_watch([`${repo}/.git/refs/heads/main`, `${repo}/.git/packed-refs`, `${repo}/settings.json`]);
|
||||
const rid = ops2.op_watch([`${repo}/.git/refs/heads/main`, `${repo}/.git/packed-refs`, `${repo}/settings.json`]);
|
||||
await enqueue2();
|
||||
const pending = (async () => {
|
||||
while (active) {
|
||||
await ops.op_watch_next(rid);
|
||||
await ops2.op_watch_next(rid);
|
||||
if (active) await enqueue2();
|
||||
}
|
||||
})();
|
||||
|
||||
+109
-70
@@ -6190,27 +6190,27 @@ var REPLACE_TAKE = { kind: "replace" };
|
||||
var PendingOps = class {
|
||||
#state = EMPTY;
|
||||
/** Queue the ops of one commit. */
|
||||
push(ops2) {
|
||||
if (ops2.length === 0)
|
||||
push(ops3) {
|
||||
if (ops3.length === 0)
|
||||
return;
|
||||
const state = this.#state;
|
||||
if (ops2[0][0] === "r") {
|
||||
this.#state = { kind: "batch", ops: ops2 };
|
||||
if (ops3[0][0] === "r") {
|
||||
this.#state = { kind: "batch", ops: ops3 };
|
||||
return;
|
||||
}
|
||||
switch (state.kind) {
|
||||
case "empty":
|
||||
this.#state = { kind: "batch", ops: ops2 };
|
||||
this.#state = { kind: "batch", ops: ops3 };
|
||||
return;
|
||||
case "batch": {
|
||||
const folding = { kind: "folding", ops: [], leaves: /* @__PURE__ */ new Map() };
|
||||
this.#state = folding;
|
||||
if (this.#fold(state.ops, folding))
|
||||
this.#fold(ops2, folding);
|
||||
this.#fold(ops3, folding);
|
||||
return;
|
||||
}
|
||||
case "folding":
|
||||
this.#fold(ops2, state);
|
||||
this.#fold(ops3, state);
|
||||
return;
|
||||
case "replace":
|
||||
return;
|
||||
@@ -6241,8 +6241,8 @@ var PendingOps = class {
|
||||
}
|
||||
}
|
||||
/** Fold `ops` into `folding`; false once the queue overflowed into `replace`. */
|
||||
#fold(ops2, folding) {
|
||||
for (const op2 of ops2) {
|
||||
#fold(ops3, folding) {
|
||||
for (const op2 of ops3) {
|
||||
switch (op2[0]) {
|
||||
case "r":
|
||||
folding.ops.length = 0;
|
||||
@@ -6511,12 +6511,12 @@ var PathError = class extends Error {
|
||||
this.name = "PathError";
|
||||
}
|
||||
};
|
||||
function apply(target, ops2) {
|
||||
return applyOps(target, ops2);
|
||||
function apply(target, ops3) {
|
||||
return applyOps(target, ops3);
|
||||
}
|
||||
function applyOps(target, ops2) {
|
||||
function applyOps(target, ops3) {
|
||||
let root = target;
|
||||
for (const op2 of ops2) {
|
||||
for (const op2 of ops3) {
|
||||
assertValidOp(op2);
|
||||
if (op2[0] === "r") {
|
||||
root = op2[1];
|
||||
@@ -6585,14 +6585,14 @@ function applyOps(target, ops2) {
|
||||
}
|
||||
return root;
|
||||
}
|
||||
function applyImmutable(target, ops2) {
|
||||
return applyImmutableBatches(target, [ops2]);
|
||||
function applyImmutable(target, ops3) {
|
||||
return applyImmutableBatches(target, [ops3]);
|
||||
}
|
||||
function applyImmutableBatches(target, batches) {
|
||||
let root = target;
|
||||
const owned = /* @__PURE__ */ new WeakSet();
|
||||
for (const ops2 of batches) {
|
||||
for (const op2 of ops2) {
|
||||
for (const ops3 of batches) {
|
||||
for (const op2 of ops3) {
|
||||
assertValidOp(op2);
|
||||
if (op2[0] === "r") {
|
||||
root = op2[1];
|
||||
@@ -6706,10 +6706,10 @@ function encoder() {
|
||||
let nextId = 0;
|
||||
let previous;
|
||||
return {
|
||||
encode(ops2) {
|
||||
encode(ops3) {
|
||||
previous = void 0;
|
||||
const out = [];
|
||||
for (const op2 of ops2) {
|
||||
for (const op2 of ops3) {
|
||||
if (op2[0] === "r") {
|
||||
out.push(op2);
|
||||
seen.clear();
|
||||
@@ -6959,9 +6959,9 @@ var Source = class {
|
||||
return [];
|
||||
}
|
||||
};
|
||||
function unbox(ops2, value2) {
|
||||
function unbox(ops3, value2) {
|
||||
const result = [];
|
||||
for (const op2 of ops2) {
|
||||
for (const op2 of ops3) {
|
||||
if (op2[0] === "r" || op2[1].length === 0)
|
||||
return [["r", value2]];
|
||||
const path = op2[1].slice(1);
|
||||
@@ -7055,8 +7055,8 @@ var Cell = class extends Source {
|
||||
throw notReady(`state ${this.key} (its init has not run)`);
|
||||
const before = this.value;
|
||||
const diff = options.diff ?? sharesStructure(before, value2);
|
||||
const ops2 = diff ? diffRevisions(before, value2) : void 0;
|
||||
this.#publish(this.#tracker.prepareReplace({ v: value2 }), "ops", true, ops2);
|
||||
const ops3 = diff ? diffRevisions(before, value2) : void 0;
|
||||
this.#publish(this.#tracker.prepareReplace({ v: value2 }), "ops", true, ops3);
|
||||
}
|
||||
/**
|
||||
* Installs a migrated value and its schema (reload commit). The caller validated the value and the derivations
|
||||
@@ -7067,7 +7067,7 @@ var Cell = class extends Source {
|
||||
this.#publish(this.#tracker.prepareReplace({ v: value2 }), "reset", false);
|
||||
}
|
||||
/** Adopts this revision and every derived one, then queues all notifications (cell first, then derivations). */
|
||||
#publish(prepared, type, validate2 = true, ops2) {
|
||||
#publish(prepared, type, validate2 = true, ops3) {
|
||||
if (prepared.ops.length === 0 && type === "ops") {
|
||||
this.#tracker.adopt(prepared);
|
||||
return;
|
||||
@@ -7084,7 +7084,7 @@ var Cell = class extends Source {
|
||||
}
|
||||
holdPublications(() => {
|
||||
this.#tracker.adopt(prepared);
|
||||
const published = ops2 !== void 0 && ops2.length > 0 ? ops2 : unbox(prepared.ops, value2);
|
||||
const published = ops3 !== void 0 && ops3.length > 0 ? ops3 : unbox(prepared.ops, value2);
|
||||
this.fanout.emit(type === "reset" ? { type, value: value2 } : { type, ops: published, value: value2 });
|
||||
for (const [derived, next] of candidates)
|
||||
derived.install(next);
|
||||
@@ -7197,9 +7197,9 @@ var Derived = class extends Source {
|
||||
install(next) {
|
||||
const before = this.#value;
|
||||
this.#value = next;
|
||||
const ops2 = before === void 0 || next === void 0 ? [["r", next ?? null]] : diffRevisions(before, next);
|
||||
if (ops2.length > 0)
|
||||
this.fanout.emit({ type: "ops", ops: ops2, value: next });
|
||||
const ops3 = before === void 0 || next === void 0 ? [["r", next ?? null]] : diffRevisions(before, next);
|
||||
if (ops3.length > 0)
|
||||
this.fanout.emit({ type: "ops", ops: ops3, value: next });
|
||||
}
|
||||
};
|
||||
var isSource = (value2) => value2 instanceof Source;
|
||||
@@ -11252,10 +11252,10 @@ var RemoteBinding = class {
|
||||
const target = pending?.values.get(name) ?? this.members.get(name);
|
||||
if (target === void 0)
|
||||
return;
|
||||
const ops2 = target.decoder.decode(frame.ops ?? []);
|
||||
target.value = applyImmutable(target.value, ops2);
|
||||
const ops3 = target.decoder.decode(frame.ops ?? []);
|
||||
target.value = applyImmutable(target.value, ops3);
|
||||
if (target === this.members.get(name))
|
||||
this.#deliver(name, { type: "ops", ops: ops2, value: target.value });
|
||||
this.#deliver(name, { type: "ops", ops: ops3, value: target.value });
|
||||
return;
|
||||
}
|
||||
const fresh = { value: frame.value, decoder: decoder() };
|
||||
@@ -13079,8 +13079,8 @@ var Watch = class extends WatchLifecycle {
|
||||
this.#retired = false;
|
||||
if (lone !== void 0)
|
||||
return lone;
|
||||
const ops2 = batch.kind === "ops" ? batch.ops : [["r", value2]];
|
||||
return { kind: "value", value: value2, ops: ops2, retired };
|
||||
const ops3 = batch.kind === "ops" ? batch.ops : [["r", value2]];
|
||||
return { kind: "value", value: value2, ops: ops3, retired };
|
||||
}
|
||||
/** The pending failure, or the value after one as a root replacement (no ops were kept since the failure). */
|
||||
#nextAfterFailure() {
|
||||
@@ -14845,7 +14845,7 @@ function describe(plan, record2) {
|
||||
const change = plan.change;
|
||||
if (change === void 0 || !publishes(plan))
|
||||
return void 0;
|
||||
const ops2 = change.loaded === void 0 ? EMPTY_OPERATIONS : change.prepared.ops;
|
||||
const ops3 = change.loaded === void 0 ? EMPTY_OPERATIONS : change.prepared.ops;
|
||||
return {
|
||||
type: "document",
|
||||
kind: "change",
|
||||
@@ -14853,11 +14853,11 @@ function describe(plan, record2) {
|
||||
conversationId,
|
||||
version: change.version,
|
||||
value: change.prepared.value,
|
||||
ops: ops2
|
||||
ops: ops3
|
||||
};
|
||||
}
|
||||
function storeHeld(hold, ops2, value2, version) {
|
||||
hold.pending.push(ops2);
|
||||
function storeHeld(hold, ops3, value2, version) {
|
||||
hold.pending.push(ops3);
|
||||
const taken = hold.pending.take();
|
||||
if (taken.kind === "replace")
|
||||
return { content: { version, kind: "base", value: value2 }, ops: [["r", value2]] };
|
||||
@@ -15938,7 +15938,7 @@ var SessionImpl = class {
|
||||
return { kind: "adoption", error };
|
||||
}
|
||||
const deadline = this.#clock() + withinMs;
|
||||
for (const { loaded, ops: ops2, base } of held2) {
|
||||
for (const { loaded, ops: ops3, base } of held2) {
|
||||
const id = loaded.record.id;
|
||||
let hold = this.#holds.get(id);
|
||||
if (hold === void 0) {
|
||||
@@ -15946,7 +15946,7 @@ var SessionImpl = class {
|
||||
hold = { loaded, pending: new PendingOps(), base: false, deadline, unpin };
|
||||
this.#holds.set(id, hold);
|
||||
}
|
||||
hold.pending.push(ops2);
|
||||
hold.pending.push(ops3);
|
||||
if (base)
|
||||
hold.base = true;
|
||||
if (deadline < hold.deadline)
|
||||
@@ -25033,14 +25033,14 @@ var TaskGraphView = class {
|
||||
const mount = this.#mount;
|
||||
if (mount === void 0)
|
||||
return;
|
||||
const ops2 = [];
|
||||
const ops3 = [];
|
||||
for (const [key, record2] of mount.records) {
|
||||
const blocked = this.#blocking.blocked(record2);
|
||||
if (blocked === mount.value.tasks[key]?.blocked)
|
||||
continue;
|
||||
ops2.push(blocked === void 0 ? ["d", ["tasks", key, "blocked"]] : ["s", ["tasks", key, "blocked"], blocked]);
|
||||
ops3.push(blocked === void 0 ? ["d", ["tasks", key, "blocked"]] : ["s", ["tasks", key, "blocked"], blocked]);
|
||||
}
|
||||
publish(mount, ops2, BACKGROUND_CONTEXT, (error) => this.#session.report(error));
|
||||
publish(mount, ops3, BACKGROUND_CONTEXT, (error) => this.#session.report(error));
|
||||
}).catch(() => {
|
||||
});
|
||||
}
|
||||
@@ -25064,7 +25064,7 @@ var TaskGraphView = class {
|
||||
}
|
||||
};
|
||||
function advance(mount, publication, context, blocked, report2) {
|
||||
const ops2 = [];
|
||||
const ops3 = [];
|
||||
const changed = /* @__PURE__ */ new Map();
|
||||
const node = (key) => changed.has(key) ? changed.get(key) : mount.value.tasks[key];
|
||||
for (const change of publication.changes) {
|
||||
@@ -25077,7 +25077,7 @@ function advance(mount, publication, context, blocked, report2) {
|
||||
mount.records.delete(key);
|
||||
if (previous === void 0)
|
||||
continue;
|
||||
ops2.push(["d", ["tasks", key]]);
|
||||
ops3.push(["d", ["tasks", key]]);
|
||||
changed.set(key, void 0);
|
||||
continue;
|
||||
}
|
||||
@@ -25086,7 +25086,7 @@ function advance(mount, publication, context, blocked, report2) {
|
||||
const next = nodeOf2(record2, previous?.conversations ?? [], blocked(subject));
|
||||
if (previous !== void 0 && JSON.stringify(next) === JSON.stringify(previous))
|
||||
continue;
|
||||
ops2.push(["s", ["tasks", key], next]);
|
||||
ops3.push(["s", ["tasks", key], next]);
|
||||
changed.set(key, next);
|
||||
}
|
||||
const created = /* @__PURE__ */ new Map();
|
||||
@@ -25099,16 +25099,16 @@ function advance(mount, publication, context, blocked, report2) {
|
||||
}
|
||||
for (const [key, ids] of created) {
|
||||
const conversations = [...node(key).conversations, ...ids].sort((a, b) => a - b);
|
||||
ops2.push(["s", ["tasks", key, "conversations"], conversations]);
|
||||
ops3.push(["s", ["tasks", key, "conversations"], conversations]);
|
||||
}
|
||||
publish(mount, ops2, context, report2);
|
||||
publish(mount, ops3, context, report2);
|
||||
}
|
||||
function publish(mount, ops2, context, report2) {
|
||||
if (ops2.length === 0)
|
||||
function publish(mount, ops3, context, report2) {
|
||||
if (ops3.length === 0)
|
||||
return;
|
||||
mount.value = applyImmutable(mount.value, ops2);
|
||||
mount.value = applyImmutable(mount.value, ops3);
|
||||
const frameContext = withoutAbortSignal(context);
|
||||
const call = { kind: "value", value: mount.value, ops: ops2, retired: false };
|
||||
const call = { kind: "value", value: mount.value, ops: ops3, retired: false };
|
||||
for (const observer of [...mount.observers])
|
||||
contained(() => observer.advance(call, frameContext), report2);
|
||||
}
|
||||
@@ -25334,19 +25334,19 @@ function advance2(id, mount, publication, context, report2) {
|
||||
const previous = windowed.value;
|
||||
let entries2 = previous.entries;
|
||||
let before = previous.before;
|
||||
let ops2 = docOps;
|
||||
let ops3 = docOps;
|
||||
if (appended.length > 0) {
|
||||
ops2 = [...docOps, ["p", ["entries"], entries2.length, 0, inserted]];
|
||||
ops3 = [...docOps, ["p", ["entries"], entries2.length, 0, inserted]];
|
||||
entries2 = entries2.concat(appended);
|
||||
if (entries2.length > windowed.max) {
|
||||
const drop = entries2.length - windowed.min;
|
||||
before = entries2[drop - 1].id;
|
||||
entries2 = entries2.slice(drop);
|
||||
ops2.push(["p", ["entries"], 0, drop, []], ["s", ["before"], before]);
|
||||
ops3.push(["p", ["entries"], 0, drop, []], ["s", ["before"], before]);
|
||||
}
|
||||
}
|
||||
windowed.value = view(mount.conversation, entries2, before, mount.docs);
|
||||
const call = { kind: "value", value: windowed.value, ops: ops2, retired: false };
|
||||
const call = { kind: "value", value: windowed.value, ops: ops3, retired: false };
|
||||
contained(() => windowed.observer.advance(call, frameContext), report2);
|
||||
}
|
||||
}
|
||||
@@ -31368,14 +31368,51 @@ function StringEnum(values, options) {
|
||||
};
|
||||
}
|
||||
|
||||
// extensions.ts
|
||||
// environment.ts
|
||||
var CodingTools2 = { ...CodingTools, tools: CodingTools.tools.map((tool) => ({
|
||||
...tool,
|
||||
execute: (args, api, context) => tool.execute(args, { ...api, env: api.env.forTool(api.callId, tool.name) }, context)
|
||||
})) };
|
||||
var Started = defineDoc({
|
||||
kind: "board.started",
|
||||
version: 1,
|
||||
checkpointWhen: () => false,
|
||||
scope: "conversation",
|
||||
history: "latest",
|
||||
fork: "initial",
|
||||
initial: () => ({})
|
||||
});
|
||||
function conversationChains(harness2, context) {
|
||||
const cache = /* @__PURE__ */ new Map();
|
||||
const chain = (id) => {
|
||||
let result = cache.get(id);
|
||||
if (!result) {
|
||||
result = (async () => {
|
||||
const record2 = await harness2.commit((tx) => tx.conversation(id), context);
|
||||
const up = record2?.owner?.conversationId ?? record2?.parent?.conversationId;
|
||||
const starter = (await harness2.snapshot(Started, id, context))?.by;
|
||||
return [.../* @__PURE__ */ new Set([
|
||||
String(id),
|
||||
...up === void 0 ? [] : await chain(up),
|
||||
...starter === void 0 ? [] : await chain(starter)
|
||||
])];
|
||||
})();
|
||||
cache.set(id, result);
|
||||
}
|
||||
return result;
|
||||
};
|
||||
return chain;
|
||||
}
|
||||
var ops = Deno.core.ops;
|
||||
|
||||
// extensions.ts
|
||||
var ops2 = Deno.core.ops;
|
||||
var encoder4 = new TextEncoder();
|
||||
var decoder4 = new TextDecoder();
|
||||
var ExtensionState = defineDoc({ kind: "sausage.extensions", version: 1, scope: "session", checkpointWhen: () => false, initial: () => ({ good: null, current: null, error: null }) });
|
||||
var Metadata = defineDoc({ kind: "sausage.metadata", version: 1, scope: "conversation", history: "latest", fork: "current", initial: () => ({ label: "", title: "" }), checkpointWhen: () => false });
|
||||
async function file(kind, path, bytes2 = []) {
|
||||
const result = await ops.op_file(kind, path, bytes2);
|
||||
const result = await ops2.op_file(kind, path, bytes2);
|
||||
if (!result.ok) throw Object.assign(new Error(result.error.message), result.error);
|
||||
return result.value;
|
||||
}
|
||||
@@ -31398,14 +31435,14 @@ async function exec(command, options) {
|
||||
program: `extension:${options.extension}`,
|
||||
conversations: options.conversations ?? []
|
||||
} : void 0;
|
||||
const rid = ops.op_exec(command, options.cwd, options.timeout, void 0, options.env, options.conversations, tag);
|
||||
const rid = ops2.op_exec(command, options.cwd, options.timeout, void 0, options.env, options.conversations, tag);
|
||||
const abort = () => Deno.core.close(rid);
|
||||
options.signal?.addEventListener("abort", abort, { once: true });
|
||||
const decoders = { stdout: new TextDecoder(), stderr: new TextDecoder() };
|
||||
const result = { code: 0, stdout: "", stderr: "" };
|
||||
try {
|
||||
for (; ; ) {
|
||||
const chunk = await ops.op_exec_next(rid);
|
||||
const chunk = await ops2.op_exec_next(rid);
|
||||
if (!chunk.stream) {
|
||||
if (chunk.error) throw new Error(chunk.error);
|
||||
result.code = chunk.exitCode;
|
||||
@@ -31432,11 +31469,12 @@ async function git(cwd, ...args) {
|
||||
function registerModules() {
|
||||
const modules = { "@earendil-works/sausage-events": events_exports, "@earendil-works/pi-schema": dist_exports, "@earendil-works/pi-durable": dist_exports3, "@earendil-works/pi-durable/tools": tools_exports, "@earendil-works/pi-ai": dist_exports4, "@earendil-works/pi-ai/models": models_exports, "@earendil-works/chord": dist_exports2, "@earendil-works/chord/context": context_exports };
|
||||
globalThis.__sausage = { modules };
|
||||
ops.op_modules(Object.fromEntries(Object.entries(modules).map(([name, value2]) => [name, Object.keys(value2)])));
|
||||
ops2.op_modules(Object.fromEntries(Object.entries(modules).map(([name, value2]) => [name, Object.keys(value2)])));
|
||||
}
|
||||
async function loadExtensions(harness2, registry2, storage2, models2, context, builtins = []) {
|
||||
registerModules();
|
||||
const eventService = eventLog(() => harness2);
|
||||
const chain = conversationChains(harness2, context);
|
||||
const repo = "/src/sausage";
|
||||
let providers = /* @__PURE__ */ new Map();
|
||||
const resources = /* @__PURE__ */ new Map();
|
||||
@@ -31447,7 +31485,7 @@ async function loadExtensions(harness2, registry2, storage2, models2, context, b
|
||||
let queue2 = Promise.resolve();
|
||||
const publish2 = async () => {
|
||||
await harness2.commit(async (tx) => Object.assign(await tx.doc(ExtensionState), state), context);
|
||||
ops.op_extension_status(JSON.stringify(state));
|
||||
ops2.op_extension_status(JSON.stringify(state));
|
||||
await fs.write("/src/sausage/.git/sausage-last-good", state.good ?? "");
|
||||
};
|
||||
const dispose = async (entries2) => {
|
||||
@@ -31455,7 +31493,7 @@ async function loadExtensions(harness2, registry2, storage2, models2, context, b
|
||||
try {
|
||||
await cleanup();
|
||||
} catch (error) {
|
||||
ops.op_output(`Extension cleanup: ${String(error)}
|
||||
ops2.op_output(`Extension cleanup: ${String(error)}
|
||||
`);
|
||||
}
|
||||
}
|
||||
@@ -31488,6 +31526,7 @@ async function loadExtensions(harness2, registry2, storage2, models2, context, b
|
||||
fs,
|
||||
exec: (command, options) => exec(command, { ...options, extension: name }),
|
||||
git,
|
||||
chain,
|
||||
implement: (api, value2) => {
|
||||
staged.set(api.name, value2);
|
||||
},
|
||||
@@ -31510,12 +31549,12 @@ async function loadExtensions(harness2, registry2, storage2, models2, context, b
|
||||
onClose: (cb) => close.push(cb),
|
||||
notice: (message) => {
|
||||
state.notice = `${name}: ${message}`;
|
||||
ops.op_extension_status(JSON.stringify(state));
|
||||
ops.op_output(`${name}: ${message}
|
||||
ops2.op_extension_status(JSON.stringify(state));
|
||||
ops2.op_output(`${name}: ${message}
|
||||
`);
|
||||
if (!notices.has(message)) {
|
||||
notices.add(message);
|
||||
void harness2.commit((tx) => eventService.emit(tx, NoticeEvent, { extension: name, message, level: "warning" }), context).catch((error) => ops.op_output(`Recording a notice failed: ${String(error)}
|
||||
void harness2.commit((tx) => eventService.emit(tx, NoticeEvent, { extension: name, message, level: "warning" }), context).catch((error) => ops2.op_output(`Recording a notice failed: ${String(error)}
|
||||
`));
|
||||
}
|
||||
}
|
||||
@@ -31594,7 +31633,7 @@ async function loadExtensions(harness2, registry2, storage2, models2, context, b
|
||||
tip = await git(repo, "rev-parse", "refs/heads/main");
|
||||
} catch (error) {
|
||||
state.error = String(error);
|
||||
ops.op_extension_status(JSON.stringify(state));
|
||||
ops2.op_extension_status(JSON.stringify(state));
|
||||
return;
|
||||
}
|
||||
try {
|
||||
@@ -31603,8 +31642,8 @@ async function loadExtensions(harness2, registry2, storage2, models2, context, b
|
||||
if (error.code === "not_found") settings = {};
|
||||
else {
|
||||
state.notice = `Settings: ${String(error)}`;
|
||||
ops.op_extension_status(JSON.stringify(state));
|
||||
ops.op_output(`${state.notice}
|
||||
ops2.op_extension_status(JSON.stringify(state));
|
||||
ops2.op_output(`${state.notice}
|
||||
`);
|
||||
}
|
||||
}
|
||||
@@ -31620,11 +31659,11 @@ Last good: ${fallback}` : failed;
|
||||
await publish2();
|
||||
};
|
||||
const enqueue2 = () => queue2 = queue2.then(reload);
|
||||
const rid = ops.op_watch([`${repo}/.git/refs/heads/main`, `${repo}/.git/packed-refs`, `${repo}/settings.json`]);
|
||||
const rid = ops2.op_watch([`${repo}/.git/refs/heads/main`, `${repo}/.git/packed-refs`, `${repo}/settings.json`]);
|
||||
await enqueue2();
|
||||
const pending = (async () => {
|
||||
while (active) {
|
||||
await ops.op_watch_next(rid);
|
||||
await ops2.op_watch_next(rid);
|
||||
if (active) await enqueue2();
|
||||
}
|
||||
})();
|
||||
|
||||
@@ -8946,27 +8946,27 @@ var REPLACE_TAKE = { kind: "replace" };
|
||||
var PendingOps = class {
|
||||
#state = EMPTY;
|
||||
/** Queue the ops of one commit. */
|
||||
push(ops2) {
|
||||
if (ops2.length === 0)
|
||||
push(ops3) {
|
||||
if (ops3.length === 0)
|
||||
return;
|
||||
const state = this.#state;
|
||||
if (ops2[0][0] === "r") {
|
||||
this.#state = { kind: "batch", ops: ops2 };
|
||||
if (ops3[0][0] === "r") {
|
||||
this.#state = { kind: "batch", ops: ops3 };
|
||||
return;
|
||||
}
|
||||
switch (state.kind) {
|
||||
case "empty":
|
||||
this.#state = { kind: "batch", ops: ops2 };
|
||||
this.#state = { kind: "batch", ops: ops3 };
|
||||
return;
|
||||
case "batch": {
|
||||
const folding = { kind: "folding", ops: [], leaves: /* @__PURE__ */ new Map() };
|
||||
this.#state = folding;
|
||||
if (this.#fold(state.ops, folding))
|
||||
this.#fold(ops2, folding);
|
||||
this.#fold(ops3, folding);
|
||||
return;
|
||||
}
|
||||
case "folding":
|
||||
this.#fold(ops2, state);
|
||||
this.#fold(ops3, state);
|
||||
return;
|
||||
case "replace":
|
||||
return;
|
||||
@@ -8997,8 +8997,8 @@ var PendingOps = class {
|
||||
}
|
||||
}
|
||||
/** Fold `ops` into `folding`; false once the queue overflowed into `replace`. */
|
||||
#fold(ops2, folding) {
|
||||
for (const op2 of ops2) {
|
||||
#fold(ops3, folding) {
|
||||
for (const op2 of ops3) {
|
||||
switch (op2[0]) {
|
||||
case "r":
|
||||
folding.ops.length = 0;
|
||||
@@ -9267,12 +9267,12 @@ var PathError = class extends Error {
|
||||
this.name = "PathError";
|
||||
}
|
||||
};
|
||||
function apply(target, ops2) {
|
||||
return applyOps(target, ops2);
|
||||
function apply(target, ops3) {
|
||||
return applyOps(target, ops3);
|
||||
}
|
||||
function applyOps(target, ops2) {
|
||||
function applyOps(target, ops3) {
|
||||
let root2 = target;
|
||||
for (const op2 of ops2) {
|
||||
for (const op2 of ops3) {
|
||||
assertValidOp(op2);
|
||||
if (op2[0] === "r") {
|
||||
root2 = op2[1];
|
||||
@@ -9341,14 +9341,14 @@ function applyOps(target, ops2) {
|
||||
}
|
||||
return root2;
|
||||
}
|
||||
function applyImmutable(target, ops2) {
|
||||
return applyImmutableBatches(target, [ops2]);
|
||||
function applyImmutable(target, ops3) {
|
||||
return applyImmutableBatches(target, [ops3]);
|
||||
}
|
||||
function applyImmutableBatches(target, batches) {
|
||||
let root2 = target;
|
||||
const owned = /* @__PURE__ */ new WeakSet();
|
||||
for (const ops2 of batches) {
|
||||
for (const op2 of ops2) {
|
||||
for (const ops3 of batches) {
|
||||
for (const op2 of ops3) {
|
||||
assertValidOp(op2);
|
||||
if (op2[0] === "r") {
|
||||
root2 = op2[1];
|
||||
@@ -9462,10 +9462,10 @@ function encoder() {
|
||||
let nextId = 0;
|
||||
let previous;
|
||||
return {
|
||||
encode(ops2) {
|
||||
encode(ops3) {
|
||||
previous = void 0;
|
||||
const out = [];
|
||||
for (const op2 of ops2) {
|
||||
for (const op2 of ops3) {
|
||||
if (op2[0] === "r") {
|
||||
out.push(op2);
|
||||
seen.clear();
|
||||
@@ -9715,9 +9715,9 @@ var Source = class {
|
||||
return [];
|
||||
}
|
||||
};
|
||||
function unbox(ops2, value2) {
|
||||
function unbox(ops3, value2) {
|
||||
const result = [];
|
||||
for (const op2 of ops2) {
|
||||
for (const op2 of ops3) {
|
||||
if (op2[0] === "r" || op2[1].length === 0)
|
||||
return [["r", value2]];
|
||||
const path = op2[1].slice(1);
|
||||
@@ -9811,8 +9811,8 @@ var Cell = class extends Source {
|
||||
throw notReady(`state ${this.key} (its init has not run)`);
|
||||
const before = this.value;
|
||||
const diff = options.diff ?? sharesStructure(before, value2);
|
||||
const ops2 = diff ? diffRevisions(before, value2) : void 0;
|
||||
this.#publish(this.#tracker.prepareReplace({ v: value2 }), "ops", true, ops2);
|
||||
const ops3 = diff ? diffRevisions(before, value2) : void 0;
|
||||
this.#publish(this.#tracker.prepareReplace({ v: value2 }), "ops", true, ops3);
|
||||
}
|
||||
/**
|
||||
* Installs a migrated value and its schema (reload commit). The caller validated the value and the derivations
|
||||
@@ -9823,7 +9823,7 @@ var Cell = class extends Source {
|
||||
this.#publish(this.#tracker.prepareReplace({ v: value2 }), "reset", false);
|
||||
}
|
||||
/** Adopts this revision and every derived one, then queues all notifications (cell first, then derivations). */
|
||||
#publish(prepared, type, validate2 = true, ops2) {
|
||||
#publish(prepared, type, validate2 = true, ops3) {
|
||||
if (prepared.ops.length === 0 && type === "ops") {
|
||||
this.#tracker.adopt(prepared);
|
||||
return;
|
||||
@@ -9840,7 +9840,7 @@ var Cell = class extends Source {
|
||||
}
|
||||
holdPublications(() => {
|
||||
this.#tracker.adopt(prepared);
|
||||
const published = ops2 !== void 0 && ops2.length > 0 ? ops2 : unbox(prepared.ops, value2);
|
||||
const published = ops3 !== void 0 && ops3.length > 0 ? ops3 : unbox(prepared.ops, value2);
|
||||
this.fanout.emit(type === "reset" ? { type, value: value2 } : { type, ops: published, value: value2 });
|
||||
for (const [derived, next] of candidates)
|
||||
derived.install(next);
|
||||
@@ -9953,9 +9953,9 @@ var Derived = class extends Source {
|
||||
install(next) {
|
||||
const before = this.#value;
|
||||
this.#value = next;
|
||||
const ops2 = before === void 0 || next === void 0 ? [["r", next ?? null]] : diffRevisions(before, next);
|
||||
if (ops2.length > 0)
|
||||
this.fanout.emit({ type: "ops", ops: ops2, value: next });
|
||||
const ops3 = before === void 0 || next === void 0 ? [["r", next ?? null]] : diffRevisions(before, next);
|
||||
if (ops3.length > 0)
|
||||
this.fanout.emit({ type: "ops", ops: ops3, value: next });
|
||||
}
|
||||
};
|
||||
var isSource = (value2) => value2 instanceof Source;
|
||||
@@ -14008,10 +14008,10 @@ var RemoteBinding = class {
|
||||
const target = pending?.values.get(name) ?? this.members.get(name);
|
||||
if (target === void 0)
|
||||
return;
|
||||
const ops2 = target.decoder.decode(frame.ops ?? []);
|
||||
target.value = applyImmutable(target.value, ops2);
|
||||
const ops3 = target.decoder.decode(frame.ops ?? []);
|
||||
target.value = applyImmutable(target.value, ops3);
|
||||
if (target === this.members.get(name))
|
||||
this.#deliver(name, { type: "ops", ops: ops2, value: target.value });
|
||||
this.#deliver(name, { type: "ops", ops: ops3, value: target.value });
|
||||
return;
|
||||
}
|
||||
const fresh = { value: frame.value, decoder: decoder() };
|
||||
@@ -15835,8 +15835,8 @@ var Watch = class extends WatchLifecycle {
|
||||
this.#retired = false;
|
||||
if (lone !== void 0)
|
||||
return lone;
|
||||
const ops2 = batch.kind === "ops" ? batch.ops : [["r", value2]];
|
||||
return { kind: "value", value: value2, ops: ops2, retired };
|
||||
const ops3 = batch.kind === "ops" ? batch.ops : [["r", value2]];
|
||||
return { kind: "value", value: value2, ops: ops3, retired };
|
||||
}
|
||||
/** The pending failure, or the value after one as a root replacement (no ops were kept since the failure). */
|
||||
#nextAfterFailure() {
|
||||
@@ -17601,7 +17601,7 @@ function describe(plan, record2) {
|
||||
const change = plan.change;
|
||||
if (change === void 0 || !publishes(plan))
|
||||
return void 0;
|
||||
const ops2 = change.loaded === void 0 ? EMPTY_OPERATIONS : change.prepared.ops;
|
||||
const ops3 = change.loaded === void 0 ? EMPTY_OPERATIONS : change.prepared.ops;
|
||||
return {
|
||||
type: "document",
|
||||
kind: "change",
|
||||
@@ -17609,11 +17609,11 @@ function describe(plan, record2) {
|
||||
conversationId,
|
||||
version: change.version,
|
||||
value: change.prepared.value,
|
||||
ops: ops2
|
||||
ops: ops3
|
||||
};
|
||||
}
|
||||
function storeHeld(hold, ops2, value2, version) {
|
||||
hold.pending.push(ops2);
|
||||
function storeHeld(hold, ops3, value2, version) {
|
||||
hold.pending.push(ops3);
|
||||
const taken = hold.pending.take();
|
||||
if (taken.kind === "replace")
|
||||
return { content: { version, kind: "base", value: value2 }, ops: [["r", value2]] };
|
||||
@@ -18694,7 +18694,7 @@ var SessionImpl = class {
|
||||
return { kind: "adoption", error };
|
||||
}
|
||||
const deadline = this.#clock() + withinMs;
|
||||
for (const { loaded, ops: ops2, base } of held2) {
|
||||
for (const { loaded, ops: ops3, base } of held2) {
|
||||
const id = loaded.record.id;
|
||||
let hold = this.#holds.get(id);
|
||||
if (hold === void 0) {
|
||||
@@ -18702,7 +18702,7 @@ var SessionImpl = class {
|
||||
hold = { loaded, pending: new PendingOps(), base: false, deadline, unpin };
|
||||
this.#holds.set(id, hold);
|
||||
}
|
||||
hold.pending.push(ops2);
|
||||
hold.pending.push(ops3);
|
||||
if (base)
|
||||
hold.base = true;
|
||||
if (deadline < hold.deadline)
|
||||
@@ -26917,14 +26917,14 @@ var TaskGraphView = class {
|
||||
const mount = this.#mount;
|
||||
if (mount === void 0)
|
||||
return;
|
||||
const ops2 = [];
|
||||
const ops3 = [];
|
||||
for (const [key, record2] of mount.records) {
|
||||
const blocked = this.#blocking.blocked(record2);
|
||||
if (blocked === mount.value.tasks[key]?.blocked)
|
||||
continue;
|
||||
ops2.push(blocked === void 0 ? ["d", ["tasks", key, "blocked"]] : ["s", ["tasks", key, "blocked"], blocked]);
|
||||
ops3.push(blocked === void 0 ? ["d", ["tasks", key, "blocked"]] : ["s", ["tasks", key, "blocked"], blocked]);
|
||||
}
|
||||
publish(mount, ops2, BACKGROUND_CONTEXT, (error) => this.#session.report(error));
|
||||
publish(mount, ops3, BACKGROUND_CONTEXT, (error) => this.#session.report(error));
|
||||
}).catch(() => {
|
||||
});
|
||||
}
|
||||
@@ -26948,7 +26948,7 @@ var TaskGraphView = class {
|
||||
}
|
||||
};
|
||||
function advance(mount, publication, context, blocked, report2) {
|
||||
const ops2 = [];
|
||||
const ops3 = [];
|
||||
const changed = /* @__PURE__ */ new Map();
|
||||
const node = (key) => changed.has(key) ? changed.get(key) : mount.value.tasks[key];
|
||||
for (const change of publication.changes) {
|
||||
@@ -26961,7 +26961,7 @@ function advance(mount, publication, context, blocked, report2) {
|
||||
mount.records.delete(key);
|
||||
if (previous === void 0)
|
||||
continue;
|
||||
ops2.push(["d", ["tasks", key]]);
|
||||
ops3.push(["d", ["tasks", key]]);
|
||||
changed.set(key, void 0);
|
||||
continue;
|
||||
}
|
||||
@@ -26970,7 +26970,7 @@ function advance(mount, publication, context, blocked, report2) {
|
||||
const next = nodeOf2(record2, previous?.conversations ?? [], blocked(subject));
|
||||
if (previous !== void 0 && JSON.stringify(next) === JSON.stringify(previous))
|
||||
continue;
|
||||
ops2.push(["s", ["tasks", key], next]);
|
||||
ops3.push(["s", ["tasks", key], next]);
|
||||
changed.set(key, next);
|
||||
}
|
||||
const created = /* @__PURE__ */ new Map();
|
||||
@@ -26983,16 +26983,16 @@ function advance(mount, publication, context, blocked, report2) {
|
||||
}
|
||||
for (const [key, ids] of created) {
|
||||
const conversations = [...node(key).conversations, ...ids].sort((a, b) => a - b);
|
||||
ops2.push(["s", ["tasks", key, "conversations"], conversations]);
|
||||
ops3.push(["s", ["tasks", key, "conversations"], conversations]);
|
||||
}
|
||||
publish(mount, ops2, context, report2);
|
||||
publish(mount, ops3, context, report2);
|
||||
}
|
||||
function publish(mount, ops2, context, report2) {
|
||||
if (ops2.length === 0)
|
||||
function publish(mount, ops3, context, report2) {
|
||||
if (ops3.length === 0)
|
||||
return;
|
||||
mount.value = applyImmutable(mount.value, ops2);
|
||||
mount.value = applyImmutable(mount.value, ops3);
|
||||
const frameContext = withoutAbortSignal(context);
|
||||
const call = { kind: "value", value: mount.value, ops: ops2, retired: false };
|
||||
const call = { kind: "value", value: mount.value, ops: ops3, retired: false };
|
||||
for (const observer of [...mount.observers])
|
||||
contained(() => observer.advance(call, frameContext), report2);
|
||||
}
|
||||
@@ -27218,19 +27218,19 @@ function advance2(id, mount, publication, context, report2) {
|
||||
const previous = windowed.value;
|
||||
let entries2 = previous.entries;
|
||||
let before = previous.before;
|
||||
let ops2 = docOps;
|
||||
let ops3 = docOps;
|
||||
if (appended.length > 0) {
|
||||
ops2 = [...docOps, ["p", ["entries"], entries2.length, 0, inserted]];
|
||||
ops3 = [...docOps, ["p", ["entries"], entries2.length, 0, inserted]];
|
||||
entries2 = entries2.concat(appended);
|
||||
if (entries2.length > windowed.max) {
|
||||
const drop = entries2.length - windowed.min;
|
||||
before = entries2[drop - 1].id;
|
||||
entries2 = entries2.slice(drop);
|
||||
ops2.push(["p", ["entries"], 0, drop, []], ["s", ["before"], before]);
|
||||
ops3.push(["p", ["entries"], 0, drop, []], ["s", ["before"], before]);
|
||||
}
|
||||
}
|
||||
windowed.value = view(mount.conversation, entries2, before, mount.docs);
|
||||
const call = { kind: "value", value: windowed.value, ops: ops2, retired: false };
|
||||
const call = { kind: "value", value: windowed.value, ops: ops3, retired: false };
|
||||
contained(() => windowed.observer.advance(call, frameContext), report2);
|
||||
}
|
||||
}
|
||||
@@ -31368,14 +31368,51 @@ function eventLog(harness2) {
|
||||
}
|
||||
}
|
||||
|
||||
// extensions.ts
|
||||
// environment.ts
|
||||
var CodingTools2 = { ...CodingTools, tools: CodingTools.tools.map((tool) => ({
|
||||
...tool,
|
||||
execute: (args, api, context) => tool.execute(args, { ...api, env: api.env.forTool(api.callId, tool.name) }, context)
|
||||
})) };
|
||||
var Started = defineDoc({
|
||||
kind: "board.started",
|
||||
version: 1,
|
||||
checkpointWhen: () => false,
|
||||
scope: "conversation",
|
||||
history: "latest",
|
||||
fork: "initial",
|
||||
initial: () => ({})
|
||||
});
|
||||
function conversationChains(harness2, context) {
|
||||
const cache = /* @__PURE__ */ new Map();
|
||||
const chain = (id) => {
|
||||
let result = cache.get(id);
|
||||
if (!result) {
|
||||
result = (async () => {
|
||||
const record2 = await harness2.commit((tx) => tx.conversation(id), context);
|
||||
const up = record2?.owner?.conversationId ?? record2?.parent?.conversationId;
|
||||
const starter = (await harness2.snapshot(Started, id, context))?.by;
|
||||
return [.../* @__PURE__ */ new Set([
|
||||
String(id),
|
||||
...up === void 0 ? [] : await chain(up),
|
||||
...starter === void 0 ? [] : await chain(starter)
|
||||
])];
|
||||
})();
|
||||
cache.set(id, result);
|
||||
}
|
||||
return result;
|
||||
};
|
||||
return chain;
|
||||
}
|
||||
var ops = Deno.core.ops;
|
||||
|
||||
// extensions.ts
|
||||
var ops2 = Deno.core.ops;
|
||||
var encoder4 = new TextEncoder();
|
||||
var decoder4 = new TextDecoder();
|
||||
var ExtensionState = defineDoc({ kind: "sausage.extensions", version: 1, scope: "session", checkpointWhen: () => false, initial: () => ({ good: null, current: null, error: null }) });
|
||||
var Metadata = defineDoc({ kind: "sausage.metadata", version: 1, scope: "conversation", history: "latest", fork: "current", initial: () => ({ label: "", title: "" }), checkpointWhen: () => false });
|
||||
async function file(kind, path, bytes2 = []) {
|
||||
const result = await ops.op_file(kind, path, bytes2);
|
||||
const result = await ops2.op_file(kind, path, bytes2);
|
||||
if (!result.ok) throw Object.assign(new Error(result.error.message), result.error);
|
||||
return result.value;
|
||||
}
|
||||
@@ -31398,14 +31435,14 @@ async function exec(command, options) {
|
||||
program: `extension:${options.extension}`,
|
||||
conversations: options.conversations ?? []
|
||||
} : void 0;
|
||||
const rid = ops.op_exec(command, options.cwd, options.timeout, void 0, options.env, options.conversations, tag);
|
||||
const rid = ops2.op_exec(command, options.cwd, options.timeout, void 0, options.env, options.conversations, tag);
|
||||
const abort = () => Deno.core.close(rid);
|
||||
options.signal?.addEventListener("abort", abort, { once: true });
|
||||
const decoders = { stdout: new TextDecoder(), stderr: new TextDecoder() };
|
||||
const result = { code: 0, stdout: "", stderr: "" };
|
||||
try {
|
||||
for (; ; ) {
|
||||
const chunk = await ops.op_exec_next(rid);
|
||||
const chunk = await ops2.op_exec_next(rid);
|
||||
if (!chunk.stream) {
|
||||
if (chunk.error) throw new Error(chunk.error);
|
||||
result.code = chunk.exitCode;
|
||||
@@ -31432,11 +31469,12 @@ async function git(cwd, ...args) {
|
||||
function registerModules() {
|
||||
const modules = { "@earendil-works/sausage-events": events_exports, "@earendil-works/pi-schema": dist_exports, "@earendil-works/pi-durable": dist_exports3, "@earendil-works/pi-durable/tools": tools_exports, "@earendil-works/pi-ai": dist_exports4, "@earendil-works/pi-ai/models": models_exports, "@earendil-works/chord": dist_exports2, "@earendil-works/chord/context": context_exports };
|
||||
globalThis.__sausage = { modules };
|
||||
ops.op_modules(Object.fromEntries(Object.entries(modules).map(([name, value2]) => [name, Object.keys(value2)])));
|
||||
ops2.op_modules(Object.fromEntries(Object.entries(modules).map(([name, value2]) => [name, Object.keys(value2)])));
|
||||
}
|
||||
async function loadExtensions(harness2, registry2, storage2, models2, context, builtins = []) {
|
||||
registerModules();
|
||||
const eventService = eventLog(() => harness2);
|
||||
const chain = conversationChains(harness2, context);
|
||||
const repo = "/src/sausage";
|
||||
let providers = /* @__PURE__ */ new Map();
|
||||
const resources = /* @__PURE__ */ new Map();
|
||||
@@ -31447,7 +31485,7 @@ async function loadExtensions(harness2, registry2, storage2, models2, context, b
|
||||
let queue2 = Promise.resolve();
|
||||
const publish2 = async () => {
|
||||
await harness2.commit(async (tx) => Object.assign(await tx.doc(ExtensionState), state), context);
|
||||
ops.op_extension_status(JSON.stringify(state));
|
||||
ops2.op_extension_status(JSON.stringify(state));
|
||||
await fs.write("/src/sausage/.git/sausage-last-good", state.good ?? "");
|
||||
};
|
||||
const dispose = async (entries2) => {
|
||||
@@ -31455,7 +31493,7 @@ async function loadExtensions(harness2, registry2, storage2, models2, context, b
|
||||
try {
|
||||
await cleanup();
|
||||
} catch (error) {
|
||||
ops.op_output(`Extension cleanup: ${String(error)}
|
||||
ops2.op_output(`Extension cleanup: ${String(error)}
|
||||
`);
|
||||
}
|
||||
}
|
||||
@@ -31488,6 +31526,7 @@ async function loadExtensions(harness2, registry2, storage2, models2, context, b
|
||||
fs,
|
||||
exec: (command, options) => exec(command, { ...options, extension: name }),
|
||||
git,
|
||||
chain,
|
||||
implement: (api, value2) => {
|
||||
staged.set(api.name, value2);
|
||||
},
|
||||
@@ -31510,12 +31549,12 @@ async function loadExtensions(harness2, registry2, storage2, models2, context, b
|
||||
onClose: (cb) => close.push(cb),
|
||||
notice: (message) => {
|
||||
state.notice = `${name}: ${message}`;
|
||||
ops.op_extension_status(JSON.stringify(state));
|
||||
ops.op_output(`${name}: ${message}
|
||||
ops2.op_extension_status(JSON.stringify(state));
|
||||
ops2.op_output(`${name}: ${message}
|
||||
`);
|
||||
if (!notices.has(message)) {
|
||||
notices.add(message);
|
||||
void harness2.commit((tx) => eventService.emit(tx, NoticeEvent, { extension: name, message, level: "warning" }), context).catch((error) => ops.op_output(`Recording a notice failed: ${String(error)}
|
||||
void harness2.commit((tx) => eventService.emit(tx, NoticeEvent, { extension: name, message, level: "warning" }), context).catch((error) => ops2.op_output(`Recording a notice failed: ${String(error)}
|
||||
`));
|
||||
}
|
||||
}
|
||||
@@ -31594,7 +31633,7 @@ async function loadExtensions(harness2, registry2, storage2, models2, context, b
|
||||
tip = await git(repo, "rev-parse", "refs/heads/main");
|
||||
} catch (error) {
|
||||
state.error = String(error);
|
||||
ops.op_extension_status(JSON.stringify(state));
|
||||
ops2.op_extension_status(JSON.stringify(state));
|
||||
return;
|
||||
}
|
||||
try {
|
||||
@@ -31603,8 +31642,8 @@ async function loadExtensions(harness2, registry2, storage2, models2, context, b
|
||||
if (error.code === "not_found") settings = {};
|
||||
else {
|
||||
state.notice = `Settings: ${String(error)}`;
|
||||
ops.op_extension_status(JSON.stringify(state));
|
||||
ops.op_output(`${state.notice}
|
||||
ops2.op_extension_status(JSON.stringify(state));
|
||||
ops2.op_output(`${state.notice}
|
||||
`);
|
||||
}
|
||||
}
|
||||
@@ -31620,11 +31659,11 @@ Last good: ${fallback}` : failed;
|
||||
await publish2();
|
||||
};
|
||||
const enqueue2 = () => queue2 = queue2.then(reload);
|
||||
const rid = ops.op_watch([`${repo}/.git/refs/heads/main`, `${repo}/.git/packed-refs`, `${repo}/settings.json`]);
|
||||
const rid = ops2.op_watch([`${repo}/.git/refs/heads/main`, `${repo}/.git/packed-refs`, `${repo}/settings.json`]);
|
||||
await enqueue2();
|
||||
const pending = (async () => {
|
||||
while (active) {
|
||||
await ops.op_watch_next(rid);
|
||||
await ops2.op_watch_next(rid);
|
||||
if (active) await enqueue2();
|
||||
}
|
||||
})();
|
||||
|
||||
@@ -7,6 +7,7 @@ import * as chord from '@earendil-works/chord';
|
||||
import * as contextApi from '@earendil-works/chord/context';
|
||||
import * as schema from '@earendil-works/pi-schema';
|
||||
import * as events from './events';
|
||||
import { conversationChains } from './environment';
|
||||
const ops = Deno.core.ops;
|
||||
const encoder = new TextEncoder();
|
||||
const decoder = new TextDecoder();
|
||||
@@ -16,6 +17,7 @@ export type Host = events.EventHost & {
|
||||
harness(): durable.Harness; storage: Pick<durable.Storage, 'scanSubmissions'>;
|
||||
models(): modelApi.Models; settings(): durable.JsonObject; metadata: typeof Metadata;
|
||||
fs: typeof fs; exec: typeof exec; git: typeof git;
|
||||
chain(conversationId:number):Promise<string[]>;
|
||||
implement<S>(api: {name:string;readonly service?:S}, value:NoInfer<S>):void;
|
||||
need<S>(api:{name:string;readonly service?:S}):S; want<S>(api:{name:string;readonly service?:S}):S|undefined;
|
||||
resource<T>(key:string, create:()=>T):T;
|
||||
@@ -73,6 +75,7 @@ export function registerModules() {
|
||||
export async function loadExtensions(harness:any, registry:any, storage:any, models:any, context:any, builtins:any[]=[]) {
|
||||
registerModules();
|
||||
const eventService = events.eventLog(()=>harness);
|
||||
const chain = conversationChains(harness,context);
|
||||
const repo = '/src/sausage';
|
||||
let providers = new Map<string,any>();
|
||||
const resources = new Map<string,any>();
|
||||
@@ -104,7 +107,7 @@ export async function loadExtensions(harness:any, registry:any, storage:any, mod
|
||||
}});
|
||||
const host:Host = {
|
||||
harness:()=>harness, storage, models:()=>models, settings:()=>settings, metadata:Metadata, fs,
|
||||
exec:(command,options)=>exec(command,{...options,extension:name}),git,
|
||||
exec:(command,options)=>exec(command,{...options,extension:name}),git,chain,
|
||||
implement:(api,value)=>{staged.set(api.name,value);}, need:handle,
|
||||
want:(api)=>providers.has(api.name)?handle(api):undefined,
|
||||
resource:(key,create)=>{const id=`${name}/${key}`;if(!resources.has(id))resources.set(id,create());return resources.get(id);},
|
||||
|
||||
@@ -30428,14 +30428,51 @@ function eventLog(harness2) {
|
||||
}
|
||||
}
|
||||
|
||||
// extensions.ts
|
||||
// environment.ts
|
||||
var CodingTools2 = { ...CodingTools, tools: CodingTools.tools.map((tool2) => ({
|
||||
...tool2,
|
||||
execute: (args, api, context) => tool2.execute(args, { ...api, env: api.env.forTool(api.callId, tool2.name) }, context)
|
||||
})) };
|
||||
var Started = defineDoc({
|
||||
kind: "board.started",
|
||||
version: 1,
|
||||
checkpointWhen: () => false,
|
||||
scope: "conversation",
|
||||
history: "latest",
|
||||
fork: "initial",
|
||||
initial: () => ({})
|
||||
});
|
||||
function conversationChains(harness2, context) {
|
||||
const cache = /* @__PURE__ */ new Map();
|
||||
const chain = (id) => {
|
||||
let result = cache.get(id);
|
||||
if (!result) {
|
||||
result = (async () => {
|
||||
const record2 = await harness2.commit((tx) => tx.conversation(id), context);
|
||||
const up = record2?.owner?.conversationId ?? record2?.parent?.conversationId;
|
||||
const starter = (await harness2.snapshot(Started, id, context))?.by;
|
||||
return [.../* @__PURE__ */ new Set([
|
||||
String(id),
|
||||
...up === void 0 ? [] : await chain(up),
|
||||
...starter === void 0 ? [] : await chain(starter)
|
||||
])];
|
||||
})();
|
||||
cache.set(id, result);
|
||||
}
|
||||
return result;
|
||||
};
|
||||
return chain;
|
||||
}
|
||||
var ops = Deno.core.ops;
|
||||
|
||||
// extensions.ts
|
||||
var ops2 = Deno.core.ops;
|
||||
var encoder4 = new TextEncoder();
|
||||
var decoder4 = new TextDecoder();
|
||||
var ExtensionState = defineDoc({ kind: "sausage.extensions", version: 1, scope: "session", checkpointWhen: () => false, initial: () => ({ good: null, current: null, error: null }) });
|
||||
var Metadata = defineDoc({ kind: "sausage.metadata", version: 1, scope: "conversation", history: "latest", fork: "current", initial: () => ({ label: "", title: "" }), checkpointWhen: () => false });
|
||||
async function file(kind, path, bytes2 = []) {
|
||||
const result = await ops.op_file(kind, path, bytes2);
|
||||
const result = await ops2.op_file(kind, path, bytes2);
|
||||
if (!result.ok) throw Object.assign(new Error(result.error.message), result.error);
|
||||
return result.value;
|
||||
}
|
||||
@@ -30458,14 +30495,14 @@ async function exec(command, options) {
|
||||
program: `extension:${options.extension}`,
|
||||
conversations: options.conversations ?? []
|
||||
} : void 0;
|
||||
const rid = ops.op_exec(command, options.cwd, options.timeout, void 0, options.env, options.conversations, tag);
|
||||
const rid = ops2.op_exec(command, options.cwd, options.timeout, void 0, options.env, options.conversations, tag);
|
||||
const abort = () => Deno.core.close(rid);
|
||||
options.signal?.addEventListener("abort", abort, { once: true });
|
||||
const decoders = { stdout: new TextDecoder(), stderr: new TextDecoder() };
|
||||
const result = { code: 0, stdout: "", stderr: "" };
|
||||
try {
|
||||
for (; ; ) {
|
||||
const chunk = await ops.op_exec_next(rid);
|
||||
const chunk = await ops2.op_exec_next(rid);
|
||||
if (!chunk.stream) {
|
||||
if (chunk.error) throw new Error(chunk.error);
|
||||
result.code = chunk.exitCode;
|
||||
@@ -30492,11 +30529,12 @@ async function git(cwd, ...args) {
|
||||
function registerModules() {
|
||||
const modules = { "@earendil-works/sausage-events": events_exports, "@earendil-works/pi-schema": dist_exports, "@earendil-works/pi-durable": dist_exports3, "@earendil-works/pi-durable/tools": tools_exports, "@earendil-works/pi-ai": dist_exports4, "@earendil-works/pi-ai/models": models_exports, "@earendil-works/chord": dist_exports2, "@earendil-works/chord/context": context_exports };
|
||||
globalThis.__sausage = { modules };
|
||||
ops.op_modules(Object.fromEntries(Object.entries(modules).map(([name, value2]) => [name, Object.keys(value2)])));
|
||||
ops2.op_modules(Object.fromEntries(Object.entries(modules).map(([name, value2]) => [name, Object.keys(value2)])));
|
||||
}
|
||||
async function loadExtensions(harness2, registry2, storage2, models2, context, builtins = []) {
|
||||
registerModules();
|
||||
const eventService = eventLog(() => harness2);
|
||||
const chain = conversationChains(harness2, context);
|
||||
const repo = "/src/sausage";
|
||||
let providers = /* @__PURE__ */ new Map();
|
||||
const resources = /* @__PURE__ */ new Map();
|
||||
@@ -30507,7 +30545,7 @@ async function loadExtensions(harness2, registry2, storage2, models2, context, b
|
||||
let queue2 = Promise.resolve();
|
||||
const publish2 = async () => {
|
||||
await harness2.commit(async (tx) => Object.assign(await tx.doc(ExtensionState), state), context);
|
||||
ops.op_extension_status(JSON.stringify(state));
|
||||
ops2.op_extension_status(JSON.stringify(state));
|
||||
await fs.write("/src/sausage/.git/sausage-last-good", state.good ?? "");
|
||||
};
|
||||
const dispose = async (entries3) => {
|
||||
@@ -30515,7 +30553,7 @@ async function loadExtensions(harness2, registry2, storage2, models2, context, b
|
||||
try {
|
||||
await cleanup();
|
||||
} catch (error) {
|
||||
ops.op_output(`Extension cleanup: ${String(error)}
|
||||
ops2.op_output(`Extension cleanup: ${String(error)}
|
||||
`);
|
||||
}
|
||||
}
|
||||
@@ -30548,6 +30586,7 @@ async function loadExtensions(harness2, registry2, storage2, models2, context, b
|
||||
fs,
|
||||
exec: (command, options) => exec(command, { ...options, extension: name }),
|
||||
git,
|
||||
chain,
|
||||
implement: (api, value2) => {
|
||||
staged.set(api.name, value2);
|
||||
},
|
||||
@@ -30570,12 +30609,12 @@ async function loadExtensions(harness2, registry2, storage2, models2, context, b
|
||||
onClose: (cb) => close.push(cb),
|
||||
notice: (message) => {
|
||||
state.notice = `${name}: ${message}`;
|
||||
ops.op_extension_status(JSON.stringify(state));
|
||||
ops.op_output(`${name}: ${message}
|
||||
ops2.op_extension_status(JSON.stringify(state));
|
||||
ops2.op_output(`${name}: ${message}
|
||||
`);
|
||||
if (!notices.has(message)) {
|
||||
notices.add(message);
|
||||
void harness2.commit((tx) => eventService.emit(tx, NoticeEvent, { extension: name, message, level: "warning" }), context).catch((error) => ops.op_output(`Recording a notice failed: ${String(error)}
|
||||
void harness2.commit((tx) => eventService.emit(tx, NoticeEvent, { extension: name, message, level: "warning" }), context).catch((error) => ops2.op_output(`Recording a notice failed: ${String(error)}
|
||||
`));
|
||||
}
|
||||
}
|
||||
@@ -30654,7 +30693,7 @@ async function loadExtensions(harness2, registry2, storage2, models2, context, b
|
||||
tip = await git(repo, "rev-parse", "refs/heads/main");
|
||||
} catch (error) {
|
||||
state.error = String(error);
|
||||
ops.op_extension_status(JSON.stringify(state));
|
||||
ops2.op_extension_status(JSON.stringify(state));
|
||||
return;
|
||||
}
|
||||
try {
|
||||
@@ -30663,8 +30702,8 @@ async function loadExtensions(harness2, registry2, storage2, models2, context, b
|
||||
if (error.code === "not_found") settings = {};
|
||||
else {
|
||||
state.notice = `Settings: ${String(error)}`;
|
||||
ops.op_extension_status(JSON.stringify(state));
|
||||
ops.op_output(`${state.notice}
|
||||
ops2.op_extension_status(JSON.stringify(state));
|
||||
ops2.op_output(`${state.notice}
|
||||
`);
|
||||
}
|
||||
}
|
||||
@@ -30680,11 +30719,11 @@ Last good: ${fallback}` : failed;
|
||||
await publish2();
|
||||
};
|
||||
const enqueue2 = () => queue2 = queue2.then(reload);
|
||||
const rid = ops.op_watch([`${repo}/.git/refs/heads/main`, `${repo}/.git/packed-refs`, `${repo}/settings.json`]);
|
||||
const rid = ops2.op_watch([`${repo}/.git/refs/heads/main`, `${repo}/.git/packed-refs`, `${repo}/settings.json`]);
|
||||
await enqueue2();
|
||||
const pending = (async () => {
|
||||
while (active) {
|
||||
await ops.op_watch_next(rid);
|
||||
await ops2.op_watch_next(rid);
|
||||
if (active) await enqueue2();
|
||||
}
|
||||
})();
|
||||
@@ -30697,43 +30736,6 @@ Last good: ${fallback}` : failed;
|
||||
} };
|
||||
}
|
||||
|
||||
// environment.ts
|
||||
var CodingTools2 = { ...CodingTools, tools: CodingTools.tools.map((tool2) => ({
|
||||
...tool2,
|
||||
execute: (args, api, context) => tool2.execute(args, { ...api, env: api.env.forTool(api.callId, tool2.name) }, context)
|
||||
})) };
|
||||
var Started = defineDoc({
|
||||
kind: "board.started",
|
||||
version: 1,
|
||||
checkpointWhen: () => false,
|
||||
scope: "conversation",
|
||||
history: "latest",
|
||||
fork: "initial",
|
||||
initial: () => ({})
|
||||
});
|
||||
function conversationChains(harness2, context) {
|
||||
const cache = /* @__PURE__ */ new Map();
|
||||
const chain = (id) => {
|
||||
let result = cache.get(id);
|
||||
if (!result) {
|
||||
result = (async () => {
|
||||
const record2 = await harness2.commit((tx) => tx.conversation(id), context);
|
||||
const up = record2?.owner?.conversationId ?? record2?.parent?.conversationId;
|
||||
const starter = (await harness2.snapshot(Started, id, context))?.by;
|
||||
return [.../* @__PURE__ */ new Set([
|
||||
String(id),
|
||||
...up === void 0 ? [] : await chain(up),
|
||||
...starter === void 0 ? [] : await chain(starter)
|
||||
])];
|
||||
})();
|
||||
cache.set(id, result);
|
||||
}
|
||||
return result;
|
||||
};
|
||||
return chain;
|
||||
}
|
||||
var ops2 = Deno.core.ops;
|
||||
|
||||
// node_modules/@earendil-works/chord/dist/sqlite/schema.js
|
||||
function validateSqliteSchemaPrefix(prefix) {
|
||||
if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(prefix)) {
|
||||
|
||||
+109
-70
@@ -7915,27 +7915,27 @@ var REPLACE_TAKE = { kind: "replace" };
|
||||
var PendingOps = class {
|
||||
#state = EMPTY;
|
||||
/** Queue the ops of one commit. */
|
||||
push(ops2) {
|
||||
if (ops2.length === 0)
|
||||
push(ops3) {
|
||||
if (ops3.length === 0)
|
||||
return;
|
||||
const state = this.#state;
|
||||
if (ops2[0][0] === "r") {
|
||||
this.#state = { kind: "batch", ops: ops2 };
|
||||
if (ops3[0][0] === "r") {
|
||||
this.#state = { kind: "batch", ops: ops3 };
|
||||
return;
|
||||
}
|
||||
switch (state.kind) {
|
||||
case "empty":
|
||||
this.#state = { kind: "batch", ops: ops2 };
|
||||
this.#state = { kind: "batch", ops: ops3 };
|
||||
return;
|
||||
case "batch": {
|
||||
const folding = { kind: "folding", ops: [], leaves: /* @__PURE__ */ new Map() };
|
||||
this.#state = folding;
|
||||
if (this.#fold(state.ops, folding))
|
||||
this.#fold(ops2, folding);
|
||||
this.#fold(ops3, folding);
|
||||
return;
|
||||
}
|
||||
case "folding":
|
||||
this.#fold(ops2, state);
|
||||
this.#fold(ops3, state);
|
||||
return;
|
||||
case "replace":
|
||||
return;
|
||||
@@ -7966,8 +7966,8 @@ var PendingOps = class {
|
||||
}
|
||||
}
|
||||
/** Fold `ops` into `folding`; false once the queue overflowed into `replace`. */
|
||||
#fold(ops2, folding) {
|
||||
for (const op2 of ops2) {
|
||||
#fold(ops3, folding) {
|
||||
for (const op2 of ops3) {
|
||||
switch (op2[0]) {
|
||||
case "r":
|
||||
folding.ops.length = 0;
|
||||
@@ -8236,12 +8236,12 @@ var PathError = class extends Error {
|
||||
this.name = "PathError";
|
||||
}
|
||||
};
|
||||
function apply(target, ops2) {
|
||||
return applyOps(target, ops2);
|
||||
function apply(target, ops3) {
|
||||
return applyOps(target, ops3);
|
||||
}
|
||||
function applyOps(target, ops2) {
|
||||
function applyOps(target, ops3) {
|
||||
let root = target;
|
||||
for (const op2 of ops2) {
|
||||
for (const op2 of ops3) {
|
||||
assertValidOp(op2);
|
||||
if (op2[0] === "r") {
|
||||
root = op2[1];
|
||||
@@ -8310,14 +8310,14 @@ function applyOps(target, ops2) {
|
||||
}
|
||||
return root;
|
||||
}
|
||||
function applyImmutable(target, ops2) {
|
||||
return applyImmutableBatches(target, [ops2]);
|
||||
function applyImmutable(target, ops3) {
|
||||
return applyImmutableBatches(target, [ops3]);
|
||||
}
|
||||
function applyImmutableBatches(target, batches) {
|
||||
let root = target;
|
||||
const owned = /* @__PURE__ */ new WeakSet();
|
||||
for (const ops2 of batches) {
|
||||
for (const op2 of ops2) {
|
||||
for (const ops3 of batches) {
|
||||
for (const op2 of ops3) {
|
||||
assertValidOp(op2);
|
||||
if (op2[0] === "r") {
|
||||
root = op2[1];
|
||||
@@ -8431,10 +8431,10 @@ function encoder() {
|
||||
let nextId = 0;
|
||||
let previous;
|
||||
return {
|
||||
encode(ops2) {
|
||||
encode(ops3) {
|
||||
previous = void 0;
|
||||
const out = [];
|
||||
for (const op2 of ops2) {
|
||||
for (const op2 of ops3) {
|
||||
if (op2[0] === "r") {
|
||||
out.push(op2);
|
||||
seen.clear();
|
||||
@@ -8684,9 +8684,9 @@ var Source = class {
|
||||
return [];
|
||||
}
|
||||
};
|
||||
function unbox(ops2, value2) {
|
||||
function unbox(ops3, value2) {
|
||||
const result = [];
|
||||
for (const op2 of ops2) {
|
||||
for (const op2 of ops3) {
|
||||
if (op2[0] === "r" || op2[1].length === 0)
|
||||
return [["r", value2]];
|
||||
const path = op2[1].slice(1);
|
||||
@@ -8780,8 +8780,8 @@ var Cell = class extends Source {
|
||||
throw notReady(`state ${this.key} (its init has not run)`);
|
||||
const before = this.value;
|
||||
const diff = options.diff ?? sharesStructure(before, value2);
|
||||
const ops2 = diff ? diffRevisions(before, value2) : void 0;
|
||||
this.#publish(this.#tracker.prepareReplace({ v: value2 }), "ops", true, ops2);
|
||||
const ops3 = diff ? diffRevisions(before, value2) : void 0;
|
||||
this.#publish(this.#tracker.prepareReplace({ v: value2 }), "ops", true, ops3);
|
||||
}
|
||||
/**
|
||||
* Installs a migrated value and its schema (reload commit). The caller validated the value and the derivations
|
||||
@@ -8792,7 +8792,7 @@ var Cell = class extends Source {
|
||||
this.#publish(this.#tracker.prepareReplace({ v: value2 }), "reset", false);
|
||||
}
|
||||
/** Adopts this revision and every derived one, then queues all notifications (cell first, then derivations). */
|
||||
#publish(prepared, type, validate2 = true, ops2) {
|
||||
#publish(prepared, type, validate2 = true, ops3) {
|
||||
if (prepared.ops.length === 0 && type === "ops") {
|
||||
this.#tracker.adopt(prepared);
|
||||
return;
|
||||
@@ -8809,7 +8809,7 @@ var Cell = class extends Source {
|
||||
}
|
||||
holdPublications(() => {
|
||||
this.#tracker.adopt(prepared);
|
||||
const published = ops2 !== void 0 && ops2.length > 0 ? ops2 : unbox(prepared.ops, value2);
|
||||
const published = ops3 !== void 0 && ops3.length > 0 ? ops3 : unbox(prepared.ops, value2);
|
||||
this.fanout.emit(type === "reset" ? { type, value: value2 } : { type, ops: published, value: value2 });
|
||||
for (const [derived, next] of candidates)
|
||||
derived.install(next);
|
||||
@@ -8922,9 +8922,9 @@ var Derived = class extends Source {
|
||||
install(next) {
|
||||
const before = this.#value;
|
||||
this.#value = next;
|
||||
const ops2 = before === void 0 || next === void 0 ? [["r", next ?? null]] : diffRevisions(before, next);
|
||||
if (ops2.length > 0)
|
||||
this.fanout.emit({ type: "ops", ops: ops2, value: next });
|
||||
const ops3 = before === void 0 || next === void 0 ? [["r", next ?? null]] : diffRevisions(before, next);
|
||||
if (ops3.length > 0)
|
||||
this.fanout.emit({ type: "ops", ops: ops3, value: next });
|
||||
}
|
||||
};
|
||||
var isSource = (value2) => value2 instanceof Source;
|
||||
@@ -12977,10 +12977,10 @@ var RemoteBinding = class {
|
||||
const target = pending?.values.get(name) ?? this.members.get(name);
|
||||
if (target === void 0)
|
||||
return;
|
||||
const ops2 = target.decoder.decode(frame.ops ?? []);
|
||||
target.value = applyImmutable(target.value, ops2);
|
||||
const ops3 = target.decoder.decode(frame.ops ?? []);
|
||||
target.value = applyImmutable(target.value, ops3);
|
||||
if (target === this.members.get(name))
|
||||
this.#deliver(name, { type: "ops", ops: ops2, value: target.value });
|
||||
this.#deliver(name, { type: "ops", ops: ops3, value: target.value });
|
||||
return;
|
||||
}
|
||||
const fresh = { value: frame.value, decoder: decoder() };
|
||||
@@ -14804,8 +14804,8 @@ var Watch = class extends WatchLifecycle {
|
||||
this.#retired = false;
|
||||
if (lone !== void 0)
|
||||
return lone;
|
||||
const ops2 = batch.kind === "ops" ? batch.ops : [["r", value2]];
|
||||
return { kind: "value", value: value2, ops: ops2, retired };
|
||||
const ops3 = batch.kind === "ops" ? batch.ops : [["r", value2]];
|
||||
return { kind: "value", value: value2, ops: ops3, retired };
|
||||
}
|
||||
/** The pending failure, or the value after one as a root replacement (no ops were kept since the failure). */
|
||||
#nextAfterFailure() {
|
||||
@@ -16570,7 +16570,7 @@ function describe(plan, record2) {
|
||||
const change = plan.change;
|
||||
if (change === void 0 || !publishes(plan))
|
||||
return void 0;
|
||||
const ops2 = change.loaded === void 0 ? EMPTY_OPERATIONS : change.prepared.ops;
|
||||
const ops3 = change.loaded === void 0 ? EMPTY_OPERATIONS : change.prepared.ops;
|
||||
return {
|
||||
type: "document",
|
||||
kind: "change",
|
||||
@@ -16578,11 +16578,11 @@ function describe(plan, record2) {
|
||||
conversationId,
|
||||
version: change.version,
|
||||
value: change.prepared.value,
|
||||
ops: ops2
|
||||
ops: ops3
|
||||
};
|
||||
}
|
||||
function storeHeld(hold, ops2, value2, version) {
|
||||
hold.pending.push(ops2);
|
||||
function storeHeld(hold, ops3, value2, version) {
|
||||
hold.pending.push(ops3);
|
||||
const taken = hold.pending.take();
|
||||
if (taken.kind === "replace")
|
||||
return { content: { version, kind: "base", value: value2 }, ops: [["r", value2]] };
|
||||
@@ -17663,7 +17663,7 @@ var SessionImpl = class {
|
||||
return { kind: "adoption", error };
|
||||
}
|
||||
const deadline = this.#clock() + withinMs;
|
||||
for (const { loaded, ops: ops2, base } of held2) {
|
||||
for (const { loaded, ops: ops3, base } of held2) {
|
||||
const id = loaded.record.id;
|
||||
let hold = this.#holds.get(id);
|
||||
if (hold === void 0) {
|
||||
@@ -17671,7 +17671,7 @@ var SessionImpl = class {
|
||||
hold = { loaded, pending: new PendingOps(), base: false, deadline, unpin };
|
||||
this.#holds.set(id, hold);
|
||||
}
|
||||
hold.pending.push(ops2);
|
||||
hold.pending.push(ops3);
|
||||
if (base)
|
||||
hold.base = true;
|
||||
if (deadline < hold.deadline)
|
||||
@@ -26442,14 +26442,14 @@ var TaskGraphView = class {
|
||||
const mount = this.#mount;
|
||||
if (mount === void 0)
|
||||
return;
|
||||
const ops2 = [];
|
||||
const ops3 = [];
|
||||
for (const [key, record2] of mount.records) {
|
||||
const blocked = this.#blocking.blocked(record2);
|
||||
if (blocked === mount.value.tasks[key]?.blocked)
|
||||
continue;
|
||||
ops2.push(blocked === void 0 ? ["d", ["tasks", key, "blocked"]] : ["s", ["tasks", key, "blocked"], blocked]);
|
||||
ops3.push(blocked === void 0 ? ["d", ["tasks", key, "blocked"]] : ["s", ["tasks", key, "blocked"], blocked]);
|
||||
}
|
||||
publish(mount, ops2, BACKGROUND_CONTEXT, (error) => this.#session.report(error));
|
||||
publish(mount, ops3, BACKGROUND_CONTEXT, (error) => this.#session.report(error));
|
||||
}).catch(() => {
|
||||
});
|
||||
}
|
||||
@@ -26473,7 +26473,7 @@ var TaskGraphView = class {
|
||||
}
|
||||
};
|
||||
function advance(mount, publication, context, blocked, report2) {
|
||||
const ops2 = [];
|
||||
const ops3 = [];
|
||||
const changed = /* @__PURE__ */ new Map();
|
||||
const node = (key) => changed.has(key) ? changed.get(key) : mount.value.tasks[key];
|
||||
for (const change of publication.changes) {
|
||||
@@ -26486,7 +26486,7 @@ function advance(mount, publication, context, blocked, report2) {
|
||||
mount.records.delete(key);
|
||||
if (previous === void 0)
|
||||
continue;
|
||||
ops2.push(["d", ["tasks", key]]);
|
||||
ops3.push(["d", ["tasks", key]]);
|
||||
changed.set(key, void 0);
|
||||
continue;
|
||||
}
|
||||
@@ -26495,7 +26495,7 @@ function advance(mount, publication, context, blocked, report2) {
|
||||
const next = nodeOf2(record2, previous?.conversations ?? [], blocked(subject));
|
||||
if (previous !== void 0 && JSON.stringify(next) === JSON.stringify(previous))
|
||||
continue;
|
||||
ops2.push(["s", ["tasks", key], next]);
|
||||
ops3.push(["s", ["tasks", key], next]);
|
||||
changed.set(key, next);
|
||||
}
|
||||
const created = /* @__PURE__ */ new Map();
|
||||
@@ -26508,16 +26508,16 @@ function advance(mount, publication, context, blocked, report2) {
|
||||
}
|
||||
for (const [key, ids] of created) {
|
||||
const conversations = [...node(key).conversations, ...ids].sort((a, b) => a - b);
|
||||
ops2.push(["s", ["tasks", key, "conversations"], conversations]);
|
||||
ops3.push(["s", ["tasks", key, "conversations"], conversations]);
|
||||
}
|
||||
publish(mount, ops2, context, report2);
|
||||
publish(mount, ops3, context, report2);
|
||||
}
|
||||
function publish(mount, ops2, context, report2) {
|
||||
if (ops2.length === 0)
|
||||
function publish(mount, ops3, context, report2) {
|
||||
if (ops3.length === 0)
|
||||
return;
|
||||
mount.value = applyImmutable(mount.value, ops2);
|
||||
mount.value = applyImmutable(mount.value, ops3);
|
||||
const frameContext = withoutAbortSignal(context);
|
||||
const call = { kind: "value", value: mount.value, ops: ops2, retired: false };
|
||||
const call = { kind: "value", value: mount.value, ops: ops3, retired: false };
|
||||
for (const observer of [...mount.observers])
|
||||
contained(() => observer.advance(call, frameContext), report2);
|
||||
}
|
||||
@@ -26743,19 +26743,19 @@ function advance2(id, mount, publication, context, report2) {
|
||||
const previous = windowed.value;
|
||||
let entries2 = previous.entries;
|
||||
let before = previous.before;
|
||||
let ops2 = docOps;
|
||||
let ops3 = docOps;
|
||||
if (appended.length > 0) {
|
||||
ops2 = [...docOps, ["p", ["entries"], entries2.length, 0, inserted]];
|
||||
ops3 = [...docOps, ["p", ["entries"], entries2.length, 0, inserted]];
|
||||
entries2 = entries2.concat(appended);
|
||||
if (entries2.length > windowed.max) {
|
||||
const drop = entries2.length - windowed.min;
|
||||
before = entries2[drop - 1].id;
|
||||
entries2 = entries2.slice(drop);
|
||||
ops2.push(["p", ["entries"], 0, drop, []], ["s", ["before"], before]);
|
||||
ops3.push(["p", ["entries"], 0, drop, []], ["s", ["before"], before]);
|
||||
}
|
||||
}
|
||||
windowed.value = view(mount.conversation, entries2, before, mount.docs);
|
||||
const call = { kind: "value", value: windowed.value, ops: ops2, retired: false };
|
||||
const call = { kind: "value", value: windowed.value, ops: ops3, retired: false };
|
||||
contained(() => windowed.observer.advance(call, frameContext), report2);
|
||||
}
|
||||
}
|
||||
@@ -30428,14 +30428,51 @@ function eventLog(harness) {
|
||||
}
|
||||
}
|
||||
|
||||
// extensions.ts
|
||||
// environment.ts
|
||||
var CodingTools2 = { ...CodingTools, tools: CodingTools.tools.map((tool) => ({
|
||||
...tool,
|
||||
execute: (args, api, context) => tool.execute(args, { ...api, env: api.env.forTool(api.callId, tool.name) }, context)
|
||||
})) };
|
||||
var Started = defineDoc({
|
||||
kind: "board.started",
|
||||
version: 1,
|
||||
checkpointWhen: () => false,
|
||||
scope: "conversation",
|
||||
history: "latest",
|
||||
fork: "initial",
|
||||
initial: () => ({})
|
||||
});
|
||||
function conversationChains(harness, context) {
|
||||
const cache = /* @__PURE__ */ new Map();
|
||||
const chain = (id) => {
|
||||
let result = cache.get(id);
|
||||
if (!result) {
|
||||
result = (async () => {
|
||||
const record2 = await harness.commit((tx) => tx.conversation(id), context);
|
||||
const up = record2?.owner?.conversationId ?? record2?.parent?.conversationId;
|
||||
const starter = (await harness.snapshot(Started, id, context))?.by;
|
||||
return [.../* @__PURE__ */ new Set([
|
||||
String(id),
|
||||
...up === void 0 ? [] : await chain(up),
|
||||
...starter === void 0 ? [] : await chain(starter)
|
||||
])];
|
||||
})();
|
||||
cache.set(id, result);
|
||||
}
|
||||
return result;
|
||||
};
|
||||
return chain;
|
||||
}
|
||||
var ops = Deno.core.ops;
|
||||
|
||||
// extensions.ts
|
||||
var ops2 = Deno.core.ops;
|
||||
var encoder4 = new TextEncoder();
|
||||
var decoder4 = new TextDecoder();
|
||||
var ExtensionState = defineDoc({ kind: "sausage.extensions", version: 1, scope: "session", checkpointWhen: () => false, initial: () => ({ good: null, current: null, error: null }) });
|
||||
var Metadata = defineDoc({ kind: "sausage.metadata", version: 1, scope: "conversation", history: "latest", fork: "current", initial: () => ({ label: "", title: "" }), checkpointWhen: () => false });
|
||||
async function file(kind, path, bytes2 = []) {
|
||||
const result = await ops.op_file(kind, path, bytes2);
|
||||
const result = await ops2.op_file(kind, path, bytes2);
|
||||
if (!result.ok) throw Object.assign(new Error(result.error.message), result.error);
|
||||
return result.value;
|
||||
}
|
||||
@@ -30458,14 +30495,14 @@ async function exec(command, options) {
|
||||
program: `extension:${options.extension}`,
|
||||
conversations: options.conversations ?? []
|
||||
} : void 0;
|
||||
const rid = ops.op_exec(command, options.cwd, options.timeout, void 0, options.env, options.conversations, tag);
|
||||
const rid = ops2.op_exec(command, options.cwd, options.timeout, void 0, options.env, options.conversations, tag);
|
||||
const abort = () => Deno.core.close(rid);
|
||||
options.signal?.addEventListener("abort", abort, { once: true });
|
||||
const decoders = { stdout: new TextDecoder(), stderr: new TextDecoder() };
|
||||
const result = { code: 0, stdout: "", stderr: "" };
|
||||
try {
|
||||
for (; ; ) {
|
||||
const chunk = await ops.op_exec_next(rid);
|
||||
const chunk = await ops2.op_exec_next(rid);
|
||||
if (!chunk.stream) {
|
||||
if (chunk.error) throw new Error(chunk.error);
|
||||
result.code = chunk.exitCode;
|
||||
@@ -30492,11 +30529,12 @@ async function git(cwd, ...args) {
|
||||
function registerModules() {
|
||||
const modules = { "@earendil-works/sausage-events": events_exports, "@earendil-works/pi-schema": dist_exports, "@earendil-works/pi-durable": dist_exports3, "@earendil-works/pi-durable/tools": tools_exports, "@earendil-works/pi-ai": dist_exports4, "@earendil-works/pi-ai/models": models_exports, "@earendil-works/chord": dist_exports2, "@earendil-works/chord/context": context_exports };
|
||||
globalThis.__sausage = { modules };
|
||||
ops.op_modules(Object.fromEntries(Object.entries(modules).map(([name, value2]) => [name, Object.keys(value2)])));
|
||||
ops2.op_modules(Object.fromEntries(Object.entries(modules).map(([name, value2]) => [name, Object.keys(value2)])));
|
||||
}
|
||||
async function loadExtensions(harness, registry, storage, models, context, builtins = []) {
|
||||
registerModules();
|
||||
const eventService = eventLog(() => harness);
|
||||
const chain = conversationChains(harness, context);
|
||||
const repo = "/src/sausage";
|
||||
let providers = /* @__PURE__ */ new Map();
|
||||
const resources = /* @__PURE__ */ new Map();
|
||||
@@ -30507,7 +30545,7 @@ async function loadExtensions(harness, registry, storage, models, context, built
|
||||
let queue2 = Promise.resolve();
|
||||
const publish2 = async () => {
|
||||
await harness.commit(async (tx) => Object.assign(await tx.doc(ExtensionState), state), context);
|
||||
ops.op_extension_status(JSON.stringify(state));
|
||||
ops2.op_extension_status(JSON.stringify(state));
|
||||
await fs.write("/src/sausage/.git/sausage-last-good", state.good ?? "");
|
||||
};
|
||||
const dispose = async (entries2) => {
|
||||
@@ -30515,7 +30553,7 @@ async function loadExtensions(harness, registry, storage, models, context, built
|
||||
try {
|
||||
await cleanup();
|
||||
} catch (error) {
|
||||
ops.op_output(`Extension cleanup: ${String(error)}
|
||||
ops2.op_output(`Extension cleanup: ${String(error)}
|
||||
`);
|
||||
}
|
||||
}
|
||||
@@ -30548,6 +30586,7 @@ async function loadExtensions(harness, registry, storage, models, context, built
|
||||
fs,
|
||||
exec: (command, options) => exec(command, { ...options, extension: name }),
|
||||
git,
|
||||
chain,
|
||||
implement: (api, value2) => {
|
||||
staged.set(api.name, value2);
|
||||
},
|
||||
@@ -30570,12 +30609,12 @@ async function loadExtensions(harness, registry, storage, models, context, built
|
||||
onClose: (cb) => close.push(cb),
|
||||
notice: (message) => {
|
||||
state.notice = `${name}: ${message}`;
|
||||
ops.op_extension_status(JSON.stringify(state));
|
||||
ops.op_output(`${name}: ${message}
|
||||
ops2.op_extension_status(JSON.stringify(state));
|
||||
ops2.op_output(`${name}: ${message}
|
||||
`);
|
||||
if (!notices.has(message)) {
|
||||
notices.add(message);
|
||||
void harness.commit((tx) => eventService.emit(tx, NoticeEvent, { extension: name, message, level: "warning" }), context).catch((error) => ops.op_output(`Recording a notice failed: ${String(error)}
|
||||
void harness.commit((tx) => eventService.emit(tx, NoticeEvent, { extension: name, message, level: "warning" }), context).catch((error) => ops2.op_output(`Recording a notice failed: ${String(error)}
|
||||
`));
|
||||
}
|
||||
}
|
||||
@@ -30654,7 +30693,7 @@ async function loadExtensions(harness, registry, storage, models, context, built
|
||||
tip = await git(repo, "rev-parse", "refs/heads/main");
|
||||
} catch (error) {
|
||||
state.error = String(error);
|
||||
ops.op_extension_status(JSON.stringify(state));
|
||||
ops2.op_extension_status(JSON.stringify(state));
|
||||
return;
|
||||
}
|
||||
try {
|
||||
@@ -30663,8 +30702,8 @@ async function loadExtensions(harness, registry, storage, models, context, built
|
||||
if (error.code === "not_found") settings = {};
|
||||
else {
|
||||
state.notice = `Settings: ${String(error)}`;
|
||||
ops.op_extension_status(JSON.stringify(state));
|
||||
ops.op_output(`${state.notice}
|
||||
ops2.op_extension_status(JSON.stringify(state));
|
||||
ops2.op_output(`${state.notice}
|
||||
`);
|
||||
}
|
||||
}
|
||||
@@ -30680,11 +30719,11 @@ Last good: ${fallback}` : failed;
|
||||
await publish2();
|
||||
};
|
||||
const enqueue2 = () => queue2 = queue2.then(reload);
|
||||
const rid = ops.op_watch([`${repo}/.git/refs/heads/main`, `${repo}/.git/packed-refs`, `${repo}/settings.json`]);
|
||||
const rid = ops2.op_watch([`${repo}/.git/refs/heads/main`, `${repo}/.git/packed-refs`, `${repo}/settings.json`]);
|
||||
await enqueue2();
|
||||
const pending = (async () => {
|
||||
while (active) {
|
||||
await ops.op_watch_next(rid);
|
||||
await ops2.op_watch_next(rid);
|
||||
if (active) await enqueue2();
|
||||
}
|
||||
})();
|
||||
|
||||
@@ -8946,27 +8946,27 @@ var REPLACE_TAKE = { kind: "replace" };
|
||||
var PendingOps = class {
|
||||
#state = EMPTY;
|
||||
/** Queue the ops of one commit. */
|
||||
push(ops2) {
|
||||
if (ops2.length === 0)
|
||||
push(ops3) {
|
||||
if (ops3.length === 0)
|
||||
return;
|
||||
const state = this.#state;
|
||||
if (ops2[0][0] === "r") {
|
||||
this.#state = { kind: "batch", ops: ops2 };
|
||||
if (ops3[0][0] === "r") {
|
||||
this.#state = { kind: "batch", ops: ops3 };
|
||||
return;
|
||||
}
|
||||
switch (state.kind) {
|
||||
case "empty":
|
||||
this.#state = { kind: "batch", ops: ops2 };
|
||||
this.#state = { kind: "batch", ops: ops3 };
|
||||
return;
|
||||
case "batch": {
|
||||
const folding = { kind: "folding", ops: [], leaves: /* @__PURE__ */ new Map() };
|
||||
this.#state = folding;
|
||||
if (this.#fold(state.ops, folding))
|
||||
this.#fold(ops2, folding);
|
||||
this.#fold(ops3, folding);
|
||||
return;
|
||||
}
|
||||
case "folding":
|
||||
this.#fold(ops2, state);
|
||||
this.#fold(ops3, state);
|
||||
return;
|
||||
case "replace":
|
||||
return;
|
||||
@@ -8997,8 +8997,8 @@ var PendingOps = class {
|
||||
}
|
||||
}
|
||||
/** Fold `ops` into `folding`; false once the queue overflowed into `replace`. */
|
||||
#fold(ops2, folding) {
|
||||
for (const op2 of ops2) {
|
||||
#fold(ops3, folding) {
|
||||
for (const op2 of ops3) {
|
||||
switch (op2[0]) {
|
||||
case "r":
|
||||
folding.ops.length = 0;
|
||||
@@ -9267,12 +9267,12 @@ var PathError = class extends Error {
|
||||
this.name = "PathError";
|
||||
}
|
||||
};
|
||||
function apply(target, ops2) {
|
||||
return applyOps(target, ops2);
|
||||
function apply(target, ops3) {
|
||||
return applyOps(target, ops3);
|
||||
}
|
||||
function applyOps(target, ops2) {
|
||||
function applyOps(target, ops3) {
|
||||
let root2 = target;
|
||||
for (const op2 of ops2) {
|
||||
for (const op2 of ops3) {
|
||||
assertValidOp(op2);
|
||||
if (op2[0] === "r") {
|
||||
root2 = op2[1];
|
||||
@@ -9341,14 +9341,14 @@ function applyOps(target, ops2) {
|
||||
}
|
||||
return root2;
|
||||
}
|
||||
function applyImmutable(target, ops2) {
|
||||
return applyImmutableBatches(target, [ops2]);
|
||||
function applyImmutable(target, ops3) {
|
||||
return applyImmutableBatches(target, [ops3]);
|
||||
}
|
||||
function applyImmutableBatches(target, batches) {
|
||||
let root2 = target;
|
||||
const owned = /* @__PURE__ */ new WeakSet();
|
||||
for (const ops2 of batches) {
|
||||
for (const op2 of ops2) {
|
||||
for (const ops3 of batches) {
|
||||
for (const op2 of ops3) {
|
||||
assertValidOp(op2);
|
||||
if (op2[0] === "r") {
|
||||
root2 = op2[1];
|
||||
@@ -9462,10 +9462,10 @@ function encoder() {
|
||||
let nextId = 0;
|
||||
let previous;
|
||||
return {
|
||||
encode(ops2) {
|
||||
encode(ops3) {
|
||||
previous = void 0;
|
||||
const out = [];
|
||||
for (const op2 of ops2) {
|
||||
for (const op2 of ops3) {
|
||||
if (op2[0] === "r") {
|
||||
out.push(op2);
|
||||
seen.clear();
|
||||
@@ -9715,9 +9715,9 @@ var Source = class {
|
||||
return [];
|
||||
}
|
||||
};
|
||||
function unbox(ops2, value2) {
|
||||
function unbox(ops3, value2) {
|
||||
const result = [];
|
||||
for (const op2 of ops2) {
|
||||
for (const op2 of ops3) {
|
||||
if (op2[0] === "r" || op2[1].length === 0)
|
||||
return [["r", value2]];
|
||||
const path = op2[1].slice(1);
|
||||
@@ -9811,8 +9811,8 @@ var Cell = class extends Source {
|
||||
throw notReady(`state ${this.key} (its init has not run)`);
|
||||
const before = this.value;
|
||||
const diff = options.diff ?? sharesStructure(before, value2);
|
||||
const ops2 = diff ? diffRevisions(before, value2) : void 0;
|
||||
this.#publish(this.#tracker.prepareReplace({ v: value2 }), "ops", true, ops2);
|
||||
const ops3 = diff ? diffRevisions(before, value2) : void 0;
|
||||
this.#publish(this.#tracker.prepareReplace({ v: value2 }), "ops", true, ops3);
|
||||
}
|
||||
/**
|
||||
* Installs a migrated value and its schema (reload commit). The caller validated the value and the derivations
|
||||
@@ -9823,7 +9823,7 @@ var Cell = class extends Source {
|
||||
this.#publish(this.#tracker.prepareReplace({ v: value2 }), "reset", false);
|
||||
}
|
||||
/** Adopts this revision and every derived one, then queues all notifications (cell first, then derivations). */
|
||||
#publish(prepared, type, validate2 = true, ops2) {
|
||||
#publish(prepared, type, validate2 = true, ops3) {
|
||||
if (prepared.ops.length === 0 && type === "ops") {
|
||||
this.#tracker.adopt(prepared);
|
||||
return;
|
||||
@@ -9840,7 +9840,7 @@ var Cell = class extends Source {
|
||||
}
|
||||
holdPublications(() => {
|
||||
this.#tracker.adopt(prepared);
|
||||
const published = ops2 !== void 0 && ops2.length > 0 ? ops2 : unbox(prepared.ops, value2);
|
||||
const published = ops3 !== void 0 && ops3.length > 0 ? ops3 : unbox(prepared.ops, value2);
|
||||
this.fanout.emit(type === "reset" ? { type, value: value2 } : { type, ops: published, value: value2 });
|
||||
for (const [derived, next] of candidates)
|
||||
derived.install(next);
|
||||
@@ -9953,9 +9953,9 @@ var Derived = class extends Source {
|
||||
install(next) {
|
||||
const before = this.#value;
|
||||
this.#value = next;
|
||||
const ops2 = before === void 0 || next === void 0 ? [["r", next ?? null]] : diffRevisions(before, next);
|
||||
if (ops2.length > 0)
|
||||
this.fanout.emit({ type: "ops", ops: ops2, value: next });
|
||||
const ops3 = before === void 0 || next === void 0 ? [["r", next ?? null]] : diffRevisions(before, next);
|
||||
if (ops3.length > 0)
|
||||
this.fanout.emit({ type: "ops", ops: ops3, value: next });
|
||||
}
|
||||
};
|
||||
var isSource = (value2) => value2 instanceof Source;
|
||||
@@ -14008,10 +14008,10 @@ var RemoteBinding = class {
|
||||
const target = pending?.values.get(name) ?? this.members.get(name);
|
||||
if (target === void 0)
|
||||
return;
|
||||
const ops2 = target.decoder.decode(frame.ops ?? []);
|
||||
target.value = applyImmutable(target.value, ops2);
|
||||
const ops3 = target.decoder.decode(frame.ops ?? []);
|
||||
target.value = applyImmutable(target.value, ops3);
|
||||
if (target === this.members.get(name))
|
||||
this.#deliver(name, { type: "ops", ops: ops2, value: target.value });
|
||||
this.#deliver(name, { type: "ops", ops: ops3, value: target.value });
|
||||
return;
|
||||
}
|
||||
const fresh = { value: frame.value, decoder: decoder() };
|
||||
@@ -15835,8 +15835,8 @@ var Watch = class extends WatchLifecycle {
|
||||
this.#retired = false;
|
||||
if (lone !== void 0)
|
||||
return lone;
|
||||
const ops2 = batch.kind === "ops" ? batch.ops : [["r", value2]];
|
||||
return { kind: "value", value: value2, ops: ops2, retired };
|
||||
const ops3 = batch.kind === "ops" ? batch.ops : [["r", value2]];
|
||||
return { kind: "value", value: value2, ops: ops3, retired };
|
||||
}
|
||||
/** The pending failure, or the value after one as a root replacement (no ops were kept since the failure). */
|
||||
#nextAfterFailure() {
|
||||
@@ -17601,7 +17601,7 @@ function describe(plan, record2) {
|
||||
const change = plan.change;
|
||||
if (change === void 0 || !publishes(plan))
|
||||
return void 0;
|
||||
const ops2 = change.loaded === void 0 ? EMPTY_OPERATIONS : change.prepared.ops;
|
||||
const ops3 = change.loaded === void 0 ? EMPTY_OPERATIONS : change.prepared.ops;
|
||||
return {
|
||||
type: "document",
|
||||
kind: "change",
|
||||
@@ -17609,11 +17609,11 @@ function describe(plan, record2) {
|
||||
conversationId,
|
||||
version: change.version,
|
||||
value: change.prepared.value,
|
||||
ops: ops2
|
||||
ops: ops3
|
||||
};
|
||||
}
|
||||
function storeHeld(hold, ops2, value2, version) {
|
||||
hold.pending.push(ops2);
|
||||
function storeHeld(hold, ops3, value2, version) {
|
||||
hold.pending.push(ops3);
|
||||
const taken = hold.pending.take();
|
||||
if (taken.kind === "replace")
|
||||
return { content: { version, kind: "base", value: value2 }, ops: [["r", value2]] };
|
||||
@@ -18694,7 +18694,7 @@ var SessionImpl = class {
|
||||
return { kind: "adoption", error };
|
||||
}
|
||||
const deadline = this.#clock() + withinMs;
|
||||
for (const { loaded, ops: ops2, base: base2 } of held2) {
|
||||
for (const { loaded, ops: ops3, base: base2 } of held2) {
|
||||
const id = loaded.record.id;
|
||||
let hold = this.#holds.get(id);
|
||||
if (hold === void 0) {
|
||||
@@ -18702,7 +18702,7 @@ var SessionImpl = class {
|
||||
hold = { loaded, pending: new PendingOps(), base: false, deadline, unpin };
|
||||
this.#holds.set(id, hold);
|
||||
}
|
||||
hold.pending.push(ops2);
|
||||
hold.pending.push(ops3);
|
||||
if (base2)
|
||||
hold.base = true;
|
||||
if (deadline < hold.deadline)
|
||||
@@ -26917,14 +26917,14 @@ var TaskGraphView = class {
|
||||
const mount = this.#mount;
|
||||
if (mount === void 0)
|
||||
return;
|
||||
const ops2 = [];
|
||||
const ops3 = [];
|
||||
for (const [key, record2] of mount.records) {
|
||||
const blocked = this.#blocking.blocked(record2);
|
||||
if (blocked === mount.value.tasks[key]?.blocked)
|
||||
continue;
|
||||
ops2.push(blocked === void 0 ? ["d", ["tasks", key, "blocked"]] : ["s", ["tasks", key, "blocked"], blocked]);
|
||||
ops3.push(blocked === void 0 ? ["d", ["tasks", key, "blocked"]] : ["s", ["tasks", key, "blocked"], blocked]);
|
||||
}
|
||||
publish(mount, ops2, BACKGROUND_CONTEXT, (error) => this.#session.report(error));
|
||||
publish(mount, ops3, BACKGROUND_CONTEXT, (error) => this.#session.report(error));
|
||||
}).catch(() => {
|
||||
});
|
||||
}
|
||||
@@ -26948,7 +26948,7 @@ var TaskGraphView = class {
|
||||
}
|
||||
};
|
||||
function advance(mount, publication, context, blocked, report2) {
|
||||
const ops2 = [];
|
||||
const ops3 = [];
|
||||
const changed = /* @__PURE__ */ new Map();
|
||||
const node = (key) => changed.has(key) ? changed.get(key) : mount.value.tasks[key];
|
||||
for (const change of publication.changes) {
|
||||
@@ -26961,7 +26961,7 @@ function advance(mount, publication, context, blocked, report2) {
|
||||
mount.records.delete(key);
|
||||
if (previous === void 0)
|
||||
continue;
|
||||
ops2.push(["d", ["tasks", key]]);
|
||||
ops3.push(["d", ["tasks", key]]);
|
||||
changed.set(key, void 0);
|
||||
continue;
|
||||
}
|
||||
@@ -26970,7 +26970,7 @@ function advance(mount, publication, context, blocked, report2) {
|
||||
const next = nodeOf2(record2, previous?.conversations ?? [], blocked(subject));
|
||||
if (previous !== void 0 && JSON.stringify(next) === JSON.stringify(previous))
|
||||
continue;
|
||||
ops2.push(["s", ["tasks", key], next]);
|
||||
ops3.push(["s", ["tasks", key], next]);
|
||||
changed.set(key, next);
|
||||
}
|
||||
const created = /* @__PURE__ */ new Map();
|
||||
@@ -26983,16 +26983,16 @@ function advance(mount, publication, context, blocked, report2) {
|
||||
}
|
||||
for (const [key, ids] of created) {
|
||||
const conversations = [...node(key).conversations, ...ids].sort((a, b) => a - b);
|
||||
ops2.push(["s", ["tasks", key, "conversations"], conversations]);
|
||||
ops3.push(["s", ["tasks", key, "conversations"], conversations]);
|
||||
}
|
||||
publish(mount, ops2, context, report2);
|
||||
publish(mount, ops3, context, report2);
|
||||
}
|
||||
function publish(mount, ops2, context, report2) {
|
||||
if (ops2.length === 0)
|
||||
function publish(mount, ops3, context, report2) {
|
||||
if (ops3.length === 0)
|
||||
return;
|
||||
mount.value = applyImmutable(mount.value, ops2);
|
||||
mount.value = applyImmutable(mount.value, ops3);
|
||||
const frameContext = withoutAbortSignal(context);
|
||||
const call2 = { kind: "value", value: mount.value, ops: ops2, retired: false };
|
||||
const call2 = { kind: "value", value: mount.value, ops: ops3, retired: false };
|
||||
for (const observer of [...mount.observers])
|
||||
contained(() => observer.advance(call2, frameContext), report2);
|
||||
}
|
||||
@@ -27218,19 +27218,19 @@ function advance2(id, mount, publication, context, report2) {
|
||||
const previous = windowed.value;
|
||||
let entries2 = previous.entries;
|
||||
let before = previous.before;
|
||||
let ops2 = docOps;
|
||||
let ops3 = docOps;
|
||||
if (appended.length > 0) {
|
||||
ops2 = [...docOps, ["p", ["entries"], entries2.length, 0, inserted]];
|
||||
ops3 = [...docOps, ["p", ["entries"], entries2.length, 0, inserted]];
|
||||
entries2 = entries2.concat(appended);
|
||||
if (entries2.length > windowed.max) {
|
||||
const drop = entries2.length - windowed.min;
|
||||
before = entries2[drop - 1].id;
|
||||
entries2 = entries2.slice(drop);
|
||||
ops2.push(["p", ["entries"], 0, drop, []], ["s", ["before"], before]);
|
||||
ops3.push(["p", ["entries"], 0, drop, []], ["s", ["before"], before]);
|
||||
}
|
||||
}
|
||||
windowed.value = view(mount.conversation, entries2, before, mount.docs);
|
||||
const call2 = { kind: "value", value: windowed.value, ops: ops2, retired: false };
|
||||
const call2 = { kind: "value", value: windowed.value, ops: ops3, retired: false };
|
||||
contained(() => windowed.observer.advance(call2, frameContext), report2);
|
||||
}
|
||||
}
|
||||
@@ -31368,14 +31368,51 @@ function eventLog(harness2) {
|
||||
}
|
||||
}
|
||||
|
||||
// extensions.ts
|
||||
// environment.ts
|
||||
var CodingTools2 = { ...CodingTools, tools: CodingTools.tools.map((tool) => ({
|
||||
...tool,
|
||||
execute: (args, api, context) => tool.execute(args, { ...api, env: api.env.forTool(api.callId, tool.name) }, context)
|
||||
})) };
|
||||
var Started = defineDoc({
|
||||
kind: "board.started",
|
||||
version: 1,
|
||||
checkpointWhen: () => false,
|
||||
scope: "conversation",
|
||||
history: "latest",
|
||||
fork: "initial",
|
||||
initial: () => ({})
|
||||
});
|
||||
function conversationChains(harness2, context) {
|
||||
const cache = /* @__PURE__ */ new Map();
|
||||
const chain = (id) => {
|
||||
let result = cache.get(id);
|
||||
if (!result) {
|
||||
result = (async () => {
|
||||
const record2 = await harness2.commit((tx) => tx.conversation(id), context);
|
||||
const up = record2?.owner?.conversationId ?? record2?.parent?.conversationId;
|
||||
const starter = (await harness2.snapshot(Started, id, context))?.by;
|
||||
return [.../* @__PURE__ */ new Set([
|
||||
String(id),
|
||||
...up === void 0 ? [] : await chain(up),
|
||||
...starter === void 0 ? [] : await chain(starter)
|
||||
])];
|
||||
})();
|
||||
cache.set(id, result);
|
||||
}
|
||||
return result;
|
||||
};
|
||||
return chain;
|
||||
}
|
||||
var ops = Deno.core.ops;
|
||||
|
||||
// extensions.ts
|
||||
var ops2 = Deno.core.ops;
|
||||
var encoder4 = new TextEncoder();
|
||||
var decoder4 = new TextDecoder();
|
||||
var ExtensionState = defineDoc({ kind: "sausage.extensions", version: 1, scope: "session", checkpointWhen: () => false, initial: () => ({ good: null, current: null, error: null }) });
|
||||
var Metadata = defineDoc({ kind: "sausage.metadata", version: 1, scope: "conversation", history: "latest", fork: "current", initial: () => ({ label: "", title: "" }), checkpointWhen: () => false });
|
||||
async function file(kind, path, bytes2 = []) {
|
||||
const result = await ops.op_file(kind, path, bytes2);
|
||||
const result = await ops2.op_file(kind, path, bytes2);
|
||||
if (!result.ok) throw Object.assign(new Error(result.error.message), result.error);
|
||||
return result.value;
|
||||
}
|
||||
@@ -31398,14 +31435,14 @@ async function exec(command, options) {
|
||||
program: `extension:${options.extension}`,
|
||||
conversations: options.conversations ?? []
|
||||
} : void 0;
|
||||
const rid = ops.op_exec(command, options.cwd, options.timeout, void 0, options.env, options.conversations, tag);
|
||||
const rid = ops2.op_exec(command, options.cwd, options.timeout, void 0, options.env, options.conversations, tag);
|
||||
const abort = () => Deno.core.close(rid);
|
||||
options.signal?.addEventListener("abort", abort, { once: true });
|
||||
const decoders = { stdout: new TextDecoder(), stderr: new TextDecoder() };
|
||||
const result = { code: 0, stdout: "", stderr: "" };
|
||||
try {
|
||||
for (; ; ) {
|
||||
const chunk = await ops.op_exec_next(rid);
|
||||
const chunk = await ops2.op_exec_next(rid);
|
||||
if (!chunk.stream) {
|
||||
if (chunk.error) throw new Error(chunk.error);
|
||||
result.code = chunk.exitCode;
|
||||
@@ -31432,11 +31469,12 @@ async function git(cwd, ...args) {
|
||||
function registerModules() {
|
||||
const modules = { "@earendil-works/sausage-events": events_exports, "@earendil-works/pi-schema": dist_exports, "@earendil-works/pi-durable": dist_exports3, "@earendil-works/pi-durable/tools": tools_exports, "@earendil-works/pi-ai": dist_exports4, "@earendil-works/pi-ai/models": models_exports, "@earendil-works/chord": dist_exports2, "@earendil-works/chord/context": context_exports };
|
||||
globalThis.__sausage = { modules };
|
||||
ops.op_modules(Object.fromEntries(Object.entries(modules).map(([name, value2]) => [name, Object.keys(value2)])));
|
||||
ops2.op_modules(Object.fromEntries(Object.entries(modules).map(([name, value2]) => [name, Object.keys(value2)])));
|
||||
}
|
||||
async function loadExtensions(harness2, registry2, storage2, models2, context, builtins = []) {
|
||||
registerModules();
|
||||
const eventService = eventLog(() => harness2);
|
||||
const chain = conversationChains(harness2, context);
|
||||
const repo = "/src/sausage";
|
||||
let providers = /* @__PURE__ */ new Map();
|
||||
const resources = /* @__PURE__ */ new Map();
|
||||
@@ -31447,7 +31485,7 @@ async function loadExtensions(harness2, registry2, storage2, models2, context, b
|
||||
let queue2 = Promise.resolve();
|
||||
const publish2 = async () => {
|
||||
await harness2.commit(async (tx) => Object.assign(await tx.doc(ExtensionState), state), context);
|
||||
ops.op_extension_status(JSON.stringify(state));
|
||||
ops2.op_extension_status(JSON.stringify(state));
|
||||
await fs.write("/src/sausage/.git/sausage-last-good", state.good ?? "");
|
||||
};
|
||||
const dispose = async (entries2) => {
|
||||
@@ -31455,7 +31493,7 @@ async function loadExtensions(harness2, registry2, storage2, models2, context, b
|
||||
try {
|
||||
await cleanup();
|
||||
} catch (error) {
|
||||
ops.op_output(`Extension cleanup: ${String(error)}
|
||||
ops2.op_output(`Extension cleanup: ${String(error)}
|
||||
`);
|
||||
}
|
||||
}
|
||||
@@ -31488,6 +31526,7 @@ async function loadExtensions(harness2, registry2, storage2, models2, context, b
|
||||
fs,
|
||||
exec: (command, options) => exec(command, { ...options, extension: name }),
|
||||
git,
|
||||
chain,
|
||||
implement: (api, value2) => {
|
||||
staged.set(api.name, value2);
|
||||
},
|
||||
@@ -31510,12 +31549,12 @@ async function loadExtensions(harness2, registry2, storage2, models2, context, b
|
||||
onClose: (cb) => close.push(cb),
|
||||
notice: (message) => {
|
||||
state.notice = `${name}: ${message}`;
|
||||
ops.op_extension_status(JSON.stringify(state));
|
||||
ops.op_output(`${name}: ${message}
|
||||
ops2.op_extension_status(JSON.stringify(state));
|
||||
ops2.op_output(`${name}: ${message}
|
||||
`);
|
||||
if (!notices.has(message)) {
|
||||
notices.add(message);
|
||||
void harness2.commit((tx) => eventService.emit(tx, NoticeEvent, { extension: name, message, level: "warning" }), context).catch((error) => ops.op_output(`Recording a notice failed: ${String(error)}
|
||||
void harness2.commit((tx) => eventService.emit(tx, NoticeEvent, { extension: name, message, level: "warning" }), context).catch((error) => ops2.op_output(`Recording a notice failed: ${String(error)}
|
||||
`));
|
||||
}
|
||||
}
|
||||
@@ -31594,7 +31633,7 @@ async function loadExtensions(harness2, registry2, storage2, models2, context, b
|
||||
tip = await git(repo, "rev-parse", "refs/heads/main");
|
||||
} catch (error) {
|
||||
state.error = String(error);
|
||||
ops.op_extension_status(JSON.stringify(state));
|
||||
ops2.op_extension_status(JSON.stringify(state));
|
||||
return;
|
||||
}
|
||||
try {
|
||||
@@ -31603,8 +31642,8 @@ async function loadExtensions(harness2, registry2, storage2, models2, context, b
|
||||
if (error.code === "not_found") settings = {};
|
||||
else {
|
||||
state.notice = `Settings: ${String(error)}`;
|
||||
ops.op_extension_status(JSON.stringify(state));
|
||||
ops.op_output(`${state.notice}
|
||||
ops2.op_extension_status(JSON.stringify(state));
|
||||
ops2.op_output(`${state.notice}
|
||||
`);
|
||||
}
|
||||
}
|
||||
@@ -31620,11 +31659,11 @@ Last good: ${fallback}` : failed;
|
||||
await publish2();
|
||||
};
|
||||
const enqueue2 = () => queue2 = queue2.then(reload);
|
||||
const rid = ops.op_watch([`${repo}/.git/refs/heads/main`, `${repo}/.git/packed-refs`, `${repo}/settings.json`]);
|
||||
const rid = ops2.op_watch([`${repo}/.git/refs/heads/main`, `${repo}/.git/packed-refs`, `${repo}/settings.json`]);
|
||||
await enqueue2();
|
||||
const pending = (async () => {
|
||||
while (active) {
|
||||
await ops.op_watch_next(rid);
|
||||
await ops2.op_watch_next(rid);
|
||||
if (active) await enqueue2();
|
||||
}
|
||||
})();
|
||||
@@ -31671,7 +31710,7 @@ var todosModule = await import(
|
||||
/* @vite-ignore */
|
||||
`${base}/todos/server.ts`
|
||||
);
|
||||
var { Plan, TaskIndex, Branch, Started } = planModule;
|
||||
var { Plan, TaskIndex, Branch, Started: Started2 } = planModule;
|
||||
var { Todos, Standing, add, mark, noteFor, DONE_LIMIT } = todosModule;
|
||||
var call = async (args, id = root.id, name = "plan") => {
|
||||
const conversation = await harness.conversation(id, BACKGROUND_CONTEXT);
|
||||
@@ -31776,7 +31815,7 @@ try {
|
||||
}, BACKGROUND_CONTEXT);
|
||||
assert(Object.keys((await harness.snapshot(Plan, branch.id, BACKGROUND_CONTEXT)).epics).length === 1, "Fork inherited plan");
|
||||
equal(JSON.parse(text(await call({ action: "show" }, branch.id))), Plan.definition.initial());
|
||||
assert((await harness.snapshot(Started, branch.id, BACKGROUND_CONTEXT)).started, "Branch started");
|
||||
assert((await harness.snapshot(Started2, branch.id, BACKGROUND_CONTEXT)).started, "Branch started");
|
||||
const ULTIMATE = 5;
|
||||
const fakeTx = () => {
|
||||
const docs = /* @__PURE__ */ new Map();
|
||||
|
||||
@@ -30428,14 +30428,51 @@ function eventLog(harness2) {
|
||||
}
|
||||
}
|
||||
|
||||
// extensions.ts
|
||||
// environment.ts
|
||||
var CodingTools2 = { ...CodingTools, tools: CodingTools.tools.map((tool) => ({
|
||||
...tool,
|
||||
execute: (args, api, context) => tool.execute(args, { ...api, env: api.env.forTool(api.callId, tool.name) }, context)
|
||||
})) };
|
||||
var Started = defineDoc({
|
||||
kind: "board.started",
|
||||
version: 1,
|
||||
checkpointWhen: () => false,
|
||||
scope: "conversation",
|
||||
history: "latest",
|
||||
fork: "initial",
|
||||
initial: () => ({})
|
||||
});
|
||||
function conversationChains(harness2, context) {
|
||||
const cache = /* @__PURE__ */ new Map();
|
||||
const chain2 = (id) => {
|
||||
let result = cache.get(id);
|
||||
if (!result) {
|
||||
result = (async () => {
|
||||
const record2 = await harness2.commit((tx) => tx.conversation(id), context);
|
||||
const up = record2?.owner?.conversationId ?? record2?.parent?.conversationId;
|
||||
const starter = (await harness2.snapshot(Started, id, context))?.by;
|
||||
return [.../* @__PURE__ */ new Set([
|
||||
String(id),
|
||||
...up === void 0 ? [] : await chain2(up),
|
||||
...starter === void 0 ? [] : await chain2(starter)
|
||||
])];
|
||||
})();
|
||||
cache.set(id, result);
|
||||
}
|
||||
return result;
|
||||
};
|
||||
return chain2;
|
||||
}
|
||||
var ops = Deno.core.ops;
|
||||
|
||||
// extensions.ts
|
||||
var ops2 = Deno.core.ops;
|
||||
var encoder4 = new TextEncoder();
|
||||
var decoder4 = new TextDecoder();
|
||||
var ExtensionState = defineDoc({ kind: "sausage.extensions", version: 1, scope: "session", checkpointWhen: () => false, initial: () => ({ good: null, current: null, error: null }) });
|
||||
var Metadata = defineDoc({ kind: "sausage.metadata", version: 1, scope: "conversation", history: "latest", fork: "current", initial: () => ({ label: "", title: "" }), checkpointWhen: () => false });
|
||||
async function file(kind, path, bytes2 = []) {
|
||||
const result = await ops.op_file(kind, path, bytes2);
|
||||
const result = await ops2.op_file(kind, path, bytes2);
|
||||
if (!result.ok) throw Object.assign(new Error(result.error.message), result.error);
|
||||
return result.value;
|
||||
}
|
||||
@@ -30458,14 +30495,14 @@ async function exec(command, options) {
|
||||
program: `extension:${options.extension}`,
|
||||
conversations: options.conversations ?? []
|
||||
} : void 0;
|
||||
const rid = ops.op_exec(command, options.cwd, options.timeout, void 0, options.env, options.conversations, tag);
|
||||
const rid = ops2.op_exec(command, options.cwd, options.timeout, void 0, options.env, options.conversations, tag);
|
||||
const abort = () => Deno.core.close(rid);
|
||||
options.signal?.addEventListener("abort", abort, { once: true });
|
||||
const decoders = { stdout: new TextDecoder(), stderr: new TextDecoder() };
|
||||
const result = { code: 0, stdout: "", stderr: "" };
|
||||
try {
|
||||
for (; ; ) {
|
||||
const chunk = await ops.op_exec_next(rid);
|
||||
const chunk = await ops2.op_exec_next(rid);
|
||||
if (!chunk.stream) {
|
||||
if (chunk.error) throw new Error(chunk.error);
|
||||
result.code = chunk.exitCode;
|
||||
@@ -30492,11 +30529,12 @@ async function git(cwd, ...args) {
|
||||
function registerModules() {
|
||||
const modules = { "@earendil-works/sausage-events": events_exports, "@earendil-works/pi-schema": dist_exports, "@earendil-works/pi-durable": dist_exports3, "@earendil-works/pi-durable/tools": tools_exports, "@earendil-works/pi-ai": dist_exports4, "@earendil-works/pi-ai/models": models_exports, "@earendil-works/chord": dist_exports2, "@earendil-works/chord/context": context_exports };
|
||||
globalThis.__sausage = { modules };
|
||||
ops.op_modules(Object.fromEntries(Object.entries(modules).map(([name, value2]) => [name, Object.keys(value2)])));
|
||||
ops2.op_modules(Object.fromEntries(Object.entries(modules).map(([name, value2]) => [name, Object.keys(value2)])));
|
||||
}
|
||||
async function loadExtensions(harness2, registry2, storage2, models2, context, builtins = []) {
|
||||
registerModules();
|
||||
const eventService = eventLog(() => harness2);
|
||||
const chain2 = conversationChains(harness2, context);
|
||||
const repo = "/src/sausage";
|
||||
let providers = /* @__PURE__ */ new Map();
|
||||
const resources = /* @__PURE__ */ new Map();
|
||||
@@ -30507,7 +30545,7 @@ async function loadExtensions(harness2, registry2, storage2, models2, context, b
|
||||
let queue2 = Promise.resolve();
|
||||
const publish2 = async () => {
|
||||
await harness2.commit(async (tx) => Object.assign(await tx.doc(ExtensionState), state), context);
|
||||
ops.op_extension_status(JSON.stringify(state));
|
||||
ops2.op_extension_status(JSON.stringify(state));
|
||||
await fs.write("/src/sausage/.git/sausage-last-good", state.good ?? "");
|
||||
};
|
||||
const dispose = async (entries2) => {
|
||||
@@ -30515,7 +30553,7 @@ async function loadExtensions(harness2, registry2, storage2, models2, context, b
|
||||
try {
|
||||
await cleanup();
|
||||
} catch (error) {
|
||||
ops.op_output(`Extension cleanup: ${String(error)}
|
||||
ops2.op_output(`Extension cleanup: ${String(error)}
|
||||
`);
|
||||
}
|
||||
}
|
||||
@@ -30548,6 +30586,7 @@ async function loadExtensions(harness2, registry2, storage2, models2, context, b
|
||||
fs,
|
||||
exec: (command, options) => exec(command, { ...options, extension: name }),
|
||||
git,
|
||||
chain: chain2,
|
||||
implement: (api, value2) => {
|
||||
staged.set(api.name, value2);
|
||||
},
|
||||
@@ -30570,12 +30609,12 @@ async function loadExtensions(harness2, registry2, storage2, models2, context, b
|
||||
onClose: (cb) => close.push(cb),
|
||||
notice: (message) => {
|
||||
state.notice = `${name}: ${message}`;
|
||||
ops.op_extension_status(JSON.stringify(state));
|
||||
ops.op_output(`${name}: ${message}
|
||||
ops2.op_extension_status(JSON.stringify(state));
|
||||
ops2.op_output(`${name}: ${message}
|
||||
`);
|
||||
if (!notices.has(message)) {
|
||||
notices.add(message);
|
||||
void harness2.commit((tx) => eventService.emit(tx, NoticeEvent, { extension: name, message, level: "warning" }), context).catch((error) => ops.op_output(`Recording a notice failed: ${String(error)}
|
||||
void harness2.commit((tx) => eventService.emit(tx, NoticeEvent, { extension: name, message, level: "warning" }), context).catch((error) => ops2.op_output(`Recording a notice failed: ${String(error)}
|
||||
`));
|
||||
}
|
||||
}
|
||||
@@ -30654,7 +30693,7 @@ async function loadExtensions(harness2, registry2, storage2, models2, context, b
|
||||
tip = await git(repo, "rev-parse", "refs/heads/main");
|
||||
} catch (error) {
|
||||
state.error = String(error);
|
||||
ops.op_extension_status(JSON.stringify(state));
|
||||
ops2.op_extension_status(JSON.stringify(state));
|
||||
return;
|
||||
}
|
||||
try {
|
||||
@@ -30663,8 +30702,8 @@ async function loadExtensions(harness2, registry2, storage2, models2, context, b
|
||||
if (error.code === "not_found") settings = {};
|
||||
else {
|
||||
state.notice = `Settings: ${String(error)}`;
|
||||
ops.op_extension_status(JSON.stringify(state));
|
||||
ops.op_output(`${state.notice}
|
||||
ops2.op_extension_status(JSON.stringify(state));
|
||||
ops2.op_output(`${state.notice}
|
||||
`);
|
||||
}
|
||||
}
|
||||
@@ -30680,11 +30719,11 @@ Last good: ${fallback}` : failed;
|
||||
await publish2();
|
||||
};
|
||||
const enqueue2 = () => queue2 = queue2.then(reload);
|
||||
const rid = ops.op_watch([`${repo}/.git/refs/heads/main`, `${repo}/.git/packed-refs`, `${repo}/settings.json`]);
|
||||
const rid = ops2.op_watch([`${repo}/.git/refs/heads/main`, `${repo}/.git/packed-refs`, `${repo}/settings.json`]);
|
||||
await enqueue2();
|
||||
const pending = (async () => {
|
||||
while (active) {
|
||||
await ops.op_watch_next(rid);
|
||||
await ops2.op_watch_next(rid);
|
||||
if (active) await enqueue2();
|
||||
}
|
||||
})();
|
||||
@@ -30697,43 +30736,6 @@ Last good: ${fallback}` : failed;
|
||||
} };
|
||||
}
|
||||
|
||||
// environment.ts
|
||||
var CodingTools2 = { ...CodingTools, tools: CodingTools.tools.map((tool) => ({
|
||||
...tool,
|
||||
execute: (args, api, context) => tool.execute(args, { ...api, env: api.env.forTool(api.callId, tool.name) }, context)
|
||||
})) };
|
||||
var Started = defineDoc({
|
||||
kind: "board.started",
|
||||
version: 1,
|
||||
checkpointWhen: () => false,
|
||||
scope: "conversation",
|
||||
history: "latest",
|
||||
fork: "initial",
|
||||
initial: () => ({})
|
||||
});
|
||||
function conversationChains(harness2, context) {
|
||||
const cache = /* @__PURE__ */ new Map();
|
||||
const chain2 = (id) => {
|
||||
let result = cache.get(id);
|
||||
if (!result) {
|
||||
result = (async () => {
|
||||
const record2 = await harness2.commit((tx) => tx.conversation(id), context);
|
||||
const up = record2?.owner?.conversationId ?? record2?.parent?.conversationId;
|
||||
const starter = (await harness2.snapshot(Started, id, context))?.by;
|
||||
return [.../* @__PURE__ */ new Set([
|
||||
String(id),
|
||||
...up === void 0 ? [] : await chain2(up),
|
||||
...starter === void 0 ? [] : await chain2(starter)
|
||||
])];
|
||||
})();
|
||||
cache.set(id, result);
|
||||
}
|
||||
return result;
|
||||
};
|
||||
return chain2;
|
||||
}
|
||||
var ops2 = Deno.core.ops;
|
||||
|
||||
// node_modules/@earendil-works/chord/dist/sqlite/schema.js
|
||||
function validateSqliteSchemaPrefix(prefix) {
|
||||
if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(prefix)) {
|
||||
|
||||
Reference in New Issue
Block a user