@rowan-agent/agent 0.6.2 → 0.7.1
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 +8 -10
- package/dist/index.d.ts +72 -19
- package/dist/index.js +419 -125
- package/package.json +4 -3
package/README.md
CHANGED
|
@@ -616,7 +616,7 @@ Two sources, merged by priority:
|
|
|
616
616
|
|
|
617
617
|
```yaml
|
|
618
618
|
---
|
|
619
|
-
name:
|
|
619
|
+
name: review
|
|
620
620
|
description: Review code for correctness and style
|
|
621
621
|
tools: [read, bash]
|
|
622
622
|
target: execute
|
|
@@ -625,15 +625,14 @@ target: execute
|
|
|
625
625
|
Review the current implementation for bugs and style issues.
|
|
626
626
|
```
|
|
627
627
|
|
|
628
|
-
**Extension-registered** — via `api.registerPhase()`.
|
|
628
|
+
**Extension-registered** — via `api.registerPhase()`. The phase name is used as its identity.
|
|
629
629
|
|
|
630
630
|
```ts
|
|
631
631
|
import type { ExtensionAPI } from "@rowan-agent/agent";
|
|
632
632
|
|
|
633
633
|
export default function myPlugin(api: ExtensionAPI) {
|
|
634
634
|
api.registerPhase({
|
|
635
|
-
|
|
636
|
-
name: "Code Review",
|
|
635
|
+
name: "review",
|
|
637
636
|
description: "Review code for correctness",
|
|
638
637
|
tools: ["read", "bash"],
|
|
639
638
|
async run(context, execution) {
|
|
@@ -648,8 +647,7 @@ export default function myPlugin(api: ExtensionAPI) {
|
|
|
648
647
|
|
|
649
648
|
```ts
|
|
650
649
|
interface Phase {
|
|
651
|
-
|
|
652
|
-
name: string;
|
|
650
|
+
name: string; // unique identity and display name
|
|
653
651
|
description: string;
|
|
654
652
|
tools?: string[]; // restrict tools (undefined = all)
|
|
655
653
|
skills?: string[]; // restrict skills
|
|
@@ -674,7 +672,7 @@ interface PhaseContext {
|
|
|
674
672
|
|
|
675
673
|
type PhaseOutput = {
|
|
676
674
|
message: string;
|
|
677
|
-
route: string; // "continue" | "stop" | <phase-
|
|
675
|
+
route: string; // "continue" | "stop" | <phase-name>
|
|
678
676
|
payload?: unknown; // data passed to the next phase
|
|
679
677
|
};
|
|
680
678
|
```
|
|
@@ -731,7 +729,7 @@ import type { ExtensionAPI } from "@rowan-agent/agent";
|
|
|
731
729
|
export default function myPlugin(rowan: ExtensionAPI) {
|
|
732
730
|
rowan.on("agent_start", (event) => { ... });
|
|
733
731
|
rowan.registerTool({ name: "my_tool", description: "...", parameters: {...}, execute: async (args) => {...} });
|
|
734
|
-
rowan.registerPhase({
|
|
732
|
+
rowan.registerPhase({ name: "review", description: "...", run: async (ctx) => {...} });
|
|
735
733
|
rowan.events.emit("my-plugin:ready", {});
|
|
736
734
|
}
|
|
737
735
|
```
|
|
@@ -775,7 +773,7 @@ type LoopMetrics = {
|
|
|
775
773
|
| `Skill` | Loaded skill bundle |
|
|
776
774
|
| `Phase` | Phase definition with content, execution, and routing config |
|
|
777
775
|
| `PhaseContext` / `PhaseOutput` | Phase input and output |
|
|
778
|
-
| `PhaseRegistry` | Map of phase
|
|
776
|
+
| `PhaseRegistry` | Map of phase names to Phase objects plus entry phase name |
|
|
779
777
|
| `Outcome` | Terminal result with message and tool results |
|
|
780
778
|
| `LoopMetrics` | Loop iteration, timing, and phase transition stats |
|
|
781
779
|
| `SessionManagerProvider` | Session lifecycle seam used by the Runtime |
|
|
@@ -792,4 +790,4 @@ type LoopMetrics = {
|
|
|
792
790
|
|
|
793
791
|
## Version
|
|
794
792
|
|
|
795
|
-
Current version: **0.
|
|
793
|
+
Current version: **0.7.1**
|
package/dist/index.d.ts
CHANGED
|
@@ -30,6 +30,7 @@ type RuntimeEventId = OpaqueId<"RuntimeEventId">;
|
|
|
30
30
|
type LeaseId = OpaqueId<"LeaseId">;
|
|
31
31
|
type RuntimeToolCallId = OpaqueId<"RuntimeToolCallId">;
|
|
32
32
|
type AgentLifecycleState = "active" | "paused";
|
|
33
|
+
type SessionLifecycleState = "active" | "archived" | "deleting";
|
|
33
34
|
type RuntimeMessageState = "queued" | "leased" | "acknowledged" | "dead_lettered";
|
|
34
35
|
type AgentRunState = "queued" | "running" | "suspended" | "completed" | "failed" | "cancelled";
|
|
35
36
|
/** Opaque execution checkpoint owned by the Agent loop and persisted by Runtime. */
|
|
@@ -46,6 +47,8 @@ type AgentRecord = {
|
|
|
46
47
|
id: AgentId;
|
|
47
48
|
sessionId: string;
|
|
48
49
|
state: AgentLifecycleState;
|
|
50
|
+
lifecycleState: SessionLifecycleState;
|
|
51
|
+
parentSessionId?: string;
|
|
49
52
|
createdAt: string;
|
|
50
53
|
updatedAt: string;
|
|
51
54
|
};
|
|
@@ -84,7 +87,7 @@ type AgentRunRecord = {
|
|
|
84
87
|
createdAt: string;
|
|
85
88
|
updatedAt: string;
|
|
86
89
|
};
|
|
87
|
-
type RuntimeEventKind = "agent_created" | "agent_paused" | "agent_resumed" | "message_enqueued" | "run_enqueued" | "run_leased" | "lease_expired" | "lease_recovered" | "run_suspended" | "run_retry_scheduled" | "run_completed" | "run_aborted" | "message_acknowledged" | "message_dead_lettered" | "tool_call_created" | "tool_call_started" | "tool_call_failed" | "tool_call_completed" | "tool_call_indeterminate";
|
|
90
|
+
type RuntimeEventKind = "agent_created" | "agent_paused" | "agent_resumed" | "session_archived" | "session_unarchived" | "message_enqueued" | "run_enqueued" | "run_leased" | "lease_expired" | "lease_recovered" | "run_suspended" | "run_retry_scheduled" | "run_completed" | "run_aborted" | "message_acknowledged" | "message_dead_lettered" | "tool_call_created" | "tool_call_started" | "tool_call_failed" | "tool_call_completed" | "tool_call_indeterminate";
|
|
88
91
|
type RuntimeEvent = {
|
|
89
92
|
id: RuntimeEventId;
|
|
90
93
|
sequence: number;
|
|
@@ -325,9 +328,7 @@ interface PhaseContext {
|
|
|
325
328
|
* Loaded Phase object
|
|
326
329
|
*/
|
|
327
330
|
interface Phase {
|
|
328
|
-
/** Unique
|
|
329
|
-
id: string;
|
|
330
|
-
/** Display name */
|
|
331
|
+
/** Unique phase identity and display name. */
|
|
331
332
|
name: string;
|
|
332
333
|
/** Description */
|
|
333
334
|
description: string;
|
|
@@ -358,9 +359,9 @@ interface Phase {
|
|
|
358
359
|
* Phase registry containing all loaded phases
|
|
359
360
|
*/
|
|
360
361
|
interface PhaseRegistry {
|
|
361
|
-
/** Map of phase
|
|
362
|
+
/** Map of phase name to Phase object */
|
|
362
363
|
phases: Map<string, Phase>;
|
|
363
|
-
/** Entry phase
|
|
364
|
+
/** Entry phase name (null until Agent applies its default phase) */
|
|
364
365
|
entryPhaseId: string | null;
|
|
365
366
|
}
|
|
366
367
|
|
|
@@ -372,7 +373,6 @@ interface PhaseRegistry {
|
|
|
372
373
|
type PhaseRun = (context: PhaseContext, execution: PhaseExecution) => Promise<PhaseOutput | void>;
|
|
373
374
|
/** Phase definition shape used by extensions */
|
|
374
375
|
type PhaseDefinition = {
|
|
375
|
-
id: string;
|
|
376
376
|
name: string;
|
|
377
377
|
description: string;
|
|
378
378
|
run?: PhaseRun;
|
|
@@ -382,7 +382,7 @@ type PhaseDefinition = {
|
|
|
382
382
|
input?: Record<string, string>;
|
|
383
383
|
model?: string;
|
|
384
384
|
};
|
|
385
|
-
type PhaseRegistration =
|
|
385
|
+
type PhaseRegistration = Omit<PhaseDefinition, 'run'> & {
|
|
386
386
|
/** Optional execution override — takes over model invocation */
|
|
387
387
|
run?: PhaseRun;
|
|
388
388
|
};
|
|
@@ -480,7 +480,6 @@ interface ExtensionManifest {
|
|
|
480
480
|
entry?: string;
|
|
481
481
|
name?: string;
|
|
482
482
|
phase?: {
|
|
483
|
-
id?: string;
|
|
484
483
|
name?: string;
|
|
485
484
|
description?: string;
|
|
486
485
|
tools?: string[];
|
|
@@ -603,7 +602,7 @@ interface EventBus {
|
|
|
603
602
|
*/
|
|
604
603
|
interface BeforePhaseEvent {
|
|
605
604
|
type: "before_phase";
|
|
606
|
-
/** Phase
|
|
605
|
+
/** Phase name about to execute */
|
|
607
606
|
phaseId: string;
|
|
608
607
|
/** Phase input */
|
|
609
608
|
input: PhaseContext;
|
|
@@ -617,7 +616,7 @@ interface BeforePhaseEvent {
|
|
|
617
616
|
*/
|
|
618
617
|
interface AfterPhaseEvent {
|
|
619
618
|
type: "after_phase";
|
|
620
|
-
/** Phase
|
|
619
|
+
/** Phase name that completed */
|
|
621
620
|
phaseId: string;
|
|
622
621
|
/** Phase output */
|
|
623
622
|
output: PhaseOutput;
|
|
@@ -629,7 +628,7 @@ interface AfterPhaseEvent {
|
|
|
629
628
|
*/
|
|
630
629
|
interface BeforePromptEvent {
|
|
631
630
|
type: "before_prompt";
|
|
632
|
-
/** Current phase
|
|
631
|
+
/** Current phase name */
|
|
633
632
|
phaseId: string;
|
|
634
633
|
/** Phase input (modifiable) */
|
|
635
634
|
input: PhaseContext;
|
|
@@ -961,10 +960,10 @@ interface ExtensionContext {
|
|
|
961
960
|
}>;
|
|
962
961
|
/** Get skill content by name */
|
|
963
962
|
getSkillContent?(skillName: string): string;
|
|
964
|
-
/** Get all available phase
|
|
963
|
+
/** Get all available phase names */
|
|
965
964
|
getAvailablePhases?(): string[];
|
|
966
|
-
/** Get phase content by
|
|
967
|
-
getPhaseContent?(
|
|
965
|
+
/** Get phase content by name */
|
|
966
|
+
getPhaseContent?(phaseName: string): string;
|
|
968
967
|
}
|
|
969
968
|
/**
|
|
970
969
|
* Utility functions available to extensions.
|
|
@@ -1033,10 +1032,10 @@ interface ExtensionAPI {
|
|
|
1033
1032
|
setPayload(payload: unknown): void;
|
|
1034
1033
|
/** Phase Out: set outcome message */
|
|
1035
1034
|
setMessage(message: string): void;
|
|
1036
|
-
/** Get current phase
|
|
1035
|
+
/** Get current phase name */
|
|
1037
1036
|
getCurrentPhase(): string;
|
|
1038
1037
|
/** Set next phase (lower priority than PHASE.md target) */
|
|
1039
|
-
setNextPhase(
|
|
1038
|
+
setNextPhase(phaseName: string): void;
|
|
1040
1039
|
/** Get the next phase set by setNextPhase */
|
|
1041
1040
|
getNextPhase(): string | undefined;
|
|
1042
1041
|
/** Get the message set by setMessage */
|
|
@@ -1055,7 +1054,7 @@ type ExtensionFactory = (api: ExtensionAPI) => void | Promise<void>;
|
|
|
1055
1054
|
* Scans for subdirectories containing PHASE.md files.
|
|
1056
1055
|
* Returns PhaseRegistry with entryPhaseId:
|
|
1057
1056
|
* - null by default (caller must explicitly set to start from a specific phase)
|
|
1058
|
-
* - Set to a specific phase
|
|
1057
|
+
* - Set to a specific phase name to start from that phase
|
|
1059
1058
|
*
|
|
1060
1059
|
* When entryPhaseId is null, Agent normalizes the registry to start from its default phase.
|
|
1061
1060
|
*/
|
|
@@ -1212,6 +1211,19 @@ declare const SkillSchema: Type.TObject<{
|
|
|
1212
1211
|
}>;
|
|
1213
1212
|
type Skill = Type.Static<typeof SkillSchema>;
|
|
1214
1213
|
|
|
1214
|
+
type SessionHeader = {
|
|
1215
|
+
type: "header";
|
|
1216
|
+
id: string;
|
|
1217
|
+
version: string;
|
|
1218
|
+
createdAt: string;
|
|
1219
|
+
updatedAt: string;
|
|
1220
|
+
systemPrompt: string;
|
|
1221
|
+
input: string;
|
|
1222
|
+
parentSessionId?: string;
|
|
1223
|
+
skills: Skill[];
|
|
1224
|
+
title?: string;
|
|
1225
|
+
currentLeafId?: string | null;
|
|
1226
|
+
};
|
|
1215
1227
|
type SessionAgentContext<TTool = unknown> = {
|
|
1216
1228
|
systemPrompt: string;
|
|
1217
1229
|
messages: AgentMessage[];
|
|
@@ -1241,6 +1253,7 @@ type SessionListItem = {
|
|
|
1241
1253
|
};
|
|
1242
1254
|
type SessionManager = {
|
|
1243
1255
|
getSessionId(): string;
|
|
1256
|
+
getHeader(): Promise<SessionHeader>;
|
|
1244
1257
|
appendMessage(message: AgentMessage): Promise<string>;
|
|
1245
1258
|
appendOutcome(outcome: Outcome): Promise<string>;
|
|
1246
1259
|
appendModelTranscript(transcript: ModelTranscript, meta?: {
|
|
@@ -1252,10 +1265,13 @@ type SessionManager = {
|
|
|
1252
1265
|
type SessionManagerProvider = {
|
|
1253
1266
|
create(input: CreateSessionManagerInput): Promise<SessionManager>;
|
|
1254
1267
|
open(sessionId: string): Promise<SessionManager | undefined>;
|
|
1268
|
+
list(): Promise<SessionListItem[]>;
|
|
1269
|
+
delete(sessionId: string): Promise<boolean>;
|
|
1255
1270
|
};
|
|
1256
1271
|
|
|
1257
1272
|
type CreateAgentInput = {
|
|
1258
1273
|
sessionId: string;
|
|
1274
|
+
parentSessionId?: string;
|
|
1259
1275
|
};
|
|
1260
1276
|
type EnqueueAgentInput = {
|
|
1261
1277
|
agentId: AgentId;
|
|
@@ -1344,6 +1360,9 @@ interface RuntimeStateStore {
|
|
|
1344
1360
|
getAgent(agentId: AgentId): Promise<AgentRecord | undefined>;
|
|
1345
1361
|
listAgents(): Promise<AgentRecord[]>;
|
|
1346
1362
|
setAgentState(agentId: AgentId, state: AgentLifecycleState): Promise<AgentRecord>;
|
|
1363
|
+
setAgentLifecycleState(agentId: AgentId, state: SessionLifecycleState): Promise<AgentRecord>;
|
|
1364
|
+
getAgentBySessionId(sessionId: string): Promise<AgentRecord | undefined>;
|
|
1365
|
+
deleteAgentData(agentId: AgentId): Promise<void>;
|
|
1347
1366
|
enqueueAgentInput(input: EnqueueAgentInput): Promise<EnqueuedAgentInput>;
|
|
1348
1367
|
getMessage(messageId: RuntimeMessageId): Promise<RuntimeMessage | undefined>;
|
|
1349
1368
|
getRun(runId: AgentRunId): Promise<AgentRunRecord | undefined>;
|
|
@@ -1399,6 +1418,17 @@ type RuntimeEventConsumer = {
|
|
|
1399
1418
|
caughtUp: Promise<void>;
|
|
1400
1419
|
stop(): void;
|
|
1401
1420
|
};
|
|
1421
|
+
type RuntimeSessionSummary = {
|
|
1422
|
+
id: string;
|
|
1423
|
+
state: SessionLifecycleState;
|
|
1424
|
+
createdAt: string;
|
|
1425
|
+
updatedAt: string;
|
|
1426
|
+
archivedAt?: string;
|
|
1427
|
+
parentSessionId?: string;
|
|
1428
|
+
agentId: AgentId;
|
|
1429
|
+
activeRunCount: number;
|
|
1430
|
+
title?: string;
|
|
1431
|
+
};
|
|
1402
1432
|
declare class AgentRuntime {
|
|
1403
1433
|
private readonly stateStore;
|
|
1404
1434
|
private readonly sessionProvider?;
|
|
@@ -1409,6 +1439,7 @@ declare class AgentRuntime {
|
|
|
1409
1439
|
private readonly toolRuntime;
|
|
1410
1440
|
private readonly bindings;
|
|
1411
1441
|
private readonly agents;
|
|
1442
|
+
private readonly invalidatedAgents;
|
|
1412
1443
|
private readonly runHandles;
|
|
1413
1444
|
private readonly runWaiters;
|
|
1414
1445
|
private readonly pendingRuns;
|
|
@@ -1436,6 +1467,13 @@ declare class AgentRuntime {
|
|
|
1436
1467
|
getRun(runId: AgentRunId): Promise<AgentRunRecord | undefined>;
|
|
1437
1468
|
listActiveRuns(): Promise<AgentRunRecord[]>;
|
|
1438
1469
|
listRuns(input?: ListRunsInput): Promise<AgentRunRecord[]>;
|
|
1470
|
+
listSessions(input?: {
|
|
1471
|
+
state?: SessionLifecycleState;
|
|
1472
|
+
}): Promise<RuntimeSessionSummary[]>;
|
|
1473
|
+
archiveSession(sessionId: string): Promise<void>;
|
|
1474
|
+
unarchiveSession(sessionId: string): Promise<void>;
|
|
1475
|
+
deleteSession(sessionId: string): Promise<void>;
|
|
1476
|
+
private requireSessionAgent;
|
|
1439
1477
|
getAgent(agentId: AgentId): Agent | undefined;
|
|
1440
1478
|
getMessage(messageId: RuntimeMessageId): Promise<RuntimeMessage | undefined>;
|
|
1441
1479
|
getToolCall(toolCallId: RuntimeToolCallId): Promise<RuntimeToolCall | undefined>;
|
|
@@ -1537,6 +1575,14 @@ declare class InMemorySessionStore implements SessionManagerProvider {
|
|
|
1537
1575
|
private readonly sessions;
|
|
1538
1576
|
create(input: CreateSessionManagerInput): Promise<SessionManager>;
|
|
1539
1577
|
open(sessionId: string): Promise<SessionManager | undefined>;
|
|
1578
|
+
list(): Promise<{
|
|
1579
|
+
id: string;
|
|
1580
|
+
title: string | undefined;
|
|
1581
|
+
createdAt: string;
|
|
1582
|
+
updatedAt: string;
|
|
1583
|
+
messageCount: any;
|
|
1584
|
+
}[]>;
|
|
1585
|
+
delete(sessionId: string): Promise<boolean>;
|
|
1540
1586
|
}
|
|
1541
1587
|
declare class JsonlSessionStore implements SessionManagerProvider {
|
|
1542
1588
|
private readonly sessionsDir;
|
|
@@ -1544,6 +1590,7 @@ declare class JsonlSessionStore implements SessionManagerProvider {
|
|
|
1544
1590
|
create(input: CreateSessionManagerInput): Promise<SessionManager>;
|
|
1545
1591
|
open(sessionId: string): Promise<SessionManager | undefined>;
|
|
1546
1592
|
list(): Promise<SessionListItem[]>;
|
|
1593
|
+
delete(sessionId: string): Promise<boolean>;
|
|
1547
1594
|
}
|
|
1548
1595
|
|
|
1549
1596
|
type CoreToolContext = {
|
|
@@ -1563,8 +1610,11 @@ declare class InMemoryRuntimeStateStore implements RuntimeStateStore {
|
|
|
1563
1610
|
private readonly eventCheckpoints;
|
|
1564
1611
|
createAgent(input: CreateAgentInput): Promise<AgentRecord>;
|
|
1565
1612
|
getAgent(agentId: AgentId): Promise<AgentRecord | undefined>;
|
|
1613
|
+
getAgentBySessionId(sessionId: string): Promise<AgentRecord | undefined>;
|
|
1566
1614
|
listAgents(): Promise<AgentRecord[]>;
|
|
1567
1615
|
setAgentState(agentId: AgentId, state: AgentRecord["state"]): Promise<AgentRecord>;
|
|
1616
|
+
setAgentLifecycleState(agentId: AgentId, state: AgentRecord["lifecycleState"]): Promise<AgentRecord>;
|
|
1617
|
+
deleteAgentData(agentId: AgentId): Promise<void>;
|
|
1568
1618
|
enqueueAgentInput(input: EnqueueAgentInput): Promise<EnqueuedAgentInput>;
|
|
1569
1619
|
private enqueueAgentInputState;
|
|
1570
1620
|
getMessage(messageId: RuntimeMessageId): Promise<RuntimeMessage | undefined>;
|
|
@@ -1603,8 +1653,11 @@ declare class SqliteRuntimeStateStore implements RuntimeStateStore {
|
|
|
1603
1653
|
close(): void;
|
|
1604
1654
|
createAgent(input: CreateAgentInput): Promise<AgentRecord>;
|
|
1605
1655
|
getAgent(agentId: AgentId): Promise<AgentRecord | undefined>;
|
|
1656
|
+
getAgentBySessionId(sessionId: string): Promise<AgentRecord | undefined>;
|
|
1606
1657
|
listAgents(): Promise<AgentRecord[]>;
|
|
1607
1658
|
setAgentState(agentId: AgentId, state: AgentRecord["state"]): Promise<AgentRecord>;
|
|
1659
|
+
setAgentLifecycleState(agentId: AgentId, state: AgentRecord["lifecycleState"]): Promise<AgentRecord>;
|
|
1660
|
+
deleteAgentData(agentId: AgentId): Promise<void>;
|
|
1608
1661
|
enqueueAgentInput(input: EnqueueAgentInput): Promise<EnqueuedAgentInput>;
|
|
1609
1662
|
private enqueueAgentInputState;
|
|
1610
1663
|
getMessage(messageId: RuntimeMessageId): Promise<RuntimeMessage | undefined>;
|
|
@@ -1648,4 +1701,4 @@ declare class SqliteRuntimeStateStore implements RuntimeStateStore {
|
|
|
1648
1701
|
private recordEventInternal;
|
|
1649
1702
|
}
|
|
1650
1703
|
|
|
1651
|
-
export { type AfterToolCall, Agent, type AgentContext, type AgentId, type AgentInputRequest, type AgentOptions, AgentRun, type AgentRunExecutionState, type AgentRunId, type AgentRunListener, type AgentRunMetadata, type AgentRunRecord, type AgentRunState, AgentRuntime, type AgentRuntimeOptions, type BeforeToolCall, type CoreToolContext, type ExecutionTurn, type ExtensionAPI, type ExtensionFactory, type HookEvent, type HookEventType, type HookHandler, InMemoryRuntimeStateStore, InMemorySessionStore, JsonlSessionStore, type LoadExtensionsResult, type LoadedExtension, type LoopMetrics, type ModelTranscript, type Phase, type PhaseContext, type PhaseExecution, type PhaseInvocation, type PhaseOutput, type PhaseRegistration, type PhaseRegistry, type PhaseState, type RuntimeEvent, type RuntimeEventConsumer, type RuntimeEventCursor, type RuntimeEventDisposition, type RuntimeEventKind, type RuntimeEventListener, type RuntimeMessage, type RuntimeMessageId, type RuntimeToolCall, type RuntimeToolCallId, type SessionManagerProvider, SqliteRuntimeStateStore, type Tool, type ToolContext, type ToolDefinition, type ToolExecutionMode, type ToolExecutionResult, type ToolRuntimePolicy, type Unsubscribe, createCoreTools, createMessage };
|
|
1704
|
+
export { type AfterToolCall, Agent, type AgentContext, type AgentId, type AgentInputRequest, type AgentOptions, AgentRun, type AgentRunExecutionState, type AgentRunId, type AgentRunListener, type AgentRunMetadata, type AgentRunRecord, type AgentRunState, AgentRuntime, type AgentRuntimeOptions, type BeforeToolCall, type CoreToolContext, type ExecutionTurn, type ExtensionAPI, type ExtensionFactory, type HookEvent, type HookEventType, type HookHandler, InMemoryRuntimeStateStore, InMemorySessionStore, JsonlSessionStore, type LoadExtensionsResult, type LoadedExtension, type LoopMetrics, type ModelTranscript, type Phase, type PhaseContext, type PhaseExecution, type PhaseInvocation, type PhaseOutput, type PhaseRegistration, type PhaseRegistry, type PhaseState, type RuntimeEvent, type RuntimeEventConsumer, type RuntimeEventCursor, type RuntimeEventDisposition, type RuntimeEventKind, type RuntimeEventListener, type RuntimeMessage, type RuntimeMessageId, type RuntimeSessionSummary, type RuntimeToolCall, type RuntimeToolCallId, type SessionLifecycleState, type SessionManagerProvider, SqliteRuntimeStateStore, type Tool, type ToolContext, type ToolDefinition, type ToolExecutionMode, type ToolExecutionResult, type ToolRuntimePolicy, type Unsubscribe, createCoreTools, createMessage };
|
package/dist/index.js
CHANGED
|
@@ -387,51 +387,29 @@ import { dirname as dirname2, join, resolve } from "path";
|
|
|
387
387
|
// src/harness/loader.ts
|
|
388
388
|
import { readFile } from "fs/promises";
|
|
389
389
|
import { basename, dirname, extname } from "path";
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
for (let i = 0; i < lines.length; i++) {
|
|
396
|
-
const line = lines[i];
|
|
397
|
-
const idx = line.indexOf(":");
|
|
398
|
-
if (idx === -1) continue;
|
|
399
|
-
const key = line.slice(0, idx).trim();
|
|
400
|
-
const value = line.slice(idx + 1).trim();
|
|
401
|
-
if (!key) continue;
|
|
402
|
-
if (value === "" && i + 1 < lines.length) {
|
|
403
|
-
const nested = {};
|
|
404
|
-
while (i + 1 < lines.length) {
|
|
405
|
-
const next = lines[i + 1];
|
|
406
|
-
if (!next.startsWith(" ") || next.trim() === "") break;
|
|
407
|
-
const nIdx = next.indexOf(":");
|
|
408
|
-
if (nIdx === -1) break;
|
|
409
|
-
const nKey = next.slice(0, nIdx).trim();
|
|
410
|
-
const nValue = next.slice(nIdx + 1).trim();
|
|
411
|
-
nested[nKey] = nValue;
|
|
412
|
-
i++;
|
|
413
|
-
}
|
|
414
|
-
if (Object.keys(nested).length) {
|
|
415
|
-
frontmatter[key] = nested;
|
|
416
|
-
}
|
|
417
|
-
continue;
|
|
418
|
-
}
|
|
419
|
-
frontmatter[key] = parseValue(value);
|
|
420
|
-
}
|
|
421
|
-
return { frontmatter, body: raw.slice(match[0].length) };
|
|
422
|
-
}
|
|
423
|
-
function parseValue(value) {
|
|
424
|
-
const arrayMatch = value.match(/^\[(.*)\]$/);
|
|
425
|
-
if (arrayMatch) {
|
|
426
|
-
return arrayMatch[1].split(",").map((s) => s.trim()).filter(Boolean);
|
|
390
|
+
import { parse as parseYaml } from "yaml";
|
|
391
|
+
var FrontmatterParseError = class extends Error {
|
|
392
|
+
constructor(message) {
|
|
393
|
+
super(message);
|
|
394
|
+
this.name = "FrontmatterParseError";
|
|
427
395
|
}
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
if (
|
|
432
|
-
|
|
396
|
+
};
|
|
397
|
+
function parseFrontmatter(raw) {
|
|
398
|
+
const normalized = raw.replace(/\r\n/g, "\n").replace(/\r/g, "\n");
|
|
399
|
+
if (!normalized.startsWith("---")) return { frontmatter: {}, body: normalized };
|
|
400
|
+
const endIndex = normalized.indexOf("\n---", 3);
|
|
401
|
+
if (endIndex === -1) return { frontmatter: {}, body: normalized };
|
|
402
|
+
const yamlString = normalized.slice(4, endIndex);
|
|
403
|
+
try {
|
|
404
|
+
const frontmatter = parseYaml(yamlString) ?? {};
|
|
405
|
+
return {
|
|
406
|
+
frontmatter,
|
|
407
|
+
body: normalized.slice(endIndex + 4).trim()
|
|
408
|
+
};
|
|
409
|
+
} catch (error) {
|
|
410
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
411
|
+
throw new FrontmatterParseError(message);
|
|
433
412
|
}
|
|
434
|
-
return value;
|
|
435
413
|
}
|
|
436
414
|
async function loadMarkdown(filePath) {
|
|
437
415
|
const raw = await readFile(filePath, "utf8");
|
|
@@ -543,6 +521,82 @@ function buildPhaseDirectiveMessage(phase, output) {
|
|
|
543
521
|
return parts.join("\n");
|
|
544
522
|
}
|
|
545
523
|
|
|
524
|
+
// src/harness/resource-validation.ts
|
|
525
|
+
var MAX_RESOURCE_ID_LENGTH = 64;
|
|
526
|
+
var MAX_DESCRIPTION_LENGTH = 1024;
|
|
527
|
+
var ResourceMetadataError = class extends Error {
|
|
528
|
+
constructor(code, message) {
|
|
529
|
+
super(message);
|
|
530
|
+
this.code = code;
|
|
531
|
+
this.name = "ResourceMetadataError";
|
|
532
|
+
}
|
|
533
|
+
code;
|
|
534
|
+
};
|
|
535
|
+
function validateResourceId(id, label = "id") {
|
|
536
|
+
const errors = [];
|
|
537
|
+
if (id.length > MAX_RESOURCE_ID_LENGTH) {
|
|
538
|
+
errors.push(`${label} exceeds ${MAX_RESOURCE_ID_LENGTH} characters (${id.length})`);
|
|
539
|
+
}
|
|
540
|
+
if (!/^[a-z0-9-]+$/.test(id)) {
|
|
541
|
+
errors.push(`${label} contains invalid characters (must be lowercase a-z, 0-9, hyphens only)`);
|
|
542
|
+
}
|
|
543
|
+
if (id.startsWith("-") || id.endsWith("-")) {
|
|
544
|
+
errors.push(`${label} must not start or end with a hyphen`);
|
|
545
|
+
}
|
|
546
|
+
if (id.includes("--")) {
|
|
547
|
+
errors.push(`${label} must not contain consecutive hyphens`);
|
|
548
|
+
}
|
|
549
|
+
return errors;
|
|
550
|
+
}
|
|
551
|
+
function validateResourceName(name, id) {
|
|
552
|
+
const errors = [];
|
|
553
|
+
if (name !== id) {
|
|
554
|
+
errors.push(`name "${name}" does not match parent directory "${id}"`);
|
|
555
|
+
errors.push(...validateResourceId(name, "name"));
|
|
556
|
+
}
|
|
557
|
+
return errors;
|
|
558
|
+
}
|
|
559
|
+
function validateDescription(value) {
|
|
560
|
+
if (typeof value !== "string" || value.trim() === "") {
|
|
561
|
+
return {
|
|
562
|
+
warnings: ["description is required"],
|
|
563
|
+
missing: true
|
|
564
|
+
};
|
|
565
|
+
}
|
|
566
|
+
if (value.length > MAX_DESCRIPTION_LENGTH) {
|
|
567
|
+
return {
|
|
568
|
+
description: value,
|
|
569
|
+
warnings: [`description exceeds ${MAX_DESCRIPTION_LENGTH} characters (${value.length})`],
|
|
570
|
+
missing: false
|
|
571
|
+
};
|
|
572
|
+
}
|
|
573
|
+
return { description: value, warnings: [], missing: false };
|
|
574
|
+
}
|
|
575
|
+
function validateSkillReferences(value) {
|
|
576
|
+
if (value === void 0) return [];
|
|
577
|
+
if (!Array.isArray(value)) return ["skills must be an array"];
|
|
578
|
+
const errors = [];
|
|
579
|
+
for (const [index, skillId] of value.entries()) {
|
|
580
|
+
if (typeof skillId !== "string") {
|
|
581
|
+
errors.push(`skills[${index}] must be a string`);
|
|
582
|
+
continue;
|
|
583
|
+
}
|
|
584
|
+
errors.push(...validateResourceId(skillId, `skills[${index}]`));
|
|
585
|
+
}
|
|
586
|
+
return errors;
|
|
587
|
+
}
|
|
588
|
+
function validatePhaseTarget(value) {
|
|
589
|
+
if (value === void 0) return [];
|
|
590
|
+
if (typeof value !== "string") return ["target must be a string"];
|
|
591
|
+
if (value === "stop") return [];
|
|
592
|
+
return validateResourceId(value, "target");
|
|
593
|
+
}
|
|
594
|
+
function warnResourceDiagnostics(kind, filePath, diagnostics) {
|
|
595
|
+
for (const diagnostic of diagnostics) {
|
|
596
|
+
console.warn(`Invalid ${kind} metadata at "${filePath}": ${diagnostic}`);
|
|
597
|
+
}
|
|
598
|
+
}
|
|
599
|
+
|
|
546
600
|
// src/harness/phases/loader.ts
|
|
547
601
|
var PHASE_MARKER = "PHASE.md";
|
|
548
602
|
function resolvePhasePath(input) {
|
|
@@ -551,28 +605,44 @@ function resolvePhasePath(input) {
|
|
|
551
605
|
}
|
|
552
606
|
async function loadPhase(targetPath) {
|
|
553
607
|
const resolved = resolvePhasePath(targetPath);
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
throw new
|
|
608
|
+
let loaded;
|
|
609
|
+
try {
|
|
610
|
+
loaded = await loadMarkdown(resolved);
|
|
611
|
+
} catch (error) {
|
|
612
|
+
if (!(error instanceof FrontmatterParseError)) throw error;
|
|
613
|
+
warnResourceDiagnostics("phase", resolved, [`frontmatter could not be parsed: ${error.message}`]);
|
|
614
|
+
throw new ResourceMetadataError("parse_failed", error.message);
|
|
615
|
+
}
|
|
616
|
+
const directoryName = inferResourceName(resolved, PHASE_MARKER);
|
|
617
|
+
const { frontmatter, body } = loaded;
|
|
618
|
+
const metadata = frontmatter;
|
|
619
|
+
const frontmatterName = typeof metadata.name === "string" ? metadata.name : void 0;
|
|
620
|
+
const name = frontmatterName || directoryName;
|
|
621
|
+
const description = validateDescription(metadata.description);
|
|
622
|
+
const diagnostics = validateResourceId(directoryName);
|
|
623
|
+
if (frontmatterName && frontmatterName !== directoryName) {
|
|
624
|
+
diagnostics.push(...validateResourceName(frontmatterName, directoryName));
|
|
625
|
+
}
|
|
626
|
+
diagnostics.push(...description.warnings);
|
|
627
|
+
diagnostics.push(...validateSkillReferences(metadata.skills));
|
|
628
|
+
diagnostics.push(...validatePhaseTarget(metadata.target));
|
|
629
|
+
warnResourceDiagnostics("phase", resolved, diagnostics);
|
|
630
|
+
if (description.missing) {
|
|
631
|
+
throw new ResourceMetadataError("invalid_metadata", "description is required");
|
|
561
632
|
}
|
|
562
633
|
const baseDir = dirname2(resolved);
|
|
563
634
|
const phase = {
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
isolated: frontmatter.isolated,
|
|
635
|
+
name,
|
|
636
|
+
description: description.description,
|
|
637
|
+
tools: metadata.tools,
|
|
638
|
+
skills: metadata.skills,
|
|
639
|
+
target: metadata.target,
|
|
640
|
+
input: metadata.input,
|
|
641
|
+
isolated: metadata.isolated,
|
|
572
642
|
filePath: resolved,
|
|
573
643
|
baseDir,
|
|
574
644
|
content: body,
|
|
575
|
-
model: parseModelRef(
|
|
645
|
+
model: parseModelRef(metadata.model)
|
|
576
646
|
};
|
|
577
647
|
const codePath = await discoverPhaseCode(baseDir);
|
|
578
648
|
if (codePath) {
|
|
@@ -598,13 +668,21 @@ async function loadPhases(targetPath) {
|
|
|
598
668
|
const phases = /* @__PURE__ */ new Map();
|
|
599
669
|
const phasesDir = resolve(targetPath);
|
|
600
670
|
if (existsSync(phasesDir) && statSync(phasesDir).isFile()) {
|
|
601
|
-
|
|
602
|
-
|
|
671
|
+
try {
|
|
672
|
+
const phase = await loadPhase(phasesDir);
|
|
673
|
+
phases.set(phase.name, phase);
|
|
674
|
+
} catch (error) {
|
|
675
|
+
if (!(error instanceof ResourceMetadataError)) throw error;
|
|
676
|
+
}
|
|
603
677
|
return { phases, entryPhaseId: null };
|
|
604
678
|
}
|
|
605
679
|
if (existsSync(join(phasesDir, PHASE_MARKER))) {
|
|
606
|
-
|
|
607
|
-
|
|
680
|
+
try {
|
|
681
|
+
const phase = await loadPhase(phasesDir);
|
|
682
|
+
phases.set(phase.name, phase);
|
|
683
|
+
} catch (error) {
|
|
684
|
+
if (!(error instanceof ResourceMetadataError)) throw error;
|
|
685
|
+
}
|
|
608
686
|
return { phases, entryPhaseId: null };
|
|
609
687
|
}
|
|
610
688
|
const entries = await readdir(phasesDir, { withFileTypes: true });
|
|
@@ -614,8 +692,9 @@ async function loadPhases(targetPath) {
|
|
|
614
692
|
if (!existsSync(phaseFile)) continue;
|
|
615
693
|
try {
|
|
616
694
|
const phase = await loadPhase(phaseFile);
|
|
617
|
-
phases.set(phase.
|
|
695
|
+
phases.set(phase.name, phase);
|
|
618
696
|
} catch (error) {
|
|
697
|
+
if (error instanceof ResourceMetadataError) continue;
|
|
619
698
|
console.warn(`Failed to load phase "${entry.name}":`, error);
|
|
620
699
|
}
|
|
621
700
|
}
|
|
@@ -653,8 +732,7 @@ async function loadPhaseCode(codePath) {
|
|
|
653
732
|
var DEFAULT_PHASE_ID = "default";
|
|
654
733
|
function createDefaultPhase() {
|
|
655
734
|
return {
|
|
656
|
-
|
|
657
|
-
name: "Execution Phase",
|
|
735
|
+
name: DEFAULT_PHASE_ID,
|
|
658
736
|
description: "Executes concrete task operations and produces artifacts.",
|
|
659
737
|
filePath: "",
|
|
660
738
|
baseDir: "",
|
|
@@ -678,7 +756,7 @@ function normalizeRelativePath(path) {
|
|
|
678
756
|
import Type from "typebox";
|
|
679
757
|
var PhaseRouteTool = "route";
|
|
680
758
|
function buildPhaseEntry(p) {
|
|
681
|
-
const entry = {
|
|
759
|
+
const entry = { name: p.name, description: p.description };
|
|
682
760
|
if (p.tools && p.tools.length > 0) {
|
|
683
761
|
entry.available_tools = p.tools.join(", ");
|
|
684
762
|
}
|
|
@@ -693,7 +771,7 @@ function buildPhaseEntry(p) {
|
|
|
693
771
|
function buildRouteDescription(availablePhases) {
|
|
694
772
|
const phasesBlock = buildStructuredSection("phase", [
|
|
695
773
|
...availablePhases.map(buildPhaseEntry),
|
|
696
|
-
{
|
|
774
|
+
{ name: "stop", description: "Terminate the workflow and return final result to the user" }
|
|
697
775
|
]);
|
|
698
776
|
return [
|
|
699
777
|
"Route execution to one or more phases concurrently, or stop execution.",
|
|
@@ -714,7 +792,7 @@ function buildRouteDescription(availablePhases) {
|
|
|
714
792
|
function createRouteTool(availablePhases) {
|
|
715
793
|
const DecisionTarget = Type.Object({
|
|
716
794
|
phase: Type.Union([
|
|
717
|
-
...availablePhases.map((p) => Type.Literal(p.
|
|
795
|
+
...availablePhases.map((p) => Type.Literal(p.name)),
|
|
718
796
|
Type.Literal("stop")
|
|
719
797
|
]),
|
|
720
798
|
reason: Type.Optional(Type.String({ description: "Brief reason for this decision" })),
|
|
@@ -1222,8 +1300,7 @@ function createCoreTools(input = {}) {
|
|
|
1222
1300
|
|
|
1223
1301
|
// src/harness/context/system-prompt.ts
|
|
1224
1302
|
function buildSystemPrompt(options) {
|
|
1225
|
-
const { systemPrompt, promptGuidelines, appendSystemPrompt, tools, skills
|
|
1226
|
-
const date = createTimestamp();
|
|
1303
|
+
const { systemPrompt, promptGuidelines, appendSystemPrompt, tools, skills } = options;
|
|
1227
1304
|
const guidelinesList = [];
|
|
1228
1305
|
const guidelinesSet = /* @__PURE__ */ new Set();
|
|
1229
1306
|
const addGuideline = (guideline) => {
|
|
@@ -1239,7 +1316,8 @@ function buildSystemPrompt(options) {
|
|
|
1239
1316
|
addGuideline(g);
|
|
1240
1317
|
}
|
|
1241
1318
|
}
|
|
1242
|
-
const
|
|
1319
|
+
const visibleSkills = (skills ?? []).filter((s) => !s.disableModelInvocation);
|
|
1320
|
+
const skillsBlock = visibleSkills.length > 0 ? buildSkillsDescription(visibleSkills) : "";
|
|
1243
1321
|
for (const g of promptGuidelines ?? []) {
|
|
1244
1322
|
addGuideline(g);
|
|
1245
1323
|
}
|
|
@@ -1260,14 +1338,6 @@ ${toolsList}
|
|
|
1260
1338
|
${skillsSection}
|
|
1261
1339
|
Guidelines:
|
|
1262
1340
|
${guidelines}`;
|
|
1263
|
-
if (date || cwd) {
|
|
1264
|
-
const contextParts = [];
|
|
1265
|
-
if (date) contextParts.push(`Current date: ${date}`);
|
|
1266
|
-
if (cwd) contextParts.push(`Working directory: ${cwd}`);
|
|
1267
|
-
prompt += `
|
|
1268
|
-
|
|
1269
|
-
${contextParts.join("\n")}`;
|
|
1270
|
-
}
|
|
1271
1341
|
if (appendSystemPrompt) {
|
|
1272
1342
|
prompt += `
|
|
1273
1343
|
|
|
@@ -1444,12 +1514,12 @@ function injectPhaseContent(phase, output, messages, mirror) {
|
|
|
1444
1514
|
try {
|
|
1445
1515
|
const phaseContent = phase.filePath ? readPhaseContent(phase) : phase.content ?? phase.description ?? "";
|
|
1446
1516
|
const content = buildPhaseDirectiveMessage(
|
|
1447
|
-
{ name: phase.
|
|
1517
|
+
{ name: phase.name, content: phaseContent },
|
|
1448
1518
|
output
|
|
1449
1519
|
);
|
|
1450
1520
|
const message = createMessage("user", content, {
|
|
1451
1521
|
kind: "phase_prompt",
|
|
1452
|
-
phase: phase.
|
|
1522
|
+
phase: phase.name
|
|
1453
1523
|
});
|
|
1454
1524
|
messages.push(message);
|
|
1455
1525
|
if (mirror && mirror !== messages) mirror.push(message);
|
|
@@ -1594,7 +1664,7 @@ async function executePhaseWithModel(ctx) {
|
|
|
1594
1664
|
for (const toolCall of collected.toolCalls) {
|
|
1595
1665
|
if (toolCall.name !== PhaseRouteTool) continue;
|
|
1596
1666
|
const messageId = ctx.messageManager.start("tool", createRouteToolResultContent(toolCall), {
|
|
1597
|
-
phase: ctx.phase.
|
|
1667
|
+
phase: ctx.phase.name
|
|
1598
1668
|
});
|
|
1599
1669
|
await ctx.messageManager.end(messageId);
|
|
1600
1670
|
}
|
|
@@ -1607,7 +1677,7 @@ async function executePhaseWithModel(ctx) {
|
|
|
1607
1677
|
for (const toolCall of executableToolCalls) {
|
|
1608
1678
|
const result = await ctx.execution.executeTool(roundContext, toolCall);
|
|
1609
1679
|
const messageId = ctx.messageManager.start("tool", createToolResultContent(result), {
|
|
1610
|
-
phase: ctx.phase.
|
|
1680
|
+
phase: ctx.phase.name
|
|
1611
1681
|
});
|
|
1612
1682
|
await ctx.messageManager.end(messageId);
|
|
1613
1683
|
}
|
|
@@ -1646,7 +1716,7 @@ async function runPhaseLoop(config, state, registry) {
|
|
|
1646
1716
|
while (currentPhaseId) {
|
|
1647
1717
|
const availablePhases = [];
|
|
1648
1718
|
for (const [, phase2] of registry.phases) {
|
|
1649
|
-
availablePhases.push({
|
|
1719
|
+
availablePhases.push({ name: phase2.name, description: phase2.description, tools: phase2.tools, skills: phase2.skills, input: phase2.input, isolated: phase2.isolated });
|
|
1650
1720
|
}
|
|
1651
1721
|
const abortResult = LoopGuard.checkAbort(config.signal);
|
|
1652
1722
|
if (abortResult.stopReason !== "none") {
|
|
@@ -1759,7 +1829,7 @@ async function runPhaseLoop(config, state, registry) {
|
|
|
1759
1829
|
} else if (latestUserInputMessage) {
|
|
1760
1830
|
const phaseInputMessage = createMessage("user", latestUserInputMessage.content, {
|
|
1761
1831
|
kind: "phase_input",
|
|
1762
|
-
phase: phase.
|
|
1832
|
+
phase: phase.name
|
|
1763
1833
|
});
|
|
1764
1834
|
config.context.messages.push(phaseInputMessage);
|
|
1765
1835
|
phaseContext.messages.push(phaseInputMessage);
|
|
@@ -1931,7 +2001,7 @@ async function runPhaseLoop(config, state, registry) {
|
|
|
1931
2001
|
ts: createTimestamp()
|
|
1932
2002
|
});
|
|
1933
2003
|
previousPayload = output.payload;
|
|
1934
|
-
previousResults = output.payload !== void 0 ? [{ name: phase.
|
|
2004
|
+
previousResults = output.payload !== void 0 ? [{ name: phase.name, output: output.payload }] : [];
|
|
1935
2005
|
currentPhaseId = targetPhaseId;
|
|
1936
2006
|
}
|
|
1937
2007
|
throw new Error("Phase machine exited without a stop or abort transition.");
|
|
@@ -2084,7 +2154,7 @@ function createPhaseExecution(config, state, allTools, phase, messageManager, to
|
|
|
2084
2154
|
},
|
|
2085
2155
|
async invokeModel(phaseContext) {
|
|
2086
2156
|
if (config.beforePrompt) {
|
|
2087
|
-
phaseContext = await config.beforePrompt(phase.
|
|
2157
|
+
phaseContext = await config.beforePrompt(phase.name, phaseContext);
|
|
2088
2158
|
}
|
|
2089
2159
|
const request = buildModelRequest({
|
|
2090
2160
|
systemPrompt: phaseContext.systemPrompt,
|
|
@@ -2111,7 +2181,7 @@ function createPhaseExecution(config, state, allTools, phase, messageManager, to
|
|
|
2111
2181
|
config,
|
|
2112
2182
|
message: messageManager,
|
|
2113
2183
|
request,
|
|
2114
|
-
phaseId: phase.
|
|
2184
|
+
phaseId: phase.name
|
|
2115
2185
|
}),
|
|
2116
2186
|
{
|
|
2117
2187
|
signal: config.signal,
|
|
@@ -2133,7 +2203,7 @@ function createPhaseExecution(config, state, allTools, phase, messageManager, to
|
|
|
2133
2203
|
}
|
|
2134
2204
|
)
|
|
2135
2205
|
);
|
|
2136
|
-
await config.onModelTranscript?.(result.transcript, { phase: phase.
|
|
2206
|
+
await config.onModelTranscript?.(result.transcript, { phase: phase.name, model: phase.model ?? config.model });
|
|
2137
2207
|
return result;
|
|
2138
2208
|
},
|
|
2139
2209
|
async executeTool(_context, toolCall) {
|
|
@@ -2166,7 +2236,7 @@ async function executeParallelPhase(config, state, registry, phase, payload, con
|
|
|
2166
2236
|
sourcePhaseId
|
|
2167
2237
|
},
|
|
2168
2238
|
state: {
|
|
2169
|
-
current: phase.
|
|
2239
|
+
current: phase.name,
|
|
2170
2240
|
available: Array.from(registry.phases.keys()),
|
|
2171
2241
|
iterations: 0,
|
|
2172
2242
|
payload
|
|
@@ -2185,7 +2255,7 @@ async function executeParallelPhase(config, state, registry, phase, payload, con
|
|
|
2185
2255
|
if (phaseMsgId) removePhaseMessage(messages, phaseMsgId);
|
|
2186
2256
|
const decision = output.toolCalls ? extractRouteCall(output.toolCalls) : void 0;
|
|
2187
2257
|
const resultPayload = decision?.decision[0]?.payload !== void 0 ? normalizePayload(decision.decision[0].payload) : output.payload;
|
|
2188
|
-
return { instanceId, phaseId: phase.
|
|
2258
|
+
return { instanceId, phaseId: phase.name, payload: resultPayload, content: output.message };
|
|
2189
2259
|
}
|
|
2190
2260
|
async function waitForBackgroundTasks(backgroundTasks) {
|
|
2191
2261
|
const entries = Array.from(backgroundTasks.entries());
|
|
@@ -2273,23 +2343,55 @@ function readSkillContent(skill) {
|
|
|
2273
2343
|
}
|
|
2274
2344
|
async function loadSkill(path) {
|
|
2275
2345
|
const resolved = resolveSkillPath(path);
|
|
2276
|
-
|
|
2346
|
+
let loaded;
|
|
2347
|
+
try {
|
|
2348
|
+
loaded = await loadMarkdown(resolved);
|
|
2349
|
+
} catch (error) {
|
|
2350
|
+
if (!(error instanceof FrontmatterParseError)) throw error;
|
|
2351
|
+
warnResourceDiagnostics("skill", resolved, [`frontmatter could not be parsed: ${error.message}`]);
|
|
2352
|
+
throw new ResourceMetadataError("parse_failed", error.message);
|
|
2353
|
+
}
|
|
2354
|
+
const { frontmatter, body } = loaded;
|
|
2355
|
+
const metadata = frontmatter;
|
|
2356
|
+
const id = inferResourceName(resolved, SKILL_MARKER);
|
|
2357
|
+
const frontmatterName = typeof metadata.name === "string" ? metadata.name : void 0;
|
|
2358
|
+
const name = frontmatterName || id;
|
|
2359
|
+
const description = validateDescription(metadata.description);
|
|
2360
|
+
const diagnostics = validateResourceId(id);
|
|
2361
|
+
if (frontmatterName && frontmatterName !== id) {
|
|
2362
|
+
diagnostics.push(...validateResourceName(frontmatterName, id));
|
|
2363
|
+
}
|
|
2364
|
+
diagnostics.push(...description.warnings);
|
|
2365
|
+
warnResourceDiagnostics("skill", resolved, diagnostics);
|
|
2366
|
+
if (description.missing) {
|
|
2367
|
+
throw new ResourceMetadataError("invalid_metadata", "description is required");
|
|
2368
|
+
}
|
|
2277
2369
|
return {
|
|
2278
|
-
name
|
|
2279
|
-
description:
|
|
2370
|
+
name,
|
|
2371
|
+
description: description.description,
|
|
2280
2372
|
filePath: resolved,
|
|
2281
2373
|
baseDir: dirname4(resolved),
|
|
2282
2374
|
content: body,
|
|
2283
|
-
disableModelInvocation:
|
|
2375
|
+
disableModelInvocation: metadata["disable-model-invocation"] === true
|
|
2284
2376
|
};
|
|
2285
2377
|
}
|
|
2286
2378
|
async function loadSkills(targetPath) {
|
|
2287
2379
|
const skillsDir = resolve4(targetPath);
|
|
2288
2380
|
if (existsSync2(skillsDir) && statSync2(skillsDir).isFile()) {
|
|
2289
|
-
|
|
2381
|
+
try {
|
|
2382
|
+
return [await loadSkill(skillsDir)];
|
|
2383
|
+
} catch (error) {
|
|
2384
|
+
if (error instanceof ResourceMetadataError) return [];
|
|
2385
|
+
throw error;
|
|
2386
|
+
}
|
|
2290
2387
|
}
|
|
2291
2388
|
if (existsSync2(join2(skillsDir, SKILL_MARKER))) {
|
|
2292
|
-
|
|
2389
|
+
try {
|
|
2390
|
+
return [await loadSkill(skillsDir)];
|
|
2391
|
+
} catch (error) {
|
|
2392
|
+
if (error instanceof ResourceMetadataError) return [];
|
|
2393
|
+
throw error;
|
|
2394
|
+
}
|
|
2293
2395
|
}
|
|
2294
2396
|
const entries = await readdir2(skillsDir, { withFileTypes: true });
|
|
2295
2397
|
const skills = [];
|
|
@@ -2301,6 +2403,7 @@ async function loadSkills(targetPath) {
|
|
|
2301
2403
|
const skill = await loadSkill(skillFile);
|
|
2302
2404
|
skills.push(skill);
|
|
2303
2405
|
} catch (error) {
|
|
2406
|
+
if (error instanceof ResourceMetadataError) continue;
|
|
2304
2407
|
console.warn(`Failed to load skill "${entry.name}":`, error);
|
|
2305
2408
|
}
|
|
2306
2409
|
}
|
|
@@ -2562,12 +2665,16 @@ function applyProviderRegistration(config) {
|
|
|
2562
2665
|
protocol: config.protocol,
|
|
2563
2666
|
provider: config.id,
|
|
2564
2667
|
baseUrl: config.baseUrl,
|
|
2668
|
+
apiKey: config.apiKey,
|
|
2565
2669
|
reasoning: modelConfig.reasoning,
|
|
2566
2670
|
input: modelConfig.input,
|
|
2567
2671
|
cost: modelConfig.cost,
|
|
2568
2672
|
contextWindow: modelConfig.contextWindow,
|
|
2569
2673
|
maxTokens: modelConfig.maxTokens,
|
|
2570
|
-
...config.headers ? { headers: config.headers } : {}
|
|
2674
|
+
...config.headers ? { headers: config.headers } : {},
|
|
2675
|
+
...config.timeoutMs !== void 0 ? { timeoutMs: config.timeoutMs } : {},
|
|
2676
|
+
...config.maxRetries !== void 0 ? { maxRetries: config.maxRetries } : {},
|
|
2677
|
+
...config.retryDelayMs !== void 0 ? { retryDelayMs: config.retryDelayMs } : {}
|
|
2571
2678
|
});
|
|
2572
2679
|
}
|
|
2573
2680
|
}
|
|
@@ -2794,8 +2901,8 @@ var ExtensionRunner = class {
|
|
|
2794
2901
|
// ---------------------------------------------------------------------------
|
|
2795
2902
|
// Phase management
|
|
2796
2903
|
// ---------------------------------------------------------------------------
|
|
2797
|
-
getPhase(
|
|
2798
|
-
const reg = this.getRegisteredPhase(
|
|
2904
|
+
getPhase(name) {
|
|
2905
|
+
const reg = this.getRegisteredPhase(name);
|
|
2799
2906
|
if (!reg) return void 0;
|
|
2800
2907
|
return this.adaptToPhase(reg);
|
|
2801
2908
|
}
|
|
@@ -2807,8 +2914,8 @@ var ExtensionRunner = class {
|
|
|
2807
2914
|
createPhaseRegistry(input = {}) {
|
|
2808
2915
|
const registered = this.collectRegisteredPhases();
|
|
2809
2916
|
const phases = /* @__PURE__ */ new Map();
|
|
2810
|
-
for (const [
|
|
2811
|
-
phases.set(
|
|
2917
|
+
for (const [name, reg] of registered) {
|
|
2918
|
+
phases.set(name, this.adaptToPhase(reg));
|
|
2812
2919
|
}
|
|
2813
2920
|
const entryPhaseId = input.entryPhaseId ?? null;
|
|
2814
2921
|
return { phases, entryPhaseId };
|
|
@@ -2817,9 +2924,8 @@ var ExtensionRunner = class {
|
|
|
2817
2924
|
adaptToPhase(reg) {
|
|
2818
2925
|
const def = reg.definition;
|
|
2819
2926
|
return {
|
|
2820
|
-
|
|
2821
|
-
|
|
2822
|
-
description: def.description ?? "",
|
|
2927
|
+
name: def.name,
|
|
2928
|
+
description: def.description,
|
|
2823
2929
|
tools: def.tools,
|
|
2824
2930
|
skills: def.skills,
|
|
2825
2931
|
target: def.target,
|
|
@@ -3086,22 +3192,27 @@ var ExtensionRunner = class {
|
|
|
3086
3192
|
});
|
|
3087
3193
|
}
|
|
3088
3194
|
registerPhase(extension, registration) {
|
|
3089
|
-
if (
|
|
3090
|
-
throw new Error(`Phase registration requires
|
|
3091
|
-
}
|
|
3092
|
-
|
|
3093
|
-
|
|
3195
|
+
if (typeof registration.name !== "string" || registration.name.length === 0) {
|
|
3196
|
+
throw new Error(`Phase registration requires a "name" field.`);
|
|
3197
|
+
}
|
|
3198
|
+
const name = registration.name;
|
|
3199
|
+
const description = validateDescription(registration.description);
|
|
3200
|
+
const errors = validateResourceId(name, "name");
|
|
3201
|
+
if (description.missing) errors.push(...description.warnings);
|
|
3202
|
+
errors.push(...validateSkillReferences(registration.skills));
|
|
3203
|
+
errors.push(...validatePhaseTarget(registration.target));
|
|
3204
|
+
if (errors.length > 0) {
|
|
3205
|
+
throw new Error(`Invalid phase registration "${name}": ${errors.join("; ")}`);
|
|
3094
3206
|
}
|
|
3095
|
-
if (
|
|
3096
|
-
|
|
3207
|
+
if (description.warnings.length > 0) {
|
|
3208
|
+
warnResourceDiagnostics("phase", `extension ${extension.path}`, description.warnings);
|
|
3097
3209
|
}
|
|
3098
|
-
if (this.phases.has(
|
|
3099
|
-
throw new Error(`Duplicate phase
|
|
3210
|
+
if (this.phases.has(name)) {
|
|
3211
|
+
throw new Error(`Duplicate phase name: ${name}`);
|
|
3100
3212
|
}
|
|
3101
3213
|
const definition = {
|
|
3102
|
-
|
|
3103
|
-
|
|
3104
|
-
description: registration.description,
|
|
3214
|
+
name,
|
|
3215
|
+
description: description.description,
|
|
3105
3216
|
run: registration.run,
|
|
3106
3217
|
...registration.model ? { model: registration.model } : {}
|
|
3107
3218
|
};
|
|
@@ -3109,8 +3220,8 @@ var ExtensionRunner = class {
|
|
|
3109
3220
|
definition,
|
|
3110
3221
|
source: { extensionPath: extension.path }
|
|
3111
3222
|
};
|
|
3112
|
-
this.phases.set(
|
|
3113
|
-
extension.phases.set(
|
|
3223
|
+
this.phases.set(name, registered);
|
|
3224
|
+
extension.phases.set(name, registered);
|
|
3114
3225
|
this._phaseCache = null;
|
|
3115
3226
|
}
|
|
3116
3227
|
registerProvider(config) {
|
|
@@ -3137,8 +3248,8 @@ var ExtensionRunner = class {
|
|
|
3137
3248
|
}
|
|
3138
3249
|
this.pendingProviders.length = 0;
|
|
3139
3250
|
}
|
|
3140
|
-
getRegisteredPhase(
|
|
3141
|
-
return this.collectRegisteredPhases().get(
|
|
3251
|
+
getRegisteredPhase(name) {
|
|
3252
|
+
return this.collectRegisteredPhases().get(name);
|
|
3142
3253
|
}
|
|
3143
3254
|
collectRegisteredPhases() {
|
|
3144
3255
|
if (this._phaseCache) return this._phaseCache;
|
|
@@ -4472,6 +4583,22 @@ var InMemorySessionStore = class {
|
|
|
4472
4583
|
async open(sessionId) {
|
|
4473
4584
|
return this.sessions.get(sessionId);
|
|
4474
4585
|
}
|
|
4586
|
+
async list() {
|
|
4587
|
+
return Promise.all([...this.sessions.values()].map(async (session) => {
|
|
4588
|
+
const header = await session.getHeader();
|
|
4589
|
+
const entries = "listEntries" in session && typeof session.listEntries === "function" ? await session.listEntries() : [];
|
|
4590
|
+
return {
|
|
4591
|
+
id: header.id,
|
|
4592
|
+
title: header.title,
|
|
4593
|
+
createdAt: header.createdAt,
|
|
4594
|
+
updatedAt: header.updatedAt,
|
|
4595
|
+
messageCount: entries.filter((entry) => entry.type === "message").length
|
|
4596
|
+
};
|
|
4597
|
+
}));
|
|
4598
|
+
}
|
|
4599
|
+
async delete(sessionId) {
|
|
4600
|
+
return this.sessions.delete(sessionId);
|
|
4601
|
+
}
|
|
4475
4602
|
};
|
|
4476
4603
|
var JsonlSessionStore = class {
|
|
4477
4604
|
constructor(sessionsDir) {
|
|
@@ -4487,6 +4614,9 @@ var JsonlSessionStore = class {
|
|
|
4487
4614
|
list() {
|
|
4488
4615
|
return LocalJsonlSessionManager.list(this.sessionsDir);
|
|
4489
4616
|
}
|
|
4617
|
+
delete(sessionId) {
|
|
4618
|
+
return LocalJsonlSessionManager.delete(this.sessionsDir, sessionId);
|
|
4619
|
+
}
|
|
4490
4620
|
};
|
|
4491
4621
|
|
|
4492
4622
|
// src/runtime/agent-runtime.ts
|
|
@@ -4762,6 +4892,7 @@ var AgentRuntime = class _AgentRuntime {
|
|
|
4762
4892
|
toolRuntime;
|
|
4763
4893
|
bindings = /* @__PURE__ */ new Map();
|
|
4764
4894
|
agents = /* @__PURE__ */ new Map();
|
|
4895
|
+
invalidatedAgents = /* @__PURE__ */ new Set();
|
|
4765
4896
|
runHandles = /* @__PURE__ */ new Map();
|
|
4766
4897
|
runWaiters = /* @__PURE__ */ new Map();
|
|
4767
4898
|
pendingRuns = [];
|
|
@@ -4808,6 +4939,15 @@ var AgentRuntime = class _AgentRuntime {
|
|
|
4808
4939
|
const runtime = new _AgentRuntime(options);
|
|
4809
4940
|
activeRuntime = runtime;
|
|
4810
4941
|
try {
|
|
4942
|
+
if (options.sessionProvider) {
|
|
4943
|
+
for (const record of await options.stateStore.listAgents()) {
|
|
4944
|
+
if (record.lifecycleState !== "deleting") continue;
|
|
4945
|
+
const activeRuns = await options.stateStore.listRuns({ agentId: record.id, states: ["queued", "running", "suspended"] });
|
|
4946
|
+
if (activeRuns.length > 0) continue;
|
|
4947
|
+
await options.stateStore.deleteAgentData(record.id);
|
|
4948
|
+
await options.sessionProvider.delete(record.sessionId);
|
|
4949
|
+
}
|
|
4950
|
+
}
|
|
4811
4951
|
await runtime.recover();
|
|
4812
4952
|
runtime.startLeaseRecovery();
|
|
4813
4953
|
return runtime;
|
|
@@ -4826,8 +4966,10 @@ var AgentRuntime = class _AgentRuntime {
|
|
|
4826
4966
|
input: "",
|
|
4827
4967
|
skills: options.context.skills
|
|
4828
4968
|
});
|
|
4969
|
+
const header = await manager.getHeader();
|
|
4829
4970
|
const record = await this.stateStore.createAgent({
|
|
4830
|
-
sessionId: manager.getSessionId()
|
|
4971
|
+
sessionId: manager.getSessionId(),
|
|
4972
|
+
parentSessionId: header.parentSessionId
|
|
4831
4973
|
});
|
|
4832
4974
|
this.pausedAgents.delete(record.id);
|
|
4833
4975
|
const agent = await this.attachAgent(record, manager, options);
|
|
@@ -4839,6 +4981,7 @@ var AgentRuntime = class _AgentRuntime {
|
|
|
4839
4981
|
assertAgentOptions(options);
|
|
4840
4982
|
const record = await this.stateStore.getAgent(agentId);
|
|
4841
4983
|
if (!record) throw new Error(`Agent not found: ${agentId}.`);
|
|
4984
|
+
if (record.lifecycleState !== "active") throw new Error(`Session ${record.sessionId} is ${record.lifecycleState}.`);
|
|
4842
4985
|
if (record.state === "paused") this.pausedAgents.add(record.id);
|
|
4843
4986
|
else this.pausedAgents.delete(record.id);
|
|
4844
4987
|
const provider = this.sessionProvider;
|
|
@@ -4947,6 +5090,82 @@ var AgentRuntime = class _AgentRuntime {
|
|
|
4947
5090
|
async listRuns(input) {
|
|
4948
5091
|
return this.stateStore.listRuns(input);
|
|
4949
5092
|
}
|
|
5093
|
+
async listSessions(input = {}) {
|
|
5094
|
+
const records = await this.stateStore.listAgents();
|
|
5095
|
+
const items = this.sessionProvider ? await this.sessionProvider.list() : [];
|
|
5096
|
+
const summaries = new Map(items.map((item) => [item.id, item]));
|
|
5097
|
+
const activeRuns = await this.stateStore.listActiveRuns();
|
|
5098
|
+
return records.filter((record) => !input.state || record.lifecycleState === input.state).map((record) => {
|
|
5099
|
+
const item = summaries.get(record.sessionId);
|
|
5100
|
+
return {
|
|
5101
|
+
id: record.sessionId,
|
|
5102
|
+
state: record.lifecycleState,
|
|
5103
|
+
createdAt: record.createdAt,
|
|
5104
|
+
updatedAt: item?.updatedAt ?? record.updatedAt,
|
|
5105
|
+
...record.lifecycleState === "archived" ? { archivedAt: record.updatedAt } : {},
|
|
5106
|
+
...record.parentSessionId ? { parentSessionId: record.parentSessionId } : {},
|
|
5107
|
+
agentId: record.id,
|
|
5108
|
+
activeRunCount: activeRuns.filter((run) => run.agentId === record.id).length,
|
|
5109
|
+
...item?.title ? { title: item.title } : {}
|
|
5110
|
+
};
|
|
5111
|
+
});
|
|
5112
|
+
}
|
|
5113
|
+
async archiveSession(sessionId) {
|
|
5114
|
+
const record = await this.requireSessionAgent(sessionId);
|
|
5115
|
+
if (record.lifecycleState === "archived") return;
|
|
5116
|
+
if (record.lifecycleState === "deleting") throw new Error(`Session ${sessionId} is being deleted.`);
|
|
5117
|
+
await this.stateStore.setAgentLifecycleState(record.id, "archived");
|
|
5118
|
+
this.publishEvents();
|
|
5119
|
+
}
|
|
5120
|
+
async unarchiveSession(sessionId) {
|
|
5121
|
+
const record = await this.requireSessionAgent(sessionId);
|
|
5122
|
+
if (record.lifecycleState === "active") return;
|
|
5123
|
+
if (record.lifecycleState === "deleting") throw new Error(`Session ${sessionId} is being deleted.`);
|
|
5124
|
+
await this.stateStore.setAgentLifecycleState(record.id, "active");
|
|
5125
|
+
await this.scheduleQueuedRuns(record.id);
|
|
5126
|
+
this.publishEvents();
|
|
5127
|
+
}
|
|
5128
|
+
async deleteSession(sessionId) {
|
|
5129
|
+
this.assertRunning();
|
|
5130
|
+
if (!this.sessionProvider) throw new Error("Agent Runtime requires a SessionManager provider to delete a Session.");
|
|
5131
|
+
if (!this.sessionProvider.delete) throw new Error("Session Manager provider does not support deletion.");
|
|
5132
|
+
const root = await this.requireSessionAgent(sessionId);
|
|
5133
|
+
const records = await this.stateStore.listAgents();
|
|
5134
|
+
const subtree = /* @__PURE__ */ new Set([root.id]);
|
|
5135
|
+
let changed = true;
|
|
5136
|
+
while (changed) {
|
|
5137
|
+
changed = false;
|
|
5138
|
+
for (const record of records) {
|
|
5139
|
+
if (record.parentSessionId && records.some((candidate) => subtree.has(candidate.id) && candidate.sessionId === record.parentSessionId) && !subtree.has(record.id)) {
|
|
5140
|
+
subtree.add(record.id);
|
|
5141
|
+
changed = true;
|
|
5142
|
+
}
|
|
5143
|
+
}
|
|
5144
|
+
}
|
|
5145
|
+
const activeRuns = await this.stateStore.listActiveRuns();
|
|
5146
|
+
if (activeRuns.some((run) => subtree.has(run.agentId))) {
|
|
5147
|
+
throw new Error(`Session ${sessionId} has active Runs and cannot be deleted.`);
|
|
5148
|
+
}
|
|
5149
|
+
const targets = records.filter((record) => subtree.has(record.id));
|
|
5150
|
+
for (const record of targets) await this.stateStore.setAgentLifecycleState(record.id, "deleting");
|
|
5151
|
+
for (const record of targets) {
|
|
5152
|
+
this.bindings.delete(record.id);
|
|
5153
|
+
this.agents.delete(record.id);
|
|
5154
|
+
this.invalidatedAgents.add(record.id);
|
|
5155
|
+
this.pausedAgents.delete(record.id);
|
|
5156
|
+
for (let index = this.pendingRuns.length - 1; index >= 0; index -= 1) {
|
|
5157
|
+
if (this.pendingRuns[index]?.agentId === record.id) this.pendingRuns.splice(index, 1);
|
|
5158
|
+
}
|
|
5159
|
+
await this.stateStore.deleteAgentData(record.id);
|
|
5160
|
+
await this.sessionProvider.delete(record.sessionId);
|
|
5161
|
+
}
|
|
5162
|
+
this.publishEvents();
|
|
5163
|
+
}
|
|
5164
|
+
async requireSessionAgent(sessionId) {
|
|
5165
|
+
const record = await this.stateStore.getAgentBySessionId(sessionId);
|
|
5166
|
+
if (!record) throw new Error(`Session not found: ${sessionId}.`);
|
|
5167
|
+
return record;
|
|
5168
|
+
}
|
|
4950
5169
|
getAgent(agentId) {
|
|
4951
5170
|
return this.agents.get(agentId);
|
|
4952
5171
|
}
|
|
@@ -5079,7 +5298,11 @@ var AgentRuntime = class _AgentRuntime {
|
|
|
5079
5298
|
}
|
|
5080
5299
|
async submitAgentInput(agentId, input, persist) {
|
|
5081
5300
|
this.assertRunning();
|
|
5301
|
+
if (this.invalidatedAgents.has(agentId)) throw new Error(`Session for Agent ${agentId} was deleted.`);
|
|
5082
5302
|
if (!this.bindings.has(agentId)) throw new Error(`Agent ${agentId} has no live Agent Binding.`);
|
|
5303
|
+
const record = await this.stateStore.getAgent(agentId);
|
|
5304
|
+
if (!record) throw new Error(`Agent ${agentId} was deleted.`);
|
|
5305
|
+
if (record.lifecycleState !== "active") throw new Error(`Session ${record.sessionId} is ${record.lifecycleState}.`);
|
|
5083
5306
|
const enqueued = await this.stateStore.enqueueAgentInput({ agentId, input });
|
|
5084
5307
|
await persist();
|
|
5085
5308
|
if (enqueued.resumed) this.notifyRun(enqueued.run);
|
|
@@ -5335,6 +5558,8 @@ var InMemoryRuntimeStateStore = class {
|
|
|
5335
5558
|
id: createId("agt"),
|
|
5336
5559
|
sessionId: input.sessionId,
|
|
5337
5560
|
state: "active",
|
|
5561
|
+
lifecycleState: "active",
|
|
5562
|
+
...input.parentSessionId ? { parentSessionId: input.parentSessionId } : {},
|
|
5338
5563
|
createdAt: timestamp,
|
|
5339
5564
|
updatedAt: timestamp
|
|
5340
5565
|
};
|
|
@@ -5346,6 +5571,10 @@ var InMemoryRuntimeStateStore = class {
|
|
|
5346
5571
|
const agent = this.agents.get(agentId);
|
|
5347
5572
|
return agent ? clone2(agent) : void 0;
|
|
5348
5573
|
}
|
|
5574
|
+
async getAgentBySessionId(sessionId) {
|
|
5575
|
+
const agent = [...this.agents.values()].find((candidate) => candidate.sessionId === sessionId);
|
|
5576
|
+
return agent ? clone2(agent) : void 0;
|
|
5577
|
+
}
|
|
5349
5578
|
async listAgents() {
|
|
5350
5579
|
return clone2([...this.agents.values()]);
|
|
5351
5580
|
}
|
|
@@ -5358,6 +5587,27 @@ var InMemoryRuntimeStateStore = class {
|
|
|
5358
5587
|
this.recordEvent(state === "paused" ? "agent_paused" : "agent_resumed", { agentId });
|
|
5359
5588
|
return clone2(agent);
|
|
5360
5589
|
}
|
|
5590
|
+
async setAgentLifecycleState(agentId, state) {
|
|
5591
|
+
const agent = this.requireAgent(agentId);
|
|
5592
|
+
if (agent.lifecycleState === state) return clone2(agent);
|
|
5593
|
+
agent.lifecycleState = state;
|
|
5594
|
+
agent.updatedAt = createTimestamp();
|
|
5595
|
+
if (state === "archived") this.recordEvent("session_archived", { agentId });
|
|
5596
|
+
if (state === "active") this.recordEvent("session_unarchived", { agentId });
|
|
5597
|
+
return clone2(agent);
|
|
5598
|
+
}
|
|
5599
|
+
async deleteAgentData(agentId) {
|
|
5600
|
+
this.requireAgent(agentId);
|
|
5601
|
+
const runIds = new Set([...this.runs.values()].filter((run) => run.agentId === agentId).map((run) => run.id));
|
|
5602
|
+
for (const [id, call] of this.toolCalls) if (call.agentId === agentId || runIds.has(call.runId)) this.toolCalls.delete(id);
|
|
5603
|
+
for (const [id, run] of this.runs) if (run.agentId === agentId) this.runs.delete(id);
|
|
5604
|
+
for (const [id, message] of this.messages) if (message.agentId === agentId) this.messages.delete(id);
|
|
5605
|
+
for (let index = this.events.length - 1; index >= 0; index -= 1) {
|
|
5606
|
+
const event = this.events[index];
|
|
5607
|
+
if (event?.agentId === agentId || event?.runId && runIds.has(event.runId)) this.events.splice(index, 1);
|
|
5608
|
+
}
|
|
5609
|
+
this.agents.delete(agentId);
|
|
5610
|
+
}
|
|
5361
5611
|
async enqueueAgentInput(input) {
|
|
5362
5612
|
return this.enqueueAgentInputState(input);
|
|
5363
5613
|
}
|
|
@@ -5787,10 +6037,15 @@ import { Database } from "bun:sqlite";
|
|
|
5787
6037
|
|
|
5788
6038
|
// src/runtime/runtime-schema.ts
|
|
5789
6039
|
var RUNTIME_SCHEMA_SQL = `
|
|
6040
|
+
CREATE TABLE IF NOT EXISTS runtime_schema (
|
|
6041
|
+
version INTEGER PRIMARY KEY NOT NULL
|
|
6042
|
+
);
|
|
5790
6043
|
CREATE TABLE IF NOT EXISTS agents (
|
|
5791
6044
|
id TEXT PRIMARY KEY NOT NULL,
|
|
5792
6045
|
session_id TEXT NOT NULL,
|
|
6046
|
+
parent_session_id TEXT,
|
|
5793
6047
|
state TEXT NOT NULL CHECK (state IN ('active', 'paused')),
|
|
6048
|
+
lifecycle_state TEXT NOT NULL CHECK (lifecycle_state IN ('active', 'archived', 'deleting')),
|
|
5794
6049
|
created_at TEXT NOT NULL,
|
|
5795
6050
|
updated_at TEXT NOT NULL
|
|
5796
6051
|
);
|
|
@@ -5873,6 +6128,8 @@ var RUNTIME_SCHEMA_SQL = `
|
|
|
5873
6128
|
|
|
5874
6129
|
CREATE INDEX IF NOT EXISTS agent_runs_runnable_idx
|
|
5875
6130
|
ON agent_runs (state, created_at);
|
|
6131
|
+
CREATE UNIQUE INDEX IF NOT EXISTS agents_session_id_unique
|
|
6132
|
+
ON agents (session_id);
|
|
5876
6133
|
`;
|
|
5877
6134
|
function initializeRuntimeSchema(database) {
|
|
5878
6135
|
const initialize = database.transaction(() => {
|
|
@@ -5889,6 +6146,7 @@ function initializeRuntimeSchema(database) {
|
|
|
5889
6146
|
}
|
|
5890
6147
|
});
|
|
5891
6148
|
initialize();
|
|
6149
|
+
database.run("INSERT OR IGNORE INTO runtime_schema (version) VALUES (2)");
|
|
5892
6150
|
}
|
|
5893
6151
|
|
|
5894
6152
|
// src/runtime/sqlite-store.ts
|
|
@@ -5932,13 +6190,15 @@ var SqliteRuntimeStateStore = class {
|
|
|
5932
6190
|
id: createId("agt"),
|
|
5933
6191
|
sessionId: input.sessionId,
|
|
5934
6192
|
state: "active",
|
|
6193
|
+
lifecycleState: "active",
|
|
6194
|
+
...input.parentSessionId ? { parentSessionId: input.parentSessionId } : {},
|
|
5935
6195
|
createdAt: timestamp,
|
|
5936
6196
|
updatedAt: timestamp
|
|
5937
6197
|
};
|
|
5938
6198
|
const create = this.database.transaction(() => {
|
|
5939
6199
|
this.database.run(
|
|
5940
|
-
"INSERT INTO agents (id, session_id, state, created_at, updated_at) VALUES (?, ?, ?, ?, ?)",
|
|
5941
|
-
[agent.id, agent.sessionId, agent.state, agent.createdAt, agent.updatedAt]
|
|
6200
|
+
"INSERT INTO agents (id, session_id, parent_session_id, state, lifecycle_state, created_at, updated_at) VALUES (?, ?, ?, ?, ?, ?, ?)",
|
|
6201
|
+
[agent.id, agent.sessionId, agent.parentSessionId ?? null, agent.state, agent.lifecycleState, agent.createdAt, agent.updatedAt]
|
|
5942
6202
|
);
|
|
5943
6203
|
this.recordEvent("agent_created", { agentId: agent.id });
|
|
5944
6204
|
});
|
|
@@ -5949,6 +6209,10 @@ var SqliteRuntimeStateStore = class {
|
|
|
5949
6209
|
const row = this.database.query("SELECT * FROM agents WHERE id = ?").get(agentId);
|
|
5950
6210
|
return row ? this.agentFromRow(row) : void 0;
|
|
5951
6211
|
}
|
|
6212
|
+
async getAgentBySessionId(sessionId) {
|
|
6213
|
+
const row = this.database.query("SELECT * FROM agents WHERE session_id = ?").get(sessionId);
|
|
6214
|
+
return row ? this.agentFromRow(row) : void 0;
|
|
6215
|
+
}
|
|
5952
6216
|
async listAgents() {
|
|
5953
6217
|
const rows = this.database.query("SELECT * FROM agents ORDER BY created_at, id").all();
|
|
5954
6218
|
return rows.map((row) => this.agentFromRow(row));
|
|
@@ -5964,6 +6228,34 @@ var SqliteRuntimeStateStore = class {
|
|
|
5964
6228
|
})();
|
|
5965
6229
|
return clone3(updated);
|
|
5966
6230
|
}
|
|
6231
|
+
async setAgentLifecycleState(agentId, state) {
|
|
6232
|
+
const current = this.requireAgent(agentId);
|
|
6233
|
+
if (current.lifecycleState === state) return clone3(current);
|
|
6234
|
+
const { timestamp } = timestampFor();
|
|
6235
|
+
const updated = this.database.transaction(() => {
|
|
6236
|
+
this.database.run("UPDATE agents SET lifecycle_state = ?, updated_at = ? WHERE id = ?", [state, timestamp, agentId]);
|
|
6237
|
+
if (state === "archived") this.recordEvent("session_archived", { agentId });
|
|
6238
|
+
if (state === "active" && current.lifecycleState === "archived") this.recordEvent("session_unarchived", { agentId });
|
|
6239
|
+
return this.requireAgent(agentId);
|
|
6240
|
+
})();
|
|
6241
|
+
return clone3(updated);
|
|
6242
|
+
}
|
|
6243
|
+
async deleteAgentData(agentId) {
|
|
6244
|
+
this.database.transaction(() => {
|
|
6245
|
+
const agent = this.requireAgent(agentId);
|
|
6246
|
+
const runIds = this.database.query("SELECT id FROM agent_runs WHERE agent_id = ?").all(agentId).map((row) => row.id);
|
|
6247
|
+
if (runIds.length > 0) {
|
|
6248
|
+
const marks = runIds.map(() => "?").join(",");
|
|
6249
|
+
this.database.run(`DELETE FROM runtime_tool_calls WHERE run_id IN (${marks})`, runIds);
|
|
6250
|
+
this.database.run(`DELETE FROM runtime_leases WHERE run_id IN (${marks})`, runIds);
|
|
6251
|
+
this.database.run(`DELETE FROM runtime_events WHERE agent_id = ? OR run_id IN (${marks})`, [agentId, ...runIds]);
|
|
6252
|
+
this.database.run(`DELETE FROM agent_runs WHERE id IN (${marks})`, runIds);
|
|
6253
|
+
}
|
|
6254
|
+
this.database.run("DELETE FROM runtime_messages WHERE agent_id = ?", [agentId]);
|
|
6255
|
+
this.database.run("DELETE FROM runtime_events WHERE agent_id = ?", [agentId]);
|
|
6256
|
+
this.database.run("DELETE FROM agents WHERE id = ?", [agentId]);
|
|
6257
|
+
})();
|
|
6258
|
+
}
|
|
5967
6259
|
async enqueueAgentInput(input) {
|
|
5968
6260
|
const enqueue = this.database.transaction(() => this.enqueueAgentInputState(input));
|
|
5969
6261
|
return clone3(enqueue());
|
|
@@ -6453,6 +6745,8 @@ var SqliteRuntimeStateStore = class {
|
|
|
6453
6745
|
id: row.id,
|
|
6454
6746
|
sessionId: row.session_id,
|
|
6455
6747
|
state: row.state,
|
|
6748
|
+
lifecycleState: row.lifecycle_state,
|
|
6749
|
+
...row.parent_session_id ? { parentSessionId: row.parent_session_id } : {},
|
|
6456
6750
|
createdAt: row.created_at,
|
|
6457
6751
|
updatedAt: row.updated_at
|
|
6458
6752
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rowan-agent/agent",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.7.1",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -23,9 +23,10 @@
|
|
|
23
23
|
"build": "tsup && bun scripts/check-public-interface.ts"
|
|
24
24
|
},
|
|
25
25
|
"dependencies": {
|
|
26
|
-
"@rowan-agent/models": "0.6.
|
|
26
|
+
"@rowan-agent/models": "0.6.3",
|
|
27
27
|
"jiti": "2.7.0",
|
|
28
|
-
"typebox": "^1.0.0"
|
|
28
|
+
"typebox": "^1.0.0",
|
|
29
|
+
"yaml": "^2.7.0"
|
|
29
30
|
},
|
|
30
31
|
"devDependencies": {
|
|
31
32
|
"tsup": "^8.5.1"
|