orchajs 0.20.3 → 0.22.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 +103 -0
- package/dist/amarsia/analytics.d.ts +21 -0
- package/dist/amarsia/analytics.js +457 -0
- package/dist/amarsia/endpoint.d.ts +1 -0
- package/dist/amarsia/endpoint.js +8 -0
- package/dist/amarsia/sync.js +1 -4
- package/dist/bin.d.ts +2 -0
- package/dist/bin.js +13 -0
- package/dist/browser.d.ts +2 -0
- package/dist/browser.js +12 -0
- package/dist/cli.js +35 -9
- package/dist/compiler/build-project.d.ts +1 -0
- package/dist/compiler/build-project.js +75 -8
- package/dist/compiler/compile.d.ts +1 -1
- package/dist/compiler/compile.js +11 -9
- package/dist/errors.d.ts +5 -0
- package/dist/errors.js +102 -0
- package/dist/file-content.js +42 -6
- package/dist/index.d.ts +1 -5
- package/dist/index.js +1 -2
- package/dist/integrations/esbuild.js +5 -2
- package/dist/integrations/expo.d.ts +1 -0
- package/dist/integrations/expo.js +1 -0
- package/dist/integrations/metro.d.ts +15 -0
- package/dist/integrations/metro.js +25 -0
- package/dist/integrations/next.d.ts +23 -0
- package/dist/integrations/next.js +53 -0
- package/dist/integrations/runtime-build.d.ts +4 -0
- package/dist/integrations/runtime-build.js +32 -0
- package/dist/integrations/vite.d.ts +23 -0
- package/dist/integrations/vite.js +32 -0
- package/dist/integrations/webpack.d.ts +24 -0
- package/dist/integrations/webpack.js +21 -0
- package/dist/orcha.js +17 -8
- package/dist/playground/server.js +57 -7
- package/dist/project-loader.js +3 -0
- package/dist/providers/browser.d.ts +2 -0
- package/dist/providers/browser.js +20 -0
- package/dist/react-native.d.ts +2 -0
- package/dist/react-native.js +12 -0
- package/dist/runtime/actions/browser-native.d.ts +3 -0
- package/dist/runtime/actions/browser-native.js +28 -0
- package/dist/runtime/actions/execute.d.ts +7 -0
- package/dist/runtime/actions/execute.js +9 -2
- package/dist/runtime/agent.d.ts +1 -0
- package/dist/runtime/agent.js +38 -10
- package/dist/runtime/compiler-hook.d.ts +5 -0
- package/dist/runtime/compiler-hook.js +8 -0
- package/dist/runtime/create-orcha.d.ts +6 -1
- package/dist/runtime/create-orcha.js +35 -21
- package/dist/runtime/platform-browser.d.ts +2 -0
- package/dist/runtime/platform-browser.js +15 -0
- package/dist/runtime/platform-node.d.ts +2 -0
- package/dist/runtime/platform-node.js +31 -0
- package/dist/runtime/platform-react-native.d.ts +2 -0
- package/dist/runtime/platform-react-native.js +15 -0
- package/dist/runtime/platform.d.ts +15 -0
- package/dist/runtime/platform.js +1 -0
- package/dist/runtime/random-id.d.ts +1 -0
- package/dist/runtime/random-id.js +26 -0
- package/dist/runtime-entry.d.ts +5 -0
- package/dist/runtime-entry.js +2 -0
- package/dist/storage/browser-indexeddb.d.ts +7 -16
- package/dist/storage/browser-indexeddb.js +102 -19
- package/dist/storage/react-native-jsonl.d.ts +7 -16
- package/dist/storage/react-native-jsonl.js +65 -19
- package/dist/types.d.ts +54 -1
- package/dist-cjs/amarsia/analytics.js +447 -0
- package/dist-cjs/amarsia/endpoint.js +32 -0
- package/dist-cjs/amarsia/sync.js +2 -5
- package/dist-cjs/browser.js +48 -0
- package/dist-cjs/compiler/build-project.js +97 -8
- package/dist-cjs/compiler/compile.js +17 -9
- package/dist-cjs/errors.js +92 -2
- package/dist-cjs/file-content.js +42 -7
- package/dist-cjs/index.js +4 -9
- package/dist-cjs/integrations/esbuild.js +6 -2
- package/dist-cjs/integrations/expo.js +29 -0
- package/dist-cjs/integrations/metro.js +51 -0
- package/dist-cjs/integrations/next.js +75 -0
- package/dist-cjs/integrations/runtime-build.js +70 -0
- package/dist-cjs/integrations/vite.js +57 -0
- package/dist-cjs/integrations/webpack.js +51 -0
- package/dist-cjs/orcha.js +17 -12
- package/dist-cjs/playground/server.js +62 -9
- package/dist-cjs/project-loader.js +10 -0
- package/dist-cjs/providers/browser.js +61 -0
- package/dist-cjs/react-native.js +48 -0
- package/dist-cjs/runtime/actions/browser-native.js +68 -0
- package/dist-cjs/runtime/actions/execute.js +12 -2
- package/dist-cjs/runtime/agent.js +37 -14
- package/dist-cjs/runtime/compiler-hook.js +37 -0
- package/dist-cjs/runtime/create-orcha.js +34 -23
- package/dist-cjs/runtime/platform-browser.js +48 -0
- package/dist-cjs/runtime/platform-node.js +59 -0
- package/dist-cjs/runtime/platform-react-native.js +45 -0
- package/dist-cjs/runtime/platform.js +17 -0
- package/dist-cjs/runtime/random-id.js +55 -0
- package/dist-cjs/runtime-entry.js +34 -0
- package/dist-cjs/storage/browser-indexeddb.js +102 -5
- package/dist-cjs/storage/react-native-jsonl.js +73 -5
- package/dist-cjs/types/react-native-async-storage.d.js +2 -0
- package/package.json +69 -9
- package/dist/playground/public/app.css +0 -1
- package/dist/playground/public/app.js +0 -31
- package/dist/playground/public/index.html +0 -15
- package/dist/playground/public/orcha-logo.svg +0 -4
|
@@ -1,11 +1,16 @@
|
|
|
1
1
|
import type { ProviderResponseGenerator } from "../providers/types.js";
|
|
2
|
+
import type { RuntimePlatform } from "./platform.js";
|
|
2
3
|
import type { AgentRuntime, CompiledBundle, OrchaInitConfiguration } from "../types.js";
|
|
3
4
|
export interface Orcha {
|
|
4
5
|
init(configuration: OrchaInitConfiguration): OrchaClient;
|
|
5
6
|
isInitialized(): boolean;
|
|
7
|
+
/** Flushes queued Amarsia telemetry without blocking normal agent runs. */
|
|
8
|
+
flushTelemetry(options?: {
|
|
9
|
+
timeoutMs?: number;
|
|
10
|
+
}): Promise<void>;
|
|
6
11
|
/** @internal Used by the development compiler. */
|
|
7
12
|
getCompiledBundle(): CompiledBundle;
|
|
8
13
|
}
|
|
9
14
|
export type OrchaClient = Orcha & Record<string, AgentRuntime>;
|
|
10
15
|
export type BundleResolver = (configuration: OrchaInitConfiguration) => CompiledBundle;
|
|
11
|
-
export declare function createOrcha(resolveBundle: BundleResolver, generateProviderResponse: ProviderResponseGenerator): OrchaClient;
|
|
16
|
+
export declare function createOrcha(resolveBundle: BundleResolver, generateProviderResponse: ProviderResponseGenerator, platform: RuntimePlatform): OrchaClient;
|
|
@@ -1,32 +1,33 @@
|
|
|
1
|
-
import { resolve } from "node:path";
|
|
2
1
|
import { RuntimeAgent } from "./agent.js";
|
|
3
|
-
import {
|
|
4
|
-
import { syncAmarsiaProject } from "../amarsia/sync.js";
|
|
2
|
+
import { AmarsiaTelemetryClient, withAmarsiaTelemetry, } from "../amarsia/analytics.js";
|
|
5
3
|
import { setOrchaRuntimeContext } from "./context.js";
|
|
6
4
|
class OrchaRuntimeCore {
|
|
7
5
|
#agents = new Map();
|
|
8
6
|
#bundle;
|
|
9
7
|
#client;
|
|
8
|
+
#telemetry;
|
|
10
9
|
#activeSessions = new Set();
|
|
11
10
|
#sessionControls = new Map();
|
|
12
11
|
#resolveBundle;
|
|
13
12
|
#generateProviderResponse;
|
|
14
|
-
|
|
13
|
+
#platform;
|
|
14
|
+
constructor(resolveBundle, generateProviderResponse, platform) {
|
|
15
15
|
this.#resolveBundle = resolveBundle;
|
|
16
16
|
this.#generateProviderResponse = generateProviderResponse;
|
|
17
|
+
this.#platform = platform;
|
|
17
18
|
}
|
|
18
19
|
attachClient(client) {
|
|
19
20
|
this.#client = client;
|
|
20
21
|
}
|
|
21
22
|
init(configuration) {
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
const
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
23
|
+
if (this.#telemetry) {
|
|
24
|
+
void this.#telemetry.close();
|
|
25
|
+
}
|
|
26
|
+
const projectRoot = this.#platform.projectRoot(configuration);
|
|
27
|
+
const bundle = configuration.bundle ?? this.#resolveBundle(configuration);
|
|
28
|
+
if (configuration.amarsia?.key && this.#platform.syncProject) {
|
|
29
|
+
this.#platform.syncProject({
|
|
30
|
+
configuration,
|
|
30
31
|
bundle,
|
|
31
32
|
projectRoot,
|
|
32
33
|
});
|
|
@@ -46,13 +47,21 @@ class OrchaRuntimeCore {
|
|
|
46
47
|
runtimeConfiguration.actions?.runtime !== bundle.actionRuntime) {
|
|
47
48
|
throw new Error(`Production bundle expects local action runtime "${bundle.actionRuntime}".`);
|
|
48
49
|
}
|
|
49
|
-
const
|
|
50
|
-
|
|
51
|
-
strategy: configuration.storage?.strategy,
|
|
52
|
-
directory: storageDirectory,
|
|
50
|
+
const createStore = (manifest) => this.#platform.createSessionStore({
|
|
51
|
+
configuration,
|
|
53
52
|
projectRoot,
|
|
54
|
-
|
|
53
|
+
manifest,
|
|
55
54
|
});
|
|
55
|
+
const telemetry = configuration.amarsia?.key
|
|
56
|
+
? new AmarsiaTelemetryClient(configuration.amarsia)
|
|
57
|
+
: undefined;
|
|
58
|
+
this.#telemetry = telemetry;
|
|
59
|
+
const createRuntimeStore = (manifest) => {
|
|
60
|
+
const store = createStore(manifest);
|
|
61
|
+
return telemetry
|
|
62
|
+
? withAmarsiaTelemetry({ store, manifest, client: telemetry })
|
|
63
|
+
: store;
|
|
64
|
+
};
|
|
56
65
|
const agents = new Map();
|
|
57
66
|
for (const [name, manifest] of Object.entries(bundle.agents)) {
|
|
58
67
|
const subagents = Object.fromEntries(Object.entries(manifest.subagents ?? {}).map(([subagentName, subagentManifest]) => [
|
|
@@ -60,23 +69,25 @@ class OrchaRuntimeCore {
|
|
|
60
69
|
new RuntimeAgent({
|
|
61
70
|
manifest: subagentManifest,
|
|
62
71
|
configuration: runtimeConfiguration,
|
|
63
|
-
store:
|
|
72
|
+
store: createRuntimeStore(subagentManifest),
|
|
64
73
|
activeSessions: this.#activeSessions,
|
|
65
74
|
sessionControls: this.#sessionControls,
|
|
66
75
|
generateProviderResponse: this.#generateProviderResponse,
|
|
67
76
|
delegatedOnly: true,
|
|
68
77
|
projectRoot,
|
|
78
|
+
randomUUID: this.#platform.randomUUID,
|
|
69
79
|
}),
|
|
70
80
|
]));
|
|
71
81
|
agents.set(name, new RuntimeAgent({
|
|
72
82
|
manifest,
|
|
73
83
|
configuration: runtimeConfiguration,
|
|
74
|
-
store:
|
|
84
|
+
store: createRuntimeStore(manifest),
|
|
75
85
|
activeSessions: this.#activeSessions,
|
|
76
86
|
sessionControls: this.#sessionControls,
|
|
77
87
|
generateProviderResponse: this.#generateProviderResponse,
|
|
78
88
|
subagents,
|
|
79
89
|
projectRoot,
|
|
90
|
+
randomUUID: this.#platform.randomUUID,
|
|
80
91
|
}));
|
|
81
92
|
}
|
|
82
93
|
this.#bundle = bundle;
|
|
@@ -93,6 +104,9 @@ class OrchaRuntimeCore {
|
|
|
93
104
|
isInitialized() {
|
|
94
105
|
return this.#bundle !== undefined;
|
|
95
106
|
}
|
|
107
|
+
async flushTelemetry(options) {
|
|
108
|
+
await this.#telemetry?.drain(options);
|
|
109
|
+
}
|
|
96
110
|
getCompiledBundle() {
|
|
97
111
|
if (!this.#bundle) {
|
|
98
112
|
throw new Error("Orcha has not been initialized.");
|
|
@@ -103,8 +117,8 @@ class OrchaRuntimeCore {
|
|
|
103
117
|
return this.#agents.get(name);
|
|
104
118
|
}
|
|
105
119
|
}
|
|
106
|
-
export function createOrcha(resolveBundle, generateProviderResponse) {
|
|
107
|
-
const core = new OrchaRuntimeCore(resolveBundle, generateProviderResponse);
|
|
120
|
+
export function createOrcha(resolveBundle, generateProviderResponse, platform) {
|
|
121
|
+
const core = new OrchaRuntimeCore(resolveBundle, generateProviderResponse, platform);
|
|
108
122
|
const client = new Proxy(core, {
|
|
109
123
|
get(target, property) {
|
|
110
124
|
if (typeof property === "string") {
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { BrowserIndexedDbSessionStore } from "../storage/browser-indexeddb.js";
|
|
2
|
+
import { portableRandomUUID } from "./random-id.js";
|
|
3
|
+
export const browserRuntimePlatform = {
|
|
4
|
+
randomUUID: portableRandomUUID,
|
|
5
|
+
projectRoot(configuration) {
|
|
6
|
+
return configuration.root ?? "/";
|
|
7
|
+
},
|
|
8
|
+
createSessionStore({ configuration, manifest }) {
|
|
9
|
+
const strategy = configuration.storage?.strategy ?? "indexeddb";
|
|
10
|
+
if (strategy !== "indexeddb") {
|
|
11
|
+
throw new Error(`Browser runtimes require storage strategy "indexeddb", received "${strategy}".`);
|
|
12
|
+
}
|
|
13
|
+
return new BrowserIndexedDbSessionStore(manifest.key ?? manifest.name, configuration.storage?.directory ?? "orchajs");
|
|
14
|
+
},
|
|
15
|
+
};
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { randomUUID } from "node:crypto";
|
|
2
|
+
import { resolve } from "node:path";
|
|
3
|
+
import { syncAmarsiaProject } from "../amarsia/sync.js";
|
|
4
|
+
import { createSessionStore } from "../storage/create-store.js";
|
|
5
|
+
export const nodeRuntimePlatform = {
|
|
6
|
+
randomUUID,
|
|
7
|
+
projectRoot(configuration) {
|
|
8
|
+
return resolve(configuration.root ??
|
|
9
|
+
process.env.ORCHA_PROJECT_ROOT ??
|
|
10
|
+
process.cwd());
|
|
11
|
+
},
|
|
12
|
+
createSessionStore({ configuration, projectRoot, manifest }) {
|
|
13
|
+
return createSessionStore({
|
|
14
|
+
strategy: configuration.storage?.strategy,
|
|
15
|
+
directory: configuration.storage?.directory,
|
|
16
|
+
projectRoot,
|
|
17
|
+
agentName: manifest.key ?? manifest.name,
|
|
18
|
+
});
|
|
19
|
+
},
|
|
20
|
+
syncProject({ configuration, bundle, projectRoot }) {
|
|
21
|
+
if (!configuration.amarsia?.key) {
|
|
22
|
+
return;
|
|
23
|
+
}
|
|
24
|
+
syncAmarsiaProject({
|
|
25
|
+
key: configuration.amarsia.key,
|
|
26
|
+
endpoint: configuration.amarsia.endpoint,
|
|
27
|
+
bundle,
|
|
28
|
+
projectRoot,
|
|
29
|
+
});
|
|
30
|
+
},
|
|
31
|
+
};
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { ReactNativeJsonlSessionStore } from "../storage/react-native-jsonl.js";
|
|
2
|
+
import { portableRandomUUID } from "./random-id.js";
|
|
3
|
+
export const reactNativeRuntimePlatform = {
|
|
4
|
+
randomUUID: portableRandomUUID,
|
|
5
|
+
projectRoot(configuration) {
|
|
6
|
+
return configuration.root ?? "/";
|
|
7
|
+
},
|
|
8
|
+
createSessionStore({ configuration, manifest }) {
|
|
9
|
+
const strategy = configuration.storage?.strategy ?? "react-native";
|
|
10
|
+
if (strategy !== "react-native") {
|
|
11
|
+
throw new Error(`React Native requires storage strategy "react-native", received "${strategy}".`);
|
|
12
|
+
}
|
|
13
|
+
return new ReactNativeJsonlSessionStore(manifest.key ?? manifest.name);
|
|
14
|
+
},
|
|
15
|
+
};
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { CompiledAgentManifest, CompiledBundle, OrchaInitConfiguration, SessionStore } from "../types.js";
|
|
2
|
+
export interface RuntimePlatform {
|
|
3
|
+
randomUUID(): string;
|
|
4
|
+
projectRoot(configuration: OrchaInitConfiguration): string;
|
|
5
|
+
createSessionStore(options: {
|
|
6
|
+
configuration: OrchaInitConfiguration;
|
|
7
|
+
projectRoot: string;
|
|
8
|
+
manifest: CompiledAgentManifest;
|
|
9
|
+
}): SessionStore;
|
|
10
|
+
syncProject?(options: {
|
|
11
|
+
configuration: OrchaInitConfiguration;
|
|
12
|
+
bundle: CompiledBundle;
|
|
13
|
+
projectRoot: string;
|
|
14
|
+
}): void;
|
|
15
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function portableRandomUUID(): string;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
let fallbackSequence = 0;
|
|
2
|
+
export function portableRandomUUID() {
|
|
3
|
+
const crypto = globalThis.crypto;
|
|
4
|
+
if (typeof crypto?.randomUUID === "function") {
|
|
5
|
+
return crypto.randomUUID();
|
|
6
|
+
}
|
|
7
|
+
if (typeof crypto?.getRandomValues === "function") {
|
|
8
|
+
return formatUuid(crypto.getRandomValues(new Uint8Array(16)));
|
|
9
|
+
}
|
|
10
|
+
fallbackSequence = (fallbackSequence + 1) >>> 0;
|
|
11
|
+
const bytes = new Uint8Array(16);
|
|
12
|
+
let state = Date.now() ^
|
|
13
|
+
fallbackSequence ^
|
|
14
|
+
Math.floor(Math.random() * 0x1_0000_0000);
|
|
15
|
+
for (let index = 0; index < bytes.length; index += 1) {
|
|
16
|
+
state = (Math.imul(state, 1_664_525) + 1_013_904_223) >>> 0;
|
|
17
|
+
bytes[index] = state >>> 24;
|
|
18
|
+
}
|
|
19
|
+
return formatUuid(bytes);
|
|
20
|
+
}
|
|
21
|
+
function formatUuid(bytes) {
|
|
22
|
+
bytes[6] = (bytes[6] & 0x0f) | 0x40;
|
|
23
|
+
bytes[8] = (bytes[8] & 0x3f) | 0x80;
|
|
24
|
+
const value = Array.from(bytes, (byte) => byte.toString(16).padStart(2, "0")).join("");
|
|
25
|
+
return `${value.slice(0, 8)}-${value.slice(8, 12)}-${value.slice(12, 16)}-${value.slice(16, 20)}-${value.slice(20)}`;
|
|
26
|
+
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export { OrchaError } from "./errors.js";
|
|
2
|
+
export type { Orcha, OrchaClient, } from "./runtime/create-orcha.js";
|
|
3
|
+
export { exportSessionJsonl, migrateSessionStore, } from "./storage/migrate.js";
|
|
4
|
+
export type { SessionStoreMigrationResult, } from "./storage/migrate.js";
|
|
5
|
+
export type { ActionConfiguration, AgentTestActionConfiguration, AgentTestActionExpectation, AgentTestActionResponse, AgentTestAssertionResult, AgentTestCaseConfiguration, AgentTestCaseReport, AgentTestCaseSummary, AgentTestExpectation, AgentTestReport, AgentTestTextExpectation, AgentTestValueExpectation, AgentTestWarning, ApiKeyProviderConfiguration, AwsCredentials, AgentContinueInput, AgentConfiguration, AgentContentInput, AgentInput, AgentRegistration, AgentRuntime, ClientCapability, ClientToolCall, CompletedRunResult, CompiledActionManifest, CompiledAgentManifest, CompiledBundle, CompiledEvaluationManifest, CompiledSkillManifest, EvaluationConfiguration, EvaluationMetricConfiguration, EvaluationMetricResult, EvaluationResult, Execution, ExecutionSnapshot, FailedRunResult, FileContent, FilePathContentInput, LocalFileContent, LocalActionRuntimeConfiguration, MessageContent, MessageContentInput, MetadataValue, OrchaErrorCode, OrchaInitConfiguration, PausedRunResult, PaginationOptions, ProjectConfiguration, ProviderConfiguration, ProviderInitConfigurations, ProviderName, BedrockProviderConfiguration, RunError, RunStatus, RunTestsOptions, ResumeInput, RunResult, SessionEvent, SessionEventListOptions, SessionEvents, SessionEventType, SessionHistory, SessionHistoryItem, SessionList, SessionListOptions, SessionMetadata, SessionSnapshot, SessionStatus, SessionStore, SkillConfiguration, SessionUpdate, StreamingRunSnapshot, SubagentConfiguration, TextContent, ToolResult, ToolResumeInput, UrlContentInput, Usage, VertexAIProviderConfiguration, GoogleServiceAccountCredentials, WaitingRunResult, WaitingForSubagentSnapshot, } from "./types.js";
|
|
@@ -1,20 +1,11 @@
|
|
|
1
1
|
import type { SessionEvent, SessionStore } from "../types.js";
|
|
2
|
-
/**
|
|
3
|
-
* Future browser storage strategy.
|
|
4
|
-
*
|
|
5
|
-
* It will persist the same ordered SessionEvent records used by the JSONL
|
|
6
|
-
* strategy in IndexedDB. Import and export will preserve the canonical JSONL
|
|
7
|
-
* representation so a session can move between browser, Node, and remote
|
|
8
|
-
* stores without changing the runtime or replay engine.
|
|
9
|
-
*
|
|
10
|
-
* Required before implementation:
|
|
11
|
-
* - transactional sequence allocation;
|
|
12
|
-
* - multi-tab writer coordination;
|
|
13
|
-
* - quota and eviction handling;
|
|
14
|
-
* - schema migrations and JSONL import/export.
|
|
15
|
-
*/
|
|
16
2
|
export declare class BrowserIndexedDbSessionStore implements SessionStore {
|
|
17
|
-
|
|
18
|
-
|
|
3
|
+
#private;
|
|
4
|
+
constructor(namespace: string, databaseName?: string);
|
|
5
|
+
read(sessionId: string): Promise<SessionEvent[]>;
|
|
6
|
+
append(sessionId: string, events: SessionEvent[]): Promise<void>;
|
|
19
7
|
listSessionIds(): Promise<string[]>;
|
|
8
|
+
subscribe(sessionId: string, listener: (events: SessionEvent[]) => void): () => void;
|
|
9
|
+
subscribeAll(listener: (sessionId: string, events: SessionEvent[]) => void): () => void;
|
|
10
|
+
describeLocation(sessionId: string): Promise<string>;
|
|
20
11
|
}
|
|
@@ -1,25 +1,108 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
*
|
|
4
|
-
* It will persist the same ordered SessionEvent records used by the JSONL
|
|
5
|
-
* strategy in IndexedDB. Import and export will preserve the canonical JSONL
|
|
6
|
-
* representation so a session can move between browser, Node, and remote
|
|
7
|
-
* stores without changing the runtime or replay engine.
|
|
8
|
-
*
|
|
9
|
-
* Required before implementation:
|
|
10
|
-
* - transactional sequence allocation;
|
|
11
|
-
* - multi-tab writer coordination;
|
|
12
|
-
* - quota and eviction handling;
|
|
13
|
-
* - schema migrations and JSONL import/export.
|
|
14
|
-
*/
|
|
1
|
+
const DATABASE_VERSION = 1;
|
|
2
|
+
const EVENT_STORE = "events";
|
|
15
3
|
export class BrowserIndexedDbSessionStore {
|
|
16
|
-
|
|
17
|
-
|
|
4
|
+
#databaseName;
|
|
5
|
+
#namespace;
|
|
6
|
+
#listeners = new Map();
|
|
7
|
+
#allListeners = new Set();
|
|
8
|
+
constructor(namespace, databaseName = "orchajs") {
|
|
9
|
+
this.#namespace = namespace;
|
|
10
|
+
this.#databaseName = databaseName;
|
|
18
11
|
}
|
|
19
|
-
async
|
|
20
|
-
|
|
12
|
+
async read(sessionId) {
|
|
13
|
+
const database = await this.#open();
|
|
14
|
+
const transaction = database.transaction(EVENT_STORE, "readonly");
|
|
15
|
+
const index = transaction.objectStore(EVENT_STORE).index("session");
|
|
16
|
+
const records = await request(index.getAll(IDBKeyRange.only([this.#namespace, sessionId])));
|
|
17
|
+
await transactionComplete(transaction);
|
|
18
|
+
return records
|
|
19
|
+
.sort((left, right) => left.sequence - right.sequence)
|
|
20
|
+
.map((record) => record.event);
|
|
21
|
+
}
|
|
22
|
+
async append(sessionId, events) {
|
|
23
|
+
if (events.length === 0) {
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
const database = await this.#open();
|
|
27
|
+
const transaction = database.transaction(EVENT_STORE, "readwrite");
|
|
28
|
+
const store = transaction.objectStore(EVENT_STORE);
|
|
29
|
+
for (const event of events) {
|
|
30
|
+
store.add({
|
|
31
|
+
id: eventId(this.#namespace, sessionId, event.sequence),
|
|
32
|
+
namespace: this.#namespace,
|
|
33
|
+
sessionId,
|
|
34
|
+
sequence: event.sequence,
|
|
35
|
+
event,
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
await transactionComplete(transaction);
|
|
39
|
+
this.#listeners.get(sessionId)?.forEach((listener) => listener(events));
|
|
40
|
+
this.#allListeners.forEach((listener) => listener(sessionId, events));
|
|
21
41
|
}
|
|
22
42
|
async listSessionIds() {
|
|
23
|
-
|
|
43
|
+
const database = await this.#open();
|
|
44
|
+
const transaction = database.transaction(EVENT_STORE, "readonly");
|
|
45
|
+
const index = transaction.objectStore(EVENT_STORE).index("namespace");
|
|
46
|
+
const records = await request(index.getAll(IDBKeyRange.only(this.#namespace)));
|
|
47
|
+
await transactionComplete(transaction);
|
|
48
|
+
return [...new Set(records.map((record) => record.sessionId))].sort();
|
|
49
|
+
}
|
|
50
|
+
subscribe(sessionId, listener) {
|
|
51
|
+
const listeners = this.#listeners.get(sessionId) ?? new Set();
|
|
52
|
+
listeners.add(listener);
|
|
53
|
+
this.#listeners.set(sessionId, listeners);
|
|
54
|
+
return () => {
|
|
55
|
+
listeners.delete(listener);
|
|
56
|
+
if (listeners.size === 0) {
|
|
57
|
+
this.#listeners.delete(sessionId);
|
|
58
|
+
}
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
subscribeAll(listener) {
|
|
62
|
+
this.#allListeners.add(listener);
|
|
63
|
+
return () => this.#allListeners.delete(listener);
|
|
64
|
+
}
|
|
65
|
+
async describeLocation(sessionId) {
|
|
66
|
+
return `indexeddb://${this.#databaseName}/${this.#namespace}/${sessionId}`;
|
|
24
67
|
}
|
|
68
|
+
#open() {
|
|
69
|
+
if (typeof indexedDB === "undefined") {
|
|
70
|
+
throw new Error("IndexedDB is unavailable in this runtime.");
|
|
71
|
+
}
|
|
72
|
+
return new Promise((resolve, reject) => {
|
|
73
|
+
const openRequest = indexedDB.open(this.#databaseName, DATABASE_VERSION);
|
|
74
|
+
openRequest.onupgradeneeded = () => {
|
|
75
|
+
const database = openRequest.result;
|
|
76
|
+
if (database.objectStoreNames.contains(EVENT_STORE)) {
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
const store = database.createObjectStore(EVENT_STORE, {
|
|
80
|
+
keyPath: "id",
|
|
81
|
+
});
|
|
82
|
+
store.createIndex("session", ["namespace", "sessionId"]);
|
|
83
|
+
store.createIndex("namespace", "namespace");
|
|
84
|
+
};
|
|
85
|
+
openRequest.onsuccess = () => resolve(openRequest.result);
|
|
86
|
+
openRequest.onerror = () => reject(openRequest.error);
|
|
87
|
+
openRequest.onblocked = () => reject(new Error("Opening the Orcha IndexedDB database was blocked."));
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
function eventId(namespace, sessionId, sequence) {
|
|
92
|
+
return `${namespace}\u0000${sessionId}\u0000${sequence
|
|
93
|
+
.toString()
|
|
94
|
+
.padStart(12, "0")}`;
|
|
95
|
+
}
|
|
96
|
+
function request(value) {
|
|
97
|
+
return new Promise((resolve, reject) => {
|
|
98
|
+
value.onsuccess = () => resolve(value.result);
|
|
99
|
+
value.onerror = () => reject(value.error);
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
function transactionComplete(transaction) {
|
|
103
|
+
return new Promise((resolve, reject) => {
|
|
104
|
+
transaction.oncomplete = () => resolve();
|
|
105
|
+
transaction.onerror = () => reject(transaction.error);
|
|
106
|
+
transaction.onabort = () => reject(transaction.error ?? new Error("IndexedDB transaction aborted."));
|
|
107
|
+
});
|
|
25
108
|
}
|
|
@@ -1,20 +1,11 @@
|
|
|
1
1
|
import type { SessionEvent, SessionStore } from "../types.js";
|
|
2
|
-
/**
|
|
3
|
-
* Future React Native storage strategy.
|
|
4
|
-
*
|
|
5
|
-
* It will store one JSONL event stream per session inside the application's
|
|
6
|
-
* private documents directory. The implementation must not assume Node's
|
|
7
|
-
* `fs` module: Expo FileSystem and bare React Native filesystem bindings need
|
|
8
|
-
* a small host adapter while retaining the exact SessionStore contract.
|
|
9
|
-
*
|
|
10
|
-
* Required before implementation:
|
|
11
|
-
* - Expo and bare React Native filesystem adapters;
|
|
12
|
-
* - atomic append/recovery behavior on iOS and Android;
|
|
13
|
-
* - app lifecycle interruption handling;
|
|
14
|
-
* - JSONL import/export conformance tests.
|
|
15
|
-
*/
|
|
16
2
|
export declare class ReactNativeJsonlSessionStore implements SessionStore {
|
|
17
|
-
|
|
18
|
-
|
|
3
|
+
#private;
|
|
4
|
+
constructor(namespace: string);
|
|
5
|
+
read(sessionId: string): Promise<SessionEvent[]>;
|
|
6
|
+
append(sessionId: string, events: SessionEvent[]): Promise<void>;
|
|
19
7
|
listSessionIds(): Promise<string[]>;
|
|
8
|
+
subscribe(sessionId: string, listener: (events: SessionEvent[]) => void): () => void;
|
|
9
|
+
subscribeAll(listener: (sessionId: string, events: SessionEvent[]) => void): () => void;
|
|
10
|
+
describeLocation(sessionId: string): Promise<string>;
|
|
20
11
|
}
|
|
@@ -1,25 +1,71 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
*
|
|
4
|
-
* It will store one JSONL event stream per session inside the application's
|
|
5
|
-
* private documents directory. The implementation must not assume Node's
|
|
6
|
-
* `fs` module: Expo FileSystem and bare React Native filesystem bindings need
|
|
7
|
-
* a small host adapter while retaining the exact SessionStore contract.
|
|
8
|
-
*
|
|
9
|
-
* Required before implementation:
|
|
10
|
-
* - Expo and bare React Native filesystem adapters;
|
|
11
|
-
* - atomic append/recovery behavior on iOS and Android;
|
|
12
|
-
* - app lifecycle interruption handling;
|
|
13
|
-
* - JSONL import/export conformance tests.
|
|
14
|
-
*/
|
|
1
|
+
import AsyncStorage from "@react-native-async-storage/async-storage";
|
|
2
|
+
const writes = new Map();
|
|
15
3
|
export class ReactNativeJsonlSessionStore {
|
|
16
|
-
|
|
17
|
-
|
|
4
|
+
#namespace;
|
|
5
|
+
#listeners = new Map();
|
|
6
|
+
#allListeners = new Set();
|
|
7
|
+
constructor(namespace) {
|
|
8
|
+
this.#namespace = namespace;
|
|
18
9
|
}
|
|
19
|
-
async
|
|
20
|
-
|
|
10
|
+
async read(sessionId) {
|
|
11
|
+
const contents = await AsyncStorage.getItem(this.#key(sessionId));
|
|
12
|
+
if (!contents) {
|
|
13
|
+
return [];
|
|
14
|
+
}
|
|
15
|
+
return contents
|
|
16
|
+
.split("\n")
|
|
17
|
+
.filter(Boolean)
|
|
18
|
+
.map((line) => JSON.parse(line));
|
|
19
|
+
}
|
|
20
|
+
async append(sessionId, events) {
|
|
21
|
+
if (events.length === 0) {
|
|
22
|
+
return;
|
|
23
|
+
}
|
|
24
|
+
const key = this.#key(sessionId);
|
|
25
|
+
const previous = writes.get(key) ?? Promise.resolve();
|
|
26
|
+
const write = previous.then(async () => {
|
|
27
|
+
const existing = await AsyncStorage.getItem(key);
|
|
28
|
+
const appended = events.map((event) => JSON.stringify(event)).join("\n");
|
|
29
|
+
await AsyncStorage.setItem(key, `${existing ? `${existing.replace(/\n*$/, "")}\n` : ""}${appended}\n`);
|
|
30
|
+
});
|
|
31
|
+
writes.set(key, write);
|
|
32
|
+
try {
|
|
33
|
+
await write;
|
|
34
|
+
}
|
|
35
|
+
finally {
|
|
36
|
+
if (writes.get(key) === write) {
|
|
37
|
+
writes.delete(key);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
this.#listeners.get(sessionId)?.forEach((listener) => listener(events));
|
|
41
|
+
this.#allListeners.forEach((listener) => listener(sessionId, events));
|
|
21
42
|
}
|
|
22
43
|
async listSessionIds() {
|
|
23
|
-
|
|
44
|
+
const prefix = `@orchajs:${this.#namespace}:`;
|
|
45
|
+
return (await AsyncStorage.getAllKeys())
|
|
46
|
+
.filter((key) => key.startsWith(prefix))
|
|
47
|
+
.map((key) => key.slice(prefix.length))
|
|
48
|
+
.sort();
|
|
49
|
+
}
|
|
50
|
+
subscribe(sessionId, listener) {
|
|
51
|
+
const listeners = this.#listeners.get(sessionId) ?? new Set();
|
|
52
|
+
listeners.add(listener);
|
|
53
|
+
this.#listeners.set(sessionId, listeners);
|
|
54
|
+
return () => {
|
|
55
|
+
listeners.delete(listener);
|
|
56
|
+
if (listeners.size === 0) {
|
|
57
|
+
this.#listeners.delete(sessionId);
|
|
58
|
+
}
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
subscribeAll(listener) {
|
|
62
|
+
this.#allListeners.add(listener);
|
|
63
|
+
return () => this.#allListeners.delete(listener);
|
|
64
|
+
}
|
|
65
|
+
async describeLocation(sessionId) {
|
|
66
|
+
return `async-storage://${this.#key(sessionId)}`;
|
|
67
|
+
}
|
|
68
|
+
#key(sessionId) {
|
|
69
|
+
return `@orchajs:${this.#namespace}:${sessionId}`;
|
|
24
70
|
}
|
|
25
71
|
}
|
package/dist/types.d.ts
CHANGED
|
@@ -43,7 +43,7 @@ export interface ProjectConfiguration {
|
|
|
43
43
|
providers: ResolvedProviderConfigurations;
|
|
44
44
|
actions?: LocalActionRuntimeConfiguration;
|
|
45
45
|
storage?: {
|
|
46
|
-
strategy?: "node-jsonl" | "sqlite";
|
|
46
|
+
strategy?: "node-jsonl" | "sqlite" | "indexeddb" | "react-native";
|
|
47
47
|
directory?: string;
|
|
48
48
|
};
|
|
49
49
|
}
|
|
@@ -54,6 +54,8 @@ export type AgentRegistration = string | {
|
|
|
54
54
|
export interface OrchaInitConfiguration {
|
|
55
55
|
providers: ProviderInitConfigurations;
|
|
56
56
|
agents: Record<string, AgentRegistration>;
|
|
57
|
+
/** Precompiled registry used by browser, React Native, and custom runtimes. */
|
|
58
|
+
bundle?: CompiledBundle;
|
|
57
59
|
amarsia?: {
|
|
58
60
|
key: string;
|
|
59
61
|
endpoint?: string;
|
|
@@ -62,6 +64,57 @@ export interface OrchaInitConfiguration {
|
|
|
62
64
|
storage?: ProjectConfiguration["storage"];
|
|
63
65
|
root?: string;
|
|
64
66
|
}
|
|
67
|
+
/** Privacy-safe, versioned run fact sent to Amarsia when `amarsia.key` is set. */
|
|
68
|
+
export interface AmarsiaTelemetryRunV1 {
|
|
69
|
+
event_id: string;
|
|
70
|
+
agent_external_id: string;
|
|
71
|
+
session_id: string;
|
|
72
|
+
run_number: number;
|
|
73
|
+
parent_agent_external_id?: string;
|
|
74
|
+
parent_session_id?: string;
|
|
75
|
+
status: string;
|
|
76
|
+
provider: string;
|
|
77
|
+
model: string;
|
|
78
|
+
region?: string;
|
|
79
|
+
output_type?: string;
|
|
80
|
+
stop_reason?: string;
|
|
81
|
+
started_at: string;
|
|
82
|
+
ended_at?: string;
|
|
83
|
+
duration_ms?: number;
|
|
84
|
+
model_duration_ms: number;
|
|
85
|
+
action_duration_ms: number;
|
|
86
|
+
input_tokens: number;
|
|
87
|
+
output_tokens: number;
|
|
88
|
+
reasoning_tokens?: number;
|
|
89
|
+
cache_read_tokens: number;
|
|
90
|
+
cache_write_tokens: number;
|
|
91
|
+
user_message_count: number;
|
|
92
|
+
assistant_message_count: number;
|
|
93
|
+
action_count: number;
|
|
94
|
+
action_success_count: number;
|
|
95
|
+
action_failure_count: number;
|
|
96
|
+
subagent_count: number;
|
|
97
|
+
actions: AmarsiaTelemetryActionV1[];
|
|
98
|
+
evaluation_count: number;
|
|
99
|
+
evaluation_pass_count: number;
|
|
100
|
+
error_code?: string;
|
|
101
|
+
error_message?: string;
|
|
102
|
+
error_retryable?: boolean;
|
|
103
|
+
}
|
|
104
|
+
export interface AmarsiaTelemetryActionV1 {
|
|
105
|
+
call_id: string;
|
|
106
|
+
name: string;
|
|
107
|
+
status: "completed" | "failed";
|
|
108
|
+
started_at: string;
|
|
109
|
+
ended_at: string;
|
|
110
|
+
duration_ms: number;
|
|
111
|
+
error_code?: string;
|
|
112
|
+
error_message?: string;
|
|
113
|
+
}
|
|
114
|
+
export interface AmarsiaTelemetryBatchV1 {
|
|
115
|
+
schema_version: 1;
|
|
116
|
+
runs: AmarsiaTelemetryRunV1[];
|
|
117
|
+
}
|
|
65
118
|
export interface SubagentConfiguration {
|
|
66
119
|
maxPerRun?: number;
|
|
67
120
|
}
|