@shivaedev/agent-claude-code 0.2.0 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (148) hide show
  1. package/CHANGELOG.md +8 -0
  2. package/README.md +71 -1
  3. package/dist/SessionConnection.d.ts +10 -0
  4. package/dist/SessionConnection.d.ts.map +1 -0
  5. package/dist/SessionConnection.js +2 -0
  6. package/dist/SessionConnection.js.map +1 -0
  7. package/dist/adapters/ClaudeProvider.d.ts +15 -0
  8. package/dist/adapters/ClaudeProvider.d.ts.map +1 -0
  9. package/dist/adapters/ClaudeProvider.js +6 -0
  10. package/dist/adapters/ClaudeProvider.js.map +1 -0
  11. package/dist/adapters/InputQueue.d.ts +11 -0
  12. package/dist/adapters/InputQueue.d.ts.map +1 -0
  13. package/dist/adapters/InputQueue.js +84 -0
  14. package/dist/adapters/InputQueue.js.map +1 -0
  15. package/dist/adapters/blocks.d.ts +7 -0
  16. package/dist/adapters/blocks.d.ts.map +1 -0
  17. package/dist/adapters/blocks.js +57 -0
  18. package/dist/adapters/blocks.js.map +1 -0
  19. package/dist/adapters/consumeSession.d.ts +8 -0
  20. package/dist/adapters/consumeSession.d.ts.map +1 -0
  21. package/dist/adapters/consumeSession.js +26 -0
  22. package/dist/adapters/consumeSession.js.map +1 -0
  23. package/dist/adapters/loadClaudeSdk.d.ts +4 -0
  24. package/dist/adapters/loadClaudeSdk.d.ts.map +1 -0
  25. package/dist/adapters/loadClaudeSdk.js +7 -0
  26. package/dist/adapters/loadClaudeSdk.js.map +1 -0
  27. package/dist/adapters/mirrorKeys.d.ts +10 -0
  28. package/dist/adapters/mirrorKeys.d.ts.map +1 -0
  29. package/dist/adapters/mirrorKeys.js +26 -0
  30. package/dist/adapters/mirrorKeys.js.map +1 -0
  31. package/dist/adapters/openSession.d.ts +23 -0
  32. package/dist/adapters/openSession.d.ts.map +1 -0
  33. package/dist/adapters/openSession.js +78 -0
  34. package/dist/adapters/openSession.js.map +1 -0
  35. package/dist/adapters/openSessionMapping.d.ts +9 -0
  36. package/dist/adapters/openSessionMapping.d.ts.map +1 -0
  37. package/dist/adapters/openSessionMapping.js +65 -0
  38. package/dist/adapters/openSessionMapping.js.map +1 -0
  39. package/dist/adapters/openSubsessions.d.ts +10 -0
  40. package/dist/adapters/openSubsessions.d.ts.map +1 -0
  41. package/dist/adapters/openSubsessions.js +45 -0
  42. package/dist/adapters/openSubsessions.js.map +1 -0
  43. package/dist/adapters/openTurnUsage.d.ts +9 -0
  44. package/dist/adapters/openTurnUsage.d.ts.map +1 -0
  45. package/dist/adapters/openTurnUsage.js +40 -0
  46. package/dist/adapters/openTurnUsage.js.map +1 -0
  47. package/dist/adapters/openWorkflowNodes.d.ts +13 -0
  48. package/dist/adapters/openWorkflowNodes.d.ts.map +1 -0
  49. package/dist/adapters/openWorkflowNodes.js +60 -0
  50. package/dist/adapters/openWorkflowNodes.js.map +1 -0
  51. package/dist/adapters/rateLimitEvent.d.ts +7 -0
  52. package/dist/adapters/rateLimitEvent.d.ts.map +1 -0
  53. package/dist/adapters/rateLimitEvent.js +27 -0
  54. package/dist/adapters/rateLimitEvent.js.map +1 -0
  55. package/dist/adapters/rawPayload.d.ts +5 -0
  56. package/dist/adapters/rawPayload.d.ts.map +1 -0
  57. package/dist/adapters/rawPayload.js +12 -0
  58. package/dist/adapters/rawPayload.js.map +1 -0
  59. package/dist/adapters/repairSession.d.ts +5 -0
  60. package/dist/adapters/repairSession.d.ts.map +1 -0
  61. package/dist/adapters/repairSession.js +17 -0
  62. package/dist/adapters/repairSession.js.map +1 -0
  63. package/dist/adapters/sessionLanes.d.ts +22 -0
  64. package/dist/adapters/sessionLanes.d.ts.map +1 -0
  65. package/dist/adapters/sessionLanes.js +100 -0
  66. package/dist/adapters/sessionLanes.js.map +1 -0
  67. package/dist/adapters/sessionOptions.d.ts +6 -0
  68. package/dist/adapters/sessionOptions.d.ts.map +1 -0
  69. package/dist/adapters/sessionOptions.js +30 -0
  70. package/dist/adapters/sessionOptions.js.map +1 -0
  71. package/dist/adapters/spilledPreview.d.ts +5 -0
  72. package/dist/adapters/spilledPreview.d.ts.map +1 -0
  73. package/dist/adapters/spilledPreview.js +22 -0
  74. package/dist/adapters/spilledPreview.js.map +1 -0
  75. package/dist/adapters/subsessionEndings.d.ts +23 -0
  76. package/dist/adapters/subsessionEndings.d.ts.map +1 -0
  77. package/dist/adapters/subsessionEndings.js +54 -0
  78. package/dist/adapters/subsessionEndings.js.map +1 -0
  79. package/dist/adapters/systemEvents.d.ts +9 -0
  80. package/dist/adapters/systemEvents.d.ts.map +1 -0
  81. package/dist/adapters/systemEvents.js +39 -0
  82. package/dist/adapters/systemEvents.js.map +1 -0
  83. package/dist/adapters/toolIdentity.d.ts +5 -0
  84. package/dist/adapters/toolIdentity.d.ts.map +1 -0
  85. package/dist/adapters/toolIdentity.js +10 -0
  86. package/dist/adapters/toolIdentity.js.map +1 -0
  87. package/dist/adapters/toolServer.d.ts +4 -0
  88. package/dist/adapters/toolServer.d.ts.map +1 -0
  89. package/dist/adapters/toolServer.js +30 -0
  90. package/dist/adapters/toolServer.js.map +1 -0
  91. package/dist/adapters/transcriptEvents.d.ts +5 -0
  92. package/dist/adapters/transcriptEvents.d.ts.map +1 -0
  93. package/dist/adapters/transcriptEvents.js +18 -0
  94. package/dist/adapters/transcriptEvents.js.map +1 -0
  95. package/dist/adapters/workflowAdoption.d.ts +13 -0
  96. package/dist/adapters/workflowAdoption.d.ts.map +1 -0
  97. package/dist/adapters/workflowAdoption.js +48 -0
  98. package/dist/adapters/workflowAdoption.js.map +1 -0
  99. package/dist/adapters/workflowIdentity.d.ts +22 -0
  100. package/dist/adapters/workflowIdentity.d.ts.map +1 -0
  101. package/dist/adapters/workflowIdentity.js +50 -0
  102. package/dist/adapters/workflowIdentity.js.map +1 -0
  103. package/dist/claudeCode.d.ts +6 -1
  104. package/dist/claudeCode.d.ts.map +1 -1
  105. package/dist/claudeCode.js +101 -1
  106. package/dist/claudeCode.js.map +1 -1
  107. package/dist/options.d.ts +17 -0
  108. package/dist/options.d.ts.map +1 -0
  109. package/dist/options.js +2 -0
  110. package/dist/options.js.map +1 -0
  111. package/dist/protocol.d.ts +7 -0
  112. package/dist/protocol.d.ts.map +1 -0
  113. package/dist/protocol.js +2 -0
  114. package/dist/protocol.js.map +1 -0
  115. package/dist/runState.d.ts +584 -0
  116. package/dist/runState.d.ts.map +1 -0
  117. package/dist/runState.js +52 -0
  118. package/dist/runState.js.map +1 -0
  119. package/package.json +32 -4
  120. package/src/SessionConnection.ts +10 -0
  121. package/src/adapters/ClaudeProvider.ts +16 -0
  122. package/src/adapters/InputQueue.ts +89 -0
  123. package/src/adapters/blocks.ts +63 -0
  124. package/src/adapters/consumeSession.ts +35 -0
  125. package/src/adapters/loadClaudeSdk.ts +6 -0
  126. package/src/adapters/mirrorKeys.ts +33 -0
  127. package/src/adapters/openSession.ts +102 -0
  128. package/src/adapters/openSessionMapping.ts +84 -0
  129. package/src/adapters/openSubsessions.ts +70 -0
  130. package/src/adapters/openTurnUsage.ts +52 -0
  131. package/src/adapters/openWorkflowNodes.ts +73 -0
  132. package/src/adapters/rateLimitEvent.ts +45 -0
  133. package/src/adapters/rawPayload.ts +13 -0
  134. package/src/adapters/repairSession.ts +20 -0
  135. package/src/adapters/sessionLanes.ts +127 -0
  136. package/src/adapters/sessionOptions.ts +42 -0
  137. package/src/adapters/spilledPreview.ts +24 -0
  138. package/src/adapters/subsessionEndings.ts +80 -0
  139. package/src/adapters/systemEvents.ts +60 -0
  140. package/src/adapters/toolIdentity.ts +12 -0
  141. package/src/adapters/toolServer.ts +31 -0
  142. package/src/adapters/transcriptEvents.ts +21 -0
  143. package/src/adapters/workflowAdoption.ts +58 -0
  144. package/src/adapters/workflowIdentity.ts +75 -0
  145. package/src/claudeCode.ts +117 -1
  146. package/src/options.ts +19 -0
  147. package/src/protocol.ts +9 -0
  148. package/src/runState.ts +70 -0
