@kuralle-agents/core 0.13.0 → 0.14.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 +4 -4
- package/dist/ai-sdk/uiMessageStream.d.ts +2 -2
- package/dist/ai-sdk/uiMessageStream.js +42 -29
- package/dist/capabilities/LivePromptAssembler.d.ts +1 -1
- package/dist/capabilities/LivePromptAssembler.js +1 -1
- package/dist/capabilities/index.d.ts +1 -1
- package/dist/eval/EvalRunner.js +4 -4
- package/dist/eval/simulation.js +4 -3
- package/dist/events/TurnHandle.d.ts +3 -3
- package/dist/flow/collectUntilComplete.js +4 -4
- package/dist/flow/degrade.js +8 -4
- package/dist/flow/emitInteractive.d.ts +2 -2
- package/dist/flow/emitInteractive.js +6 -3
- package/dist/flow/extraction.d.ts +2 -2
- package/dist/flow/extraction.js +10 -4
- package/dist/flow/reduceTransition.d.ts +2 -2
- package/dist/flow/reduceTransition.js +7 -3
- package/dist/flow/runFlow.js +34 -12
- package/dist/foundation/AgentStateController.d.ts +1 -1
- package/dist/foundation/ToolExecutor.d.ts +2 -2
- package/dist/foundation/index.d.ts +0 -11
- package/dist/foundation/index.js +0 -5
- package/dist/index.d.ts +9 -14
- package/dist/index.js +5 -6
- package/dist/outcomes/streamPart.d.ts +2 -2
- package/dist/outcomes/streamPart.js +2 -3
- package/dist/prompts/PromptBuilder.d.ts +3 -3
- package/dist/prompts/PromptBuilder.js +6 -6
- package/dist/prompts/index.d.ts +1 -1
- package/dist/prompts/types.d.ts +7 -2
- package/dist/runtime/InMemoryRetrievalCache.d.ts +1 -1
- package/dist/runtime/KnowledgeProvider.d.ts +3 -2
- package/dist/runtime/KnowledgeProvider.js +54 -30
- package/dist/runtime/Runtime.d.ts +3 -1
- package/dist/runtime/Runtime.js +84 -30
- package/dist/runtime/TraceRecorder.d.ts +22 -4
- package/dist/runtime/TraceRecorder.js +65 -29
- package/dist/runtime/buildAgentToolSurface.js +1 -1
- package/dist/runtime/channels/TextDriver.js +12 -9
- package/dist/runtime/channels/executeModelTool.d.ts +6 -0
- package/dist/runtime/channels/executeModelTool.js +70 -10
- package/dist/runtime/channels/extractionTurn.js +1 -1
- package/dist/runtime/channels/index.d.ts +0 -3
- package/dist/runtime/channels/index.js +0 -6
- package/dist/runtime/channels/streaming/hostControlSpeak.js +5 -1
- package/dist/runtime/channels/streaming/speakGated.js +24 -12
- package/dist/runtime/citations/index.d.ts +3 -3
- package/dist/runtime/closeRun.js +3 -2
- package/dist/runtime/controlFlowSignal.d.ts +24 -0
- package/dist/runtime/controlFlowSignal.js +29 -0
- package/dist/runtime/ctx.d.ts +3 -4
- package/dist/runtime/ctx.js +39 -5
- package/dist/runtime/durable/RunStore.js +1 -1
- package/dist/runtime/grounding/gather.d.ts +1 -1
- package/dist/runtime/grounding/inMemoryKnowledge.d.ts +1 -1
- package/dist/runtime/grounding/knowledge.d.ts +1 -1
- package/dist/runtime/hostLoop.js +26 -6
- package/dist/runtime/index.d.ts +1 -2
- package/dist/runtime/index.js +1 -1
- package/dist/runtime/outcomeMarking.d.ts +2 -2
- package/dist/runtime/outcomeMarking.js +2 -1
- package/dist/runtime/policies/agentTurn.d.ts +1 -1
- package/dist/runtime/runHookSafely.d.ts +3 -0
- package/dist/runtime/runHookSafely.js +19 -0
- package/dist/scheduler/index.d.ts +2 -2
- package/dist/scheduler/index.js +1 -1
- package/dist/services/TracingService.js +2 -3
- package/dist/skills/collectSkills.d.ts +11 -3
- package/dist/skills/collectSkills.js +51 -8
- package/dist/skills/compositeSkillStore.d.ts +26 -0
- package/dist/skills/compositeSkillStore.js +54 -0
- package/dist/skills/defineSkill.d.ts +21 -0
- package/dist/skills/defineSkill.js +26 -0
- package/dist/skills/fsSkillStore.d.ts +3 -0
- package/dist/skills/fsSkillStore.js +79 -0
- package/dist/skills/index.d.ts +4 -0
- package/dist/skills/index.js +4 -0
- package/dist/skills/parseSkillFrontmatter.d.ts +14 -0
- package/dist/skills/parseSkillFrontmatter.js +183 -0
- package/dist/skills/wireAgentSkills.d.ts +2 -1
- package/dist/skills/wireAgentSkills.js +2 -2
- package/dist/testing/mocks.d.ts +3 -3
- package/dist/testing/mocks.js +7 -3
- package/dist/tools/controlResults.d.ts +14 -0
- package/dist/tools/controlResults.js +15 -0
- package/dist/tools/effect/ToolExecutor.d.ts +6 -0
- package/dist/tools/effect/ToolExecutor.js +71 -35
- package/dist/tools/effect/defineTool.d.ts +9 -0
- package/dist/tools/effect/defineTool.js +1 -0
- package/dist/tools/effect/errors.d.ts +2 -5
- package/dist/tools/effect/errors.js +2 -5
- package/dist/tools/index.d.ts +0 -1
- package/dist/tools/index.js +0 -1
- package/dist/types/effectTool.d.ts +2 -0
- package/dist/types/flow.d.ts +1 -1
- package/dist/types/guardrails.d.ts +5 -0
- package/dist/types/hooks.d.ts +2 -2
- package/dist/types/index.d.ts +25 -16
- package/dist/types/index.js +8 -13
- package/dist/types/knowledge.d.ts +213 -0
- package/dist/types/processors.d.ts +0 -58
- package/dist/types/run-context.d.ts +3 -7
- package/dist/types/runtime.d.ts +3 -74
- package/dist/types/session.d.ts +1 -1
- package/dist/types/skills.d.ts +15 -1
- package/dist/types/stream.d.ts +133 -71
- package/dist/types/stream.js +34 -1
- package/dist/types/trace.d.ts +2 -0
- package/guides/AGENTS.md +3 -3
- package/guides/FLOWS.md +1 -1
- package/guides/GETTING_STARTED.md +1 -1
- package/guides/RUNTIME.md +20 -15
- package/guides/TOOLS.md +1 -1
- package/package.json +3 -13
- package/dist/foundation/AgentDefinition.d.ts +0 -17
- package/dist/foundation/ConversationEventLog.d.ts +0 -71
- package/dist/foundation/ConversationEventLog.js +0 -1
- package/dist/foundation/ConversationState.d.ts +0 -33
- package/dist/foundation/ConversationState.js +0 -1
- package/dist/foundation/DefaultConversationEventLog.d.ts +0 -27
- package/dist/foundation/DefaultConversationEventLog.js +0 -205
- package/dist/foundation/DefaultConversationState.d.ts +0 -36
- package/dist/foundation/DefaultConversationState.js +0 -103
- package/dist/foundation/DefaultToolExecutor.d.ts +0 -57
- package/dist/foundation/DefaultToolExecutor.js +0 -132
- package/dist/foundation/createFoundation.d.ts +0 -32
- package/dist/foundation/createFoundation.js +0 -33
- package/dist/hooks/HookRunner.d.ts +0 -37
- package/dist/hooks/HookRunner.js +0 -111
- package/dist/hooks/builtin/logging.d.ts +0 -5
- package/dist/hooks/builtin/logging.js +0 -98
- package/dist/hooks/builtin/metrics.d.ts +0 -7
- package/dist/hooks/builtin/metrics.js +0 -60
- package/dist/hooks/builtin/observability.d.ts +0 -20
- package/dist/hooks/builtin/observability.js +0 -572
- package/dist/hooks/helpers.d.ts +0 -22
- package/dist/hooks/helpers.js +0 -183
- package/dist/hooks/index.d.ts +0 -8
- package/dist/hooks/index.js +0 -6
- package/dist/realtime/RealtimeAudioClient.d.ts +0 -166
- package/dist/realtime/RealtimeAudioClient.js +0 -14
- package/dist/realtime/RealtimeSessionHandle.d.ts +0 -6
- package/dist/realtime/RealtimeSessionHandle.js +0 -1
- package/dist/realtime/index.d.ts +0 -2
- package/dist/realtime/index.js +0 -1
- package/dist/runtime/channels/VoiceDriver.d.ts +0 -31
- package/dist/runtime/channels/VoiceDriver.js +0 -321
- package/dist/runtime/channels/voiceTools.d.ts +0 -9
- package/dist/runtime/channels/voiceTools.js +0 -51
- package/dist/tools/errorHandling.d.ts +0 -35
- package/dist/tools/errorHandling.js +0 -145
- package/dist/types/voice.d.ts +0 -517
- package/dist/types/voice.js +0 -14
- package/dist/utils/isRecord.d.ts +0 -1
- package/dist/utils/isRecord.js +0 -3
- package/dist/utils/messageNormalization.d.ts +0 -2
- package/dist/utils/messageNormalization.js +0 -137
- /package/dist/{foundation/AgentDefinition.js → types/knowledge.js} +0 -0
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { SuspendError } from './durable/RunStore.js';
|
|
2
|
+
import { ToolApprovalDeniedError } from '../tools/effect/errors.js';
|
|
3
|
+
/**
|
|
4
|
+
* The run is not finished: stop the turn, keep the journal, resume when a signal arrives.
|
|
5
|
+
* It unwinds the stack like an error but is not a failure, so it must never be reported to
|
|
6
|
+
* the model as a tool error, shown to the user as an error, or journaled as one.
|
|
7
|
+
*
|
|
8
|
+
* Only `SuspendError` qualifies. An approval *denial* is deliberately not a signal — see
|
|
9
|
+
* `isApprovalDenial`.
|
|
10
|
+
*/
|
|
11
|
+
export function isControlFlowSignal(error) {
|
|
12
|
+
return error instanceof SuspendError;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* A human answered "no". Neither a failure nor a suspend — the opposite of a suspend, in
|
|
16
|
+
* fact: a suspend defers the decision, a denial resolves it.
|
|
17
|
+
*
|
|
18
|
+
* Two rules follow, and they differ by who is driving the call:
|
|
19
|
+
*
|
|
20
|
+
* - It must never be degraded into "something went wrong on my side". Nothing went wrong;
|
|
21
|
+
* saying so would be a lie to the user.
|
|
22
|
+
* - On the **model** path it becomes a tool result, because there is no author code to
|
|
23
|
+
* catch it and the agent needs to be able to tell the user the request was declined.
|
|
24
|
+
* On the **flow** path it keeps propagating, because an `action` node's author chose to
|
|
25
|
+
* call the tool and can catch it or branch on `ctx.approve()` instead.
|
|
26
|
+
*/
|
|
27
|
+
export function isApprovalDenial(error) {
|
|
28
|
+
return error instanceof ToolApprovalDeniedError;
|
|
29
|
+
}
|
package/dist/runtime/ctx.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { LanguageModel } from 'ai';
|
|
2
2
|
import type { Session } from '../types/session.js';
|
|
3
|
-
import type { EffectToolExecutor,
|
|
4
|
-
import type {
|
|
3
|
+
import type { EffectToolExecutor, MemoryService, AutoRetrieveProvider, RunContext } from '../types/run-context.js';
|
|
4
|
+
import type { StreamPart } from '../types/stream.js';
|
|
5
5
|
import type { RefinementCapability } from '../capabilities/RefinementCapability.js';
|
|
6
6
|
import type { ValidationCapability } from '../capabilities/ValidationCapability.js';
|
|
7
7
|
import type { InputProcessor, OutputProcessor } from '../types/processors.js';
|
|
@@ -19,7 +19,6 @@ export interface CtxDeps {
|
|
|
19
19
|
runStore: RunStore;
|
|
20
20
|
steps: StepRecord[];
|
|
21
21
|
toolExecutor: EffectToolExecutor;
|
|
22
|
-
hookRunner?: HookRunner;
|
|
23
22
|
model: LanguageModel;
|
|
24
23
|
controlModel?: LanguageModel;
|
|
25
24
|
outOfBandControl?: boolean;
|
|
@@ -34,7 +33,7 @@ export interface CtxDeps {
|
|
|
34
33
|
bargeIn?: AbortSignal;
|
|
35
34
|
abortSignal?: AbortSignal;
|
|
36
35
|
clock?: EffectClock;
|
|
37
|
-
emit?: (part:
|
|
36
|
+
emit?: (part: StreamPart) => void;
|
|
38
37
|
}
|
|
39
38
|
export declare function createRunContext(deps: CtxDeps): Promise<RunContext>;
|
|
40
39
|
export {};
|
package/dist/runtime/ctx.js
CHANGED
|
@@ -14,6 +14,22 @@ function makeCtx(deps) {
|
|
|
14
14
|
const emit = deps.emit ?? (() => { });
|
|
15
15
|
// Mutable holder: handoff reassigns runCtx.toolExecutor; ctx.tool must see the swap.
|
|
16
16
|
const toolExecutorHolder = { executor: deps.toolExecutor };
|
|
17
|
+
let pendingAppendTail = Promise.resolve();
|
|
18
|
+
const serializePendingAppend = (operation) => {
|
|
19
|
+
const previous = pendingAppendTail;
|
|
20
|
+
let release;
|
|
21
|
+
pendingAppendTail = new Promise((resolve) => {
|
|
22
|
+
release = resolve;
|
|
23
|
+
});
|
|
24
|
+
return previous.then(async () => {
|
|
25
|
+
try {
|
|
26
|
+
return await operation();
|
|
27
|
+
}
|
|
28
|
+
finally {
|
|
29
|
+
release();
|
|
30
|
+
}
|
|
31
|
+
});
|
|
32
|
+
};
|
|
17
33
|
const consumeCallsite = () => {
|
|
18
34
|
const site = String(effectOrdinal);
|
|
19
35
|
effectOrdinal += 1;
|
|
@@ -85,9 +101,28 @@ function makeCtx(deps) {
|
|
|
85
101
|
}
|
|
86
102
|
}
|
|
87
103
|
const isRetry = hit?.status === 'running';
|
|
88
|
-
const stepIndex = options?.index ?? hit?.index
|
|
104
|
+
const stepIndex = options?.index ?? hit?.index;
|
|
89
105
|
if (!isRetry) {
|
|
90
|
-
|
|
106
|
+
const append = async () => {
|
|
107
|
+
let index = stepIndex;
|
|
108
|
+
if (index === undefined) {
|
|
109
|
+
if (deps.runStore.reserveSteps) {
|
|
110
|
+
index = (await deps.runStore.reserveSteps(deps.runState.runId, 1))[0];
|
|
111
|
+
}
|
|
112
|
+
else {
|
|
113
|
+
// ctx.tool owns ordinal assignment: use the store's atomic reservation when available,
|
|
114
|
+
// and serialize legacy stores that do not expose reserveSteps within this context.
|
|
115
|
+
index = steps.length;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
await appendPendingStep(key, kind, name, index);
|
|
119
|
+
};
|
|
120
|
+
if (stepIndex === undefined && !deps.runStore.reserveSteps) {
|
|
121
|
+
await serializePendingAppend(append);
|
|
122
|
+
}
|
|
123
|
+
else {
|
|
124
|
+
await append();
|
|
125
|
+
}
|
|
91
126
|
}
|
|
92
127
|
let result;
|
|
93
128
|
try {
|
|
@@ -131,7 +166,7 @@ function makeCtx(deps) {
|
|
|
131
166
|
deps.runState.status = 'paused';
|
|
132
167
|
deps.runState.updatedAt = Date.now();
|
|
133
168
|
await deps.runStore.putRunState(deps.runState);
|
|
134
|
-
emit({ type: 'paused', waitingFor: signalName });
|
|
169
|
+
emit({ channel: 'internal', type: 'paused', payload: { waitingFor: signalName } });
|
|
135
170
|
throw new SuspendError(signalName);
|
|
136
171
|
};
|
|
137
172
|
const pauseEffect = async (signalName, meta) => {
|
|
@@ -160,7 +195,6 @@ function makeCtx(deps) {
|
|
|
160
195
|
set toolExecutor(executor) {
|
|
161
196
|
toolExecutorHolder.executor = executor;
|
|
162
197
|
},
|
|
163
|
-
hookRunner: deps.hookRunner ?? {},
|
|
164
198
|
model: deps.model,
|
|
165
199
|
controlModel: deps.controlModel ?? deps.model,
|
|
166
200
|
outOfBandControl: deps.outOfBandControl ?? false,
|
|
@@ -223,7 +257,7 @@ function makeCtx(deps) {
|
|
|
223
257
|
toolCtx: options?.toolCtx,
|
|
224
258
|
});
|
|
225
259
|
if (def?.replay === false) {
|
|
226
|
-
const auditKey = `${key}:${steps.length}`;
|
|
260
|
+
const auditKey = `${key}:${steps.length}:${options?.index ?? callsite}`;
|
|
227
261
|
return replayOrExecute(auditKey, 'tool', name, executeTool, { index: options?.index });
|
|
228
262
|
}
|
|
229
263
|
return replayOrExecute(key, 'tool', name, executeTool, { index: options?.index });
|
|
@@ -3,7 +3,7 @@ export class LogConflictError extends Error {
|
|
|
3
3
|
expectedIndex;
|
|
4
4
|
actualIndex;
|
|
5
5
|
constructor(runId, expectedIndex, actualIndex) {
|
|
6
|
-
super(`Log conflict for run ${runId}: expected append at index ${expectedIndex}, current length is ${actualIndex}
|
|
6
|
+
super(`Log conflict for run ${runId}: expected append at index ${expectedIndex}, current length is ${actualIndex}. For parallel durable effects, use ctx.tool directly or reserve callsites with ctx.reserveCallsites(count) before supplying explicit indices.`);
|
|
7
7
|
this.name = 'LogConflictError';
|
|
8
8
|
this.runId = runId;
|
|
9
9
|
this.expectedIndex = expectedIndex;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { SourceRef } from '../../types/
|
|
1
|
+
import type { SourceRef } from '../../types/knowledge.js';
|
|
2
2
|
import type { GatherScope, RunContext } from '../../types/run-context.js';
|
|
3
3
|
export type { GatherScope } from '../../types/run-context.js';
|
|
4
4
|
export interface GatherResult {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { KnowledgeProviderConfig, KnowledgeRetrieverAdapter } from '../../types/
|
|
1
|
+
import type { KnowledgeProviderConfig, KnowledgeRetrieverAdapter } from '../../types/knowledge.js';
|
|
2
2
|
export interface InMemoryKnowledgeDocument {
|
|
3
3
|
id?: string;
|
|
4
4
|
text: string;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { AgentConfig } from '../../types/agentConfig.js';
|
|
2
2
|
import type { AutoRetrieveProvider } from '../../types/run-context.js';
|
|
3
3
|
import type { AnyTool } from '../../types/effectTool.js';
|
|
4
|
-
import type { KnowledgeProviderConfig } from '../../types/
|
|
4
|
+
import type { KnowledgeProviderConfig } from '../../types/knowledge.js';
|
|
5
5
|
import { KnowledgeProvider } from '../KnowledgeProvider.js';
|
|
6
6
|
export declare function buildKnowledgeProvider(config: KnowledgeProviderConfig): KnowledgeProvider;
|
|
7
7
|
export declare function buildAutoRetrieveProvider(provider: KnowledgeProvider, agent: AgentConfig): AutoRetrieveProvider | undefined;
|
package/dist/runtime/hostLoop.js
CHANGED
|
@@ -36,7 +36,7 @@ export async function hostLoop(options) {
|
|
|
36
36
|
return { kind: 'paused' };
|
|
37
37
|
}
|
|
38
38
|
if (error instanceof LimitsExceededError) {
|
|
39
|
-
ctx.emit({ type: 'error', error: error.message });
|
|
39
|
+
ctx.emit({ channel: 'client', type: 'error', payload: { error: error.message } });
|
|
40
40
|
return { kind: 'ended', reason: error.message };
|
|
41
41
|
}
|
|
42
42
|
throw error;
|
|
@@ -160,7 +160,11 @@ function guardVerdictToTelemetryVerdict(verdict) {
|
|
|
160
160
|
return 'keep';
|
|
161
161
|
}
|
|
162
162
|
function emitHostGuardTelemetry(ctx, data) {
|
|
163
|
-
ctx.emit({
|
|
163
|
+
ctx.emit({
|
|
164
|
+
channel: 'internal',
|
|
165
|
+
type: 'custom',
|
|
166
|
+
payload: { name: 'host-guard', data },
|
|
167
|
+
});
|
|
164
168
|
}
|
|
165
169
|
function guardVerdictToControl(verdict, agent) {
|
|
166
170
|
const selection = verdictToSelection(verdict, agent);
|
|
@@ -174,7 +178,11 @@ function guardVerdictToControl(verdict, agent) {
|
|
|
174
178
|
}
|
|
175
179
|
async function executeHostControl(agent, run, driver, ctx, control) {
|
|
176
180
|
if (!control) {
|
|
177
|
-
ctx.emit({
|
|
181
|
+
ctx.emit({
|
|
182
|
+
channel: 'client',
|
|
183
|
+
type: 'error',
|
|
184
|
+
payload: { error: 'No valid host control target resolved' },
|
|
185
|
+
});
|
|
178
186
|
return { kind: 'ended', reason: 'dispatch_failed' };
|
|
179
187
|
}
|
|
180
188
|
if (control.type === 'enterFlow') {
|
|
@@ -182,18 +190,30 @@ async function executeHostControl(agent, run, driver, ctx, control) {
|
|
|
182
190
|
if (flow) {
|
|
183
191
|
return await runActiveFlow(flow, run, driver, ctx, agent);
|
|
184
192
|
}
|
|
185
|
-
ctx.emit({
|
|
193
|
+
ctx.emit({
|
|
194
|
+
channel: 'client',
|
|
195
|
+
type: 'error',
|
|
196
|
+
payload: { error: `Flow not found: ${control.flowName}` },
|
|
197
|
+
});
|
|
186
198
|
return { kind: 'ended', reason: 'flow_not_found' };
|
|
187
199
|
}
|
|
188
200
|
if (control.type === 'handoff') {
|
|
189
|
-
ctx.emit({
|
|
201
|
+
ctx.emit({
|
|
202
|
+
channel: 'internal',
|
|
203
|
+
type: 'handoff',
|
|
204
|
+
payload: { targetAgent: control.target, reason: control.reason },
|
|
205
|
+
});
|
|
190
206
|
return { kind: 'handoff', to: control.target, reason: control.reason };
|
|
191
207
|
}
|
|
192
208
|
if (control.type === 'end') {
|
|
193
209
|
return { kind: 'ended', reason: control.reason };
|
|
194
210
|
}
|
|
195
211
|
if (control.type === 'escalate') {
|
|
196
|
-
|
|
212
|
+
// No handoff part is emitted here. Runtime owns it: it emits one for any
|
|
213
|
+
// target in `terminalHandoffTargets` (default ['human']) on this exact
|
|
214
|
+
// return value. Emitting here too produced two handoff spans per escalation
|
|
215
|
+
// and a meaningless `human -> human` self-edge, which then mis-attributed
|
|
216
|
+
// every later span in the turn to `human`.
|
|
197
217
|
return { kind: 'handoff', to: 'human', reason: control.reason, category: control.category };
|
|
198
218
|
}
|
|
199
219
|
if (control.type === 'recover') {
|
package/dist/runtime/index.d.ts
CHANGED
|
@@ -3,8 +3,7 @@ export type { RuntimeLike } from './RuntimeLike.js';
|
|
|
3
3
|
export { TraceRecorder, runOnce, type TraceRecorderOptions } from './TraceRecorder.js';
|
|
4
4
|
export type { AgentSpan, AgentTrace, SpanKind } from '../types/trace.js';
|
|
5
5
|
export { SessionWorkingMemory } from './WorkingMemory.js';
|
|
6
|
-
export { TextDriver
|
|
7
|
-
export type { VoiceDriverConfig } from './channels/index.js';
|
|
6
|
+
export { TextDriver } from './channels/index.js';
|
|
8
7
|
export { setPendingUserInput, consumePendingUserInput, consumeAllPendingUserInput, peekPendingUserInput, hasPendingUserInput, } from './channels/index.js';
|
|
9
8
|
export { userInputToText, mergeUserInputContents, hasMediaParts, transcribeAudioParts, type UserInputContent, } from './userInput.js';
|
|
10
9
|
export { isAbortSignal, type InterruptionEvent, type AbortOptions, type CancellationReason, } from '../types/index.js';
|
package/dist/runtime/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
export { createRuntime, Runtime, } from './Runtime.js';
|
|
2
2
|
export { TraceRecorder, runOnce } from './TraceRecorder.js';
|
|
3
3
|
export { SessionWorkingMemory } from './WorkingMemory.js';
|
|
4
|
-
export { TextDriver
|
|
4
|
+
export { TextDriver } from './channels/index.js';
|
|
5
5
|
// Pending-input buffer helpers — required by custom ChannelDriver authors to
|
|
6
6
|
// implement awaitUser the same FIFO-aware way the built-in drivers do (the
|
|
7
7
|
// buffer is an ordered queue since 0.3.13/H3, not a single slot).
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import type { Session } from '../types/session.js';
|
|
2
2
|
import type { SessionStore } from '../session/SessionStore.js';
|
|
3
3
|
import type { ConversationOutcome, ConversationOutcomeMarkedBy, ConversationOutcomeRecord } from '../outcomes/types.js';
|
|
4
|
-
import type {
|
|
4
|
+
import type { StreamPart } from '../types/stream.js';
|
|
5
5
|
export declare function isTerminalOutcome(outcome: ConversationOutcome): boolean;
|
|
6
6
|
export declare function markSessionOutcome(sessionStore: SessionStore, session: Session, outcome: ConversationOutcome, opts?: {
|
|
7
7
|
reason?: string;
|
|
8
8
|
markedBy?: ConversationOutcomeMarkedBy;
|
|
9
|
-
}, emit?: (part:
|
|
9
|
+
}, emit?: (part: StreamPart) => void): Promise<ConversationOutcomeRecord>;
|
|
@@ -22,8 +22,9 @@ export async function markSessionOutcome(sessionStore, session, outcome, opts =
|
|
|
22
22
|
await sessionStore.save(session);
|
|
23
23
|
if (emit) {
|
|
24
24
|
emit({
|
|
25
|
+
channel: 'client',
|
|
25
26
|
type: 'conversation-outcome',
|
|
26
|
-
outcome: record.outcome,
|
|
27
|
+
payload: { outcome: record.outcome },
|
|
27
28
|
});
|
|
28
29
|
}
|
|
29
30
|
return record;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { TurnControl } from '../../types/channel.js';
|
|
2
2
|
import type { ToolCallRecord } from '../../types/session.js';
|
|
3
|
-
import type { SourceRef } from '../../types/
|
|
3
|
+
import type { SourceRef } from '../../types/knowledge.js';
|
|
4
4
|
import type { RunContext } from '../../types/run-context.js';
|
|
5
5
|
export interface PreTurnResult {
|
|
6
6
|
proceed: boolean;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
export function runHookSafely(name, callback) {
|
|
2
|
+
if (!callback)
|
|
3
|
+
return Promise.resolve();
|
|
4
|
+
try {
|
|
5
|
+
return Promise.resolve(callback()).catch((error) => reportHookError(name, error));
|
|
6
|
+
}
|
|
7
|
+
catch (error) {
|
|
8
|
+
reportHookError(name, error);
|
|
9
|
+
return Promise.resolve();
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
function reportHookError(name, error) {
|
|
13
|
+
try {
|
|
14
|
+
console.error(`Hook ${name} failed`, error);
|
|
15
|
+
}
|
|
16
|
+
catch {
|
|
17
|
+
// Hook failure reporting must not affect run correctness.
|
|
18
|
+
}
|
|
19
|
+
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
2
|
import type { Tool } from '../types/effectTool.js';
|
|
3
|
-
import type {
|
|
3
|
+
import type { StreamPart, TurnHandle } from '../types/stream.js';
|
|
4
4
|
/**
|
|
5
5
|
* Deferred-work scheduling for proactive (agent-initiated) turns.
|
|
6
6
|
*
|
|
@@ -51,7 +51,7 @@ export interface WakeDelivery {
|
|
|
51
51
|
reason: string;
|
|
52
52
|
payload?: Record<string, unknown>;
|
|
53
53
|
/** Full stream of the wake turn (text, tool events, interactive parts…). */
|
|
54
|
-
parts:
|
|
54
|
+
parts: StreamPart[];
|
|
55
55
|
/** Concatenated assistant text of the wake turn. */
|
|
56
56
|
text: string;
|
|
57
57
|
}
|
package/dist/scheduler/index.js
CHANGED
|
@@ -11,9 +11,8 @@ export class TracingService {
|
|
|
11
11
|
}
|
|
12
12
|
startSpan(name, attributes, parentSpanId) {
|
|
13
13
|
if (!this.config) {
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
throw new Error('Tracing not initialized. Call initTracing() first.');
|
|
14
|
+
throw new Error('TracingService not initialized. Construct it with a config — ' +
|
|
15
|
+
'new TracingService(config) — or call .init(config) before startSpan().');
|
|
17
16
|
}
|
|
18
17
|
const span = {
|
|
19
18
|
id: this.generateSpanId(),
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { AnyTool } from '../types/effectTool.js';
|
|
2
|
-
import type {
|
|
2
|
+
import type { FileSystem } from '../types/filesystem.js';
|
|
3
|
+
import type { SkillEntry, SkillLike, SkillSource, SkillStoreLike } from '../types/skills.js';
|
|
3
4
|
export interface SkillWireAgent {
|
|
4
5
|
skills?: SkillSource;
|
|
5
6
|
tools?: Record<string, AnyTool>;
|
|
@@ -8,10 +9,17 @@ export interface SkillWireAgent {
|
|
|
8
9
|
name: string;
|
|
9
10
|
}>;
|
|
10
11
|
}
|
|
11
|
-
export declare function isSkillStore(value: SkillSource): value is SkillStoreLike;
|
|
12
|
+
export declare function isSkillStore(value: SkillEntry | SkillSource): value is SkillStoreLike;
|
|
12
13
|
export declare function collectRegisteredNames(agent: SkillWireAgent): Set<string>;
|
|
13
14
|
export declare function validateSkillAllowedTools(skills: SkillLike[], registered: Set<string>): void;
|
|
14
|
-
|
|
15
|
+
/**
|
|
16
|
+
* Turns whatever the author wrote in `AgentConfig.skills` into one store.
|
|
17
|
+
*
|
|
18
|
+
* `fs` is the agent's workspace filesystem, needed only to resolve `string` entries. A path
|
|
19
|
+
* without a workspace is an authoring mistake, so it throws rather than silently yielding no
|
|
20
|
+
* skills — a missing skill is invisible at runtime and looks like the model ignoring it.
|
|
21
|
+
*/
|
|
22
|
+
export declare function prepareSkillStore(source: SkillSource, fs?: FileSystem): Promise<{
|
|
15
23
|
store: SkillStoreLike;
|
|
16
24
|
skills: SkillLike[];
|
|
17
25
|
}>;
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import { InlineSkillStore } from './inlineSkillStore.js';
|
|
2
|
+
import { CompositeSkillStore } from './compositeSkillStore.js';
|
|
3
|
+
import { fsSkillStore } from './fsSkillStore.js';
|
|
2
4
|
export function isSkillStore(value) {
|
|
3
5
|
return (typeof value === 'object' &&
|
|
4
6
|
value !== null &&
|
|
@@ -7,9 +9,6 @@ export function isSkillStore(value) {
|
|
|
7
9
|
typeof value.list === 'function');
|
|
8
10
|
}
|
|
9
11
|
async function collectSkillsFromSource(source) {
|
|
10
|
-
if (!isSkillStore(source)) {
|
|
11
|
-
return Array.isArray(source) ? source : [source];
|
|
12
|
-
}
|
|
13
12
|
if (typeof source.getAllSkills === 'function') {
|
|
14
13
|
const all = source.getAllSkills();
|
|
15
14
|
return Array.isArray(all) ? all : await all;
|
|
@@ -47,10 +46,54 @@ export function validateSkillAllowedTools(skills, registered) {
|
|
|
47
46
|
}
|
|
48
47
|
}
|
|
49
48
|
}
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
49
|
+
/**
|
|
50
|
+
* Turns whatever the author wrote in `AgentConfig.skills` into one store.
|
|
51
|
+
*
|
|
52
|
+
* `fs` is the agent's workspace filesystem, needed only to resolve `string` entries. A path
|
|
53
|
+
* without a workspace is an authoring mistake, so it throws rather than silently yielding no
|
|
54
|
+
* skills — a missing skill is invisible at runtime and looks like the model ignoring it.
|
|
55
|
+
*/
|
|
56
|
+
export async function prepareSkillStore(source, fs) {
|
|
57
|
+
const entries = Array.isArray(source)
|
|
58
|
+
? [...source]
|
|
59
|
+
: [source];
|
|
60
|
+
// A lone store stays itself: wrapping one store in a composite would add a layer with
|
|
61
|
+
// nothing to compose, and lose any extra capability the store exposes.
|
|
62
|
+
if (entries.length === 1 && isSkillStore(entries[0])) {
|
|
63
|
+
const only = entries[0];
|
|
64
|
+
return { store: only, skills: await collectSkillsFromSource(only) };
|
|
65
|
+
}
|
|
66
|
+
const stores = [];
|
|
67
|
+
let inline = [];
|
|
68
|
+
const flushInline = () => {
|
|
69
|
+
if (inline.length > 0) {
|
|
70
|
+
stores.push(new InlineSkillStore(inline));
|
|
71
|
+
inline = [];
|
|
72
|
+
}
|
|
73
|
+
};
|
|
74
|
+
for (const entry of entries) {
|
|
75
|
+
if (typeof entry === 'string') {
|
|
76
|
+
if (!fs) {
|
|
77
|
+
throw new Error(`[skills] "${entry}" is a workspace path, but this agent has no \`workspace\` filesystem. ` +
|
|
78
|
+
'Set `workspace` on the agent, or pass skills inline with defineSkill().');
|
|
79
|
+
}
|
|
80
|
+
flushInline();
|
|
81
|
+
stores.push(fsSkillStore(fs, [entry]));
|
|
82
|
+
continue;
|
|
83
|
+
}
|
|
84
|
+
if (isSkillStore(entry)) {
|
|
85
|
+
flushInline();
|
|
86
|
+
stores.push(entry);
|
|
87
|
+
continue;
|
|
88
|
+
}
|
|
89
|
+
// Consecutive inline skills share one store so ordering within a run is preserved.
|
|
90
|
+
inline.push(entry);
|
|
91
|
+
}
|
|
92
|
+
flushInline();
|
|
93
|
+
if (stores.length === 1) {
|
|
94
|
+
const only = stores[0];
|
|
95
|
+
return { store: only, skills: await collectSkillsFromSource(only) };
|
|
53
96
|
}
|
|
54
|
-
const
|
|
55
|
-
return { store:
|
|
97
|
+
const store = new CompositeSkillStore(stores);
|
|
98
|
+
return { store, skills: await collectSkillsFromSource(store) };
|
|
56
99
|
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { SkillMeta, SkillStoreLike } from '../types/skills.js';
|
|
2
|
+
/**
|
|
3
|
+
* Presents several skill stores as one, with **later stores winning** a name collision.
|
|
4
|
+
*
|
|
5
|
+
* Delegates rather than flattening on purpose: flattening would force every body and
|
|
6
|
+
* resource to load up front, which is exactly what progressive disclosure exists to avoid.
|
|
7
|
+
* Only `list()` (Level 1 — names and descriptions) is eager; bodies and resources stay
|
|
8
|
+
* behind their own calls.
|
|
9
|
+
*/
|
|
10
|
+
export declare class CompositeSkillStore implements SkillStoreLike {
|
|
11
|
+
private readonly stores;
|
|
12
|
+
/** Name → the store that owns it. Built on first `list()`, reused after. */
|
|
13
|
+
private owners?;
|
|
14
|
+
/** @param stores In precedence order, lowest first. */
|
|
15
|
+
constructor(stores: readonly SkillStoreLike[]);
|
|
16
|
+
list(): Promise<SkillMeta[]>;
|
|
17
|
+
loadBody(name: string): Promise<string>;
|
|
18
|
+
loadResource(name: string, path: string): Promise<string | Uint8Array>;
|
|
19
|
+
loadAllSkills(): Promise<{
|
|
20
|
+
name: string;
|
|
21
|
+
description: string;
|
|
22
|
+
body: string;
|
|
23
|
+
}[]>;
|
|
24
|
+
private ownerOf;
|
|
25
|
+
private resolveOwners;
|
|
26
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Presents several skill stores as one, with **later stores winning** a name collision.
|
|
3
|
+
*
|
|
4
|
+
* Delegates rather than flattening on purpose: flattening would force every body and
|
|
5
|
+
* resource to load up front, which is exactly what progressive disclosure exists to avoid.
|
|
6
|
+
* Only `list()` (Level 1 — names and descriptions) is eager; bodies and resources stay
|
|
7
|
+
* behind their own calls.
|
|
8
|
+
*/
|
|
9
|
+
export class CompositeSkillStore {
|
|
10
|
+
stores;
|
|
11
|
+
/** Name → the store that owns it. Built on first `list()`, reused after. */
|
|
12
|
+
owners;
|
|
13
|
+
/** @param stores In precedence order, lowest first. */
|
|
14
|
+
constructor(stores) {
|
|
15
|
+
this.stores = stores;
|
|
16
|
+
}
|
|
17
|
+
async list() {
|
|
18
|
+
const merged = new Map();
|
|
19
|
+
const owners = new Map();
|
|
20
|
+
for (const store of this.stores) {
|
|
21
|
+
for (const meta of await store.list()) {
|
|
22
|
+
merged.set(meta.name, meta);
|
|
23
|
+
owners.set(meta.name, store);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
this.owners = owners;
|
|
27
|
+
return [...merged.values()].sort((a, b) => a.name.localeCompare(b.name));
|
|
28
|
+
}
|
|
29
|
+
async loadBody(name) {
|
|
30
|
+
return (await this.ownerOf(name)).loadBody(name);
|
|
31
|
+
}
|
|
32
|
+
async loadResource(name, path) {
|
|
33
|
+
return (await this.ownerOf(name)).loadResource(name, path);
|
|
34
|
+
}
|
|
35
|
+
async loadAllSkills() {
|
|
36
|
+
const owners = await this.resolveOwners();
|
|
37
|
+
const out = [];
|
|
38
|
+
for (const [name, store] of owners) {
|
|
39
|
+
out.push({ name, description: '', body: await store.loadBody(name) });
|
|
40
|
+
}
|
|
41
|
+
return out;
|
|
42
|
+
}
|
|
43
|
+
async ownerOf(name) {
|
|
44
|
+
const owner = (await this.resolveOwners()).get(name);
|
|
45
|
+
if (!owner)
|
|
46
|
+
throw new Error(`[skills] Skill "${name}" not found.`);
|
|
47
|
+
return owner;
|
|
48
|
+
}
|
|
49
|
+
async resolveOwners() {
|
|
50
|
+
if (!this.owners)
|
|
51
|
+
await this.list();
|
|
52
|
+
return this.owners;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { SkillLike } from '../types/skills.js';
|
|
2
|
+
export interface DefineSkillConfig {
|
|
3
|
+
/** Lowercase, hyphen-separated, ≤64 chars — the Agent Skills naming rule. */
|
|
4
|
+
name: string;
|
|
5
|
+
/** When the model should reach for this skill. Disclosed up front, so keep it specific. ≤1024 chars. */
|
|
6
|
+
description: string;
|
|
7
|
+
/** The skill body — Level 2, loaded only when the model calls `load_skill`. */
|
|
8
|
+
instructions: string;
|
|
9
|
+
/** Level 3: fetched individually via `read_skill_resource`, never loaded up front. */
|
|
10
|
+
resources?: Record<string, string | Uint8Array>;
|
|
11
|
+
/** Restricts this skill to a subset of the agent's tools. Unknown names fail at wiring time. */
|
|
12
|
+
allowedTools?: string[];
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Authors a skill inline, with no filesystem involved.
|
|
16
|
+
*
|
|
17
|
+
* Validated against the same rules as a `SKILL.md` on disk — an inline skill that would be
|
|
18
|
+
* rejected as a file is rejected here too, so moving one to `agents/<id>/skills/` later
|
|
19
|
+
* cannot start failing.
|
|
20
|
+
*/
|
|
21
|
+
export declare function defineSkill(config: DefineSkillConfig): SkillLike;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { validateSkillDescription, validateSkillName } from './parseSkillFrontmatter.js';
|
|
2
|
+
/**
|
|
3
|
+
* Authors a skill inline, with no filesystem involved.
|
|
4
|
+
*
|
|
5
|
+
* Validated against the same rules as a `SKILL.md` on disk — an inline skill that would be
|
|
6
|
+
* rejected as a file is rejected here too, so moving one to `agents/<id>/skills/` later
|
|
7
|
+
* cannot start failing.
|
|
8
|
+
*/
|
|
9
|
+
export function defineSkill(config) {
|
|
10
|
+
const name = config.name?.trim();
|
|
11
|
+
const description = config.description?.trim();
|
|
12
|
+
if (!name)
|
|
13
|
+
throw new Error('[skills] defineSkill requires a non-empty name.');
|
|
14
|
+
if (!description) {
|
|
15
|
+
throw new Error(`[skills] Skill "${name}" requires a non-empty description.`);
|
|
16
|
+
}
|
|
17
|
+
validateSkillName(name, name);
|
|
18
|
+
validateSkillDescription(description, name);
|
|
19
|
+
return {
|
|
20
|
+
name,
|
|
21
|
+
description,
|
|
22
|
+
body: config.instructions,
|
|
23
|
+
resources: config.resources,
|
|
24
|
+
allowedTools: config.allowedTools,
|
|
25
|
+
};
|
|
26
|
+
}
|