@fungi.computer/truffle 0.1.1 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +282 -34
- package/dist/auth-cli.d.ts +10 -0
- package/dist/auth-cli.js +36 -14
- package/dist/fungi-account-command.d.ts +1 -1
- package/dist/fungi-account-command.js +6 -3
- package/dist/index.d.ts +10 -2
- package/dist/index.js +8 -2
- package/dist/internal/image-fit-metafile.json +106 -0
- package/dist/internal/local-image-fit-worker.mjs +960 -0
- package/dist/internal/private-workerd-sandbox-worker.mjs +11 -12
- package/dist/internal/sandbox-metafile.json +7 -7
- package/dist/local-archive-handoff.d.ts +12 -0
- package/dist/local-archive-handoff.js +21 -0
- package/dist/local-extensions.d.ts +58 -12
- package/dist/local-extensions.js +195 -55
- package/dist/local-image-fit.d.ts +30 -0
- package/dist/local-image-fit.js +105 -0
- package/dist/local-models.d.ts +4 -2
- package/dist/local-models.js +11 -6
- package/dist/local-session-archives.d.ts +8 -0
- package/dist/local-session-archives.js +152 -0
- package/dist/local-skill-source.d.ts +18 -3
- package/dist/local-skill-source.js +216 -140
- package/dist/log-event.d.ts +7 -0
- package/dist/log-event.js +8 -4
- package/dist/private-bounded-response.d.ts +2 -0
- package/dist/private-bounded-response.js +41 -0
- package/dist/private-file-credential-store.d.ts +28 -3
- package/dist/private-file-credential-store.js +57 -9
- package/dist/private-files-patch.d.ts +2 -1
- package/dist/private-files-patch.js +21 -3
- package/dist/private-fungi-account.d.ts +40 -23
- package/dist/private-fungi-account.js +201 -21
- package/dist/private-fungi-mcp-connection.d.ts +4 -0
- package/dist/private-fungi-mcp-connection.js +25 -6
- package/dist/private-fungi-mcp-selection-schema.d.ts +7 -0
- package/dist/private-fungi-mcp-selection-schema.js +15 -0
- package/dist/private-fungi-mcp-selection.d.ts +6 -6
- package/dist/private-fungi-mcp-selection.js +29 -16
- package/dist/private-fungi-model-api.js +4 -4
- package/dist/private-fungi-model-catalog.d.ts +10 -0
- package/dist/private-fungi-model-catalog.js +10 -2
- package/dist/private-fungi-model-provider.d.ts +1 -1
- package/dist/private-fungi-model-provider.js +6 -1
- package/dist/private-fungi-pending-logouts.d.ts +1 -1
- package/dist/private-fungi-pending-logouts.js +1 -2
- package/dist/private-fungi-service-logout.d.ts +44 -0
- package/dist/private-fungi-service-logout.js +59 -0
- package/dist/private-invocation-computer.d.ts +24 -3
- package/dist/private-invocation-computer.js +101 -62
- package/dist/private-invocation-files.d.ts +16 -1
- package/dist/private-invocation-files.js +154 -83
- package/dist/private-local-acp-stdio.js +3 -0
- package/dist/private-local-admission-gate.d.ts +14 -0
- package/dist/private-local-admission-gate.js +52 -0
- package/dist/private-local-agent-control.d.ts +101 -0
- package/dist/private-local-agent-control.js +30 -0
- package/dist/private-local-agent-default-model.d.ts +12 -0
- package/dist/private-local-agent-default-model.js +31 -0
- package/dist/private-local-auth-rpc.d.ts +67 -7
- package/dist/private-local-auth-rpc.js +140 -17
- package/dist/private-local-bwrap.d.ts +13 -0
- package/dist/private-local-bwrap.js +70 -0
- package/dist/private-local-client-version.d.ts +5 -0
- package/dist/private-local-client-version.js +43 -0
- package/dist/private-local-computer-attach.d.ts +3 -3
- package/dist/private-local-computer-attach.js +17 -3
- package/dist/private-local-computer-processes.d.ts +6 -3
- package/dist/private-local-computer-processes.js +55 -31
- package/dist/private-local-config-root.d.ts +21 -0
- package/dist/private-local-config-root.js +19 -0
- package/dist/private-local-containment.d.ts +51 -0
- package/dist/private-local-containment.js +161 -0
- package/dist/private-local-delegation.d.ts +10 -3
- package/dist/private-local-delegation.js +70 -15
- package/dist/private-local-effective-model.d.ts +9 -1
- package/dist/private-local-effective-model.js +3 -0
- package/dist/private-local-exec-environment.d.ts +16 -0
- package/dist/private-local-exec-environment.js +11 -0
- package/dist/private-local-linux-sandbox.d.ts +8 -0
- package/dist/private-local-linux-sandbox.js +67 -0
- package/dist/private-local-mail.d.ts +4 -2
- package/dist/private-local-mail.js +41 -33
- package/dist/private-local-mesh-enrollment.d.ts +34 -0
- package/dist/private-local-mesh-enrollment.js +188 -0
- package/dist/private-local-mesh-links.d.ts +21 -0
- package/dist/private-local-mesh-links.js +229 -0
- package/dist/private-local-mesh-record.d.ts +120 -0
- package/dist/private-local-mesh-record.js +55 -0
- package/dist/private-local-process-identity.d.ts +20 -0
- package/dist/private-local-process-identity.js +40 -0
- package/dist/private-local-runtime-host.d.ts +64 -13
- package/dist/private-local-runtime-host.js +361 -172
- package/dist/private-local-sandbox-policy.d.ts +21 -0
- package/dist/private-local-sandbox-policy.js +121 -0
- package/dist/private-local-sandbox.d.ts +37 -0
- package/dist/private-local-sandbox.js +114 -0
- package/dist/private-local-seatbelt.d.ts +24 -0
- package/dist/private-local-seatbelt.js +68 -0
- package/dist/private-local-service-contract.d.ts +91 -11
- package/dist/private-local-service-discovery.d.ts +129 -13
- package/dist/private-local-service-discovery.js +148 -47
- package/dist/private-local-service-lease.d.ts +16 -0
- package/dist/private-local-service-lease.js +105 -0
- package/dist/private-local-service-listener.d.ts +9 -2
- package/dist/private-local-service-listener.js +260 -64
- package/dist/private-local-service-log.d.ts +128 -0
- package/dist/private-local-service-log.js +321 -0
- package/dist/private-local-service-owner.d.ts +87 -1519
- package/dist/private-local-service-owner.js +1069 -71
- package/dist/private-local-service-process.js +3 -1
- package/dist/private-local-service-termination.d.ts +7 -0
- package/dist/private-local-service-termination.js +67 -0
- package/dist/private-local-service.d.ts +31 -2
- package/dist/private-local-service.js +402 -126
- package/dist/private-local-session-discovery.d.ts +52 -5
- package/dist/private-local-session-discovery.js +132 -11
- package/dist/private-local-shiitake-host.js +2 -2
- package/dist/private-local-startup-model.d.ts +51 -0
- package/dist/private-local-startup-model.js +86 -0
- package/dist/private-local-stop-all.d.ts +27 -0
- package/dist/private-local-stop-all.js +26 -0
- package/dist/private-local-ui-attachment.d.ts +19 -0
- package/dist/private-local-ui-attachment.js +71 -0
- package/dist/private-local-worker-server.d.ts +1 -0
- package/dist/private-local-worker-server.js +13 -5
- package/dist/private-mcp-form-contract.d.ts +186 -0
- package/dist/private-mcp-form-contract.js +53 -0
- package/dist/private-mcp-form-elicitation.d.ts +18 -0
- package/dist/private-mcp-form-elicitation.js +97 -0
- package/dist/private-mcp-form-session.d.ts +172 -0
- package/dist/private-mcp-form-session.js +148 -0
- package/dist/private-openai-claims.d.ts +30 -0
- package/dist/private-openai-claims.js +69 -0
- package/dist/private-oyster-mcp-auth.d.ts +13 -0
- package/dist/private-oyster-mcp-auth.js +57 -0
- package/dist/private-provider-rejection.d.ts +15 -0
- package/dist/private-provider-rejection.js +24 -0
- package/dist/private-sqlite-owner.d.ts +34 -2
- package/dist/private-sqlite-owner.js +103 -37
- package/dist/private-team-computers.d.ts +4 -4
- package/dist/private-team-computers.js +11 -2
- package/dist/seatbelt/safe-base.sbpl +79 -0
- package/dist/subscription-activation.d.ts +9 -0
- package/dist/subscription-activation.js +62 -0
- package/dist/subscription-identity.d.ts +23 -0
- package/dist/subscription-identity.js +124 -0
- package/dist/subscription-turn.d.ts +17 -0
- package/dist/subscription-turn.js +143 -0
- package/dist/team-apps.d.ts +2 -2
- package/dist/team-apps.js +7 -2
- package/native/bin/LICENSE.bubblewrap +502 -0
- package/native/bin/LICENSE.libcap +112 -0
- package/native/bin/LICENSE.sandbox-runtime +201 -0
- package/native/bin/SOURCE.bubblewrap.tar.xz +0 -0
- package/native/bin/bwrap-build-source/Dockerfile +48 -0
- package/native/bin/bwrap-build-source/README.md +96 -0
- package/native/bin/bwrap-build-source/build.mjs +52 -0
- package/native/bin/bwrap-build-source/linux-arm64.ini +14 -0
- package/native/bin/bwrap-build-source/pin.mjs +21 -0
- package/native/bin/bwrap-build-source/record.mjs +74 -0
- package/native/bin/bwrap-build-source/vendor/LICENSE.sandbox-runtime +201 -0
- package/native/bin/bwrap-build-source/vendor/README.md +23 -0
- package/native/bin/bwrap-build-source/vendor/seccomp-unix-block.c +280 -0
- package/native/bin/bwrap-build.json +44 -0
- package/native/bin/fungi-bwrap +0 -0
- package/native/bin/fungi-bwrap.sha256 +1 -0
- package/native/bin/fungi-seccomp.bpf +0 -0
- package/native/bin/fungi-seccomp.bpf.sha256 +1 -0
- package/package.json +44 -31
- package/dist/private-local-model-settings.d.ts +0 -5
- package/dist/private-local-model-settings.js +0 -31
- package/dist/private-local-subagents.d.ts +0 -27
- package/dist/private-local-subagents.js +0 -77
|
@@ -86,12 +86,14 @@ function localDirectory(manager, databasePath, invocationRoot) {
|
|
|
86
86
|
const selected = await manager.connect({
|
|
87
87
|
invocationRoot: input.cwd,
|
|
88
88
|
databasePath,
|
|
89
|
+
uiAttachmentLifetime: "connection",
|
|
89
90
|
}, { signal: input.signal });
|
|
90
91
|
try {
|
|
91
92
|
const created = await manager.createSession({
|
|
92
93
|
invocationRoot: input.cwd,
|
|
93
94
|
databasePath: selected.databasePath,
|
|
94
95
|
requestId: createPrivateLocalSessionRequestId(),
|
|
96
|
+
uiAttachmentLifetime: "connection",
|
|
95
97
|
}, { signal: input.signal });
|
|
96
98
|
return binding(created);
|
|
97
99
|
}
|
|
@@ -105,6 +107,7 @@ function localDirectory(manager, databasePath, invocationRoot) {
|
|
|
105
107
|
invocationRoot: input.cwd,
|
|
106
108
|
databasePath,
|
|
107
109
|
sessionId: input.sessionId,
|
|
110
|
+
uiAttachmentLifetime: "connection",
|
|
108
111
|
}, { signal: input.signal });
|
|
109
112
|
return binding(connected);
|
|
110
113
|
},
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Admissions of work to one Agent share it as short leases; an archive takes the
|
|
3
|
+
* Agent exclusively. So an archive sees every command admitted before it, and
|
|
4
|
+
* work offered after it finds the Agent archived: nothing runs in between.
|
|
5
|
+
*/
|
|
6
|
+
export declare class AdmissionGate {
|
|
7
|
+
#private;
|
|
8
|
+
/** Nothing holds or waits for this gate, so it can be forgotten. */
|
|
9
|
+
get idle(): boolean;
|
|
10
|
+
/** Hold a shared lease; the returned release may be called more than once. */
|
|
11
|
+
lease(): Promise<() => void>;
|
|
12
|
+
/** Run alone: after every held lease is released and before any new one. */
|
|
13
|
+
exclusive<Value>(run: () => Promise<Value>): Promise<Value>;
|
|
14
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Admissions of work to one Agent share it as short leases; an archive takes the
|
|
3
|
+
* Agent exclusively. So an archive sees every command admitted before it, and
|
|
4
|
+
* work offered after it finds the Agent archived: nothing runs in between.
|
|
5
|
+
*/
|
|
6
|
+
export class AdmissionGate {
|
|
7
|
+
#leases = 0;
|
|
8
|
+
#exclusive = false;
|
|
9
|
+
#waiters = [];
|
|
10
|
+
#changed() {
|
|
11
|
+
for (const wake of this.#waiters.splice(0))
|
|
12
|
+
wake();
|
|
13
|
+
}
|
|
14
|
+
#change() {
|
|
15
|
+
return new Promise((resolve) => {
|
|
16
|
+
this.#waiters.push(resolve);
|
|
17
|
+
});
|
|
18
|
+
}
|
|
19
|
+
/** Nothing holds or waits for this gate, so it can be forgotten. */
|
|
20
|
+
get idle() {
|
|
21
|
+
return this.#leases === 0 && !this.#exclusive && this.#waiters.length === 0;
|
|
22
|
+
}
|
|
23
|
+
/** Hold a shared lease; the returned release may be called more than once. */
|
|
24
|
+
async lease() {
|
|
25
|
+
while (this.#exclusive)
|
|
26
|
+
await this.#change();
|
|
27
|
+
this.#leases += 1;
|
|
28
|
+
let held = true;
|
|
29
|
+
return () => {
|
|
30
|
+
if (!held)
|
|
31
|
+
return;
|
|
32
|
+
held = false;
|
|
33
|
+
this.#leases -= 1;
|
|
34
|
+
this.#changed();
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
/** Run alone: after every held lease is released and before any new one. */
|
|
38
|
+
async exclusive(run) {
|
|
39
|
+
while (this.#exclusive)
|
|
40
|
+
await this.#change();
|
|
41
|
+
this.#exclusive = true;
|
|
42
|
+
try {
|
|
43
|
+
while (this.#leases > 0)
|
|
44
|
+
await this.#change();
|
|
45
|
+
return await run();
|
|
46
|
+
}
|
|
47
|
+
finally {
|
|
48
|
+
this.#exclusive = false;
|
|
49
|
+
this.#changed();
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import { type AgentControlAgent, type AgentControlDirectory } from "@fungi.computer/shiitake/agent-control";
|
|
2
|
+
import { createConversationAgents } from "@fungi.computer/shiitake-extensions/roster/conversation-agents";
|
|
3
|
+
/** One Agent creating an Agent, as the project's Agent directory receives it. */
|
|
4
|
+
export type LocalCreateRequest = Parameters<NonNullable<AgentControlDirectory["create"]>>[0] & Readonly<{
|
|
5
|
+
/** The Agent creating, resolved by the creator's own host, never by the caller. */
|
|
6
|
+
createdBy: string;
|
|
7
|
+
}>;
|
|
8
|
+
/**
|
|
9
|
+
* One ordinary Agent as the project's directory lists it. `unreadable` is
|
|
10
|
+
* set when this Agent's own store cannot be read (for
|
|
11
|
+
* example one written before the current schema); only that row is affected.
|
|
12
|
+
*/
|
|
13
|
+
export type LocalAgentControlAgent = AgentControlAgent & Readonly<{
|
|
14
|
+
unreadable?: string;
|
|
15
|
+
}>;
|
|
16
|
+
/**
|
|
17
|
+
* Who archives or restores an Agent: an Agent, named by its own host and never
|
|
18
|
+
* by the caller, or a person at the CLI or TUI. A person may archive any
|
|
19
|
+
* Agent; an Agent only one it created.
|
|
20
|
+
*/
|
|
21
|
+
export type LocalArchiver = Readonly<{
|
|
22
|
+
kind: "agent";
|
|
23
|
+
agentId: string;
|
|
24
|
+
}> | Readonly<{
|
|
25
|
+
kind: "human";
|
|
26
|
+
}>;
|
|
27
|
+
/** One archive or restore request, as the project's Agent directory receives it. */
|
|
28
|
+
export type LocalArchiveRequest = Readonly<{
|
|
29
|
+
agentId: string;
|
|
30
|
+
by: LocalArchiver;
|
|
31
|
+
}>;
|
|
32
|
+
/**
|
|
33
|
+
* The project's Agent directory a Local host receives. Its create names the
|
|
34
|
+
* creating Agent, so the directory owner can hold the created Agent to what
|
|
35
|
+
* the creator holds; its archive names who archives.
|
|
36
|
+
*/
|
|
37
|
+
export type LocalAgentControlDirectory = Omit<AgentControlDirectory, "create" | "list" | "archive" | "unarchive"> & Readonly<{
|
|
38
|
+
list: (signal: AbortSignal, options?: Readonly<{
|
|
39
|
+
archived?: boolean;
|
|
40
|
+
}>) => Promise<readonly LocalAgentControlAgent[]>;
|
|
41
|
+
create: (input: LocalCreateRequest, signal: AbortSignal) => Promise<AgentControlAgent>;
|
|
42
|
+
archive: (input: LocalArchiveRequest, signal: AbortSignal) => Promise<void>;
|
|
43
|
+
unarchive: (input: LocalArchiveRequest, signal: AbortSignal) => Promise<void>;
|
|
44
|
+
}>;
|
|
45
|
+
/**
|
|
46
|
+
* Ordinary Agent controls bound to this authenticated creating Agent; each
|
|
47
|
+
* conversation's accepted creates and sends are recorded in this Agent's own
|
|
48
|
+
* store for its footer.
|
|
49
|
+
*/
|
|
50
|
+
export declare function createPrivateLocalAgentControl(input: Readonly<{
|
|
51
|
+
agentId: string;
|
|
52
|
+
directory: LocalAgentControlDirectory;
|
|
53
|
+
storage: Parameters<typeof createConversationAgents>[0];
|
|
54
|
+
}>): {
|
|
55
|
+
control: Readonly<{
|
|
56
|
+
id: string;
|
|
57
|
+
acquire: (context: import("@fungi.computer/mycelium").CapabilitySourceContext) => Promise<import("@fungi.computer/mycelium").CapabilitySourceAcquisition>;
|
|
58
|
+
}>;
|
|
59
|
+
source: Readonly<{
|
|
60
|
+
read: (sessionId: string, limit: number) => Promise<Readonly<{
|
|
61
|
+
agents: readonly import("@fungi.computer/shiitake-extensions/subagents/host").SubagentRecord[];
|
|
62
|
+
more: number;
|
|
63
|
+
}>>;
|
|
64
|
+
open: (agentId: string, signal: AbortSignal) => Promise<Readonly<{
|
|
65
|
+
agent: import("@fungi.computer/shiitake-extensions/subagents/host").SubagentAgent;
|
|
66
|
+
release: () => Promise<void>;
|
|
67
|
+
}>>;
|
|
68
|
+
}>;
|
|
69
|
+
directory: Readonly<{
|
|
70
|
+
list: (signal: AbortSignal, options?: Readonly<{
|
|
71
|
+
archived?: boolean;
|
|
72
|
+
}>) => Promise<readonly AgentControlAgent[]>;
|
|
73
|
+
send?: (input: import("@fungi.computer/shiitake/agent-control").AgentDelegationInput, sourceSessionId: string, signal: AbortSignal, invocation?: import("@fungi.computer/shiitake/agent-control").AgentDelegationInvocation) => Promise<import("@fungi.computer/shiitake").Accepted>;
|
|
74
|
+
recall?: (input: import("@fungi.computer/shiitake/agent-control").AgentRecallInput, sourceSessionId: string, signal: AbortSignal) => Promise<import("@fungi.computer/shiitake").CancelResult>;
|
|
75
|
+
create?: (input: Readonly<{
|
|
76
|
+
requestId: string;
|
|
77
|
+
name?: string;
|
|
78
|
+
model?: import("@fungi.computer/shiitake").ModelPreference;
|
|
79
|
+
extensions?: readonly string[];
|
|
80
|
+
}>, signal: AbortSignal) => Promise<AgentControlAgent>;
|
|
81
|
+
setModel?: (input: Readonly<{
|
|
82
|
+
agentId: string;
|
|
83
|
+
model: import("@fungi.computer/shiitake").ModelPreference;
|
|
84
|
+
}>, signal: AbortSignal) => Promise<void>;
|
|
85
|
+
archive?: (input: Readonly<{
|
|
86
|
+
agentId: string;
|
|
87
|
+
}>, signal: AbortSignal) => Promise<void>;
|
|
88
|
+
unarchive?: (input: Readonly<{
|
|
89
|
+
agentId: string;
|
|
90
|
+
}>, signal: AbortSignal) => Promise<void>;
|
|
91
|
+
models?: (signal: AbortSignal) => Promise<readonly import("@fungi.computer/shiitake/agent-control").AgentControlModel[]>;
|
|
92
|
+
checkModel?: (input: Readonly<{
|
|
93
|
+
agentId: string;
|
|
94
|
+
sessionId: string;
|
|
95
|
+
}>, signal: AbortSignal) => Promise<void>;
|
|
96
|
+
open: (agentId: string, signal: AbortSignal) => Promise<Readonly<{
|
|
97
|
+
agent: import("@fungi.computer/shiitake").ShiitakeAgent;
|
|
98
|
+
release: () => Promise<void>;
|
|
99
|
+
}>>;
|
|
100
|
+
}>;
|
|
101
|
+
};
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { AgentCreateRefused, } from "@fungi.computer/shiitake/agent-control";
|
|
2
|
+
import { createConversationAgents } from "@fungi.computer/shiitake-extensions/roster/conversation-agents";
|
|
3
|
+
/**
|
|
4
|
+
* Ordinary Agent controls bound to this authenticated creating Agent; each
|
|
5
|
+
* conversation's accepted creates and sends are recorded in this Agent's own
|
|
6
|
+
* store for its footer.
|
|
7
|
+
*/
|
|
8
|
+
export function createPrivateLocalAgentControl(input) {
|
|
9
|
+
const conversations = createConversationAgents(input.storage);
|
|
10
|
+
function bound(parentSessionId) {
|
|
11
|
+
const { create, archive, unarchive } = input.directory;
|
|
12
|
+
const by = { kind: "agent", agentId: input.agentId };
|
|
13
|
+
return {
|
|
14
|
+
...input.directory,
|
|
15
|
+
archive: ({ agentId }, signal) => archive({ agentId, by }, signal),
|
|
16
|
+
unarchive: ({ agentId }, signal) => unarchive({ agentId, by }, signal),
|
|
17
|
+
create: async (request, signal) => {
|
|
18
|
+
if (parentSessionId === undefined)
|
|
19
|
+
throw new AgentCreateRefused("no_creator", "creation requires an invoking Session");
|
|
20
|
+
return create({ ...request, createdBy: input.agentId }, signal);
|
|
21
|
+
},
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
const control = conversations.control(bound);
|
|
25
|
+
return {
|
|
26
|
+
control,
|
|
27
|
+
source: conversations.source(input.directory),
|
|
28
|
+
directory: conversations.recording(undefined, bound(undefined)),
|
|
29
|
+
};
|
|
30
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { PrivateSqliteDatabase } from "./private-sqlite-owner.js";
|
|
2
|
+
import { type ModelPreference } from "@fungi.computer/shiitake/model-preference";
|
|
3
|
+
/** Create the Agent default's table in a store this host owns. */
|
|
4
|
+
export declare function ensureAgentDefaultModelTable(database: PrivateSqliteDatabase): void;
|
|
5
|
+
/** Keep an Agent's first default; later changes go through the writer. */
|
|
6
|
+
export declare function seedAgentDefaultModel(database: PrivateSqliteDatabase, model: ModelPreference | null): void;
|
|
7
|
+
/** The stored default as written (JSON text); undefined when the row is missing. */
|
|
8
|
+
export declare function readAgentDefaultModelJson(database: PrivateSqliteDatabase): string | undefined;
|
|
9
|
+
/** Read the Agent default a store holds; null when none is set. */
|
|
10
|
+
export declare function readAgentDefaultModel(database: PrivateSqliteDatabase): ModelPreference | null;
|
|
11
|
+
/** Replace the Agent default, thinking level included. */
|
|
12
|
+
export declare function writeAgentDefaultModel(database: PrivateSqliteDatabase, model: ModelPreference): void;
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { parseModelPreferenceJson, } from "@fungi.computer/shiitake/model-preference";
|
|
3
|
+
/** The one module that owns the Agent default's table SQL. */
|
|
4
|
+
const settingsTable = "private_local_model_settings";
|
|
5
|
+
const storedModelSchema = z.string().optional();
|
|
6
|
+
/** Create the Agent default's table in a store this host owns. */
|
|
7
|
+
export function ensureAgentDefaultModelTable(database) {
|
|
8
|
+
database.exec(`CREATE TABLE IF NOT EXISTS ${settingsTable} (singleton INTEGER PRIMARY KEY CHECK(singleton = 1), model TEXT NOT NULL) STRICT`);
|
|
9
|
+
}
|
|
10
|
+
/** Keep an Agent's first default; later changes go through the writer. */
|
|
11
|
+
export function seedAgentDefaultModel(database, model) {
|
|
12
|
+
database
|
|
13
|
+
.prepare(`INSERT OR IGNORE INTO ${settingsTable} VALUES (1, ?)`)
|
|
14
|
+
.run(JSON.stringify(model));
|
|
15
|
+
}
|
|
16
|
+
/** The stored default as written (JSON text); undefined when the row is missing. */
|
|
17
|
+
export function readAgentDefaultModelJson(database) {
|
|
18
|
+
return storedModelSchema.parse(database
|
|
19
|
+
.prepare(`SELECT model FROM ${settingsTable} WHERE singleton = 1`)
|
|
20
|
+
.get()?.model);
|
|
21
|
+
}
|
|
22
|
+
/** Read the Agent default a store holds; null when none is set. */
|
|
23
|
+
export function readAgentDefaultModel(database) {
|
|
24
|
+
return parseModelPreferenceJson(readAgentDefaultModelJson(database));
|
|
25
|
+
}
|
|
26
|
+
/** Replace the Agent default, thinking level included. */
|
|
27
|
+
export function writeAgentDefaultModel(database, model) {
|
|
28
|
+
database
|
|
29
|
+
.prepare(`UPDATE ${settingsTable} SET model = ? WHERE singleton = 1`)
|
|
30
|
+
.run(JSON.stringify(model));
|
|
31
|
+
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { AuthInteraction, AuthEvent } from "@earendil-works/pi-ai";
|
|
2
2
|
import { RpcTarget, type RpcStub } from "@fungi.computer/capnweb";
|
|
3
3
|
import { z } from "zod";
|
|
4
|
+
import { type FungiAccountLogoutRequest, type FungiAccountLogoutResult } from "./private-fungi-service-logout.js";
|
|
4
5
|
import { runLocalAuthCli, type LocalAuthResult } from "./auth-cli.js";
|
|
5
6
|
declare const requestSchema: z.ZodObject<{
|
|
6
7
|
command: z.ZodEnum<{
|
|
@@ -10,8 +11,8 @@ declare const requestSchema: z.ZodObject<{
|
|
|
10
11
|
}>;
|
|
11
12
|
provider: z.ZodOptional<z.ZodString>;
|
|
12
13
|
authType: z.ZodOptional<z.ZodEnum<{
|
|
13
|
-
api_key: "api_key";
|
|
14
14
|
oauth: "oauth";
|
|
15
|
+
api_key: "api_key";
|
|
15
16
|
}>>;
|
|
16
17
|
method: z.ZodOptional<z.ZodString>;
|
|
17
18
|
}, z.core.$strip>;
|
|
@@ -38,6 +39,58 @@ declare const promptSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
|
38
39
|
}, z.core.$strip>>;
|
|
39
40
|
}, z.core.$strip>], "type">;
|
|
40
41
|
export type AuthPromptRequest = z.infer<typeof promptSchema>;
|
|
42
|
+
/** What one auth RPC operation returns: a result or a typed failure. */
|
|
43
|
+
export type LocalAuthWireResult = z.output<typeof wireResultSchema>;
|
|
44
|
+
declare const wireResultSchema: z.ZodUnion<readonly [z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
45
|
+
kind: z.ZodLiteral<"completed">;
|
|
46
|
+
}, z.core.$strip>, z.ZodObject<{
|
|
47
|
+
kind: z.ZodLiteral<"status">;
|
|
48
|
+
providers: z.ZodArray<z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
49
|
+
providerId: z.ZodString;
|
|
50
|
+
providerName: z.ZodString;
|
|
51
|
+
state: z.ZodLiteral<"unconfigured">;
|
|
52
|
+
}, z.core.$strip>, z.ZodObject<{
|
|
53
|
+
providerId: z.ZodString;
|
|
54
|
+
providerName: z.ZodString;
|
|
55
|
+
state: z.ZodLiteral<"configured">;
|
|
56
|
+
type: z.ZodEnum<{
|
|
57
|
+
oauth: "oauth";
|
|
58
|
+
api_key: "api_key";
|
|
59
|
+
}>;
|
|
60
|
+
}, z.core.$strip>], "state">>;
|
|
61
|
+
}, z.core.$strip>], "kind">, z.ZodObject<{
|
|
62
|
+
kind: z.ZodLiteral<"failed">;
|
|
63
|
+
failure: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
64
|
+
category: z.ZodLiteral<"identity-unavailable">;
|
|
65
|
+
detail: z.ZodOptional<z.ZodObject<{
|
|
66
|
+
claim: z.ZodEnum<{
|
|
67
|
+
access_token: "access_token";
|
|
68
|
+
"access_token.iss": "access_token.iss";
|
|
69
|
+
"access_token.sub": "access_token.sub";
|
|
70
|
+
}>;
|
|
71
|
+
problem: z.ZodEnum<{
|
|
72
|
+
absent: "absent";
|
|
73
|
+
malformed: "malformed";
|
|
74
|
+
invalid: "invalid";
|
|
75
|
+
}>;
|
|
76
|
+
claimNames: z.ZodArray<z.ZodString>;
|
|
77
|
+
authClaimNames: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
78
|
+
}, z.core.$strict>>;
|
|
79
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
80
|
+
category: z.ZodLiteral<"callback-port-in-use">;
|
|
81
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
82
|
+
category: z.ZodLiteral<"oauth-state-mismatch">;
|
|
83
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
84
|
+
category: z.ZodLiteral<"token-exchange-rejected">;
|
|
85
|
+
status: z.ZodNumber;
|
|
86
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
87
|
+
category: z.ZodLiteral<"cancelled">;
|
|
88
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
89
|
+
category: z.ZodLiteral<"no-key-entered">;
|
|
90
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
91
|
+
category: z.ZodLiteral<"unknown">;
|
|
92
|
+
}, z.core.$strict>], "category">;
|
|
93
|
+
}, z.core.$strict>]>;
|
|
41
94
|
declare class AuthPromptTarget extends RpcTarget {
|
|
42
95
|
#private;
|
|
43
96
|
constructor(prompt: AuthPromptRequest, interaction: AuthInteraction);
|
|
@@ -54,16 +107,23 @@ declare class AuthPresenterTarget extends RpcTarget {
|
|
|
54
107
|
/** The auth-only RPC door shared by its producer and remote client. */
|
|
55
108
|
export type PrivateLocalAuthEndpoint = RpcTarget & {
|
|
56
109
|
cancel(): void;
|
|
57
|
-
|
|
110
|
+
signOutFungi(input: FungiAccountLogoutRequest): Promise<FungiAccountLogoutResult>;
|
|
111
|
+
run(input: PrivateLocalAuthRequest, presenter: RpcStub<AuthPresenterTarget>): Promise<LocalAuthWireResult>;
|
|
58
112
|
};
|
|
59
|
-
/** One transient connection runs one
|
|
113
|
+
/** One transient connection runs one provider or Fungi-account operation. */
|
|
60
114
|
export declare class PrivateLocalAuthTarget extends RpcTarget implements PrivateLocalAuthEndpoint {
|
|
61
115
|
#private;
|
|
62
|
-
constructor(run
|
|
116
|
+
constructor(run: typeof runLocalAuthCli, signOutAccount: (input: FungiAccountLogoutRequest) => Promise<FungiAccountLogoutResult>);
|
|
117
|
+
signOutFungi(input: FungiAccountLogoutRequest): Promise<FungiAccountLogoutResult>;
|
|
63
118
|
cancel(): void;
|
|
64
119
|
[Symbol.dispose](): void;
|
|
65
|
-
run(input: PrivateLocalAuthRequest, presenter: RpcStub<AuthPresenterTarget>): Promise<
|
|
120
|
+
run(input: PrivateLocalAuthRequest, presenter: RpcStub<AuthPresenterTarget>): Promise<LocalAuthWireResult>;
|
|
66
121
|
}
|
|
67
|
-
/**
|
|
68
|
-
|
|
122
|
+
/**
|
|
123
|
+
* Takes ownership of a dedicated auth RPC connection, never the Session client.
|
|
124
|
+
* A cancel the caller asked for is awaited before the connection is released,
|
|
125
|
+
* so an ordinary cancel is never cut off by its own teardown; a cancel that
|
|
126
|
+
* genuinely fails is handed to `reportCleanupFailure` with its cause.
|
|
127
|
+
*/
|
|
128
|
+
export declare function runPrivateLocalAuthRemote(remote: RpcStub<PrivateLocalAuthEndpoint>, request: PrivateLocalAuthRequest, interaction: AuthInteraction, reportCleanupFailure: (cause: unknown) => void): Promise<LocalAuthResult>;
|
|
69
129
|
export type { AuthPresenterTarget, AuthPromptTarget };
|
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
import { RpcTarget } from "@fungi.computer/capnweb";
|
|
2
2
|
import { z } from "zod";
|
|
3
|
-
import {
|
|
3
|
+
import { fungiAccountLogoutRequestSchema, fungiAccountLogoutResultSchema, } from "./private-fungi-service-logout.js";
|
|
4
|
+
import { LocalAuthRefusal, localAuthInputLimit, runLocalAuthCli, } from "./auth-cli.js";
|
|
5
|
+
import { openAIIdentityDetailSchema } from "./private-openai-claims.js";
|
|
6
|
+
import { CredentialActivationError } from "./subscription-activation.js";
|
|
7
|
+
import { OpenAIIdentityUnavailable } from "./subscription-identity.js";
|
|
4
8
|
const requestSchema = z.object({
|
|
5
9
|
command: z.enum(["login", "logout", "status"]),
|
|
6
10
|
provider: z.string().optional(),
|
|
@@ -76,6 +80,103 @@ const eventSchema = z.discriminatedUnion("type", [
|
|
|
76
80
|
}),
|
|
77
81
|
]);
|
|
78
82
|
const answerSchema = z.string().max(localAuthInputLimit);
|
|
83
|
+
/**
|
|
84
|
+
* The closed set of failures that cross the service. Provider errors can
|
|
85
|
+
* contain credential material, so no free text, cause or provider message
|
|
86
|
+
* crosses: only a fixed category, HTTP status, or missing-person claim NAMES.
|
|
87
|
+
*/
|
|
88
|
+
const localAuthFailureSchema = z.discriminatedUnion("category", [
|
|
89
|
+
z.strictObject({
|
|
90
|
+
category: z.literal("identity-unavailable"),
|
|
91
|
+
detail: openAIIdentityDetailSchema.optional(),
|
|
92
|
+
}),
|
|
93
|
+
z.strictObject({ category: z.literal("callback-port-in-use") }),
|
|
94
|
+
z.strictObject({ category: z.literal("oauth-state-mismatch") }),
|
|
95
|
+
z.strictObject({
|
|
96
|
+
category: z.literal("token-exchange-rejected"),
|
|
97
|
+
status: z.number().int().min(100).max(599),
|
|
98
|
+
}),
|
|
99
|
+
z.strictObject({ category: z.literal("cancelled") }),
|
|
100
|
+
z.strictObject({ category: z.literal("no-key-entered") }),
|
|
101
|
+
z.strictObject({ category: z.literal("unknown") }),
|
|
102
|
+
]);
|
|
103
|
+
const wireResultSchema = z.union([
|
|
104
|
+
authResultSchema,
|
|
105
|
+
z.strictObject({
|
|
106
|
+
kind: z.literal("failed"),
|
|
107
|
+
failure: localAuthFailureSchema,
|
|
108
|
+
}),
|
|
109
|
+
]);
|
|
110
|
+
/** Pi 1.3.0's fixed error prefixes; never retain a provider body suffix. */
|
|
111
|
+
const callbackPortInUse = /^Port \d+ is in use\b/u;
|
|
112
|
+
const oauthStateMismatch = "OAuth state mismatch";
|
|
113
|
+
const openAITokenRequestFailed = /^OpenAI OAuth token request failed \(([1-5]\d{2})\)/u;
|
|
114
|
+
function classifyFailure(cause, aborted) {
|
|
115
|
+
if (aborted)
|
|
116
|
+
return { category: "cancelled" };
|
|
117
|
+
if (cause instanceof LocalAuthRefusal)
|
|
118
|
+
return { category: "no-key-entered" };
|
|
119
|
+
if (cause instanceof OpenAIIdentityUnavailable)
|
|
120
|
+
return { category: "identity-unavailable", detail: cause.detail };
|
|
121
|
+
if (cause instanceof CredentialActivationError &&
|
|
122
|
+
cause.category === "identity-unavailable")
|
|
123
|
+
return { category: "identity-unavailable" };
|
|
124
|
+
if (cause instanceof Error) {
|
|
125
|
+
const tokenRequest = openAITokenRequestFailed.exec(cause.message);
|
|
126
|
+
if (tokenRequest !== null)
|
|
127
|
+
return {
|
|
128
|
+
category: "token-exchange-rejected",
|
|
129
|
+
status: Number(tokenRequest[1]),
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
if (cause instanceof Error && callbackPortInUse.test(cause.message))
|
|
133
|
+
return { category: "callback-port-in-use" };
|
|
134
|
+
if (cause instanceof Error && cause.message === oauthStateMismatch)
|
|
135
|
+
return { category: "oauth-state-mismatch" };
|
|
136
|
+
return { category: "unknown" };
|
|
137
|
+
}
|
|
138
|
+
/** Fixed wording per category; the dialog shows exactly this. */
|
|
139
|
+
function failureText(failure) {
|
|
140
|
+
switch (failure.category) {
|
|
141
|
+
case "identity-unavailable": {
|
|
142
|
+
const detail = failure.detail;
|
|
143
|
+
if (detail === undefined)
|
|
144
|
+
return "Sign-in could not establish the account identity (identity-unavailable). Nothing was saved.";
|
|
145
|
+
const auth = detail.authClaimNames === undefined
|
|
146
|
+
? ""
|
|
147
|
+
: "; auth claims: " + (detail.authClaimNames.join(", ") || "none");
|
|
148
|
+
return ("OpenAI sign-in did not identify the person (identity-unavailable: " +
|
|
149
|
+
detail.claim +
|
|
150
|
+
" " +
|
|
151
|
+
detail.problem +
|
|
152
|
+
"; claims: " +
|
|
153
|
+
(detail.claimNames.join(", ") || "none") +
|
|
154
|
+
auth +
|
|
155
|
+
"). Nothing was saved.");
|
|
156
|
+
}
|
|
157
|
+
case "callback-port-in-use":
|
|
158
|
+
return "The sign-in callback port is in use, probably by another sign-in or the Codex CLI. Close it and try again.";
|
|
159
|
+
case "oauth-state-mismatch":
|
|
160
|
+
return "The sign-in redirect did not belong to this attempt. Start the sign-in again.";
|
|
161
|
+
case "token-exchange-rejected":
|
|
162
|
+
return `OpenAI refused the sign-in token exchange (HTTP ${String(failure.status)}).`;
|
|
163
|
+
case "cancelled":
|
|
164
|
+
return "Authentication cancelled";
|
|
165
|
+
case "no-key-entered":
|
|
166
|
+
return new LocalAuthRefusal().message;
|
|
167
|
+
case "unknown":
|
|
168
|
+
return "Local authentication failed";
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
/** The client-side error for one typed failure; its message is fixed text. */
|
|
172
|
+
class LocalAuthFailure extends Error {
|
|
173
|
+
failure;
|
|
174
|
+
constructor(failure) {
|
|
175
|
+
super(failureText(failure));
|
|
176
|
+
this.failure = failure;
|
|
177
|
+
this.name = "LocalAuthFailure";
|
|
178
|
+
}
|
|
179
|
+
}
|
|
79
180
|
class AuthPromptTarget extends RpcTarget {
|
|
80
181
|
#controller = new AbortController();
|
|
81
182
|
#result;
|
|
@@ -167,14 +268,22 @@ class AuthPresenterTarget extends RpcTarget {
|
|
|
167
268
|
this.#interaction.notify(decoded);
|
|
168
269
|
}
|
|
169
270
|
}
|
|
170
|
-
/** One transient connection runs one
|
|
271
|
+
/** One transient connection runs one provider or Fungi-account operation. */
|
|
171
272
|
export class PrivateLocalAuthTarget extends RpcTarget {
|
|
172
273
|
#controller = new AbortController();
|
|
173
274
|
#used = false;
|
|
174
275
|
#run;
|
|
175
|
-
|
|
276
|
+
#signOutAccount;
|
|
277
|
+
constructor(run, signOutAccount) {
|
|
176
278
|
super();
|
|
177
279
|
this.#run = run;
|
|
280
|
+
this.#signOutAccount = signOutAccount;
|
|
281
|
+
}
|
|
282
|
+
async signOutFungi(input) {
|
|
283
|
+
if (this.#used)
|
|
284
|
+
throw new Error("Authentication connection already used");
|
|
285
|
+
this.#used = true;
|
|
286
|
+
return fungiAccountLogoutResultSchema.parse(await this.#signOutAccount(fungiAccountLogoutRequestSchema.parse(input)));
|
|
178
287
|
}
|
|
179
288
|
cancel() {
|
|
180
289
|
this.#controller.abort();
|
|
@@ -235,39 +344,53 @@ export class PrivateLocalAuthTarget extends RpcTarget {
|
|
|
235
344
|
});
|
|
236
345
|
return authResultSchema.parse(result);
|
|
237
346
|
}
|
|
238
|
-
catch {
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
347
|
+
catch (cause) {
|
|
348
|
+
return {
|
|
349
|
+
kind: "failed",
|
|
350
|
+
failure: classifyFailure(cause, signal.aborted),
|
|
351
|
+
};
|
|
243
352
|
}
|
|
244
353
|
finally {
|
|
245
354
|
this.cancel();
|
|
246
355
|
}
|
|
247
356
|
}
|
|
248
357
|
}
|
|
249
|
-
/**
|
|
250
|
-
|
|
358
|
+
/**
|
|
359
|
+
* Takes ownership of a dedicated auth RPC connection, never the Session client.
|
|
360
|
+
* A cancel the caller asked for is awaited before the connection is released,
|
|
361
|
+
* so an ordinary cancel is never cut off by its own teardown; a cancel that
|
|
362
|
+
* genuinely fails is handed to `reportCleanupFailure` with its cause.
|
|
363
|
+
*/
|
|
364
|
+
export async function runPrivateLocalAuthRemote(remote, request, interaction, reportCleanupFailure) {
|
|
251
365
|
const controller = new AbortController();
|
|
252
366
|
const signal = interaction.signal === undefined
|
|
253
367
|
? controller.signal
|
|
254
368
|
: AbortSignal.any([controller.signal, interaction.signal]);
|
|
255
369
|
const presenter = new AuthPresenterTarget({ ...interaction, signal });
|
|
370
|
+
let cancellation;
|
|
256
371
|
const cancel = () => {
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
});
|
|
372
|
+
cancellation = (async () => {
|
|
373
|
+
await remote.cancel();
|
|
374
|
+
})();
|
|
261
375
|
};
|
|
262
376
|
signal.addEventListener("abort", cancel, { once: true });
|
|
263
377
|
try {
|
|
264
378
|
signal.throwIfAborted();
|
|
265
|
-
const result = await remote.run(request, presenter);
|
|
266
|
-
|
|
379
|
+
const result = wireResultSchema.parse(await remote.run(request, presenter));
|
|
380
|
+
if (result.kind === "failed")
|
|
381
|
+
throw new LocalAuthFailure(result.failure);
|
|
382
|
+
return result;
|
|
267
383
|
}
|
|
268
384
|
finally {
|
|
269
385
|
signal.removeEventListener("abort", cancel);
|
|
270
386
|
controller.abort();
|
|
271
|
-
|
|
387
|
+
try {
|
|
388
|
+
const [cancelled] = await Promise.allSettled([cancellation]);
|
|
389
|
+
if (cancelled.status === "rejected")
|
|
390
|
+
reportCleanupFailure(cancelled.reason);
|
|
391
|
+
}
|
|
392
|
+
finally {
|
|
393
|
+
remote[Symbol.dispose]();
|
|
394
|
+
}
|
|
272
395
|
}
|
|
273
396
|
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export declare class LocalBwrapUnavailable extends Error {
|
|
2
|
+
readonly reason: "missing" | "invalid-digest" | "digest-mismatch" | "not-executable";
|
|
3
|
+
readonly name = "LocalBwrapUnavailable";
|
|
4
|
+
constructor(reason: "missing" | "invalid-digest" | "digest-mismatch" | "not-executable", message: string, options?: ErrorOptions);
|
|
5
|
+
}
|
|
6
|
+
export type LinuxSandboxPayload = {
|
|
7
|
+
readonly executable: string;
|
|
8
|
+
readonly seccompFilter: Uint8Array;
|
|
9
|
+
};
|
|
10
|
+
/** Check both packaged artifacts on every use; never cache verification. */
|
|
11
|
+
export declare function verifyLocalLinuxSandboxPayload(executable: string): LinuxSandboxPayload;
|
|
12
|
+
/** Same optional native-package lookup as apply_patch, with no PATH fallback. */
|
|
13
|
+
export declare function localLinuxSandboxPayload(): LinuxSandboxPayload;
|