@@ -0,0 +1,75 @@
1
+ import type { SDKMessage } from "@anthropic-ai/claude-agent-sdk";
2
+ import { isRecord } from "#adapters/blocks.ts";
3
+ import type { ChildOutcome } from "#protocol.ts";
4
+
5
+ type SystemMessage = Extract<
6
+ SDKMessage,
7
+ {
8
+ type: "system";
9
+ }
10
+ >;
11
+ type TaskProgress = Extract<
12
+ SystemMessage,
13
+ {
14
+ subtype: "task_progress";
15
+ }
16
+ >;
17
+ export interface WorkflowIdentity {
18
+ readonly ended: ChildOutcome | undefined;
19
+ readonly label: string | undefined;
20
+ readonly model: string | undefined;
21
+ readonly phase: string | undefined;
22
+ readonly spawnedBy: string | undefined;
23
+ }
24
+ function stringAt(entry: Record<string, unknown>, field: string) {
25
+ return typeof entry[field] === "string" ? entry[field] : undefined;
26
+ }
27
+ function endingOf(state: string | undefined): ChildOutcome | undefined {
28
+ if (state === "done") {
29
+ return "completed";
30
+ }
31
+ return state === "error" || state === "blocked" ? "failed" : undefined;
32
+ }
33
+ function labelOf(entry: Record<string, unknown>): string | undefined {
34
+ const label = stringAt(entry, "label");
35
+ const phase = stringAt(entry, "phaseTitle");
36
+ if (label === undefined) {
37
+ return phase;
38
+ }
39
+ return phase === undefined ? label : `${phase}: ${label}`;
40
+ }
41
+ function identityOf(entry: Record<string, unknown>, spawnedBy: string | undefined): WorkflowIdentity {
42
+ return {
43
+ ended: endingOf(stringAt(entry, "state")),
44
+ label: labelOf(entry),
45
+ model: stringAt(entry, "model"),
46
+ phase: stringAt(entry, "phaseTitle"),
47
+ spawnedBy,
48
+ };
49
+ }
50
+ function snapshot(message: TaskProgress): readonly unknown[] {
51
+ const carried = "workflow_progress" in message && message.workflow_progress;
52
+ return Array.isArray(carried) ? carried : [];
53
+ }
54
+ export interface WorkflowIdentities {
55
+ readonly observe: (message: TaskProgress) => void;
56
+ readonly of: (agentId: string) => WorkflowIdentity | undefined;
57
+ }
58
+ // `workflow_progress` is absent from the published SDK types; live progress frames carry the workflow-agent identity snapshot.
59
+ export function openWorkflowIdentities(): WorkflowIdentities {
60
+ const identities = new Map<string, WorkflowIdentity>();
61
+ return {
62
+ observe: (message) => {
63
+ for (const entry of snapshot(message)) {
64
+ if (!isRecord(entry) || entry.type !== "workflow_agent") {
65
+ continue;
66
+ }
67
+ const agentId = stringAt(entry, "agentId");
68
+ if (agentId !== undefined) {
69
+ identities.set(agentId, identityOf(entry, message.tool_use_id));
70
+ }
71
+ }
72
+ },
73
+ of: (agentId) => identities.get(agentId),
74
+ };
75
+ }
package/src/claudeCode.ts CHANGED
@@ -1 +1,117 @@
1
- export {};
1
+ import { Deferred, Effect, Layer, Option, Ref, Schema, Stream } from "effect";
2
+ import { Backend, type ConfiguredBackend } from "@shivaedev/agent/backend.ts";
3
+ import { ProtocolFailure, Rejected, UnknownAcceptance } from "@shivaedev/agent/failures.ts";
4
+ import type { BackendReference, Receipt, StartRequest } from "@shivaedev/agent/run.ts";
5
+ import { ClaudeProvider } from "#adapters/ClaudeProvider.ts";
6
+ import { openSession } from "#adapters/openSession.ts";
7
+ import type { ClaudeOptions } from "#options.ts";
8
+ import { makeRunState, type RunState } from "#runState.ts";
9
+
10
+ const decodeEffort = Schema.decodeUnknownOption(Schema.Literals(["low", "medium", "high", "xhigh", "max"]));
11
+ function protocol(message: string) {
12
+ return new ProtocolFailure({ backend: "claude-code", message });
13
+ }
14
+ export const makeClaudeBackend = Effect.fn("ClaudeCode.makeClaudeBackend")(function* (
15
+ options: ClaudeOptions = {},
16
+ ): Effect.fn.Return<ConfiguredBackend, never, ClaudeProvider> {
17
+ const peer = yield* ClaudeProvider;
18
+ const runs = new Map<string, RunState>();
19
+ function lookup(receipt: Receipt) {
20
+ return Effect.suspend(() => {
21
+ const run =
22
+ receipt.backend === "claude-code" && receipt.reference.version === 1 && receipt.reference.value === receipt.operationId
23
+ ? runs.get(receipt.operationId)
24
+ : undefined;
25
+ return run === undefined ? Effect.fail(protocol("No run evidence for this receipt in this backend instance")) : Effect.succeed(run);
26
+ });
27
+ }
28
+ const start = Effect.fn("ClaudeCode.start")(function* (request: StartRequest, resume?: typeof BackendReference.Type) {
29
+ if (resume !== undefined && resume.version !== 1) {
30
+ return yield* Effect.fail(new Rejected({ backend: "claude-code", message: "Unsupported native session reference version" }));
31
+ }
32
+ const effort = request.effort === undefined ? Option.none() : decodeEffort(request.effort);
33
+ if (request.effort !== undefined && Option.isNone(effort)) {
34
+ return yield* Effect.fail(new Rejected({ backend: "claude-code", message: `Unsupported Claude effort: ${request.effort}` }));
35
+ }
36
+ const existing = runs.get(request.operationId);
37
+ if (existing !== undefined) {
38
+ return yield* Deferred.await(existing.acknowledged);
39
+ }
40
+ const session = yield* openSession(request, options, Option.getOrUndefined(effort), resume?.value).pipe(
41
+ Effect.provideService(ClaudeProvider, peer),
42
+ Effect.mapError((error) => new UnknownAcceptance({ backend: "claude-code", message: error.message, operationId: request.operationId })),
43
+ );
44
+ const run = yield* makeRunState(request.operationId, session);
45
+ runs.set(request.operationId, run);
46
+ yield* session
47
+ .queue(request.input)
48
+ .pipe(Effect.mapError((error) => new UnknownAcceptance({ backend: "claude-code", message: error.message, operationId: request.operationId })));
49
+ return yield* Deferred.await(run.acknowledged);
50
+ });
51
+ return {
52
+ backend: "claude-code",
53
+ events: (receipt) => Stream.unwrap(Effect.map(lookup(receipt), (run) => run.events)),
54
+ features: {
55
+ imageInput: { reason: "This adapter accepts text input only", support: "unsupported" },
56
+ interruption: {
57
+ implementation: { interrupt: (receipt) => Effect.flatMap(lookup(receipt), (run) => run.session.interrupt) },
58
+ support: "supported",
59
+ },
60
+ models: { reason: "Model discovery is not exposed; supply a provider model in the start request", support: "unsupported" },
61
+ queuedInput: {
62
+ implementation: { queue: (receipt, input) => Effect.flatMap(lookup(receipt), (run) => run.session.queue(input)) },
63
+ support: "supported",
64
+ },
65
+ sessions: { implementation: { resume: (session, request) => start(request, session) }, support: "supported" },
66
+ steering: {
67
+ implementation: { steer: (receipt, input) => Effect.flatMap(lookup(receipt), (run) => run.session.queue(input, "now")) },
68
+ support: "supported",
69
+ },
70
+ transcript: {
71
+ implementation: {
72
+ read: (receipt) =>
73
+ Effect.gen(function* () {
74
+ const run = yield* lookup(receipt);
75
+ const events = yield* Ref.get(run.history);
76
+ return { completeness: events.some((event) => event.type === "gap") ? "partial" : "complete", events } as const;
77
+ }),
78
+ },
79
+ support: "supported",
80
+ },
81
+ },
82
+ inspect: (receipt) =>
83
+ Effect.gen(function* () {
84
+ const found = runs.get(receipt.operationId);
85
+ if (found === undefined) {
86
+ return { reason: "This backend instance has no evidence; absence does not prove non-acceptance", type: "missing" } as const;
87
+ }
88
+ const run = yield* lookup(receipt);
89
+ const error = yield* Ref.get(run.failure);
90
+ if (error !== undefined) {
91
+ return yield* Effect.fail(error);
92
+ }
93
+ return { state: yield* Ref.get(run.state), type: "known" } as const;
94
+ }),
95
+ reconcile: (operationId) =>
96
+ Effect.gen(function* () {
97
+ const run = runs.get(operationId);
98
+ const acknowledgement = run === undefined ? Option.none() : yield* Deferred.poll(run.acknowledged);
99
+ if (Option.isSome(acknowledgement)) {
100
+ return yield* acknowledgement.value.pipe(
101
+ Effect.map((receipt) => ({ receipt, type: "accepted" }) as const),
102
+ Effect.catchTag("UnknownAcceptance", (error) => Effect.succeed({ operationId, reason: error.message, type: "uncertain" } as const)),
103
+ );
104
+ }
105
+ return {
106
+ operationId,
107
+ reason: "No native acknowledgement in this backend instance; no replacement work was submitted",
108
+ type: "uncertain",
109
+ } as const;
110
+ }),
111
+ start,
112
+ };
113
+ });
114
+
115
+ export function claudeCodeLayer(options: ClaudeOptions = {}) {
116
+ return Layer.effect(Backend, makeClaudeBackend(options)).pipe(Layer.provide(ClaudeProvider.layer));
117
+ }
package/src/options.ts ADDED
@@ -0,0 +1,19 @@
1
+ import type { Effect } from "effect";
2
+ export interface ClaudeTool {
3
+ readonly call: (
4
+ args: unknown,
5
+ callId: string,
6
+ ) => Effect.Effect<{
7
+ readonly text: string;
8
+ readonly ok: boolean;
9
+ }>;
10
+ readonly description: string;
11
+ readonly inputSchema: Readonly<Record<string, unknown>>;
12
+ readonly name: string;
13
+ }
14
+ export interface ClaudeOptions {
15
+ readonly executable?: string;
16
+ readonly skills?: string;
17
+ readonly systemPrompt?: string;
18
+ readonly tools?: readonly ClaudeTool[];
19
+ }
@@ -0,0 +1,9 @@
1
+ import type { RunEvent } from "@shivaedev/agent/events.ts";
2
+ export type Origin = NonNullable<RunEvent["origin"]>;
3
+ export type RawPayload = NonNullable<RunEvent["raw"]>;
4
+ export type ChildOutcome = Extract<
5
+ RunEvent,
6
+ {
7
+ type: "child.ended";
8
+ }
9
+ >["outcome"];
@@ -0,0 +1,70 @@
1
+ import { Deferred, Effect, Exit, PubSub, Ref, Stream, type Take } from "effect";
2
+ import type { RunEvent } from "@shivaedev/agent/events.ts";
3
+ import { type ReadFailure, UnknownAcceptance } from "@shivaedev/agent/failures.ts";
4
+ import type { ExecutionState, Receipt } from "@shivaedev/agent/run.ts";
5
+ import type { SessionConnection } from "#SessionConnection.ts";
6
+ export const makeRunState = Effect.fn("ClaudeCode.makeRunState")(function* (operationId: string, session: SessionConnection) {
7
+ const acknowledged = yield* Deferred.make<Receipt, UnknownAcceptance>();
8
+ const history = yield* Ref.make<readonly RunEvent[]>([]);
9
+ const state = yield* Ref.make<typeof ExecutionState.Type>("accepted");
10
+ const terminalSeen = yield* Ref.make(false);
11
+ const failure = yield* Ref.make<ReadFailure | undefined>(undefined);
12
+ const hub = yield* PubSub.unbounded<Take.Take<RunEvent, ReadFailure>>({ replay: Number.POSITIVE_INFINITY });
13
+ function publish(event: RunEvent) {
14
+ return Ref.update(history, (events) => [...events, event]).pipe(Effect.andThen(PubSub.publish(hub, [event])));
15
+ }
16
+ function observe(event: RunEvent) {
17
+ return Effect.gen(function* () {
18
+ if (event.type === "session.opened") {
19
+ const receipt: Receipt = {
20
+ backend: "claude-code",
21
+ operationId,
22
+ reference: { value: operationId, version: 1 },
23
+ session: { value: event.reference, version: 1 },
24
+ };
25
+ if (yield* Deferred.succeed(acknowledged, receipt)) {
26
+ yield* publish({ receipt, type: "acknowledged" });
27
+ }
28
+ }
29
+ if (event.type === "execution") {
30
+ yield* Ref.set(state, event.state);
31
+ if (event.state !== "awaiting-input") {
32
+ yield* Ref.set(terminalSeen, event.state === "completed" || event.state === "failed" || event.state === "interrupted");
33
+ }
34
+ }
35
+ yield* publish(event);
36
+ });
37
+ }
38
+ const consume = session.events.pipe(
39
+ Stream.runForEach(observe),
40
+ Effect.matchEffect({
41
+ onFailure: (error) =>
42
+ Ref.set(failure, error).pipe(
43
+ Effect.andThen(publish({ detail: error.message, kind: "provider-unavailable", type: "gap" })),
44
+ Effect.andThen(PubSub.end(hub, Exit.fail(error))),
45
+ ),
46
+ onSuccess: () =>
47
+ Effect.gen(function* () {
48
+ const terminal = yield* Ref.get(terminalSeen);
49
+ if (!terminal) {
50
+ yield* publish({ detail: "The provider stream ended without a terminal turn result", kind: "missing-output", type: "gap" });
51
+ }
52
+ yield* PubSub.end(hub, Exit.void);
53
+ }),
54
+ }),
55
+ Effect.ensuring(
56
+ Deferred.fail(
57
+ acknowledged,
58
+ new UnknownAcceptance({
59
+ backend: "claude-code",
60
+ message: "The provider ended before acknowledging its native session identity",
61
+ operationId,
62
+ }),
63
+ ),
64
+ ),
65
+ Effect.onInterrupt(() => PubSub.end(hub, Exit.void)),
66
+ );
67
+ yield* Effect.forkScoped(consume);
68
+ return { acknowledged, events: Stream.fromPubSubTake(hub), failure, history, session, state };
69
+ });
70
+ export type RunState = Effect.Success<ReturnType<typeof makeRunState>>;