@byok-sdk/client 0.4.2 → 0.6.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.
- package/README.md +18 -0
- package/dist/bin/byok-agent.js +204 -11
- package/dist/bin/byok-agent.js.map +1 -1
- package/dist/bin/byok-approval-mcp.js.map +1 -1
- package/dist/bin/config.d.ts +3 -1
- package/dist/bin/format.d.ts +2 -1
- package/dist/bin/official-release.d.ts +1 -0
- package/dist/bin/version.d.ts +2 -0
- package/dist/daemon/connection-manager.d.ts +2 -0
- package/dist/daemon/control-protocol.d.ts +3 -0
- package/dist/daemon/create-daemon.d.ts +14 -2
- package/dist/daemon/presence-publisher.d.ts +7 -1
- package/dist/daemon/progress-batcher.d.ts +13 -0
- package/dist/daemon/task-runner.d.ts +22 -0
- package/dist/daemon/ws-transport.d.ts +2 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +178 -11
- package/dist/index.js.map +1 -1
- package/dist/release-identity.d.ts +15 -0
- package/package.json +4 -4
package/README.md
CHANGED
|
@@ -63,6 +63,24 @@ validated registry. Only those logical IDs are advertised in `conn.hello`
|
|
|
63
63
|
and hosted presence; command, args, environment, headers, and credentials
|
|
64
64
|
remain local.
|
|
65
65
|
|
|
66
|
+
Hosted deployments that enforce an activity-ingress byte ceiling should inject
|
|
67
|
+
the same ceiling into the daemon. The byte count is the UTF-8 length of
|
|
68
|
+
`JSON.stringify(events)`; it does not include envelope or transport overhead.
|
|
69
|
+
One event that cannot fit fails the task locally without truncation or network
|
|
70
|
+
delivery.
|
|
71
|
+
|
|
72
|
+
```ts
|
|
73
|
+
createDaemon({
|
|
74
|
+
// ...normal device and transport configuration
|
|
75
|
+
progressBatch: {
|
|
76
|
+
maxBatchBytes: 64 * 1024,
|
|
77
|
+
},
|
|
78
|
+
});
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
The value is intentionally host-owned and has no SDK default because it is a
|
|
82
|
+
deployment/read-model policy, not a frozen protocol limit.
|
|
83
|
+
|
|
66
84
|
For a concrete private host composition, see the
|
|
67
85
|
[`examples/salesko-connector-broker`](../../examples/salesko-connector-broker)
|
|
68
86
|
reference. It keeps `@byok-sdk/client` credential-blind while combining
|
package/dist/bin/byok-agent.js
CHANGED
|
@@ -5,7 +5,7 @@ import { readFileSync, promises, linkSync, fstatSync, lstatSync, unlinkSync, con
|
|
|
5
5
|
import path20, { isAbsolute, join } from 'path';
|
|
6
6
|
import os from 'os';
|
|
7
7
|
import { DEVICE_ASSERTION_AUDIENCE_MAX_BYTES, DEVICE_ASSERTION_DEFAULT_TTL_MS, DEVICE_ASSERTION_MAX_TTL_MS, nonceSigningBytes, CapabilityDeclarationSchema, hasCapability, DeviceAssertionClaimsSchema, deviceAssertionSigningInput, DEVICE_ASSERTION_SCHEMA_ID } from '@byok-sdk/core';
|
|
8
|
-
import { TASK_STATES, CONFIGURED_TOOLSETS_MAX_ITEMS, ToolsetIdSchema, BYOK_PAIR_PATH, BYOK_CHALLENGE_PATH, BYOK_TOKEN_PATH, partitionAgentEvents, TASK_TRANSITIONS, encodeEnvelope, createEnvelope, byokBlobUrlPath, BYOK_BLOBS_PATH, byokBlobFinalizePath, checkResultDocument, MAX_MESSAGES_PER_BATCH, BYOK_CAPABILITIES_PATH, BYOK_PRESENCE_PATH, RuntimeIdSchema,
|
|
8
|
+
import { TASK_STATES, CONFIGURED_TOOLSETS_MAX_ITEMS, ToolsetIdSchema, BYOK_PAIR_PATH, BYOK_CHALLENGE_PATH, BYOK_TOKEN_PATH, partitionAgentEvents, TASK_TRANSITIONS, encodeEnvelope, PROTOCOL_VERSION, createEnvelope, byokBlobUrlPath, BYOK_BLOBS_PATH, byokBlobFinalizePath, checkResultDocument, MAX_MESSAGES_PER_BATCH, BYOK_CAPABILITIES_PATH, BYOK_PRESENCE_PATH, RuntimeIdSchema, TERMINAL_INFERENCE_USAGE_MAX_DURATION_MS, TERMINAL_INFERENCE_USAGE_MAX_TOKENS, RESULT_DOCUMENT_MAX_BYTES, decodeEnvelope, BYOK_MESSAGES_PATH, MessagesSendResponseSchema, BYOK_EVENTS_PATH, parseMessage, UnknownMessageTypeError, BYOK_WS_PATH } from '@byok-sdk/protocol';
|
|
9
9
|
import { promisify } from 'util';
|
|
10
10
|
import { fileURLToPath } from 'url';
|
|
11
11
|
import 'readline';
|
|
@@ -77,6 +77,28 @@ var SteerUnsupportedError = class extends Error {
|
|
|
77
77
|
}
|
|
78
78
|
};
|
|
79
79
|
|
|
80
|
+
// src/release-identity.ts
|
|
81
|
+
var LOCAL_AGENT_RELEASE_VERSION_MAX_LENGTH = 128;
|
|
82
|
+
var LOCAL_AGENT_RELEASE_BUILD_ID_MAX_LENGTH = 128;
|
|
83
|
+
var STRICT_SEMVER_PATTERN = new RegExp(
|
|
84
|
+
"^(0|[1-9]\\d*)\\.(0|[1-9]\\d*)\\.(0|[1-9]\\d*)(?:-(?:0|[1-9]\\d*|\\d*[A-Za-z-][0-9A-Za-z-]*)(?:\\.(?:0|[1-9]\\d*|\\d*[A-Za-z-][0-9A-Za-z-]*))*)?(?:\\+[0-9A-Za-z-]+(?:\\.[0-9A-Za-z-]+)*)?$"
|
|
85
|
+
);
|
|
86
|
+
var BUILD_ID_PATTERN = /^[A-Za-z0-9][A-Za-z0-9._:-]*$/;
|
|
87
|
+
function resolveLocalAgentReleaseIdentity(input) {
|
|
88
|
+
if (input === void 0 || typeof input.version !== "string" || input.version.length > LOCAL_AGENT_RELEASE_VERSION_MAX_LENGTH || !STRICT_SEMVER_PATTERN.test(input.version)) {
|
|
89
|
+
throw new Error("DaemonConfig.localAgentRelease.version must be canonical strict SemVer");
|
|
90
|
+
}
|
|
91
|
+
if (input.buildId !== void 0 && (typeof input.buildId !== "string" || input.buildId.length > LOCAL_AGENT_RELEASE_BUILD_ID_MAX_LENGTH || !BUILD_ID_PATTERN.test(input.buildId))) {
|
|
92
|
+
throw new Error(
|
|
93
|
+
`DaemonConfig.localAgentRelease.buildId must be 1-${LOCAL_AGENT_RELEASE_BUILD_ID_MAX_LENGTH} safe opaque characters`
|
|
94
|
+
);
|
|
95
|
+
}
|
|
96
|
+
return Object.freeze({
|
|
97
|
+
version: input.version,
|
|
98
|
+
...input.buildId === void 0 ? {} : { buildId: input.buildId }
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
|
|
80
102
|
// src/runtime-failure.ts
|
|
81
103
|
var RUNTIME_EXECUTION_FAILURE_BRAND = /* @__PURE__ */ Symbol.for("@byok-sdk/client/RuntimeExecutionFailure/v1");
|
|
82
104
|
var RUNTIME_DISPOSAL_FAILURE_BRAND = /* @__PURE__ */ Symbol.for("@byok-sdk/client/RuntimeDisposalFailure/v1");
|
|
@@ -4419,7 +4441,20 @@ var PresencePublisher = class {
|
|
|
4419
4441
|
headers: { "content-type": "application/json" },
|
|
4420
4442
|
body: JSON.stringify({
|
|
4421
4443
|
level: "online",
|
|
4422
|
-
...this.opts.configuredToolsets === void 0 ? {} : { configuredToolsets: this.opts.configuredToolsets }
|
|
4444
|
+
...this.opts.configuredToolsets === void 0 ? {} : { configuredToolsets: this.opts.configuredToolsets },
|
|
4445
|
+
...this.opts.clientVersion === void 0 ? {} : { clientVersion: this.opts.clientVersion },
|
|
4446
|
+
...this.opts.protocolVersions === void 0 ? {} : { protocolVersions: [...this.opts.protocolVersions] },
|
|
4447
|
+
// Presence is a lightweight readiness fact, not a second
|
|
4448
|
+
// capability-negotiation channel. Keep its projection identical
|
|
4449
|
+
// to the persisted presence contract: the conn.hello snapshot's
|
|
4450
|
+
// runtime identity/version/auth facts, with no capabilities blob.
|
|
4451
|
+
...this.opts.runtimes === void 0 ? {} : {
|
|
4452
|
+
runtimes: this.opts.runtimes.map(({ id, version, authPresent }) => ({
|
|
4453
|
+
id,
|
|
4454
|
+
...version === void 0 ? {} : { version },
|
|
4455
|
+
...authPresent === void 0 ? {} : { authPresent }
|
|
4456
|
+
}))
|
|
4457
|
+
}
|
|
4423
4458
|
})
|
|
4424
4459
|
},
|
|
4425
4460
|
this.opts.auth
|
|
@@ -5145,6 +5180,7 @@ var WsTransport = class {
|
|
|
5145
5180
|
capabilities: this.opts.capabilities,
|
|
5146
5181
|
deviceId: this.opts.deviceId,
|
|
5147
5182
|
productId: this.opts.productId,
|
|
5183
|
+
clientVersion: this.opts.clientVersion,
|
|
5148
5184
|
runtimes: this.opts.runtimes,
|
|
5149
5185
|
configuredToolsets: this.opts.configuredToolsets === void 0 ? void 0 : [...this.opts.configuredToolsets],
|
|
5150
5186
|
cursor: this.opts.getCursor?.()
|
|
@@ -5235,6 +5271,7 @@ var ConnectionManager = class {
|
|
|
5235
5271
|
deviceId: opts.deviceId,
|
|
5236
5272
|
productId: opts.productId,
|
|
5237
5273
|
capabilities: opts.capabilities,
|
|
5274
|
+
clientVersion: opts.clientVersion,
|
|
5238
5275
|
runtimes: opts.runtimes,
|
|
5239
5276
|
configuredToolsets: opts.configuredToolsets,
|
|
5240
5277
|
getCursor: () => this.cursor,
|
|
@@ -8275,6 +8312,11 @@ function createStatfsFreeBytesProvider(dir) {
|
|
|
8275
8312
|
var BASE_PLATFORM_ALLOWLIST = [
|
|
8276
8313
|
"PATH",
|
|
8277
8314
|
"HOME",
|
|
8315
|
+
// macOS credential-store discovery used by subscription-authenticated
|
|
8316
|
+
// agent CLIs depends on the login account name as well as HOME. Omitting
|
|
8317
|
+
// USER makes `claude auth status` report logged out under the filtered
|
|
8318
|
+
// child environment even when the host CLI is logged in.
|
|
8319
|
+
"USER",
|
|
8278
8320
|
"USERPROFILE",
|
|
8279
8321
|
"TMPDIR",
|
|
8280
8322
|
"TEMP",
|
|
@@ -8382,11 +8424,37 @@ function computeEffectivePolicy(offered, ceiling) {
|
|
|
8382
8424
|
}
|
|
8383
8425
|
|
|
8384
8426
|
// src/daemon/progress-batcher.ts
|
|
8427
|
+
var ProgressEventTooLargeError = class extends Error {
|
|
8428
|
+
constructor(actualBytes, maxBatchBytes) {
|
|
8429
|
+
super(`Progress event requires ${actualBytes} UTF-8 bytes, exceeding maxBatchBytes ${maxBatchBytes}.`);
|
|
8430
|
+
this.actualBytes = actualBytes;
|
|
8431
|
+
this.maxBatchBytes = maxBatchBytes;
|
|
8432
|
+
this.name = "ProgressEventTooLargeError";
|
|
8433
|
+
}
|
|
8434
|
+
actualBytes;
|
|
8435
|
+
maxBatchBytes;
|
|
8436
|
+
};
|
|
8437
|
+
var encoder = new TextEncoder();
|
|
8438
|
+
function assertPositiveSafeInteger(value, name) {
|
|
8439
|
+
if (value !== void 0 && (!Number.isSafeInteger(value) || value <= 0)) {
|
|
8440
|
+
throw new TypeError(`${name} must be a positive safe integer when configured.`);
|
|
8441
|
+
}
|
|
8442
|
+
}
|
|
8443
|
+
function validateProgressBatcherOptions(options = {}) {
|
|
8444
|
+
assertPositiveSafeInteger(options.maxBatchSize, "maxBatchSize");
|
|
8445
|
+
assertPositiveSafeInteger(options.flushIntervalMs, "flushIntervalMs");
|
|
8446
|
+
assertPositiveSafeInteger(options.maxBatchBytes, "maxBatchBytes");
|
|
8447
|
+
}
|
|
8448
|
+
function encodedEventsBytes(events) {
|
|
8449
|
+
return encoder.encode(JSON.stringify(events)).length;
|
|
8450
|
+
}
|
|
8385
8451
|
var ProgressBatcher = class {
|
|
8386
8452
|
constructor(emit, options = {}) {
|
|
8387
8453
|
this.emit = emit;
|
|
8454
|
+
validateProgressBatcherOptions(options);
|
|
8388
8455
|
this.maxBatchSize = options.maxBatchSize ?? 10;
|
|
8389
8456
|
this.flushIntervalMs = options.flushIntervalMs ?? 250;
|
|
8457
|
+
this.maxBatchBytes = options.maxBatchBytes;
|
|
8390
8458
|
}
|
|
8391
8459
|
emit;
|
|
8392
8460
|
buffer = [];
|
|
@@ -8394,7 +8462,17 @@ var ProgressBatcher = class {
|
|
|
8394
8462
|
timer;
|
|
8395
8463
|
maxBatchSize;
|
|
8396
8464
|
flushIntervalMs;
|
|
8465
|
+
maxBatchBytes;
|
|
8397
8466
|
push(event) {
|
|
8467
|
+
if (this.maxBatchBytes !== void 0) {
|
|
8468
|
+
const eventBytes = encodedEventsBytes([event]);
|
|
8469
|
+
if (eventBytes > this.maxBatchBytes) {
|
|
8470
|
+
throw new ProgressEventTooLargeError(eventBytes, this.maxBatchBytes);
|
|
8471
|
+
}
|
|
8472
|
+
if (this.buffer.length > 0 && encodedEventsBytes([...this.buffer, event]) > this.maxBatchBytes) {
|
|
8473
|
+
this.flush();
|
|
8474
|
+
}
|
|
8475
|
+
}
|
|
8398
8476
|
this.buffer.push(event);
|
|
8399
8477
|
if (this.buffer.length >= this.maxBatchSize) {
|
|
8400
8478
|
this.flush();
|
|
@@ -8447,6 +8525,7 @@ var MAX_INLINE_ARTIFACT_BYTES = 64 * 1024;
|
|
|
8447
8525
|
var MAX_TRACKED_TASK_IDS = 2e3;
|
|
8448
8526
|
var MAX_DURATION_EXCEEDED_REASON_PREFIX = "resource limit exceeded: maxDurationMs";
|
|
8449
8527
|
var MAX_OUTPUT_BYTES_EXCEEDED_REASON_PREFIX = "resource limit exceeded: maxTaskOutputBytes";
|
|
8528
|
+
var MAX_PROGRESS_BATCH_BYTES_EXCEEDED_REASON_PREFIX = "resource limit exceeded: progressBatch.maxBatchBytes";
|
|
8450
8529
|
var RESULT_DOCUMENT_UNDELIVERABLE_REASON_PREFIX = "result document undeliverable";
|
|
8451
8530
|
function resultDocumentRejectionDetail(check) {
|
|
8452
8531
|
switch (check.reason) {
|
|
@@ -8466,6 +8545,9 @@ var DEFAULT_MAX_TASK_OUTPUT_BYTES = 64 * 1024 * 1024;
|
|
|
8466
8545
|
function isKnownRuntimeId(id) {
|
|
8467
8546
|
return RuntimeIdSchema.safeParse(id).success;
|
|
8468
8547
|
}
|
|
8548
|
+
function terminalUsageNumber(value, maximum) {
|
|
8549
|
+
return value !== void 0 && Number.isSafeInteger(value) && value >= 0 && value <= maximum ? value : void 0;
|
|
8550
|
+
}
|
|
8469
8551
|
var DEFAULT_RUNTIME_PREFERENCE = ["claude", "codex", "pi"];
|
|
8470
8552
|
function orderByPreference(candidates, preference) {
|
|
8471
8553
|
const rank = new Map(preference.map((id, index) => [id, index]));
|
|
@@ -8765,7 +8847,13 @@ var TaskRunner = class {
|
|
|
8765
8847
|
const timeoutMs = this.deps.shutdownInterruptTimeoutMs ?? DEFAULT_SHUTDOWN_INTERRUPT_TIMEOUT_MS;
|
|
8766
8848
|
await raceSettleFirst(() => active.session.interrupt(), timeoutMs);
|
|
8767
8849
|
if (this.tasks.get(active.taskId) !== active) return true;
|
|
8768
|
-
this.deps.send(
|
|
8850
|
+
this.deps.send(
|
|
8851
|
+
createEnvelope(
|
|
8852
|
+
"task.fail",
|
|
8853
|
+
{ reason, retryable, ...this.terminalInferenceUsagePayload(active) },
|
|
8854
|
+
{ taskId: active.taskId }
|
|
8855
|
+
)
|
|
8856
|
+
);
|
|
8769
8857
|
return this.finish(active.taskId);
|
|
8770
8858
|
}
|
|
8771
8859
|
/** Graceful-shutdown caller of {@link teardownActiveTask} — see `shutdownActiveTasks`'s own doc comment. `retryable: true`: nothing about the task/policy itself was ever at fault, only this device's own availability right now. */
|
|
@@ -9143,7 +9231,8 @@ var TaskRunner = class {
|
|
|
9143
9231
|
this.deps.batcherOptions
|
|
9144
9232
|
),
|
|
9145
9233
|
approvalQueue: [],
|
|
9146
|
-
outputBytesSoFar: 0
|
|
9234
|
+
outputBytesSoFar: 0,
|
|
9235
|
+
startedAtMs: Date.now()
|
|
9147
9236
|
};
|
|
9148
9237
|
if (this.pendingCancelled.has(taskId)) {
|
|
9149
9238
|
const reason = this.pendingCancelled.get(taskId);
|
|
@@ -9155,7 +9244,13 @@ var TaskRunner = class {
|
|
|
9155
9244
|
} catch {
|
|
9156
9245
|
}
|
|
9157
9246
|
await this.updateGitPhaseBestEffort(gitWorkspaceId, "cancelled");
|
|
9158
|
-
this.deps.send(
|
|
9247
|
+
this.deps.send(
|
|
9248
|
+
createEnvelope(
|
|
9249
|
+
"task.cancelled",
|
|
9250
|
+
{ reason, ...this.terminalInferenceUsagePayload(active) },
|
|
9251
|
+
{ taskId }
|
|
9252
|
+
)
|
|
9253
|
+
);
|
|
9159
9254
|
await this.finish(taskId);
|
|
9160
9255
|
return;
|
|
9161
9256
|
}
|
|
@@ -9228,6 +9323,9 @@ var TaskRunner = class {
|
|
|
9228
9323
|
);
|
|
9229
9324
|
return;
|
|
9230
9325
|
}
|
|
9326
|
+
if (event.type === "usage") {
|
|
9327
|
+
active.lastUsage = event;
|
|
9328
|
+
}
|
|
9231
9329
|
if (event.type === "needs_approval") {
|
|
9232
9330
|
active.batcher.flush();
|
|
9233
9331
|
const { taskId } = active;
|
|
@@ -9280,7 +9378,8 @@ var TaskRunner = class {
|
|
|
9280
9378
|
// (where the codec actually serializes it), so a contextual
|
|
9281
9379
|
// `toJSON(key)` or an unstable getter cannot make the wire
|
|
9282
9380
|
// bytes differ from what the cap gate approved.
|
|
9283
|
-
...outcome.document !== void 0 ? { document: outcome.document } : {}
|
|
9381
|
+
...outcome.document !== void 0 ? { document: outcome.document } : {},
|
|
9382
|
+
...this.terminalInferenceUsagePayload(active)
|
|
9284
9383
|
},
|
|
9285
9384
|
{ taskId: active.taskId, sessionRef: active.session.sessionRef }
|
|
9286
9385
|
)
|
|
@@ -9303,6 +9402,13 @@ var TaskRunner = class {
|
|
|
9303
9402
|
} catch (err) {
|
|
9304
9403
|
if (this.tasks.get(active.taskId) !== active || active.beingTornDown) return;
|
|
9305
9404
|
active.batcher.flush();
|
|
9405
|
+
if (err instanceof ProgressEventTooLargeError) {
|
|
9406
|
+
await this.failActiveTaskForResourceLimit(
|
|
9407
|
+
active.taskId,
|
|
9408
|
+
`${MAX_PROGRESS_BATCH_BYTES_EXCEEDED_REASON_PREFIX}: event requires ${err.actualBytes} UTF-8 bytes, exceeding the configured limit of ${err.maxBatchBytes} bytes`
|
|
9409
|
+
);
|
|
9410
|
+
return;
|
|
9411
|
+
}
|
|
9306
9412
|
const failure = projectRuntimeBoundaryFailure(err, "run");
|
|
9307
9413
|
if (failure.contractViolation) {
|
|
9308
9414
|
console.error("[byok/client] runtime adapter events iterable returned an untyped failure", err);
|
|
@@ -9390,7 +9496,13 @@ var TaskRunner = class {
|
|
|
9390
9496
|
} catch {
|
|
9391
9497
|
}
|
|
9392
9498
|
await this.observeGit(active, "salvage");
|
|
9393
|
-
this.deps.send(
|
|
9499
|
+
this.deps.send(
|
|
9500
|
+
createEnvelope(
|
|
9501
|
+
"task.cancelled",
|
|
9502
|
+
{ reason, ...this.terminalInferenceUsagePayload(active) },
|
|
9503
|
+
{ taskId }
|
|
9504
|
+
)
|
|
9505
|
+
);
|
|
9394
9506
|
await this.finish(taskId);
|
|
9395
9507
|
}
|
|
9396
9508
|
/** M3-B: bounded insert for `pendingCancelled` — see its class-level doc comment and `MAX_TRACKED_TASK_IDS`. Evicts the oldest SAFE-TO-EVICT entry once over cap — see `evictPendingCancelled` (finding #5: not simply "the oldest entry", which could be an in-flight offer's own cancel marker). */
|
|
@@ -9828,7 +9940,13 @@ var TaskRunner = class {
|
|
|
9828
9940
|
} catch {
|
|
9829
9941
|
}
|
|
9830
9942
|
await this.observeGit(active, "salvage");
|
|
9831
|
-
this.deps.send(
|
|
9943
|
+
this.deps.send(
|
|
9944
|
+
createEnvelope(
|
|
9945
|
+
"task.fail",
|
|
9946
|
+
{ reason: reason ?? "rejected", retryable: false, ...this.terminalInferenceUsagePayload(active) },
|
|
9947
|
+
{ taskId }
|
|
9948
|
+
)
|
|
9949
|
+
);
|
|
9832
9950
|
await this.finish(taskId);
|
|
9833
9951
|
}
|
|
9834
9952
|
/** Pre-claim, fail-closed rejection (protocol §3.2) — never claims first. */
|
|
@@ -9846,9 +9964,44 @@ var TaskRunner = class {
|
|
|
9846
9964
|
return;
|
|
9847
9965
|
}
|
|
9848
9966
|
if (active) await this.observeGit(active, "salvage");
|
|
9849
|
-
this.deps.send(
|
|
9967
|
+
this.deps.send(
|
|
9968
|
+
createEnvelope(
|
|
9969
|
+
"task.fail",
|
|
9970
|
+
active === void 0 ? { reason, retryable } : { reason, retryable, ...this.terminalInferenceUsagePayload(active) },
|
|
9971
|
+
{ taskId }
|
|
9972
|
+
)
|
|
9973
|
+
);
|
|
9850
9974
|
await this.finish(taskId);
|
|
9851
9975
|
}
|
|
9976
|
+
/**
|
|
9977
|
+
* Build the optional terminal observation from facts this running daemon
|
|
9978
|
+
* actually has. No offered `dispatchSelection` is echoed here: it is a
|
|
9979
|
+
* requested execution target, not an adapter-reported provider/model fact.
|
|
9980
|
+
* The bundled adapter event contracts currently expose token observations
|
|
9981
|
+
* (Codex and Claude) but no provider/model observation, so those keys stay
|
|
9982
|
+
* absent. Pi exposes no native usage observation, so its terminal payload
|
|
9983
|
+
* omits this optional block rather than fabricating a usage observation from
|
|
9984
|
+
* independently known runtime, elapsed duration, or Local Agent version.
|
|
9985
|
+
*/
|
|
9986
|
+
terminalInferenceUsagePayload(active) {
|
|
9987
|
+
const release = this.deps.localAgentRelease;
|
|
9988
|
+
const runtimeId = active.adapter.descriptor.id;
|
|
9989
|
+
if (release === void 0 || active.lastUsage === void 0 || !isKnownRuntimeId(runtimeId)) return {};
|
|
9990
|
+
const nowMs = Date.now();
|
|
9991
|
+
const durationMs = terminalUsageNumber(nowMs - active.startedAtMs, TERMINAL_INFERENCE_USAGE_MAX_DURATION_MS);
|
|
9992
|
+
const promptTokens = terminalUsageNumber(active.lastUsage?.inputTokens, TERMINAL_INFERENCE_USAGE_MAX_TOKENS);
|
|
9993
|
+
const completionTokens = terminalUsageNumber(active.lastUsage?.outputTokens, TERMINAL_INFERENCE_USAGE_MAX_TOKENS);
|
|
9994
|
+
return {
|
|
9995
|
+
usage: {
|
|
9996
|
+
runtime: runtimeId,
|
|
9997
|
+
clientVersion: release.version,
|
|
9998
|
+
reportedAt: new Date(nowMs).toISOString(),
|
|
9999
|
+
...promptTokens === void 0 ? {} : { promptTokens },
|
|
10000
|
+
...completionTokens === void 0 ? {} : { completionTokens },
|
|
10001
|
+
...durationMs === void 0 ? {} : { durationMs }
|
|
10002
|
+
}
|
|
10003
|
+
};
|
|
10004
|
+
}
|
|
9852
10005
|
/**
|
|
9853
10006
|
* additive-minor (`task.complete.document`): the whole daemon-side gate
|
|
9854
10007
|
* between a configured {@link ResultDocumentExtractor} and the wire —
|
|
@@ -10377,6 +10530,7 @@ function createDaemonWithAdapters(config, adapters, overrides = {}) {
|
|
|
10377
10530
|
return buildDaemonWithAdapters(config, adapters, overrides);
|
|
10378
10531
|
}
|
|
10379
10532
|
function buildDaemonWithAdapters(config, adapters, overrides = {}, assertionProbe) {
|
|
10533
|
+
const localAgentRelease = resolveLocalAgentReleaseIdentity(config.localAgentRelease);
|
|
10380
10534
|
const mcpToolsets = resolveMcpToolsets(config.mcpToolsets);
|
|
10381
10535
|
const configuredToolsets = Object.freeze(
|
|
10382
10536
|
[...mcpToolsets?.keys() ?? []].sort()
|
|
@@ -10389,6 +10543,7 @@ function buildDaemonWithAdapters(config, adapters, overrides = {}, assertionProb
|
|
|
10389
10543
|
`DaemonConfig.maxTaskOutputBytes must be a positive number (or omitted to use the ${DEFAULT_MAX_TASK_OUTPUT_BYTES}-byte default) \u2014 got ${config.maxTaskOutputBytes}. Pass Number.POSITIVE_INFINITY to explicitly disable the cap; 0 or a negative number is rejected rather than silently treated as "disabled".`
|
|
10390
10544
|
);
|
|
10391
10545
|
}
|
|
10546
|
+
validateProgressBatcherOptions(config.progressBatch);
|
|
10392
10547
|
const presenceCadence = {
|
|
10393
10548
|
intervalMs: config.presence?.intervalMs ?? DEFAULT_PRESENCE_HEARTBEAT_INTERVAL_MS,
|
|
10394
10549
|
ttlMs: config.presence?.ttlMs ?? DEFAULT_PRESENCE_TTL_MS,
|
|
@@ -10489,6 +10644,7 @@ function buildDaemonWithAdapters(config, adapters, overrides = {}, assertionProb
|
|
|
10489
10644
|
let controlServerHandle;
|
|
10490
10645
|
let daemonOwnerLease;
|
|
10491
10646
|
let presencePublisher;
|
|
10647
|
+
let detectedRuntimeFacts = [];
|
|
10492
10648
|
let presenceDiscovery;
|
|
10493
10649
|
let presenceDiscoveryInFlight = false;
|
|
10494
10650
|
let shutdownPromise;
|
|
@@ -10622,6 +10778,7 @@ function buildDaemonWithAdapters(config, adapters, overrides = {}, assertionProb
|
|
|
10622
10778
|
Promise.resolve(new BlobClient(config.serverUrl, auth))
|
|
10623
10779
|
]);
|
|
10624
10780
|
observer.noteRuntimesDetected(runtimes);
|
|
10781
|
+
detectedRuntimeFacts = runtimes;
|
|
10625
10782
|
const capabilities = computeCapabilities(adapters);
|
|
10626
10783
|
const journalIdentity = config.hostedJournal ? { tenantId: config.hostedJournal.tenantId, productId: config.productId, deviceId: record.deviceId } : void 0;
|
|
10627
10784
|
const sendEnvelope = activeJournal && journalIdentity ? (envelope) => {
|
|
@@ -10674,7 +10831,7 @@ function buildDaemonWithAdapters(config, adapters, overrides = {}, assertionProb
|
|
|
10674
10831
|
// untouched. See `observer.ts`'s module doc comment.
|
|
10675
10832
|
send: sendEnvelope,
|
|
10676
10833
|
blobClient,
|
|
10677
|
-
batcherOptions:
|
|
10834
|
+
batcherOptions: config.progressBatch,
|
|
10678
10835
|
sessionWorkspaces,
|
|
10679
10836
|
gitWorkspaceManager,
|
|
10680
10837
|
gitWorkspaceStore,
|
|
@@ -10699,6 +10856,10 @@ function buildDaemonWithAdapters(config, adapters, overrides = {}, assertionProb
|
|
|
10699
10856
|
approvalRegistry,
|
|
10700
10857
|
storeDir,
|
|
10701
10858
|
productId: config.productId,
|
|
10859
|
+
// U2 terminal inference usage consumes the one U4a-resolved,
|
|
10860
|
+
// process-immutable identity captured above. This is composition-only:
|
|
10861
|
+
// TaskRunner receives no config, manifest, or second identity resolver.
|
|
10862
|
+
localAgentRelease,
|
|
10702
10863
|
approvalTimeoutMs: overrides.approvalTimeoutMs,
|
|
10703
10864
|
// Finding F5(a): see TaskRunnerDeps.shutdownInterruptTimeoutMs's own doc comment.
|
|
10704
10865
|
shutdownInterruptTimeoutMs: overrides.shutdown?.taskInterruptTimeoutMs,
|
|
@@ -10744,6 +10905,7 @@ function buildDaemonWithAdapters(config, adapters, overrides = {}, assertionProb
|
|
|
10744
10905
|
deviceId: record.deviceId,
|
|
10745
10906
|
productId: config.productId,
|
|
10746
10907
|
capabilities,
|
|
10908
|
+
clientVersion: localAgentRelease.version,
|
|
10747
10909
|
runtimes,
|
|
10748
10910
|
configuredToolsets,
|
|
10749
10911
|
auth,
|
|
@@ -10833,6 +10995,9 @@ function buildDaemonWithAdapters(config, adapters, overrides = {}, assertionProb
|
|
|
10833
10995
|
serverUrl: config.serverUrl,
|
|
10834
10996
|
auth,
|
|
10835
10997
|
configuredToolsets,
|
|
10998
|
+
clientVersion: localAgentRelease.version,
|
|
10999
|
+
protocolVersions: [PROTOCOL_VERSION],
|
|
11000
|
+
runtimes: detectedRuntimeFacts,
|
|
10836
11001
|
...presenceCadence,
|
|
10837
11002
|
onDegraded: (reason) => console.warn(`[byok/client] ${reason}`)
|
|
10838
11003
|
});
|
|
@@ -11014,6 +11179,7 @@ function buildDaemonWithAdapters(config, adapters, overrides = {}, assertionProb
|
|
|
11014
11179
|
const activeTasks = observer.tasks().filter((task) => TASK_TRANSITIONS[task.state].length > 0).map((task) => ({ taskId: task.taskId, state: task.state }));
|
|
11015
11180
|
const pendingApprovals = approvalRegistry.list();
|
|
11016
11181
|
return {
|
|
11182
|
+
localAgentRelease,
|
|
11017
11183
|
pid: process.pid,
|
|
11018
11184
|
uptimeMs: startedAt !== void 0 ? Date.now() - startedAt : 0,
|
|
11019
11185
|
paired: auth.deviceId !== void 0,
|
|
@@ -11212,6 +11378,7 @@ function buildDaemonWithAdapters(config, adapters, overrides = {}, assertionProb
|
|
|
11212
11378
|
}
|
|
11213
11379
|
function status() {
|
|
11214
11380
|
return {
|
|
11381
|
+
localAgentRelease,
|
|
11215
11382
|
paired: auth.deviceId !== void 0,
|
|
11216
11383
|
connected: connectionState === "open",
|
|
11217
11384
|
degraded: connection?.isTransportDegraded() ?? false,
|
|
@@ -11841,6 +12008,13 @@ function createServiceLifecycle(def, opts = {}) {
|
|
|
11841
12008
|
throw new UnsupportedServicePlatformError(platform);
|
|
11842
12009
|
}
|
|
11843
12010
|
}
|
|
12011
|
+
|
|
12012
|
+
// src/bin/official-release.ts
|
|
12013
|
+
var OFFICIAL_LOCAL_AGENT_RELEASE = resolveLocalAgentReleaseIdentity({
|
|
12014
|
+
version: "0.6.0"
|
|
12015
|
+
});
|
|
12016
|
+
|
|
12017
|
+
// src/bin/config.ts
|
|
11844
12018
|
var REQUIRED_FIELDS = ["productName", "productId", "serverUrl", "workspaceRoot"];
|
|
11845
12019
|
var ConfigError = class extends Error {
|
|
11846
12020
|
constructor(message) {
|
|
@@ -11863,6 +12037,9 @@ function loadConfig(configPath, overrides = {}) {
|
|
|
11863
12037
|
throw new ConfigError(`config at "${configPath}" is not valid JSON: ${err instanceof Error ? err.message : String(err)}`);
|
|
11864
12038
|
}
|
|
11865
12039
|
}
|
|
12040
|
+
if (Object.prototype.hasOwnProperty.call(base, "localAgentRelease") || Object.prototype.hasOwnProperty.call(overrides, "localAgentRelease")) {
|
|
12041
|
+
throw new ConfigError('config field "localAgentRelease" is distribution-owned and must not be supplied');
|
|
12042
|
+
}
|
|
11866
12043
|
const merged = { ...base, ...overrides };
|
|
11867
12044
|
if (merged.gitWorkspace !== void 0) {
|
|
11868
12045
|
try {
|
|
@@ -11876,7 +12053,7 @@ function loadConfig(configPath, overrides = {}) {
|
|
|
11876
12053
|
throw new ConfigError(`config is missing required field "${field}"`);
|
|
11877
12054
|
}
|
|
11878
12055
|
}
|
|
11879
|
-
return merged;
|
|
12056
|
+
return { ...merged, localAgentRelease: OFFICIAL_LOCAL_AGENT_RELEASE };
|
|
11880
12057
|
}
|
|
11881
12058
|
function resolveStoreDir(config) {
|
|
11882
12059
|
return DeviceStore.resolveDir(config.productId, config.storeDir);
|
|
@@ -12050,6 +12227,9 @@ function formatStatusLines(view) {
|
|
|
12050
12227
|
const lines = [];
|
|
12051
12228
|
const label = view.branding?.displayName ?? view.productName;
|
|
12052
12229
|
lines.push(`product: ${label} (${view.productId})`);
|
|
12230
|
+
lines.push(
|
|
12231
|
+
`local-agent-release: ${view.localAgentRelease.version}${view.localAgentRelease.buildId ? ` buildId=${view.localAgentRelease.buildId}` : ""}`
|
|
12232
|
+
);
|
|
12053
12233
|
if (view.branding?.supportUrl) lines.push(`support: ${view.branding.supportUrl}`);
|
|
12054
12234
|
lines.push(`paired: ${view.paired ? "yes" : "no"}${view.deviceId ? ` deviceId=${view.deviceId}` : ""}`);
|
|
12055
12235
|
lines.push(
|
|
@@ -12065,8 +12245,10 @@ function formatStatusLines(view) {
|
|
|
12065
12245
|
return lines;
|
|
12066
12246
|
}
|
|
12067
12247
|
function formatLiveStatusLines(live) {
|
|
12248
|
+
const liveRelease = live.localAgentRelease;
|
|
12068
12249
|
const lines = [
|
|
12069
12250
|
`live: pid=${live.pid} uptimeMs=${live.uptimeMs} transport=${live.transport}`,
|
|
12251
|
+
liveRelease ? `live-local-agent-release: ${liveRelease.version}${liveRelease.buildId ? ` buildId=${liveRelease.buildId}` : ""}` : "live-local-agent-release: unknown",
|
|
12070
12252
|
`live-paired: ${live.paired ? "yes" : "no"}${live.deviceId ? ` deviceId=${live.deviceId}` : ""}`,
|
|
12071
12253
|
`live-runtimes: ${live.runtimeIds.length ? live.runtimeIds.join(",") : "(none)"}`
|
|
12072
12254
|
];
|
|
@@ -13755,6 +13937,7 @@ async function runStatusCommand(config, deps = {}) {
|
|
|
13755
13937
|
]);
|
|
13756
13938
|
const tasks = deriveTasksFromEvents(events);
|
|
13757
13939
|
const view = {
|
|
13940
|
+
localAgentRelease: config.localAgentRelease,
|
|
13758
13941
|
productName: config.productName,
|
|
13759
13942
|
productId: config.productId,
|
|
13760
13943
|
branding: config.branding,
|
|
@@ -14183,11 +14366,17 @@ async function runWorkspacesCommand(config, deps = {}) {
|
|
|
14183
14366
|
for (const record of records) log(formatWorkspaceLine(record, deps.showPaths === true));
|
|
14184
14367
|
}
|
|
14185
14368
|
|
|
14369
|
+
// src/bin/version.ts
|
|
14370
|
+
function runVersionCommand(log = console.log) {
|
|
14371
|
+
log(OFFICIAL_LOCAL_AGENT_RELEASE.version);
|
|
14372
|
+
}
|
|
14373
|
+
|
|
14186
14374
|
// src/bin/byok-agent.ts
|
|
14187
14375
|
function usage() {
|
|
14188
14376
|
console.error(
|
|
14189
14377
|
[
|
|
14190
14378
|
"Usage:",
|
|
14379
|
+
" byok-agent --version",
|
|
14191
14380
|
" byok-agent pair <code> --server <url> [--config <path>]",
|
|
14192
14381
|
" byok-agent start [--config <path>] (or BYOK_CONFIG env var)",
|
|
14193
14382
|
" byok-agent status [--config <path>]",
|
|
@@ -14223,6 +14412,10 @@ function abortOnSignal() {
|
|
|
14223
14412
|
}
|
|
14224
14413
|
async function main() {
|
|
14225
14414
|
const [, , command, ...rest] = process.argv;
|
|
14415
|
+
if (command === "--version") {
|
|
14416
|
+
runVersionCommand();
|
|
14417
|
+
return;
|
|
14418
|
+
}
|
|
14226
14419
|
if (command === "pair") {
|
|
14227
14420
|
const [code] = positionalArgs(rest, ["--server", "--config"]);
|
|
14228
14421
|
const server = argValue(rest, "--server");
|