@civilization/dsh-agent-loop 0.0.0-stage → 0.2.1-rc.3

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.
@@ -0,0 +1,70 @@
1
+ /**
2
+ * Default Agent driver over queued turns and step-boundary input. Every request
3
+ * is derived from the session log.
4
+ * @module dsh-agent-loop/agent
5
+ */
6
+ import type { Agent, AgentCancelCause, AgentOptions, AgentStatus, CancelOptions, InboxTarget } from '@deepseek-ai/dsh-agent';
7
+ import type { Scope } from '@deepseek-ai/dsh-scope';
8
+ import type { Session, SessionId, UserMessage } from '@deepseek-ai/dsh-session';
9
+ import type { Context } from '@deepseek-ai/cordis';
10
+ import { ReactLoopInbox } from './inbox.ts';
11
+ /** Drives one session through turn and step boundaries. */
12
+ export declare class ReactLoopAgent implements Agent {
13
+ private loopCtx;
14
+ readonly id: SessionId;
15
+ readonly options: AgentOptions;
16
+ readonly session: Session;
17
+ readonly inbox: ReactLoopInbox;
18
+ private phase;
19
+ private activityDone;
20
+ /** The agent-scoped registration boundary; the lifecycle owner unwinds it after the driver exits. */
21
+ readonly scope: Scope;
22
+ readonly ctx: Context;
23
+ /** Fused dispatcher, built once in the constructor so hot-path dispatches never allocate. */
24
+ private readonly dispatch;
25
+ /** Whether this loop instance has appended its initial/resume request anchor. */
26
+ private requestHeaderLogged;
27
+ /** Surface generation at attachment or the preceding built request. */
28
+ private requestSurfaceGeneration;
29
+ private readonly runtimeContext;
30
+ /** Process-local revision of assistant frames for this attached Session. */
31
+ private assistantStreamRevision;
32
+ private assistantAttemptCounter;
33
+ private readonly systemPrompt;
34
+ /** Identities fully frozen by this loop; weak references do not retain replaced history. */
35
+ private readonly frozenMessages;
36
+ constructor(loopCtx: Context, id: SessionId, options: AgentOptions, session: Session);
37
+ get status(): AgentStatus;
38
+ /** Commit a phase and publish its externally visible status transition. */
39
+ private setPhase;
40
+ send(message: UserMessage, target: InboxTarget, wakeup: boolean): void;
41
+ followup(input: UserMessage): void;
42
+ steer(input: UserMessage): void;
43
+ inject(input: UserMessage): void;
44
+ cancel(cause: AgentCancelCause, options?: CancelOptions): void;
45
+ runMaintenance<T>(job: (signal: AbortSignal) => Promise<T>): Promise<T>;
46
+ /**
47
+ * Start one driver, or latch its wake behind maintenance or an aborted
48
+ * activity. A wake sent while idle always opens its turn boundary, even
49
+ * when its message was cleared; only a latched replay is suppressed when
50
+ * the queue no longer holds the wake.
51
+ * @param wakeAfterAbort - the {@link send} classification, captured before
52
+ * the inbox insertion so a reentrant cancel cannot reclassify it.
53
+ */
54
+ private wakeDriver;
55
+ whenIdle(): Promise<void>;
56
+ /** Report one failure at its live boundary, then preserve it for driver containment. */
57
+ private throwError;
58
+ private kick;
59
+ private preStep;
60
+ /** Whether the assembled tool schemas differ from the logged request header's. */
61
+ private toolsChanged;
62
+ /** Open one turn before claiming its first proposed step. */
63
+ private turn;
64
+ private step;
65
+ /** Resolve request config and bind its adapter before admitting model-visible input. */
66
+ private prepareRequest;
67
+ /** Log the resolved envelope and derive a frozen request from the admitted surface. */
68
+ private buildRequest;
69
+ }
70
+ //# sourceMappingURL=agent.d.ts.map
@@ -0,0 +1,53 @@
1
+ /** Process-local assistant attempt framing and durable stream accumulation. */
2
+ import { LlmAttemptId, type ContentBlock, type FinishReason, type ReplayEnvelope, type StreamChunk, type TokenUsage } from '@deepseek-ai/dsh-llm';
3
+ import type { AssistantStreamFrame } from '@deepseek-ai/dsh-agent';
4
+ import type { SessionEventMap, SessionId, SessionSeq } from '@deepseek-ai/dsh-session';
5
+ /** Folds one model attempt into one compact stream plus ordered transient frames. */
6
+ export declare class AssistantStreamAttempt {
7
+ private readonly nextRevision;
8
+ readonly turn: number;
9
+ readonly step: number;
10
+ private readonly emit;
11
+ private readonly accumulator;
12
+ private readonly assembler;
13
+ private index;
14
+ private terminal;
15
+ /** Attempt identity unique within this Agent lifecycle. */
16
+ readonly attemptId: LlmAttemptId;
17
+ /** Whether this started attempt has emitted its terminal frame. */
18
+ get ended(): boolean;
19
+ /**
20
+ * @param sessionId - identity embedded only in the Agent-lifecycle-local attempt id.
21
+ * @param attempt - attached-Session-local attempt counter.
22
+ * @param nextRevision - allocates the next emitted frame revision.
23
+ * @param turn - durable turn owning the request.
24
+ * @param step - durable step owning the request.
25
+ * @param emit - agent-scoped notification publisher.
26
+ */
27
+ constructor(sessionId: SessionId, attempt: number, nextRevision: () => number, turn: number, step: number, emit: (frame: AssistantStreamFrame) => void);
28
+ /** Publish the opening marker before the first delivered chunk. */
29
+ start(): void;
30
+ /** Snapshot one chunk once, then feed durable compaction, assembly, and live publication. */
31
+ push(chunk: StreamChunk): void;
32
+ /**
33
+ * Publish terminal settlement after the matching durable event commits.
34
+ * @param eventType - durable settlement type.
35
+ * @param append - synchronous durable append returning its committed seq.
36
+ */
37
+ settle(eventType: 'assistant/message' | 'assistant/attempt', append: () => SessionSeq): void;
38
+ /** Publish abandonment when no durable attempt event can be committed. */
39
+ abandon(): void;
40
+ /** Exact compact stream for the final durable event. */
41
+ get stream(): SessionEventMap['assistant/attempt']['stream'];
42
+ /** Canonical completed-message blocks from the same chunks. */
43
+ blocks(): ContentBlock[];
44
+ /** Safe visible prefix when cancellation interrupts the attempt. */
45
+ interruptedBlocks(): ContentBlock[];
46
+ /** Latest adapter-reported usage in the stream. */
47
+ get usage(): TokenUsage | undefined;
48
+ /** Terminal reason, defaulting to stop when the stream omitted one. */
49
+ get finish(): FinishReason;
50
+ /** Replay state carried by the terminal finish record. */
51
+ get replayState(): ReplayEnvelope | undefined;
52
+ }
53
+ //# sourceMappingURL=assistant-stream.d.ts.map
@@ -0,0 +1,6 @@
1
+ /** Shared agent-loop scheduler defaults.
2
+ * @module dsh-agent-loop/constants
3
+ */
4
+ /** Default maximum in-flight parallel-safe calls per agent step. */
5
+ export declare const DEFAULT_MAX_PARALLEL_TOOL_CALLS = 10;
6
+ //# sourceMappingURL=constants.d.ts.map
@@ -0,0 +1,99 @@
1
+ /**
2
+ * Driver-owned durable agent inbox projection and command facade.
3
+ *
4
+ * @module @deepseek-ai/dsh-agent-loop/inbox
5
+ */
6
+ import type { MessageId } from '@deepseek-ai/dsh-llm';
7
+ import type SessionProjectionRegistry from '@deepseek-ai/dsh-session-projection';
8
+ import type { Session, UserMessage } from '@deepseek-ai/dsh-session';
9
+ import type { AgentEventDispatch, Inbox as InboxContract, InboxState, InboxTarget, InboxWireState } from '@deepseek-ai/dsh-agent';
10
+ import { z } from 'zod';
11
+ /** Wire validation for pending agent input reconstructed from durable inbox splices. */
12
+ export declare const inboxProjectionSchema: z.ZodReadonly<z.ZodObject<{
13
+ 'next-turn': z.ZodReadonly<z.ZodArray<z.ZodCustom<UserMessage, UserMessage>>>;
14
+ 'next-step': z.ZodReadonly<z.ZodArray<z.ZodCustom<UserMessage, UserMessage>>>;
15
+ }, z.core.$strip>>;
16
+ /** Standard fold that reconstructs pending input and rejects invalid durable splice history. */
17
+ export declare const inboxProjectionDefinition: {
18
+ key: "inbox";
19
+ stateSchema: z.ZodReadonly<z.ZodObject<{
20
+ 'next-turn': z.ZodReadonly<z.ZodArray<z.ZodCustom<UserMessage, UserMessage>>>;
21
+ 'next-step': z.ZodReadonly<z.ZodArray<z.ZodCustom<UserMessage, UserMessage>>>;
22
+ }, z.core.$strip>>;
23
+ init: () => InboxState;
24
+ apply(state: InboxState, event: import("@deepseek-ai/dsh-session").SessionEvent): InboxState;
25
+ wire: {
26
+ viewSchema: z.ZodType<InboxWireState>;
27
+ view: (state: InboxState) => InboxWireState;
28
+ };
29
+ stateVersion: number;
30
+ };
31
+ /**
32
+ * Driver-owned durable Inbox implementation used by ReactLoopAgent and focused
33
+ * provider tests.
34
+ * @param projections - registry with the standard Inbox projection registered by AgentLoop.
35
+ * @param session - session whose durable events store pending input.
36
+ * @param dispatch - agent-scoped notifications for Inbox lifecycle events.
37
+ */
38
+ export declare class ReactLoopInbox implements InboxContract {
39
+ private readonly projections;
40
+ private readonly session;
41
+ private readonly dispatch;
42
+ constructor(projections: SessionProjectionRegistry, session: Session, dispatch: AgentEventDispatch);
43
+ /** Prompts awaiting individual turns. */
44
+ get nextTurn(): readonly UserMessage[];
45
+ /** Input awaiting the next step boundary. */
46
+ get nextStep(): readonly UserMessage[];
47
+ /** Whether either pending-message list contains work. */
48
+ get hasPending(): boolean;
49
+ /** Durably cancel all pending input, clearing next-step before next-turn. */
50
+ clear(): void;
51
+ /**
52
+ * Remove and return the complete batch proposed for one step.
53
+ * @param target - whether this boundary also consumes one queued turn.
54
+ * @param turn - turn that will own the claimed batch.
55
+ * @returns next-step input followed by the queued turn, when requested.
56
+ */
57
+ claim(target: InboxTarget, turn: number): UserMessage[];
58
+ /**
59
+ * Append one message to a pending list.
60
+ * @param target - pending list to extend.
61
+ * @param message - message to append.
62
+ */
63
+ append(target: InboxTarget, message: UserMessage): void;
64
+ /**
65
+ * Prepend one message to a pending list.
66
+ * @param target - pending list to extend.
67
+ * @param message - message to prepend.
68
+ */
69
+ prepend(target: InboxTarget, message: UserMessage): void;
70
+ /**
71
+ * Replace one pending message in place.
72
+ * @param messageId - identity of the pending message to replace.
73
+ * @param newMessage - replacement message.
74
+ * @returns whether the message was still pending.
75
+ */
76
+ replace(messageId: MessageId, newMessage: UserMessage): boolean;
77
+ /**
78
+ * Remove one pending message.
79
+ * @param messageId - identity of the pending message to remove.
80
+ * @returns whether the message was still pending.
81
+ */
82
+ remove(messageId: MessageId): boolean;
83
+ /**
84
+ * Apply standard splice semantics and durably record the normalized result.
85
+ * @param target - pending list to mutate.
86
+ * @param start - splice position.
87
+ * @param deleteCount - maximum number of messages to remove.
88
+ * @param inserted - messages to insert at the resolved position.
89
+ * @returns messages removed by the splice.
90
+ */
91
+ splice(target: InboxTarget, start: number, deleteCount: number, inserted: UserMessage[]): UserMessage[];
92
+ /** Locate one pending identity across both owned lists. */
93
+ private locate;
94
+ /** Read the current durable projection state. */
95
+ private current;
96
+ /** Commit one normalized mutation and publish its live events. */
97
+ private mutate;
98
+ }
99
+ //# sourceMappingURL=inbox.d.ts.map
@@ -0,0 +1,179 @@
1
+ /**
2
+ * Concrete agent-loop plugin: creates scoped ReactLoopAgents, publishes them
3
+ * through the agent/session registries, and owns their ordered teardown.
4
+ *
5
+ * @module @deepseek-ai/dsh-agent-loop
6
+ */
7
+ import type { Volatile } from '@deepseek-ai/cosmokit';
8
+ import { Context, Service } from '@deepseek-ai/cordis';
9
+ import z from '@deepseek-ai/schemastery';
10
+ import { z as zod } from 'zod';
11
+ import type { Agent, AgentFactory, AgentHandle, AgentOptions, CreateAgentOptions, ResumeAgentOptions, TurnBoundaryProjection } from '@deepseek-ai/dsh-agent';
12
+ import type { SessionHeader, SessionId } from '@deepseek-ai/dsh-session';
13
+ import { DEFAULT_MAX_PARALLEL_TOOL_CALLS } from './constants.ts';
14
+ /** Host projection of agent turn and step boundaries. */
15
+ export declare const turnBoundaryProjectionDefinition: {
16
+ key: "turnBoundary";
17
+ stateVersion: number;
18
+ stateSchema: zod.ZodType<TurnBoundaryProjection, unknown, zod.core.$ZodTypeInternals<TurnBoundaryProjection, unknown>>;
19
+ init: () => {
20
+ openTurnStartSeq: null;
21
+ lastStepStartSeq: null;
22
+ lastStepBoundary: null;
23
+ lastTurn: number;
24
+ };
25
+ apply: (state: NoInfer<TurnBoundaryProjection>, event: import("@deepseek-ai/dsh-session").SessionEvent) => TurnBoundaryProjection;
26
+ };
27
+ declare module '@deepseek-ai/cordis' {
28
+ interface Context {
29
+ agentLoop: AgentLoop;
30
+ /**
31
+ * Launcher-owned exact session identities for configured agents, keyed by
32
+ * the agent's config `id` and set with `ctx.provide()` before any Loader
33
+ * entry mounts (see {@link CONFIGURED_AGENT_IDENTITIES_KEY}). A launcher
34
+ * owns identity because only it knows whether the session already exists,
35
+ * while the `cordis.yml` row keeps the model route as ordinary patchable
36
+ * config. An entry with no matching key keeps its configured identity.
37
+ */
38
+ configuredAgentIdentities?: ConfiguredAgentIdentities;
39
+ }
40
+ interface Events {
41
+ /**
42
+ * A declarative agent entry failed before it could publish a live agent.
43
+ * Consumers that buffer work for the configured identity use this
44
+ * transient signal to reject that work instead of waiting forever. Normal
45
+ * factory teardown suppresses failures from the cancelled startup attempt.
46
+ * @param payload.sessionId - exact shared agent/session identity that failed startup.
47
+ * @param payload.error - persistence, setup, or publication failure.
48
+ * @mode emit
49
+ */
50
+ 'agent-loop/config-start-failed'(payload: {
51
+ sessionId: SessionId;
52
+ error: unknown;
53
+ }): void;
54
+ }
55
+ }
56
+ export { DEFAULT_MAX_PARALLEL_TOOL_CALLS };
57
+ /**
58
+ * One launcher-selected session identity for a configured agent. `resume`
59
+ * distinguishes rehydrating existing persisted history from creating the
60
+ * session fresh under that exact id, which the two config keys express as
61
+ * `resumeSessionId` and `sessionId`.
62
+ */
63
+ export interface LauncherAgentIdentity {
64
+ /** Exact session id to create fresh or resume. */
65
+ id: SessionId;
66
+ /** Resume existing persisted history instead of creating the session fresh. */
67
+ resume: boolean;
68
+ }
69
+ /** Launcher-selected identities keyed by the configured agent's `id`. */
70
+ export interface ConfiguredAgentIdentities extends Readonly<Record<string, LauncherAgentIdentity>> {
71
+ }
72
+ /**
73
+ * Context key a launcher sets before any Loader entry mounts
74
+ * (`ctx.provide(CONFIGURED_AGENT_IDENTITIES_KEY, identities)`) to fix
75
+ * configured agents' session identities without a config key, so an overlay
76
+ * repointing the row's model route cannot drop them.
77
+ */
78
+ export declare const CONFIGURED_AGENT_IDENTITIES_KEY = "configuredAgentIdentities";
79
+ /** Agent-loop plugin configuration. */
80
+ export interface Config {
81
+ /**
82
+ * Maximum parallel-safe calls in flight per agent step. `1` is serial;
83
+ * omission defaults to {@link DEFAULT_MAX_PARALLEL_TOOL_CALLS}.
84
+ */
85
+ maxParallelToolCalls: Volatile<number>;
86
+ /** Agents created or resumed at plugin startup. */
87
+ agents: (AgentOptions & {
88
+ /** Stable config label used in logs and as the fresh combined-id prefix. */
89
+ id: string;
90
+ /** Optional stable identity; remounts resume its materialized history, while first use creates it fresh. */
91
+ sessionId?: SessionId;
92
+ /** Optional workspace for a fresh session. */
93
+ cwd?: string;
94
+ /** Persisted session to resume instead of creating a fresh session. */
95
+ resumeSessionId?: SessionId;
96
+ })[];
97
+ }
98
+ /** Concrete agent factory and driver service. */
99
+ export declare class AgentLoop extends Service implements AgentFactory {
100
+ static inject: string[];
101
+ /** Runtime schema for declarative agents. */
102
+ static Config: z<{
103
+ agents?: Config['agents'];
104
+ maxParallelToolCalls?: number;
105
+ }, Config>;
106
+ /** Validated configuration owned by the agent-loop service. */
107
+ readonly config: Config;
108
+ private readonly ownership;
109
+ /** Plain holder prevents Cordis from re-tracing the factory's dependency context through a caller shadow. */
110
+ private readonly runtime;
111
+ constructor(ctx: Context, config: Config);
112
+ /** Report a contained declarative-start failure to identity-bound consumers. */
113
+ private reportConfiguredStartupFailure;
114
+ /** Restore a materialized exact config identity on remount, or create it on first use. */
115
+ private restoreOrCreateConfigured;
116
+ /** Wait for a draining same-id lifecycle to finish registry teardown. */
117
+ private waitForDrainingConfiguredIdentity;
118
+ /**
119
+ * Construct the driver, scope, and one memoized reverse teardown for a new
120
+ * agent. The teardown is registered with the factory and the owner fiber
121
+ * BEFORE publication, so a mid-setup unload rolls everything back; `signal`
122
+ * fuses caller cancellation with lifecycle teardown for setup awaits.
123
+ */
124
+ private prepare;
125
+ /**
126
+ * Create an agent and session under one caller-supplied identity, owned by
127
+ * the accessing fiber. Constructor-driven config calls mint a fresh combined
128
+ * id before entering this boundary. When a persistence backend is mounted,
129
+ * the session's durable identity and any seed are stored before publication.
130
+ * @param id - shared agent/session identity.
131
+ * @param options - concrete loop options.
132
+ * @param meta - optional fresh-session workspace metadata.
133
+ * @returns the published running agent.
134
+ */
135
+ create(id: SessionId, options?: AgentOptions, meta?: Pick<SessionHeader, 'cwd'>): Promise<Agent>;
136
+ /**
137
+ * Take a fresh session's write ownership when persistence is mounted.
138
+ * Nothing is appended here: the constructor seed (which never re-emits
139
+ * through `session/event`) is stored by `appendUnstoredSuffix` at the
140
+ * publication commit point, so a failed or cancelled validation or setup
141
+ * closes an unmaterialized handle and leaves no stored residue — the same
142
+ * id can be created again.
143
+ * @param session - the unpublished session to store.
144
+ * @param signal - optional cancellation forwarded to the backend create.
145
+ * @returns the owned handle and stored cursor, or `undefined` without a backend.
146
+ */
147
+ private createStoredSession;
148
+ /**
149
+ * Durably store the session events appended since the last stored cursor.
150
+ * Pre-publication appends (constructor seed markers, setup-window events
151
+ * such as delegation policy records) never re-emit through `session/event`,
152
+ * so publication must flush them through the handle before live events
153
+ * start routing into it.
154
+ * @param stored - the session's owned handle and stored cursor, if any.
155
+ * @param session - the unpublished session whose suffix is stored.
156
+ */
157
+ private appendUnstoredSuffix;
158
+ /**
159
+ * Create an owned agent on a caller-supplied session id.
160
+ * @param ownerCtx - caller context that structurally owns the lifecycle.
161
+ * @param options - identities, optional live parent, session seed/metadata, loop options, setup, and cancellation.
162
+ * @returns the published handle.
163
+ */
164
+ createAgent(ownerCtx: Context, options: CreateAgentOptions): Promise<AgentHandle>;
165
+ /** Prepare one Agent around an acquired Session, run setup, and publish it. */
166
+ private setupAndPublish;
167
+ private initializeAgent;
168
+ /**
169
+ * Resume an owned agent from the configured persistence service.
170
+ * @param ownerCtx - caller context that owns load, setup, and the live lifecycle.
171
+ * @param options - persisted identity, optional live parent, loop options, setup, and cancellation.
172
+ * @returns the published handle.
173
+ */
174
+ resume(ownerCtx: Context, options: ResumeAgentOptions): Promise<AgentHandle>;
175
+ /** Resume through an explicit persistence handle used by the deferred config path. */
176
+ private resumeWith;
177
+ }
178
+ export default AgentLoop;
179
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1,75 @@
1
+ /**
2
+ * Durable projection state for the two loop-owned surface messages the system
3
+ * prompt plugin forms: the system prompt (surface node 0 and any in-history
4
+ * replacement) and the dynamic runtime-context snapshot.
5
+ * @module @deepseek-ai/dsh-agent-loop/runtime-context
6
+ */
7
+ import type { ContextFormed, ContextSnapshotSection } from '@deepseek-ai/dsh-llm';
8
+ import type { Session, SurfaceIntent, SystemMessage, UserMessage } from '@deepseek-ai/dsh-session';
9
+ import type { Context } from '@deepseek-ai/cordis';
10
+ declare module '@deepseek-ai/dsh-llm' {
11
+ interface MessageSourceMap {
12
+ 'runtime-context': {
13
+ kind: 'runtime-context';
14
+ } & ContextFormed;
15
+ }
16
+ }
17
+ /** One uncommitted system-prompt surface operation for request admission or reconciliation. */
18
+ export interface SystemPromptCommit {
19
+ /** Rendered prompt or empty content: an empty head records no prompt; empty tails are dormant. */
20
+ message: SystemMessage;
21
+ /** `append` for a new system node, otherwise a replacement of one surviving system node. */
22
+ intent: SurfaceIntent<'system/message'>;
23
+ }
24
+ /** The request-series facts one prompt decision is made under. */
25
+ export interface SystemPromptDecisionInput {
26
+ /** Whether the prepared route for this attempt reads a later `system` message as the effective prompt. */
27
+ inHistory: boolean;
28
+ /**
29
+ * Whether prompt admission must consolidate: a pre-step listener declared a
30
+ * new series, the surface changed since the last request, or assembled tools
31
+ * changed on a route without tool-update support.
32
+ */
33
+ startsSeries: boolean;
34
+ }
35
+ /**
36
+ * Decides how a rendered system prompt reaches the surface without owning the
37
+ * commit. The first prompt, even empty, reserves surface node 0.
38
+ * A capable continuing series appends changed nonempty text after the
39
+ * cached history. An incapable route, broken series, or cleared prompt instead
40
+ * normalizes the first system node and empties later active nodes. Dormant empty
41
+ * tails do not supply effective text or require repeated replacements.
42
+ */
43
+ export declare class SystemPromptProjection {
44
+ private readonly session;
45
+ constructor(session: Session);
46
+ /** The surviving `system/message` nodes in surface order. */
47
+ private systemNodes;
48
+ /**
49
+ * Reconcile effective text and retained nodes with the prepared route and series.
50
+ * @param rendered - the fully rendered system prompt; `''` when none is active.
51
+ * @param input - the route capability and series facts for this step.
52
+ * @returns ordered per-node updates; an empty list means no update is needed.
53
+ */
54
+ project(rendered: string, input: SystemPromptDecisionInput): SystemPromptCommit[];
55
+ private replace;
56
+ }
57
+ /** Tracks the last retained runtime-context snapshot without owning its commit. */
58
+ export declare class RuntimeContextProjection {
59
+ /** `undefined` means no snapshot ever existed; `null` means none is retained. */
60
+ private retained;
61
+ /**
62
+ * Restore projection state once, then follow authoritative session events.
63
+ * @param ctx - agent-scoped event context.
64
+ * @param session - session receiving projected messages.
65
+ */
66
+ constructor(ctx: Context, session: Session);
67
+ /**
68
+ * Create an uncommitted snapshot only when the retained value differs.
69
+ * @param current - fully rendered dynamic context.
70
+ * @param sections - named contributions that formed the current snapshot.
71
+ * @returns a candidate user message, or `undefined` when no update is needed.
72
+ */
73
+ project(current: string, sections: readonly ContextSnapshotSection[]): UserMessage | undefined;
74
+ }
75
+ //# sourceMappingURL=runtime-context.d.ts.map
@@ -0,0 +1,38 @@
1
+ /**
2
+ * Schedules one assistant step's tool calls. Exclusive calls form barriers;
3
+ * parallel calls use a bounded rolling pool and are reclassified before start.
4
+ * Dispatch may overlap, while policy, results, and result context remain
5
+ * model-ordered. Abort or an internal scheduler failure stops replenishment
6
+ * and drains started calls.
7
+ *
8
+ * Abort records synthetic error results for skipped calls so replay stays
9
+ * valid. A terminal scheduler failure rejects after draining; the owning step
10
+ * records conservative recovery results before closing.
11
+ * @module dsh-agent-loop/tool-calls
12
+ */
13
+ import type { Context } from '@deepseek-ai/cordis';
14
+ import { type ToolCallBlock } from '@deepseek-ai/dsh-llm';
15
+ import type { UserMessage } from '@deepseek-ai/dsh-session';
16
+ /**
17
+ * Schedule one assistant step's tool calls by their live concurrency mode.
18
+ * Ordinary completion and abort commit started-call results in order. Abort
19
+ * drains them, records synthetic results for unstarted calls, and returns with
20
+ * the signal still aborted after accepting started-call context through the
21
+ * caller-supplied acceptor (the machine stages it in its next-step inbox for the
22
+ * step boundary). An internal scheduler failure stops new dispatches, drains
23
+ * already-started dispatches, and rejects with the first failure. The owning
24
+ * step supplies error results for requests without a committed outcome.
25
+ * The committed step's AgentLoop driver boundary supplies the initiating Agent
26
+ * that becomes each explicit {@link ToolExecutionInput.agent}.
27
+ *
28
+ * @param ctx - loop context that owns the tool registry and carries the initiating Agent.
29
+ * @param turn - current turn number.
30
+ * @param step - current step number.
31
+ * @param toolCalls - assistant calls in model order.
32
+ * @param signal - abort signal shared by the step.
33
+ * @param acceptContext - accepts committed result context for the next step boundary.
34
+ */
35
+ export declare function executeToolCalls(ctx: Context, turn: number, step: number, toolCalls: ToolCallBlock[], signal: AbortSignal, acceptContext: (context: UserMessage) => void): Promise<{
36
+ concluded: boolean;
37
+ }>;
38
+ //# sourceMappingURL=tool-calls.d.ts.map
package/locale/en.json ADDED
@@ -0,0 +1,5 @@
1
+ {
2
+ "meta": {
3
+ "title": "agent-loop"
4
+ }
5
+ }
package/locale/zh.json ADDED
@@ -0,0 +1,6 @@
1
+ {
2
+ "meta": {
3
+ "title": "智能体运行循环",
4
+ "description": "协调模型请求、工具调用和后续步骤,推进智能体任务。"
5
+ }
6
+ }
package/package.json CHANGED
@@ -1,6 +1,61 @@
1
1
  {
2
2
  "name": "@civilization/dsh-agent-loop",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
3
+ "description": "The concrete agent loop plugin for the DeepSeek Harness",
4
+ "version": "0.2.1-rc.3",
5
+ "publishConfig": {
6
+ "access": "public"
7
+ },
8
+ "repository": {
9
+ "type": "git",
10
+ "url": "git+https://github.com/deepseek-ai/deepseek-harness.git",
11
+ "directory": "packages/core/agent-loop"
12
+ },
13
+ "type": "module",
14
+ "main": "lib/index.js",
15
+ "types": "lib/types/index.d.ts",
16
+ "exports": {
17
+ ".": {
18
+ "types": "./lib/types/index.d.ts",
19
+ "default": "./lib/index.js"
20
+ },
21
+ "./package.json": "./package.json",
22
+ "./locale/*.json": "./locale/*.json"
23
+ },
24
+ "files": [
25
+ "locale/*.json",
26
+ "lib/index.js",
27
+ "lib/types/**/*.d.ts"
28
+ ],
29
+ "license": "MIT",
30
+ "peerDependencies": {
31
+ "@deepseek-ai/cordis": "~4.0.5",
32
+ "@deepseek-ai/dsh-agent": "0.2.1-rc.2",
33
+ "@deepseek-ai/dsh-llm": "0.2.1-rc.2",
34
+ "@deepseek-ai/dsh-scope": "0.2.1-rc.2",
35
+ "@deepseek-ai/dsh-session": "0.2.1-rc.2",
36
+ "@deepseek-ai/dsh-session-persistence": "0.2.1-rc.2",
37
+ "@deepseek-ai/dsh-session-projection": "0.2.1-rc.2",
38
+ "@deepseek-ai/dsh-system-prompt": "0.2.1-rc.2",
39
+ "@deepseek-ai/dsh-tools": "0.2.1-rc.2"
40
+ },
41
+ "dependencies": {
42
+ "@deepseek-ai/dsh-brand": "0.2.1-rc.2",
43
+ "@deepseek-ai/dsh-util-values": "0.2.1-rc.2",
44
+ "@deepseek-ai/schemastery": "~3.18.5",
45
+ "zod": "4.4.3"
46
+ },
47
+ "devDependencies": {
48
+ "@deepseek-ai/dsh-llm-deepseek-api-key": "0.2.1-rc.2",
49
+ "@deepseek-ai/cordis": "~4.0.5",
50
+ "@deepseek-ai/dsh-llm-pi-ai": "0.2.1-rc.2",
51
+ "@deepseek-ai/dsh-agent": "0.2.1-rc.2",
52
+ "@deepseek-ai/dsh-llm": "0.2.1-rc.2",
53
+ "@deepseek-ai/dsh-scope": "0.2.1-rc.2",
54
+ "@deepseek-ai/dsh-session": "0.2.1-rc.2",
55
+ "@deepseek-ai/dsh-session-persistence": "0.2.1-rc.2",
56
+ "@deepseek-ai/dsh-session-persistence-jsonl": "0.2.1-rc.2",
57
+ "@deepseek-ai/dsh-session-projection": "0.2.1-rc.2",
58
+ "@deepseek-ai/dsh-system-prompt": "0.2.1-rc.2",
59
+ "@deepseek-ai/dsh-tools": "0.2.1-rc.2"
60
+ }
6
61
  }