@karowanorg/orc-harness-claude 0.1.0 → 0.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/index.js +66 -299
- package/package.json +4 -4
package/README.md
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
# @karowanorg/orc-harness-claude
|
|
2
2
|
|
|
3
|
-
Claude harness for orc
|
|
3
|
+
Claude harness for orc via the Anthropic Agent SDK.
|
|
4
4
|
|
|
5
5
|
Part of orc - model-authored promise-native agent programs with deterministic replay, live monitoring, and pluggable harnesses. See the orc repository README for the full picture.
|
package/dist/index.js
CHANGED
|
@@ -1,15 +1,11 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* The claude harness — built-in harness #1.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
* SDK owns that child's lifecycle; cancellation goes through abortController.
|
|
10
|
-
* - REMOTE (host set): the claude CLI over the executor (ssh), speaking the
|
|
11
|
-
* same stream-json protocol. v1 remote supports approval modes auto/bypass
|
|
12
|
-
* only (manual/accept-edits need an approval back-channel — fail closed).
|
|
4
|
+
* Transport: @anthropic-ai/claude-agent-sdk `query()` — canUseTool approval
|
|
5
|
+
* bridging, settingSources (user's own settings are the policy substrate),
|
|
6
|
+
* native structured output, session resume. Note the SDK still SPAWNS the
|
|
7
|
+
* bundled CLI as a subprocess (verified in sdk source) — the SDK owns that
|
|
8
|
+
* child's lifecycle; cancellation goes through abortController.
|
|
13
9
|
*
|
|
14
10
|
* Approval-mode mapping (verified against SDK 0.3.215 types):
|
|
15
11
|
* - manual -> permissionMode "default" + canUseTool -> requestApproval
|
|
@@ -51,7 +47,6 @@ export function systemClaudePath() {
|
|
|
51
47
|
}
|
|
52
48
|
const WRITE_TOOLS = ["Edit", "Write", "NotebookEdit"];
|
|
53
49
|
const READ_ONLY_DISALLOWED_TOOLS = ["Bash", ...WRITE_TOOLS];
|
|
54
|
-
const POST_EXIT_DRAIN_MS = 10_000;
|
|
55
50
|
export const claudeHarness = {
|
|
56
51
|
name: "claude",
|
|
57
52
|
async discover({ executor }) {
|
|
@@ -66,64 +61,58 @@ export const claudeHarness = {
|
|
|
66
61
|
detail: "claude CLI not found on this executor",
|
|
67
62
|
};
|
|
68
63
|
}
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
64
|
+
const models = [];
|
|
65
|
+
// Native catalog via the SDK's supportedModels control request. Each
|
|
66
|
+
// ModelInfo carries its OWN supportedEffortLevels (per-model). Point at
|
|
67
|
+
// the system claude — orc doesn't ship the SDK's bundled CLI.
|
|
68
|
+
try {
|
|
69
|
+
const claudePath = systemClaudePath();
|
|
70
|
+
const q = query({
|
|
71
|
+
prompt: "",
|
|
72
|
+
options: { maxTurns: 0, ...(claudePath ? { pathToClaudeCodeExecutable: claudePath } : {}) },
|
|
73
|
+
});
|
|
74
|
+
// NB: clear the deadline timer once the race settles — an uncleared
|
|
75
|
+
// setTimeout keeps the Node event loop alive, so the CLI would print
|
|
76
|
+
// its result and then appear to hang until the timer fired.
|
|
77
|
+
let deadline;
|
|
78
|
+
const infos = await Promise.race([
|
|
79
|
+
q.supportedModels(),
|
|
80
|
+
new Promise((_, rej) => {
|
|
81
|
+
deadline = setTimeout(() => rej(new Error("timeout")), 20_000);
|
|
82
|
+
}),
|
|
83
|
+
]).finally(() => deadline && clearTimeout(deadline));
|
|
84
|
+
const seen = new Set();
|
|
85
|
+
for (const m of infos) {
|
|
86
|
+
const id = m.resolvedModel ?? m.value;
|
|
87
|
+
if (!id || seen.has(id))
|
|
88
|
+
continue;
|
|
89
|
+
seen.add(id);
|
|
90
|
+
const info = m;
|
|
91
|
+
models.push({
|
|
92
|
+
id,
|
|
93
|
+
displayName: info.displayName,
|
|
94
|
+
reasoningEfforts: info.supportsEffort === false ? [] : (info.supportedEffortLevels ?? []),
|
|
79
95
|
});
|
|
80
|
-
// NB: clear the deadline timer once the race settles — an uncleared
|
|
81
|
-
// setTimeout keeps the Node event loop alive, so the CLI would print
|
|
82
|
-
// its result and then appear to hang until the timer fired.
|
|
83
|
-
let deadline;
|
|
84
|
-
const infos = await Promise.race([
|
|
85
|
-
q.supportedModels(),
|
|
86
|
-
new Promise((_, rej) => {
|
|
87
|
-
deadline = setTimeout(() => rej(new Error("timeout")), 20_000);
|
|
88
|
-
}),
|
|
89
|
-
]).finally(() => deadline && clearTimeout(deadline));
|
|
90
|
-
const seen = new Set();
|
|
91
|
-
for (const m of infos) {
|
|
92
|
-
const id = m.resolvedModel ?? m.value;
|
|
93
|
-
if (!id || seen.has(id))
|
|
94
|
-
continue;
|
|
95
|
-
seen.add(id);
|
|
96
|
-
const info = m;
|
|
97
|
-
models.push({
|
|
98
|
-
id,
|
|
99
|
-
displayName: info.displayName,
|
|
100
|
-
reasoningEfforts: info.supportsEffort === false ? [] : (info.supportedEffortLevels ?? []),
|
|
101
|
-
});
|
|
102
|
-
}
|
|
103
|
-
await q.interrupt().catch(() => undefined);
|
|
104
|
-
}
|
|
105
|
-
catch {
|
|
106
|
-
/* version-only capability report */
|
|
107
96
|
}
|
|
97
|
+
await q.interrupt().catch(() => undefined);
|
|
98
|
+
}
|
|
99
|
+
catch {
|
|
100
|
+
/* version-only capability report */
|
|
108
101
|
}
|
|
109
102
|
return {
|
|
110
103
|
available: true,
|
|
111
104
|
version: version.stdout.trim().split("\n")[0],
|
|
112
105
|
models,
|
|
113
|
-
approvalModes:
|
|
106
|
+
approvalModes: ["manual", "accept-edits", "auto", "bypass"],
|
|
114
107
|
structuredOutput: true,
|
|
115
108
|
sessions: true,
|
|
116
|
-
detail:
|
|
117
|
-
? "claude CLI over ssh (stream-json); remote approvals limited to auto/bypass in v1"
|
|
118
|
-
: "Agent SDK; model catalog via supportedModels()",
|
|
109
|
+
detail: "Agent SDK; model catalog via supportedModels()",
|
|
119
110
|
};
|
|
120
111
|
},
|
|
121
|
-
invoke
|
|
122
|
-
return req.host ? invokeRemoteCli(req, ctx) : invokeSdk(req, ctx);
|
|
123
|
-
},
|
|
112
|
+
invoke: invokeSdk,
|
|
124
113
|
};
|
|
125
114
|
// ---------------------------------------------------------------------------
|
|
126
|
-
//
|
|
115
|
+
// Transport: Agent SDK
|
|
127
116
|
// ---------------------------------------------------------------------------
|
|
128
117
|
async function* invokeSdk(req, ctx) {
|
|
129
118
|
if (ctx.signal.aborted) {
|
|
@@ -216,6 +205,7 @@ async function* invokeSdk(req, ctx) {
|
|
|
216
205
|
// from the first frame; refine `model` to the SDK's resolved id when it arrives.
|
|
217
206
|
yield { kind: "model", model: req.model, reasoningEffort: req.reasoningEffort };
|
|
218
207
|
let reportedModel = req.model;
|
|
208
|
+
let syntheticFailure;
|
|
219
209
|
try {
|
|
220
210
|
for await (const message of query({ prompt: req.prompt, options })) {
|
|
221
211
|
const m = message.message?.model ??
|
|
@@ -224,6 +214,16 @@ async function* invokeSdk(req, ctx) {
|
|
|
224
214
|
reportedModel = m;
|
|
225
215
|
yield { kind: "model", model: m, reasoningEffort: req.reasoningEffort };
|
|
226
216
|
}
|
|
217
|
+
if (m === "<synthetic>") {
|
|
218
|
+
syntheticFailure = assistantText(message) || "Claude Code returned a synthetic local failure";
|
|
219
|
+
yield { kind: "error", message: `claude unavailable: ${syntheticFailure}` };
|
|
220
|
+
continue;
|
|
221
|
+
}
|
|
222
|
+
// Claude Code emits a nominal success result after local auth/quota
|
|
223
|
+
// failures. Do not turn that status prose into `{text: ...}` and then
|
|
224
|
+
// misreport it as an output-schema defect.
|
|
225
|
+
if (syntheticFailure && message.type === "result")
|
|
226
|
+
continue;
|
|
227
227
|
for (const ev of mapSdkMessage(message))
|
|
228
228
|
yield ev;
|
|
229
229
|
}
|
|
@@ -240,6 +240,17 @@ async function* invokeSdk(req, ctx) {
|
|
|
240
240
|
ctx.signal.removeEventListener("abort", onAbort);
|
|
241
241
|
}
|
|
242
242
|
}
|
|
243
|
+
function assistantText(message) {
|
|
244
|
+
if (message.type !== "assistant")
|
|
245
|
+
return "";
|
|
246
|
+
return (message.message.content ?? [])
|
|
247
|
+
.flatMap((block) => {
|
|
248
|
+
const candidate = block;
|
|
249
|
+
return candidate.type === "text" && typeof candidate.text === "string" ? [candidate.text] : [];
|
|
250
|
+
})
|
|
251
|
+
.join("\n")
|
|
252
|
+
.trim();
|
|
253
|
+
}
|
|
243
254
|
function* mapSdkMessage(message) {
|
|
244
255
|
const now = Date.now();
|
|
245
256
|
if (message.type === "system" && message.subtype === "init") {
|
|
@@ -303,205 +314,6 @@ function* mapSdkMessage(message) {
|
|
|
303
314
|
}
|
|
304
315
|
}
|
|
305
316
|
// ---------------------------------------------------------------------------
|
|
306
|
-
// Remote transport: claude CLI over the executor (same stream-json protocol)
|
|
307
|
-
// ---------------------------------------------------------------------------
|
|
308
|
-
async function* invokeRemoteCli(req, ctx) {
|
|
309
|
-
if (ctx.signal.aborted) {
|
|
310
|
-
yield { kind: "error", message: `aborted: ${String(ctx.signal.reason ?? "cancelled")}` };
|
|
311
|
-
return;
|
|
312
|
-
}
|
|
313
|
-
if (req.approvalMode === "manual" || req.approvalMode === "accept-edits") {
|
|
314
|
-
yield {
|
|
315
|
-
kind: "error",
|
|
316
|
-
message: `approval mode "${req.approvalMode}" is not supported for remote claude leaves in v1 ` +
|
|
317
|
-
`(no approval back-channel over ssh yet) — use auto or bypass, fail-closed`,
|
|
318
|
-
};
|
|
319
|
-
return;
|
|
320
|
-
}
|
|
321
|
-
const args = ["claude", "-p", "--verbose", "--output-format", "stream-json"];
|
|
322
|
-
const sandboxWrites = req.sandbox === true && !req.readOnly;
|
|
323
|
-
if (sandboxWrites) {
|
|
324
|
-
args.push("--permission-mode", "acceptEdits");
|
|
325
|
-
}
|
|
326
|
-
else if (req.approvalMode === "bypass" && !req.readOnly) {
|
|
327
|
-
args.push("--permission-mode", "bypassPermissions", "--dangerously-skip-permissions");
|
|
328
|
-
}
|
|
329
|
-
else {
|
|
330
|
-
args.push("--permission-mode", "dontAsk");
|
|
331
|
-
}
|
|
332
|
-
const roots = sandboxRoots(req);
|
|
333
|
-
if (req.readOnly)
|
|
334
|
-
args.push("--disallowedTools", READ_ONLY_DISALLOWED_TOOLS.join(","));
|
|
335
|
-
if (sandboxWrites)
|
|
336
|
-
args.push("--setting-sources=", "--strict-mcp-config");
|
|
337
|
-
if (sandboxWrites) {
|
|
338
|
-
args.push("--settings", JSON.stringify({
|
|
339
|
-
sandbox: {
|
|
340
|
-
enabled: true,
|
|
341
|
-
failIfUnavailable: true,
|
|
342
|
-
allowUnsandboxedCommands: false,
|
|
343
|
-
filesystem: { allowWrite: roots },
|
|
344
|
-
},
|
|
345
|
-
}));
|
|
346
|
-
for (const root of roots.slice(1))
|
|
347
|
-
args.push("--add-dir", root);
|
|
348
|
-
}
|
|
349
|
-
if (req.model)
|
|
350
|
-
args.push("--model", req.model);
|
|
351
|
-
if (req.sessionId)
|
|
352
|
-
args.push("--resume", req.sessionId);
|
|
353
|
-
if (req.system)
|
|
354
|
-
args.push("--append-system-prompt", req.system);
|
|
355
|
-
if (req.schema)
|
|
356
|
-
args.push("--json-schema", JSON.stringify(req.schema));
|
|
357
|
-
const proc = ctx.executor.spawn(args, { cwd: req.cwd, stdin: "pipe" });
|
|
358
|
-
const onAbort = () => proc.kill();
|
|
359
|
-
ctx.signal.addEventListener("abort", onAbort, { once: true });
|
|
360
|
-
if (ctx.signal.aborted)
|
|
361
|
-
onAbort();
|
|
362
|
-
const stderrDone = drainStderr(proc.stderr, ctx.log);
|
|
363
|
-
const cancelStdoutGuard = guardPostExitDrain(proc.stdout, proc.exited, "stdout", ctx.log);
|
|
364
|
-
const cancelStderrGuard = guardPostExitDrain(proc.stderr, proc.exited, "stderr", ctx.log);
|
|
365
|
-
let stdinError;
|
|
366
|
-
if (!proc.stdin) {
|
|
367
|
-
ctx.signal.removeEventListener("abort", onAbort);
|
|
368
|
-
proc.kill();
|
|
369
|
-
destroyStream(proc.stdout);
|
|
370
|
-
await stderrDone;
|
|
371
|
-
cancelStdoutGuard();
|
|
372
|
-
cancelStderrGuard();
|
|
373
|
-
yield { kind: "error", message: "remote claude stdin is unavailable" };
|
|
374
|
-
return;
|
|
375
|
-
}
|
|
376
|
-
proc.stdin.on("error", (err) => {
|
|
377
|
-
stdinError = err;
|
|
378
|
-
if (err.code !== "EPIPE")
|
|
379
|
-
proc.kill();
|
|
380
|
-
});
|
|
381
|
-
try {
|
|
382
|
-
proc.stdin.end(req.prompt);
|
|
383
|
-
}
|
|
384
|
-
catch (err) {
|
|
385
|
-
stdinError = err instanceof Error ? err : new Error(String(err));
|
|
386
|
-
proc.kill();
|
|
387
|
-
}
|
|
388
|
-
yield { kind: "model", model: req.model, reasoningEffort: req.reasoningEffort };
|
|
389
|
-
let sawResult = false;
|
|
390
|
-
try {
|
|
391
|
-
try {
|
|
392
|
-
for await (const line of lines(proc.stdout)) {
|
|
393
|
-
let msg;
|
|
394
|
-
try {
|
|
395
|
-
msg = JSON.parse(line);
|
|
396
|
-
}
|
|
397
|
-
catch {
|
|
398
|
-
continue; // non-JSON noise
|
|
399
|
-
}
|
|
400
|
-
for (const ev of mapCliLine(msg)) {
|
|
401
|
-
if (ev.kind === "result")
|
|
402
|
-
sawResult = true;
|
|
403
|
-
yield ev;
|
|
404
|
-
}
|
|
405
|
-
}
|
|
406
|
-
}
|
|
407
|
-
finally {
|
|
408
|
-
cancelStdoutGuard();
|
|
409
|
-
}
|
|
410
|
-
const code = await proc.exited;
|
|
411
|
-
if (!sawResult) {
|
|
412
|
-
yield {
|
|
413
|
-
kind: "error",
|
|
414
|
-
message: stdinError
|
|
415
|
-
? `remote claude stdin failed: ${stdinError.message}`
|
|
416
|
-
: `remote claude exited with code ${code} without a result`,
|
|
417
|
-
};
|
|
418
|
-
}
|
|
419
|
-
}
|
|
420
|
-
finally {
|
|
421
|
-
ctx.signal.removeEventListener("abort", onAbort);
|
|
422
|
-
proc.kill();
|
|
423
|
-
await stderrDone;
|
|
424
|
-
cancelStdoutGuard();
|
|
425
|
-
cancelStderrGuard();
|
|
426
|
-
}
|
|
427
|
-
}
|
|
428
|
-
function* mapCliLine(msg) {
|
|
429
|
-
const now = Date.now();
|
|
430
|
-
const type = msg.type;
|
|
431
|
-
if (type === "system" && msg.subtype === "init" && typeof msg.session_id === "string") {
|
|
432
|
-
yield { kind: "session", sessionId: msg.session_id };
|
|
433
|
-
}
|
|
434
|
-
else if (type === "assistant") {
|
|
435
|
-
const content = msg.message?.content;
|
|
436
|
-
if (Array.isArray(content)) {
|
|
437
|
-
for (const block of content) {
|
|
438
|
-
const b = block;
|
|
439
|
-
if (b.type === "tool_use" && b.id && b.name) {
|
|
440
|
-
yield { kind: "tool-call-open", id: b.id, name: b.name, input: b.input, atMs: now };
|
|
441
|
-
}
|
|
442
|
-
}
|
|
443
|
-
}
|
|
444
|
-
}
|
|
445
|
-
else if (type === "user") {
|
|
446
|
-
const content = msg.message?.content;
|
|
447
|
-
if (Array.isArray(content)) {
|
|
448
|
-
for (const block of content) {
|
|
449
|
-
const b = block;
|
|
450
|
-
if (b.type === "tool_result" && b.tool_use_id) {
|
|
451
|
-
yield {
|
|
452
|
-
kind: "tool-call-close",
|
|
453
|
-
id: b.tool_use_id,
|
|
454
|
-
status: b.is_error ? "error" : "ok",
|
|
455
|
-
result: toolResultText(b.content),
|
|
456
|
-
atMs: now,
|
|
457
|
-
};
|
|
458
|
-
}
|
|
459
|
-
}
|
|
460
|
-
}
|
|
461
|
-
}
|
|
462
|
-
else if (type === "result") {
|
|
463
|
-
const usage = msg.usage;
|
|
464
|
-
yield {
|
|
465
|
-
kind: "usage",
|
|
466
|
-
tokensIn: usage?.input_tokens,
|
|
467
|
-
tokensOut: usage?.output_tokens,
|
|
468
|
-
costUsd: msg.total_cost_usd,
|
|
469
|
-
};
|
|
470
|
-
if (msg.subtype === "success") {
|
|
471
|
-
if (msg.structured_output !== undefined) {
|
|
472
|
-
yield { kind: "result", output: msg.structured_output };
|
|
473
|
-
}
|
|
474
|
-
else {
|
|
475
|
-
yield { kind: "result", output: extractJson(String(msg.result ?? "")) };
|
|
476
|
-
}
|
|
477
|
-
}
|
|
478
|
-
else {
|
|
479
|
-
yield { kind: "error", message: `claude leaf failed: ${String(msg.subtype)}` };
|
|
480
|
-
}
|
|
481
|
-
}
|
|
482
|
-
}
|
|
483
|
-
// ---------------------------------------------------------------------------
|
|
484
|
-
async function* lines(stream) {
|
|
485
|
-
let buf = "";
|
|
486
|
-
try {
|
|
487
|
-
for await (const chunk of stream) {
|
|
488
|
-
buf += chunk.toString();
|
|
489
|
-
let idx;
|
|
490
|
-
while ((idx = buf.indexOf("\n")) >= 0) {
|
|
491
|
-
const line = buf.slice(0, idx).trim();
|
|
492
|
-
buf = buf.slice(idx + 1);
|
|
493
|
-
if (line)
|
|
494
|
-
yield line;
|
|
495
|
-
}
|
|
496
|
-
}
|
|
497
|
-
}
|
|
498
|
-
catch (err) {
|
|
499
|
-
if (err.code !== "ERR_STREAM_PREMATURE_CLOSE")
|
|
500
|
-
throw err;
|
|
501
|
-
}
|
|
502
|
-
if (buf.trim())
|
|
503
|
-
yield buf.trim();
|
|
504
|
-
}
|
|
505
317
|
function sandboxRoots(req) {
|
|
506
318
|
return [
|
|
507
319
|
...new Set([req.cwd, ...(req.sandboxDirs ?? [])].map((root) => path.normalize(path.isAbsolute(root) ? root : path.resolve(req.cwd, root)))),
|
|
@@ -514,51 +326,6 @@ function pathWithin(target, roots, cwd) {
|
|
|
514
326
|
return rel === "" || (rel !== ".." && !rel.startsWith(`..${path.sep}`) && !path.isAbsolute(rel));
|
|
515
327
|
});
|
|
516
328
|
}
|
|
517
|
-
function guardPostExitDrain(stream, exited, name, log) {
|
|
518
|
-
let cancelled = false;
|
|
519
|
-
let timer;
|
|
520
|
-
void exited.then(() => {
|
|
521
|
-
if (cancelled)
|
|
522
|
-
return;
|
|
523
|
-
timer = setTimeout(() => {
|
|
524
|
-
log(`remote claude ${name} remained open after exit; stopping drain`);
|
|
525
|
-
destroyStream(stream);
|
|
526
|
-
}, POST_EXIT_DRAIN_MS);
|
|
527
|
-
timer.unref?.();
|
|
528
|
-
});
|
|
529
|
-
return () => {
|
|
530
|
-
cancelled = true;
|
|
531
|
-
if (timer)
|
|
532
|
-
clearTimeout(timer);
|
|
533
|
-
};
|
|
534
|
-
}
|
|
535
|
-
function destroyStream(stream) {
|
|
536
|
-
stream.destroy?.();
|
|
537
|
-
}
|
|
538
|
-
async function drainStderr(stream, log) {
|
|
539
|
-
let buf = "";
|
|
540
|
-
try {
|
|
541
|
-
for await (const chunk of stream) {
|
|
542
|
-
buf += chunk.toString();
|
|
543
|
-
let idx;
|
|
544
|
-
while ((idx = buf.indexOf("\n")) >= 0) {
|
|
545
|
-
const line = buf.slice(0, idx).trim();
|
|
546
|
-
buf = buf.slice(idx + 1);
|
|
547
|
-
if (line)
|
|
548
|
-
log(line);
|
|
549
|
-
}
|
|
550
|
-
}
|
|
551
|
-
}
|
|
552
|
-
catch (err) {
|
|
553
|
-
if (err.code !== "ERR_STREAM_PREMATURE_CLOSE") {
|
|
554
|
-
log(`remote claude stderr read failed: ${String(err)}`);
|
|
555
|
-
}
|
|
556
|
-
}
|
|
557
|
-
finally {
|
|
558
|
-
if (buf.trim())
|
|
559
|
-
log(buf.trim());
|
|
560
|
-
}
|
|
561
|
-
}
|
|
562
329
|
/** Normalize a tool_result content field into a bounded string for the trace. */
|
|
563
330
|
export function toolResultText(content) {
|
|
564
331
|
const MAX = 8 * 1024;
|
package/package.json
CHANGED
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@karowanorg/orc-harness-claude",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.2",
|
|
4
4
|
"license": "0BSD",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
7
7
|
"dependencies": {
|
|
8
|
-
"@karowanorg/orc-core": "^0.1.
|
|
9
|
-
"@karowanorg/orc-executors": "^0.1.
|
|
8
|
+
"@karowanorg/orc-core": "^0.1.1",
|
|
9
|
+
"@karowanorg/orc-executors": "^0.1.1",
|
|
10
10
|
"@anthropic-ai/claude-agent-sdk": "^0.3.0",
|
|
11
11
|
"zod": "^4.1.0"
|
|
12
12
|
},
|
|
13
|
-
"description": "Claude harness for orc
|
|
13
|
+
"description": "Claude harness for orc via the Anthropic Agent SDK.",
|
|
14
14
|
"types": "dist/index.d.ts",
|
|
15
15
|
"exports": {
|
|
16
16
|
".": {
|