@kaiba-cloud/orbital 0.0.3 → 0.0.4
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 +18 -2
- package/dist/index.js +16924 -15860
- package/dist/mod.json +1 -1
- package/dist/orbital.js +16663 -15501
- package/dist/runner.js +178 -88
- package/package.json +1 -1
- package/schemas/telemetry-snapshot.schema.json +27 -0
- package/skills/orbital-plugin-builder/SKILL.md +4 -15
- package/skills/orbital-plugin-builder/references/host-api.md +9 -5
- package/skills/orbital-plugin-builder/references/ports.md +17 -6
- package/skills/orbital-plugin-builder/references/recipes.md +2 -85
package/dist/runner.js
CHANGED
|
@@ -45510,7 +45510,7 @@ function handleIntersectionResults(result, left, right) {
|
|
|
45510
45510
|
const unrecKeys = /* @__PURE__ */ new Map();
|
|
45511
45511
|
let unrecIssue;
|
|
45512
45512
|
const keyIssues = /* @__PURE__ */ new Map();
|
|
45513
|
-
const
|
|
45513
|
+
const collect2 = (iss, side) => {
|
|
45514
45514
|
let keys;
|
|
45515
45515
|
if (iss.code === "unrecognized_keys" && !iss.path?.length) {
|
|
45516
45516
|
unrecIssue ?? (unrecIssue = iss);
|
|
@@ -45531,11 +45531,11 @@ function handleIntersectionResults(result, left, right) {
|
|
|
45531
45531
|
return true;
|
|
45532
45532
|
};
|
|
45533
45533
|
for (const iss of left.issues) {
|
|
45534
|
-
if (!
|
|
45534
|
+
if (!collect2(iss, "l"))
|
|
45535
45535
|
result.issues.push(iss);
|
|
45536
45536
|
}
|
|
45537
45537
|
for (const iss of right.issues) {
|
|
45538
|
-
if (!
|
|
45538
|
+
if (!collect2(iss, "r"))
|
|
45539
45539
|
result.issues.push(iss);
|
|
45540
45540
|
}
|
|
45541
45541
|
const bothKeys = [...unrecKeys].filter(([, f]) => f.l && f.r).map(([k]) => k);
|
|
@@ -61386,6 +61386,7 @@ var ORBITAL_META = {
|
|
|
61386
61386
|
requestedModel: "orbital/requestedModel",
|
|
61387
61387
|
fromAgent: "orbital/fromAgent",
|
|
61388
61388
|
fromSession: "orbital/fromSession",
|
|
61389
|
+
fromTeam: "orbital/fromTeam",
|
|
61389
61390
|
peerHops: "orbital/peerHops",
|
|
61390
61391
|
entry: "orbital/entry",
|
|
61391
61392
|
block: "orbital/block",
|
|
@@ -61645,6 +61646,16 @@ var MissionChangeSchema = external_exports.object({
|
|
|
61645
61646
|
by: external_exports.string(),
|
|
61646
61647
|
note: external_exports.string().nullable()
|
|
61647
61648
|
});
|
|
61649
|
+
var MissionLinkSchema = external_exports.object({
|
|
61650
|
+
label: external_exports.string().min(1),
|
|
61651
|
+
url: external_exports.string().url().refine((url2) => /^https?:\/\//i.test(url2), "a link is an http or https URL"),
|
|
61652
|
+
state: external_exports.string().min(1).nullable().default(null)
|
|
61653
|
+
});
|
|
61654
|
+
var MissionAskedSchema = external_exports.object({
|
|
61655
|
+
id: external_exports.string().min(1),
|
|
61656
|
+
question: external_exports.string().min(1),
|
|
61657
|
+
at: external_exports.string()
|
|
61658
|
+
});
|
|
61648
61659
|
var MissionStateFileSchema = external_exports.object({
|
|
61649
61660
|
mission: external_exports.string().min(1),
|
|
61650
61661
|
node: external_exports.string().min(1),
|
|
@@ -61659,6 +61670,8 @@ var MissionStateFileSchema = external_exports.object({
|
|
|
61659
61670
|
lead: external_exports.string().nullable(),
|
|
61660
61671
|
claim: external_exports.string().nullable(),
|
|
61661
61672
|
claimedAt: external_exports.string().nullable(),
|
|
61673
|
+
links: external_exports.array(MissionLinkSchema).default([]),
|
|
61674
|
+
questions: external_exports.array(MissionAskedSchema).default([]),
|
|
61662
61675
|
history: external_exports.array(MissionChangeSchema)
|
|
61663
61676
|
});
|
|
61664
61677
|
var MISSION_ID = /^[a-z0-9][a-z0-9-]{0,62}$/;
|
|
@@ -61672,11 +61685,14 @@ var MissionRecordSchema = external_exports.object({
|
|
|
61672
61685
|
declared: external_exports.boolean(),
|
|
61673
61686
|
givenBy: external_exports.string().nullable(),
|
|
61674
61687
|
brief: external_exports.string().nullable(),
|
|
61688
|
+
summary: external_exports.string().nullable().default(null),
|
|
61689
|
+
links: external_exports.array(MissionLinkSchema).default([]),
|
|
61675
61690
|
sessionId: external_exports.string().nullable(),
|
|
61676
61691
|
taskId: external_exports.string().nullable(),
|
|
61677
61692
|
source: external_exports.string().nullable(),
|
|
61678
61693
|
result: external_exports.string().nullable(),
|
|
61679
61694
|
question: external_exports.string().nullable(),
|
|
61695
|
+
questions: external_exports.array(MissionAskedSchema).default([]),
|
|
61680
61696
|
giver: external_exports.string().nullable(),
|
|
61681
61697
|
lead: external_exports.string().nullable(),
|
|
61682
61698
|
lastActivity: external_exports.string().nullable().default(null),
|
|
@@ -61693,6 +61709,11 @@ var MissionHandoffSchema = external_exports.object({
|
|
|
61693
61709
|
state: MissionOutcomeSchema.default("done"),
|
|
61694
61710
|
result: external_exports.string().min(1)
|
|
61695
61711
|
});
|
|
61712
|
+
var MissionLinkRequestSchema = MissionLinkSchema.extend({ missionId: MissionIdSchema.optional() });
|
|
61713
|
+
var MissionAskRequestSchema = external_exports.object({
|
|
61714
|
+
missionId: MissionIdSchema.optional(),
|
|
61715
|
+
question: external_exports.string().min(1)
|
|
61716
|
+
});
|
|
61696
61717
|
var MissionStartRequestSchema = external_exports.object({
|
|
61697
61718
|
mission: MissionIdSchema,
|
|
61698
61719
|
team: external_exports.string().min(1).nullable().default(null),
|
|
@@ -61711,11 +61732,13 @@ var MissionNewRequestSchema = external_exports.object({
|
|
|
61711
61732
|
var MissionWrittenSchema = external_exports.object({ missionId: external_exports.string().min(1), file: external_exports.string().min(1) });
|
|
61712
61733
|
var MissionReplySchema = external_exports.object({
|
|
61713
61734
|
missionId: MissionIdSchema,
|
|
61714
|
-
text: external_exports.string().min(1)
|
|
61735
|
+
text: external_exports.string().min(1),
|
|
61736
|
+
questionId: external_exports.string().min(1).optional()
|
|
61715
61737
|
});
|
|
61716
61738
|
var RemoteGiverSchema = external_exports.object({ node: external_exports.string().min(1), sessionId: external_exports.string().min(1) });
|
|
61717
|
-
var
|
|
61718
|
-
var
|
|
61739
|
+
var SENDER_FIELDS = { team: external_exports.string().min(1).nullable().default(null), agent: external_exports.string().min(1).nullable().default(null) };
|
|
61740
|
+
var RelayJoinSchema = external_exports.object({ missionId: MissionIdSchema, giver: RemoteGiverSchema, ...SENDER_FIELDS });
|
|
61741
|
+
var RelayReplySchema = external_exports.object({ missionId: MissionIdSchema, text: external_exports.string().min(1), questionId: external_exports.string().min(1).optional(), by: external_exports.string().min(1), ...SENDER_FIELDS });
|
|
61719
61742
|
var MissionControlSchema = external_exports.object({ missionId: MissionIdSchema });
|
|
61720
61743
|
var SayRequestSchema = external_exports.object({
|
|
61721
61744
|
target: external_exports.string().min(1),
|
|
@@ -61726,15 +61749,17 @@ var SaidSchema = external_exports.object({
|
|
|
61726
61749
|
missionId: external_exports.string().nullable(),
|
|
61727
61750
|
delivery: external_exports.enum(["reply", "steer", "turn"])
|
|
61728
61751
|
});
|
|
61729
|
-
var RelayMessageSchema = external_exports.object({ missionId: MissionIdSchema, text: external_exports.string().min(1), by: external_exports.string().min(1) });
|
|
61730
|
-
var RelayControlSchema = external_exports.object({ missionId: MissionIdSchema, by: external_exports.string().min(1) });
|
|
61752
|
+
var RelayMessageSchema = external_exports.object({ missionId: MissionIdSchema, text: external_exports.string().min(1), by: external_exports.string().min(1), ...SENDER_FIELDS });
|
|
61753
|
+
var RelayControlSchema = external_exports.object({ missionId: MissionIdSchema, by: external_exports.string().min(1), ...SENDER_FIELDS });
|
|
61731
61754
|
var MissionQuestionSchema = external_exports.object({
|
|
61732
61755
|
missionId: MissionIdSchema,
|
|
61733
61756
|
node: external_exports.string().min(1),
|
|
61734
61757
|
lead: external_exports.string().nullable(),
|
|
61735
61758
|
leadSessionId: external_exports.string().min(1),
|
|
61736
61759
|
question: external_exports.string(),
|
|
61737
|
-
|
|
61760
|
+
questionId: external_exports.string().min(1).optional(),
|
|
61761
|
+
giver: RemoteGiverSchema,
|
|
61762
|
+
team: external_exports.string().min(1).nullable().default(null)
|
|
61738
61763
|
});
|
|
61739
61764
|
var MissionJoinSchema = external_exports.object({
|
|
61740
61765
|
missionId: MissionIdSchema
|
|
@@ -61868,6 +61893,7 @@ var TelemetrySnapshotSchema = external_exports.object({
|
|
|
61868
61893
|
giver: external_exports.string().nullable(),
|
|
61869
61894
|
givenBy: external_exports.string().nullable(),
|
|
61870
61895
|
question: external_exports.string().nullable(),
|
|
61896
|
+
questions: external_exports.array(MissionAskedSchema).default([]),
|
|
61871
61897
|
result: external_exports.string().nullable(),
|
|
61872
61898
|
updatedAt: external_exports.string().nullable()
|
|
61873
61899
|
})),
|
|
@@ -62258,6 +62284,12 @@ var NodeClient = class {
|
|
|
62258
62284
|
async handoff(handoff2) {
|
|
62259
62285
|
return MissionRecordSchema.parse(await this.request("/orbital/missions/handoff", { method: "POST", body: handoff2 }));
|
|
62260
62286
|
}
|
|
62287
|
+
async link(link) {
|
|
62288
|
+
return MissionRecordSchema.parse(await this.request("/orbital/missions/link", { method: "POST", body: link }));
|
|
62289
|
+
}
|
|
62290
|
+
async ask(ask) {
|
|
62291
|
+
return MissionRecordSchema.parse(await this.request("/orbital/missions/ask", { method: "POST", body: ask }));
|
|
62292
|
+
}
|
|
62261
62293
|
async transcript(sessionId, limit) {
|
|
62262
62294
|
return SessionTranscriptSchema.parse(await this.request(`/orbital/sessions/${encodeURIComponent(sessionId)}/transcript?limit=${limit}`));
|
|
62263
62295
|
}
|
|
@@ -62482,6 +62514,7 @@ var DeliverySchema = external_exports.object({
|
|
|
62482
62514
|
fromAgent: external_exports.string().nullable(),
|
|
62483
62515
|
fromSession: external_exports.string().nullable(),
|
|
62484
62516
|
mission: external_exports.string().nullable().default(null),
|
|
62517
|
+
question: external_exports.string().nullable().default(null),
|
|
62485
62518
|
text: external_exports.string()
|
|
62486
62519
|
});
|
|
62487
62520
|
var SettleSchema = external_exports.object({
|
|
@@ -62532,6 +62565,7 @@ var HarnessFolderSchema = external_exports.strictObject({
|
|
|
62532
62565
|
var HarnessAdapterDeclarationSchema = HarnessFolderSchema.extend({ id: external_exports.string().regex(/^[a-z][a-z0-9-]{0,31}$/) });
|
|
62533
62566
|
var HarnessInstallResultSchema = external_exports.object({
|
|
62534
62567
|
written: external_exports.array(external_exports.string()).default([]),
|
|
62568
|
+
removed: external_exports.array(external_exports.string()).default([]),
|
|
62535
62569
|
notes: external_exports.array(external_exports.string()).default([])
|
|
62536
62570
|
});
|
|
62537
62571
|
var HarnessAgentFileSchema = external_exports.object({
|
|
@@ -62720,12 +62754,6 @@ function localAgentSystemPrompt(systemPrompt, promptBlock) {
|
|
|
62720
62754
|
${promptBlock}` : systemPrompt;
|
|
62721
62755
|
}
|
|
62722
62756
|
|
|
62723
|
-
// src/protocol/keychain.ts
|
|
62724
|
-
var KEYCHAIN_PATH = "/orbital/keychain";
|
|
62725
|
-
var KEYCHAIN_MODE = "keychain";
|
|
62726
|
-
var KeychainCallSchema = external_exports.object({ args: external_exports.array(external_exports.string()), input: external_exports.string().default("") });
|
|
62727
|
-
var KeychainAnswerSchema = external_exports.object({ status: external_exports.number().int(), stdout: external_exports.string(), stderr: external_exports.string() });
|
|
62728
|
-
|
|
62729
62757
|
// src/runner/runtime/harness/claude-browser.ts
|
|
62730
62758
|
import { existsSync, readFileSync } from "node:fs";
|
|
62731
62759
|
import { homedir } from "node:os";
|
|
@@ -75023,7 +75051,8 @@ var ClaudeCodeHarness = class {
|
|
|
75023
75051
|
...QUIET_ENV,
|
|
75024
75052
|
...this.args.toolEnvironment?.(),
|
|
75025
75053
|
...credential && { CLAUDE_CONFIG_DIR: this.configDir },
|
|
75026
|
-
...credential && (isOAuth ? { CLAUDE_CODE_OAUTH_TOKEN: credential.payload } : { ANTHROPIC_API_KEY: credential.payload })
|
|
75054
|
+
...credential && (isOAuth ? { CLAUDE_CODE_OAUTH_TOKEN: credential.payload } : { ANTHROPIC_API_KEY: credential.payload }),
|
|
75055
|
+
...!credential && this.host.login && { CLAUDE_CODE_OAUTH_TOKEN: this.host.login }
|
|
75027
75056
|
};
|
|
75028
75057
|
delete env.NODE_OPTIONS;
|
|
75029
75058
|
return env;
|
|
@@ -81317,6 +81346,8 @@ function createAgentBridgeTools(options) {
|
|
|
81317
81346
|
if (messaging.peers) host.registerTool(buildAgentPeersTool(messaging, options));
|
|
81318
81347
|
if (messaging.mission) host.registerTool(buildMissionGetTool(messaging, options));
|
|
81319
81348
|
if (messaging.handoffMission) host.registerTool(buildMissionHandoffTool(messaging, options));
|
|
81349
|
+
if (messaging.linkMission) host.registerTool(buildMissionLinkTool(messaging, options));
|
|
81350
|
+
if (messaging.askMission) host.registerTool(buildMissionAskTool(messaging, options));
|
|
81320
81351
|
host.registerTool(buildAgentStopTool(jobs, messaging, options, log2));
|
|
81321
81352
|
host.registerTool(buildAgentKillTool(jobs, ledger, messaging, options, log2));
|
|
81322
81353
|
});
|
|
@@ -81658,6 +81689,48 @@ function buildMissionHandoffTool(messaging, options) {
|
|
|
81658
81689
|
}
|
|
81659
81690
|
};
|
|
81660
81691
|
}
|
|
81692
|
+
var MissionLinkParams = Type.Object({
|
|
81693
|
+
label: Type.String({ minLength: 1, description: "A short label, such as PR #42." }),
|
|
81694
|
+
url: Type.String({ minLength: 1, description: "The http or https URL of the link." }),
|
|
81695
|
+
state: Type.Optional(Type.String({ minLength: 1, description: "The state of the linked thing, such as open, merged or closed." }))
|
|
81696
|
+
});
|
|
81697
|
+
function buildMissionLinkTool(messaging, options) {
|
|
81698
|
+
return {
|
|
81699
|
+
name: "missionLink",
|
|
81700
|
+
label: "Link Mission",
|
|
81701
|
+
description: "Add a link to the mission of your session, as its lead, or change the state of a link it has: such as the pull request you opened, with its state open, merged or closed. One URL or one label is one link, so a call with the same URL replaces it. The node keeps the links with the mission, and the person sees them in the live view.",
|
|
81702
|
+
promptSnippet: "missionLink: add a link to your mission, such as the PR you opened, with its state",
|
|
81703
|
+
parameters: MissionLinkParams,
|
|
81704
|
+
executionMode: "sequential",
|
|
81705
|
+
async execute(_toolCallId, params) {
|
|
81706
|
+
try {
|
|
81707
|
+
return textResult(await messaging.linkMission(options.callerSessionId, { label: params.label, url: params.url, state: params.state ?? null }));
|
|
81708
|
+
} catch (err) {
|
|
81709
|
+
return errorResult(`missionLink failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
81710
|
+
}
|
|
81711
|
+
}
|
|
81712
|
+
};
|
|
81713
|
+
}
|
|
81714
|
+
var MissionAskParams = Type.Object({
|
|
81715
|
+
question: Type.String({ minLength: 1, description: "The question, in full, with the options you see and what each one means: the giver has none of your context." })
|
|
81716
|
+
});
|
|
81717
|
+
function buildMissionAskTool(messaging, options) {
|
|
81718
|
+
return {
|
|
81719
|
+
name: "missionAsk",
|
|
81720
|
+
label: "Ask Mission Giver",
|
|
81721
|
+
description: "Ask the giver of the mission of your session a question, as its lead, at any moment, also while your workers run. The node keeps each question open with the mission until the giver answers it, shows it to the person as waiting on them, and pushes it to the giver at once. The answer comes back to you as a message that names the question. Use it for every decision you need from the giver: a question only in your reply text reaches no one. Keep working on what does not depend on the answer.",
|
|
81722
|
+
promptSnippet: "missionAsk: ask the giver of your mission a question at any moment, also while your workers run",
|
|
81723
|
+
parameters: MissionAskParams,
|
|
81724
|
+
executionMode: "sequential",
|
|
81725
|
+
async execute(_toolCallId, params) {
|
|
81726
|
+
try {
|
|
81727
|
+
return textResult(await messaging.askMission(options.callerSessionId, { question: params.question }));
|
|
81728
|
+
} catch (err) {
|
|
81729
|
+
return errorResult(`missionAsk failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
81730
|
+
}
|
|
81731
|
+
}
|
|
81732
|
+
};
|
|
81733
|
+
}
|
|
81661
81734
|
function buildAgentMessageTool(jobs, ledger, messaging, options, log2) {
|
|
81662
81735
|
return {
|
|
81663
81736
|
name: "agentMessage",
|
|
@@ -83119,8 +83192,9 @@ var RunSpecSchema = external_exports.object({
|
|
|
83119
83192
|
harness: external_exports.enum(RUNNER_HARNESSES).default("claude"),
|
|
83120
83193
|
claude: external_exports.object({
|
|
83121
83194
|
settings: external_exports.string().nullable(),
|
|
83122
|
-
args: external_exports.array(external_exports.string())
|
|
83123
|
-
|
|
83195
|
+
args: external_exports.array(external_exports.string()),
|
|
83196
|
+
login: external_exports.string().nullable().default(null)
|
|
83197
|
+
}).default({ settings: null, args: [], login: null }),
|
|
83124
83198
|
agent: external_exports.discriminatedUnion("kind", [
|
|
83125
83199
|
external_exports.object({ kind: external_exports.literal("directory"), id: external_exports.string(), bootstrap: external_exports.record(external_exports.string(), external_exports.unknown()) }),
|
|
83126
83200
|
external_exports.object({ kind: external_exports.literal("local"), definition: LocalAgentSchema })
|
|
@@ -83139,19 +83213,88 @@ function readRunSpec(path10) {
|
|
|
83139
83213
|
}
|
|
83140
83214
|
|
|
83141
83215
|
// src/runner/local-store.ts
|
|
83142
|
-
import {
|
|
83216
|
+
import { existsSync as existsSync5, mkdirSync as mkdirSync3, readFileSync as readFileSync4, readdirSync as readdirSync2, statSync as statSync2, writeFileSync as writeFileSync3 } from "node:fs";
|
|
83143
83217
|
import path7 from "node:path";
|
|
83218
|
+
|
|
83219
|
+
// src/runner/jsonl-log.ts
|
|
83220
|
+
import { appendFileSync, closeSync, existsSync as existsSync4, openSync, readSync, renameSync as renameSync2, statSync } from "node:fs";
|
|
83221
|
+
var CHUNK = 64 * 1024;
|
|
83222
|
+
function rotatedPath(file2) {
|
|
83223
|
+
return file2.replace(/\.jsonl$/, ".1.jsonl");
|
|
83224
|
+
}
|
|
83225
|
+
function appendCapped(file2, text, cap) {
|
|
83226
|
+
if (existsSync4(file2) && statSync(file2).size + Buffer.byteLength(text) > cap) renameSync2(file2, rotatedPath(file2));
|
|
83227
|
+
appendFileSync(file2, text);
|
|
83228
|
+
}
|
|
83229
|
+
function backward(file2, take, into) {
|
|
83230
|
+
if (!existsSync4(file2)) return true;
|
|
83231
|
+
const fd = openSync(file2, "r");
|
|
83232
|
+
try {
|
|
83233
|
+
let position = statSync(file2).size;
|
|
83234
|
+
let rest = Buffer.alloc(0);
|
|
83235
|
+
while (position > 0) {
|
|
83236
|
+
const length = Math.min(CHUNK, position);
|
|
83237
|
+
position -= length;
|
|
83238
|
+
const chunk = Buffer.alloc(length);
|
|
83239
|
+
readSync(fd, chunk, 0, length, position);
|
|
83240
|
+
const joined = Buffer.concat([chunk, rest]);
|
|
83241
|
+
let end = joined.length;
|
|
83242
|
+
for (let at = joined.lastIndexOf(10, end - 1); at !== -1; at = end > 0 ? joined.lastIndexOf(10, end - 1) : -1) {
|
|
83243
|
+
const line = joined.subarray(at + 1, end).toString("utf8");
|
|
83244
|
+
end = at;
|
|
83245
|
+
if (line.trim() === "") continue;
|
|
83246
|
+
if (!take(line)) return false;
|
|
83247
|
+
into.push(line);
|
|
83248
|
+
}
|
|
83249
|
+
rest = joined.subarray(0, end);
|
|
83250
|
+
}
|
|
83251
|
+
const first = rest.toString("utf8");
|
|
83252
|
+
if (first.trim() !== "") {
|
|
83253
|
+
if (!take(first)) return false;
|
|
83254
|
+
into.push(first);
|
|
83255
|
+
}
|
|
83256
|
+
return true;
|
|
83257
|
+
} finally {
|
|
83258
|
+
closeSync(fd);
|
|
83259
|
+
}
|
|
83260
|
+
}
|
|
83261
|
+
function collect(file2, take) {
|
|
83262
|
+
const lines = [];
|
|
83263
|
+
if (backward(file2, take, lines)) backward(rotatedPath(file2), take, lines);
|
|
83264
|
+
return lines.reverse();
|
|
83265
|
+
}
|
|
83266
|
+
function tailLines(file2, count, maxBytes = Number.POSITIVE_INFINITY) {
|
|
83267
|
+
let taken = 0;
|
|
83268
|
+
let bytes = 0;
|
|
83269
|
+
return count <= 0 ? [] : collect(file2, (line) => {
|
|
83270
|
+
if (taken >= count || taken > 0 && bytes + line.length > maxBytes) return false;
|
|
83271
|
+
taken += 1;
|
|
83272
|
+
bytes += line.length;
|
|
83273
|
+
return true;
|
|
83274
|
+
});
|
|
83275
|
+
}
|
|
83276
|
+
function sequenceOf(line) {
|
|
83277
|
+
const found = /"sequence":(\d+)/.exec(line);
|
|
83278
|
+
return found ? Number(found[1]) : 0;
|
|
83279
|
+
}
|
|
83280
|
+
function lastSequenceOf(file2) {
|
|
83281
|
+
const last = tailLines(file2, 1)[0];
|
|
83282
|
+
return last ? sequenceOf(last) : 0;
|
|
83283
|
+
}
|
|
83284
|
+
|
|
83285
|
+
// src/runner/local-store.ts
|
|
83144
83286
|
var RUNNER_LOG = "runner.jsonl";
|
|
83287
|
+
var RUNNER_LOG_CAP = 32 * 1024 * 1024;
|
|
83145
83288
|
var HISTORY_DIR = "history";
|
|
83146
83289
|
function folderUnit(dir) {
|
|
83147
83290
|
const walk = (folder) => readdirSync2(folder).flatMap((name) => {
|
|
83148
83291
|
const full = path7.join(folder, name);
|
|
83149
|
-
return
|
|
83292
|
+
return statSync2(full).isDirectory() ? walk(full) : [full];
|
|
83150
83293
|
});
|
|
83151
83294
|
return {
|
|
83152
83295
|
kinds: ["history"],
|
|
83153
83296
|
async list() {
|
|
83154
|
-
if (!
|
|
83297
|
+
if (!existsSync5(dir)) return null;
|
|
83155
83298
|
return Promise.all(walk(dir).map(async (full) => {
|
|
83156
83299
|
const bytes = readFileSync4(full);
|
|
83157
83300
|
return { path: path7.relative(dir, full).split(path7.sep).join("/"), hash: await sha256Hex(bytes), size: bytes.length };
|
|
@@ -83159,7 +83302,7 @@ function folderUnit(dir) {
|
|
|
83159
83302
|
},
|
|
83160
83303
|
async read(_ref, file2) {
|
|
83161
83304
|
const full = path7.join(dir, ...file2.split("/"));
|
|
83162
|
-
return
|
|
83305
|
+
return existsSync5(full) ? { path: file2, contentBase64: readFileSync4(full).toString("base64") } : null;
|
|
83163
83306
|
},
|
|
83164
83307
|
async write() {
|
|
83165
83308
|
throw new Error("The runner reads a session's history unit; the host writes it.");
|
|
@@ -83185,8 +83328,8 @@ var LocalSessionStore = class {
|
|
|
83185
83328
|
async persistEvent(sessionId, event) {
|
|
83186
83329
|
const sequence = (this.sequences.get(sessionId) ?? 0) + 1;
|
|
83187
83330
|
this.sequences.set(sessionId, sequence);
|
|
83188
|
-
|
|
83189
|
-
|
|
83331
|
+
appendCapped(path7.join(this.recordDir(sessionId), RUNNER_LOG), `${JSON.stringify({ sequence, ...event })}
|
|
83332
|
+
`, RUNNER_LOG_CAP);
|
|
83190
83333
|
this.observer.event?.(sessionId, { ...event, sequence });
|
|
83191
83334
|
}
|
|
83192
83335
|
async seedSequence(sessionId, sequence) {
|
|
@@ -83196,9 +83339,7 @@ var LocalSessionStore = class {
|
|
|
83196
83339
|
this.sequences.delete(sessionId);
|
|
83197
83340
|
}
|
|
83198
83341
|
async lastEventSequence(sessionId) {
|
|
83199
|
-
const
|
|
83200
|
-
const lines = existsSync4(file2) ? readFileSync4(file2, "utf8").trim().split("\n").filter(Boolean) : [];
|
|
83201
|
-
const logged = lines.length === 0 ? 0 : JSON.parse(lines[lines.length - 1]).sequence;
|
|
83342
|
+
const logged = lastSequenceOf(path7.join(this.sessionsDir, sessionId, RUNNER_LOG));
|
|
83202
83343
|
return Math.max(logged, (await this.history(sessionId))?.record.watermark ?? 0);
|
|
83203
83344
|
}
|
|
83204
83345
|
async readConversation(sessionId) {
|
|
@@ -83238,7 +83379,7 @@ function createLocalBootstrapSource(options) {
|
|
|
83238
83379
|
mcps: []
|
|
83239
83380
|
};
|
|
83240
83381
|
}
|
|
83241
|
-
return { organizationId: LOCAL_ORGANIZATION, ...spec.agent.bootstrap };
|
|
83382
|
+
return { organizationId: LOCAL_ORGANIZATION, ...spec.agent.bootstrap, systemPrompt: localAgentSystemPrompt(String(spec.agent.bootstrap.systemPrompt ?? ""), spec.promptBlock) };
|
|
83242
83383
|
}
|
|
83243
83384
|
return {
|
|
83244
83385
|
async fetchBootstrap(agentId) {
|
|
@@ -83352,6 +83493,12 @@ var NodeMessaging = class {
|
|
|
83352
83493
|
async handoffMission(callerSessionId, handoff2) {
|
|
83353
83494
|
return this.as(callerSessionId).handoff(handoff2);
|
|
83354
83495
|
}
|
|
83496
|
+
async linkMission(callerSessionId, link) {
|
|
83497
|
+
return this.as(callerSessionId).link(link);
|
|
83498
|
+
}
|
|
83499
|
+
async askMission(callerSessionId, ask) {
|
|
83500
|
+
return this.as(callerSessionId).ask(ask);
|
|
83501
|
+
}
|
|
83355
83502
|
};
|
|
83356
83503
|
|
|
83357
83504
|
// src/runner/node-gateway.ts
|
|
@@ -83415,40 +83562,6 @@ function localLayout(runDir, workspaceDir) {
|
|
|
83415
83562
|
};
|
|
83416
83563
|
}
|
|
83417
83564
|
|
|
83418
|
-
// src/runner/keychain-client.ts
|
|
83419
|
-
async function keychainClient(specPath, args, streams, fetchImpl = fetch) {
|
|
83420
|
-
const spec = readRunSpec(specPath);
|
|
83421
|
-
const input2 = args.length === 1 && args[0] === "-i" ? await streams.stdin() : "";
|
|
83422
|
-
try {
|
|
83423
|
-
const response = await fetchImpl(`${spec.node.url.replace(/\/a2a$/, "")}${KEYCHAIN_PATH}`, {
|
|
83424
|
-
method: "POST",
|
|
83425
|
-
headers: { authorization: `Bearer ${spec.node.token}`, [NODE_SESSION_HEADER]: spec.sessionId, "content-type": "application/json" },
|
|
83426
|
-
body: JSON.stringify({ args, input: input2 })
|
|
83427
|
-
});
|
|
83428
|
-
if (!response.ok) {
|
|
83429
|
-
streams.stderr(`security: the node refused the keychain call (${response.status}): ${(await response.text()).slice(0, 300)}
|
|
83430
|
-
`);
|
|
83431
|
-
return 1;
|
|
83432
|
-
}
|
|
83433
|
-
const answer = KeychainAnswerSchema.parse(await response.json());
|
|
83434
|
-
streams.stdout(answer.stdout);
|
|
83435
|
-
streams.stderr(answer.stderr);
|
|
83436
|
-
return answer.status;
|
|
83437
|
-
} catch (error62) {
|
|
83438
|
-
streams.stderr(`security: the node did not answer the keychain call: ${error62 instanceof Error ? error62.message : String(error62)}
|
|
83439
|
-
`);
|
|
83440
|
-
return 1;
|
|
83441
|
-
}
|
|
83442
|
-
}
|
|
83443
|
-
function readStdin() {
|
|
83444
|
-
return new Promise((resolve2, reject) => {
|
|
83445
|
-
const chunks = [];
|
|
83446
|
-
process.stdin.on("data", (chunk) => chunks.push(chunk));
|
|
83447
|
-
process.stdin.on("end", () => resolve2(Buffer.concat(chunks).toString("utf8")));
|
|
83448
|
-
process.stdin.on("error", reject);
|
|
83449
|
-
});
|
|
83450
|
-
}
|
|
83451
|
-
|
|
83452
83565
|
// src/runner/main.ts
|
|
83453
83566
|
function emit(record2) {
|
|
83454
83567
|
process.stdout.write(`${JSON.stringify(record2)}
|
|
@@ -83461,7 +83574,8 @@ function claudeOnThisMachine(spec) {
|
|
|
83461
83574
|
settingSources: ["user", "project", "local"],
|
|
83462
83575
|
subAgents: true,
|
|
83463
83576
|
...spec.claude.settings && { settings: spec.claude.settings },
|
|
83464
|
-
extraArgs: spec.claude.args
|
|
83577
|
+
extraArgs: spec.claude.args,
|
|
83578
|
+
login: spec.claude.login
|
|
83465
83579
|
};
|
|
83466
83580
|
}
|
|
83467
83581
|
function startLocalRun(spec, fetchImpl = fetch) {
|
|
@@ -83577,31 +83691,7 @@ async function runToCompletion(run, spec, interactiveMode = interactive) {
|
|
|
83577
83691
|
await run.sessions.closeAllSessions();
|
|
83578
83692
|
}
|
|
83579
83693
|
}
|
|
83580
|
-
|
|
83581
|
-
const [specPath, ...args] = process.argv.slice(3);
|
|
83582
|
-
if (!specPath) throw new Error(`usage: runner ${KEYCHAIN_MODE} <spec.json> <security arguments>`);
|
|
83583
|
-
installProxyDispatcher();
|
|
83584
|
-
let out = "";
|
|
83585
|
-
let err = "";
|
|
83586
|
-
const code = await keychainClient(specPath, args, { stdin: readStdin, stdout: (text) => {
|
|
83587
|
-
out += text;
|
|
83588
|
-
}, stderr: (text) => {
|
|
83589
|
-
err += text;
|
|
83590
|
-
} });
|
|
83591
|
-
await new Promise((done) => process.stdout.write(out, () => done()));
|
|
83592
|
-
await new Promise((done) => process.stderr.write(err, () => done()));
|
|
83593
|
-
return code;
|
|
83594
|
-
}
|
|
83595
|
-
var entered = process.argv[1] !== void 0 && import.meta.url === pathToFileURL(realpathSync2(process.argv[1])).href;
|
|
83596
|
-
if (entered && process.argv[2] === KEYCHAIN_MODE) {
|
|
83597
|
-
keychain().then(
|
|
83598
|
-
(code) => process.exit(code),
|
|
83599
|
-
(err) => {
|
|
83600
|
-
process.stderr.write(`security: ${err instanceof Error ? err.message : String(err)}
|
|
83601
|
-
`, () => process.exit(1));
|
|
83602
|
-
}
|
|
83603
|
-
);
|
|
83604
|
-
} else if (entered) {
|
|
83694
|
+
if (process.argv[1] && import.meta.url === pathToFileURL(realpathSync2(process.argv[1])).href) {
|
|
83605
83695
|
main().then(
|
|
83606
83696
|
() => process.exit(0),
|
|
83607
83697
|
(err) => {
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kaiba-cloud/orbital",
|
|
3
3
|
"description": "orbital: the control plane lite for agents on any harness: the node, its runner, its terminal UI, its protocol and its Claude Code mod, in one command line",
|
|
4
|
-
"version": "0.0.
|
|
4
|
+
"version": "0.0.4",
|
|
5
5
|
"license": "UNLICENSED",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"engines": {
|
|
@@ -297,6 +297,32 @@
|
|
|
297
297
|
"null"
|
|
298
298
|
]
|
|
299
299
|
},
|
|
300
|
+
"questions": {
|
|
301
|
+
"default": [],
|
|
302
|
+
"type": "array",
|
|
303
|
+
"items": {
|
|
304
|
+
"type": "object",
|
|
305
|
+
"properties": {
|
|
306
|
+
"id": {
|
|
307
|
+
"type": "string",
|
|
308
|
+
"minLength": 1
|
|
309
|
+
},
|
|
310
|
+
"question": {
|
|
311
|
+
"type": "string",
|
|
312
|
+
"minLength": 1
|
|
313
|
+
},
|
|
314
|
+
"at": {
|
|
315
|
+
"type": "string"
|
|
316
|
+
}
|
|
317
|
+
},
|
|
318
|
+
"required": [
|
|
319
|
+
"id",
|
|
320
|
+
"question",
|
|
321
|
+
"at"
|
|
322
|
+
],
|
|
323
|
+
"additionalProperties": false
|
|
324
|
+
}
|
|
325
|
+
},
|
|
300
326
|
"result": {
|
|
301
327
|
"type": [
|
|
302
328
|
"string",
|
|
@@ -319,6 +345,7 @@
|
|
|
319
345
|
"giver",
|
|
320
346
|
"givenBy",
|
|
321
347
|
"question",
|
|
348
|
+
"questions",
|
|
322
349
|
"result",
|
|
323
350
|
"updatedAt"
|
|
324
351
|
],
|
|
@@ -16,7 +16,7 @@ plain object for each port.
|
|
|
16
16
|
| [references/ports.md](references/ports.md) | Each port: what it is for, when the node calls it, the config, the interface, the remote methods, an example. |
|
|
17
17
|
| [references/host-api.md](references/host-api.md) | Each method of the host API: `submit`, `deliver`, `completeModel`, `missions`, `snapshot` and the others. |
|
|
18
18
|
| [references/manifest.md](references/manifest.md) | `orbital-plugin.json`, the `orbital.yaml` entry, the manifest, permissions, secrets, `context.init`. |
|
|
19
|
-
| [references/recipes.md](references/recipes.md) | Whole plugins: a cross-machine missions relay with a directory and placements, a telemetry dashboard, a Slack channel, a policy engine, a
|
|
19
|
+
| [references/recipes.md](references/recipes.md) | Whole plugins: a cross-machine missions relay with a directory and placements, a telemetry dashboard, a Slack channel, a policy engine, a harness. |
|
|
20
20
|
|
|
21
21
|
## Ports
|
|
22
22
|
|
|
@@ -113,8 +113,7 @@ npm test # build, then the conformance checks and your own checks
|
|
|
113
113
|
- Test your own logic next to the checks: call the port methods with a fake
|
|
114
114
|
`PluginContext`, as the template's test does.
|
|
115
115
|
- For a test against a real node, start the plugin with `startRemotePlugin`
|
|
116
|
-
and a node with `runNode
|
|
117
|
-
source repository.
|
|
116
|
+
and a node with `runNode`.
|
|
118
117
|
|
|
119
118
|
### 4. Check through the real protocol
|
|
120
119
|
|
|
@@ -215,15 +214,5 @@ start stops the start with "A plugin did not start: …".
|
|
|
215
214
|
|
|
216
215
|
## Worked examples
|
|
217
216
|
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
- `plugins/channel-slack`: a channel with a Socket Mode service, `host.deliver`, idempotent send, secrets, a panel and a command.
|
|
221
|
-
- `plugins/policy-judge`: a policy that calls a model through `host.completeModel`.
|
|
222
|
-
- `plugins/gatekeeper`: a policy that asks an agent of the receiving team through `host.askAgent` if a message from the network may reach the team.
|
|
223
|
-
- `plugins/store-object`: a store in an object store.
|
|
224
|
-
- `plugins/reasoner-open-ontologies`: a reasoner.
|
|
225
|
-
- `packages/orbital/src/core/node/static-directory.ts`: a directory, a `sessions.create` hook and a placement that send `<agent>@<node>` to another node.
|
|
226
|
-
- `packages/orbital/__tests__/core/fixtures/echo-plugin`: session hooks, a directory, a hosted tool, a panel, a service that submits a turn, and a command.
|
|
227
|
-
- `packages/orbital/__tests__/core/fixtures/echo-harness`: a harness and its turn program.
|
|
228
|
-
- `packages/orbital-harness-pi`: the adapter of the harness `pi`: its folder `.pi`, its install, and a turn program on the Pi coding agent.
|
|
229
|
-
- `packages/orbital-harness-codex`: the adapter of the harness `codex`: its folder `.codex`, its install in `config.toml`, and a turn program on the OpenAI Codex CLI.
|
|
217
|
+
- `@kaiba-cloud/orbital-harness-pi` on npm: the adapter of the harness `pi`: its folder `.pi`, its install, and a turn program on the Pi coding agent.
|
|
218
|
+
- `@kaiba-cloud/orbital-harness-codex` on npm: the adapter of the harness `codex`: its folder `.codex`, its install in `config.toml`, and a turn program on the OpenAI Codex CLI.
|
|
@@ -209,12 +209,12 @@ holds the mission calls them when the same plugin on another node sends it a
|
|
|
209
209
|
request. See [ports.md](ports.md#missions).
|
|
210
210
|
|
|
211
211
|
```ts
|
|
212
|
-
interface RelayJoin { missionId: string; giver: { node: string; sessionId: string } }
|
|
213
|
-
interface RelayReply { missionId: string; text: string; by: string }
|
|
214
|
-
interface RelayControl { missionId: string; by: string }
|
|
215
|
-
interface RelayMessage { missionId: string; text: string; by: string }
|
|
212
|
+
interface RelayJoin { missionId: string; giver: { node: string; sessionId: string }; team: string | null; agent: string | null }
|
|
213
|
+
interface RelayReply { missionId: string; text: string; by: string; team: string | null; agent: string | null }
|
|
214
|
+
interface RelayControl { missionId: string; by: string; team: string | null; agent: string | null }
|
|
215
|
+
interface RelayMessage { missionId: string; text: string; by: string; team: string | null; agent: string | null }
|
|
216
216
|
interface Said { sessionId: string; missionId: string | null; delivery: 'reply' | 'steer' | 'turn' }
|
|
217
|
-
interface MissionQuestion { missionId: string; node: string; lead: string | null; leadSessionId: string; question: string; giver: { node: string; sessionId: string } }
|
|
217
|
+
interface MissionQuestion { missionId: string; node: string; lead: string | null; leadSessionId: string; question: string; giver: { node: string; sessionId: string }; team: string | null }
|
|
218
218
|
```
|
|
219
219
|
|
|
220
220
|
- `joinMission` makes the remote session the giver of the mission. When the
|
|
@@ -229,6 +229,10 @@ interface MissionQuestion { missionId: string; node: string; lead: string | null
|
|
|
229
229
|
paused one.
|
|
230
230
|
- `askGiver` gives the question to the giver session on this node. It answers
|
|
231
231
|
false when the giver is not an attached session of this node.
|
|
232
|
+
- `team` and `agent` name the team and the agent of the session that sent the
|
|
233
|
+
request, or are null. In a question, `team` is the team of the mission and
|
|
234
|
+
`lead` is the agent. The node gives them to the `channel.receive` policy
|
|
235
|
+
action as `origin.team` and `origin.agent`, as a claim of the sender.
|
|
232
236
|
- Each of these acts only on this node. None relays again.
|
|
233
237
|
|
|
234
238
|
### snapshot
|
|
@@ -185,7 +185,7 @@ engine can govern each message that reaches the node over the network.
|
|
|
185
185
|
type ChannelTrust = 'verified' | 'unverified';
|
|
186
186
|
type ChannelOrigin =
|
|
187
187
|
| { kind: 'local' }
|
|
188
|
-
| { kind: 'network'; peer: string; address: Address; trust: ChannelTrust };
|
|
188
|
+
| { kind: 'network'; peer: string; address: Address; team: string | null; agent: string | null; trust: ChannelTrust };
|
|
189
189
|
type ChannelReceive = {
|
|
190
190
|
kind: 'channel.receive';
|
|
191
191
|
channel: string;
|
|
@@ -205,6 +205,12 @@ type ChannelReceive = {
|
|
|
205
205
|
`ade`. Else it is the name of the sender, or the name that the carrying
|
|
206
206
|
plugin gives.
|
|
207
207
|
- `origin.address` is the address of the sender.
|
|
208
|
+
- `origin.team` and `origin.agent` name the team and the agent of the session
|
|
209
|
+
that sent the message from its node. They are null when no session of a team
|
|
210
|
+
sent it, for example a person, a plain node or the CLI. The sending node puts
|
|
211
|
+
them in the message metadata (`orbital/fromTeam`, `orbital/fromAgent`) or in
|
|
212
|
+
the relay request (`team`, `agent`). They are as trusted as the channel: with
|
|
213
|
+
`trust: unverified` they are only the claim of the sender.
|
|
208
214
|
- `origin.trust` is `verified` only when the channel declares
|
|
209
215
|
`authenticated: true`. Every other path gives `unverified`.
|
|
210
216
|
- `origin.kind` is `local` only for a message on a channel that declares
|
|
@@ -1029,6 +1035,11 @@ transport, then calls the host API of its node:
|
|
|
1029
1035
|
| `resume(request)` | `host.resumeMission(request)` |
|
|
1030
1036
|
| `ask(question)` | `host.askGiver(question)` (true when delivered) |
|
|
1031
1037
|
|
|
1038
|
+
The near node fills `team` and `agent` with the team and the agent of the
|
|
1039
|
+
session that sends the request, or null. In a question, `team` is the team of
|
|
1040
|
+
the mission and `lead` is the agent. Carry the request whole: the far node
|
|
1041
|
+
gives these fields to its policy as `origin.team` and `origin.agent`.
|
|
1042
|
+
|
|
1032
1043
|
The far node does not relay a request again. To find which node holds a
|
|
1033
1044
|
mission, read `host.missions()` on each node: it gives the missions of that
|
|
1034
1045
|
node only. To show the missions of other nodes in the mission list, and for
|
|
@@ -1048,12 +1059,12 @@ interface MissionRelayPort {
|
|
|
1048
1059
|
say(request: RelayMessage): Promise<Said | null>;
|
|
1049
1060
|
}
|
|
1050
1061
|
interface RemoteGiver { node: string; sessionId: string }
|
|
1051
|
-
interface RelayJoin { missionId: string; giver: RemoteGiver }
|
|
1052
|
-
interface RelayReply { missionId: string; text: string; by: string }
|
|
1053
|
-
interface RelayControl { missionId: string; by: string }
|
|
1054
|
-
interface RelayMessage { missionId: string; text: string; by: string }
|
|
1062
|
+
interface RelayJoin { missionId: string; giver: RemoteGiver; team: string | null; agent: string | null }
|
|
1063
|
+
interface RelayReply { missionId: string; text: string; by: string; team: string | null; agent: string | null }
|
|
1064
|
+
interface RelayControl { missionId: string; by: string; team: string | null; agent: string | null }
|
|
1065
|
+
interface RelayMessage { missionId: string; text: string; by: string; team: string | null; agent: string | null }
|
|
1055
1066
|
interface Said { sessionId: string; missionId: string | null; delivery: 'reply' | 'steer' | 'turn' }
|
|
1056
|
-
interface MissionQuestion { missionId: string; node: string; lead: string | null; leadSessionId: string; question: string; giver: RemoteGiver }
|
|
1067
|
+
interface MissionQuestion { missionId: string; node: string; lead: string | null; leadSessionId: string; question: string; giver: RemoteGiver; team: string | null }
|
|
1057
1068
|
interface MissionRecord {
|
|
1058
1069
|
missionId: string; team: string | null; node: string; file: string | null;
|
|
1059
1070
|
state: 'open' | 'active' | 'waiting' | 'paused' | 'done' | 'failed';
|