@temporalio/agent-harness 0.0.0-stage → 0.1.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/LICENSE +21 -0
- package/README.md +5 -2
- package/dist/src/ai-sdks/google-genai-plugin/index.d.ts +6 -0
- package/dist/src/ai-sdks/google-genai-plugin/index.js +24 -0
- package/dist/src/ai-sdks/google-genai-plugin/interactions-conversation.d.ts +35 -0
- package/dist/src/ai-sdks/google-genai-plugin/interactions-conversation.js +178 -0
- package/dist/src/ai-sdks/google-genai-plugin/interactions-models.d.ts +8 -0
- package/dist/src/ai-sdks/google-genai-plugin/interactions-models.js +4 -0
- package/dist/src/ai-sdks/google-genai-plugin/interactions-workflow.d.ts +8 -0
- package/dist/src/ai-sdks/google-genai-plugin/interactions-workflow.js +15 -0
- package/dist/src/ai-sdks/google-genai-plugin/temporal-async-client.d.ts +10 -0
- package/dist/src/ai-sdks/google-genai-plugin/temporal-async-client.js +9 -0
- package/dist/src/ai-sdks/google-genai-plugin/temporal-interactions.d.ts +31 -0
- package/dist/src/ai-sdks/google-genai-plugin/temporal-interactions.js +43 -0
- package/dist/src/ai-sdks/google-genai-plugin/workflow.d.ts +16 -0
- package/dist/src/ai-sdks/google-genai-plugin/workflow.js +14 -0
- package/dist/src/ai-sdks/jev/evaluator.d.ts +38 -0
- package/dist/src/ai-sdks/jev/evaluator.js +155 -0
- package/dist/src/ai-sdks/openai-agents-harness.d.ts +94 -0
- package/dist/src/ai-sdks/openai-agents-harness.js +347 -0
- package/dist/src/harness/agent-protocol/agent-interface.d.ts +369 -0
- package/dist/src/harness/agent-protocol/agent-interface.js +222 -0
- package/dist/src/harness/agent-protocol/events.d.ts +10 -0
- package/dist/src/harness/agent-protocol/events.js +6 -0
- package/dist/src/harness/agent-protocol/index.d.ts +5 -0
- package/dist/src/harness/agent-protocol/index.js +7 -0
- package/dist/src/harness/agent-protocol/schema.d.ts +34 -0
- package/dist/src/harness/agent-protocol/schema.js +53 -0
- package/dist/src/harness/agent-protocol/subagent-interface.d.ts +25 -0
- package/dist/src/harness/agent-protocol/subagent-interface.js +17 -0
- package/dist/src/harness/agent-schema.d.ts +4 -0
- package/dist/src/harness/agent-schema.js +106 -0
- package/dist/src/harness/agent-workflow/errors.d.ts +26 -0
- package/dist/src/harness/agent-workflow/errors.js +67 -0
- package/dist/src/harness/agent-workflow/types.d.ts +88 -0
- package/dist/src/harness/agent-workflow/types.js +1 -0
- package/dist/src/harness/agent-workflow.d.ts +79 -0
- package/dist/src/harness/agent-workflow.js +824 -0
- package/dist/src/harness/agent.d.ts +137 -0
- package/dist/src/harness/agent.js +272 -0
- package/dist/src/harness/code-mode/activity-fs.d.ts +46 -0
- package/dist/src/harness/code-mode/activity-fs.js +102 -0
- package/dist/src/harness/code-mode/base64.d.ts +2 -0
- package/dist/src/harness/code-mode/base64.js +41 -0
- package/dist/src/harness/code-mode/driver.d.ts +78 -0
- package/dist/src/harness/code-mode/driver.js +266 -0
- package/dist/src/harness/code-mode/index.d.ts +4 -0
- package/dist/src/harness/code-mode/index.js +6 -0
- package/dist/src/harness/code-mode/mount-decl.d.ts +43 -0
- package/dist/src/harness/code-mode/mount-decl.js +100 -0
- package/dist/src/harness/code-mode/okf-tool.d.ts +10 -0
- package/dist/src/harness/code-mode/okf-tool.js +89 -0
- package/dist/src/harness/code-mode/okf.d.ts +91 -0
- package/dist/src/harness/code-mode/okf.js +315 -0
- package/dist/src/harness/code-mode/stubs.d.ts +13 -0
- package/dist/src/harness/code-mode/stubs.js +203 -0
- package/dist/src/harness/code-mode/texts.d.ts +4 -0
- package/dist/src/harness/code-mode/texts.js +6 -0
- package/dist/src/harness/code-mode/tool.d.ts +37 -0
- package/dist/src/harness/code-mode/tool.js +79 -0
- package/dist/src/harness/code-mode/vfs.d.ts +209 -0
- package/dist/src/harness/code-mode/vfs.js +862 -0
- package/dist/src/harness/py-repr.d.ts +2 -0
- package/dist/src/harness/py-repr.js +24 -0
- package/dist/src/harness/state/index.d.ts +40 -0
- package/dist/src/harness/state/index.js +142 -0
- package/dist/src/harness/stream-context.d.ts +8 -0
- package/dist/src/harness/stream-context.js +9 -0
- package/dist/src/harness/subagent-toolset.d.ts +29 -0
- package/dist/src/harness/subagent-toolset.js +87 -0
- package/dist/src/index.d.ts +12 -0
- package/dist/src/index.js +13 -0
- package/dist/src/sandbox.d.ts +12 -0
- package/package.json +95 -3
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
// Python's repr() of JSON-native values, which is what the Python harness shows a model.
|
|
2
|
+
/** Python's repr() for JSON-native values: what the model sees as the script's result. */
|
|
3
|
+
export function pyRepr(value) {
|
|
4
|
+
if (value === null || value === undefined)
|
|
5
|
+
return "None";
|
|
6
|
+
if (value === true)
|
|
7
|
+
return "True";
|
|
8
|
+
if (value === false)
|
|
9
|
+
return "False";
|
|
10
|
+
if (typeof value === "string") {
|
|
11
|
+
const quote = value.includes("'") && !value.includes('"') ? '"' : "'";
|
|
12
|
+
const escaped = value.replace(/[\\\n\r\t]/g, (c) => ({ "\\": "\\\\", "\n": "\\n", "\r": "\\r", "\t": "\\t" })[c]);
|
|
13
|
+
return `${quote}${escaped.replaceAll(quote, `\\${quote}`)}${quote}`;
|
|
14
|
+
}
|
|
15
|
+
if (typeof value === "number")
|
|
16
|
+
return String(value);
|
|
17
|
+
if (Array.isArray(value))
|
|
18
|
+
return `[${value.map(pyRepr).join(", ")}]`;
|
|
19
|
+
if (typeof value === "object")
|
|
20
|
+
return `{${Object.entries(value)
|
|
21
|
+
.map(([k, v]) => `${pyRepr(k)}: ${pyRepr(v)}`)
|
|
22
|
+
.join(", ")}}`;
|
|
23
|
+
return String(value);
|
|
24
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { type Draft } from "immer";
|
|
2
|
+
import type { z } from "zod";
|
|
3
|
+
export declare class HarnessStateError extends Error {
|
|
4
|
+
name: string;
|
|
5
|
+
}
|
|
6
|
+
export interface StateSnapshotEvent {
|
|
7
|
+
type: "state_snapshot";
|
|
8
|
+
state_id: string;
|
|
9
|
+
version: number;
|
|
10
|
+
value: Record<string, unknown>;
|
|
11
|
+
}
|
|
12
|
+
export interface StatePatchEvent {
|
|
13
|
+
type: "state_patch";
|
|
14
|
+
state_id: string;
|
|
15
|
+
version: number;
|
|
16
|
+
ops: Record<string, unknown>[];
|
|
17
|
+
}
|
|
18
|
+
export type StateEvent = StateSnapshotEvent | StatePatchEvent;
|
|
19
|
+
export interface StateDecl<S extends z.ZodType<Record<string, unknown>>> {
|
|
20
|
+
readonly schema: S;
|
|
21
|
+
/** Receives the agent's validated init data (null/undefined when it has none). */
|
|
22
|
+
readonly initial: (data: unknown) => z.input<S>;
|
|
23
|
+
}
|
|
24
|
+
/** Declare a piece of observable state. Without `initial`, the schema's defaults must cover it. */
|
|
25
|
+
export declare function state<S extends z.ZodType<Record<string, unknown>>>(schema: S, initial?: (data: unknown) => z.input<S>): StateDecl<S>;
|
|
26
|
+
/** Why a Python peer can't read `value` back as sent, if it can't. */
|
|
27
|
+
export declare function portabilityProblem(value: unknown, depth?: number): string | undefined;
|
|
28
|
+
export declare class StateRef<T extends Record<string, unknown>> {
|
|
29
|
+
#private;
|
|
30
|
+
readonly stateId: string;
|
|
31
|
+
constructor(stateId: string, decl: StateDecl<z.ZodType<T>>, publish: (event: StateEvent) => void, data?: unknown);
|
|
32
|
+
/** The committed value. Deeply frozen: change it only through `mutate` or `set`. */
|
|
33
|
+
get current(): T;
|
|
34
|
+
get version(): number;
|
|
35
|
+
snapshot(): StateSnapshotEvent;
|
|
36
|
+
/** Apply `recipe` to a draft and publish what it changed. Synchronous by contract. */
|
|
37
|
+
mutate(recipe: (draft: Draft<T>) => void): void;
|
|
38
|
+
/** Replace the whole value (one `replace` op at the root). */
|
|
39
|
+
set(value: z.input<z.ZodType<T>>): void;
|
|
40
|
+
}
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
// Observable agent state. Declare it on an agent with `states: { board: state(BoardSchema) }`;
|
|
2
|
+
// the key is its `state_id`. Every committed `ref.mutate((d) => { ... })` is published on the
|
|
3
|
+
// event stream as RFC 6902 ops, so a consumer that applies one snapshot and then every patch in
|
|
4
|
+
// version order holds the exact value without polling. Immer records the ops (it emits only
|
|
5
|
+
// add/replace/remove, which is all the protocol allows); zod re-validates every commit.
|
|
6
|
+
import { applyPatches, castDraft, enablePatches, freeze, produceWithPatches, } from "immer";
|
|
7
|
+
enablePatches();
|
|
8
|
+
export class HarnessStateError extends Error {
|
|
9
|
+
name = "HarnessStateError";
|
|
10
|
+
}
|
|
11
|
+
/** Declare a piece of observable state. Without `initial`, the schema's defaults must cover it. */
|
|
12
|
+
export function state(schema, initial = () => ({})) {
|
|
13
|
+
return { schema, initial };
|
|
14
|
+
}
|
|
15
|
+
// Python's JSON parser rejects lone surrogates and nesting past about 200 levels, and a JSON number
|
|
16
|
+
// past 2^53 has already been rounded by the time JavaScript sees it.
|
|
17
|
+
const MAX_JSON_DEPTH = 200;
|
|
18
|
+
/** Why a Python peer can't read `value` back as sent, if it can't. */
|
|
19
|
+
export function portabilityProblem(value, depth = 0) {
|
|
20
|
+
if (typeof value === "string")
|
|
21
|
+
return value.isWellFormed() ? undefined : "has a string with a lone surrogate";
|
|
22
|
+
if (typeof value === "number") {
|
|
23
|
+
// From 1e21 up JSON writes numbers in exponent form, which every peer reads as a float.
|
|
24
|
+
if (Number.isInteger(value) && !Number.isSafeInteger(value) && Math.abs(value) < 1e21) {
|
|
25
|
+
return `has the integer ${value}, past 2^53, which a JSON number can't carry exactly; send it as a string`;
|
|
26
|
+
}
|
|
27
|
+
return undefined;
|
|
28
|
+
}
|
|
29
|
+
if (typeof value !== "object" || value === null)
|
|
30
|
+
return undefined;
|
|
31
|
+
if (depth >= MAX_JSON_DEPTH)
|
|
32
|
+
return `nests deeper than ${MAX_JSON_DEPTH} levels`;
|
|
33
|
+
for (const [key, item] of Object.entries(value)) {
|
|
34
|
+
if (!key.isWellFormed())
|
|
35
|
+
return "has a key with a lone surrogate";
|
|
36
|
+
const problem = portabilityProblem(item, depth + 1);
|
|
37
|
+
if (problem !== undefined)
|
|
38
|
+
return problem;
|
|
39
|
+
}
|
|
40
|
+
return undefined;
|
|
41
|
+
}
|
|
42
|
+
// structuredClone is not available in the workflow isolate; state values are JSON by contract.
|
|
43
|
+
const jsonClone = (value) => JSON.parse(JSON.stringify(value));
|
|
44
|
+
const escape = (segment) => String(segment).replaceAll("~", "~0").replaceAll("/", "~1");
|
|
45
|
+
function toOp(patch) {
|
|
46
|
+
const op = {
|
|
47
|
+
op: patch.op,
|
|
48
|
+
path: patch.path.map((s) => `/${escape(s)}`).join(""),
|
|
49
|
+
};
|
|
50
|
+
if (patch.op !== "remove")
|
|
51
|
+
op.value = jsonClone(patch.value);
|
|
52
|
+
return op;
|
|
53
|
+
}
|
|
54
|
+
// Python writes an append as "/-"; an add at an array's current length means the same thing.
|
|
55
|
+
function toOps(base, patches) {
|
|
56
|
+
let value = base;
|
|
57
|
+
return patches.flatMap((patch) => {
|
|
58
|
+
const op = toOp(patch);
|
|
59
|
+
const index = patch.path.at(-1);
|
|
60
|
+
const parent = patch.path
|
|
61
|
+
.slice(0, -1)
|
|
62
|
+
.reduce((v, key) => v[key], value);
|
|
63
|
+
// immer checks `key in base`, so deleting an absent key named like an inherited member
|
|
64
|
+
// (`toString`) comes out as a remove of a path that does not exist.
|
|
65
|
+
if (patch.op === "remove" && !Array.isArray(parent) && !Object.hasOwn(parent, index))
|
|
66
|
+
return [];
|
|
67
|
+
if (patch.op === "add" && Array.isArray(parent) && index === parent.length)
|
|
68
|
+
op.path = `${op.path.slice(0, -String(index).length)}-`;
|
|
69
|
+
value = applyPatches(value, [patch]);
|
|
70
|
+
return [op];
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
export class StateRef {
|
|
74
|
+
#current;
|
|
75
|
+
#version = 0;
|
|
76
|
+
#mutating = false;
|
|
77
|
+
#schema;
|
|
78
|
+
#publish;
|
|
79
|
+
stateId;
|
|
80
|
+
constructor(stateId, decl, publish, data) {
|
|
81
|
+
this.stateId = stateId;
|
|
82
|
+
this.#schema = decl.schema;
|
|
83
|
+
this.#current = freeze(this.#schema.parse(decl.initial(data)), true);
|
|
84
|
+
this.#publish = publish;
|
|
85
|
+
}
|
|
86
|
+
/** The committed value. Deeply frozen: change it only through `mutate` or `set`. */
|
|
87
|
+
get current() {
|
|
88
|
+
return this.#current;
|
|
89
|
+
}
|
|
90
|
+
get version() {
|
|
91
|
+
return this.#version;
|
|
92
|
+
}
|
|
93
|
+
snapshot() {
|
|
94
|
+
return {
|
|
95
|
+
type: "state_snapshot",
|
|
96
|
+
state_id: this.stateId,
|
|
97
|
+
version: this.#version,
|
|
98
|
+
value: jsonClone(this.#current),
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
/** Apply `recipe` to a draft and publish what it changed. Synchronous by contract. */
|
|
102
|
+
mutate(recipe) {
|
|
103
|
+
if (this.#mutating)
|
|
104
|
+
throw new HarnessStateError(`state ${this.stateId} already has an open mutate() block`);
|
|
105
|
+
this.#mutating = true;
|
|
106
|
+
try {
|
|
107
|
+
const [next, patches] = produceWithPatches(this.#current, (draft) => {
|
|
108
|
+
const returned = recipe(draft);
|
|
109
|
+
if (returned instanceof Promise) {
|
|
110
|
+
throw new HarnessStateError(`mutate() on state ${this.stateId} must be synchronous; it must not span an await`);
|
|
111
|
+
}
|
|
112
|
+
});
|
|
113
|
+
const ops = toOps(this.#current, patches);
|
|
114
|
+
if (ops.length === 0)
|
|
115
|
+
return;
|
|
116
|
+
this.#commit(next, ops);
|
|
117
|
+
}
|
|
118
|
+
finally {
|
|
119
|
+
this.#mutating = false;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
/** Replace the whole value (one `replace` op at the root). */
|
|
123
|
+
set(value) {
|
|
124
|
+
if (this.#mutating)
|
|
125
|
+
throw new HarnessStateError(`state ${this.stateId} cannot be set() inside its open mutate() block`);
|
|
126
|
+
const next = this.#schema.parse(value);
|
|
127
|
+
this.#commit(next, [{ op: "replace", path: "", value: jsonClone(next) }]);
|
|
128
|
+
}
|
|
129
|
+
// Validates but commits `next` itself, so the published ops describe exactly the committed value.
|
|
130
|
+
#commit(next, ops) {
|
|
131
|
+
const validated = this.#schema.safeParse(next);
|
|
132
|
+
if (!validated.success) {
|
|
133
|
+
throw new HarnessStateError(`state ${this.stateId}: the change does not fit its schema: ${validated.error.message}`);
|
|
134
|
+
}
|
|
135
|
+
const problem = portabilityProblem(next);
|
|
136
|
+
if (problem !== undefined)
|
|
137
|
+
throw new HarnessStateError(`state ${this.stateId}: the change ${problem}`);
|
|
138
|
+
this.#current = freeze(castDraft(next), true);
|
|
139
|
+
this.#version += 1;
|
|
140
|
+
this.#publish({ type: "state_patch", state_id: this.stateId, version: this.#version, ops });
|
|
141
|
+
}
|
|
142
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
export declare const TurnStreamContextSchema: z.ZodObject<{
|
|
3
|
+
turn_id: z.ZodString;
|
|
4
|
+
turn_number: z.ZodNumber;
|
|
5
|
+
message_id: z.ZodDefault<z.ZodNullable<z.ZodString>>;
|
|
6
|
+
agent_id: z.ZodString;
|
|
7
|
+
}, z.core.$strip>;
|
|
8
|
+
export type TurnStreamContext = z.output<typeof TurnStreamContextSchema>;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
// Identifies the in-flight turn for code that publishes to the agent's stream from outside the
|
|
2
|
+
// workflow, e.g. an activity (Python's stream_context.py).
|
|
3
|
+
import { z } from "zod";
|
|
4
|
+
export const TurnStreamContextSchema = z.object({
|
|
5
|
+
turn_id: z.string(),
|
|
6
|
+
turn_number: z.number().int(),
|
|
7
|
+
message_id: z.string().nullable().default(null),
|
|
8
|
+
agent_id: z.string(),
|
|
9
|
+
});
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { type AgentWorkflow, type AnyHarnessTool } from "./agent.ts";
|
|
2
|
+
export declare const SubagentToolPolicy: {
|
|
3
|
+
/** Every handler its author marked model_callable (the default). */
|
|
4
|
+
allowModelCallable: () => {
|
|
5
|
+
allowedNames: string[] | null;
|
|
6
|
+
includeNonModelCallable: boolean;
|
|
7
|
+
};
|
|
8
|
+
allowOnly: (...names: string[]) => {
|
|
9
|
+
allowedNames: string[];
|
|
10
|
+
includeNonModelCallable: boolean;
|
|
11
|
+
};
|
|
12
|
+
dangerouslyAllowAll: () => {
|
|
13
|
+
allowedNames: string[] | null;
|
|
14
|
+
includeNonModelCallable: boolean;
|
|
15
|
+
};
|
|
16
|
+
};
|
|
17
|
+
export type SubagentToolPolicy = ReturnType<typeof SubagentToolPolicy.allowModelCallable>;
|
|
18
|
+
/**
|
|
19
|
+
* Tools that let a model start, message, and stop instances of another agent as subagents:
|
|
20
|
+
* `start_<key>`, one `<key>_<handler>` per selected handler, and `stop_<key>`. Each send tool takes
|
|
21
|
+
* the handle as `subagent` and the handler's input under its `inputName`.
|
|
22
|
+
*/
|
|
23
|
+
export declare function subagentToolset(agent: AgentWorkflow, options: {
|
|
24
|
+
key: string;
|
|
25
|
+
taskQueue: string;
|
|
26
|
+
workflowType?: string;
|
|
27
|
+
tools?: SubagentToolPolicy;
|
|
28
|
+
initData?: () => unknown;
|
|
29
|
+
}): AnyHarnessTool[];
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
// Subagents as tools (Python's subagent_toolset.py): a model starts, messages, and stops instances
|
|
2
|
+
// of another agent through `start_<key>`, one `<key>_<handler>` per selected handler, and
|
|
3
|
+
// `stop_<key>`.
|
|
4
|
+
import { z } from "zod";
|
|
5
|
+
import { currentToolCall } from "./agent-workflow.js";
|
|
6
|
+
import { tool } from "./agent.js";
|
|
7
|
+
export const SubagentToolPolicy = {
|
|
8
|
+
/** Every handler its author marked model_callable (the default). */
|
|
9
|
+
allowModelCallable: () => ({
|
|
10
|
+
allowedNames: null,
|
|
11
|
+
includeNonModelCallable: false,
|
|
12
|
+
}),
|
|
13
|
+
allowOnly: (...names) => ({ allowedNames: names, includeNonModelCallable: true }),
|
|
14
|
+
dangerouslyAllowAll: () => ({
|
|
15
|
+
allowedNames: null,
|
|
16
|
+
includeNonModelCallable: true,
|
|
17
|
+
}),
|
|
18
|
+
};
|
|
19
|
+
/**
|
|
20
|
+
* Tools that let a model start, message, and stop instances of another agent as subagents:
|
|
21
|
+
* `start_<key>`, one `<key>_<handler>` per selected handler, and `stop_<key>`. Each send tool takes
|
|
22
|
+
* the handle as `subagent` and the handler's input under its `inputName`.
|
|
23
|
+
*/
|
|
24
|
+
export function subagentToolset(agent, options) {
|
|
25
|
+
const def = agent.definition;
|
|
26
|
+
const { key, taskQueue } = options;
|
|
27
|
+
const workflowType = options.workflowType ?? def.name;
|
|
28
|
+
if (options.initData !== undefined && def.initData === undefined) {
|
|
29
|
+
throw new TypeError(`initData was given, but ${def.name} takes no init data.`);
|
|
30
|
+
}
|
|
31
|
+
if (options.initData === undefined && def.initData?.required) {
|
|
32
|
+
throw new TypeError(`${def.name} requires init data, so subagentToolset needs an initData callback that supplies it.`);
|
|
33
|
+
}
|
|
34
|
+
const policy = options.tools ?? SubagentToolPolicy.allowModelCallable();
|
|
35
|
+
const selected = Object.entries(def.handlers).filter(([name, h]) => (policy.allowedNames === null || policy.allowedNames.includes(name)) &&
|
|
36
|
+
(h.modelCallable || policy.includeNonModelCallable));
|
|
37
|
+
if (selected.length === 0) {
|
|
38
|
+
throw new TypeError(`${def.name} exposes no handlers under the given SubagentToolPolicy, so it has no callable surface.`);
|
|
39
|
+
}
|
|
40
|
+
for (const [name, h] of selected) {
|
|
41
|
+
if (h.inputName === "subagent")
|
|
42
|
+
throw new TypeError(`${def.name}.${name} names its input 'subagent', which its send tool uses for the handle.`);
|
|
43
|
+
}
|
|
44
|
+
const start = tool({
|
|
45
|
+
name: `start_${key}`,
|
|
46
|
+
description: `Start a new ${key} subagent and return its short handle. Call this first; then pass the returned handle as the ` +
|
|
47
|
+
`\`subagent\` argument to the ${key}_* tools to drive that instance, and to stop_${key} to shut it down. You may ` +
|
|
48
|
+
`start several instances to work on subtasks in parallel — each returns its own handle.`,
|
|
49
|
+
inputSchema: z.object({}),
|
|
50
|
+
outputSchema: z.string(),
|
|
51
|
+
execute: async () => {
|
|
52
|
+
const { runner } = currentToolCall(`start_${key}`);
|
|
53
|
+
let data = options.initData?.();
|
|
54
|
+
if (def.initData !== undefined && data !== undefined && data !== null)
|
|
55
|
+
data = def.initData.schema.parse(data);
|
|
56
|
+
else if (def.initData?.required)
|
|
57
|
+
throw new TypeError(`initData returned ${data}, but the ${key} subagent requires data.`);
|
|
58
|
+
return runner.startSubagent(key, workflowType, taskQueue, { data: data ?? undefined });
|
|
59
|
+
},
|
|
60
|
+
});
|
|
61
|
+
const sends = selected.map(([name, h]) => tool({
|
|
62
|
+
name: `${key}_${name}`,
|
|
63
|
+
description: `${h.description}\n\nSends this to the ${key} subagent identified by \`subagent\` (the handle returned by ` +
|
|
64
|
+
`start_${key}) and returns its reply. Send several messages to the same handle to have that instance expand on ` +
|
|
65
|
+
`or clarify its earlier work.`,
|
|
66
|
+
inputSchema: z.object({ subagent: z.string(), [h.inputName]: h.input }),
|
|
67
|
+
outputSchema: h.output,
|
|
68
|
+
execute: async (input) => {
|
|
69
|
+
const { runner } = currentToolCall(`${key}_${name}`);
|
|
70
|
+
const subagent = input.subagent;
|
|
71
|
+
const payload = jsonClone(h.input.parse(input[h.inputName]));
|
|
72
|
+
return h.output.parse(await runner.runSubagentTurn(subagent, name, payload));
|
|
73
|
+
},
|
|
74
|
+
}));
|
|
75
|
+
const stop = tool({
|
|
76
|
+
name: `stop_${key}`,
|
|
77
|
+
description: `Stop the ${key} subagent identified by \`subagent\` (the handle returned by start_${key}). Use it when that instance's work is done.`,
|
|
78
|
+
inputSchema: z.object({ subagent: z.string() }),
|
|
79
|
+
outputSchema: z.string(),
|
|
80
|
+
execute: async ({ subagent }) => {
|
|
81
|
+
await currentToolCall(`stop_${key}`).runner.stopSubagent(subagent);
|
|
82
|
+
return `stopped subagent '${subagent}'`;
|
|
83
|
+
},
|
|
84
|
+
});
|
|
85
|
+
return [start, ...sends, stop];
|
|
86
|
+
}
|
|
87
|
+
const jsonClone = (value) => JSON.parse(JSON.stringify(value));
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export * from "./harness/agent-protocol/index.ts";
|
|
2
|
+
export * from "./harness/agent.ts";
|
|
3
|
+
export * from "./harness/subagent-toolset.ts";
|
|
4
|
+
export { AgentWorkflowRunner, ToolApprovalDenied, CallbackToolError, type HandlerDecl, type HandlerImpl, } from "./harness/agent-workflow.ts";
|
|
5
|
+
export { state, StateRef, HarnessStateError, type StateDecl } from "./harness/state/index.ts";
|
|
6
|
+
export { codeModeTool, codeModeTypeCheck, renderHostInterface, renderTypeCheckStubs, type CodeModeTool, FileTree, InMemoryFileSystem, Mount, osError, type FileStat, type FileSystem, FsError, type OsErrorType, type Seed, } from "./harness/code-mode/index.ts";
|
|
7
|
+
export { activityFilesystem, activityName, MAX_VIEW_BYTES, type ActivityFilesystem, } from "./harness/code-mode/activity-fs.ts";
|
|
8
|
+
export { FileIndex, indexTrack } from "./harness/code-mode/vfs.ts";
|
|
9
|
+
export { okfBundleVfsMount, vfsMount } from "./harness/code-mode/mount-decl.ts";
|
|
10
|
+
export { OKF_VERSION } from "./harness/code-mode/okf.ts";
|
|
11
|
+
export { okfCodeModeTool } from "./harness/code-mode/okf-tool.ts";
|
|
12
|
+
export { jevEvaluator, buildRequest as jevBuildRequest, decide as jevDecide, DEFAULT_JEV_MODEL, } from "./ai-sdks/jev/evaluator.ts";
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
// Workflow-safe authoring surface: import this from agent workflow modules.
|
|
2
|
+
export * from "./harness/agent-protocol/index.js";
|
|
3
|
+
export * from "./harness/agent.js";
|
|
4
|
+
export * from "./harness/subagent-toolset.js";
|
|
5
|
+
export { AgentWorkflowRunner, ToolApprovalDenied, CallbackToolError, } from "./harness/agent-workflow.js";
|
|
6
|
+
export { state, StateRef, HarnessStateError } from "./harness/state/index.js";
|
|
7
|
+
export { codeModeTool, codeModeTypeCheck, renderHostInterface, renderTypeCheckStubs, FileTree, InMemoryFileSystem, Mount, osError, FsError, } from "./harness/code-mode/index.js";
|
|
8
|
+
export { activityFilesystem, activityName, MAX_VIEW_BYTES, } from "./harness/code-mode/activity-fs.js";
|
|
9
|
+
export { FileIndex, indexTrack } from "./harness/code-mode/vfs.js";
|
|
10
|
+
export { okfBundleVfsMount, vfsMount } from "./harness/code-mode/mount-decl.js";
|
|
11
|
+
export { OKF_VERSION } from "./harness/code-mode/okf.js";
|
|
12
|
+
export { okfCodeModeTool } from "./harness/code-mode/okf-tool.js";
|
|
13
|
+
export { jevEvaluator, buildRequest as jevBuildRequest, decide as jevDecide, DEFAULT_JEV_MODEL, } from "./ai-sdks/jev/evaluator.js";
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
// Globals Temporal's workflow sandbox provides that the workflow-safe package uses. It compiles
|
|
2
|
+
// without the DOM's or Node's global types, so a browser or Node API fails to compile here;
|
|
3
|
+
// these few are declared as the sandbox has them.
|
|
4
|
+
|
|
5
|
+
declare class TextEncoder {
|
|
6
|
+
encode(input?: string): Uint8Array;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
declare class TextDecoder {
|
|
10
|
+
constructor(label?: string, options?: { fatal?: boolean });
|
|
11
|
+
decode(input?: Uint8Array): string;
|
|
12
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,98 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@temporalio/agent-harness",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"
|
|
5
|
-
"
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"license": "MIT",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"description": "Temporal-native agent harness for TypeScript: the workflow-safe authoring API for durable agents.",
|
|
7
|
+
"publishConfig": {
|
|
8
|
+
"access": "public",
|
|
9
|
+
"registry": "https://registry.npmjs.org/"
|
|
10
|
+
},
|
|
11
|
+
"sideEffects": false,
|
|
12
|
+
"exports": {
|
|
13
|
+
".": {
|
|
14
|
+
"types": "./dist/src/index.d.ts",
|
|
15
|
+
"default": "./dist/src/index.js"
|
|
16
|
+
},
|
|
17
|
+
"./harness/agent-protocol": {
|
|
18
|
+
"types": "./dist/src/harness/agent-protocol/index.d.ts",
|
|
19
|
+
"default": "./dist/src/harness/agent-protocol/index.js"
|
|
20
|
+
},
|
|
21
|
+
"./harness/agent-schema": {
|
|
22
|
+
"types": "./dist/src/harness/agent-schema.d.ts",
|
|
23
|
+
"default": "./dist/src/harness/agent-schema.js"
|
|
24
|
+
},
|
|
25
|
+
"./harness/agent-workflow": {
|
|
26
|
+
"types": "./dist/src/harness/agent-workflow.d.ts",
|
|
27
|
+
"default": "./dist/src/harness/agent-workflow.js"
|
|
28
|
+
},
|
|
29
|
+
"./harness/code-mode": {
|
|
30
|
+
"types": "./dist/src/harness/code-mode/index.d.ts",
|
|
31
|
+
"default": "./dist/src/harness/code-mode/index.js"
|
|
32
|
+
},
|
|
33
|
+
"./harness/code-mode/vfs": {
|
|
34
|
+
"types": "./dist/src/harness/code-mode/vfs.d.ts",
|
|
35
|
+
"default": "./dist/src/harness/code-mode/vfs.js"
|
|
36
|
+
},
|
|
37
|
+
"./harness/code-mode/okf": {
|
|
38
|
+
"types": "./dist/src/harness/code-mode/okf.d.ts",
|
|
39
|
+
"default": "./dist/src/harness/code-mode/okf.js"
|
|
40
|
+
},
|
|
41
|
+
"./ai-sdks/google-genai-plugin": {
|
|
42
|
+
"types": "./dist/src/ai-sdks/google-genai-plugin/index.d.ts",
|
|
43
|
+
"default": "./dist/src/ai-sdks/google-genai-plugin/index.js"
|
|
44
|
+
},
|
|
45
|
+
"./ai-sdks/jev/evaluator": {
|
|
46
|
+
"types": "./dist/src/ai-sdks/jev/evaluator.d.ts",
|
|
47
|
+
"default": "./dist/src/ai-sdks/jev/evaluator.js"
|
|
48
|
+
},
|
|
49
|
+
"./ai-sdks/openai-agents-harness": {
|
|
50
|
+
"types": "./dist/src/ai-sdks/openai-agents-harness.d.ts",
|
|
51
|
+
"default": "./dist/src/ai-sdks/openai-agents-harness.js"
|
|
52
|
+
}
|
|
53
|
+
},
|
|
54
|
+
"files": [
|
|
55
|
+
"dist/src"
|
|
56
|
+
],
|
|
57
|
+
"dependencies": {
|
|
58
|
+
"@standard-schema/spec": "^1.1.0",
|
|
59
|
+
"@temporalio/agent-harness-client": "^0.1.0",
|
|
60
|
+
"@temporalio/common": "^1.24.0",
|
|
61
|
+
"@temporalio/workflow": "^1.24.0",
|
|
62
|
+
"@temporalio/workflow-streams": "^1.24.0",
|
|
63
|
+
"immer": "^11.1.18",
|
|
64
|
+
"zod": "^4.6.5"
|
|
65
|
+
},
|
|
66
|
+
"peerDependencies": {
|
|
67
|
+
"@google/genai": "^2.31.0",
|
|
68
|
+
"@openai/agents-core": "~0.14.3",
|
|
69
|
+
"@temporalio/openai-agents": "^1.24.0"
|
|
70
|
+
},
|
|
71
|
+
"peerDependenciesMeta": {
|
|
72
|
+
"@google/genai": {
|
|
73
|
+
"optional": true
|
|
74
|
+
},
|
|
75
|
+
"@openai/agents-core": {
|
|
76
|
+
"optional": true
|
|
77
|
+
},
|
|
78
|
+
"@temporalio/openai-agents": {
|
|
79
|
+
"optional": true
|
|
80
|
+
}
|
|
81
|
+
},
|
|
82
|
+
"devDependencies": {
|
|
83
|
+
"@google/genai": "^2.31.0",
|
|
84
|
+
"@openai/agents-core": "~0.14.3",
|
|
85
|
+
"@temporalio/openai-agents": "^1.24.0",
|
|
86
|
+
"@types/node": "^22.20.4",
|
|
87
|
+
"typescript": "^7.0.2"
|
|
88
|
+
},
|
|
89
|
+
"repository": {
|
|
90
|
+
"type": "git",
|
|
91
|
+
"url": "git+https://github.com/temporal-community/temporal-agent-harness.git",
|
|
92
|
+
"directory": "typescript/packages/core"
|
|
93
|
+
},
|
|
94
|
+
"scripts": {
|
|
95
|
+
"build": "tsc -p tsconfig.json --outDir dist",
|
|
96
|
+
"typecheck": "tsc -p tsconfig.json --noEmit"
|
|
97
|
+
}
|
|
6
98
|
}
|