@oharness/engine 0.1.0 → 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 +26 -0
- package/dist/agent/agent.d.ts +11 -0
- package/dist/agent/agent.js +30 -9
- package/dist/agent/agent.js.map +1 -1
- package/dist/agent/hooks.d.ts +2 -0
- package/dist/agent/hooks.js +11 -0
- package/dist/agent/hooks.js.map +1 -1
- package/dist/agent/memory.d.ts +10 -1
- package/dist/agent/memory.js +147 -49
- package/dist/agent/memory.js.map +1 -1
- package/dist/agent/permissions.js +21 -3
- package/dist/agent/permissions.js.map +1 -1
- package/dist/artifacts/publish.d.ts +2 -2
- package/dist/artifacts/publish.js +2 -2
- package/dist/auth/index.d.ts +1 -1
- package/dist/auth/index.js +1 -1
- package/dist/auth/index.js.map +1 -1
- package/dist/auth/manager.d.ts +14 -0
- package/dist/auth/manager.js +25 -5
- package/dist/auth/manager.js.map +1 -1
- package/dist/auth/providers/account.d.ts +10 -45
- package/dist/auth/providers/account.js +123 -233
- package/dist/auth/providers/account.js.map +1 -1
- package/dist/config-files.d.ts +27 -0
- package/dist/config-files.js +116 -0
- package/dist/config-files.js.map +1 -0
- package/dist/config.d.ts +5 -25
- package/dist/config.js +16 -2
- package/dist/config.js.map +1 -1
- package/dist/core/audit.d.ts +20 -0
- package/dist/core/audit.js +2 -0
- package/dist/core/audit.js.map +1 -0
- package/dist/harness.d.ts +35 -2
- package/dist/harness.js +162 -50
- package/dist/harness.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/providers/registry.d.ts +42 -0
- package/dist/providers/registry.js +71 -9
- package/dist/providers/registry.js.map +1 -1
- package/dist/runtime-plugins/audit.d.ts +11 -0
- package/dist/runtime-plugins/audit.js +31 -0
- package/dist/runtime-plugins/audit.js.map +1 -0
- package/dist/runtime-plugins/builtin/jsonl-audit.d.ts +7 -0
- package/dist/runtime-plugins/builtin/jsonl-audit.js +24 -0
- package/dist/runtime-plugins/builtin/jsonl-audit.js.map +1 -0
- package/dist/runtime-plugins/builtin/memory.d.ts +70 -0
- package/dist/runtime-plugins/builtin/memory.js +360 -0
- package/dist/runtime-plugins/builtin/memory.js.map +1 -0
- package/dist/runtime-plugins/cloud-connector.d.ts +56 -0
- package/dist/runtime-plugins/cloud-connector.js +261 -0
- package/dist/runtime-plugins/cloud-connector.js.map +1 -0
- package/dist/runtime-plugins/composio/client.d.ts +65 -0
- package/dist/runtime-plugins/composio/client.js +190 -0
- package/dist/runtime-plugins/composio/client.js.map +1 -0
- package/dist/runtime-plugins/composio/index.d.ts +23 -0
- package/dist/runtime-plugins/composio/index.js +113 -0
- package/dist/runtime-plugins/composio/index.js.map +1 -0
- package/dist/runtime-plugins/connector.d.ts +15 -0
- package/dist/runtime-plugins/connector.js +17 -0
- package/dist/runtime-plugins/connector.js.map +1 -0
- package/dist/runtime-plugins/extensions.d.ts +21 -0
- package/dist/runtime-plugins/extensions.js +23 -0
- package/dist/runtime-plugins/extensions.js.map +1 -0
- package/dist/runtime-plugins/host.d.ts +34 -0
- package/dist/runtime-plugins/host.js +263 -0
- package/dist/runtime-plugins/host.js.map +1 -0
- package/dist/runtime-plugins/index.d.ts +12 -0
- package/dist/runtime-plugins/index.js +7 -0
- package/dist/runtime-plugins/index.js.map +1 -0
- package/dist/runtime-plugins/loader.d.ts +5 -0
- package/dist/runtime-plugins/loader.js +36 -0
- package/dist/runtime-plugins/loader.js.map +1 -0
- package/dist/runtime-plugins/tool-adapter.d.ts +3 -0
- package/dist/runtime-plugins/tool-adapter.js +13 -0
- package/dist/runtime-plugins/tool-adapter.js.map +1 -0
- package/dist/runtime-plugins/types.d.ts +81 -0
- package/dist/runtime-plugins/types.js +2 -0
- package/dist/runtime-plugins/types.js.map +1 -0
- package/dist/sync/settings.d.ts +2 -0
- package/dist/sync/settings.js +17 -1
- package/dist/sync/settings.js.map +1 -1
- package/dist/tools/registry.d.ts +2 -0
- package/dist/tools/registry.js +10 -0
- package/dist/tools/registry.js.map +1 -1
- package/dist/tools/types.d.ts +2 -0
- package/dist/tools/types.js.map +1 -1
- package/official.json +1 -2
- package/package.json +1 -1
package/dist/config.d.ts
CHANGED
|
@@ -9,6 +9,7 @@ import type { ScheduleDefinition } from "./schedule/scheduler.ts";
|
|
|
9
9
|
import type { ToolFilter } from "./tools/registry.ts";
|
|
10
10
|
import type { LogLevel } from "./util/logger.ts";
|
|
11
11
|
import type { PluginConfig } from "./plugins.ts";
|
|
12
|
+
import type { RuntimePluginConfig } from "./runtime-plugins/types.ts";
|
|
12
13
|
export interface HarnessConfig {
|
|
13
14
|
/** Default model alias, e.g. "sonnet" or "openai/gpt-4.1". */
|
|
14
15
|
model?: string;
|
|
@@ -59,6 +60,8 @@ export interface HarnessConfig {
|
|
|
59
60
|
memoryDir?: string;
|
|
60
61
|
/** Declarative plugin bundles to discover and activate. */
|
|
61
62
|
plugins?: PluginConfig;
|
|
63
|
+
/** Trusted runtime code: plugins.json or embedding API; legacy user config is read-only compatible. */
|
|
64
|
+
runtimePlugins?: RuntimePluginConfig[];
|
|
62
65
|
/** Extra or overriding model specs, merged over the built-in catalog. */
|
|
63
66
|
models?: ModelSpec[];
|
|
64
67
|
/** Per-provider credentials and endpoints; env vars still apply as fallback. */
|
|
@@ -98,34 +101,11 @@ export interface HarnessConfig {
|
|
|
98
101
|
};
|
|
99
102
|
/** Where transcripts are mirrored so a second device can continue them. */
|
|
100
103
|
sync?: SyncConfig;
|
|
101
|
-
/**
|
|
102
|
-
* The account system `auth login official` signs in to.
|
|
103
|
-
*
|
|
104
|
-
* User config only — see `projectSafe`. This is the setting a fork or a
|
|
105
|
-
* self-hosted deployment uses to point the harness at its own Supabase
|
|
106
|
-
* project without rebuilding anything, and it beats the ids a published
|
|
107
|
-
* build was packaged with.
|
|
108
|
-
*/
|
|
104
|
+
/** @deprecated Read-only migration compatibility. Use cloud.json for the public site URL. */
|
|
109
105
|
account?: AccountConfig;
|
|
110
106
|
}
|
|
111
107
|
export interface AccountConfig {
|
|
112
|
-
/**
|
|
113
|
-
* The Supabase project accounts live in, e.g.
|
|
114
|
-
* `https://<project>.supabase.co`. Set this and `supabaseAnonKey` together;
|
|
115
|
-
* one without the other is treated as neither.
|
|
116
|
-
*/
|
|
117
|
-
supabaseUrl?: string;
|
|
118
|
-
/**
|
|
119
|
-
* The project's anon key. A publishable client credential — it grants
|
|
120
|
-
* nothing beyond what an anonymous visitor has, and row level security is
|
|
121
|
-
* what protects the data — so it is no more sensitive here than in the
|
|
122
|
-
* browser bundle of the site it belongs to.
|
|
123
|
-
*/
|
|
124
|
-
supabaseAnonKey?: string;
|
|
125
|
-
/**
|
|
126
|
-
* The website that owns those accounts, for the browser sign-in handoff.
|
|
127
|
-
* Defaults to the official one.
|
|
128
|
-
*/
|
|
108
|
+
/** @deprecated Use cloud.json; Supabase fields in legacy files are no longer consumed. */
|
|
129
109
|
siteUrl?: string;
|
|
130
110
|
}
|
|
131
111
|
export interface SyncConfig {
|
package/dist/config.js
CHANGED
|
@@ -3,6 +3,7 @@ import { mkdir, readFile, rename, writeFile } from "node:fs/promises";
|
|
|
3
3
|
import path from "node:path";
|
|
4
4
|
import { ConfigError } from "./core/errors.js";
|
|
5
5
|
import { parseJson } from "./util/json.js";
|
|
6
|
+
import { readMcpFile, readPluginFile } from "./config-files.js";
|
|
6
7
|
export const CONFIG_FILENAMES = ["oharness.config.json", ".oharness.json"];
|
|
7
8
|
const DEFAULT_INSTRUCTIONS = ["AGENTS.md", "CLAUDE.md", ".oharness/instructions.md"];
|
|
8
9
|
/**
|
|
@@ -14,8 +15,18 @@ const DEFAULT_INSTRUCTIONS = ["AGENTS.md", "CLAUDE.md", ".oharness/instructions.
|
|
|
14
15
|
*/
|
|
15
16
|
export async function loadConfig(cwd = process.cwd()) {
|
|
16
17
|
const user = await readConfigFile(userPath("config.json"));
|
|
18
|
+
// Account deployment data is read separately by the Cloud client, not exposed
|
|
19
|
+
// to the Harness or plugins through general configuration.
|
|
20
|
+
const { account: _legacyAccount, ...preferences } = user ?? {};
|
|
21
|
+
const plugins = readPluginFile();
|
|
22
|
+
const mcp = readMcpFile();
|
|
23
|
+
const effective = {
|
|
24
|
+
...preferences,
|
|
25
|
+
...(plugins ? { runtimePlugins: plugins.runtimePlugins ?? [], plugins: plugins.plugins ?? {} } : {}),
|
|
26
|
+
...(mcp ? { mcpServers: mcp.mcpServers ?? {} } : {}),
|
|
27
|
+
};
|
|
17
28
|
const project = await loadProjectConfig(cwd);
|
|
18
|
-
return mergeConfig(
|
|
29
|
+
return mergeConfig(effective, projectSafe(project ?? {}));
|
|
19
30
|
}
|
|
20
31
|
/**
|
|
21
32
|
* The project's own config file, unmerged.
|
|
@@ -36,6 +47,8 @@ export async function loadProjectConfig(cwd = process.cwd()) {
|
|
|
36
47
|
}
|
|
37
48
|
/**
|
|
38
49
|
* A project config with the parts a repository may not decide removed.
|
|
50
|
+
* Runtime plugin modules/options are user-owned too: a repository may not
|
|
51
|
+
* introduce executable code or redirect the credentials/endpoints it uses.
|
|
39
52
|
*
|
|
40
53
|
* Applied here rather than inside `mergeConfig`, because this is the only
|
|
41
54
|
* place that knows which side is the person and which is the repository —
|
|
@@ -55,7 +68,7 @@ export async function loadProjectConfig(cwd = process.cwd()) {
|
|
|
55
68
|
* keep.
|
|
56
69
|
*/
|
|
57
70
|
function projectSafe(project) {
|
|
58
|
-
const { account: _account, ...rest } = project;
|
|
71
|
+
const { account: _account, runtimePlugins: _runtimePlugins, ...rest } = project;
|
|
59
72
|
if (!rest.sync)
|
|
60
73
|
return rest;
|
|
61
74
|
const { url: _url, token: _token, team: _team, ...allowed } = rest.sync;
|
|
@@ -104,6 +117,7 @@ export function mergeConfig(base, override) {
|
|
|
104
117
|
writableRoots: [...(base.writableRoots ?? []), ...(override.writableRoots ?? [])],
|
|
105
118
|
readableRoots: [...(base.readableRoots ?? []), ...(override.readableRoots ?? [])],
|
|
106
119
|
plugins: mergePlugins(base.plugins, override.plugins),
|
|
120
|
+
runtimePlugins: override.runtimePlugins ?? base.runtimePlugins,
|
|
107
121
|
models: [...(base.models ?? []), ...(override.models ?? [])],
|
|
108
122
|
providers: { ...base.providers, ...override.providers },
|
|
109
123
|
openAiCompatible: [...(base.openAiCompatible ?? []), ...(override.openAiCompatible ?? [])],
|
package/dist/config.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AACtC,OAAO,EAAE,KAAK,EAAE,QAAQ,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AACtE,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAU/C,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { AuditEntry } from "../agent/permissions.ts";
|
|
2
|
+
/** Observation only: sinks cannot authorize a call or rewrite its outcome. */
|
|
3
|
+
export type RuntimeAuditEvent = {
|
|
4
|
+
type: "permission";
|
|
5
|
+
timestamp: number;
|
|
6
|
+
sessionId: string;
|
|
7
|
+
entry: AuditEntry;
|
|
8
|
+
} | {
|
|
9
|
+
type: "tool_execution";
|
|
10
|
+
timestamp: number;
|
|
11
|
+
sessionId: string;
|
|
12
|
+
agentId: string;
|
|
13
|
+
toolCallId: string;
|
|
14
|
+
toolName: string;
|
|
15
|
+
pluginId?: string;
|
|
16
|
+
durationMs: number;
|
|
17
|
+
isError: boolean;
|
|
18
|
+
};
|
|
19
|
+
/** Inputs, outputs and credentials are deliberately excluded from execution events. */
|
|
20
|
+
export type AuditSink = (event: RuntimeAuditEvent) => void | Promise<void>;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"audit.js","sourceRoot":"","sources":["../../src/core/audit.ts"],"names":[],"mappings":""}
|
package/dist/harness.d.ts
CHANGED
|
@@ -11,18 +11,23 @@ import { SubagentRegistry } from "./agent/subagent.ts";
|
|
|
11
11
|
import { SkillRegistry } from "./agent/skills.ts";
|
|
12
12
|
import { SettingsStore } from "./agent/settings.ts";
|
|
13
13
|
import type { WorkspacePolicy } from "./agent/workspace.ts";
|
|
14
|
-
import { MemoryStore } from "./agent/memory.ts";
|
|
14
|
+
import type { MemoryStore } from "./agent/memory.ts";
|
|
15
|
+
import { PluginExtensions } from "./runtime-plugins/extensions.ts";
|
|
16
|
+
import { MemoryService } from "./runtime-plugins/builtin/memory.ts";
|
|
15
17
|
import type { HarnessConfig } from "./config.ts";
|
|
16
18
|
import type { PluginDefinition } from "./plugins.ts";
|
|
17
19
|
import { McpManager } from "./mcp/manager.ts";
|
|
18
20
|
import { Scheduler } from "./schedule/scheduler.ts";
|
|
19
21
|
import type { McpServerStatus } from "./mcp/manager.ts";
|
|
20
22
|
import { ProviderRegistry } from "./providers/registry.ts";
|
|
23
|
+
import type { VerifyResult } from "./providers/registry.ts";
|
|
21
24
|
import type { Catalog, CatalogProvider } from "./providers/catalog.ts";
|
|
22
25
|
import type { ModelSpec } from "./providers/types.ts";
|
|
23
26
|
import { ToolRegistry } from "./tools/registry.ts";
|
|
24
27
|
import type { Tool } from "./tools/types.ts";
|
|
25
28
|
import type { Logger, LogSink } from "./util/logger.ts";
|
|
29
|
+
import type { RuntimePluginRegistration, RuntimePluginStatus } from "./runtime-plugins/types.ts";
|
|
30
|
+
import { CloudConnectorService } from "./runtime-plugins/cloud-connector.ts";
|
|
26
31
|
/** Whether decisions are recorded, where else, and whose call that is. */
|
|
27
32
|
export interface AuditStatus {
|
|
28
33
|
on: boolean;
|
|
@@ -40,6 +45,12 @@ export interface HarnessOptions {
|
|
|
40
45
|
tools?: Tool[];
|
|
41
46
|
/** Extra auth providers, e.g. a subscription vendor with your own client id. */
|
|
42
47
|
authProviders?: AuthProvider[];
|
|
48
|
+
/** Explicitly trusted in-process plugins; separate from declarative resource bundles. */
|
|
49
|
+
runtimePlugins?: RuntimePluginRegistration[];
|
|
50
|
+
/** Platform connector tools; default on outside ephemeral/test Harness instances. */
|
|
51
|
+
cloudConnectors?: boolean;
|
|
52
|
+
/** Optional broker for embedders/tests; config files cannot inject a service. */
|
|
53
|
+
cloudConnectorService?: CloudConnectorService;
|
|
43
54
|
logger?: Logger;
|
|
44
55
|
/**
|
|
45
56
|
* Where diagnostics go, when the default stderr is the wrong place.
|
|
@@ -68,7 +79,9 @@ export declare class Harness {
|
|
|
68
79
|
readonly hooks: HookRegistry;
|
|
69
80
|
readonly subagents: SubagentRegistry;
|
|
70
81
|
readonly skills: SkillRegistry;
|
|
71
|
-
readonly
|
|
82
|
+
readonly extensions: PluginExtensions;
|
|
83
|
+
memoryService?: MemoryService;
|
|
84
|
+
get memory(): MemoryStore | undefined;
|
|
72
85
|
readonly settings: SettingsStore;
|
|
73
86
|
readonly workspace: WorkspacePolicy;
|
|
74
87
|
readonly mcp: McpManager;
|
|
@@ -85,8 +98,16 @@ export declare class Harness {
|
|
|
85
98
|
readonly logger: Logger;
|
|
86
99
|
private readonly options;
|
|
87
100
|
private projectInstructions?;
|
|
101
|
+
private readonly pluginHost;
|
|
102
|
+
readonly cloudConnectors: CloudConnectorService;
|
|
103
|
+
private readonly runtimeAudit;
|
|
104
|
+
private readonly lifecycle;
|
|
105
|
+
private closing?;
|
|
106
|
+
private readonly activeRuns;
|
|
88
107
|
private constructor();
|
|
89
108
|
static create(opts?: HarnessOptions): Promise<Harness>;
|
|
109
|
+
/** Includes disabled/failed instances; contains neither config nor credentials. */
|
|
110
|
+
runtimePluginStatus(): RuntimePluginStatus[];
|
|
90
111
|
/**
|
|
91
112
|
* Begin mirroring, if this machine is configured and signed in for it.
|
|
92
113
|
*
|
|
@@ -209,6 +230,17 @@ export declare class Harness {
|
|
|
209
230
|
* the user catalog so the next run starts with a current list.
|
|
210
231
|
*/
|
|
211
232
|
refreshModels(providerId?: string): Promise<ModelSpec[]>;
|
|
233
|
+
/**
|
|
234
|
+
* Check a provider's credential by asking it what it serves, and keep the
|
|
235
|
+
* answer.
|
|
236
|
+
*
|
|
237
|
+
* The one call does both halves of what a login needs afterwards: a key that
|
|
238
|
+
* cannot list models is a key that will not complete one either, and a key
|
|
239
|
+
* that can has just produced the roster the user is about to pick from. Done
|
|
240
|
+
* separately, the check is a wasted round trip and the roster is a second
|
|
241
|
+
* thing to remember to ask for.
|
|
242
|
+
*/
|
|
243
|
+
verifyProvider(providerId: string): Promise<VerifyResult>;
|
|
212
244
|
/** Merge discovered models into the user catalog on disk. */
|
|
213
245
|
private saveDiscovered;
|
|
214
246
|
/**
|
|
@@ -293,6 +325,7 @@ export declare class Harness {
|
|
|
293
325
|
signal?: AbortSignal;
|
|
294
326
|
}): AsyncGenerator<AgentEvent, void, void>;
|
|
295
327
|
close(): Promise<void>;
|
|
328
|
+
private shutdown;
|
|
296
329
|
/**
|
|
297
330
|
* First model whose provider has credentials. Only the cheap synchronous
|
|
298
331
|
* check runs here — a subscription-only provider is picked up once the
|
package/dist/harness.js
CHANGED
|
@@ -9,7 +9,9 @@ import { BUILTIN_SUBAGENTS, SubagentRegistry, loadMarkdownSubagents } from "./ag
|
|
|
9
9
|
import { SkillRegistry } from "./agent/skills.js";
|
|
10
10
|
import { SettingsStore } from "./agent/settings.js";
|
|
11
11
|
import { createPolicy } from "./agent/workspace.js";
|
|
12
|
-
import {
|
|
12
|
+
import { PluginExtensions } from "./runtime-plugins/extensions.js";
|
|
13
|
+
import { createMemoryPlugin, MemoryService } from "./runtime-plugins/builtin/memory.js";
|
|
14
|
+
import { siteUrl } from "./auth/providers/account.js";
|
|
13
15
|
import { loadConfig, loadProjectConfig, loadProjectInstructions, mergeConfig } from "./config.js";
|
|
14
16
|
import { discoverPlugins, pluginContributions } from "./plugins.js";
|
|
15
17
|
import { McpManager } from "./mcp/manager.js";
|
|
@@ -18,7 +20,7 @@ import { expandSecrets } from "./mcp/secrets.js";
|
|
|
18
20
|
import { Scheduler } from "./schedule/scheduler.js";
|
|
19
21
|
import { SyncClient } from "./sync/client.js";
|
|
20
22
|
import { SessionMirror } from "./sync/mirror.js";
|
|
21
|
-
import { isExcluded, resolveSync } from "./sync/settings.js";
|
|
23
|
+
import { isExcluded, resolveSync, resolveSyncUrl } from "./sync/settings.js";
|
|
22
24
|
import { projectId } from "./storage/project.js";
|
|
23
25
|
import { updateUserConfig } from "./config.js";
|
|
24
26
|
import { ProviderRegistry } from "./providers/registry.js";
|
|
@@ -28,6 +30,9 @@ import { ToolRegistry } from "./tools/registry.js";
|
|
|
28
30
|
import { appendFile, mkdir, readFile, writeFile } from "node:fs/promises";
|
|
29
31
|
import path from "node:path";
|
|
30
32
|
import { createLogger } from "./util/logger.js";
|
|
33
|
+
import { PluginHost } from "./runtime-plugins/host.js";
|
|
34
|
+
import { RuntimeAuditBus } from "./runtime-plugins/audit.js";
|
|
35
|
+
import { CloudConnectorService, createCloudConnectorPlugin } from "./runtime-plugins/cloud-connector.js";
|
|
31
36
|
/**
|
|
32
37
|
* Top-level facade: owns providers, tools, MCP connections, hooks and
|
|
33
38
|
* subagents, and hands out `Agent` instances bound to a `Session`.
|
|
@@ -43,7 +48,9 @@ export class Harness {
|
|
|
43
48
|
hooks;
|
|
44
49
|
subagents;
|
|
45
50
|
skills;
|
|
46
|
-
|
|
51
|
+
extensions = new PluginExtensions();
|
|
52
|
+
memoryService;
|
|
53
|
+
get memory() { return this.memoryService?.store; }
|
|
47
54
|
settings;
|
|
48
55
|
workspace;
|
|
49
56
|
mcp;
|
|
@@ -63,6 +70,12 @@ export class Harness {
|
|
|
63
70
|
logger;
|
|
64
71
|
options;
|
|
65
72
|
projectInstructions;
|
|
73
|
+
pluginHost;
|
|
74
|
+
cloudConnectors;
|
|
75
|
+
runtimeAudit;
|
|
76
|
+
lifecycle = new AbortController();
|
|
77
|
+
closing;
|
|
78
|
+
activeRuns = new Set();
|
|
66
79
|
constructor(opts, config, catalog, plugins = []) {
|
|
67
80
|
this.options = opts;
|
|
68
81
|
this.cwd = opts.cwd ?? process.cwd();
|
|
@@ -130,13 +143,25 @@ export class Harness {
|
|
|
130
143
|
],
|
|
131
144
|
...(config.readableRoots !== undefined ? { readableRoots: config.readableRoots } : {}),
|
|
132
145
|
});
|
|
133
|
-
this.memory = new MemoryStore(config.memoryDir
|
|
134
|
-
? path.resolve(this.cwd, config.memoryDir)
|
|
135
|
-
: path.join(this.cwd, ".oharness", "memory"));
|
|
136
146
|
this.mcp = new McpManager(this.logger, this.auth.store);
|
|
137
147
|
this.store = new SessionStore(config.sessionDir);
|
|
138
148
|
this.scheduler = new Scheduler(this, config.schedules ?? []);
|
|
139
149
|
this.ephemeral = opts.ephemeral === true;
|
|
150
|
+
this.runtimeAudit = new RuntimeAuditBus(this.logger);
|
|
151
|
+
this.cloudConnectors = opts.cloudConnectorService ?? new CloudConnectorService({ token: async (origin) => {
|
|
152
|
+
// A still-valid access token must not leak to a newly configured Cloud,
|
|
153
|
+
// even when no refresh (which also verifies the origin) is necessary.
|
|
154
|
+
const credential = await this.auth.store.get("official");
|
|
155
|
+
if (credential?.type === "oauth" && credential.metadata?.cloudOrigin && credential.metadata.cloudOrigin !== origin) {
|
|
156
|
+
throw new Error("Cloud 地址已改变,请重新登录后连接应用。");
|
|
157
|
+
}
|
|
158
|
+
return this.accountToken();
|
|
159
|
+
} });
|
|
160
|
+
this.pluginHost = new PluginHost({
|
|
161
|
+
tools: this.tools, hooks: this.hooks, models: this.providers,
|
|
162
|
+
auth: this.auth, audit: this.runtimeAudit, logger: this.logger.child("plugins"),
|
|
163
|
+
extensions: this.extensions,
|
|
164
|
+
});
|
|
140
165
|
}
|
|
141
166
|
static async create(opts = {}) {
|
|
142
167
|
const cwd = opts.cwd ?? process.cwd();
|
|
@@ -148,51 +173,82 @@ export class Harness {
|
|
|
148
173
|
// The catalog is data on disk, so it can be updated without a release.
|
|
149
174
|
const catalog = await loadCatalog(config.catalog ? [config.catalog] : []);
|
|
150
175
|
const harness = new Harness(opts, config, catalog, plugins);
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
.
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
server
|
|
184
|
-
|
|
185
|
-
|
|
176
|
+
try {
|
|
177
|
+
harness.projectInstructions = await loadProjectInstructions(cwd, config.instructionsFile);
|
|
178
|
+
// Rules the user approved in earlier sessions live in settings files, not
|
|
179
|
+
// in the config the project checks in.
|
|
180
|
+
const persisted = await harness.settings.readAll();
|
|
181
|
+
harness.config.permissions = {
|
|
182
|
+
allow: [...(config.permissions?.allow ?? []), ...(persisted.allow ?? [])],
|
|
183
|
+
deny: [...(config.permissions?.deny ?? []), ...(persisted.deny ?? [])],
|
|
184
|
+
ask: [...(config.permissions?.ask ?? []), ...(persisted.ask ?? [])],
|
|
185
|
+
};
|
|
186
|
+
// Best effort: reclaiming disk should never block or fail a session.
|
|
187
|
+
if (config.sessionRetention && !opts.ephemeral) {
|
|
188
|
+
harness.store
|
|
189
|
+
.prune(config.sessionRetention)
|
|
190
|
+
.then((removed) => {
|
|
191
|
+
if (removed.length > 0) {
|
|
192
|
+
harness.logger.info("pruned old sessions", { count: removed.length });
|
|
193
|
+
}
|
|
194
|
+
})
|
|
195
|
+
.catch((err) => harness.logger.warn("session pruning failed", { error: String(err) }));
|
|
196
|
+
}
|
|
197
|
+
await harness.skills.discover(cwd, config.skillDirs ?? []);
|
|
198
|
+
// Markdown definitions layer over the JSON ones, so a repo can drop in an
|
|
199
|
+
// agent file without editing config.
|
|
200
|
+
for (const definition of await loadMarkdownSubagents(cwd, config.agentDirs ?? [])) {
|
|
201
|
+
harness.subagents.register(definition);
|
|
202
|
+
}
|
|
203
|
+
if (config.mcpServers && Object.keys(config.mcpServers).length > 0) {
|
|
204
|
+
const statuses = await harness.mcp.connectAll(config.mcpServers);
|
|
205
|
+
harness.tools.registerAll(harness.mcp.tools());
|
|
206
|
+
for (const status of statuses) {
|
|
207
|
+
if (status.error) {
|
|
208
|
+
harness.logger.warn("MCP server unavailable", {
|
|
209
|
+
server: status.name,
|
|
210
|
+
error: status.error,
|
|
211
|
+
});
|
|
212
|
+
}
|
|
186
213
|
}
|
|
187
214
|
}
|
|
215
|
+
const platform = (opts.cloudConnectors ?? !opts.ephemeral)
|
|
216
|
+
? [{ plugin: createCloudConnectorPlugin(harness.cloudConnectors), required: false }] : [];
|
|
217
|
+
const memory = createMemoryPlugin({ cwd, directory: config.memoryDir ? path.resolve(cwd, config.memoryDir) : path.join(cwd, ".oharness", "memory"),
|
|
218
|
+
identity: async () => {
|
|
219
|
+
const credential = await harness.auth.store.get("official");
|
|
220
|
+
if (credential?.type !== "oauth")
|
|
221
|
+
return undefined;
|
|
222
|
+
const origin = siteUrl();
|
|
223
|
+
if (credential.metadata?.cloudOrigin && credential.metadata.cloudOrigin !== origin)
|
|
224
|
+
return undefined;
|
|
225
|
+
// This claim is only a local account label/consent pin, never Cloud authorization.
|
|
226
|
+
try {
|
|
227
|
+
const payload = JSON.parse(Buffer.from(credential.access.split(".")[1] ?? "", "base64url").toString("utf8"));
|
|
228
|
+
return typeof payload.sub === "string" && /^[a-zA-Z0-9_-]{1,128}$/.test(payload.sub) ? { userId: payload.sub, origin } : undefined;
|
|
229
|
+
}
|
|
230
|
+
catch {
|
|
231
|
+
return undefined;
|
|
232
|
+
}
|
|
233
|
+
}, token: () => harness.accountToken(), syncUrl: () => resolveSyncUrl(harness.config.sync),
|
|
234
|
+
}, (service) => { harness.memoryService = service; });
|
|
235
|
+
await harness.pluginHost.boot([{ plugin: memory }, ...platform, ...(opts.runtimePlugins ?? [])], config.runtimePlugins);
|
|
236
|
+
// Awaited, though it touches no network: deciding whether to mirror reads
|
|
237
|
+
// config and asks git for the project id, and both are local. Leaving it
|
|
238
|
+
// unawaited made `syncStatus()` lie for the first few milliseconds of
|
|
239
|
+
// every process, which is exactly when a UI asks. The connecting happens
|
|
240
|
+
// inside the mirror and is not waited for.
|
|
241
|
+
await harness.startSync();
|
|
242
|
+
return harness;
|
|
188
243
|
}
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
244
|
+
catch (error) {
|
|
245
|
+
await harness.close();
|
|
246
|
+
throw error;
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
/** Includes disabled/failed instances; contains neither config nor credentials. */
|
|
250
|
+
runtimePluginStatus() {
|
|
251
|
+
return this.pluginHost.status();
|
|
196
252
|
}
|
|
197
253
|
/**
|
|
198
254
|
* Begin mirroring, if this machine is configured and signed in for it.
|
|
@@ -479,6 +535,25 @@ export class Harness {
|
|
|
479
535
|
}
|
|
480
536
|
return discovered;
|
|
481
537
|
}
|
|
538
|
+
/**
|
|
539
|
+
* Check a provider's credential by asking it what it serves, and keep the
|
|
540
|
+
* answer.
|
|
541
|
+
*
|
|
542
|
+
* The one call does both halves of what a login needs afterwards: a key that
|
|
543
|
+
* cannot list models is a key that will not complete one either, and a key
|
|
544
|
+
* that can has just produced the roster the user is about to pick from. Done
|
|
545
|
+
* separately, the check is a wasted round trip and the roster is a second
|
|
546
|
+
* thing to remember to ask for.
|
|
547
|
+
*/
|
|
548
|
+
async verifyProvider(providerId) {
|
|
549
|
+
const result = await this.providers.verify(providerId);
|
|
550
|
+
if (result.status === "ok" && result.models.length > 0) {
|
|
551
|
+
await this.saveDiscovered(result.models).catch((err) => this.logger.warn("could not persist the discovered catalog", {
|
|
552
|
+
error: String(err),
|
|
553
|
+
}));
|
|
554
|
+
}
|
|
555
|
+
return result;
|
|
556
|
+
}
|
|
482
557
|
/** Merge discovered models into the user catalog on disk. */
|
|
483
558
|
async saveDiscovered(models) {
|
|
484
559
|
const file = userCatalogPath();
|
|
@@ -520,6 +595,8 @@ export class Harness {
|
|
|
520
595
|
}
|
|
521
596
|
/** Start a fresh session. */
|
|
522
597
|
createSession(overrides = {}) {
|
|
598
|
+
if (this.lifecycle.signal.aborted)
|
|
599
|
+
throw new ConfigError("Harness is closed");
|
|
523
600
|
return this.equip(new Session({
|
|
524
601
|
cwd: this.cwd,
|
|
525
602
|
model: overrides.model ?? this.config.model ?? this.defaultModel(),
|
|
@@ -576,6 +653,7 @@ export class Harness {
|
|
|
576
653
|
* path, not just louder.
|
|
577
654
|
*/
|
|
578
655
|
record(sessionId, entry) {
|
|
656
|
+
this.runtimeAudit.publish({ type: "permission", timestamp: Date.now(), sessionId, entry });
|
|
579
657
|
const configured = this.config.auditLog;
|
|
580
658
|
this.auditQueue = this.auditQueue
|
|
581
659
|
.then(async () => {
|
|
@@ -640,13 +718,15 @@ export class Harness {
|
|
|
640
718
|
}
|
|
641
719
|
/** Build an agent bound to a session, wired with everything the harness owns. */
|
|
642
720
|
agent(session, overrides = {}) {
|
|
721
|
+
if (this.lifecycle.signal.aborted)
|
|
722
|
+
throw new ConfigError("Harness is closed");
|
|
643
723
|
return new Agent({
|
|
644
724
|
session,
|
|
645
725
|
tools: this.tools,
|
|
646
726
|
hooks: this.hooks,
|
|
647
727
|
subagents: this.subagents,
|
|
648
728
|
skills: this.skills,
|
|
649
|
-
|
|
729
|
+
promptSections: () => this.extensions.promptSections(),
|
|
650
730
|
onSkillsChanged: () => this.reloadSkills(),
|
|
651
731
|
mcp: this.mcp,
|
|
652
732
|
permissionPrompt: this.options.permissionPrompt,
|
|
@@ -660,6 +740,15 @@ export class Harness {
|
|
|
660
740
|
maxTurns: this.config.maxTurns,
|
|
661
741
|
cache: this.config.cache,
|
|
662
742
|
logger: this.logger,
|
|
743
|
+
lifecycleSignal: this.lifecycle.signal,
|
|
744
|
+
onRunStart: () => {
|
|
745
|
+
let finish;
|
|
746
|
+
const done = new Promise((resolve) => { finish = resolve; });
|
|
747
|
+
this.activeRuns.add(done);
|
|
748
|
+
return () => { this.activeRuns.delete(done); finish(); };
|
|
749
|
+
},
|
|
750
|
+
onAudit: (event) => { if (this.config.audit !== false)
|
|
751
|
+
this.runtimeAudit.publish(event); },
|
|
663
752
|
...overrides,
|
|
664
753
|
});
|
|
665
754
|
}
|
|
@@ -672,11 +761,34 @@ export class Harness {
|
|
|
672
761
|
const agent = this.agent(session);
|
|
673
762
|
yield* agent.run(prompt, { signal: opts.signal });
|
|
674
763
|
}
|
|
675
|
-
|
|
764
|
+
close() {
|
|
765
|
+
return this.closing ??= this.shutdown();
|
|
766
|
+
}
|
|
767
|
+
async shutdown() {
|
|
768
|
+
this.lifecycle.abort();
|
|
769
|
+
this.cloudConnectors.close();
|
|
770
|
+
this.scheduler.stop();
|
|
676
771
|
// Stopped before MCP, because it is the only thing here holding a socket
|
|
677
772
|
// open on purpose — a long poll would otherwise keep the process alive for
|
|
678
773
|
// up to thirty seconds after everything else had shut down.
|
|
679
774
|
this.mirror?.stop();
|
|
775
|
+
let timer;
|
|
776
|
+
try {
|
|
777
|
+
await Promise.race([
|
|
778
|
+
Promise.all(this.activeRuns),
|
|
779
|
+
new Promise((resolve) => {
|
|
780
|
+
timer = setTimeout(() => {
|
|
781
|
+
this.logger.warn("Agent drain timed out during shutdown");
|
|
782
|
+
resolve();
|
|
783
|
+
}, 5000);
|
|
784
|
+
}),
|
|
785
|
+
]);
|
|
786
|
+
}
|
|
787
|
+
finally {
|
|
788
|
+
clearTimeout(timer);
|
|
789
|
+
}
|
|
790
|
+
await this.pluginHost.close();
|
|
791
|
+
await this.auditQueue;
|
|
680
792
|
await this.mcp.closeAll();
|
|
681
793
|
}
|
|
682
794
|
/**
|