@inflexa-ai/harness 0.46.0 → 0.46.2
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/dist/app/chat-turn-workflow.js +5 -6
- package/dist/lib/hooks.d.ts +9 -0
- package/dist/lib/hooks.js +6 -1
- package/dist/loop/sub-agent.js +5 -6
- package/dist/loop/turn-credentials.d.ts +5 -0
- package/dist/loop/turn-credentials.js +17 -0
- package/dist/providers/request-headers.js +12 -3
- package/dist/testing.d.ts +2 -0
- package/dist/testing.js +6 -1
- package/package.json +1 -1
|
@@ -29,7 +29,7 @@ import { askPartOf } from "../tools/approval/gateway.js";
|
|
|
29
29
|
import { stepUuid } from "../tools/workspace/edit-file.js";
|
|
30
30
|
import { openThread, readTurnContext, runOpenTurn, userTextOf, } from "./chat-turn.js";
|
|
31
31
|
import { CHAT_STREAM_KEY, createTurnStreamWriter, readChatTurn } from "../loop/chat-stream.js";
|
|
32
|
-
import { credentialGuard, turnCredentials, withRootTurn } from "../loop/turn-credentials.js";
|
|
32
|
+
import { credentialGuard, liveAuth, turnCredentials, withRootTurn } from "../loop/turn-credentials.js";
|
|
33
33
|
import { assembleMessages, loadHistoryView, sanitizedUserMessage } from "./message-assembly.js";
|
|
34
34
|
/** The name of the turn workflow. */
|
|
35
35
|
export const CHAT_TURN_WORKFLOW = "chat-turn";
|
|
@@ -270,11 +270,10 @@ async function runTurnWorkflow(deps, input, signal) {
|
|
|
270
270
|
const session = {
|
|
271
271
|
identity: { user: input.userId },
|
|
272
272
|
scope: { kind: "analysis", analysisId: input.analysisId, threadId: input.threadId },
|
|
273
|
-
// The credential comes at each read, thus a call after a resume uses the
|
|
274
|
-
// turn with no credential makes no model call, thus no adapter reads the
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
},
|
|
273
|
+
// The credential comes at each read, also through a copy of the session, thus a call after a resume uses the
|
|
274
|
+
// credential that the resume put. A turn with no credential makes no model call, thus no adapter reads the
|
|
275
|
+
// empty value.
|
|
276
|
+
auth: liveAuth(() => turnCredentials.of(input.turnId)),
|
|
278
277
|
};
|
|
279
278
|
const ran = await runOpenTurn(deps, { analysisId: input.analysisId, threadId: input.threadId, userInput: input.userText }, { kind: "ok", threadType: input.threadType, history: view, userMessage: opening.userMessage, contextRecords: opening.contextRecords }, agent.value, { session, author: input.author, startedAtMs: input.startedAtMs, promptCache: input.promptCache }, transport);
|
|
280
279
|
const { outcome } = ran;
|
package/dist/lib/hooks.d.ts
CHANGED
|
@@ -24,6 +24,11 @@ export type GateFailure = {
|
|
|
24
24
|
readonly reason: string;
|
|
25
25
|
/** True when the host asks the harness to suspend the work in place of a failure. */
|
|
26
26
|
readonly suspend: boolean;
|
|
27
|
+
/**
|
|
28
|
+
* True when the host refuses the credential of the caller: it is expired, revoked, or absent. A person repairs
|
|
29
|
+
* it, thus a work that can pause waits for a resume. The suspend flag wins over it.
|
|
30
|
+
*/
|
|
31
|
+
readonly credential?: boolean;
|
|
27
32
|
};
|
|
28
33
|
/**
|
|
29
34
|
* The settlement a gate's `ResultAsync` resolves to, read structurally. An embedder whose neverthrow is
|
|
@@ -54,6 +59,10 @@ export type GateRefusal = {
|
|
|
54
59
|
readonly kind: "suspended";
|
|
55
60
|
readonly gate: GateName;
|
|
56
61
|
readonly reason: string;
|
|
62
|
+
} | {
|
|
63
|
+
readonly kind: "credential";
|
|
64
|
+
readonly gate: GateName;
|
|
65
|
+
readonly reason: string;
|
|
57
66
|
};
|
|
58
67
|
/**
|
|
59
68
|
* The host can build its `Result` with its own copy of neverthrow. The helper
|
package/dist/lib/hooks.js
CHANGED
|
@@ -30,6 +30,11 @@ async function settlementOf(result) {
|
|
|
30
30
|
}
|
|
31
31
|
return await result;
|
|
32
32
|
}
|
|
33
|
+
function refusalKindOf(failure) {
|
|
34
|
+
if (failure.suspend)
|
|
35
|
+
return "suspended";
|
|
36
|
+
return failure.credential === true ? "credential" : "failed";
|
|
37
|
+
}
|
|
33
38
|
/**
|
|
34
39
|
* The host can build its `Result` with its own copy of neverthrow. The helper
|
|
35
40
|
* gives the outcome again as a `Result` of the copy of the harness. Thus the
|
|
@@ -41,7 +46,7 @@ export function passGate(gate, result) {
|
|
|
41
46
|
const settled = await settlementOf(result);
|
|
42
47
|
if (settlementOk(settled))
|
|
43
48
|
return ok(settled.value);
|
|
44
|
-
return err({ kind: settled.error
|
|
49
|
+
return err({ kind: refusalKindOf(settled.error), gate, reason: settled.error.reason });
|
|
45
50
|
})());
|
|
46
51
|
}
|
|
47
52
|
export async function deliverNotice(log, notice, result) {
|
package/dist/loop/sub-agent.js
CHANGED
|
@@ -23,7 +23,7 @@ import { DATA_PROFILE_RUN_LITERAL } from "../contracts/data-profile.js";
|
|
|
23
23
|
import { createSubAgentActivityTracker } from "../contracts/subagent-activity.js";
|
|
24
24
|
import { CHAT_STREAM_KEY, createTurnStreamWriter, SUB_AGENT_STREAM_PART } from "./chat-stream.js";
|
|
25
25
|
import { addChatUsage } from "./metrics.js";
|
|
26
|
-
import { credentialGuard, currentRootTurn, turnCredentials, withRootTurn } from "./turn-credentials.js";
|
|
26
|
+
import { credentialGuard, currentRootTurn, liveAuth, turnCredentials, withRootTurn } from "./turn-credentials.js";
|
|
27
27
|
/** The work of a child, from its input. A data profile runs under one constant frame and starts no child, thus it gives none. */
|
|
28
28
|
function workOf(input) {
|
|
29
29
|
const { analysisId, threadId } = input.scope;
|
|
@@ -59,11 +59,10 @@ export function registerSubAgent(name, body, pauses) {
|
|
|
59
59
|
scope: input.scope,
|
|
60
60
|
provenance: input.provenance,
|
|
61
61
|
runFrame: input.runFrame,
|
|
62
|
-
// The credential comes at each read, thus a call after a resume of the
|
|
63
|
-
// resume put. A child with no credential makes no model call, thus no
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
},
|
|
62
|
+
// The credential comes at each read, also through a copy of the session, thus a call after a resume of the
|
|
63
|
+
// turn uses the credential that the resume put. A child with no credential makes no model call, thus no
|
|
64
|
+
// adapter reads the empty value.
|
|
65
|
+
auth: liveAuth(authOf),
|
|
67
66
|
};
|
|
68
67
|
// The input holds the provenance of the caller. The body of the sub-agent extends it.
|
|
69
68
|
const caller = { agentId: input.provenance.agentId, callPath: [...input.provenance.callPath] };
|
|
@@ -17,6 +17,11 @@ export declare const turnCredentials: {
|
|
|
17
17
|
/** The function of the host for a turn that this process did not open. */
|
|
18
18
|
setHostCredential(credentialFor: ((turnId: string) => AuthContext | undefined) | undefined): void;
|
|
19
19
|
};
|
|
20
|
+
/**
|
|
21
|
+
* The `auth` of a session whose credential can come later: each read goes to `current`, and no credential reads as an
|
|
22
|
+
* empty object. A copy of the session keeps this object, thus each copy sees the credential that a resume puts.
|
|
23
|
+
*/
|
|
24
|
+
export declare function liveAuth(current: () => AuthContext | undefined): AuthContext;
|
|
20
25
|
/** Run the body of a turn, or of one of its sub-agents, with the id of its root turn in its context. */
|
|
21
26
|
export declare function withRootTurn<T>(turnId: string, body: () => Promise<T>): Promise<T>;
|
|
22
27
|
/** The id of the root chat turn of the body that runs, or `undefined` outside a chat turn. */
|
|
@@ -29,6 +29,23 @@ export const turnCredentials = {
|
|
|
29
29
|
hostCredential = credentialFor;
|
|
30
30
|
},
|
|
31
31
|
};
|
|
32
|
+
/**
|
|
33
|
+
* The `auth` of a session whose credential can come later: each read goes to `current`, and no credential reads as an
|
|
34
|
+
* empty object. A copy of the session keeps this object, thus each copy sees the credential that a resume puts.
|
|
35
|
+
*/
|
|
36
|
+
export function liveAuth(current) {
|
|
37
|
+
const credential = () => current() ?? {};
|
|
38
|
+
return new Proxy({}, {
|
|
39
|
+
get: (_shell, key) => Reflect.get(credential(), key),
|
|
40
|
+
has: (_shell, key) => Reflect.has(credential(), key),
|
|
41
|
+
ownKeys: () => Reflect.ownKeys(credential()),
|
|
42
|
+
getOwnPropertyDescriptor: (_shell, key) => {
|
|
43
|
+
const descriptor = Reflect.getOwnPropertyDescriptor(credential(), key);
|
|
44
|
+
// A proxy can report a key that its shell does not hold only as configurable.
|
|
45
|
+
return descriptor === undefined ? undefined : { ...descriptor, configurable: true };
|
|
46
|
+
},
|
|
47
|
+
});
|
|
48
|
+
}
|
|
32
49
|
/** Run the body of a turn, or of one of its sub-agents, with the id of its root turn in its context. */
|
|
33
50
|
export function withRootTurn(turnId, body) {
|
|
34
51
|
return rootTurn.run(turnId, body);
|
|
@@ -16,7 +16,16 @@ export function requestHeadersFor(hook, session) {
|
|
|
16
16
|
}
|
|
17
17
|
export function headersRefusalError(refusal, workload) {
|
|
18
18
|
const message = `The request headers hook refused the model call for ${workload}: ${refusal.reason}`;
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
19
|
+
switch (refusal.kind) {
|
|
20
|
+
case "suspended":
|
|
21
|
+
return { type: "suspend", retryable: false, reason: refusal.reason, message };
|
|
22
|
+
case "credential":
|
|
23
|
+
return { type: "auth", retryable: false, message };
|
|
24
|
+
case "failed":
|
|
25
|
+
return { type: "provider", retryable: false, message };
|
|
26
|
+
default: {
|
|
27
|
+
const exhaustive = refusal;
|
|
28
|
+
return exhaustive;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
22
31
|
}
|
package/dist/testing.d.ts
CHANGED
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
* embedder imports it from `@inflexa-ai/harness/testing`.
|
|
8
8
|
*/
|
|
9
9
|
import type { LanguageModelV4CallOptions, SharedV4ProviderMetadata } from "@ai-sdk/provider";
|
|
10
|
+
import type { ResolveRequestHeaders } from "./providers/request-headers.js";
|
|
10
11
|
export { inProcessChatTurns } from "./app/in-process-turn.js";
|
|
11
12
|
import type { ChatProvider, ChatUsage, ProviderCapabilities } from "./providers/types.js";
|
|
12
13
|
export type ScriptedPart = {
|
|
@@ -57,4 +58,5 @@ export declare function callTools(...calls: readonly ScriptedPart[]): ScriptedRe
|
|
|
57
58
|
export declare function scriptedModel(script: readonly ScriptStep[], options?: {
|
|
58
59
|
readonly capabilities?: Partial<ProviderCapabilities>;
|
|
59
60
|
readonly maxRetries?: number;
|
|
61
|
+
readonly resolveRequestHeaders?: ResolveRequestHeaders;
|
|
60
62
|
}): ScriptedModel;
|
package/dist/testing.js
CHANGED
|
@@ -91,6 +91,11 @@ export function scriptedModel(script, options = {}) {
|
|
|
91
91
|
return { stream: convertArrayToReadableStream(streamOf(entry, index)) };
|
|
92
92
|
},
|
|
93
93
|
});
|
|
94
|
-
const provider = createAiSdkProvider({
|
|
94
|
+
const provider = createAiSdkProvider({
|
|
95
|
+
model,
|
|
96
|
+
capabilities: options.capabilities,
|
|
97
|
+
maxRetries: options.maxRetries ?? 0,
|
|
98
|
+
resolveRequestHeaders: options.resolveRequestHeaders,
|
|
99
|
+
});
|
|
95
100
|
return { provider, calls };
|
|
96
101
|
}
|
package/package.json
CHANGED