Make credential requests durable and store proxy authentication
This commit is contained in:
+102
-42
@@ -33805,9 +33805,9 @@ async function createAgent(tx, conversation) {
|
||||
function agentHooks(agent, taskName) {
|
||||
const handlers = [];
|
||||
for (const extension of agent.extensions) {
|
||||
for (const hook of extension.hooks ?? []) {
|
||||
if (hook.task === taskName)
|
||||
handlers.push({ extension: extension.name, handlers: hook.handlers });
|
||||
for (const hook2 of extension.hooks ?? []) {
|
||||
if (hook2.task === taskName)
|
||||
handlers.push({ extension: extension.name, handlers: hook2.handlers });
|
||||
}
|
||||
}
|
||||
return handlers;
|
||||
@@ -33815,7 +33815,7 @@ function agentHooks(agent, taskName) {
|
||||
function sectionExtension(agent, key) {
|
||||
let found;
|
||||
for (const extension of agent.extensions) {
|
||||
const provides = extension.sections?.some((section) => section.key === key) === true;
|
||||
const provides = extension.sections?.some((section2) => section2.key === key) === true;
|
||||
const wraps = extension.wraps?.some((wrap) => "section" in wrap && wrap.section === key) === true;
|
||||
if (provides || wraps)
|
||||
found = extension.name;
|
||||
@@ -33840,8 +33840,8 @@ function resolveAgent(state2, snapshot, settings, report2) {
|
||||
composed.set(tool.name, tool);
|
||||
const sections = /* @__PURE__ */ new Map();
|
||||
for (const extension of extensions) {
|
||||
for (const section of extension.sections ?? [])
|
||||
sections.set(section.key, section);
|
||||
for (const section2 of extension.sections ?? [])
|
||||
sections.set(section2.key, section2);
|
||||
}
|
||||
for (const extension of extensions) {
|
||||
for (const wrap of extension.wraps ?? []) {
|
||||
@@ -33849,7 +33849,7 @@ function resolveAgent(state2, snapshot, settings, report2) {
|
||||
if ("tool" in wrap)
|
||||
applyWrap(composed, wrap.tool, (tool) => wrap.wrap(tool), (tool) => tool.name, wrapReport);
|
||||
else
|
||||
applyWrap(sections, wrap.section, (section) => wrap.wrap(section), (section) => section.key, wrapReport);
|
||||
applyWrap(sections, wrap.section, (section2) => wrap.wrap(section2), (section2) => section2.key, wrapReport);
|
||||
}
|
||||
}
|
||||
const enabled = filterTools([...composed.values()], state2?.tools);
|
||||
@@ -34685,24 +34685,24 @@ function replaySections(messages) {
|
||||
}
|
||||
async function renderSections(sections, input, shown, report2, context) {
|
||||
const desired = /* @__PURE__ */ new Map();
|
||||
for (const section of sections) {
|
||||
for (const section2 of sections) {
|
||||
let text2;
|
||||
try {
|
||||
text2 = await section.render(input, context);
|
||||
text2 = await section2.render(input, context);
|
||||
} catch (error) {
|
||||
if (context.abortSignal?.aborted)
|
||||
throw error;
|
||||
report2(error, section);
|
||||
const kept = shown.get(section.key);
|
||||
report2(error, section2);
|
||||
const kept = shown.get(section2.key);
|
||||
if (kept !== void 0)
|
||||
desired.set(section.key, kept);
|
||||
desired.set(section2.key, kept);
|
||||
continue;
|
||||
}
|
||||
if (text2 === void 0)
|
||||
continue;
|
||||
desired.set(section.key, section.tag === false ? text2 : `<${section.key}>
|
||||
desired.set(section2.key, section2.tag === false ? text2 : `<${section2.key}>
|
||||
${text2}
|
||||
</${section.key}>`);
|
||||
</${section2.key}>`);
|
||||
}
|
||||
return desired;
|
||||
}
|
||||
@@ -38706,11 +38706,11 @@ var ToolTask = defineTask({
|
||||
return reject(runtime, task.input, call, tool, invalid(checked.error), context);
|
||||
let args = checked.args;
|
||||
let block;
|
||||
await runtime.hooks.each("beforeTool", async (hook) => {
|
||||
await runtime.hooks.each("beforeTool", async (hook2) => {
|
||||
if (block !== void 0)
|
||||
return;
|
||||
try {
|
||||
const decision = await hook({ ...call, arguments: args }, runtime, context);
|
||||
const decision = await hook2({ ...call, arguments: args }, runtime, context);
|
||||
if (decision?.block !== void 0)
|
||||
block = decision.block;
|
||||
else if (decision?.arguments !== void 0)
|
||||
@@ -39052,8 +39052,8 @@ async function finalResult(runtime, input, call, tool, result, reported, context
|
||||
details: result.details === void 0 ? reported.details : result.details,
|
||||
diagnostics: [...reported.diagnostics, ...result.diagnostics ?? []]
|
||||
};
|
||||
await runtime.hooks.each("afterTool", async (hook) => {
|
||||
final = await hook(call, final, runtime, context) ?? final;
|
||||
await runtime.hooks.each("afterTool", async (hook2) => {
|
||||
final = await hook2(call, final, runtime, context) ?? final;
|
||||
});
|
||||
if (retained !== void 0 && retained.droppedBytes > 0 && final.output === output) {
|
||||
harness2.push(truncated(retained, reported.limits.retain));
|
||||
@@ -39085,8 +39085,8 @@ async function finalResult(runtime, input, call, tool, result, reported, context
|
||||
}
|
||||
async function afterHarnessResult(runtime, input, call, tool, result, truncation, context) {
|
||||
let final = result;
|
||||
await runtime.hooks.each("afterTool", async (hook) => {
|
||||
final = await hook(call, final, runtime, context) ?? final;
|
||||
await runtime.hooks.each("afterTool", async (hook2) => {
|
||||
final = await hook2(call, final, runtime, context) ?? final;
|
||||
});
|
||||
const harness2 = [];
|
||||
if (truncation !== void 0 && final.output === result.output)
|
||||
@@ -39406,9 +39406,9 @@ var GenerationTask = defineTask({
|
||||
runtime.report(error, { kind: "host", callback: "env" });
|
||||
}
|
||||
const input = { conversationId, agent, env, shown: Object.fromEntries(shown), read: runtime };
|
||||
const desired = await renderSections(agent.sections, input, shown, (error, section) => runtime.report(error, {
|
||||
const desired = await renderSections(agent.sections, input, shown, (error, section2) => runtime.report(error, {
|
||||
kind: "extension",
|
||||
...optionalExtension(sectionExtension(agent, section.key))
|
||||
...optionalExtension(sectionExtension(agent, section2.key))
|
||||
}), context);
|
||||
const entries2 = planSystemEntries(view2, desired, agent.tools, runtime.now());
|
||||
const threshold = compacted === void 0 ? thresholdCompaction(view2, entries2, resolved.contextWindow, compactionPolicy(settings, model3)) : void 0;
|
||||
@@ -39456,8 +39456,8 @@ var GenerationTask = defineTask({
|
||||
return failNoModel(runtime, ref, context);
|
||||
const view2 = await runtime.context(conversationId, context, { at: cutoff });
|
||||
let messages = view2.messages;
|
||||
await runtime.hooks.each("beforeRequest", async (hook) => {
|
||||
const replaced = await hook({ messages }, runtime, context);
|
||||
await runtime.hooks.each("beforeRequest", async (hook2) => {
|
||||
const replaced = await hook2({ messages }, runtime, context);
|
||||
if (replaced !== void 0)
|
||||
messages = replaced.messages;
|
||||
});
|
||||
@@ -39545,8 +39545,8 @@ var GenerationTask = defineTask({
|
||||
if (cutOffTooOften(task, runtime))
|
||||
return endAnsweredCutOff(runtime, task, run2.answer, context);
|
||||
const prepared = [];
|
||||
await runtime.hooks.each("afterRun", async (hook) => {
|
||||
const asked = await hook(run2, runtime, context);
|
||||
await runtime.hooks.each("afterRun", async (hook2) => {
|
||||
const asked = await hook2(run2, runtime, context);
|
||||
if (asked !== void 0)
|
||||
prepareRunEndTasks(runtime, asked.tasks, prepared);
|
||||
});
|
||||
@@ -39754,8 +39754,8 @@ async function convertPartial(tx, live, conversationId) {
|
||||
}
|
||||
async function providerPayload(runtime, model3, payload, context) {
|
||||
let current = payload;
|
||||
await runtime.hooks.each("beforeProviderRequest", async (hook) => {
|
||||
const replaced = await hook({ model: model3, payload: current }, runtime, context);
|
||||
await runtime.hooks.each("beforeProviderRequest", async (hook2) => {
|
||||
const replaced = await hook2({ model: model3, payload: current }, runtime, context);
|
||||
if (replaced !== void 0)
|
||||
current = replaced.payload;
|
||||
});
|
||||
@@ -39839,7 +39839,7 @@ async function classify2(runtime, request2, message, context) {
|
||||
}, context);
|
||||
return;
|
||||
}
|
||||
await runtime.hooks.each("afterResponse", (hook) => hook(message, runtime, context));
|
||||
await runtime.hooks.each("afterResponse", (hook2) => hook2(message, runtime, context));
|
||||
const calls = message.content.filter((content) => content.type === "toolCall");
|
||||
if (message.stopReason === "toolUse" && calls.length > 0) {
|
||||
return startToolRound(runtime, request2, message, calls, context);
|
||||
@@ -39892,10 +39892,10 @@ async function classify2(runtime, request2, message, context) {
|
||||
}
|
||||
async function answer(runtime, message, context) {
|
||||
let continuation;
|
||||
await runtime.hooks.each("onYield", async (hook) => {
|
||||
await runtime.hooks.each("onYield", async (hook2) => {
|
||||
if (continuation !== void 0)
|
||||
return;
|
||||
continuation = (await hook(message, runtime, context))?.continue;
|
||||
continuation = (await hook2(message, runtime, context))?.continue;
|
||||
});
|
||||
const afterRun = await asksAfterRun(runtime, context);
|
||||
const conversationId = runtime.conversationId;
|
||||
@@ -40038,7 +40038,7 @@ async function finishToolRound(runtime, assistant, tools, context) {
|
||||
});
|
||||
const slots = (await runtime.snapshot(LiveDoc, conversationId, context))?.tools ?? [];
|
||||
const results = slots.flatMap((slot) => slot.entry === void 0 ? [] : [slot.entry]);
|
||||
await runtime.hooks.each("afterTools", (hook) => hook(assistant, results, runtime, context));
|
||||
await runtime.hooks.each("afterTools", (hook2) => hook2(assistant, results, runtime, context));
|
||||
const ordered = slots.map((slot) => slot.taskId === void 0 ? void 0 : controls.get(slot.taskId));
|
||||
const terminate = slots.length > 0 && ordered.every((control) => control?.terminate === true);
|
||||
const added = ordered.flatMap((control) => control?.addTools ?? []);
|
||||
@@ -40583,9 +40583,9 @@ var CompactionTask = defineTask({
|
||||
...instructions === void 0 ? {} : { instructions }
|
||||
};
|
||||
let decision;
|
||||
await runtime.hooks.each("beforeCompact", async (hook) => {
|
||||
await runtime.hooks.each("beforeCompact", async (hook2) => {
|
||||
if (decision === void 0)
|
||||
decision = await hook(compaction, runtime, context);
|
||||
decision = await hook2(compaction, runtime, context);
|
||||
});
|
||||
if (decision !== void 0 && "decline" in decision)
|
||||
return complete(runtime, context);
|
||||
@@ -40919,7 +40919,7 @@ var RegistryState = class {
|
||||
return this.#extensions.flatMap((extension) => (extension.tools ?? []).map((tool) => ({ extension, tool })));
|
||||
}
|
||||
sections() {
|
||||
return this.#extensions.flatMap((extension) => (extension.sections ?? []).map((section) => ({ extension, section })));
|
||||
return this.#extensions.flatMap((extension) => (extension.sections ?? []).map((section2) => ({ extension, section: section2 })));
|
||||
}
|
||||
tasks() {
|
||||
return [...this.#tasks.values()];
|
||||
@@ -44424,10 +44424,67 @@ function environment(cwd = "/", conversation) {
|
||||
return env;
|
||||
}
|
||||
|
||||
// main.ts
|
||||
if (!process.env.PI_PROXY_BASE_URL || !process.env.PI_PROXY_API_KEY) {
|
||||
throw new Error("PI_PROXY_BASE_URL and PI_PROXY_API_KEY are required");
|
||||
// credentials.ts
|
||||
var Requests = defineDoc({
|
||||
kind: "sausage.credential.requests",
|
||||
version: 1,
|
||||
scope: "conversation",
|
||||
history: "latest",
|
||||
fork: "initial",
|
||||
initial: () => ({ ids: [] }),
|
||||
checkpointWhen: () => false
|
||||
});
|
||||
var Wait = defineTask({
|
||||
name: "sausage.credential.wait",
|
||||
version: 1,
|
||||
initial: () => ({ phase: "wait" }),
|
||||
phases: {
|
||||
wait: async (task, runtime, context) => {
|
||||
const request2 = Deno.core.ops.op_credential_requests().find((r) => r.id === task.input.id);
|
||||
if (!request2 || request2.state === "pending") {
|
||||
await runtime.sleepUntil(Date.now() + 1e3, { phase: "wait" }, context);
|
||||
return;
|
||||
}
|
||||
const conversation = await runtime.conversation(runtime.conversationId, context);
|
||||
await runtime.commit(async (tx) => {
|
||||
await conversation.submit(tx, { type: "input", content: request2.message, requestId: `credential:${request2.id}` });
|
||||
return { status: "terminal", outcome: { status: "completed", result: null } };
|
||||
}, context);
|
||||
}
|
||||
},
|
||||
abort: (_task, runtime, context) => runtime.commit(() => ({ status: "terminal", outcome: { status: "aborted" } }), context)
|
||||
});
|
||||
var CredentialRequests = defineExtension({ name: "sausage.credentials", tasks: [Wait] });
|
||||
async function discoverRequests(harness2, context) {
|
||||
for (const request2 of Deno.core.ops.op_credential_requests()) {
|
||||
const conversation = await harness2.conversation(Number(request2.conversation), context);
|
||||
if (!conversation) continue;
|
||||
await conversation.commit(async (tx) => {
|
||||
const recorded = await tx.doc(Requests, conversation.id);
|
||||
if (recorded.ids.includes(request2.id)) return;
|
||||
await tx.createTask(Wait, { id: request2.id }, { ownership: { kind: "conversation" }, background: true });
|
||||
recorded.ids.push(request2.id);
|
||||
}, context);
|
||||
}
|
||||
}
|
||||
async function startRequests(harness2, context) {
|
||||
await discoverRequests(harness2, context);
|
||||
let active = true;
|
||||
const pending = (async () => {
|
||||
while (active) {
|
||||
await Deno.core.ops.op_credential_next();
|
||||
if (active) await discoverRequests(harness2, context);
|
||||
}
|
||||
})();
|
||||
return async () => {
|
||||
active = false;
|
||||
await pending;
|
||||
};
|
||||
}
|
||||
|
||||
// main.ts
|
||||
var proxy = Deno.core.ops.op_model_proxy();
|
||||
if (!proxy?.base_url || !proxy?.api_key) throw new Error("The llm-proxy credential is required");
|
||||
var models = createModels();
|
||||
var provider2 = openaiProvider();
|
||||
var model2 = {
|
||||
@@ -44435,7 +44492,7 @@ var model2 = {
|
||||
name: "gpt-6.1-sol",
|
||||
provider: "openai",
|
||||
api: "openai-responses",
|
||||
baseUrl: `${process.env.PI_PROXY_BASE_URL}/v1`,
|
||||
baseUrl: `${proxy.base_url}/v1`,
|
||||
reasoning: true,
|
||||
input: ["text"],
|
||||
thinkingLevelMap: { off: null, minimal: null },
|
||||
@@ -44449,21 +44506,22 @@ models.setProvider({
|
||||
getModels: () => [model2],
|
||||
auth: { apiKey: { name: "Sausage proxy", resolve: async () => ({
|
||||
auth: {
|
||||
apiKey: process.env.PI_PROXY_API_KEY,
|
||||
apiKey: proxy.api_key,
|
||||
baseUrl: model2.baseUrl,
|
||||
headers: { "Shopify-Usage-Tag": JSON.stringify(["pi", "sausage"]) }
|
||||
},
|
||||
source: "PI_PROXY_API_KEY"
|
||||
source: "llm-proxy"
|
||||
}) } }
|
||||
});
|
||||
var registry = createRegistry();
|
||||
registry.install(CodingTools);
|
||||
registry.install(CredentialRequests);
|
||||
var harness = await Harness.open(await openStorage(globalThis.storagePath), {
|
||||
models,
|
||||
registry,
|
||||
env: (target) => environment(target.cwd ?? "/", target.conversationId)
|
||||
}, BACKGROUND_CONTEXT);
|
||||
var root = await harness.root(BACKGROUND_CONTEXT, { agent: { model: { provider: "openai", modelId: model2.id }, thinkingLevel: "low", cwd: "/", extensions: [CodingTools] } });
|
||||
var root = await harness.root(BACKGROUND_CONTEXT, { agent: { model: { provider: "openai", modelId: model2.id }, thinkingLevel: "low", cwd: "/", extensions: [CodingTools, CredentialRequests] } });
|
||||
var watch = await root.watch(BACKGROUND_CONTEXT);
|
||||
var emitted = /* @__PURE__ */ new Map();
|
||||
var partial = "";
|
||||
@@ -44488,6 +44546,7 @@ for (const entry2 of watch.value.entries) {
|
||||
watch.start((event) => {
|
||||
if (event.kind === "value") render(event.value);
|
||||
});
|
||||
var stopRequests = await startRequests(harness, BACKGROUND_CONTEXT);
|
||||
harness.resume();
|
||||
await harness.waitForIdle(BACKGROUND_CONTEXT);
|
||||
while (true) {
|
||||
@@ -44503,5 +44562,6 @@ while (true) {
|
||||
Deno.core.ops.op_output("\n");
|
||||
if (result.status !== "done") throw new Error(`Run ended: ${JSON.stringify(result)}`);
|
||||
}
|
||||
await stopRequests();
|
||||
await watch.stop();
|
||||
await harness.close(BACKGROUND_CONTEXT);
|
||||
|
||||
Reference in New Issue
Block a user