@p4code/cli 0.6.2 → 0.6.3
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/dist/bin.mjs +1965 -884
- package/dist/client/assets/{DiffPanel-Dr0Q8nc0.js → DiffPanel-Dz2KC-p6.js} +3 -3
- package/dist/client/assets/{FilePreviewPanel-DF3Qm8mq.js → FilePreviewPanel-D3Hb6YHz.js} +14 -14
- package/dist/client/assets/PreviewPanel-CHM6nHtt.js +2 -0
- package/dist/client/assets/{PullRequestCodeTab-CmXZgH9P.js → PullRequestCodeTab-BPiTYW6L.js} +3 -3
- package/dist/client/assets/{fileCommentAnnotations-C24vUPt7.js → fileCommentAnnotations-bier3V4K.js} +2 -2
- package/dist/client/assets/index-BMR_XqN8.js +2977 -0
- package/dist/client/assets/{index-B_qrDEeL.css → index-mpULC1Of.css} +1 -1
- package/dist/client/assets/previewAssetResource-oaiSyQyE.js +48 -0
- package/dist/client/assets/renderFileChildren-CVP7M3Sh.js +2 -0
- package/dist/client/assets/toggle-group-CUemgizn.js +2 -0
- package/dist/client/index.html +3 -3
- package/package.json +1 -1
- package/dist/client/assets/PreviewPanel-fURDlBeN.js +0 -2
- package/dist/client/assets/index-Bu7C-0KP.js +0 -2968
- package/dist/client/assets/previewAssetResource-CYZbubRz.js +0 -48
- package/dist/client/assets/renderFileChildren-CSWS4Las.js +0 -2
- package/dist/client/assets/toggle-group-DOifOCJp.js +0 -2
package/dist/bin.mjs
CHANGED
|
@@ -29,15 +29,15 @@ import * as Duration from "effect/Duration";
|
|
|
29
29
|
import * as Option from "effect/Option";
|
|
30
30
|
import * as SchemaTransformation from "effect/SchemaTransformation";
|
|
31
31
|
import * as HttpApiSchema from "effect/unstable/httpapi/HttpApiSchema";
|
|
32
|
-
import * as HttpApi from "effect/unstable/httpapi/HttpApi";
|
|
33
|
-
import * as HttpApiEndpoint from "effect/unstable/httpapi/HttpApiEndpoint";
|
|
34
|
-
import * as HttpApiGroup from "effect/unstable/httpapi/HttpApiGroup";
|
|
35
|
-
import * as HttpApiMiddleware from "effect/unstable/httpapi/HttpApiMiddleware";
|
|
36
32
|
import * as HttpServerRespondable$1 from "effect/unstable/http/HttpServerRespondable";
|
|
37
33
|
import * as HttpServerResponse$1 from "effect/unstable/http/HttpServerResponse";
|
|
38
34
|
import * as SchemaIssue from "effect/SchemaIssue";
|
|
39
35
|
import * as SchemaGetter from "effect/SchemaGetter";
|
|
40
36
|
import * as Struct from "effect/Struct";
|
|
37
|
+
import * as HttpApi from "effect/unstable/httpapi/HttpApi";
|
|
38
|
+
import * as HttpApiEndpoint from "effect/unstable/httpapi/HttpApiEndpoint";
|
|
39
|
+
import * as HttpApiGroup from "effect/unstable/httpapi/HttpApiGroup";
|
|
40
|
+
import * as HttpApiMiddleware from "effect/unstable/httpapi/HttpApiMiddleware";
|
|
41
41
|
import { Schema } from "effect";
|
|
42
42
|
import * as Rpc from "effect/unstable/rpc/Rpc";
|
|
43
43
|
import * as RpcGroup from "effect/unstable/rpc/RpcGroup";
|
|
@@ -263,10 +263,10 @@ const make$122 = () => {
|
|
|
263
263
|
})
|
|
264
264
|
};
|
|
265
265
|
};
|
|
266
|
-
const layer$
|
|
266
|
+
const layer$123 = Layer.sync(NetService, make$122);
|
|
267
267
|
//#endregion
|
|
268
268
|
//#region package.json
|
|
269
|
-
var version$1 = "0.6.
|
|
269
|
+
var version$1 = "0.6.3";
|
|
270
270
|
//#endregion
|
|
271
271
|
//#region src/config.ts
|
|
272
272
|
/**
|
|
@@ -288,7 +288,7 @@ var ServerConfig$1 = class extends Context.Service()("@p4code/cli/config/ServerC
|
|
|
288
288
|
static layerTest = (cwd, baseDirOrPrefix) => layerTest$4(cwd, baseDirOrPrefix);
|
|
289
289
|
};
|
|
290
290
|
const make$121 = (config) => ServerConfig$1.of(config);
|
|
291
|
-
const layer$
|
|
291
|
+
const layer$122 = (config) => Layer.succeed(ServerConfig$1, make$121(config));
|
|
292
292
|
const deriveServerPaths = Effect.fn(function* (baseDir, devUrl, options = {}) {
|
|
293
293
|
const { join } = yield* Path$1.Path;
|
|
294
294
|
const stateDir = join(baseDir, devUrl !== void 0 && !options.baseDirIsExplicit ? "dev" : "userdata");
|
|
@@ -1377,276 +1377,6 @@ Schema$1.Struct({
|
|
|
1377
1377
|
expiresAt: Schema$1.Number
|
|
1378
1378
|
});
|
|
1379
1379
|
//#endregion
|
|
1380
|
-
//#region ../../packages/contracts/src/hubNotifications.ts
|
|
1381
|
-
const MAX_NOTIFICATION_ID_CHARS = 128;
|
|
1382
|
-
const MAX_PUSH_TOKEN_CHARS = 4096;
|
|
1383
|
-
const MAX_HUB_ORIGIN_CHARS = 2048;
|
|
1384
|
-
const MAX_ACTIVITY_TITLE_CHARS = 160;
|
|
1385
|
-
const MAX_ACTIVITY_DETAIL_CHARS = 240;
|
|
1386
|
-
const identifier = Schema$1.String.check(Schema$1.isMinLength(1), Schema$1.isMaxLength(MAX_NOTIFICATION_ID_CHARS), Schema$1.isPattern(/^[A-Za-z0-9][A-Za-z0-9._-]*$(?![\s\S])/));
|
|
1387
|
-
const pushToken = Schema$1.String.check(Schema$1.isMinLength(1), Schema$1.isMaxLength(MAX_PUSH_TOKEN_CHARS), Schema$1.isPattern(/^[\x21-\x7e]+$(?![\s\S])/));
|
|
1388
|
-
/** Server emission is bound to the authenticated environment, never a supplied account. */
|
|
1389
|
-
const HubAgentActivityInput = Schema$1.Struct({
|
|
1390
|
-
eventId: identifier,
|
|
1391
|
-
threadId: ThreadId,
|
|
1392
|
-
revision: Schema$1.Int.check(Schema$1.isGreaterThanOrEqualTo(0)),
|
|
1393
|
-
state: Schema$1.Literals([
|
|
1394
|
-
"running",
|
|
1395
|
-
"approval_required",
|
|
1396
|
-
"question_required",
|
|
1397
|
-
"completed",
|
|
1398
|
-
"failed",
|
|
1399
|
-
"stopped"
|
|
1400
|
-
]),
|
|
1401
|
-
title: Schema$1.String.check(Schema$1.isMinLength(1), Schema$1.isMaxLength(MAX_ACTIVITY_TITLE_CHARS)),
|
|
1402
|
-
detail: Schema$1.String.check(Schema$1.isMaxLength(MAX_ACTIVITY_DETAIL_CHARS))
|
|
1403
|
-
});
|
|
1404
|
-
/** Each authenticated client owns its registration; tokens never belong in public client state. */
|
|
1405
|
-
const HubPushRegistrationInput = Schema$1.Union([Schema$1.Struct({
|
|
1406
|
-
platform: Schema$1.Literal("ios"),
|
|
1407
|
-
token: pushToken,
|
|
1408
|
-
sandbox: Schema$1.Boolean
|
|
1409
|
-
}), Schema$1.Struct({
|
|
1410
|
-
platform: Schema$1.Literal("android"),
|
|
1411
|
-
token: pushToken
|
|
1412
|
-
})]);
|
|
1413
|
-
/** Explicit opt-in, independently reversible. Omission never implies permission. */
|
|
1414
|
-
const HubNotificationPreferences = Schema$1.Struct({
|
|
1415
|
-
attention: Schema$1.Boolean,
|
|
1416
|
-
completion: Schema$1.Boolean,
|
|
1417
|
-
liveActivity: Schema$1.Boolean
|
|
1418
|
-
});
|
|
1419
|
-
Schema$1.Struct({
|
|
1420
|
-
registered: Schema$1.Boolean,
|
|
1421
|
-
registrationGeneration: Schema$1.NullOr(identifier),
|
|
1422
|
-
preferences: HubNotificationPreferences
|
|
1423
|
-
});
|
|
1424
|
-
/** ActivityKit update token, distinct from the app's APNs registration token. */
|
|
1425
|
-
const HubActivityOwnership = Schema$1.Struct({
|
|
1426
|
-
origin: Schema$1.String.check(Schema$1.isMinLength(1), Schema$1.isMaxLength(MAX_HUB_ORIGIN_CHARS)),
|
|
1427
|
-
environmentId: EnvironmentId,
|
|
1428
|
-
clientId: HubClientId,
|
|
1429
|
-
registrationGeneration: identifier,
|
|
1430
|
-
threadId: ThreadId,
|
|
1431
|
-
activityId: identifier
|
|
1432
|
-
});
|
|
1433
|
-
const HubLiveActivityRegistrationInput = Schema$1.Struct({
|
|
1434
|
-
threadId: ThreadId,
|
|
1435
|
-
registrationGeneration: identifier,
|
|
1436
|
-
activityId: identifier,
|
|
1437
|
-
token: Schema$1.String.check(Schema$1.isMinLength(1), Schema$1.isMaxLength(MAX_PUSH_TOKEN_CHARS), Schema$1.isPattern(/^(?:[a-fA-F0-9]{2})+$(?![\s\S])/)),
|
|
1438
|
-
sandbox: Schema$1.Boolean,
|
|
1439
|
-
p4Ownership: Schema$1.optional(HubActivityOwnership)
|
|
1440
|
-
});
|
|
1441
|
-
/** Close only this presentation; delayed requests must not retire a newer activity. */
|
|
1442
|
-
const HubLiveActivityRemoveInput = Schema$1.Struct({
|
|
1443
|
-
threadId: ThreadId,
|
|
1444
|
-
registrationGeneration: identifier,
|
|
1445
|
-
activityId: identifier
|
|
1446
|
-
});
|
|
1447
|
-
const HUB_TUNNEL_WEBSOCKET_PROTOCOL = "p4-hub-tunnel-v1";
|
|
1448
|
-
const HUB_TUNNEL_INITIAL_WINDOW_BYTES = 256 * 1024;
|
|
1449
|
-
const HUB_TUNNEL_MAX_WINDOW_BYTES = 1024 * 1024;
|
|
1450
|
-
const MAX_HEADERS = 64;
|
|
1451
|
-
const MAX_HEADER_NAME_CHARS = 128;
|
|
1452
|
-
const MAX_HEADER_VALUE_CHARS = 8192;
|
|
1453
|
-
const MAX_PATH_CHARS = 8192;
|
|
1454
|
-
const MAX_PROTOCOLS = 16;
|
|
1455
|
-
const MAX_PROTOCOL_CHARS = 128;
|
|
1456
|
-
const HeaderName = Schema$1.String.check(Schema$1.isMinLength(1), Schema$1.isMaxLength(MAX_HEADER_NAME_CHARS), Schema$1.isPattern(/^[!#$%&'*+.^_`|~0-9A-Za-z-]+$(?![\s\S])/));
|
|
1457
|
-
const HeaderValue = Schema$1.String.check(Schema$1.isMaxLength(MAX_HEADER_VALUE_CHARS), Schema$1.isPattern(/^[\t\x20-\x7e]*$(?![\s\S])/));
|
|
1458
|
-
const Headers$2 = Schema$1.Array(Schema$1.Tuple([HeaderName, HeaderValue])).check(Schema$1.isMaxLength(MAX_HEADERS));
|
|
1459
|
-
const Path = Schema$1.String.check(Schema$1.isMinLength(1), Schema$1.isMaxLength(MAX_PATH_CHARS), Schema$1.isPattern(/^\/(?!\/)[^\s\\#\x00-\x1f\x7f]*$(?![\s\S])/));
|
|
1460
|
-
const Protocol = Schema$1.String.check(Schema$1.isMinLength(1), Schema$1.isMaxLength(MAX_PROTOCOL_CHARS), Schema$1.isPattern(/^[!#$%&'*+.^_`|~0-9A-Za-z-]+$(?![\s\S])/));
|
|
1461
|
-
const FailureReason = Schema$1.Literals([
|
|
1462
|
-
"cancelled",
|
|
1463
|
-
"unavailable",
|
|
1464
|
-
"protocol_error"
|
|
1465
|
-
]);
|
|
1466
|
-
const CloseCode = Schema$1.Int.check(Schema$1.makeFilter((code) => code >= 1e3 && code <= 1014 && code !== 1004 && code !== 1005 && code !== 1006 || code >= 3e3 && code <= 4999));
|
|
1467
|
-
/** No caller-supplied account, environment, origin, or connector ownership authority. */
|
|
1468
|
-
const HubTunnelControl = Schema$1.Union([
|
|
1469
|
-
Schema$1.Struct({
|
|
1470
|
-
type: Schema$1.Literal("connector_ready"),
|
|
1471
|
-
protocolVersion: Schema$1.Literal(1)
|
|
1472
|
-
}),
|
|
1473
|
-
Schema$1.Struct({
|
|
1474
|
-
type: Schema$1.Literal("http_request_start"),
|
|
1475
|
-
method: Schema$1.Literals([
|
|
1476
|
-
"GET",
|
|
1477
|
-
"HEAD",
|
|
1478
|
-
"POST",
|
|
1479
|
-
"PUT",
|
|
1480
|
-
"PATCH",
|
|
1481
|
-
"DELETE",
|
|
1482
|
-
"OPTIONS"
|
|
1483
|
-
]),
|
|
1484
|
-
path: Path,
|
|
1485
|
-
headers: Headers$2
|
|
1486
|
-
}),
|
|
1487
|
-
Schema$1.Struct({ type: Schema$1.Literal("http_request_end") }),
|
|
1488
|
-
Schema$1.Struct({
|
|
1489
|
-
type: Schema$1.Literal("http_request_abort"),
|
|
1490
|
-
reason: FailureReason
|
|
1491
|
-
}),
|
|
1492
|
-
Schema$1.Struct({
|
|
1493
|
-
type: Schema$1.Literal("http_response_start"),
|
|
1494
|
-
status: Schema$1.Int.check(Schema$1.isGreaterThanOrEqualTo(200), Schema$1.isLessThanOrEqualTo(599)),
|
|
1495
|
-
headers: Headers$2
|
|
1496
|
-
}),
|
|
1497
|
-
Schema$1.Struct({ type: Schema$1.Literal("http_response_end") }),
|
|
1498
|
-
Schema$1.Struct({
|
|
1499
|
-
type: Schema$1.Literal("http_response_abort"),
|
|
1500
|
-
reason: FailureReason
|
|
1501
|
-
}),
|
|
1502
|
-
Schema$1.Struct({
|
|
1503
|
-
type: Schema$1.Literal("websocket_open"),
|
|
1504
|
-
path: Path,
|
|
1505
|
-
headers: Headers$2,
|
|
1506
|
-
protocols: Schema$1.Array(Protocol).check(Schema$1.isMaxLength(MAX_PROTOCOLS))
|
|
1507
|
-
}),
|
|
1508
|
-
Schema$1.Struct({
|
|
1509
|
-
type: Schema$1.Literal("websocket_accept"),
|
|
1510
|
-
protocol: Schema$1.optional(Protocol)
|
|
1511
|
-
}),
|
|
1512
|
-
Schema$1.Struct({
|
|
1513
|
-
type: Schema$1.Literal("websocket_reject"),
|
|
1514
|
-
status: Schema$1.Int.check(Schema$1.isGreaterThanOrEqualTo(400), Schema$1.isLessThanOrEqualTo(599)),
|
|
1515
|
-
reason: FailureReason
|
|
1516
|
-
}),
|
|
1517
|
-
Schema$1.Struct({
|
|
1518
|
-
type: Schema$1.Literal("websocket_close"),
|
|
1519
|
-
code: CloseCode,
|
|
1520
|
-
reason: FailureReason
|
|
1521
|
-
}),
|
|
1522
|
-
Schema$1.Struct({
|
|
1523
|
-
type: Schema$1.Literal("window_update"),
|
|
1524
|
-
creditBytes: Schema$1.Int.check(Schema$1.isGreaterThan(0), Schema$1.isLessThanOrEqualTo(HUB_TUNNEL_MAX_WINDOW_BYTES))
|
|
1525
|
-
})
|
|
1526
|
-
]);
|
|
1527
|
-
//#endregion
|
|
1528
|
-
//#region ../../packages/contracts/src/environment.ts
|
|
1529
|
-
const ExecutionEnvironmentPlatformOs = Schema$1.Literals([
|
|
1530
|
-
"darwin",
|
|
1531
|
-
"linux",
|
|
1532
|
-
"windows",
|
|
1533
|
-
"unknown"
|
|
1534
|
-
]);
|
|
1535
|
-
const ExecutionEnvironmentPlatformArch = Schema$1.Literals([
|
|
1536
|
-
"arm64",
|
|
1537
|
-
"x64",
|
|
1538
|
-
"other"
|
|
1539
|
-
]);
|
|
1540
|
-
const ExecutionEnvironmentPlatform = Schema$1.Struct({
|
|
1541
|
-
os: ExecutionEnvironmentPlatformOs,
|
|
1542
|
-
arch: ExecutionEnvironmentPlatformArch
|
|
1543
|
-
});
|
|
1544
|
-
/** How a server can replace itself with another version when asked over RPC:
|
|
1545
|
-
"boot-service" rewrites the systemd user unit and restarts it; "respawn"
|
|
1546
|
-
installs the target version and respawns the foreground process. */
|
|
1547
|
-
const ServerSelfUpdateMethod = Schema$1.Literals(["boot-service", "respawn"]);
|
|
1548
|
-
/** What update path a client should offer for a server: one of the RPC
|
|
1549
|
-
self-update methods above, or "desktop-managed" when the backend's
|
|
1550
|
-
version belongs to the P4Code desktop app supervising it — updating the
|
|
1551
|
-
app on that machine is the only way to update the server. */
|
|
1552
|
-
const ServerSelfUpdateCapability = Schema$1.Literals([
|
|
1553
|
-
"boot-service",
|
|
1554
|
-
"respawn",
|
|
1555
|
-
"desktop-managed"
|
|
1556
|
-
]);
|
|
1557
|
-
const ExecutionEnvironmentCapabilities = Schema$1.Struct({
|
|
1558
|
-
computerUseControls: Schema$1.optionalKey(Schema$1.Boolean),
|
|
1559
|
-
usagePriceOverrides: Schema$1.optionalKey(Schema$1.Boolean),
|
|
1560
|
-
projectAutoPull: Schema$1.optionalKey(Schema$1.Boolean),
|
|
1561
|
-
sharedProjectModelDefault: Schema$1.optionalKey(Schema$1.Boolean),
|
|
1562
|
-
projectScriptDefaults: Schema$1.optionalKey(Schema$1.Boolean),
|
|
1563
|
-
customModelDefinitions: Schema$1.optionalKey(Schema$1.Boolean),
|
|
1564
|
-
threadLifecycleV2: Schema$1.optionalKey(Schema$1.Boolean),
|
|
1565
|
-
repositoryIdentity: Schema$1.Boolean.pipe(Schema$1.withDecodingDefault(Effect.succeed(false))),
|
|
1566
|
-
connectionProbe: Schema$1.optionalKey(Schema$1.Boolean),
|
|
1567
|
-
/** Server understands thread.settle / thread.unsettle commands. Absent on
|
|
1568
|
-
pre-settlement servers, so clients treat missing as unsupported and
|
|
1569
|
-
never send the commands under version skew. */
|
|
1570
|
-
threadSettlement: Schema$1.optionalKey(Schema$1.Boolean),
|
|
1571
|
-
/** Server understands thread.snooze / thread.unsnooze commands. Same
|
|
1572
|
-
version-skew contract as threadSettlement. */
|
|
1573
|
-
threadSnooze: Schema$1.optionalKey(Schema$1.Boolean),
|
|
1574
|
-
/** Server understands thread.pin / thread.unpin / thread.pin.reorder
|
|
1575
|
-
commands. Same version-skew contract as threadSettlement. */
|
|
1576
|
-
threadPinning: Schema$1.optionalKey(Schema$1.Boolean),
|
|
1577
|
-
/** Server understands the thread.fork command. Same version-skew contract
|
|
1578
|
-
as threadSettlement. */
|
|
1579
|
-
threadFork: Schema$1.optionalKey(Schema$1.Boolean),
|
|
1580
|
-
/** Server understands thread.scheduled-task.create / cancel and fires
|
|
1581
|
-
pending tasks. Same version-skew contract as threadSettlement. */
|
|
1582
|
-
threadScheduledTasks: Schema$1.optionalKey(Schema$1.Boolean),
|
|
1583
|
-
/** Server exposes the pull-request list, detail, activity, diff, and mutation APIs. Absent on
|
|
1584
|
-
servers from before the pull-request workspace shipped, so clients must not probe them. */
|
|
1585
|
-
pullRequests: Schema$1.optionalKey(Schema$1.Boolean),
|
|
1586
|
-
/** Whole-host capacity snapshots for automatic draft routing. */
|
|
1587
|
-
hostResources: Schema$1.optionalKey(Schema$1.Boolean),
|
|
1588
|
-
/** Device open returns lease IDs and close honors expectedLeaseId. */
|
|
1589
|
-
deviceSessionLeases: Schema$1.optionalKey(Schema$1.Boolean),
|
|
1590
|
-
/** Manual detail/action requests can require an exact viewing lifetime. */
|
|
1591
|
-
deviceActionLeases: Schema$1.optionalKey(Schema$1.Boolean),
|
|
1592
|
-
/** Stack-scoped mutations preserve expected heads. Require true before sending stack payloads;
|
|
1593
|
-
older servers may otherwise interpret the request as a single-PR action. */
|
|
1594
|
-
pullRequestStackActions: Schema$1.optionalKey(Schema$1.Boolean),
|
|
1595
|
-
/** The update path clients should offer for this server. Absent on
|
|
1596
|
-
servers that must be relaunched manually (dev checkouts, Windows
|
|
1597
|
-
foreground runs, pre-update servers). */
|
|
1598
|
-
serverSelfUpdate: Schema$1.optionalKey(ServerSelfUpdateCapability),
|
|
1599
|
-
/**
|
|
1600
|
-
* Set when a service supervises the server, so a client offering a manual
|
|
1601
|
-
* update must say `service update` rather than a bare relaunch — which would
|
|
1602
|
-
* start a second server beside the supervised one.
|
|
1603
|
-
*
|
|
1604
|
-
* A new optional field rather than a new `ServerSelfUpdateCapability` member:
|
|
1605
|
-
* adding a literal to that union makes older clients fail to decode the whole
|
|
1606
|
-
* descriptor, while an unknown key is ignored.
|
|
1607
|
-
*/
|
|
1608
|
-
serverServiceSupervised: Schema$1.optionalKey(Schema$1.Boolean)
|
|
1609
|
-
});
|
|
1610
|
-
const ExecutionEnvironmentDescriptor = Schema$1.Struct({
|
|
1611
|
-
environmentId: EnvironmentId,
|
|
1612
|
-
label: TrimmedNonEmptyString,
|
|
1613
|
-
platform: ExecutionEnvironmentPlatform,
|
|
1614
|
-
serverVersion: TrimmedNonEmptyString,
|
|
1615
|
-
capabilities: ExecutionEnvironmentCapabilities
|
|
1616
|
-
});
|
|
1617
|
-
Schema$1.Literals([
|
|
1618
|
-
"connecting",
|
|
1619
|
-
"connected",
|
|
1620
|
-
"disconnected",
|
|
1621
|
-
"error"
|
|
1622
|
-
]);
|
|
1623
|
-
const RepositoryIdentityLocator = Schema$1.Struct({
|
|
1624
|
-
source: Schema$1.Literal("git-remote"),
|
|
1625
|
-
remoteName: TrimmedNonEmptyString,
|
|
1626
|
-
remoteUrl: TrimmedNonEmptyString
|
|
1627
|
-
});
|
|
1628
|
-
const RepositoryIdentity = Schema$1.Struct({
|
|
1629
|
-
canonicalKey: TrimmedNonEmptyString,
|
|
1630
|
-
locator: RepositoryIdentityLocator,
|
|
1631
|
-
rootPath: Schema$1.optionalKey(TrimmedNonEmptyString),
|
|
1632
|
-
displayName: Schema$1.optionalKey(TrimmedNonEmptyString),
|
|
1633
|
-
provider: Schema$1.optionalKey(TrimmedNonEmptyString),
|
|
1634
|
-
owner: Schema$1.optionalKey(TrimmedNonEmptyString),
|
|
1635
|
-
name: Schema$1.optionalKey(TrimmedNonEmptyString)
|
|
1636
|
-
});
|
|
1637
|
-
Schema$1.Struct({
|
|
1638
|
-
environmentId: EnvironmentId,
|
|
1639
|
-
projectId: ProjectId
|
|
1640
|
-
});
|
|
1641
|
-
Schema$1.Struct({
|
|
1642
|
-
environmentId: EnvironmentId,
|
|
1643
|
-
threadId: ThreadId
|
|
1644
|
-
});
|
|
1645
|
-
Schema$1.Struct({
|
|
1646
|
-
environmentId: EnvironmentId,
|
|
1647
|
-
threadId: ThreadId
|
|
1648
|
-
});
|
|
1649
|
-
//#endregion
|
|
1650
1380
|
//#region ../../packages/contracts/src/vcs.ts
|
|
1651
1381
|
const VcsDriverKind = Schema$1.Literals([
|
|
1652
1382
|
"git",
|
|
@@ -3215,6 +2945,128 @@ const MODEL_SLUG_ALIASES_BY_PROVIDER = {
|
|
|
3215
2945
|
[OPENCODE_DRIVER_KIND]: {}
|
|
3216
2946
|
};
|
|
3217
2947
|
//#endregion
|
|
2948
|
+
//#region ../../packages/contracts/src/environment.ts
|
|
2949
|
+
const ExecutionEnvironmentPlatformOs = Schema$1.Literals([
|
|
2950
|
+
"darwin",
|
|
2951
|
+
"linux",
|
|
2952
|
+
"windows",
|
|
2953
|
+
"unknown"
|
|
2954
|
+
]);
|
|
2955
|
+
const ExecutionEnvironmentPlatformArch = Schema$1.Literals([
|
|
2956
|
+
"arm64",
|
|
2957
|
+
"x64",
|
|
2958
|
+
"other"
|
|
2959
|
+
]);
|
|
2960
|
+
const ExecutionEnvironmentPlatform = Schema$1.Struct({
|
|
2961
|
+
os: ExecutionEnvironmentPlatformOs,
|
|
2962
|
+
arch: ExecutionEnvironmentPlatformArch
|
|
2963
|
+
});
|
|
2964
|
+
/** How a server can replace itself with another version when asked over RPC:
|
|
2965
|
+
"boot-service" rewrites the systemd user unit and restarts it; "respawn"
|
|
2966
|
+
installs the target version and respawns the foreground process. */
|
|
2967
|
+
const ServerSelfUpdateMethod = Schema$1.Literals(["boot-service", "respawn"]);
|
|
2968
|
+
/** What update path a client should offer for a server: one of the RPC
|
|
2969
|
+
self-update methods above, or "desktop-managed" when the backend's
|
|
2970
|
+
version belongs to the P4Code desktop app supervising it — updating the
|
|
2971
|
+
app on that machine is the only way to update the server. */
|
|
2972
|
+
const ServerSelfUpdateCapability = Schema$1.Literals([
|
|
2973
|
+
"boot-service",
|
|
2974
|
+
"respawn",
|
|
2975
|
+
"desktop-managed"
|
|
2976
|
+
]);
|
|
2977
|
+
const ExecutionEnvironmentCapabilities = Schema$1.Struct({
|
|
2978
|
+
computerUseControls: Schema$1.optionalKey(Schema$1.Boolean),
|
|
2979
|
+
usagePriceOverrides: Schema$1.optionalKey(Schema$1.Boolean),
|
|
2980
|
+
projectAutoPull: Schema$1.optionalKey(Schema$1.Boolean),
|
|
2981
|
+
sharedProjectModelDefault: Schema$1.optionalKey(Schema$1.Boolean),
|
|
2982
|
+
projectScriptDefaults: Schema$1.optionalKey(Schema$1.Boolean),
|
|
2983
|
+
customModelDefinitions: Schema$1.optionalKey(Schema$1.Boolean),
|
|
2984
|
+
threadLifecycleV2: Schema$1.optionalKey(Schema$1.Boolean),
|
|
2985
|
+
repositoryIdentity: Schema$1.Boolean.pipe(Schema$1.withDecodingDefault(Effect.succeed(false))),
|
|
2986
|
+
connectionProbe: Schema$1.optionalKey(Schema$1.Boolean),
|
|
2987
|
+
/** Server understands thread.settle / thread.unsettle commands. Absent on
|
|
2988
|
+
pre-settlement servers, so clients treat missing as unsupported and
|
|
2989
|
+
never send the commands under version skew. */
|
|
2990
|
+
threadSettlement: Schema$1.optionalKey(Schema$1.Boolean),
|
|
2991
|
+
/** Server understands thread.snooze / thread.unsnooze commands. Same
|
|
2992
|
+
version-skew contract as threadSettlement. */
|
|
2993
|
+
threadSnooze: Schema$1.optionalKey(Schema$1.Boolean),
|
|
2994
|
+
/** Server understands thread.pin / thread.unpin / thread.pin.reorder
|
|
2995
|
+
commands. Same version-skew contract as threadSettlement. */
|
|
2996
|
+
threadPinning: Schema$1.optionalKey(Schema$1.Boolean),
|
|
2997
|
+
/** Server understands the thread.fork command. Same version-skew contract
|
|
2998
|
+
as threadSettlement. */
|
|
2999
|
+
threadFork: Schema$1.optionalKey(Schema$1.Boolean),
|
|
3000
|
+
/** Server understands thread.scheduled-task.create / cancel and fires
|
|
3001
|
+
pending tasks. Same version-skew contract as threadSettlement. */
|
|
3002
|
+
threadScheduledTasks: Schema$1.optionalKey(Schema$1.Boolean),
|
|
3003
|
+
/** Server exposes the pull-request list, detail, activity, diff, and mutation APIs. Absent on
|
|
3004
|
+
servers from before the pull-request workspace shipped, so clients must not probe them. */
|
|
3005
|
+
pullRequests: Schema$1.optionalKey(Schema$1.Boolean),
|
|
3006
|
+
/** Whole-host capacity snapshots for automatic draft routing. */
|
|
3007
|
+
hostResources: Schema$1.optionalKey(Schema$1.Boolean),
|
|
3008
|
+
/** Device open returns lease IDs and close honors expectedLeaseId. */
|
|
3009
|
+
deviceSessionLeases: Schema$1.optionalKey(Schema$1.Boolean),
|
|
3010
|
+
/** Manual detail/action requests can require an exact viewing lifetime. */
|
|
3011
|
+
deviceActionLeases: Schema$1.optionalKey(Schema$1.Boolean),
|
|
3012
|
+
/** Stack-scoped mutations preserve expected heads. Require true before sending stack payloads;
|
|
3013
|
+
older servers may otherwise interpret the request as a single-PR action. */
|
|
3014
|
+
pullRequestStackActions: Schema$1.optionalKey(Schema$1.Boolean),
|
|
3015
|
+
/** The update path clients should offer for this server. Absent on
|
|
3016
|
+
servers that must be relaunched manually (dev checkouts, Windows
|
|
3017
|
+
foreground runs, pre-update servers). */
|
|
3018
|
+
serverSelfUpdate: Schema$1.optionalKey(ServerSelfUpdateCapability),
|
|
3019
|
+
/**
|
|
3020
|
+
* Set when a service supervises the server, so a client offering a manual
|
|
3021
|
+
* update must say `service update` rather than a bare relaunch — which would
|
|
3022
|
+
* start a second server beside the supervised one.
|
|
3023
|
+
*
|
|
3024
|
+
* A new optional field rather than a new `ServerSelfUpdateCapability` member:
|
|
3025
|
+
* adding a literal to that union makes older clients fail to decode the whole
|
|
3026
|
+
* descriptor, while an unknown key is ignored.
|
|
3027
|
+
*/
|
|
3028
|
+
serverServiceSupervised: Schema$1.optionalKey(Schema$1.Boolean)
|
|
3029
|
+
});
|
|
3030
|
+
const ExecutionEnvironmentDescriptor = Schema$1.Struct({
|
|
3031
|
+
environmentId: EnvironmentId,
|
|
3032
|
+
label: TrimmedNonEmptyString,
|
|
3033
|
+
platform: ExecutionEnvironmentPlatform,
|
|
3034
|
+
serverVersion: TrimmedNonEmptyString,
|
|
3035
|
+
capabilities: ExecutionEnvironmentCapabilities
|
|
3036
|
+
});
|
|
3037
|
+
Schema$1.Literals([
|
|
3038
|
+
"connecting",
|
|
3039
|
+
"connected",
|
|
3040
|
+
"disconnected",
|
|
3041
|
+
"error"
|
|
3042
|
+
]);
|
|
3043
|
+
const RepositoryIdentityLocator = Schema$1.Struct({
|
|
3044
|
+
source: Schema$1.Literal("git-remote"),
|
|
3045
|
+
remoteName: TrimmedNonEmptyString,
|
|
3046
|
+
remoteUrl: TrimmedNonEmptyString
|
|
3047
|
+
});
|
|
3048
|
+
const RepositoryIdentity = Schema$1.Struct({
|
|
3049
|
+
canonicalKey: TrimmedNonEmptyString,
|
|
3050
|
+
locator: RepositoryIdentityLocator,
|
|
3051
|
+
rootPath: Schema$1.optionalKey(TrimmedNonEmptyString),
|
|
3052
|
+
displayName: Schema$1.optionalKey(TrimmedNonEmptyString),
|
|
3053
|
+
provider: Schema$1.optionalKey(TrimmedNonEmptyString),
|
|
3054
|
+
owner: Schema$1.optionalKey(TrimmedNonEmptyString),
|
|
3055
|
+
name: Schema$1.optionalKey(TrimmedNonEmptyString)
|
|
3056
|
+
});
|
|
3057
|
+
Schema$1.Struct({
|
|
3058
|
+
environmentId: EnvironmentId,
|
|
3059
|
+
projectId: ProjectId
|
|
3060
|
+
});
|
|
3061
|
+
Schema$1.Struct({
|
|
3062
|
+
environmentId: EnvironmentId,
|
|
3063
|
+
threadId: ThreadId
|
|
3064
|
+
});
|
|
3065
|
+
Schema$1.Struct({
|
|
3066
|
+
environmentId: EnvironmentId,
|
|
3067
|
+
threadId: ThreadId
|
|
3068
|
+
});
|
|
3069
|
+
//#endregion
|
|
3218
3070
|
//#region ../../packages/contracts/src/orchestration.ts
|
|
3219
3071
|
const ORCHESTRATION_WS_METHODS = {
|
|
3220
3072
|
dispatchCommand: "orchestration.dispatchCommand",
|
|
@@ -3296,6 +3148,12 @@ const COMPRESS_MODE_VALUES = [
|
|
|
3296
3148
|
"ultra",
|
|
3297
3149
|
"wenyan"
|
|
3298
3150
|
];
|
|
3151
|
+
/**
|
|
3152
|
+
* The strict mode set, for a reader that must not accept a mode it cannot
|
|
3153
|
+
* name. `CompressMode` below stays tolerant for wire compatibility; anything
|
|
3154
|
+
* storing a mode as a fact uses this one instead, because coercing an unknown
|
|
3155
|
+
* mode to the default would record a compression pass that never happened.
|
|
3156
|
+
*/
|
|
3299
3157
|
const CompressModeLiteral = Schema$1.Literals(COMPRESS_MODE_VALUES);
|
|
3300
3158
|
/**
|
|
3301
3159
|
* Tolerant on the wire: an unknown mode decodes to the default instead of
|
|
@@ -5347,6 +5205,579 @@ var OrchestrationSearchThreadsError = class extends Schema$1.TaggedErrorClass()(
|
|
|
5347
5205
|
message: TrimmedNonEmptyString,
|
|
5348
5206
|
cause: Schema$1.optional(Schema$1.Defect())
|
|
5349
5207
|
}) {};
|
|
5208
|
+
const ThreadWatchEventsInput = Schema$1.Struct({
|
|
5209
|
+
threadId: ThreadId.annotate({ description: "The thread to read events from. Any thread on this server, not only this session's own." }),
|
|
5210
|
+
afterSequence: Schema$1.optional(NonNegativeInt.annotate({ description: "Exclusive sequence cursor. Events with a higher sequence are returned. Omit to read from the beginning." })),
|
|
5211
|
+
limit: Schema$1.optional(NonNegativeInt.check(Schema$1.isLessThanOrEqualTo(200)).annotate({ description: `Maximum events to return (default 50, max 200).` }))
|
|
5212
|
+
});
|
|
5213
|
+
const ThreadWatchEventsResult = Schema$1.Struct({
|
|
5214
|
+
threadId: ThreadId,
|
|
5215
|
+
/** Ordered by sequence ascending; resume with the last event's sequence. */
|
|
5216
|
+
events: Schema$1.Array(OrchestrationEvent),
|
|
5217
|
+
/**
|
|
5218
|
+
* The engine's latest persisted sequence at read time. Equal to the last
|
|
5219
|
+
* returned event's sequence when the watcher is caught up.
|
|
5220
|
+
*/
|
|
5221
|
+
headSequence: NonNegativeInt,
|
|
5222
|
+
/** True when more events for this thread exist past the returned page. */
|
|
5223
|
+
hasMore: Schema$1.Boolean
|
|
5224
|
+
});
|
|
5225
|
+
/** Compact review limits are server-owned, independent of raw event paging. */
|
|
5226
|
+
const THREAD_REVIEW_SCAN_LIMIT = 2e3;
|
|
5227
|
+
const THREAD_REVIEW_ITEM_MAX_BYTES = 8e3;
|
|
5228
|
+
const ThreadWatchReviewInput = Schema$1.Struct({
|
|
5229
|
+
threadId: ThreadId,
|
|
5230
|
+
afterSequence: Schema$1.optional(NonNegativeInt),
|
|
5231
|
+
throughSequence: Schema$1.optional(NonNegativeInt.annotate({ description: "Inclusive fixed review boundary. Omit on the first page to capture the head; reuse the returned boundary on every subsequent page." }))
|
|
5232
|
+
});
|
|
5233
|
+
const ReviewSourceFields = {
|
|
5234
|
+
firstSequence: NonNegativeInt,
|
|
5235
|
+
lastSequence: NonNegativeInt,
|
|
5236
|
+
truncated: Schema$1.Boolean
|
|
5237
|
+
};
|
|
5238
|
+
const ThreadReviewItem = Schema$1.Union([Schema$1.Struct({
|
|
5239
|
+
...ReviewSourceFields,
|
|
5240
|
+
kind: Schema$1.Literal("message"),
|
|
5241
|
+
messageId: Schema$1.String,
|
|
5242
|
+
turnId: Schema$1.NullOr(Schema$1.String),
|
|
5243
|
+
role: Schema$1.String,
|
|
5244
|
+
/** Apply append/replace by message identity across page boundaries. */
|
|
5245
|
+
operation: Schema$1.Literals(["append", "replace"]),
|
|
5246
|
+
complete: Schema$1.Boolean,
|
|
5247
|
+
text: Schema$1.String,
|
|
5248
|
+
/**
|
|
5249
|
+
* What the message carried besides text.
|
|
5250
|
+
*
|
|
5251
|
+
* Present only when there was something: a message whose fragments are
|
|
5252
|
+
* merged by identity would otherwise lose every trace of an image or a
|
|
5253
|
+
* document the builder attached. Bounded by
|
|
5254
|
+
* {@link THREAD_REVIEW_ITEM_MAX_ATTACHMENTS} so the item stays trimmable -
|
|
5255
|
+
* only its text shrinks under the byte cap, so nothing else may be able to
|
|
5256
|
+
* grow without limit.
|
|
5257
|
+
*/
|
|
5258
|
+
attachments: Schema$1.optionalKey(Schema$1.Array(Schema$1.Struct({
|
|
5259
|
+
type: Schema$1.String,
|
|
5260
|
+
name: Schema$1.String,
|
|
5261
|
+
sizeBytes: NonNegativeInt
|
|
5262
|
+
})))
|
|
5263
|
+
}), Schema$1.Struct({
|
|
5264
|
+
...ReviewSourceFields,
|
|
5265
|
+
kind: Schema$1.Literal("event"),
|
|
5266
|
+
type: Schema$1.String,
|
|
5267
|
+
/** JSON evidence excerpt. Truncated excerpts require targeted raw recovery. */
|
|
5268
|
+
payload: Schema$1.String
|
|
5269
|
+
})]);
|
|
5270
|
+
/**
|
|
5271
|
+
* Compressed evidence standing in for the items it replaces.
|
|
5272
|
+
*
|
|
5273
|
+
* The supervisor reads this instead of paging the range event by event. The
|
|
5274
|
+
* sequence bounds are what make that safe: any claim can be verified at source
|
|
5275
|
+
* with a targeted `thread_watch_events` read over exactly this range, so the
|
|
5276
|
+
* summary is a starting point rather than the only record.
|
|
5277
|
+
*
|
|
5278
|
+
* `degraded` is set whenever the items came back unsummarized, and names why.
|
|
5279
|
+
* Most reasons are failures - unset model, provider error, timeout, empty or
|
|
5280
|
+
* unusable output - but `page-under-threshold` is not: it means the page was
|
|
5281
|
+
* small enough that summarizing it would have cost more than reading it. The
|
|
5282
|
+
* supervisor is never left guessing whether it is reading everything.
|
|
5283
|
+
*/
|
|
5284
|
+
const ThreadReviewSummary = Schema$1.Struct({
|
|
5285
|
+
text: Schema$1.String,
|
|
5286
|
+
firstSequence: NonNegativeInt,
|
|
5287
|
+
lastSequence: NonNegativeInt,
|
|
5288
|
+
/** How many builder turns the range covered. */
|
|
5289
|
+
turnCount: NonNegativeInt,
|
|
5290
|
+
degraded: Schema$1.NullOr(Schema$1.String)
|
|
5291
|
+
});
|
|
5292
|
+
const ThreadWatchReviewResult = Schema$1.Struct({
|
|
5293
|
+
threadId: ThreadId,
|
|
5294
|
+
items: Schema$1.Array(ThreadReviewItem),
|
|
5295
|
+
throughSequence: NonNegativeInt,
|
|
5296
|
+
/** Resume from the source scan cursor, even when items is empty. */
|
|
5297
|
+
nextAfterSequence: NonNegativeInt,
|
|
5298
|
+
hasMore: Schema$1.Boolean,
|
|
5299
|
+
scannedEvents: NonNegativeInt,
|
|
5300
|
+
/**
|
|
5301
|
+
* Present only when summarization ran. When its `degraded` is null the
|
|
5302
|
+
* `items` for the summarized range are omitted and this stands in for them;
|
|
5303
|
+
* otherwise the items are intact and this explains why.
|
|
5304
|
+
*/
|
|
5305
|
+
summary: Schema$1.optionalKey(Schema$1.NullOr(ThreadReviewSummary))
|
|
5306
|
+
});
|
|
5307
|
+
/**
|
|
5308
|
+
* How a review ended, from the supervisor's own account of it.
|
|
5309
|
+
*
|
|
5310
|
+
* Three states rather than a boolean because they are acted on differently: a
|
|
5311
|
+
* summary that arrived is evidence, a fallback is a working review that cost
|
|
5312
|
+
* full context, and a failure is a misconfiguration worth fixing. The fourth
|
|
5313
|
+
* state - a read no report ever answered - is not sayable here: only the
|
|
5314
|
+
* server can observe it, through `ThreadEvidenceActivityOutcome`.
|
|
5315
|
+
*/
|
|
5316
|
+
const ThreadEvidenceReportOutcome = Schema$1.Literals([
|
|
5317
|
+
"summarized",
|
|
5318
|
+
"fallback",
|
|
5319
|
+
"failed"
|
|
5320
|
+
]);
|
|
5321
|
+
/** Bounded so one report cannot dominate a thread's activity storage. */
|
|
5322
|
+
const THREAD_EVIDENCE_REPORT_MAX_CHARS = 8e3;
|
|
5323
|
+
const ThreadEvidenceReportInput = Schema$1.Struct({
|
|
5324
|
+
/** The watched thread the summary covers, not the reporting thread. */
|
|
5325
|
+
threadId: ThreadId,
|
|
5326
|
+
firstSequence: NonNegativeInt,
|
|
5327
|
+
lastSequence: NonNegativeInt,
|
|
5328
|
+
/**
|
|
5329
|
+
* The fixed review boundary the pages were read against.
|
|
5330
|
+
*
|
|
5331
|
+
* Identity, not description: the evidence-read rows are keyed on it, so a
|
|
5332
|
+
* report that guessed from its own last sequence would look for a baseline
|
|
5333
|
+
* that never existed whenever the range ended on a filtered event.
|
|
5334
|
+
*/
|
|
5335
|
+
throughSequence: NonNegativeInt,
|
|
5336
|
+
outcome: ThreadEvidenceReportOutcome,
|
|
5337
|
+
/** The summarizer's exact text. Required when the outcome is summarized. */
|
|
5338
|
+
summary: Schema$1.optional(Schema$1.String),
|
|
5339
|
+
/** The model the subagent ran on, when one was spawned. */
|
|
5340
|
+
model: Schema$1.optional(Schema$1.String),
|
|
5341
|
+
/** Why the summary is missing or degraded. Required unless summarized. */
|
|
5342
|
+
reason: Schema$1.optional(Schema$1.String)
|
|
5343
|
+
});
|
|
5344
|
+
/**
|
|
5345
|
+
* The supervisor's own context around the read, correlated by the server.
|
|
5346
|
+
*
|
|
5347
|
+
* Self-reported numbers would be worth little, so these are read from the
|
|
5348
|
+
* reporting thread's projected context-window rows: `before` is what was
|
|
5349
|
+
* recorded when the evidence read happened, `after` what is recorded when the
|
|
5350
|
+
* report lands. `delta` can be negative when a compaction ran in between, and
|
|
5351
|
+
* is null whenever no read-time baseline exists.
|
|
5352
|
+
*/
|
|
5353
|
+
const ThreadEvidenceReportContext = Schema$1.Struct({
|
|
5354
|
+
/**
|
|
5355
|
+
* Null when no reading was recorded at read time.
|
|
5356
|
+
*
|
|
5357
|
+
* Never the report-time value standing in for itself: a delta of zero read
|
|
5358
|
+
* as measurement would be a lie about a review that cost context.
|
|
5359
|
+
*/
|
|
5360
|
+
usedTokensBefore: Schema$1.NullOr(NonNegativeInt),
|
|
5361
|
+
usedTokensAfter: NonNegativeInt,
|
|
5362
|
+
/** Null exactly when `usedTokensBefore` is. */
|
|
5363
|
+
deltaTokens: Schema$1.NullOr(Schema$1.Number),
|
|
5364
|
+
maxTokens: Schema$1.NullOr(NonNegativeInt),
|
|
5365
|
+
capacityPercent: Schema$1.NullOr(Schema$1.Number)
|
|
5366
|
+
});
|
|
5367
|
+
/**
|
|
5368
|
+
* How the recorded row ended, which is the supervisor's three outcomes plus the
|
|
5369
|
+
* one only the server can state: the review closed with no report at all.
|
|
5370
|
+
*
|
|
5371
|
+
* `unreported` is never accepted from a tool call. The server writes it when a
|
|
5372
|
+
* supervisor turn ends over an evidence read that nothing answered, so the
|
|
5373
|
+
* absence survives outside a live client that could infer it from two rows.
|
|
5374
|
+
*/
|
|
5375
|
+
const ThreadEvidenceActivityOutcome = Schema$1.Literals([
|
|
5376
|
+
"summarized",
|
|
5377
|
+
"fallback",
|
|
5378
|
+
"failed",
|
|
5379
|
+
"unreported"
|
|
5380
|
+
]);
|
|
5381
|
+
/**
|
|
5382
|
+
* Server clock around one review, never the supervisor's own account of it.
|
|
5383
|
+
*
|
|
5384
|
+
* `readAt` is when the evidence-read row was recorded and `closedAt` when the
|
|
5385
|
+
* row that closes the review was: the report for a reported review, the
|
|
5386
|
+
* completed supervisor turn for an unreported one. A supervisor turn that
|
|
5387
|
+
* failed or was interrupted closes nothing, so its read keeps no timing until
|
|
5388
|
+
* some later turn reports on it.
|
|
5389
|
+
*/
|
|
5390
|
+
const ThreadEvidenceReportTiming = Schema$1.Struct({
|
|
5391
|
+
readAt: IsoDateTime,
|
|
5392
|
+
closedAt: IsoDateTime,
|
|
5393
|
+
durationMs: NonNegativeInt
|
|
5394
|
+
});
|
|
5395
|
+
/** Payload of the single `fusion.evidence.summary` activity a report records. */
|
|
5396
|
+
const ThreadEvidenceReportActivity = Schema$1.Struct({
|
|
5397
|
+
watchedThreadId: ThreadId,
|
|
5398
|
+
firstSequence: NonNegativeInt,
|
|
5399
|
+
lastSequence: NonNegativeInt,
|
|
5400
|
+
throughSequence: NonNegativeInt,
|
|
5401
|
+
outcome: ThreadEvidenceActivityOutcome,
|
|
5402
|
+
summary: Schema$1.NullOr(Schema$1.String),
|
|
5403
|
+
/** True when a snapshot dropped the text of a superseded report. */
|
|
5404
|
+
summaryDropped: Schema$1.optionalKey(Schema$1.Boolean),
|
|
5405
|
+
model: Schema$1.NullOr(Schema$1.String),
|
|
5406
|
+
reason: Schema$1.NullOr(Schema$1.String),
|
|
5407
|
+
/** Null when no context-window row exists yet for the reporting thread. */
|
|
5408
|
+
context: Schema$1.NullOr(ThreadEvidenceReportContext),
|
|
5409
|
+
/**
|
|
5410
|
+
* Null when the evidence-read row behind this review is no longer readable,
|
|
5411
|
+
* and absent on rows stored before the server measured a review at all.
|
|
5412
|
+
*/
|
|
5413
|
+
timing: Schema$1.optionalKey(Schema$1.NullOr(ThreadEvidenceReportTiming))
|
|
5414
|
+
});
|
|
5415
|
+
const ThreadEvidenceReportResult = Schema$1.Struct({
|
|
5416
|
+
recorded: Schema$1.Boolean,
|
|
5417
|
+
context: Schema$1.NullOr(ThreadEvidenceReportContext)
|
|
5418
|
+
});
|
|
5419
|
+
var WatchToolUnavailableError = class extends Schema$1.TaggedErrorClass()("WatchToolUnavailableError", {
|
|
5420
|
+
capability: Schema$1.Literal("watch"),
|
|
5421
|
+
environmentId: EnvironmentId,
|
|
5422
|
+
threadId: ThreadId,
|
|
5423
|
+
providerSessionId: TrimmedNonEmptyString,
|
|
5424
|
+
providerInstanceId: ProviderInstanceId
|
|
5425
|
+
}) {
|
|
5426
|
+
get message() {
|
|
5427
|
+
return `MCP credential does not grant the ${this.capability} capability.`;
|
|
5428
|
+
}
|
|
5429
|
+
};
|
|
5430
|
+
/**
|
|
5431
|
+
* The capability is present but the requested thread is not in the granted
|
|
5432
|
+
* set. Separate from {@link WatchToolUnavailableError} so a watcher can tell
|
|
5433
|
+
* "you cannot watch at all" apart from "not this thread".
|
|
5434
|
+
*/
|
|
5435
|
+
var ThreadWatchNotPermittedError = class extends Schema$1.TaggedErrorClass()("ThreadWatchNotPermittedError", { threadId: ThreadId }) {
|
|
5436
|
+
get message() {
|
|
5437
|
+
return `This session was not granted watch access to thread ${this.threadId}.`;
|
|
5438
|
+
}
|
|
5439
|
+
};
|
|
5440
|
+
var ThreadWatchFailedError = class extends Schema$1.TaggedErrorClass()("ThreadWatchFailedError", {
|
|
5441
|
+
threadId: ThreadId,
|
|
5442
|
+
detail: Schema$1.String
|
|
5443
|
+
}) {
|
|
5444
|
+
get message() {
|
|
5445
|
+
return `Reading events for thread ${this.threadId} failed: ${this.detail}`;
|
|
5446
|
+
}
|
|
5447
|
+
};
|
|
5448
|
+
const ThreadWatchToolError = Schema$1.Union([
|
|
5449
|
+
WatchToolUnavailableError,
|
|
5450
|
+
ThreadWatchNotPermittedError,
|
|
5451
|
+
ThreadWatchFailedError
|
|
5452
|
+
]);
|
|
5453
|
+
//#endregion
|
|
5454
|
+
//#region ../../packages/contracts/src/hubAnalytics.ts
|
|
5455
|
+
/**
|
|
5456
|
+
* Account-scoped analytics facts the hub stores for its own account holder.
|
|
5457
|
+
*
|
|
5458
|
+
* Append-only and versioned: a fact is what happened once, never a row that is
|
|
5459
|
+
* later corrected, so a changed shape ships as a new `version` beside the old
|
|
5460
|
+
* one rather than a rewrite of stored rows. Every payload is bounded, because
|
|
5461
|
+
* these travel from environments the hub does not control and a fact that can
|
|
5462
|
+
* grow without limit is a store that can be filled by one caller.
|
|
5463
|
+
*
|
|
5464
|
+
* Nothing here leaves the account: the hub is the only sink, and no field is
|
|
5465
|
+
* shaped for an external telemetry vendor.
|
|
5466
|
+
*
|
|
5467
|
+
* @module hubAnalytics
|
|
5468
|
+
*/
|
|
5469
|
+
const MAX_FACT_ID_CHARS = 128;
|
|
5470
|
+
const MAX_QUESTION_CHARS$2 = 2e3;
|
|
5471
|
+
const MAX_HEADER_CHARS$2 = 120;
|
|
5472
|
+
const MAX_OPTION_LABEL_CHARS$1 = 200;
|
|
5473
|
+
const MAX_OPTION_DESCRIPTION_CHARS$1 = 600;
|
|
5474
|
+
const MAX_PRESENTED_OPTIONS$1 = 12;
|
|
5475
|
+
const MAX_CUSTOM_ANSWER_CHARS$1 = 2e3;
|
|
5476
|
+
const MAX_SELECTED_ANSWERS = MAX_PRESENTED_OPTIONS$1;
|
|
5477
|
+
const MAX_VERSION_CHARS = 40;
|
|
5478
|
+
const MAX_REASON_CHARS$1 = 200;
|
|
5479
|
+
const MAX_MODEL_CHARS$1 = 120;
|
|
5480
|
+
const MAX_QUERY_KINDS = 3;
|
|
5481
|
+
const MAX_QUERY_LIMIT = 200;
|
|
5482
|
+
const DEFAULT_QUERY_LIMIT = 50;
|
|
5483
|
+
const MAX_CURSOR_CHARS = 160;
|
|
5484
|
+
const factId = Schema$1.String.check(Schema$1.isMinLength(1), Schema$1.isMaxLength(MAX_FACT_ID_CHARS), Schema$1.isPattern(/^[A-Za-z0-9][A-Za-z0-9._:-]*$(?![\s\S])/));
|
|
5485
|
+
const nonNegativeInt$1 = Schema$1.Int.check(Schema$1.isGreaterThanOrEqualTo(0));
|
|
5486
|
+
/**
|
|
5487
|
+
* One option exactly as the user saw it, so a later preference read has its
|
|
5488
|
+
* context. Deliberately its own shape rather than the live question contract:
|
|
5489
|
+
* a fact records what was shown that day, and must not change meaning when the
|
|
5490
|
+
* question schema does.
|
|
5491
|
+
*/
|
|
5492
|
+
const PresentedOption = Schema$1.Struct({
|
|
5493
|
+
label: Schema$1.String.check(Schema$1.isMinLength(1), Schema$1.isMaxLength(MAX_OPTION_LABEL_CHARS$1)),
|
|
5494
|
+
description: Schema$1.String.check(Schema$1.isMaxLength(MAX_OPTION_DESCRIPTION_CHARS$1))
|
|
5495
|
+
});
|
|
5496
|
+
/**
|
|
5497
|
+
* What the user actually answered.
|
|
5498
|
+
*
|
|
5499
|
+
* The custom text is kept verbatim: an answer nobody offered is the most
|
|
5500
|
+
* informative one there is, and summarizing it away would leave the fact
|
|
5501
|
+
* saying only that the offered options were all wrong.
|
|
5502
|
+
*/
|
|
5503
|
+
const QuestionAnswer = Schema$1.Union([
|
|
5504
|
+
Schema$1.Struct({
|
|
5505
|
+
kind: Schema$1.Literal("option"),
|
|
5506
|
+
optionIndexes: Schema$1.Array(nonNegativeInt$1).check(Schema$1.isMinLength(1), Schema$1.isMaxLength(MAX_SELECTED_ANSWERS)),
|
|
5507
|
+
labels: Schema$1.Array(Schema$1.String.check(Schema$1.isMinLength(1), Schema$1.isMaxLength(MAX_OPTION_LABEL_CHARS$1))).check(Schema$1.isMinLength(1), Schema$1.isMaxLength(MAX_SELECTED_ANSWERS))
|
|
5508
|
+
}),
|
|
5509
|
+
Schema$1.Struct({
|
|
5510
|
+
kind: Schema$1.Literal("custom"),
|
|
5511
|
+
text: Schema$1.String.check(Schema$1.isMinLength(1), Schema$1.isMaxLength(MAX_CUSTOM_ANSWER_CHARS$1))
|
|
5512
|
+
}),
|
|
5513
|
+
Schema$1.Struct({ kind: Schema$1.Literal("dismissed") })
|
|
5514
|
+
]);
|
|
5515
|
+
/** An `ask_user_question` as presented, and what came back. */
|
|
5516
|
+
const HubQuestionFactV1 = Schema$1.Struct({
|
|
5517
|
+
questionId: factId,
|
|
5518
|
+
threadId: Schema$1.optional(ThreadId),
|
|
5519
|
+
header: Schema$1.String.check(Schema$1.isMaxLength(MAX_HEADER_CHARS$2)),
|
|
5520
|
+
question: Schema$1.String.check(Schema$1.isMinLength(1), Schema$1.isMaxLength(MAX_QUESTION_CHARS$2)),
|
|
5521
|
+
multiSelect: Schema$1.Boolean,
|
|
5522
|
+
options: Schema$1.Array(PresentedOption).check(Schema$1.isMaxLength(MAX_PRESENTED_OPTIONS$1)),
|
|
5523
|
+
answer: QuestionAnswer,
|
|
5524
|
+
answeredInMs: Schema$1.optional(nonNegativeInt$1)
|
|
5525
|
+
});
|
|
5526
|
+
/**
|
|
5527
|
+
* One Fusion handoff, from one implementer thread.
|
|
5528
|
+
*
|
|
5529
|
+
* Efficiency is reported as what the handoff carried and what it cost, never
|
|
5530
|
+
* as a score: the counts are comparable across versions, a verdict is not.
|
|
5531
|
+
*/
|
|
5532
|
+
const HubFusionHandoffFactV1 = Schema$1.Struct({
|
|
5533
|
+
fusionVersion: Schema$1.String.check(Schema$1.isMinLength(1), Schema$1.isMaxLength(MAX_VERSION_CHARS)),
|
|
5534
|
+
threadId: ThreadId,
|
|
5535
|
+
/**
|
|
5536
|
+
* The recorded review outcome, in the server's own vocabulary: `unreported`
|
|
5537
|
+
* is a real result - a review that closed answering nothing - and collapsing
|
|
5538
|
+
* it into a failure would lose the only row that says so.
|
|
5539
|
+
*/
|
|
5540
|
+
status: ThreadEvidenceActivityOutcome,
|
|
5541
|
+
/** The model the report names, or null when the review recorded none. */
|
|
5542
|
+
model: Schema$1.NullOr(Schema$1.String.check(Schema$1.isMaxLength(MAX_MODEL_CHARS$1))),
|
|
5543
|
+
reason: Schema$1.NullOr(Schema$1.String.check(Schema$1.isMaxLength(MAX_REASON_CHARS$1))),
|
|
5544
|
+
transcriptEvents: nonNegativeInt$1,
|
|
5545
|
+
transcriptIncluded: nonNegativeInt$1,
|
|
5546
|
+
transcriptDropped: nonNegativeInt$1,
|
|
5547
|
+
transcriptClippedFields: nonNegativeInt$1,
|
|
5548
|
+
ledgerRows: nonNegativeInt$1,
|
|
5549
|
+
ledgerTotalRows: nonNegativeInt$1,
|
|
5550
|
+
summaryChars: Schema$1.NullOr(nonNegativeInt$1),
|
|
5551
|
+
contextDeltaTokens: Schema$1.NullOr(Schema$1.Int),
|
|
5552
|
+
durationMs: Schema$1.NullOr(nonNegativeInt$1)
|
|
5553
|
+
});
|
|
5554
|
+
/**
|
|
5555
|
+
* One compression pass: what it started from, what it left, and what it cost.
|
|
5556
|
+
*
|
|
5557
|
+
* The mode is the product's own strict mode set, so a mode added there is
|
|
5558
|
+
* recordable here the day it ships; a local copy of the literals would quietly
|
|
5559
|
+
* reject the new one and lose exactly the passes worth measuring. The strict
|
|
5560
|
+
* schema rather than the tolerant wire one: a fact that relabelled an unknown
|
|
5561
|
+
* mode as the default would be a record of something that never ran.
|
|
5562
|
+
*/
|
|
5563
|
+
const HubCompressionFactV1 = Schema$1.Struct({
|
|
5564
|
+
mode: CompressModeLiteral,
|
|
5565
|
+
threadId: Schema$1.optional(ThreadId),
|
|
5566
|
+
beforeTokens: nonNegativeInt$1,
|
|
5567
|
+
afterTokens: nonNegativeInt$1,
|
|
5568
|
+
/** Negative when the pass cost more than it saved; the sign is the finding. */
|
|
5569
|
+
deltaTokens: Schema$1.Int,
|
|
5570
|
+
durationMs: nonNegativeInt$1
|
|
5571
|
+
});
|
|
5572
|
+
const HubAnalyticsFactKind = Schema$1.Literals([
|
|
5573
|
+
"question.answered",
|
|
5574
|
+
"fusion.handoff",
|
|
5575
|
+
"compression.applied"
|
|
5576
|
+
]);
|
|
5577
|
+
const factBase = {
|
|
5578
|
+
/** Dedupe identity: the same id appended twice stores one fact. */
|
|
5579
|
+
eventId: factId,
|
|
5580
|
+
occurredAt: IsoDateTime
|
|
5581
|
+
};
|
|
5582
|
+
/** One append. The kind fixes the payload shape, and the version fixes its meaning. */
|
|
5583
|
+
const HubAnalyticsFactInput = Schema$1.Union([
|
|
5584
|
+
Schema$1.Struct({
|
|
5585
|
+
...factBase,
|
|
5586
|
+
kind: Schema$1.Literal("question.answered"),
|
|
5587
|
+
version: Schema$1.Literal(1),
|
|
5588
|
+
payload: HubQuestionFactV1
|
|
5589
|
+
}),
|
|
5590
|
+
Schema$1.Struct({
|
|
5591
|
+
...factBase,
|
|
5592
|
+
kind: Schema$1.Literal("fusion.handoff"),
|
|
5593
|
+
version: Schema$1.Literal(1),
|
|
5594
|
+
payload: HubFusionHandoffFactV1
|
|
5595
|
+
}),
|
|
5596
|
+
Schema$1.Struct({
|
|
5597
|
+
...factBase,
|
|
5598
|
+
kind: Schema$1.Literal("compression.applied"),
|
|
5599
|
+
version: Schema$1.Literal(1),
|
|
5600
|
+
payload: HubCompressionFactV1
|
|
5601
|
+
})
|
|
5602
|
+
]);
|
|
5603
|
+
/** A stored fact as it is read back, with the environment that reported it. */
|
|
5604
|
+
const HubAnalyticsFact = Schema$1.Struct({
|
|
5605
|
+
eventId: factId,
|
|
5606
|
+
environmentId: EnvironmentId,
|
|
5607
|
+
kind: HubAnalyticsFactKind,
|
|
5608
|
+
version: Schema$1.Int.check(Schema$1.isGreaterThanOrEqualTo(1)),
|
|
5609
|
+
occurredAt: IsoDateTime,
|
|
5610
|
+
payload: Schema$1.Unknown
|
|
5611
|
+
});
|
|
5612
|
+
/**
|
|
5613
|
+
* An opaque page position: the occurrence millisecond and the event id that
|
|
5614
|
+
* produced it, which together order every row exactly once.
|
|
5615
|
+
*/
|
|
5616
|
+
const HubAnalyticsCursor = Schema$1.String.check(Schema$1.isMaxLength(MAX_CURSOR_CHARS), Schema$1.isPattern(/^\d+\|[A-Za-z0-9][A-Za-z0-9._:-]*$(?![\s\S])/));
|
|
5617
|
+
const HubAnalyticsQuery = Schema$1.Struct({
|
|
5618
|
+
kinds: Schema$1.optional(Schema$1.Array(HubAnalyticsFactKind).check(Schema$1.isMinLength(1), Schema$1.isMaxLength(MAX_QUERY_KINDS))),
|
|
5619
|
+
since: Schema$1.optional(IsoDateTime),
|
|
5620
|
+
until: Schema$1.optional(IsoDateTime),
|
|
5621
|
+
limit: Schema$1.optional(Schema$1.Int.check(Schema$1.isGreaterThanOrEqualTo(1), Schema$1.isLessThanOrEqualTo(MAX_QUERY_LIMIT))),
|
|
5622
|
+
cursor: Schema$1.optional(HubAnalyticsCursor)
|
|
5623
|
+
});
|
|
5624
|
+
const HubAnalyticsPage = Schema$1.Struct({
|
|
5625
|
+
facts: Schema$1.Array(HubAnalyticsFact),
|
|
5626
|
+
/** Null when the page reached the end, never an empty string. */
|
|
5627
|
+
nextCursor: Schema$1.NullOr(HubAnalyticsCursor)
|
|
5628
|
+
});
|
|
5629
|
+
const HUB_ANALYTICS_QUERY_LIMITS = {
|
|
5630
|
+
defaultLimit: DEFAULT_QUERY_LIMIT,
|
|
5631
|
+
maxLimit: MAX_QUERY_LIMIT
|
|
5632
|
+
};
|
|
5633
|
+
//#endregion
|
|
5634
|
+
//#region ../../packages/contracts/src/hubNotifications.ts
|
|
5635
|
+
const MAX_NOTIFICATION_ID_CHARS = 128;
|
|
5636
|
+
const MAX_PUSH_TOKEN_CHARS = 4096;
|
|
5637
|
+
const MAX_HUB_ORIGIN_CHARS = 2048;
|
|
5638
|
+
const MAX_ACTIVITY_TITLE_CHARS = 160;
|
|
5639
|
+
const MAX_ACTIVITY_DETAIL_CHARS = 240;
|
|
5640
|
+
const identifier = Schema$1.String.check(Schema$1.isMinLength(1), Schema$1.isMaxLength(MAX_NOTIFICATION_ID_CHARS), Schema$1.isPattern(/^[A-Za-z0-9][A-Za-z0-9._-]*$(?![\s\S])/));
|
|
5641
|
+
const pushToken = Schema$1.String.check(Schema$1.isMinLength(1), Schema$1.isMaxLength(MAX_PUSH_TOKEN_CHARS), Schema$1.isPattern(/^[\x21-\x7e]+$(?![\s\S])/));
|
|
5642
|
+
/** Server emission is bound to the authenticated environment, never a supplied account. */
|
|
5643
|
+
const HubAgentActivityInput = Schema$1.Struct({
|
|
5644
|
+
eventId: identifier,
|
|
5645
|
+
threadId: ThreadId,
|
|
5646
|
+
revision: Schema$1.Int.check(Schema$1.isGreaterThanOrEqualTo(0)),
|
|
5647
|
+
state: Schema$1.Literals([
|
|
5648
|
+
"running",
|
|
5649
|
+
"approval_required",
|
|
5650
|
+
"question_required",
|
|
5651
|
+
"completed",
|
|
5652
|
+
"failed",
|
|
5653
|
+
"stopped"
|
|
5654
|
+
]),
|
|
5655
|
+
title: Schema$1.String.check(Schema$1.isMinLength(1), Schema$1.isMaxLength(MAX_ACTIVITY_TITLE_CHARS)),
|
|
5656
|
+
detail: Schema$1.String.check(Schema$1.isMaxLength(MAX_ACTIVITY_DETAIL_CHARS))
|
|
5657
|
+
});
|
|
5658
|
+
/** Each authenticated client owns its registration; tokens never belong in public client state. */
|
|
5659
|
+
const HubPushRegistrationInput = Schema$1.Union([Schema$1.Struct({
|
|
5660
|
+
platform: Schema$1.Literal("ios"),
|
|
5661
|
+
token: pushToken,
|
|
5662
|
+
sandbox: Schema$1.Boolean
|
|
5663
|
+
}), Schema$1.Struct({
|
|
5664
|
+
platform: Schema$1.Literal("android"),
|
|
5665
|
+
token: pushToken
|
|
5666
|
+
})]);
|
|
5667
|
+
/** Explicit opt-in, independently reversible. Omission never implies permission. */
|
|
5668
|
+
const HubNotificationPreferences = Schema$1.Struct({
|
|
5669
|
+
attention: Schema$1.Boolean,
|
|
5670
|
+
completion: Schema$1.Boolean,
|
|
5671
|
+
liveActivity: Schema$1.Boolean
|
|
5672
|
+
});
|
|
5673
|
+
Schema$1.Struct({
|
|
5674
|
+
registered: Schema$1.Boolean,
|
|
5675
|
+
registrationGeneration: Schema$1.NullOr(identifier),
|
|
5676
|
+
preferences: HubNotificationPreferences
|
|
5677
|
+
});
|
|
5678
|
+
/** ActivityKit update token, distinct from the app's APNs registration token. */
|
|
5679
|
+
const HubActivityOwnership = Schema$1.Struct({
|
|
5680
|
+
origin: Schema$1.String.check(Schema$1.isMinLength(1), Schema$1.isMaxLength(MAX_HUB_ORIGIN_CHARS)),
|
|
5681
|
+
environmentId: EnvironmentId,
|
|
5682
|
+
clientId: HubClientId,
|
|
5683
|
+
registrationGeneration: identifier,
|
|
5684
|
+
threadId: ThreadId,
|
|
5685
|
+
activityId: identifier
|
|
5686
|
+
});
|
|
5687
|
+
const HubLiveActivityRegistrationInput = Schema$1.Struct({
|
|
5688
|
+
threadId: ThreadId,
|
|
5689
|
+
registrationGeneration: identifier,
|
|
5690
|
+
activityId: identifier,
|
|
5691
|
+
token: Schema$1.String.check(Schema$1.isMinLength(1), Schema$1.isMaxLength(MAX_PUSH_TOKEN_CHARS), Schema$1.isPattern(/^(?:[a-fA-F0-9]{2})+$(?![\s\S])/)),
|
|
5692
|
+
sandbox: Schema$1.Boolean,
|
|
5693
|
+
p4Ownership: Schema$1.optional(HubActivityOwnership)
|
|
5694
|
+
});
|
|
5695
|
+
/** Close only this presentation; delayed requests must not retire a newer activity. */
|
|
5696
|
+
const HubLiveActivityRemoveInput = Schema$1.Struct({
|
|
5697
|
+
threadId: ThreadId,
|
|
5698
|
+
registrationGeneration: identifier,
|
|
5699
|
+
activityId: identifier
|
|
5700
|
+
});
|
|
5701
|
+
const HUB_TUNNEL_WEBSOCKET_PROTOCOL = "p4-hub-tunnel-v1";
|
|
5702
|
+
const HUB_TUNNEL_INITIAL_WINDOW_BYTES = 256 * 1024;
|
|
5703
|
+
const HUB_TUNNEL_MAX_WINDOW_BYTES = 1024 * 1024;
|
|
5704
|
+
const MAX_HEADERS = 64;
|
|
5705
|
+
const MAX_HEADER_NAME_CHARS = 128;
|
|
5706
|
+
const MAX_HEADER_VALUE_CHARS = 8192;
|
|
5707
|
+
const MAX_PATH_CHARS = 8192;
|
|
5708
|
+
const MAX_PROTOCOLS = 16;
|
|
5709
|
+
const MAX_PROTOCOL_CHARS = 128;
|
|
5710
|
+
const HeaderName = Schema$1.String.check(Schema$1.isMinLength(1), Schema$1.isMaxLength(MAX_HEADER_NAME_CHARS), Schema$1.isPattern(/^[!#$%&'*+.^_`|~0-9A-Za-z-]+$(?![\s\S])/));
|
|
5711
|
+
const HeaderValue = Schema$1.String.check(Schema$1.isMaxLength(MAX_HEADER_VALUE_CHARS), Schema$1.isPattern(/^[\t\x20-\x7e]*$(?![\s\S])/));
|
|
5712
|
+
const Headers$2 = Schema$1.Array(Schema$1.Tuple([HeaderName, HeaderValue])).check(Schema$1.isMaxLength(MAX_HEADERS));
|
|
5713
|
+
const Path = Schema$1.String.check(Schema$1.isMinLength(1), Schema$1.isMaxLength(MAX_PATH_CHARS), Schema$1.isPattern(/^\/(?!\/)[^\s\\#\x00-\x1f\x7f]*$(?![\s\S])/));
|
|
5714
|
+
const Protocol = Schema$1.String.check(Schema$1.isMinLength(1), Schema$1.isMaxLength(MAX_PROTOCOL_CHARS), Schema$1.isPattern(/^[!#$%&'*+.^_`|~0-9A-Za-z-]+$(?![\s\S])/));
|
|
5715
|
+
const FailureReason = Schema$1.Literals([
|
|
5716
|
+
"cancelled",
|
|
5717
|
+
"unavailable",
|
|
5718
|
+
"protocol_error"
|
|
5719
|
+
]);
|
|
5720
|
+
const CloseCode = Schema$1.Int.check(Schema$1.makeFilter((code) => code >= 1e3 && code <= 1014 && code !== 1004 && code !== 1005 && code !== 1006 || code >= 3e3 && code <= 4999));
|
|
5721
|
+
/** No caller-supplied account, environment, origin, or connector ownership authority. */
|
|
5722
|
+
const HubTunnelControl = Schema$1.Union([
|
|
5723
|
+
Schema$1.Struct({
|
|
5724
|
+
type: Schema$1.Literal("connector_ready"),
|
|
5725
|
+
protocolVersion: Schema$1.Literal(1)
|
|
5726
|
+
}),
|
|
5727
|
+
Schema$1.Struct({
|
|
5728
|
+
type: Schema$1.Literal("http_request_start"),
|
|
5729
|
+
method: Schema$1.Literals([
|
|
5730
|
+
"GET",
|
|
5731
|
+
"HEAD",
|
|
5732
|
+
"POST",
|
|
5733
|
+
"PUT",
|
|
5734
|
+
"PATCH",
|
|
5735
|
+
"DELETE",
|
|
5736
|
+
"OPTIONS"
|
|
5737
|
+
]),
|
|
5738
|
+
path: Path,
|
|
5739
|
+
headers: Headers$2
|
|
5740
|
+
}),
|
|
5741
|
+
Schema$1.Struct({ type: Schema$1.Literal("http_request_end") }),
|
|
5742
|
+
Schema$1.Struct({
|
|
5743
|
+
type: Schema$1.Literal("http_request_abort"),
|
|
5744
|
+
reason: FailureReason
|
|
5745
|
+
}),
|
|
5746
|
+
Schema$1.Struct({
|
|
5747
|
+
type: Schema$1.Literal("http_response_start"),
|
|
5748
|
+
status: Schema$1.Int.check(Schema$1.isGreaterThanOrEqualTo(200), Schema$1.isLessThanOrEqualTo(599)),
|
|
5749
|
+
headers: Headers$2
|
|
5750
|
+
}),
|
|
5751
|
+
Schema$1.Struct({ type: Schema$1.Literal("http_response_end") }),
|
|
5752
|
+
Schema$1.Struct({
|
|
5753
|
+
type: Schema$1.Literal("http_response_abort"),
|
|
5754
|
+
reason: FailureReason
|
|
5755
|
+
}),
|
|
5756
|
+
Schema$1.Struct({
|
|
5757
|
+
type: Schema$1.Literal("websocket_open"),
|
|
5758
|
+
path: Path,
|
|
5759
|
+
headers: Headers$2,
|
|
5760
|
+
protocols: Schema$1.Array(Protocol).check(Schema$1.isMaxLength(MAX_PROTOCOLS))
|
|
5761
|
+
}),
|
|
5762
|
+
Schema$1.Struct({
|
|
5763
|
+
type: Schema$1.Literal("websocket_accept"),
|
|
5764
|
+
protocol: Schema$1.optional(Protocol)
|
|
5765
|
+
}),
|
|
5766
|
+
Schema$1.Struct({
|
|
5767
|
+
type: Schema$1.Literal("websocket_reject"),
|
|
5768
|
+
status: Schema$1.Int.check(Schema$1.isGreaterThanOrEqualTo(400), Schema$1.isLessThanOrEqualTo(599)),
|
|
5769
|
+
reason: FailureReason
|
|
5770
|
+
}),
|
|
5771
|
+
Schema$1.Struct({
|
|
5772
|
+
type: Schema$1.Literal("websocket_close"),
|
|
5773
|
+
code: CloseCode,
|
|
5774
|
+
reason: FailureReason
|
|
5775
|
+
}),
|
|
5776
|
+
Schema$1.Struct({
|
|
5777
|
+
type: Schema$1.Literal("window_update"),
|
|
5778
|
+
creditBytes: Schema$1.Int.check(Schema$1.isGreaterThan(0), Schema$1.isLessThanOrEqualTo(HUB_TUNNEL_MAX_WINDOW_BYTES))
|
|
5779
|
+
})
|
|
5780
|
+
]);
|
|
5350
5781
|
//#endregion
|
|
5351
5782
|
//#region ../../packages/contracts/src/environmentHttp.ts
|
|
5352
5783
|
const OptionalBearerHeaders = Schema$1.Struct({
|
|
@@ -7866,9 +8297,16 @@ const UserInputResolvedPayload = Schema$1.Struct({ answers: UnknownRecordSchema
|
|
|
7866
8297
|
* that outlives its turn is in practice a watch loop). Canonical single copy,
|
|
7867
8298
|
* so the server liveness registry and any client fold classify identically.
|
|
7868
8299
|
*/
|
|
8300
|
+
const MONITOR_TOOL_TASK_TYPES = /* @__PURE__ */ new Set(["monitor", "monitor_mcp"]);
|
|
8301
|
+
/**
|
|
8302
|
+
* A watch loop the agent asked for by name, without the background shells.
|
|
8303
|
+
*
|
|
8304
|
+
* A backgrounded shell is only a watch loop in practice; it can equally run a
|
|
8305
|
+
* build or edit a file, so a consumer that needs proof of waiting rather than
|
|
8306
|
+
* a liveness hint must use this narrower set.
|
|
8307
|
+
*/
|
|
7869
8308
|
const MONITOR_TASK_TYPES = /* @__PURE__ */ new Set([
|
|
7870
|
-
|
|
7871
|
-
"monitor_mcp",
|
|
8309
|
+
...MONITOR_TOOL_TASK_TYPES,
|
|
7872
8310
|
"local_bash",
|
|
7873
8311
|
"shell"
|
|
7874
8312
|
]);
|
|
@@ -12568,251 +13006,6 @@ var ThreadAskFailedError = class extends Schema$1.TaggedErrorClass()("ThreadAskF
|
|
|
12568
13006
|
}
|
|
12569
13007
|
};
|
|
12570
13008
|
const ThreadAskToolError = Schema$1.Union([ThreadAskNotPermittedError, ThreadAskFailedError]);
|
|
12571
|
-
const ThreadWatchEventsInput = Schema$1.Struct({
|
|
12572
|
-
threadId: ThreadId.annotate({ description: "The thread to read events from. Any thread on this server, not only this session's own." }),
|
|
12573
|
-
afterSequence: Schema$1.optional(NonNegativeInt.annotate({ description: "Exclusive sequence cursor. Events with a higher sequence are returned. Omit to read from the beginning." })),
|
|
12574
|
-
limit: Schema$1.optional(NonNegativeInt.check(Schema$1.isLessThanOrEqualTo(200)).annotate({ description: `Maximum events to return (default 50, max 200).` }))
|
|
12575
|
-
});
|
|
12576
|
-
const ThreadWatchEventsResult = Schema$1.Struct({
|
|
12577
|
-
threadId: ThreadId,
|
|
12578
|
-
/** Ordered by sequence ascending; resume with the last event's sequence. */
|
|
12579
|
-
events: Schema$1.Array(OrchestrationEvent),
|
|
12580
|
-
/**
|
|
12581
|
-
* The engine's latest persisted sequence at read time. Equal to the last
|
|
12582
|
-
* returned event's sequence when the watcher is caught up.
|
|
12583
|
-
*/
|
|
12584
|
-
headSequence: NonNegativeInt,
|
|
12585
|
-
/** True when more events for this thread exist past the returned page. */
|
|
12586
|
-
hasMore: Schema$1.Boolean
|
|
12587
|
-
});
|
|
12588
|
-
/** Compact review limits are server-owned, independent of raw event paging. */
|
|
12589
|
-
const THREAD_REVIEW_SCAN_LIMIT = 2e3;
|
|
12590
|
-
const THREAD_REVIEW_ITEM_MAX_BYTES = 8e3;
|
|
12591
|
-
const ThreadWatchReviewInput = Schema$1.Struct({
|
|
12592
|
-
threadId: ThreadId,
|
|
12593
|
-
afterSequence: Schema$1.optional(NonNegativeInt),
|
|
12594
|
-
throughSequence: Schema$1.optional(NonNegativeInt.annotate({ description: "Inclusive fixed review boundary. Omit on the first page to capture the head; reuse the returned boundary on every subsequent page." }))
|
|
12595
|
-
});
|
|
12596
|
-
const ReviewSourceFields = {
|
|
12597
|
-
firstSequence: NonNegativeInt,
|
|
12598
|
-
lastSequence: NonNegativeInt,
|
|
12599
|
-
truncated: Schema$1.Boolean
|
|
12600
|
-
};
|
|
12601
|
-
const ThreadReviewItem = Schema$1.Union([Schema$1.Struct({
|
|
12602
|
-
...ReviewSourceFields,
|
|
12603
|
-
kind: Schema$1.Literal("message"),
|
|
12604
|
-
messageId: Schema$1.String,
|
|
12605
|
-
turnId: Schema$1.NullOr(Schema$1.String),
|
|
12606
|
-
role: Schema$1.String,
|
|
12607
|
-
/** Apply append/replace by message identity across page boundaries. */
|
|
12608
|
-
operation: Schema$1.Literals(["append", "replace"]),
|
|
12609
|
-
complete: Schema$1.Boolean,
|
|
12610
|
-
text: Schema$1.String,
|
|
12611
|
-
/**
|
|
12612
|
-
* What the message carried besides text.
|
|
12613
|
-
*
|
|
12614
|
-
* Present only when there was something: a message whose fragments are
|
|
12615
|
-
* merged by identity would otherwise lose every trace of an image or a
|
|
12616
|
-
* document the builder attached. Bounded by
|
|
12617
|
-
* {@link THREAD_REVIEW_ITEM_MAX_ATTACHMENTS} so the item stays trimmable -
|
|
12618
|
-
* only its text shrinks under the byte cap, so nothing else may be able to
|
|
12619
|
-
* grow without limit.
|
|
12620
|
-
*/
|
|
12621
|
-
attachments: Schema$1.optionalKey(Schema$1.Array(Schema$1.Struct({
|
|
12622
|
-
type: Schema$1.String,
|
|
12623
|
-
name: Schema$1.String,
|
|
12624
|
-
sizeBytes: NonNegativeInt
|
|
12625
|
-
})))
|
|
12626
|
-
}), Schema$1.Struct({
|
|
12627
|
-
...ReviewSourceFields,
|
|
12628
|
-
kind: Schema$1.Literal("event"),
|
|
12629
|
-
type: Schema$1.String,
|
|
12630
|
-
/** JSON evidence excerpt. Truncated excerpts require targeted raw recovery. */
|
|
12631
|
-
payload: Schema$1.String
|
|
12632
|
-
})]);
|
|
12633
|
-
/**
|
|
12634
|
-
* Compressed evidence standing in for the items it replaces.
|
|
12635
|
-
*
|
|
12636
|
-
* The supervisor reads this instead of paging the range event by event. The
|
|
12637
|
-
* sequence bounds are what make that safe: any claim can be verified at source
|
|
12638
|
-
* with a targeted `thread_watch_events` read over exactly this range, so the
|
|
12639
|
-
* summary is a starting point rather than the only record.
|
|
12640
|
-
*
|
|
12641
|
-
* `degraded` is set whenever the items came back unsummarized, and names why.
|
|
12642
|
-
* Most reasons are failures - unset model, provider error, timeout, empty or
|
|
12643
|
-
* unusable output - but `page-under-threshold` is not: it means the page was
|
|
12644
|
-
* small enough that summarizing it would have cost more than reading it. The
|
|
12645
|
-
* supervisor is never left guessing whether it is reading everything.
|
|
12646
|
-
*/
|
|
12647
|
-
const ThreadReviewSummary = Schema$1.Struct({
|
|
12648
|
-
text: Schema$1.String,
|
|
12649
|
-
firstSequence: NonNegativeInt,
|
|
12650
|
-
lastSequence: NonNegativeInt,
|
|
12651
|
-
/** How many builder turns the range covered. */
|
|
12652
|
-
turnCount: NonNegativeInt,
|
|
12653
|
-
degraded: Schema$1.NullOr(Schema$1.String)
|
|
12654
|
-
});
|
|
12655
|
-
const ThreadWatchReviewResult = Schema$1.Struct({
|
|
12656
|
-
threadId: ThreadId,
|
|
12657
|
-
items: Schema$1.Array(ThreadReviewItem),
|
|
12658
|
-
throughSequence: NonNegativeInt,
|
|
12659
|
-
/** Resume from the source scan cursor, even when items is empty. */
|
|
12660
|
-
nextAfterSequence: NonNegativeInt,
|
|
12661
|
-
hasMore: Schema$1.Boolean,
|
|
12662
|
-
scannedEvents: NonNegativeInt,
|
|
12663
|
-
/**
|
|
12664
|
-
* Present only when summarization ran. When its `degraded` is null the
|
|
12665
|
-
* `items` for the summarized range are omitted and this stands in for them;
|
|
12666
|
-
* otherwise the items are intact and this explains why.
|
|
12667
|
-
*/
|
|
12668
|
-
summary: Schema$1.optionalKey(Schema$1.NullOr(ThreadReviewSummary))
|
|
12669
|
-
});
|
|
12670
|
-
/**
|
|
12671
|
-
* How a review ended, from the supervisor's own account of it.
|
|
12672
|
-
*
|
|
12673
|
-
* Three states rather than a boolean because they are acted on differently: a
|
|
12674
|
-
* summary that arrived is evidence, a fallback is a working review that cost
|
|
12675
|
-
* full context, and a failure is a misconfiguration worth fixing. The fourth
|
|
12676
|
-
* state - a read no report ever answered - is not sayable here: only the
|
|
12677
|
-
* server can observe it, through `ThreadEvidenceActivityOutcome`.
|
|
12678
|
-
*/
|
|
12679
|
-
const ThreadEvidenceReportOutcome = Schema$1.Literals([
|
|
12680
|
-
"summarized",
|
|
12681
|
-
"fallback",
|
|
12682
|
-
"failed"
|
|
12683
|
-
]);
|
|
12684
|
-
/** Bounded so one report cannot dominate a thread's activity storage. */
|
|
12685
|
-
const THREAD_EVIDENCE_REPORT_MAX_CHARS = 8e3;
|
|
12686
|
-
const ThreadEvidenceReportInput = Schema$1.Struct({
|
|
12687
|
-
/** The watched thread the summary covers, not the reporting thread. */
|
|
12688
|
-
threadId: ThreadId,
|
|
12689
|
-
firstSequence: NonNegativeInt,
|
|
12690
|
-
lastSequence: NonNegativeInt,
|
|
12691
|
-
/**
|
|
12692
|
-
* The fixed review boundary the pages were read against.
|
|
12693
|
-
*
|
|
12694
|
-
* Identity, not description: the evidence-read rows are keyed on it, so a
|
|
12695
|
-
* report that guessed from its own last sequence would look for a baseline
|
|
12696
|
-
* that never existed whenever the range ended on a filtered event.
|
|
12697
|
-
*/
|
|
12698
|
-
throughSequence: NonNegativeInt,
|
|
12699
|
-
outcome: ThreadEvidenceReportOutcome,
|
|
12700
|
-
/** The summarizer's exact text. Required when the outcome is summarized. */
|
|
12701
|
-
summary: Schema$1.optional(Schema$1.String),
|
|
12702
|
-
/** The model the subagent ran on, when one was spawned. */
|
|
12703
|
-
model: Schema$1.optional(Schema$1.String),
|
|
12704
|
-
/** Why the summary is missing or degraded. Required unless summarized. */
|
|
12705
|
-
reason: Schema$1.optional(Schema$1.String)
|
|
12706
|
-
});
|
|
12707
|
-
/**
|
|
12708
|
-
* The supervisor's own context around the read, correlated by the server.
|
|
12709
|
-
*
|
|
12710
|
-
* Self-reported numbers would be worth little, so these are read from the
|
|
12711
|
-
* reporting thread's projected context-window rows: `before` is what was
|
|
12712
|
-
* recorded when the evidence read happened, `after` what is recorded when the
|
|
12713
|
-
* report lands. `delta` can be negative when a compaction ran in between, and
|
|
12714
|
-
* is null whenever no read-time baseline exists.
|
|
12715
|
-
*/
|
|
12716
|
-
const ThreadEvidenceReportContext = Schema$1.Struct({
|
|
12717
|
-
/**
|
|
12718
|
-
* Null when no reading was recorded at read time.
|
|
12719
|
-
*
|
|
12720
|
-
* Never the report-time value standing in for itself: a delta of zero read
|
|
12721
|
-
* as measurement would be a lie about a review that cost context.
|
|
12722
|
-
*/
|
|
12723
|
-
usedTokensBefore: Schema$1.NullOr(NonNegativeInt),
|
|
12724
|
-
usedTokensAfter: NonNegativeInt,
|
|
12725
|
-
/** Null exactly when `usedTokensBefore` is. */
|
|
12726
|
-
deltaTokens: Schema$1.NullOr(Schema$1.Number),
|
|
12727
|
-
maxTokens: Schema$1.NullOr(NonNegativeInt),
|
|
12728
|
-
capacityPercent: Schema$1.NullOr(Schema$1.Number)
|
|
12729
|
-
});
|
|
12730
|
-
/**
|
|
12731
|
-
* How the recorded row ended, which is the supervisor's three outcomes plus the
|
|
12732
|
-
* one only the server can state: the review closed with no report at all.
|
|
12733
|
-
*
|
|
12734
|
-
* `unreported` is never accepted from a tool call. The server writes it when a
|
|
12735
|
-
* supervisor turn ends over an evidence read that nothing answered, so the
|
|
12736
|
-
* absence survives outside a live client that could infer it from two rows.
|
|
12737
|
-
*/
|
|
12738
|
-
const ThreadEvidenceActivityOutcome = Schema$1.Literals([
|
|
12739
|
-
"summarized",
|
|
12740
|
-
"fallback",
|
|
12741
|
-
"failed",
|
|
12742
|
-
"unreported"
|
|
12743
|
-
]);
|
|
12744
|
-
/**
|
|
12745
|
-
* Server clock around one review, never the supervisor's own account of it.
|
|
12746
|
-
*
|
|
12747
|
-
* `readAt` is when the evidence-read row was recorded and `closedAt` when the
|
|
12748
|
-
* row that closes the review was: the report for a reported review, the
|
|
12749
|
-
* completed supervisor turn for an unreported one. A supervisor turn that
|
|
12750
|
-
* failed or was interrupted closes nothing, so its read keeps no timing until
|
|
12751
|
-
* some later turn reports on it.
|
|
12752
|
-
*/
|
|
12753
|
-
const ThreadEvidenceReportTiming = Schema$1.Struct({
|
|
12754
|
-
readAt: IsoDateTime,
|
|
12755
|
-
closedAt: IsoDateTime,
|
|
12756
|
-
durationMs: NonNegativeInt
|
|
12757
|
-
});
|
|
12758
|
-
/** Payload of the single `fusion.evidence.summary` activity a report records. */
|
|
12759
|
-
const ThreadEvidenceReportActivity = Schema$1.Struct({
|
|
12760
|
-
watchedThreadId: ThreadId,
|
|
12761
|
-
firstSequence: NonNegativeInt,
|
|
12762
|
-
lastSequence: NonNegativeInt,
|
|
12763
|
-
throughSequence: NonNegativeInt,
|
|
12764
|
-
outcome: ThreadEvidenceActivityOutcome,
|
|
12765
|
-
summary: Schema$1.NullOr(Schema$1.String),
|
|
12766
|
-
/** True when a snapshot dropped the text of a superseded report. */
|
|
12767
|
-
summaryDropped: Schema$1.optionalKey(Schema$1.Boolean),
|
|
12768
|
-
model: Schema$1.NullOr(Schema$1.String),
|
|
12769
|
-
reason: Schema$1.NullOr(Schema$1.String),
|
|
12770
|
-
/** Null when no context-window row exists yet for the reporting thread. */
|
|
12771
|
-
context: Schema$1.NullOr(ThreadEvidenceReportContext),
|
|
12772
|
-
/**
|
|
12773
|
-
* Null when the evidence-read row behind this review is no longer readable,
|
|
12774
|
-
* and absent on rows stored before the server measured a review at all.
|
|
12775
|
-
*/
|
|
12776
|
-
timing: Schema$1.optionalKey(Schema$1.NullOr(ThreadEvidenceReportTiming))
|
|
12777
|
-
});
|
|
12778
|
-
const ThreadEvidenceReportResult = Schema$1.Struct({
|
|
12779
|
-
recorded: Schema$1.Boolean,
|
|
12780
|
-
context: Schema$1.NullOr(ThreadEvidenceReportContext)
|
|
12781
|
-
});
|
|
12782
|
-
var WatchToolUnavailableError = class extends Schema$1.TaggedErrorClass()("WatchToolUnavailableError", {
|
|
12783
|
-
capability: Schema$1.Literal("watch"),
|
|
12784
|
-
environmentId: EnvironmentId,
|
|
12785
|
-
threadId: ThreadId,
|
|
12786
|
-
providerSessionId: TrimmedNonEmptyString,
|
|
12787
|
-
providerInstanceId: ProviderInstanceId
|
|
12788
|
-
}) {
|
|
12789
|
-
get message() {
|
|
12790
|
-
return `MCP credential does not grant the ${this.capability} capability.`;
|
|
12791
|
-
}
|
|
12792
|
-
};
|
|
12793
|
-
/**
|
|
12794
|
-
* The capability is present but the requested thread is not in the granted
|
|
12795
|
-
* set. Separate from {@link WatchToolUnavailableError} so a watcher can tell
|
|
12796
|
-
* "you cannot watch at all" apart from "not this thread".
|
|
12797
|
-
*/
|
|
12798
|
-
var ThreadWatchNotPermittedError = class extends Schema$1.TaggedErrorClass()("ThreadWatchNotPermittedError", { threadId: ThreadId }) {
|
|
12799
|
-
get message() {
|
|
12800
|
-
return `This session was not granted watch access to thread ${this.threadId}.`;
|
|
12801
|
-
}
|
|
12802
|
-
};
|
|
12803
|
-
var ThreadWatchFailedError = class extends Schema$1.TaggedErrorClass()("ThreadWatchFailedError", {
|
|
12804
|
-
threadId: ThreadId,
|
|
12805
|
-
detail: Schema$1.String
|
|
12806
|
-
}) {
|
|
12807
|
-
get message() {
|
|
12808
|
-
return `Reading events for thread ${this.threadId} failed: ${this.detail}`;
|
|
12809
|
-
}
|
|
12810
|
-
};
|
|
12811
|
-
const ThreadWatchToolError = Schema$1.Union([
|
|
12812
|
-
WatchToolUnavailableError,
|
|
12813
|
-
ThreadWatchNotPermittedError,
|
|
12814
|
-
ThreadWatchFailedError
|
|
12815
|
-
]);
|
|
12816
13009
|
//#endregion
|
|
12817
13010
|
//#region ../../packages/contracts/src/threadControl.ts
|
|
12818
13011
|
/**
|
|
@@ -14412,10 +14605,19 @@ const UserInputRankingInput = Schema$1.Struct({
|
|
|
14412
14605
|
requestId: ApprovalRequestId,
|
|
14413
14606
|
questionId: Schema$1.String
|
|
14414
14607
|
});
|
|
14415
|
-
|
|
14416
|
-
|
|
14417
|
-
|
|
14418
|
-
|
|
14608
|
+
/**
|
|
14609
|
+
* `probability` is the option's share of the ranker's distribution and
|
|
14610
|
+
* `confidence` how concentrated that distribution is. Both come from the
|
|
14611
|
+
* TypeSafe answer; neither is invented when the answer carries none.
|
|
14612
|
+
*/
|
|
14613
|
+
const UserInputRankingResult = Schema$1.Struct({
|
|
14614
|
+
confidence: Schema$1.Number,
|
|
14615
|
+
ranks: Schema$1.Array(Schema$1.Struct({
|
|
14616
|
+
optionIndex: Schema$1.Int,
|
|
14617
|
+
rank: Schema$1.Int,
|
|
14618
|
+
probability: Schema$1.Number
|
|
14619
|
+
}))
|
|
14620
|
+
});
|
|
14419
14621
|
var UserInputRankingError = class extends Schema$1.TaggedErrorClass()("UserInputRankingError", { detail: Schema$1.String }) {};
|
|
14420
14622
|
//#endregion
|
|
14421
14623
|
//#region ../../packages/contracts/src/providerSetup.ts
|
|
@@ -14928,6 +15130,7 @@ const WS_METHODS = {
|
|
|
14928
15130
|
hubSetSyncMode: "hub.setSyncMode",
|
|
14929
15131
|
hubSetShareMode: "hub.setShareMode",
|
|
14930
15132
|
hubMintToken: "hub.mintToken",
|
|
15133
|
+
hubAnalyticsQuery: "hub.analyticsQuery",
|
|
14931
15134
|
settingsSyncNow: "settingsSync.now",
|
|
14932
15135
|
storageCleanupPreview: "storageCleanup.preview",
|
|
14933
15136
|
storageCleanupSweep: "storageCleanup.sweep",
|
|
@@ -15141,6 +15344,19 @@ const WsWorktreeCleanupSweepRpc = Rpc.make(WS_METHODS.worktreeCleanupSweep, {
|
|
|
15141
15344
|
success: WorktreeCleanupPreview,
|
|
15142
15345
|
error: Schema$1.Union([EnvironmentAuthorizationError])
|
|
15143
15346
|
});
|
|
15347
|
+
/**
|
|
15348
|
+
* The account's own analytics facts, read through this server.
|
|
15349
|
+
*
|
|
15350
|
+
* The hub credential belongs to the environment, not to a browser tab, so the
|
|
15351
|
+
* client never holds it: it asks its own server, which is already the only
|
|
15352
|
+
* thing that can authenticate against the hub. Read-only, and the query is the
|
|
15353
|
+
* hub's own contract, so a filter the hub rejects is rejected here too.
|
|
15354
|
+
*/
|
|
15355
|
+
const WsHubAnalyticsQueryRpc = Rpc.make(WS_METHODS.hubAnalyticsQuery, {
|
|
15356
|
+
payload: HubAnalyticsQuery,
|
|
15357
|
+
success: HubAnalyticsPage,
|
|
15358
|
+
error: HubRpcError
|
|
15359
|
+
});
|
|
15144
15360
|
const WsHubGetSyncStatusRpc = Rpc.make(WS_METHODS.hubGetSyncStatus, {
|
|
15145
15361
|
payload: Schema$1.Struct({}),
|
|
15146
15362
|
success: AgentSyncStatus,
|
|
@@ -16092,7 +16308,7 @@ const WsSubscribeAuthAccessRpc = Rpc.make(WS_METHODS.subscribeAuthAccess, {
|
|
|
16092
16308
|
error: Schema$1.Union([AuthAccessStreamError, EnvironmentAuthorizationError]),
|
|
16093
16309
|
stream: true
|
|
16094
16310
|
});
|
|
16095
|
-
const WsRpcGroup = RpcGroup.make(WsDeviceGetStateRpc, WsDeviceListRpc, WsDeviceConfigureRpc, WsDeviceOpenRpc, WsDeviceCloseRpc, WsDeviceDetailRpc, WsDeviceActionRpc, WsSubscribeDevicesRpc, WsServerProbeRpc, WsServerGetConfigRpc, WsServerRefreshProvidersRpc, WsServerProviderAuthStartRpc, WsServerProviderAuthCompleteRpc, WsServerProviderAuthCancelRpc, WsServerProviderAuthLogoutRpc, WsServerProviderAuthSubscribeRpc, WsServerProviderInstallStartRpc, WsServerProviderInstallCancelRpc, WsServerProviderInstallRemoveRpc, WsServerProviderInstallSubscribeRpc, WsServerUpdateProviderRpc, WsServerUpdateServerRpc, WsServerUpsertKeybindingRpc, WsServerRemoveKeybindingRpc, WsServerGetSettingsRpc, WsServerGetFusionDefaultPromptsRpc, WsServerUpdateSettingsRpc, WsServerDiscoverSourceControlRpc, WsServerGetProviderUsageRpc, WsServerGetHostResourcesRpc, WsServerGetResourceTelemetryRpc, WsServerGetResourceTelemetryHistoryRpc, WsServerRetryResourceTelemetryRpc, WsSubscribeResourceTelemetryRpc, WsThreadHandoffRpc, WsUserInputRankRpc, WsBtwAskRpc, WsBtwCancelRpc, WsServerGetUsageSummaryRpc, WsServerGetTraceDiagnosticsRpc, WsServerGetProcessDiagnosticsRpc, WsServerGetProcessResourceHistoryRpc, WsServerSignalProcessRpc, WsPullRequestsListRpc, WsPullRequestsListStatsRpc, WsPullRequestsDetailRpc, WsPullRequestsStackRpc, WsPullRequestsActivityRpc, WsPullRequestsDiffFileContentsRpc, WsPullRequestsRunActionRpc, WsPullRequestsUpdateRpc, WsPullRequestsCommentRpc, WsPullRequestsUpdateCommentRpc, WsPullRequestsSubmitReviewRpc, WsPullRequestsReplyToThreadRpc, WsPullRequestsSetThreadResolutionRpc, WsPullRequestsSetReactionRpc, WsPullRequestsInvalidateRpc, WsPullRequestsReviewerCandidatesRpc, WsPullRequestsRequestReviewersRpc, WsSourceControlLookupRepositoryRpc, WsSourceControlCloneRepositoryRpc, WsSourceControlPublishRepositoryRpc, WsProjectsListEntriesRpc, WsProjectsReadFileRpc, WsProjectsSearchContentsRpc, WsProjectsSearchEntriesRpc, WsProjectsWriteFileRpc, WsShellOpenInEditorRpc, WsFilesystemBrowseRpc, WsAssetsCreateUrlRpc, WsSubscribeVcsStatusRpc, WsVcsPullRpc, WsVcsRefreshStatusRpc, WsGitRunStackedActionRpc, WsGitResolvePullRequestRpc, WsGitPreparePullRequestThreadRpc, WsVcsListRefsRpc, WsVcsCreateWorktreeRpc, WsVcsRemoveWorktreeRpc, WsVcsCreateRefRpc, WsVcsSwitchRefRpc, WsVcsInitRpc, WsReviewGetDiffPreviewRpc, WsTerminalOpenRpc, WsTerminalAttachRpc, WsTerminalWriteRpc, WsTerminalResizeRpc, WsTerminalClearRpc, WsTerminalRestartRpc, WsTerminalCloseRpc, WsSubscribeTerminalEventsRpc, WsSubscribeTerminalMetadataRpc, WsPreviewOpenRpc, WsPreviewNavigateRpc, WsPreviewResizeRpc, WsPreviewRefreshRpc, WsPreviewCloseRpc, WsPreviewListRpc, WsPreviewReportStatusRpc, WsComputerUseConnectRpc, WsComputerUseListRpc, WsComputerUseControlsRpc, WsComputerUseControlRpc, WsComputerUsePreviewRpc, WsComputerUseRespondRpc, WsPreviewAutomationConnectRpc, WsPreviewAutomationRespondRpc, WsPreviewAutomationFocusHostRpc, WsSubscribePreviewEventsRpc, WsSubscribeDiscoveredLocalServersRpc, WsSubscribeServerConfigRpc, WsSubscribeServerLifecycleRpc, WsSubscribeAuthAccessRpc, WsTasksListRpc, WsTasksGetRpc, WsTasksCreateRpc, WsTasksUpdateRpc, WsTasksDeleteRpc, WsTasksStartThreadRpc, WsFeedListRpc, WsFeedRefreshRpc, WsFeedSourcesListRpc, WsFeedSourcesUpsertRpc, WsFeedSourcesDeleteRpc, WsFeedMarkReadRpc, WsFeedCleanupRpc, WsSubscribeTasksRpc, WsStorageCleanupPreviewRpc, WsStorageCleanupSweepRpc, WsWorktreeCleanupPreviewRpc, WsWorktreeCleanupSweepRpc, WsHubGetSyncStatusRpc, WsHubConnectRpc, WsHubDisconnectRpc, WsHubSetSyncModeRpc, WsHubSetShareModeRpc, WsHubMintTokenRpc, WsSettingsSyncNowRpc, WsSkillsSyncRpc, WsSkillsPublishRpc, WsSkillsPublishAllRpc, WsSkillsUnpublishRpc, WsAssetsReadRpc, WsAssetsSaveRpc, WsAssetsDeleteRpc, WsAssetsCreateLocalRpc, WsAssetsRemoveLocalRpc, WsSkillRegistrySearchRpc, WsSkillRegistryFetchRpc, WsMcpListRpc, WsMcpSaveRpc, WsMcpRemoveRpc, WsMcpSetSecretRpc, WsMcpOAuthBeginRpc, WsMcpOAuthDisconnectRpc, WsTrackerStatusRpc, WsTrackerSetApiKeyRpc, WsSupervisorSecretStatusRpc, WsSupervisorSetApiKeyRpc, WsSupervisorClearApiKeyRpc, WsTrackerClearApiKeyRpc, WsOrchestrationDispatchCommandRpc, WsOrchestrationGetTurnDiffRpc, WsOrchestrationGetWorkflowScriptRpc, WsOrchestrationGetFullThreadDiffRpc, WsOrchestrationSearchThreadsRpc, WsOrchestrationGetArchivedShellSnapshotRpc, WsOrchestrationSubscribeShellRpc, WsOrchestrationSubscribeThreadRpc);
|
|
16311
|
+
const WsRpcGroup = RpcGroup.make(WsDeviceGetStateRpc, WsDeviceListRpc, WsDeviceConfigureRpc, WsDeviceOpenRpc, WsDeviceCloseRpc, WsDeviceDetailRpc, WsDeviceActionRpc, WsSubscribeDevicesRpc, WsServerProbeRpc, WsServerGetConfigRpc, WsServerRefreshProvidersRpc, WsServerProviderAuthStartRpc, WsServerProviderAuthCompleteRpc, WsServerProviderAuthCancelRpc, WsServerProviderAuthLogoutRpc, WsServerProviderAuthSubscribeRpc, WsServerProviderInstallStartRpc, WsServerProviderInstallCancelRpc, WsServerProviderInstallRemoveRpc, WsServerProviderInstallSubscribeRpc, WsServerUpdateProviderRpc, WsServerUpdateServerRpc, WsServerUpsertKeybindingRpc, WsServerRemoveKeybindingRpc, WsServerGetSettingsRpc, WsServerGetFusionDefaultPromptsRpc, WsServerUpdateSettingsRpc, WsServerDiscoverSourceControlRpc, WsServerGetProviderUsageRpc, WsServerGetHostResourcesRpc, WsServerGetResourceTelemetryRpc, WsServerGetResourceTelemetryHistoryRpc, WsServerRetryResourceTelemetryRpc, WsSubscribeResourceTelemetryRpc, WsThreadHandoffRpc, WsUserInputRankRpc, WsBtwAskRpc, WsBtwCancelRpc, WsServerGetUsageSummaryRpc, WsServerGetTraceDiagnosticsRpc, WsServerGetProcessDiagnosticsRpc, WsServerGetProcessResourceHistoryRpc, WsServerSignalProcessRpc, WsPullRequestsListRpc, WsPullRequestsListStatsRpc, WsPullRequestsDetailRpc, WsPullRequestsStackRpc, WsPullRequestsActivityRpc, WsPullRequestsDiffFileContentsRpc, WsPullRequestsRunActionRpc, WsPullRequestsUpdateRpc, WsPullRequestsCommentRpc, WsPullRequestsUpdateCommentRpc, WsPullRequestsSubmitReviewRpc, WsPullRequestsReplyToThreadRpc, WsPullRequestsSetThreadResolutionRpc, WsPullRequestsSetReactionRpc, WsPullRequestsInvalidateRpc, WsPullRequestsReviewerCandidatesRpc, WsPullRequestsRequestReviewersRpc, WsSourceControlLookupRepositoryRpc, WsSourceControlCloneRepositoryRpc, WsSourceControlPublishRepositoryRpc, WsProjectsListEntriesRpc, WsProjectsReadFileRpc, WsProjectsSearchContentsRpc, WsProjectsSearchEntriesRpc, WsProjectsWriteFileRpc, WsShellOpenInEditorRpc, WsFilesystemBrowseRpc, WsAssetsCreateUrlRpc, WsSubscribeVcsStatusRpc, WsVcsPullRpc, WsVcsRefreshStatusRpc, WsGitRunStackedActionRpc, WsGitResolvePullRequestRpc, WsGitPreparePullRequestThreadRpc, WsVcsListRefsRpc, WsVcsCreateWorktreeRpc, WsVcsRemoveWorktreeRpc, WsVcsCreateRefRpc, WsVcsSwitchRefRpc, WsVcsInitRpc, WsReviewGetDiffPreviewRpc, WsTerminalOpenRpc, WsTerminalAttachRpc, WsTerminalWriteRpc, WsTerminalResizeRpc, WsTerminalClearRpc, WsTerminalRestartRpc, WsTerminalCloseRpc, WsSubscribeTerminalEventsRpc, WsSubscribeTerminalMetadataRpc, WsPreviewOpenRpc, WsPreviewNavigateRpc, WsPreviewResizeRpc, WsPreviewRefreshRpc, WsPreviewCloseRpc, WsPreviewListRpc, WsPreviewReportStatusRpc, WsComputerUseConnectRpc, WsComputerUseListRpc, WsComputerUseControlsRpc, WsComputerUseControlRpc, WsComputerUsePreviewRpc, WsComputerUseRespondRpc, WsPreviewAutomationConnectRpc, WsPreviewAutomationRespondRpc, WsPreviewAutomationFocusHostRpc, WsSubscribePreviewEventsRpc, WsSubscribeDiscoveredLocalServersRpc, WsSubscribeServerConfigRpc, WsSubscribeServerLifecycleRpc, WsSubscribeAuthAccessRpc, WsTasksListRpc, WsTasksGetRpc, WsTasksCreateRpc, WsTasksUpdateRpc, WsTasksDeleteRpc, WsTasksStartThreadRpc, WsFeedListRpc, WsFeedRefreshRpc, WsFeedSourcesListRpc, WsFeedSourcesUpsertRpc, WsFeedSourcesDeleteRpc, WsFeedMarkReadRpc, WsFeedCleanupRpc, WsSubscribeTasksRpc, WsStorageCleanupPreviewRpc, WsStorageCleanupSweepRpc, WsWorktreeCleanupPreviewRpc, WsWorktreeCleanupSweepRpc, WsHubAnalyticsQueryRpc, WsHubGetSyncStatusRpc, WsHubConnectRpc, WsHubDisconnectRpc, WsHubSetSyncModeRpc, WsHubSetShareModeRpc, WsHubMintTokenRpc, WsSettingsSyncNowRpc, WsSkillsSyncRpc, WsSkillsPublishRpc, WsSkillsPublishAllRpc, WsSkillsUnpublishRpc, WsAssetsReadRpc, WsAssetsSaveRpc, WsAssetsDeleteRpc, WsAssetsCreateLocalRpc, WsAssetsRemoveLocalRpc, WsSkillRegistrySearchRpc, WsSkillRegistryFetchRpc, WsMcpListRpc, WsMcpSaveRpc, WsMcpRemoveRpc, WsMcpSetSecretRpc, WsMcpOAuthBeginRpc, WsMcpOAuthDisconnectRpc, WsTrackerStatusRpc, WsTrackerSetApiKeyRpc, WsSupervisorSecretStatusRpc, WsSupervisorSetApiKeyRpc, WsSupervisorClearApiKeyRpc, WsTrackerClearApiKeyRpc, WsOrchestrationDispatchCommandRpc, WsOrchestrationGetTurnDiffRpc, WsOrchestrationGetWorkflowScriptRpc, WsOrchestrationGetFullThreadDiffRpc, WsOrchestrationSearchThreadsRpc, WsOrchestrationGetArchivedShellSnapshotRpc, WsOrchestrationSubscribeShellRpc, WsOrchestrationSubscribeThreadRpc);
|
|
16096
16312
|
//#endregion
|
|
16097
16313
|
//#region ../../packages/shared/src/oauthScope.ts
|
|
16098
16314
|
const OAUTH_SCOPE_TOKEN = /^[\u0021\u0023-\u005b\u005d-\u007e]+$/u;
|
|
@@ -16256,7 +16472,7 @@ const make$120 = Effect.gen(function* () {
|
|
|
16256
16472
|
};
|
|
16257
16473
|
return EnvironmentAuthPolicy.of({ getDescriptor: () => Effect.succeed(descriptor).pipe(Effect.withSpan("EnvironmentAuthPolicy.getDescriptor")) });
|
|
16258
16474
|
});
|
|
16259
|
-
const layer$
|
|
16475
|
+
const layer$121 = Layer.effect(EnvironmentAuthPolicy, make$120);
|
|
16260
16476
|
//#endregion
|
|
16261
16477
|
//#region src/persistence/Errors.ts
|
|
16262
16478
|
function summarizeSchemaIssue(issue) {
|
|
@@ -16571,7 +16787,7 @@ const make$119 = Effect.gen(function* () {
|
|
|
16571
16787
|
setLastConnectedAt
|
|
16572
16788
|
};
|
|
16573
16789
|
});
|
|
16574
|
-
const layer$
|
|
16790
|
+
const layer$120 = Layer.effect(AuthSessionRepository, make$119);
|
|
16575
16791
|
//#endregion
|
|
16576
16792
|
//#region src/auth/ServerSecretStore.ts
|
|
16577
16793
|
const secretStoreErrorContext = {
|
|
@@ -16717,7 +16933,7 @@ const make$118 = Effect.gen(function* () {
|
|
|
16717
16933
|
const config = yield* ServerConfig$1;
|
|
16718
16934
|
return yield* makeForDirectory(config.secretsDir);
|
|
16719
16935
|
});
|
|
16720
|
-
const layer$
|
|
16936
|
+
const layer$119 = Layer.effect(ServerSecretStore, make$118);
|
|
16721
16937
|
//#endregion
|
|
16722
16938
|
//#region src/auth/SessionStore.ts
|
|
16723
16939
|
var MalformedSessionTokenError = class extends Schema$1.TaggedErrorClass()("MalformedSessionTokenError", {}) {
|
|
@@ -17270,7 +17486,7 @@ const make$117 = Effect.gen(function* () {
|
|
|
17270
17486
|
markDisconnected
|
|
17271
17487
|
});
|
|
17272
17488
|
});
|
|
17273
|
-
const layer$
|
|
17489
|
+
const layer$118 = Layer.effect(SessionStore, make$117).pipe(Layer.provideMerge(layer$120));
|
|
17274
17490
|
//#endregion
|
|
17275
17491
|
//#region src/persistence/AuthPairingLinks.ts
|
|
17276
17492
|
const AuthPairingLinkRecord = Schema$1.Struct({
|
|
@@ -17466,7 +17682,7 @@ const make$116 = Effect.gen(function* () {
|
|
|
17466
17682
|
getByCredential
|
|
17467
17683
|
};
|
|
17468
17684
|
});
|
|
17469
|
-
const layer$
|
|
17685
|
+
const layer$117 = Layer.effect(AuthPairingLinkRepository, make$116);
|
|
17470
17686
|
//#endregion
|
|
17471
17687
|
//#region src/auth/PairingGrantStore.ts
|
|
17472
17688
|
var UnknownBootstrapCredentialError = class extends Schema$1.TaggedErrorClass()("UnknownBootstrapCredentialError", {}) {
|
|
@@ -17759,7 +17975,7 @@ const make$115 = Effect.gen(function* () {
|
|
|
17759
17975
|
consume
|
|
17760
17976
|
});
|
|
17761
17977
|
});
|
|
17762
|
-
const layer$
|
|
17978
|
+
const layer$116 = Layer.effect(PairingGrantStore, make$115).pipe(Layer.provideMerge(layer$117));
|
|
17763
17979
|
//#endregion
|
|
17764
17980
|
//#region src/persistence/DatabaseSnapshot.ts
|
|
17765
17981
|
/**
|
|
@@ -17901,6 +18117,21 @@ var _060_ThreadPairTriageCursor_default = Effect.gen(function* () {
|
|
|
17901
18117
|
yield* (yield* SqlClient.SqlClient)`ALTER TABLE thread_pairs ADD COLUMN triaged_from_sequence INTEGER`;
|
|
17902
18118
|
});
|
|
17903
18119
|
//#endregion
|
|
18120
|
+
//#region src/persistence/Migrations/061_AnalyticsFactOutbox.ts
|
|
18121
|
+
var _061_AnalyticsFactOutbox_default = Effect.gen(function* () {
|
|
18122
|
+
const sql = yield* SqlClient.SqlClient;
|
|
18123
|
+
yield* sql`CREATE TABLE analytics_fact_outbox (
|
|
18124
|
+
event_id TEXT PRIMARY KEY, kind TEXT NOT NULL, version INTEGER NOT NULL,
|
|
18125
|
+
occurred_at TEXT NOT NULL, payload TEXT NOT NULL,
|
|
18126
|
+
pending INTEGER NOT NULL DEFAULT 1, created_at INTEGER NOT NULL
|
|
18127
|
+
)`;
|
|
18128
|
+
yield* sql`CREATE INDEX analytics_fact_outbox_pending
|
|
18129
|
+
ON analytics_fact_outbox(pending, created_at, event_id)`;
|
|
18130
|
+
yield* sql`CREATE TABLE analytics_fact_cursors (
|
|
18131
|
+
source TEXT PRIMARY KEY, after_sequence INTEGER NOT NULL
|
|
18132
|
+
)`;
|
|
18133
|
+
});
|
|
18134
|
+
//#endregion
|
|
17904
18135
|
//#region src/persistence/Migrations/001_OrchestrationEvents.ts
|
|
17905
18136
|
var _001_OrchestrationEvents_default = Effect.gen(function* () {
|
|
17906
18137
|
const sql = yield* SqlClient.SqlClient;
|
|
@@ -20082,6 +20313,11 @@ const migrationEntries = [
|
|
|
20082
20313
|
60,
|
|
20083
20314
|
"ThreadPairTriageCursor",
|
|
20084
20315
|
_060_ThreadPairTriageCursor_default
|
|
20316
|
+
],
|
|
20317
|
+
[
|
|
20318
|
+
61,
|
|
20319
|
+
"AnalyticsFactOutbox",
|
|
20320
|
+
_061_AnalyticsFactOutbox_default
|
|
20085
20321
|
]
|
|
20086
20322
|
];
|
|
20087
20323
|
const makeMigrationLoader = (throughId) => Migrator.fromRecord(Object.fromEntries(migrationEntries.filter(([id]) => throughId === void 0 || id <= throughId).map(([id, name, migration]) => [`${id}_${name}`, migration])));
|
|
@@ -20429,22 +20665,22 @@ function classifyToolCategory(input) {
|
|
|
20429
20665
|
if (normalized.includes("image")) return "image_view";
|
|
20430
20666
|
return "tool";
|
|
20431
20667
|
}
|
|
20432
|
-
function asRecord$
|
|
20668
|
+
function asRecord$12(value) {
|
|
20433
20669
|
return value !== null && typeof value === "object" && !Array.isArray(value) ? value : void 0;
|
|
20434
20670
|
}
|
|
20435
20671
|
/** Classify from a runtime item payload's `data` (`{ toolName, input }`). */
|
|
20436
20672
|
function classifyToolCategoryFromToolData(data) {
|
|
20437
|
-
const record = asRecord$
|
|
20673
|
+
const record = asRecord$12(data);
|
|
20438
20674
|
const toolName = record?.toolName;
|
|
20439
20675
|
if (typeof toolName !== "string" || toolName.trim().length === 0) return;
|
|
20440
20676
|
return classifyToolCategory({
|
|
20441
20677
|
toolName,
|
|
20442
|
-
toolInput: asRecord$
|
|
20678
|
+
toolInput: asRecord$12(record?.input)
|
|
20443
20679
|
});
|
|
20444
20680
|
}
|
|
20445
20681
|
//#endregion
|
|
20446
20682
|
//#region src/orchestration/ActivityPayloadProjection.ts
|
|
20447
|
-
function asRecord$
|
|
20683
|
+
function asRecord$11(value) {
|
|
20448
20684
|
return value !== null && typeof value === "object" && !Array.isArray(value) ? value : null;
|
|
20449
20685
|
}
|
|
20450
20686
|
function asTrimmedString$2(value) {
|
|
@@ -20474,7 +20710,7 @@ function compactMcpRecord(record) {
|
|
|
20474
20710
|
const entries = Object.entries(record);
|
|
20475
20711
|
const projected = {};
|
|
20476
20712
|
for (const [key, value] of entries.slice(0, MCP_TOOL_ACTIVITY_FIELD_LIMIT)) {
|
|
20477
|
-
const nestedItem = key === "item" ? asRecord$
|
|
20713
|
+
const nestedItem = key === "item" ? asRecord$11(value) : null;
|
|
20478
20714
|
projected[key] = nestedItem === null ? compactMcpField(value) : compactMcpRecord(nestedItem);
|
|
20479
20715
|
}
|
|
20480
20716
|
if (entries.length > MCP_TOOL_ACTIVITY_FIELD_LIMIT) projected.truncatedFields = entries.length - MCP_TOOL_ACTIVITY_FIELD_LIMIT;
|
|
@@ -20500,7 +20736,7 @@ function collectChangedFiles(value, target, seen, depth) {
|
|
|
20500
20736
|
}
|
|
20501
20737
|
return;
|
|
20502
20738
|
}
|
|
20503
|
-
const record = asRecord$
|
|
20739
|
+
const record = asRecord$11(value);
|
|
20504
20740
|
if (!record) return;
|
|
20505
20741
|
pushChangedFile(target, seen, record.path);
|
|
20506
20742
|
pushChangedFile(target, seen, record.filePath);
|
|
@@ -20526,13 +20762,13 @@ function collectChangedFiles(value, target, seen, depth) {
|
|
|
20526
20762
|
}
|
|
20527
20763
|
}
|
|
20528
20764
|
function projectCommandData(data) {
|
|
20529
|
-
const item = asRecord$
|
|
20765
|
+
const item = asRecord$11(data.item);
|
|
20530
20766
|
if (!item) return;
|
|
20531
20767
|
const projectedItem = {};
|
|
20532
20768
|
if ("command" in item) projectedItem.command = item.command;
|
|
20533
|
-
const input = asRecord$
|
|
20769
|
+
const input = asRecord$11(item.input);
|
|
20534
20770
|
if (input && "command" in input) projectedItem.input = { command: input.command };
|
|
20535
|
-
const result = asRecord$
|
|
20771
|
+
const result = asRecord$11(item.result);
|
|
20536
20772
|
if (result && "command" in result) projectedItem.result = { command: result.command };
|
|
20537
20773
|
return Object.keys(projectedItem).length > 0 ? projectedItem : void 0;
|
|
20538
20774
|
}
|
|
@@ -20556,7 +20792,7 @@ function summarizeToolTextOutput(value) {
|
|
|
20556
20792
|
return meaningfulLineCount > 1 ? `${meaningfulLineCount.toLocaleString()} lines` : null;
|
|
20557
20793
|
}
|
|
20558
20794
|
function projectRawOutput(value) {
|
|
20559
|
-
const rawOutput = asRecord$
|
|
20795
|
+
const rawOutput = asRecord$11(value);
|
|
20560
20796
|
if (!rawOutput) return;
|
|
20561
20797
|
if (typeof rawOutput.totalFiles === "number" && Number.isFinite(rawOutput.totalFiles)) return {
|
|
20562
20798
|
totalFiles: rawOutput.totalFiles,
|
|
@@ -20579,8 +20815,8 @@ function projectRawOutput(value) {
|
|
|
20579
20815
|
* tool outputs do not accumulate twice in the event store and activity projection.
|
|
20580
20816
|
*/
|
|
20581
20817
|
function projectActivityPayload(activity) {
|
|
20582
|
-
const payload = asRecord$
|
|
20583
|
-
const data = asRecord$
|
|
20818
|
+
const payload = asRecord$11(activity.payload);
|
|
20819
|
+
const data = asRecord$11(payload?.data);
|
|
20584
20820
|
if (!payload || !data) return activity;
|
|
20585
20821
|
if (payload.itemType === "mcp_tool_call") {
|
|
20586
20822
|
const projectedMcpData = projectMcpToolCallData(data);
|
|
@@ -20597,7 +20833,7 @@ function projectActivityPayload(activity) {
|
|
|
20597
20833
|
const item = projectCommandData(data);
|
|
20598
20834
|
if (item) projectedData.item = item;
|
|
20599
20835
|
if ("command" in data) projectedData.command = data.command;
|
|
20600
|
-
const input = asRecord$
|
|
20836
|
+
const input = asRecord$11(data.input);
|
|
20601
20837
|
if (input) {
|
|
20602
20838
|
const projectedInput = {};
|
|
20603
20839
|
if ("command" in input) projectedInput.command = input.command;
|
|
@@ -20637,7 +20873,7 @@ function projectActivityPayload(activity) {
|
|
|
20637
20873
|
*/
|
|
20638
20874
|
function isResolvableContextWindowActivity(activity) {
|
|
20639
20875
|
if (activity.kind !== "context-window.updated") return false;
|
|
20640
|
-
const usedTokens = asRecord$
|
|
20876
|
+
const usedTokens = asRecord$11(activity.payload)?.usedTokens;
|
|
20641
20877
|
return typeof usedTokens === "number" && Number.isFinite(usedTokens) && usedTokens >= 0;
|
|
20642
20878
|
}
|
|
20643
20879
|
/**
|
|
@@ -20653,7 +20889,7 @@ function isResolvableContextWindowActivity(activity) {
|
|
|
20653
20889
|
* client.
|
|
20654
20890
|
*/
|
|
20655
20891
|
function withoutContextWindowBreakdown$1(activity) {
|
|
20656
|
-
const payload = asRecord$
|
|
20892
|
+
const payload = asRecord$11(activity.payload);
|
|
20657
20893
|
if (!payload || payload.breakdown === void 0) return activity;
|
|
20658
20894
|
const { breakdown: _breakdown, ...rest } = payload;
|
|
20659
20895
|
return {
|
|
@@ -20668,7 +20904,7 @@ function dropStaleContextWindowActivities(activities) {
|
|
|
20668
20904
|
const retainedIndexes = new Set(latestIndexByTurn.values());
|
|
20669
20905
|
let breakdownIndex = null;
|
|
20670
20906
|
for (const index of retainedIndexes) {
|
|
20671
|
-
if (asRecord$
|
|
20907
|
+
if (asRecord$11(activities[index].payload)?.breakdown === void 0) continue;
|
|
20672
20908
|
if (breakdownIndex === null || index > breakdownIndex) breakdownIndex = index;
|
|
20673
20909
|
}
|
|
20674
20910
|
return activities.flatMap((activity, index) => {
|
|
@@ -20678,12 +20914,12 @@ function dropStaleContextWindowActivities(activities) {
|
|
|
20678
20914
|
});
|
|
20679
20915
|
}
|
|
20680
20916
|
function toolLifecycleIdentity(activity) {
|
|
20681
|
-
return asTrimmedString$2(asRecord$
|
|
20917
|
+
return asTrimmedString$2(asRecord$11(asRecord$11(activity.payload)?.data)?.toolCallId);
|
|
20682
20918
|
}
|
|
20683
20919
|
/** Clients retain fields missing from a completion when folding an update. */
|
|
20684
20920
|
function completionPreservesUpdate(update, completion) {
|
|
20685
|
-
const updatePayload = asRecord$
|
|
20686
|
-
const completionPayload = asRecord$
|
|
20921
|
+
const updatePayload = asRecord$11(update.payload);
|
|
20922
|
+
const completionPayload = asRecord$11(completion.payload);
|
|
20687
20923
|
if (!updatePayload || !completionPayload) return false;
|
|
20688
20924
|
return Object.entries(updatePayload).every(([key, value]) => key === "status" || NodeUtil.isDeepStrictEqual(value, completionPayload[key]));
|
|
20689
20925
|
}
|
|
@@ -20722,13 +20958,13 @@ function dropSupersededEvidenceSummaries(activities) {
|
|
|
20722
20958
|
for (let index = 0; index < activities.length; index += 1) {
|
|
20723
20959
|
const activity = activities[index];
|
|
20724
20960
|
if (activity.kind !== "fusion.evidence.summary") continue;
|
|
20725
|
-
const watchedThreadId = asRecord$
|
|
20961
|
+
const watchedThreadId = asRecord$11(activity.payload)?.watchedThreadId;
|
|
20726
20962
|
if (typeof watchedThreadId === "string") latestIndexByWatched.set(watchedThreadId, index);
|
|
20727
20963
|
}
|
|
20728
20964
|
if (latestIndexByWatched.size === 0) return activities;
|
|
20729
20965
|
return activities.map((activity, index) => {
|
|
20730
20966
|
if (activity.kind !== "fusion.evidence.summary") return activity;
|
|
20731
|
-
const payload = asRecord$
|
|
20967
|
+
const payload = asRecord$11(activity.payload);
|
|
20732
20968
|
const watchedThreadId = payload?.watchedThreadId;
|
|
20733
20969
|
if (!payload || typeof watchedThreadId !== "string" || latestIndexByWatched.get(watchedThreadId) === index || payload.summary === null || payload.summary === void 0) return activity;
|
|
20734
20970
|
return {
|
|
@@ -21339,9 +21575,9 @@ const make$114 = Effect.gen(function* () {
|
|
|
21339
21575
|
issueStartupPairingUrl
|
|
21340
21576
|
});
|
|
21341
21577
|
});
|
|
21342
|
-
const layer$
|
|
21343
|
-
const storageLayer = Layer.mergeAll(layer$
|
|
21344
|
-
const runtimeLayer = layer$
|
|
21578
|
+
const layer$115 = Layer.effect(EnvironmentAuth, make$114).pipe(Layer.provideMerge(layer$116), Layer.provideMerge(layer$118), Layer.provideMerge(layer$121));
|
|
21579
|
+
const storageLayer = Layer.mergeAll(layer$119, layerConfig);
|
|
21580
|
+
const runtimeLayer = layer$115.pipe(Layer.provideMerge(storageLayer));
|
|
21345
21581
|
//#endregion
|
|
21346
21582
|
//#region ../../packages/shared/src/Struct.ts
|
|
21347
21583
|
function deepMerge(current, patch) {
|
|
@@ -23189,7 +23425,7 @@ const runWithEnvironmentAuth = (flags, run, options) => Effect.gen(function* ()
|
|
|
23189
23425
|
}
|
|
23190
23426
|
return yield* Effect.gen(function* () {
|
|
23191
23427
|
return yield* run(yield* EnvironmentAuth);
|
|
23192
|
-
}).pipe(Effect.provide(Layer.mergeAll(runtimeLayer).pipe(Layer.provide(layer$
|
|
23428
|
+
}).pipe(Effect.provide(Layer.mergeAll(runtimeLayer).pipe(Layer.provide(layer$122(config)), Layer.provide(Layer.succeed(References.MinimumLogLevel, minimumLogLevel)))));
|
|
23193
23429
|
});
|
|
23194
23430
|
const ttlFlag = Flag.string("ttl").pipe(Flag.withSchema(DurationFromString), Flag.withDescription("TTL, for example `5m`, `1h`, `30d`, or `15 minutes`."), Flag.optional);
|
|
23195
23431
|
const jsonFlag = Flag.boolean("json").pipe(Flag.withDescription("Emit JSON instead of human-readable output."), Flag.withDefault(false));
|
|
@@ -23349,7 +23585,7 @@ const make$113 = Effect.fn("HubAuthThrottle.make")(function* (limits = DEFAULT_L
|
|
|
23349
23585
|
})
|
|
23350
23586
|
});
|
|
23351
23587
|
});
|
|
23352
|
-
const layer$
|
|
23588
|
+
const layer$114 = Layer.effect(HubAuthThrottle, make$113());
|
|
23353
23589
|
//#endregion
|
|
23354
23590
|
//#region src/hub/HubAuth.ts
|
|
23355
23591
|
/**
|
|
@@ -24122,6 +24358,88 @@ const makeTaskRepository = Effect.gen(function* () {
|
|
|
24122
24358
|
});
|
|
24123
24359
|
const TaskRepositoryLive = Layer.effect(TaskRepository, makeTaskRepository);
|
|
24124
24360
|
//#endregion
|
|
24361
|
+
//#region src/hub/HubClientCors.ts
|
|
24362
|
+
const MAX_ORIGIN_CHARS = 2048;
|
|
24363
|
+
const DESKTOP_RENDERER_ORIGINS$1 = /* @__PURE__ */ new Set(["p4code://app", "p4code-dev://app"]);
|
|
24364
|
+
const MAX_PREFLIGHT_HEADER_CHARS = 128;
|
|
24365
|
+
const ALLOWED_HEADERS = /* @__PURE__ */ new Set([
|
|
24366
|
+
"authorization",
|
|
24367
|
+
"content-type",
|
|
24368
|
+
"accept"
|
|
24369
|
+
]);
|
|
24370
|
+
const CLIENT_ROUTES = {
|
|
24371
|
+
"/v1/client-enrollments/exchange": ["POST"],
|
|
24372
|
+
"/v1/clients/me": ["GET", "DELETE"],
|
|
24373
|
+
"/v1/clients/me/events": ["GET"],
|
|
24374
|
+
"/v1/clients/me/socket-ticket": ["POST"],
|
|
24375
|
+
"/v1/clients/me/asset-grant": ["POST"],
|
|
24376
|
+
"/v1/clients/me/notifications": [
|
|
24377
|
+
"GET",
|
|
24378
|
+
"PUT",
|
|
24379
|
+
"DELETE"
|
|
24380
|
+
],
|
|
24381
|
+
"/v1/clients/me/notifications/preferences": ["PUT"],
|
|
24382
|
+
"/v1/clients/me/live-activities": ["PUT", "DELETE"]
|
|
24383
|
+
};
|
|
24384
|
+
const routeMethods = (path) => Object.hasOwn(CLIENT_ROUTES, path) ? CLIENT_ROUTES[path] : void 0;
|
|
24385
|
+
const denied$2 = () => HttpServerResponse.jsonUnsafe({ error: "invalid_client_origin" }, {
|
|
24386
|
+
status: 403,
|
|
24387
|
+
headers: {
|
|
24388
|
+
"cache-control": "no-store",
|
|
24389
|
+
vary: "Origin"
|
|
24390
|
+
}
|
|
24391
|
+
});
|
|
24392
|
+
/** Arbitrary self-hosted browser clients are allowed; authentication uses explicit credentials, never cookies. */
|
|
24393
|
+
function hubClientCorsOrigin(value) {
|
|
24394
|
+
if (DESKTOP_RENDERER_ORIGINS$1.has(value)) return value;
|
|
24395
|
+
if (value.length > MAX_ORIGIN_CHARS) return void 0;
|
|
24396
|
+
try {
|
|
24397
|
+
const url = new URL(value);
|
|
24398
|
+
if (url.protocol !== "https:" && url.protocol !== "http:" || value !== url.origin || url.username || url.password) return void 0;
|
|
24399
|
+
return url.origin;
|
|
24400
|
+
} catch {
|
|
24401
|
+
return;
|
|
24402
|
+
}
|
|
24403
|
+
}
|
|
24404
|
+
/** Wrap only client-authenticated routes, after their generic failure mapping. */
|
|
24405
|
+
const withHubClientCors = (operation) => Effect.gen(function* () {
|
|
24406
|
+
const request = yield* HttpServerRequest.HttpServerRequest;
|
|
24407
|
+
const path = request.url.split("?")[0];
|
|
24408
|
+
if (!routeMethods(path)?.includes(request.method)) return denied$2();
|
|
24409
|
+
const raw = request.headers.origin;
|
|
24410
|
+
if (raw === void 0) return yield* operation;
|
|
24411
|
+
const origin = hubClientCorsOrigin(raw);
|
|
24412
|
+
if (!origin) return denied$2();
|
|
24413
|
+
const response = yield* operation;
|
|
24414
|
+
return response.pipe(HttpServerResponse.setHeaders({
|
|
24415
|
+
"access-control-allow-origin": origin,
|
|
24416
|
+
"access-control-expose-headers": "retry-after",
|
|
24417
|
+
"cache-control": "no-store",
|
|
24418
|
+
vary: response.headers.vary ? `${response.headers.vary}, Origin` : "Origin"
|
|
24419
|
+
}));
|
|
24420
|
+
});
|
|
24421
|
+
const hubClientPreflight = Effect.gen(function* () {
|
|
24422
|
+
const request = yield* HttpServerRequest.HttpServerRequest;
|
|
24423
|
+
const origin = hubClientCorsOrigin(request.headers.origin ?? "");
|
|
24424
|
+
const methods = routeMethods(request.url);
|
|
24425
|
+
const method = request.headers["access-control-request-method"];
|
|
24426
|
+
const raw = request.headers["access-control-request-headers"] ?? "";
|
|
24427
|
+
if (raw.length > MAX_PREFLIGHT_HEADER_CHARS) return denied$2();
|
|
24428
|
+
const headers = raw === "" ? [] : raw.split(",").map((value) => value.trim().toLowerCase());
|
|
24429
|
+
if (!origin || !methods || !method || !methods.includes(method) || raw.length > MAX_PREFLIGHT_HEADER_CHARS || headers.length > ALLOWED_HEADERS.size || new Set(headers).size !== headers.length || headers.some((value) => !ALLOWED_HEADERS.has(value))) return denied$2();
|
|
24430
|
+
return HttpServerResponse.empty({
|
|
24431
|
+
status: 204,
|
|
24432
|
+
headers: {
|
|
24433
|
+
"access-control-allow-origin": origin,
|
|
24434
|
+
"access-control-allow-methods": methods.join(", "),
|
|
24435
|
+
"access-control-allow-headers": "authorization, content-type, accept",
|
|
24436
|
+
"cache-control": "no-store",
|
|
24437
|
+
vary: "Origin, Access-Control-Request-Method, Access-Control-Request-Headers"
|
|
24438
|
+
}
|
|
24439
|
+
});
|
|
24440
|
+
});
|
|
24441
|
+
const layer$113 = Layer.mergeAll(Layer.empty, ...Object.keys(CLIENT_ROUTES).map((path) => HttpRouter.add("OPTIONS", path, hubClientPreflight)));
|
|
24442
|
+
//#endregion
|
|
24125
24443
|
//#region src/hub/HubClientRevocations.ts
|
|
24126
24444
|
var HubClientRevocations = class extends Context.Service()("@p4code/cli/hub/HubClientRevocations") {};
|
|
24127
24445
|
/** Coalesced invalidations carry no identity or credential; consumers recheck durable state. */
|
|
@@ -24282,88 +24600,6 @@ const signOutHubClient = Effect.fn("HubClients.signOut")(function* (credential)
|
|
|
24282
24600
|
WHERE id = ${principal.clientId} AND credential_hash = ${hashHubToken(credential)}`;
|
|
24283
24601
|
})).pipe(Effect.ensuring(notifications.changed));
|
|
24284
24602
|
});
|
|
24285
|
-
//#endregion
|
|
24286
|
-
//#region src/hub/HubClientCors.ts
|
|
24287
|
-
const MAX_ORIGIN_CHARS = 2048;
|
|
24288
|
-
const DESKTOP_RENDERER_ORIGINS$1 = /* @__PURE__ */ new Set(["p4code://app", "p4code-dev://app"]);
|
|
24289
|
-
const MAX_PREFLIGHT_HEADER_CHARS = 128;
|
|
24290
|
-
const ALLOWED_HEADERS = /* @__PURE__ */ new Set([
|
|
24291
|
-
"authorization",
|
|
24292
|
-
"content-type",
|
|
24293
|
-
"accept"
|
|
24294
|
-
]);
|
|
24295
|
-
const CLIENT_ROUTES = {
|
|
24296
|
-
"/v1/client-enrollments/exchange": ["POST"],
|
|
24297
|
-
"/v1/clients/me": ["GET", "DELETE"],
|
|
24298
|
-
"/v1/clients/me/events": ["GET"],
|
|
24299
|
-
"/v1/clients/me/socket-ticket": ["POST"],
|
|
24300
|
-
"/v1/clients/me/asset-grant": ["POST"],
|
|
24301
|
-
"/v1/clients/me/notifications": [
|
|
24302
|
-
"GET",
|
|
24303
|
-
"PUT",
|
|
24304
|
-
"DELETE"
|
|
24305
|
-
],
|
|
24306
|
-
"/v1/clients/me/notifications/preferences": ["PUT"],
|
|
24307
|
-
"/v1/clients/me/live-activities": ["PUT", "DELETE"]
|
|
24308
|
-
};
|
|
24309
|
-
const routeMethods = (path) => Object.hasOwn(CLIENT_ROUTES, path) ? CLIENT_ROUTES[path] : void 0;
|
|
24310
|
-
const denied$2 = () => HttpServerResponse.jsonUnsafe({ error: "invalid_client_origin" }, {
|
|
24311
|
-
status: 403,
|
|
24312
|
-
headers: {
|
|
24313
|
-
"cache-control": "no-store",
|
|
24314
|
-
vary: "Origin"
|
|
24315
|
-
}
|
|
24316
|
-
});
|
|
24317
|
-
/** Arbitrary self-hosted browser clients are allowed; authentication uses explicit credentials, never cookies. */
|
|
24318
|
-
function hubClientCorsOrigin(value) {
|
|
24319
|
-
if (DESKTOP_RENDERER_ORIGINS$1.has(value)) return value;
|
|
24320
|
-
if (value.length > MAX_ORIGIN_CHARS) return void 0;
|
|
24321
|
-
try {
|
|
24322
|
-
const url = new URL(value);
|
|
24323
|
-
if (url.protocol !== "https:" && url.protocol !== "http:" || value !== url.origin || url.username || url.password) return void 0;
|
|
24324
|
-
return url.origin;
|
|
24325
|
-
} catch {
|
|
24326
|
-
return;
|
|
24327
|
-
}
|
|
24328
|
-
}
|
|
24329
|
-
/** Wrap only client-authenticated routes, after their generic failure mapping. */
|
|
24330
|
-
const withHubClientCors = (operation) => Effect.gen(function* () {
|
|
24331
|
-
const request = yield* HttpServerRequest.HttpServerRequest;
|
|
24332
|
-
const path = request.url.split("?")[0];
|
|
24333
|
-
if (!routeMethods(path)?.includes(request.method)) return denied$2();
|
|
24334
|
-
const raw = request.headers.origin;
|
|
24335
|
-
if (raw === void 0) return yield* operation;
|
|
24336
|
-
const origin = hubClientCorsOrigin(raw);
|
|
24337
|
-
if (!origin) return denied$2();
|
|
24338
|
-
const response = yield* operation;
|
|
24339
|
-
return response.pipe(HttpServerResponse.setHeaders({
|
|
24340
|
-
"access-control-allow-origin": origin,
|
|
24341
|
-
"access-control-expose-headers": "retry-after",
|
|
24342
|
-
"cache-control": "no-store",
|
|
24343
|
-
vary: response.headers.vary ? `${response.headers.vary}, Origin` : "Origin"
|
|
24344
|
-
}));
|
|
24345
|
-
});
|
|
24346
|
-
const hubClientPreflight = Effect.gen(function* () {
|
|
24347
|
-
const request = yield* HttpServerRequest.HttpServerRequest;
|
|
24348
|
-
const origin = hubClientCorsOrigin(request.headers.origin ?? "");
|
|
24349
|
-
const methods = routeMethods(request.url);
|
|
24350
|
-
const method = request.headers["access-control-request-method"];
|
|
24351
|
-
const raw = request.headers["access-control-request-headers"] ?? "";
|
|
24352
|
-
if (raw.length > MAX_PREFLIGHT_HEADER_CHARS) return denied$2();
|
|
24353
|
-
const headers = raw === "" ? [] : raw.split(",").map((value) => value.trim().toLowerCase());
|
|
24354
|
-
if (!origin || !methods || !method || !methods.includes(method) || raw.length > MAX_PREFLIGHT_HEADER_CHARS || headers.length > ALLOWED_HEADERS.size || new Set(headers).size !== headers.length || headers.some((value) => !ALLOWED_HEADERS.has(value))) return denied$2();
|
|
24355
|
-
return HttpServerResponse.empty({
|
|
24356
|
-
status: 204,
|
|
24357
|
-
headers: {
|
|
24358
|
-
"access-control-allow-origin": origin,
|
|
24359
|
-
"access-control-allow-methods": methods.join(", "),
|
|
24360
|
-
"access-control-allow-headers": "authorization, content-type, accept",
|
|
24361
|
-
"cache-control": "no-store",
|
|
24362
|
-
vary: "Origin, Access-Control-Request-Method, Access-Control-Request-Headers"
|
|
24363
|
-
}
|
|
24364
|
-
});
|
|
24365
|
-
});
|
|
24366
|
-
const layer$111 = Layer.mergeAll(Layer.empty, ...Object.keys(CLIENT_ROUTES).map((path) => HttpRouter.add("OPTIONS", path, hubClientPreflight)));
|
|
24367
24603
|
const HUB_TUNNEL_MAX_FRAME_PAYLOAD_BYTES = 64 * 1024;
|
|
24368
24604
|
const HUB_TUNNEL_MAX_MESSAGE_BYTES = 128 * 1024 * 1024;
|
|
24369
24605
|
const HUB_TUNNEL_FRAME_MAGIC = 1345606705;
|
|
@@ -24650,7 +24886,7 @@ var HubTunnelConnectorBudget = class {
|
|
|
24650
24886
|
const HUB_TUNNEL_RETIRE_TIMEOUT_MS = 1e4;
|
|
24651
24887
|
var HubTunnelUnavailable = class extends Data.TaggedError("HubTunnelUnavailable") {};
|
|
24652
24888
|
var HubTunnelRegistry = class extends Context.Service()("@p4code/cli/hub/HubTunnelRegistry") {};
|
|
24653
|
-
const layer$
|
|
24889
|
+
const layer$111 = Layer.effect(HubTunnelRegistry, Effect.gen(function* () {
|
|
24654
24890
|
const config = yield* HubConfig;
|
|
24655
24891
|
const lock = yield* Semaphore.make(1);
|
|
24656
24892
|
const entries = /* @__PURE__ */ new Map();
|
|
@@ -24903,7 +25139,7 @@ const makeHubClientSocketTickets = Effect.gen(function* () {
|
|
|
24903
25139
|
};
|
|
24904
25140
|
});
|
|
24905
25141
|
var HubClientSocketTickets = class extends Context.Service()("@p4code/cli/hub/HubClientSocketTickets") {};
|
|
24906
|
-
const layer$
|
|
25142
|
+
const layer$110 = Layer.effect(HubClientSocketTickets, makeHubClientSocketTickets);
|
|
24907
25143
|
//#endregion
|
|
24908
25144
|
//#region src/hub/HubClientLease.ts
|
|
24909
25145
|
/** Other Hub processes sharing the database are observed within this interval. */
|
|
@@ -24992,7 +25228,7 @@ const readBody$1 = Effect.fn("HubClientRoutes.readBody")(function* (schema, maxi
|
|
|
24992
25228
|
}) : request.stream.pipe(Stream.mapError(() => new InvalidClientRequest()))).pipe(Stream.runFoldEffect(() => Buffer.alloc(0), (buffer, chunk) => buffer.byteLength + chunk.byteLength > maximumBytes ? Effect.fail(new InvalidClientRequest()) : Effect.succeed(Buffer.concat([buffer, chunk]))));
|
|
24993
25229
|
return yield* Schema$1.decodeUnknownEffect(Schema$1.fromJsonString(schema))(body.toString("utf8"), { onExcessProperty: "error" }).pipe(Effect.mapError(() => new InvalidClientRequest()));
|
|
24994
25230
|
});
|
|
24995
|
-
const json$
|
|
25231
|
+
const json$2 = (body) => HttpServerResponse.jsonUnsafe(body, { headers: { "cache-control": "no-store" } });
|
|
24996
25232
|
const handle$1 = (operation, accountingOwner) => operation.pipe(Effect.timeout(HUB_CLIENT_REQUEST_TIMEOUT_MS), Effect.catch((error) => Effect.gen(function* () {
|
|
24997
25233
|
if (error instanceof HubThrottledError) return hubThrottledResponse(error.retryAfterSeconds);
|
|
24998
25234
|
if (error instanceof HubUnauthorizedError) {
|
|
@@ -25013,15 +25249,15 @@ const handle$1 = (operation, accountingOwner) => operation.pipe(Effect.timeout(H
|
|
|
25013
25249
|
const issue$1 = handle$1(Effect.gen(function* () {
|
|
25014
25250
|
const principal = yield* authenticateHubServerMembership();
|
|
25015
25251
|
const input = yield* readBody$1(HubClientEnrollmentInput);
|
|
25016
|
-
return json$
|
|
25252
|
+
return json$2(yield* issueHubClientEnrollment(principal, input));
|
|
25017
25253
|
}), "server-auth");
|
|
25018
25254
|
const exchange$1 = handle$1(Effect.gen(function* () {
|
|
25019
25255
|
const input = yield* readBody$1(HubClientEnrollmentExchange);
|
|
25020
|
-
return json$
|
|
25256
|
+
return json$2(yield* consumeHubClientEnrollment(input.secret));
|
|
25021
25257
|
}), "client-route");
|
|
25022
25258
|
const identity$2 = handle$1(Effect.gen(function* () {
|
|
25023
25259
|
const request = yield* HttpServerRequest.HttpServerRequest;
|
|
25024
|
-
return json$
|
|
25260
|
+
return json$2(yield* authenticateHubClient(readBearerToken(request.headers.authorization)));
|
|
25025
25261
|
}), "client-route");
|
|
25026
25262
|
const events = handle$1(Effect.gen(function* () {
|
|
25027
25263
|
return yield* hubClientEvents(readBearerToken((yield* HttpServerRequest.HttpServerRequest).headers.authorization));
|
|
@@ -25046,7 +25282,7 @@ const socketTicket = handle$1(Effect.gen(function* () {
|
|
|
25046
25282
|
yield* authenticateHubClient(credential);
|
|
25047
25283
|
const input = yield* readBody$1(HubClientSocketTicketInput, MAX_SOCKET_TICKET_REQUEST_BYTES);
|
|
25048
25284
|
const tickets = yield* HubClientSocketTickets;
|
|
25049
|
-
return json$
|
|
25285
|
+
return json$2(yield* tickets.issue(credential, {
|
|
25050
25286
|
method: "GET",
|
|
25051
25287
|
url: `${HUB_TUNNEL_CLIENT_SOCKET_PATH}${input.path}`,
|
|
25052
25288
|
headers: {
|
|
@@ -25059,7 +25295,143 @@ const socketTicket = handle$1(Effect.gen(function* () {
|
|
|
25059
25295
|
}
|
|
25060
25296
|
}));
|
|
25061
25297
|
}), "client-route");
|
|
25062
|
-
const layer$
|
|
25298
|
+
const layer$109 = Layer.mergeAll(layer$113, HttpRouter.add("POST", "/v1/clients/me/socket-ticket", withHubClientCors(socketTicket)), HttpRouter.add("POST", "/v1/client-enrollments", issue$1), HttpRouter.add("POST", "/v1/client-enrollments/exchange", withHubClientCors(exchange$1)), HttpRouter.add("GET", "/v1/clients/me", withHubClientCors(identity$2)), HttpRouter.add("GET", "/v1/clients/me/events", withHubClientCors(events)), HttpRouter.add("DELETE", "/v1/clients/me", withHubClientCors(signOut)), HttpRouter.add("POST", "/v1/clients/revoke", revoke));
|
|
25299
|
+
//#endregion
|
|
25300
|
+
//#region src/hub/HubAnalytics.ts
|
|
25301
|
+
/**
|
|
25302
|
+
* Account-scoped analytics facts: append and read.
|
|
25303
|
+
*
|
|
25304
|
+
* Every row belongs to the authenticated account, and every read is filtered
|
|
25305
|
+
* by it, so one account's facts can never appear in another's page. Appends
|
|
25306
|
+
* are idempotent on the caller's own event id rather than deduplicated by
|
|
25307
|
+
* content: a retry after a dropped response must not double-count, and a fact
|
|
25308
|
+
* is never rewritten once stored.
|
|
25309
|
+
*
|
|
25310
|
+
* @module HubAnalytics
|
|
25311
|
+
*/
|
|
25312
|
+
const CURSOR_SEPARATOR = "|";
|
|
25313
|
+
const decodeFact = Schema$1.decodeUnknownEffect(HubAnalyticsFactInput);
|
|
25314
|
+
const decodeQuery = Schema$1.decodeUnknownEffect(HubAnalyticsQuery);
|
|
25315
|
+
const encodePayload$1 = Schema$1.encodeEffect(Schema$1.fromJsonString(Schema$1.Unknown));
|
|
25316
|
+
const decodePayload$1 = Schema$1.decodeUnknownEffect(Schema$1.fromJsonString(Schema$1.Unknown));
|
|
25317
|
+
/** The page position of a row, in the exact order the index reads. */
|
|
25318
|
+
const cursorOf = (row) => `${row.occurred_at}${CURSOR_SEPARATOR}${row.event_id}`;
|
|
25319
|
+
const parseCursor = (cursor) => {
|
|
25320
|
+
const separator = cursor.indexOf(CURSOR_SEPARATOR);
|
|
25321
|
+
if (separator <= 0) return null;
|
|
25322
|
+
const occurredAt = Number.parseInt(cursor.slice(0, separator), 10);
|
|
25323
|
+
const eventId = cursor.slice(separator + 1);
|
|
25324
|
+
return Number.isFinite(occurredAt) && eventId.length > 0 ? {
|
|
25325
|
+
occurredAt,
|
|
25326
|
+
eventId
|
|
25327
|
+
} : null;
|
|
25328
|
+
};
|
|
25329
|
+
/**
|
|
25330
|
+
* Append one fact for the authenticated account.
|
|
25331
|
+
*
|
|
25332
|
+
* The principal is rechecked inside the transaction, the way every other hub
|
|
25333
|
+
* write does it: a credential revoked while the body was being read must not
|
|
25334
|
+
* still land a row.
|
|
25335
|
+
*/
|
|
25336
|
+
const appendHubAnalyticsFact = Effect.fn("HubAnalytics.append")(function* (principal, input) {
|
|
25337
|
+
const config = yield* HubConfig;
|
|
25338
|
+
if (!currentPrincipal(config, principal)) return yield* Effect.fail(new HubUnauthorizedError());
|
|
25339
|
+
const fact = yield* decodeFact(input, { onExcessProperty: "error" });
|
|
25340
|
+
const payload = yield* encodePayload$1(fact.payload);
|
|
25341
|
+
const occurredAt = Date.parse(fact.occurredAt);
|
|
25342
|
+
const sql = yield* SqlClient.SqlClient;
|
|
25343
|
+
const now = yield* Clock.currentTimeMillis;
|
|
25344
|
+
return yield* sql.withTransaction(Effect.gen(function* () {
|
|
25345
|
+
if (!currentPrincipal(config, principal)) return yield* Effect.fail(new HubUnauthorizedError());
|
|
25346
|
+
if ((yield* sql`SELECT event_id FROM hub_analytics_facts
|
|
25347
|
+
WHERE account_id = ${principal.accountId} AND event_id = ${fact.eventId}`).length > 0) return { accepted: false };
|
|
25348
|
+
yield* sql`INSERT INTO hub_analytics_facts
|
|
25349
|
+
(account_id, environment_id, event_id, kind, version, occurred_at, payload, created_at)
|
|
25350
|
+
VALUES (${principal.accountId}, ${principal.environmentId}, ${fact.eventId}, ${fact.kind},
|
|
25351
|
+
${fact.version}, ${occurredAt}, ${payload}, ${now})
|
|
25352
|
+
ON CONFLICT(account_id, event_id) DO NOTHING`;
|
|
25353
|
+
return { accepted: true };
|
|
25354
|
+
}));
|
|
25355
|
+
});
|
|
25356
|
+
/**
|
|
25357
|
+
* Read this account's facts, newest first.
|
|
25358
|
+
*
|
|
25359
|
+
* Paged by keyset rather than offset, so a fact appended between two pages
|
|
25360
|
+
* cannot shift a row into being read twice or skipped. The limit is bounded by
|
|
25361
|
+
* the contract, and one extra row is read to decide whether a next page
|
|
25362
|
+
* exists without a second count query.
|
|
25363
|
+
*/
|
|
25364
|
+
const queryHubAnalyticsFacts = Effect.fn("HubAnalytics.query")(function* (principal, input) {
|
|
25365
|
+
if (!currentPrincipal(yield* HubConfig, principal)) return yield* Effect.fail(new HubUnauthorizedError());
|
|
25366
|
+
const query = yield* decodeQuery(input, { onExcessProperty: "error" });
|
|
25367
|
+
const limit = query.limit ?? HUB_ANALYTICS_QUERY_LIMITS.defaultLimit;
|
|
25368
|
+
const kinds = query.kinds === void 0 ? null : `,${query.kinds.join(",")},`;
|
|
25369
|
+
const since = query.since === void 0 ? null : Date.parse(query.since);
|
|
25370
|
+
const until = query.until === void 0 ? null : Date.parse(query.until);
|
|
25371
|
+
const cursor = query.cursor === void 0 ? null : parseCursor(query.cursor);
|
|
25372
|
+
const cursorOccurredAt = cursor === null ? null : cursor.occurredAt;
|
|
25373
|
+
const cursorEventId = cursor === null ? null : cursor.eventId;
|
|
25374
|
+
const rows = yield* (yield* SqlClient.SqlClient)`
|
|
25375
|
+
SELECT environment_id, event_id, kind, version, occurred_at, payload
|
|
25376
|
+
FROM hub_analytics_facts
|
|
25377
|
+
WHERE account_id = ${principal.accountId}
|
|
25378
|
+
AND (${kinds} IS NULL OR instr(${kinds}, ',' || kind || ',') > 0)
|
|
25379
|
+
AND (${since} IS NULL OR occurred_at >= ${since})
|
|
25380
|
+
AND (${until} IS NULL OR occurred_at <= ${until})
|
|
25381
|
+
AND (
|
|
25382
|
+
${cursorOccurredAt} IS NULL
|
|
25383
|
+
OR occurred_at < ${cursorOccurredAt}
|
|
25384
|
+
OR (occurred_at = ${cursorOccurredAt} AND event_id < ${cursorEventId})
|
|
25385
|
+
)
|
|
25386
|
+
ORDER BY occurred_at DESC, event_id DESC
|
|
25387
|
+
LIMIT ${limit + 1}`;
|
|
25388
|
+
const page = rows.slice(0, limit);
|
|
25389
|
+
const facts = [];
|
|
25390
|
+
for (const row of page) facts.push({
|
|
25391
|
+
eventId: row.event_id,
|
|
25392
|
+
environmentId: row.environment_id,
|
|
25393
|
+
kind: row.kind,
|
|
25394
|
+
version: row.version,
|
|
25395
|
+
occurredAt: new Date(row.occurred_at).toISOString(),
|
|
25396
|
+
payload: yield* decodePayload$1(row.payload)
|
|
25397
|
+
});
|
|
25398
|
+
const last = page[page.length - 1];
|
|
25399
|
+
return {
|
|
25400
|
+
facts,
|
|
25401
|
+
nextCursor: rows.length > limit && last !== void 0 ? cursorOf(last) : null
|
|
25402
|
+
};
|
|
25403
|
+
});
|
|
25404
|
+
//#endregion
|
|
25405
|
+
//#region src/hub/HubAnalyticsRoutes.ts
|
|
25406
|
+
/**
|
|
25407
|
+
* The account's own analytics endpoints.
|
|
25408
|
+
*
|
|
25409
|
+
* Both routes require the same server membership every other environment-owned
|
|
25410
|
+
* hub write uses, and neither forwards anything anywhere: the hub is the only
|
|
25411
|
+
* sink for these facts.
|
|
25412
|
+
*
|
|
25413
|
+
* @module HubAnalyticsRoutes
|
|
25414
|
+
*/
|
|
25415
|
+
const MAX_ANALYTICS_BODY_BYTES = 16 * 1024;
|
|
25416
|
+
const json$1 = (value) => HttpServerResponse.jsonUnsafe(value, { headers: { "cache-control": "no-store" } });
|
|
25417
|
+
/** Only the filters the query contract knows; anything else is a decode failure. */
|
|
25418
|
+
const queryFromUrl = (url) => Object.fromEntries([
|
|
25419
|
+
["kinds", url.searchParams.getAll("kind")],
|
|
25420
|
+
["since", url.searchParams.get("since")],
|
|
25421
|
+
["until", url.searchParams.get("until")],
|
|
25422
|
+
["limit", url.searchParams.get("limit")],
|
|
25423
|
+
["cursor", url.searchParams.get("cursor")]
|
|
25424
|
+
].filter(([, value]) => Array.isArray(value) ? value.length > 0 : value !== null).map(([key, value]) => key === "limit" && typeof value === "string" ? [key, Number.parseInt(value, 10)] : [key, value]));
|
|
25425
|
+
const layer$108 = Layer.mergeAll(HttpRouter.add("POST", "/v1/analytics/facts", handle$1(Effect.gen(function* () {
|
|
25426
|
+
const principal = yield* authenticateHubServerMembership();
|
|
25427
|
+
const input = yield* readBody$1(HubAnalyticsFactInput, MAX_ANALYTICS_BODY_BYTES);
|
|
25428
|
+
return json$1(yield* appendHubAnalyticsFact(principal, input));
|
|
25429
|
+
}), "server-auth")), HttpRouter.add("GET", "/v1/analytics/facts", handle$1(Effect.gen(function* () {
|
|
25430
|
+
const principal = yield* authenticateHubServerMembership();
|
|
25431
|
+
const request = yield* HttpServerRequest.HttpServerRequest;
|
|
25432
|
+
const url = new URL(request.url, "http://hub.invalid");
|
|
25433
|
+
return json$1(yield* queryHubAnalyticsFacts(principal, queryFromUrl(url)));
|
|
25434
|
+
}), "server-auth")));
|
|
25063
25435
|
//#endregion
|
|
25064
25436
|
//#region src/hub/HubPushTokenProtection.ts
|
|
25065
25437
|
const KEY_NAME = "hub-push-token-encryption-v1";
|
|
@@ -26812,7 +27184,7 @@ const layer$100 = Layer.effect(HubTunnelPublished, Effect.gen(function* () {
|
|
|
26812
27184
|
}));
|
|
26813
27185
|
//#endregion
|
|
26814
27186
|
//#region src/hub/HubTunnelCors.ts
|
|
26815
|
-
const MAX_HEADER_CHARS = 256;
|
|
27187
|
+
const MAX_HEADER_CHARS$1 = 256;
|
|
26816
27188
|
const METHODS = [
|
|
26817
27189
|
"GET",
|
|
26818
27190
|
"HEAD",
|
|
@@ -26842,7 +27214,7 @@ function hubTunnelCors(method, headers) {
|
|
|
26842
27214
|
};
|
|
26843
27215
|
if (method === "OPTIONS" && headers["access-control-request-method"] !== void 0) {
|
|
26844
27216
|
const raw = headers["access-control-request-headers"] ?? "";
|
|
26845
|
-
if (raw.length > MAX_HEADER_CHARS) return null;
|
|
27217
|
+
if (raw.length > MAX_HEADER_CHARS$1) return null;
|
|
26846
27218
|
const requested = raw ? raw.split(",").map((name) => name.trim().toLowerCase()) : [];
|
|
26847
27219
|
if (!METHODS.includes(headers["access-control-request-method"]) || requested.length > HEADERS.length || new Set(requested).size !== requested.length || requested.some((name) => !HEADERS.includes(name))) return null;
|
|
26848
27220
|
result["access-control-allow-methods"] = METHODS.join(", ");
|
|
@@ -28597,8 +28969,8 @@ const releaseFeedLeaseRoute = HttpRouter.add("DELETE", "/feed/lease", respondToH
|
|
|
28597
28969
|
return HttpServerResponse.empty({ status: 204 });
|
|
28598
28970
|
})));
|
|
28599
28971
|
/** Provide around HttpRouter.serve so request fibers share these process-owned services. */
|
|
28600
|
-
const routingServices = Layer.mergeAll(layer$112, layer$
|
|
28601
|
-
const layer$94 = Layer.mergeAll(layer$99, layer$98, layer$102, layer$96, layer$95, layer$
|
|
28972
|
+
const routingServices = Layer.mergeAll(layer$112, layer$110, layer$101, layer$100).pipe(Layer.provideMerge(layer$111));
|
|
28973
|
+
const layer$94 = Layer.mergeAll(layer$99, layer$98, layer$102, layer$96, layer$95, layer$109, layer$97, healthRoute, identityRoute, listTasksRoute, getTaskRoute, createTaskRoute, putTaskRoute, updateTaskRoute, deleteTaskRoute, listAssetsRoute, getAssetRoute, putAssetRoute, deleteAssetRoute, listFeedSourcesRoute, upsertFeedSourceRoute, deleteFeedSourceRoute, listFeedRoute, putFeedArticleRoute, putFeedEngagementRoute, markFeedReadRoute, cleanupFeedRoute, setFeedRefreshedAtRoute, acquireFeedLeaseRoute, renewFeedLeaseRoute, releaseFeedLeaseRoute).pipe(Layer.provideMerge(layer$112), Layer.provideMerge(layer$110), Layer.provideMerge(layer$101));
|
|
28602
28974
|
//#endregion
|
|
28603
28975
|
//#region src/hub/Migrations/001_Tasks.ts
|
|
28604
28976
|
/**
|
|
@@ -29080,6 +29452,21 @@ var _021_NotificationDeliveryProgress_default = Effect.gen(function* () {
|
|
|
29080
29452
|
yield* (yield* SqlClient.SqlClient)`ALTER TABLE hub_notification_deliveries ADD COLUMN app_delivered INTEGER NOT NULL DEFAULT 0`;
|
|
29081
29453
|
});
|
|
29082
29454
|
//#endregion
|
|
29455
|
+
//#region src/hub/Migrations/022_AnalyticsFacts.ts
|
|
29456
|
+
var _022_AnalyticsFacts_default = Effect.gen(function* () {
|
|
29457
|
+
const sql = yield* SqlClient.SqlClient;
|
|
29458
|
+
yield* sql`CREATE TABLE hub_analytics_facts (
|
|
29459
|
+
account_id TEXT NOT NULL, environment_id TEXT NOT NULL, event_id TEXT NOT NULL,
|
|
29460
|
+
kind TEXT NOT NULL, version INTEGER NOT NULL, occurred_at INTEGER NOT NULL,
|
|
29461
|
+
payload TEXT NOT NULL, created_at INTEGER NOT NULL,
|
|
29462
|
+
PRIMARY KEY(account_id, event_id)
|
|
29463
|
+
)`;
|
|
29464
|
+
yield* sql`CREATE INDEX hub_analytics_facts_recent
|
|
29465
|
+
ON hub_analytics_facts(account_id, occurred_at DESC, event_id DESC)`;
|
|
29466
|
+
yield* sql`CREATE INDEX hub_analytics_facts_kind
|
|
29467
|
+
ON hub_analytics_facts(account_id, kind, occurred_at DESC, event_id DESC)`;
|
|
29468
|
+
});
|
|
29469
|
+
//#endregion
|
|
29083
29470
|
//#region src/hub/Migrations.ts
|
|
29084
29471
|
/**
|
|
29085
29472
|
* Hub migrations.
|
|
@@ -29195,6 +29582,11 @@ const hubMigrationEntries = [
|
|
|
29195
29582
|
21,
|
|
29196
29583
|
"NotificationDeliveryProgress",
|
|
29197
29584
|
_021_NotificationDeliveryProgress_default
|
|
29585
|
+
],
|
|
29586
|
+
[
|
|
29587
|
+
22,
|
|
29588
|
+
"AnalyticsFacts",
|
|
29589
|
+
_022_AnalyticsFacts_default
|
|
29198
29590
|
]
|
|
29199
29591
|
];
|
|
29200
29592
|
const hubMigrationLoader = Migrator.fromRecord(Object.fromEntries(hubMigrationEntries.map(([id, name, migration]) => [`${id}_${name}`, migration])));
|
|
@@ -29286,7 +29678,7 @@ const announce = Layer.effectDiscard(Effect.gen(function* () {
|
|
|
29286
29678
|
});
|
|
29287
29679
|
if (isRemoteReachableHost(config.host)) yield* Effect.logWarning("Hub is reachable beyond loopback. The bearer token is the only control — repeated auth failures are not rate limited.", { host: config.host });
|
|
29288
29680
|
}));
|
|
29289
|
-
const makeHubLayer = HttpRouter.serve(Layer.mergeAll(layer$94, layer$106)).pipe(Layer.provideMerge(routingServices), Layer.provideMerge(layer$104), Layer.provideMerge(layer$105), Layer.provideMerge(layer$103), Layer.provideMerge(announce), Layer.provideMerge(HubHttpServerLive), Layer.provideMerge(layer$
|
|
29681
|
+
const makeHubLayer = HttpRouter.serve(Layer.mergeAll(layer$94, layer$106, layer$108)).pipe(Layer.provideMerge(routingServices), Layer.provideMerge(layer$104), Layer.provideMerge(layer$105), Layer.provideMerge(layer$103), Layer.provideMerge(announce), Layer.provideMerge(HubHttpServerLive), Layer.provideMerge(layer$114), Layer.provideMerge(TaskRepositoryLive), Layer.provideMerge(AgentAssetRepositoryLive), Layer.provideMerge(HubPersistenceLive));
|
|
29290
29682
|
const runHub = Layer.launch(makeHubLayer);
|
|
29291
29683
|
//#endregion
|
|
29292
29684
|
//#region src/atomicWrite.ts
|
|
@@ -30944,6 +31336,16 @@ const decodeRegistration$1 = Schema$1.decodeUnknownExit(RegistrationFromJson$1);
|
|
|
30944
31336
|
const encodeRegistration = Schema$1.encodeSync(RegistrationFromJson$1);
|
|
30945
31337
|
var McpRegistry = class extends Context.Service()("@p4code/cli/mcp/McpRegistry") {};
|
|
30946
31338
|
const slotsOf = (registration) => registration.secrets ?? [];
|
|
31339
|
+
/**
|
|
31340
|
+
* What a session is told when a server is left out for want of a credential.
|
|
31341
|
+
*
|
|
31342
|
+
* The name and where the row came from, and nothing else: a token, a url query
|
|
31343
|
+
* or a header would end up in a log file that outlives the session. The action
|
|
31344
|
+
* names the one place a person can fix it, because "absent" on its own reads as
|
|
31345
|
+
* a bug rather than as a sign-in that has not happened.
|
|
31346
|
+
*/
|
|
31347
|
+
const OMITTED_FOR_SIGN_IN = "mcp server omitted: sign-in required";
|
|
31348
|
+
const SIGN_IN_ACTION = "Connect it in Settings > MCP servers.";
|
|
30947
31349
|
const make$108 = Effect.gen(function* () {
|
|
30948
31350
|
const config = yield* ServerConfig$1;
|
|
30949
31351
|
const secrets = yield* ServerSecretStore;
|
|
@@ -31060,6 +31462,14 @@ const make$108 = Effect.gen(function* () {
|
|
|
31060
31462
|
if (registration.transport !== "stdio" && !hasExplicitAuthorization) {
|
|
31061
31463
|
const token = yield* oauth.accessTokenFor(registration);
|
|
31062
31464
|
if (Option.isSome(token)) headers["Authorization"] = `Bearer ${token.value}`;
|
|
31465
|
+
else if (Option.isSome(yield* oauth.statusFor(registration))) {
|
|
31466
|
+
yield* Effect.logWarning(OMITTED_FOR_SIGN_IN, {
|
|
31467
|
+
server: registration.name,
|
|
31468
|
+
source: "registration",
|
|
31469
|
+
action: SIGN_IN_ACTION
|
|
31470
|
+
});
|
|
31471
|
+
continue;
|
|
31472
|
+
}
|
|
31063
31473
|
}
|
|
31064
31474
|
resolved[registration.name] = registration.transport === "stdio" ? {
|
|
31065
31475
|
type: "stdio",
|
|
@@ -31095,7 +31505,15 @@ const make$108 = Effect.gen(function* () {
|
|
|
31095
31505
|
if (!Object.keys(headers).some((key) => key.toLowerCase() === "authorization")) {
|
|
31096
31506
|
const token = yield* oauth.accessTokenFor(userScope);
|
|
31097
31507
|
if (Option.isSome(token)) headers["Authorization"] = `Bearer ${token.value}`;
|
|
31098
|
-
else if (Option.isSome(yield* oauth.statusFor(userScope)))
|
|
31508
|
+
else if (Option.isSome(yield* oauth.statusFor(userScope))) {
|
|
31509
|
+
yield* Effect.logWarning(OMITTED_FOR_SIGN_IN, {
|
|
31510
|
+
server: userScope.name,
|
|
31511
|
+
source: "user-scope",
|
|
31512
|
+
sessionName,
|
|
31513
|
+
action: SIGN_IN_ACTION
|
|
31514
|
+
});
|
|
31515
|
+
continue;
|
|
31516
|
+
}
|
|
31099
31517
|
}
|
|
31100
31518
|
resolved[sessionName] = {
|
|
31101
31519
|
type: userScope.transport,
|
|
@@ -33858,7 +34276,7 @@ const findAgentPreset = (requested) => {
|
|
|
33858
34276
|
};
|
|
33859
34277
|
//#endregion
|
|
33860
34278
|
//#region src/cli/assets.ts
|
|
33861
|
-
const SyncServicesLive = layer$87.pipe(Layer.provideMerge(layer$88), Layer.provideMerge(layer$89), Layer.provideMerge(layer$93.pipe(Layer.provide(layer$
|
|
34279
|
+
const SyncServicesLive = layer$87.pipe(Layer.provideMerge(layer$88), Layer.provideMerge(layer$89), Layer.provideMerge(layer$93.pipe(Layer.provide(layer$119))), Layer.provideMerge(layer$119), Layer.provideMerge(FetchHttpClient.layer));
|
|
33862
34280
|
const runWithSync = (flags, run) => Effect.gen(function* () {
|
|
33863
34281
|
const config = yield* resolveCliAuthConfig(flags, yield* GlobalFlag.LogLevel);
|
|
33864
34282
|
return yield* Effect.gen(function* () {
|
|
@@ -33866,7 +34284,7 @@ const runWithSync = (flags, run) => Effect.gen(function* () {
|
|
|
33866
34284
|
sync: yield* AssetSync,
|
|
33867
34285
|
link: yield* HubLink
|
|
33868
34286
|
});
|
|
33869
|
-
}).pipe(Effect.provide(SyncServicesLive.pipe(Layer.provide(layer$
|
|
34287
|
+
}).pipe(Effect.provide(SyncServicesLive.pipe(Layer.provide(layer$122(config)), Layer.provide(Layer.succeed(References.MinimumLogLevel, "Error")))));
|
|
33870
34288
|
});
|
|
33871
34289
|
const STATE_LABEL = {
|
|
33872
34290
|
synced: "synced",
|
|
@@ -36240,7 +36658,11 @@ function projectEvent(model, event) {
|
|
|
36240
36658
|
case "thread.activity-appended": return decodeForEvent(ThreadActivityAppendedPayload, event.payload, event.type, "payload").pipe(Effect.map((payload) => {
|
|
36241
36659
|
const thread = nextBase.threads.find((entry) => entry.id === payload.threadId);
|
|
36242
36660
|
if (!thread) return nextBase;
|
|
36243
|
-
const
|
|
36661
|
+
const appended = payload.activity.sequence === void 0 ? {
|
|
36662
|
+
...payload.activity,
|
|
36663
|
+
sequence: event.sequence
|
|
36664
|
+
} : payload.activity;
|
|
36665
|
+
const activities = [...thread.activities.filter((entry) => entry.id !== appended.id), appended].toSorted(compareThreadActivities).slice(-500);
|
|
36244
36666
|
return {
|
|
36245
36667
|
...nextBase,
|
|
36246
36668
|
threads: updateThread(nextBase.threads, payload.threadId, {
|
|
@@ -40666,7 +41088,7 @@ const makeOrchestrationProjectionPipeline = Effect.fn("makeOrchestrationProjecti
|
|
|
40666
41088
|
kind: activity.kind,
|
|
40667
41089
|
summary: activity.summary,
|
|
40668
41090
|
payload: activity.payload,
|
|
40669
|
-
|
|
41091
|
+
sequence: activity.sequence ?? event.sequence,
|
|
40670
41092
|
createdAt: activity.createdAt
|
|
40671
41093
|
});
|
|
40672
41094
|
return;
|
|
@@ -47399,7 +47821,7 @@ const startDeviceHub = Effect.fn("DeviceHubProcess.start")(function* (input) {
|
|
|
47399
47821
|
});
|
|
47400
47822
|
//#endregion
|
|
47401
47823
|
//#region src/device/AgentDeviceProcessLive.ts
|
|
47402
|
-
const decodeJson$
|
|
47824
|
+
const decodeJson$3 = Schema$1.decodeUnknownSync(Schema$1.UnknownFromJsonString);
|
|
47403
47825
|
var AgentDeviceProcessLiveFailure = class extends Data.TaggedError("AgentDeviceProcessLiveFailure") {};
|
|
47404
47826
|
const READY_TIMEOUT_MS = 3e4;
|
|
47405
47827
|
const MAX_READY_LINE = 256;
|
|
@@ -47511,7 +47933,7 @@ const make$93 = Effect.fn("AgentDeviceProcess.make")(function* () {
|
|
|
47511
47933
|
maxBytes: MAX_RESPONSE_BYTES$7
|
|
47512
47934
|
});
|
|
47513
47935
|
if (response.status !== 200 || body.truncated || body.invalidUtf8) return yield* new AgentDeviceProcessLiveFailure({ message: "Agent authorization failed" });
|
|
47514
|
-
const result = yield* Effect.try(() => decodeJson$
|
|
47936
|
+
const result = yield* Effect.try(() => decodeJson$3(body.text));
|
|
47515
47937
|
if (!result || typeof result !== "object" || !("id" in result) || result.id !== "p4-readiness" || !("result" in result) || !result.result || typeof result.result !== "object" || !("ok" in result.result) || result.result.ok !== true) return yield* new AgentDeviceProcessLiveFailure({ message: "Agent readiness rejected" });
|
|
47516
47938
|
endpoint = {
|
|
47517
47939
|
baseUrl,
|
|
@@ -49511,7 +49933,7 @@ main().catch(() => { if (!stopping) emit({ phase: 'failed', message: 'Remote dev
|
|
|
49511
49933
|
}
|
|
49512
49934
|
//#endregion
|
|
49513
49935
|
//#region src/device/SshDeviceConnection.ts
|
|
49514
|
-
const decodeJson$
|
|
49936
|
+
const decodeJson$2 = Schema$1.decodeUnknownSync(Schema$1.UnknownFromJsonString);
|
|
49515
49937
|
const START_TIMEOUT_MS = 66e4;
|
|
49516
49938
|
const FORWARD_TIMEOUT_MS = 15e3;
|
|
49517
49939
|
const VERIFY_TIMEOUT_MS = 3e4;
|
|
@@ -49666,7 +50088,7 @@ const connectSshDevice = Effect.fn("SshDeviceConnection.connect")(function* (inp
|
|
|
49666
50088
|
maxBytes: MAX_RESPONSE_BYTES$4
|
|
49667
50089
|
});
|
|
49668
50090
|
if (response.status !== 200 || body.truncated || body.invalidUtf8) return yield* fail("verifying forwarded agent")(void 0);
|
|
49669
|
-
const result = yield* Effect.try(() => decodeJson$
|
|
50091
|
+
const result = yield* Effect.try(() => decodeJson$2(body.text));
|
|
49670
50092
|
if (!result || typeof result !== "object" || !("id" in result) || result.id !== identity.nonce || !("result" in result) || !result.result || typeof result.result !== "object" || !("ok" in result.result) || result.result.ok !== true) return yield* fail("authenticating forwarded agent")(void 0);
|
|
49671
50093
|
}).pipe(Effect.scoped, Effect.timeout(Duration.millis(VERIFY_TIMEOUT_MS)), Effect.mapError(fail("verifying forwarded agent")));
|
|
49672
50094
|
return {
|
|
@@ -51095,7 +51517,7 @@ const runProjectMutation = Effect.fn("runProjectMutation")(function* (flags, run
|
|
|
51095
51517
|
});
|
|
51096
51518
|
yield* Console.log(output);
|
|
51097
51519
|
}));
|
|
51098
|
-
const offlineRuntimeLayer = ProjectCliRuntimeLive.pipe(Layer.provide(layer$
|
|
51520
|
+
const offlineRuntimeLayer = ProjectCliRuntimeLive.pipe(Layer.provide(layer$122(config)), Layer.provide(Layer.succeed(References.MinimumLogLevel, minimumLogLevel)));
|
|
51099
51521
|
return yield* Effect.gen(function* () {
|
|
51100
51522
|
const snapshot = yield* getOfflineSnapshot();
|
|
51101
51523
|
const orchestrationEngine = yield* OrchestrationEngineService;
|
|
@@ -51106,7 +51528,7 @@ const runProjectMutation = Effect.fn("runProjectMutation")(function* (flags, run
|
|
|
51106
51528
|
});
|
|
51107
51529
|
yield* Console.log(output);
|
|
51108
51530
|
}).pipe(Effect.provide(offlineRuntimeLayer));
|
|
51109
|
-
}).pipe(Effect.provide(Layer.mergeAll(runtimeLayer, layer$70).pipe(Layer.provideMerge(FetchHttpClient.layer), Layer.provide(layer$
|
|
51531
|
+
}).pipe(Effect.provide(Layer.mergeAll(runtimeLayer, layer$70).pipe(Layer.provideMerge(FetchHttpClient.layer), Layer.provide(layer$122(config)), Layer.provide(Layer.succeed(References.MinimumLogLevel, minimumLogLevel)))));
|
|
51110
51532
|
});
|
|
51111
51533
|
const projectAddCommand = Command.make("add", {
|
|
51112
51534
|
...projectLocationFlags,
|
|
@@ -59682,7 +60104,7 @@ const TYPESAFE_REQUEST_BUDGET_TOKENS = 32e3;
|
|
|
59682
60104
|
const TYPESAFE_BUDGET_RESERVE_TOKENS = 2e3;
|
|
59683
60105
|
var TypeSafeRequestError = class extends Data.TaggedError("TypeSafeRequestError") {};
|
|
59684
60106
|
var TypeSafeClient = class extends Context.Service()("@p4code/cli/orchestration/triage/TypeSafeClient") {};
|
|
59685
|
-
const asRecord$
|
|
60107
|
+
const asRecord$10 = (value) => typeof value === "object" && value !== null && !Array.isArray(value) ? value : void 0;
|
|
59686
60108
|
/**
|
|
59687
60109
|
* The finite numbers out of a probability map, or nothing.
|
|
59688
60110
|
*
|
|
@@ -59691,7 +60113,7 @@ const asRecord$8 = (value) => typeof value === "object" && value !== null && !Ar
|
|
|
59691
60113
|
* invented number is worse than one built on fewer.
|
|
59692
60114
|
*/
|
|
59693
60115
|
function readProbabilities(value) {
|
|
59694
|
-
const record = asRecord$
|
|
60116
|
+
const record = asRecord$10(value);
|
|
59695
60117
|
if (record === void 0) return void 0;
|
|
59696
60118
|
const probabilities = {};
|
|
59697
60119
|
for (const [key, entry] of Object.entries(record)) if (typeof entry === "number" && Number.isFinite(entry)) probabilities[key] = entry;
|
|
@@ -59705,7 +60127,7 @@ function readProbabilities(value) {
|
|
|
59705
60127
|
* can never reach the caller wearing a confidence it did not have.
|
|
59706
60128
|
*/
|
|
59707
60129
|
function parseTypeSafeAnswer(value) {
|
|
59708
|
-
const record = asRecord$
|
|
60130
|
+
const record = asRecord$10(value);
|
|
59709
60131
|
if (record === void 0) return void 0;
|
|
59710
60132
|
const confidence = record["confidence"];
|
|
59711
60133
|
switch (record["type"]) {
|
|
@@ -59746,7 +60168,7 @@ function parseTypeSafeAnswer(value) {
|
|
|
59746
60168
|
}
|
|
59747
60169
|
/** The one answer for the question that was asked, out of the answers map. */
|
|
59748
60170
|
function readAnswerFor(body, questionId) {
|
|
59749
|
-
const answers = asRecord$
|
|
60171
|
+
const answers = asRecord$10(asRecord$10(body)?.["answers"]);
|
|
59750
60172
|
return answers === void 0 ? void 0 : parseTypeSafeAnswer(answers[questionId]);
|
|
59751
60173
|
}
|
|
59752
60174
|
const makeTypeSafeClient = Effect.gen(function* () {
|
|
@@ -59850,19 +60272,19 @@ const estimateTokens$1 = (text) => Math.ceil(text.length / 4);
|
|
|
59850
60272
|
/** One line per event is plenty to answer "did anything reviewable happen". */
|
|
59851
60273
|
const MAX_LINE_CHARS = 400;
|
|
59852
60274
|
const oneLine = (text) => text.replace(/\s+/gu, " ").trim();
|
|
59853
|
-
const clip$
|
|
60275
|
+
const clip$2 = (text) => text.length <= MAX_LINE_CHARS ? text : `${text.slice(0, MAX_LINE_CHARS)}…`;
|
|
59854
60276
|
/** The line an event contributes, or null when it says nothing about the work. */
|
|
59855
60277
|
function describeEvent(event, implementerThreadId) {
|
|
59856
60278
|
switch (event.type) {
|
|
59857
60279
|
case "thread.message-sent": {
|
|
59858
60280
|
if (event.payload.threadId !== implementerThreadId) return null;
|
|
59859
60281
|
const text = oneLine(event.payload.text);
|
|
59860
|
-
return text.length === 0 ? null : clip$
|
|
60282
|
+
return text.length === 0 ? null : clip$2(`${event.payload.role}: ${text}`);
|
|
59861
60283
|
}
|
|
59862
60284
|
case "thread.activity-appended": {
|
|
59863
60285
|
if (event.payload.threadId !== implementerThreadId) return null;
|
|
59864
60286
|
const activity = event.payload.activity;
|
|
59865
|
-
return clip$
|
|
60287
|
+
return clip$2(oneLine(`${activity.kind}: ${activity.summary}`));
|
|
59866
60288
|
}
|
|
59867
60289
|
case "thread.turn-completed":
|
|
59868
60290
|
if (event.payload.threadId !== implementerThreadId) return null;
|
|
@@ -60085,7 +60507,7 @@ function renderPrefetchHint(selected) {
|
|
|
60085
60507
|
//#endregion
|
|
60086
60508
|
//#region src/orchestration/userInputRanking.ts
|
|
60087
60509
|
const MAX_OPTIONS = 255;
|
|
60088
|
-
const MAX_QUESTION_CHARS = 16e3;
|
|
60510
|
+
const MAX_QUESTION_CHARS$1 = 16e3;
|
|
60089
60511
|
const RANKING_TIMEOUT_MS = 1e4;
|
|
60090
60512
|
const PROBABILITY_SUM_TOLERANCE = .01;
|
|
60091
60513
|
/** Require a complete distribution; never invent missing option ranks. Equal scores share rank. */
|
|
@@ -60103,13 +60525,17 @@ function ranksFromAnswer(answer, count) {
|
|
|
60103
60525
|
})).sort((a, b) => b.probability - a.probability || a.optionIndex - b.optionIndex);
|
|
60104
60526
|
if (Math.abs(ranked.reduce((sum, item) => sum + item.probability, 0) - 1) > PROBABILITY_SUM_TOLERANCE) return null;
|
|
60105
60527
|
let rank = 1;
|
|
60106
|
-
return {
|
|
60107
|
-
|
|
60108
|
-
|
|
60109
|
-
|
|
60110
|
-
|
|
60111
|
-
|
|
60112
|
-
|
|
60528
|
+
return {
|
|
60529
|
+
confidence: answer.confidence,
|
|
60530
|
+
ranks: ranked.map((item, index) => {
|
|
60531
|
+
if (index > 0 && item.probability !== ranked[index - 1]?.probability) rank = index + 1;
|
|
60532
|
+
return {
|
|
60533
|
+
optionIndex: item.optionIndex,
|
|
60534
|
+
rank,
|
|
60535
|
+
probability: item.probability
|
|
60536
|
+
};
|
|
60537
|
+
})
|
|
60538
|
+
};
|
|
60113
60539
|
}
|
|
60114
60540
|
const makeUserInputRanker = Effect.gen(function* () {
|
|
60115
60541
|
const query = yield* ProjectionSnapshotQuery;
|
|
@@ -60128,7 +60554,7 @@ const makeUserInputRanker = Effect.gen(function* () {
|
|
|
60128
60554
|
const decoded = Schema$1.decodeUnknownOption(Schema$1.Struct({ questions: Schema$1.Array(UserInputQuestion) }))(payload);
|
|
60129
60555
|
const question = Option.isSome(decoded) ? decoded.value.questions.find((question) => question.id === input.questionId) : void 0;
|
|
60130
60556
|
if (!question) return yield* fail("This question is no longer pending.");
|
|
60131
|
-
if (question.options.length < 2 || question.options.length > MAX_OPTIONS || question.question.length + question.options.reduce((sum, option) => sum + option.label.length + option.description.length, 0) > MAX_QUESTION_CHARS) return yield* fail("This question cannot be ranked.");
|
|
60557
|
+
if (question.options.length < 2 || question.options.length > MAX_OPTIONS || question.question.length + question.options.reduce((sum, option) => sum + option.label.length + option.description.length, 0) > MAX_QUESTION_CHARS$1) return yield* fail("This question cannot be ranked.");
|
|
60132
60558
|
const context = query.getHandoffContext ? yield* query.getHandoffContext(input.threadId).pipe(Effect.mapError(() => fail("Could not read question context."))) : Option.none();
|
|
60133
60559
|
if (Option.isNone(context)) return yield* fail("Question context is unavailable.");
|
|
60134
60560
|
const state = context.value.transcript;
|
|
@@ -60818,7 +61244,7 @@ const forceStopOwnedProcess = (threadId, options) => threadLocks.withPermit(thre
|
|
|
60818
61244
|
}));
|
|
60819
61245
|
//#endregion
|
|
60820
61246
|
//#region ../../packages/shared/src/toolActivity.ts
|
|
60821
|
-
function asRecord$
|
|
61247
|
+
function asRecord$9(value) {
|
|
60822
61248
|
return value !== null && typeof value === "object" && !Array.isArray(value) ? value : void 0;
|
|
60823
61249
|
}
|
|
60824
61250
|
function asTrimmedString$1(value) {
|
|
@@ -60848,10 +61274,10 @@ function extractCommandFromTitle$1(title) {
|
|
|
60848
61274
|
return /`([^`]+)`/u.exec(title)?.[1]?.trim() || void 0;
|
|
60849
61275
|
}
|
|
60850
61276
|
function extractToolCommand(data, title) {
|
|
60851
|
-
const item = asRecord$
|
|
60852
|
-
const itemInput = asRecord$
|
|
60853
|
-
const itemResult = asRecord$
|
|
60854
|
-
const rawInput = asRecord$
|
|
61277
|
+
const item = asRecord$9(data?.item);
|
|
61278
|
+
const itemInput = asRecord$9(item?.input);
|
|
61279
|
+
const itemResult = asRecord$9(item?.result);
|
|
61280
|
+
const rawInput = asRecord$9(data?.rawInput);
|
|
60855
61281
|
const direct = [
|
|
60856
61282
|
normalizeCommandValue$1(item?.command),
|
|
60857
61283
|
normalizeCommandValue$1(itemInput?.command),
|
|
@@ -60879,7 +61305,7 @@ function collectPaths(value, paths, seen, depth) {
|
|
|
60879
61305
|
}
|
|
60880
61306
|
return;
|
|
60881
61307
|
}
|
|
60882
|
-
const record = asRecord$
|
|
61308
|
+
const record = asRecord$9(value);
|
|
60883
61309
|
if (!record) return;
|
|
60884
61310
|
for (const key of [
|
|
60885
61311
|
"path",
|
|
@@ -60938,7 +61364,7 @@ function deriveToolActivityPresentation(input) {
|
|
|
60938
61364
|
const title = asTrimmedString$1(input.title);
|
|
60939
61365
|
const detail = stripTrailingExitCode(asTrimmedString$1(input.detail));
|
|
60940
61366
|
const fallbackSummary = asTrimmedString$1(input.fallbackSummary) ?? "Tool";
|
|
60941
|
-
const data = asRecord$
|
|
61367
|
+
const data = asRecord$9(input.data);
|
|
60942
61368
|
const command = extractToolCommand(data, title);
|
|
60943
61369
|
const primaryPath = extractPrimaryPath(data);
|
|
60944
61370
|
const action = classifyToolAction({
|
|
@@ -60962,7 +61388,7 @@ function deriveToolActivityPresentation(input) {
|
|
|
60962
61388
|
...primaryPath ? { detail: primaryPath } : {}
|
|
60963
61389
|
};
|
|
60964
61390
|
if (action === "search") {
|
|
60965
|
-
const query = asTrimmedString$1(asRecord$
|
|
61391
|
+
const query = asTrimmedString$1(asRecord$9(data?.rawInput)?.query) ?? asTrimmedString$1(asRecord$9(data?.rawInput)?.pattern) ?? asTrimmedString$1(asRecord$9(data?.rawInput)?.searchTerm);
|
|
60966
61392
|
return {
|
|
60967
61393
|
summary: "Searched files",
|
|
60968
61394
|
...query ? { detail: query } : {}
|
|
@@ -98870,7 +99296,7 @@ const makeOpenCodeRuntime = Effect.gen(function* () {
|
|
|
98870
99296
|
};
|
|
98871
99297
|
});
|
|
98872
99298
|
var OpenCodeRuntime = class extends Context.Service()("@p4code/cli/provider/opencodeRuntime") {};
|
|
98873
|
-
const OpenCodeRuntimeLive = Layer.effect(OpenCodeRuntime, makeOpenCodeRuntime).pipe(Layer.provide(layer$
|
|
99299
|
+
const OpenCodeRuntimeLive = Layer.effect(OpenCodeRuntime, makeOpenCodeRuntime).pipe(Layer.provide(layer$123));
|
|
98874
99300
|
//#endregion
|
|
98875
99301
|
//#region src/textGeneration/OpenCodeTextGeneration.ts
|
|
98876
99302
|
const OPENCODE_TEXT_GENERATION_IDLE_TTL = "30 seconds";
|
|
@@ -103776,6 +104202,136 @@ const openForActiveThread = Effect.fn("DeviceThreadAdmission.open")(function* (i
|
|
|
103776
104202
|
function isRegisteredProviderWorkspace(cwd, projects, threads) {
|
|
103777
104203
|
return projects.some((project) => project.workspaceRoot === cwd) || threads.some((thread) => thread.worktreePath === cwd);
|
|
103778
104204
|
}
|
|
104205
|
+
//#endregion
|
|
104206
|
+
//#region src/hub/HubActivityTransport.ts
|
|
104207
|
+
const REQUEST_TIMEOUT_MS$3 = 8e3;
|
|
104208
|
+
const CLEANUP_TIMEOUT_MS = 1e3;
|
|
104209
|
+
const MAX_RESPONSE_BYTES = 4096;
|
|
104210
|
+
const MAX_REQUEST_BYTES = 16 * 1024;
|
|
104211
|
+
const MAX_BEARER_CHARS = 8192;
|
|
104212
|
+
var HubActivityUnavailable = class extends Data.TaggedError("HubActivityUnavailable") {};
|
|
104213
|
+
var HubActivityUnauthorized = class extends Data.TaggedError("HubActivityUnauthorized") {};
|
|
104214
|
+
/** All errors are payload-free; late responses still cancel their bodies after owner interruption. */
|
|
104215
|
+
const makeHubActivityTransport = Effect.fn("HubActivityTransport.make")(function* (settings, fetcher = fetch) {
|
|
104216
|
+
const origin = yield* Effect.try({
|
|
104217
|
+
try: () => {
|
|
104218
|
+
const url = new URL(settings.baseUrl);
|
|
104219
|
+
if (!["http:", "https:"].includes(url.protocol) || url.username || url.password || url.search || url.hash || url.pathname !== "/" || settings.token.length === 0 || settings.token.length > MAX_BEARER_CHARS || !/^[\x21-\x7e]+$/.test(settings.token)) throw new HubActivityUnavailable();
|
|
104220
|
+
return url.origin;
|
|
104221
|
+
},
|
|
104222
|
+
catch: () => new HubActivityUnavailable()
|
|
104223
|
+
});
|
|
104224
|
+
const request = Effect.fn("HubActivityTransport.request")(function* (path, body, membership) {
|
|
104225
|
+
return yield* Effect.tryPromise({
|
|
104226
|
+
try: async (signal) => {
|
|
104227
|
+
const controller = new AbortController();
|
|
104228
|
+
const abort = () => controller.abort();
|
|
104229
|
+
signal.addEventListener("abort", abort, { once: true });
|
|
104230
|
+
if (signal.aborted) abort();
|
|
104231
|
+
const timer = setTimeout(abort, REQUEST_TIMEOUT_MS$3);
|
|
104232
|
+
let reader;
|
|
104233
|
+
const cancel = async (work) => {
|
|
104234
|
+
let timeout;
|
|
104235
|
+
try {
|
|
104236
|
+
await Promise.race([Promise.resolve().then(work).catch(() => void 0), new Promise((resolve) => {
|
|
104237
|
+
timeout = setTimeout(resolve, CLEANUP_TIMEOUT_MS);
|
|
104238
|
+
})]);
|
|
104239
|
+
} finally {
|
|
104240
|
+
if (timeout) clearTimeout(timeout);
|
|
104241
|
+
}
|
|
104242
|
+
};
|
|
104243
|
+
try {
|
|
104244
|
+
if (controller.signal.aborted) throw new HubActivityUnavailable();
|
|
104245
|
+
const response = await fetcher(`${origin}${path}`, {
|
|
104246
|
+
method: body === void 0 ? "GET" : "POST",
|
|
104247
|
+
headers: {
|
|
104248
|
+
authorization: `Bearer ${settings.token}`,
|
|
104249
|
+
...membership ? {
|
|
104250
|
+
"x-p4-hub-account": membership.accountId,
|
|
104251
|
+
"x-p4-hub-environment": membership.environmentId
|
|
104252
|
+
} : {},
|
|
104253
|
+
...body === void 0 ? {} : { "content-type": "application/json" }
|
|
104254
|
+
},
|
|
104255
|
+
body,
|
|
104256
|
+
signal: controller.signal,
|
|
104257
|
+
redirect: "error",
|
|
104258
|
+
credentials: "omit",
|
|
104259
|
+
cache: "no-store"
|
|
104260
|
+
});
|
|
104261
|
+
if (controller.signal.aborted) {
|
|
104262
|
+
await cancel(() => response.body?.cancel() ?? Promise.resolve());
|
|
104263
|
+
throw new HubActivityUnavailable();
|
|
104264
|
+
}
|
|
104265
|
+
reader = response.body?.getReader();
|
|
104266
|
+
if (response.status === 401 || response.status === 403) throw new HubActivityUnauthorized();
|
|
104267
|
+
if (response.status !== 200 || !/^application\/json(?:\s*;|$)/i.test(response.headers.get("content-type") ?? "") || !reader) throw new HubActivityUnavailable();
|
|
104268
|
+
let length = 0;
|
|
104269
|
+
const chunks = [];
|
|
104270
|
+
while (true) {
|
|
104271
|
+
const item = await reader.read();
|
|
104272
|
+
if (controller.signal.aborted) throw new HubActivityUnavailable();
|
|
104273
|
+
if (item.done) break;
|
|
104274
|
+
length += item.value.byteLength;
|
|
104275
|
+
if (length > MAX_RESPONSE_BYTES) throw new HubActivityUnavailable();
|
|
104276
|
+
if (item.value.byteLength) chunks.push(item.value);
|
|
104277
|
+
}
|
|
104278
|
+
const bytes = new Uint8Array(length);
|
|
104279
|
+
let offset = 0;
|
|
104280
|
+
for (const chunk of chunks) {
|
|
104281
|
+
bytes.set(chunk, offset);
|
|
104282
|
+
offset += chunk.byteLength;
|
|
104283
|
+
}
|
|
104284
|
+
return Schema$1.decodeUnknownSync(Schema$1.UnknownFromJsonString)(new TextDecoder("utf-8", { fatal: true }).decode(bytes));
|
|
104285
|
+
} finally {
|
|
104286
|
+
controller.abort();
|
|
104287
|
+
clearTimeout(timer);
|
|
104288
|
+
signal.removeEventListener("abort", abort);
|
|
104289
|
+
if (reader) await cancel(() => reader.cancel());
|
|
104290
|
+
}
|
|
104291
|
+
},
|
|
104292
|
+
catch: (error) => error instanceof HubActivityUnauthorized ? error : new HubActivityUnavailable()
|
|
104293
|
+
}).pipe(Effect.timeout(9e3), Effect.mapError((error) => error instanceof HubActivityUnauthorized ? error : new HubActivityUnavailable()));
|
|
104294
|
+
});
|
|
104295
|
+
return {
|
|
104296
|
+
identity: Effect.fn("HubActivityTransport.identity")(function* () {
|
|
104297
|
+
const membership = yield* request("/v1/identity").pipe(Effect.flatMap(Schema$1.decodeUnknownEffect(HubServerMembership, { onExcessProperty: "error" })), Effect.mapError((error) => error instanceof HubActivityUnauthorized ? error : new HubActivityUnavailable()));
|
|
104298
|
+
const destination = yield* Schema$1.encodeUnknownEffect(Schema$1.UnknownFromJsonString)([
|
|
104299
|
+
origin,
|
|
104300
|
+
membership.accountId,
|
|
104301
|
+
membership.environmentId
|
|
104302
|
+
]);
|
|
104303
|
+
return {
|
|
104304
|
+
...membership,
|
|
104305
|
+
destination: NodeCrypto.createHash("sha256").update(destination).digest("hex")
|
|
104306
|
+
};
|
|
104307
|
+
}),
|
|
104308
|
+
post: Effect.fn("HubActivityTransport.post")(function* (input, membership) {
|
|
104309
|
+
const activity = yield* Schema$1.decodeUnknownEffect(HubAgentActivityInput)(input, { onExcessProperty: "error" }).pipe(Effect.mapError(() => new HubActivityUnavailable()));
|
|
104310
|
+
const body = yield* Schema$1.encodeEffect(Schema$1.fromJsonString(HubAgentActivityInput))(activity);
|
|
104311
|
+
if (Buffer.byteLength(body) > MAX_REQUEST_BYTES) return yield* Effect.fail(new HubActivityUnavailable());
|
|
104312
|
+
const result = yield* request("/v1/agent-activity", body, membership);
|
|
104313
|
+
return yield* Schema$1.decodeUnknownEffect(Schema$1.Struct({ accepted: Schema$1.Boolean }))(result, { onExcessProperty: "error" }).pipe(Effect.mapError(() => new HubActivityUnavailable()));
|
|
104314
|
+
}),
|
|
104315
|
+
postFact: Effect.fn("HubActivityTransport.postFact")(function* (input, membership) {
|
|
104316
|
+
const fact = yield* Schema$1.decodeUnknownEffect(HubAnalyticsFactInput)(input, { onExcessProperty: "error" }).pipe(Effect.mapError(() => new HubActivityUnavailable()));
|
|
104317
|
+
const body = yield* Schema$1.encodeEffect(Schema$1.fromJsonString(HubAnalyticsFactInput))(fact);
|
|
104318
|
+
if (Buffer.byteLength(body) > MAX_REQUEST_BYTES) return yield* Effect.fail(new HubActivityUnavailable());
|
|
104319
|
+
const result = yield* request("/v1/analytics/facts", body, membership);
|
|
104320
|
+
return yield* Schema$1.decodeUnknownEffect(Schema$1.Struct({ accepted: Schema$1.Boolean }))(result, { onExcessProperty: "error" }).pipe(Effect.mapError(() => new HubActivityUnavailable()));
|
|
104321
|
+
}),
|
|
104322
|
+
queryFacts: Effect.fn("HubActivityTransport.queryFacts")(function* (query, membership) {
|
|
104323
|
+
const search = new URLSearchParams();
|
|
104324
|
+
for (const kind of query.kinds ?? []) search.append("kind", kind);
|
|
104325
|
+
if (query.since !== void 0) search.set("since", query.since);
|
|
104326
|
+
if (query.until !== void 0) search.set("until", query.until);
|
|
104327
|
+
if (query.limit !== void 0) search.set("limit", String(query.limit));
|
|
104328
|
+
if (query.cursor !== void 0) search.set("cursor", query.cursor);
|
|
104329
|
+
const suffix = search.size === 0 ? "" : `?${search.toString()}`;
|
|
104330
|
+
const result = yield* request(`/v1/analytics/facts${suffix}`, void 0, membership);
|
|
104331
|
+
return yield* Schema$1.decodeUnknownEffect(HubAnalyticsPage)(result, { onExcessProperty: "error" }).pipe(Effect.mapError(() => new HubActivityUnavailable()));
|
|
104332
|
+
})
|
|
104333
|
+
};
|
|
104334
|
+
});
|
|
103779
104335
|
(() => {
|
|
103780
104336
|
try {
|
|
103781
104337
|
return process.env.NODE_ENV === "development";
|
|
@@ -104486,7 +105042,7 @@ const NESTED_PAYLOAD_KEYS = [
|
|
|
104486
105042
|
"operations"
|
|
104487
105043
|
];
|
|
104488
105044
|
const MAX_COLLECT_DEPTH = 4;
|
|
104489
|
-
function asRecord$
|
|
105045
|
+
function asRecord$8(value) {
|
|
104490
105046
|
return typeof value === "object" && value !== null && !Array.isArray(value) ? value : null;
|
|
104491
105047
|
}
|
|
104492
105048
|
function pushChangedFilePath(target, value) {
|
|
@@ -104501,7 +105057,7 @@ function collectChangedFilePaths(value, target, depth) {
|
|
|
104501
105057
|
for (const entry of value) collectChangedFilePaths(entry, target, depth + 1);
|
|
104502
105058
|
return;
|
|
104503
105059
|
}
|
|
104504
|
-
const record = asRecord$
|
|
105060
|
+
const record = asRecord$8(value);
|
|
104505
105061
|
if (!record) return;
|
|
104506
105062
|
for (const field of CHANGED_FILE_FIELDS) pushChangedFilePath(target, record[field]);
|
|
104507
105063
|
for (const nestedKey of NESTED_PAYLOAD_KEYS) if (nestedKey in record) collectChangedFilePaths(record[nestedKey], target, depth + 1);
|
|
@@ -104512,7 +105068,7 @@ function collectChangedFilePaths(value, target, depth) {
|
|
|
104512
105068
|
*/
|
|
104513
105069
|
function collectActivityChangedFilePaths(payload) {
|
|
104514
105070
|
const target = /* @__PURE__ */ new Set();
|
|
104515
|
-
collectChangedFilePaths(asRecord$
|
|
105071
|
+
collectChangedFilePaths(asRecord$8(asRecord$8(payload)?.data), target, 0);
|
|
104516
105072
|
return target;
|
|
104517
105073
|
}
|
|
104518
105074
|
/**
|
|
@@ -122485,7 +123041,7 @@ const makeBtwRequestCoordinator = (dependencies) => Effect.gen(function* () {
|
|
|
122485
123041
|
* already negotiated.
|
|
122486
123042
|
*/
|
|
122487
123043
|
const MCP_PROTOCOL_VERSION = "2025-06-18";
|
|
122488
|
-
const REQUEST_TIMEOUT_MS$
|
|
123044
|
+
const REQUEST_TIMEOUT_MS$2 = 15e3;
|
|
122489
123045
|
var McpToolCallError = class extends Schema$1.TaggedErrorClass()("McpToolCallError", {
|
|
122490
123046
|
detail: Schema$1.String,
|
|
122491
123047
|
status: Schema$1.NullOr(Schema$1.Number)
|
|
@@ -122549,7 +123105,7 @@ const callMcpTool = Effect.fn("McpToolClient.callMcpTool")(function* (input) {
|
|
|
122549
123105
|
...input.headers,
|
|
122550
123106
|
...extraHeaders,
|
|
122551
123107
|
accept: "application/json, text/event-stream"
|
|
122552
|
-
}), body)).pipe(Effect.timeout(REQUEST_TIMEOUT_MS$
|
|
123108
|
+
}), body)).pipe(Effect.timeout(REQUEST_TIMEOUT_MS$2), Effect.catchCause((cause) => Effect.fail(new McpToolCallError({
|
|
122553
123109
|
detail: `Could not reach ${input.url}: ${String(cause)}`,
|
|
122554
123110
|
status: null
|
|
122555
123111
|
}))));
|
|
@@ -122643,7 +123199,7 @@ var LinearUnavailable = class extends Schema$1.TaggedErrorClass()("LinearUnavail
|
|
|
122643
123199
|
return this.detail;
|
|
122644
123200
|
}
|
|
122645
123201
|
};
|
|
122646
|
-
const asRecord$
|
|
123202
|
+
const asRecord$7 = (value) => typeof value === "object" && value !== null && !Array.isArray(value) ? value : void 0;
|
|
122647
123203
|
const asArray$1 = (value) => Array.isArray(value) ? value : void 0;
|
|
122648
123204
|
/**
|
|
122649
123205
|
* Find the payload inside whatever the tool returned.
|
|
@@ -122673,22 +123229,22 @@ const readPayload = (result) => {
|
|
|
122673
123229
|
*/
|
|
122674
123230
|
const readRows = (payload) => {
|
|
122675
123231
|
const direct = asArray$1(payload);
|
|
122676
|
-
if (direct !== void 0) return direct.map(asRecord$
|
|
122677
|
-
const record = asRecord$
|
|
123232
|
+
if (direct !== void 0) return direct.map(asRecord$7).filter((row) => row !== void 0);
|
|
123233
|
+
const record = asRecord$7(payload);
|
|
122678
123234
|
if (record === void 0) return [];
|
|
122679
123235
|
for (const value of Object.values(record)) {
|
|
122680
123236
|
const rows = asArray$1(value);
|
|
122681
|
-
if (rows !== void 0) return rows.map(asRecord$
|
|
123237
|
+
if (rows !== void 0) return rows.map(asRecord$7).filter((row) => row !== void 0);
|
|
122682
123238
|
}
|
|
122683
123239
|
return [];
|
|
122684
123240
|
};
|
|
122685
123241
|
/** The single object out of a result, unwrapping one level of nesting. */
|
|
122686
123242
|
const readOne = (payload) => {
|
|
122687
|
-
const record = asRecord$
|
|
123243
|
+
const record = asRecord$7(payload);
|
|
122688
123244
|
if (record === void 0) return;
|
|
122689
123245
|
if (record["id"] !== void 0 || record["identifier"] !== void 0) return record;
|
|
122690
123246
|
for (const value of Object.values(record)) {
|
|
122691
|
-
const nested = asRecord$
|
|
123247
|
+
const nested = asRecord$7(value);
|
|
122692
123248
|
if (nested?.["id"] !== void 0) return nested;
|
|
122693
123249
|
}
|
|
122694
123250
|
};
|
|
@@ -122798,7 +123354,7 @@ const TrackerSecretsLive = Layer.effect(TrackerSecrets, makeTrackerSecrets);
|
|
|
122798
123354
|
*
|
|
122799
123355
|
* @module tracker/graphqlRequest
|
|
122800
123356
|
*/
|
|
122801
|
-
const REQUEST_TIMEOUT_MS$
|
|
123357
|
+
const REQUEST_TIMEOUT_MS$1 = 15e3;
|
|
122802
123358
|
var GraphqlRequestError = class extends Schema$1.TaggedErrorClass()("GraphqlRequestError", {
|
|
122803
123359
|
detail: Schema$1.String,
|
|
122804
123360
|
status: Schema$1.NullOr(Schema$1.Number)
|
|
@@ -122807,14 +123363,14 @@ var GraphqlRequestError = class extends Schema$1.TaggedErrorClass()("GraphqlRequ
|
|
|
122807
123363
|
return this.detail;
|
|
122808
123364
|
}
|
|
122809
123365
|
};
|
|
122810
|
-
const asRecord$
|
|
123366
|
+
const asRecord$6 = (value) => typeof value === "object" && value !== null && !Array.isArray(value) ? value : void 0;
|
|
122811
123367
|
const errorMessages = (value) => {
|
|
122812
123368
|
if (!Array.isArray(value)) return [];
|
|
122813
|
-
return value.map((entry) => asRecord$
|
|
123369
|
+
return value.map((entry) => asRecord$6(entry)?.["message"]).filter((message) => typeof message === "string");
|
|
122814
123370
|
};
|
|
122815
123371
|
const parseJsonRecord = (text) => {
|
|
122816
123372
|
try {
|
|
122817
|
-
return asRecord$
|
|
123373
|
+
return asRecord$6(JSON.parse(text));
|
|
122818
123374
|
} catch {
|
|
122819
123375
|
return;
|
|
122820
123376
|
}
|
|
@@ -122823,7 +123379,7 @@ const graphqlRequest = Effect.fn("tracker/graphqlRequest")(function* (input) {
|
|
|
122823
123379
|
const response = yield* (yield* HttpClient$1.HttpClient).execute(HttpClientRequest$1.bodyJsonUnsafe(HttpClientRequest$1.setHeaders(HttpClientRequest$1.post(input.url), input.headers), {
|
|
122824
123380
|
query: input.query,
|
|
122825
123381
|
variables: input.variables ?? {}
|
|
122826
|
-
})).pipe(Effect.timeout(REQUEST_TIMEOUT_MS$
|
|
123382
|
+
})).pipe(Effect.timeout(REQUEST_TIMEOUT_MS$1), Effect.catchCause((cause) => Effect.fail(new GraphqlRequestError({
|
|
122827
123383
|
detail: `Could not reach ${input.url}: ${String(cause)}`,
|
|
122828
123384
|
status: null
|
|
122829
123385
|
}))));
|
|
@@ -122838,7 +123394,7 @@ const graphqlRequest = Effect.fn("tracker/graphqlRequest")(function* (input) {
|
|
|
122838
123394
|
status: response.status
|
|
122839
123395
|
});
|
|
122840
123396
|
return {
|
|
122841
|
-
data: asRecord$
|
|
123397
|
+
data: asRecord$6(record["data"]),
|
|
122842
123398
|
errors: errorMessages(record["errors"])
|
|
122843
123399
|
};
|
|
122844
123400
|
});
|
|
@@ -122891,7 +123447,7 @@ const STATE_TYPES = /* @__PURE__ */ new Set([
|
|
|
122891
123447
|
"completed",
|
|
122892
123448
|
"canceled"
|
|
122893
123449
|
]);
|
|
122894
|
-
const asRecord$
|
|
123450
|
+
const asRecord$5 = (value) => typeof value === "object" && value !== null && !Array.isArray(value) ? value : void 0;
|
|
122895
123451
|
const asArray = (value) => Array.isArray(value) ? value : [];
|
|
122896
123452
|
const asString = (value) => typeof value === "string" && value.trim().length > 0 ? value.trim() : void 0;
|
|
122897
123453
|
/**
|
|
@@ -122901,9 +123457,9 @@ const asString = (value) => typeof value === "string" && value.trim().length > 0
|
|
|
122901
123457
|
* `labels` stay structured - the mapping already accepts both forms.
|
|
122902
123458
|
*/
|
|
122903
123459
|
const toIssueRow = (node) => {
|
|
122904
|
-
const state = asRecord$
|
|
122905
|
-
const parent = asRecord$
|
|
122906
|
-
const labels = asRecord$
|
|
123460
|
+
const state = asRecord$5(node["state"]);
|
|
123461
|
+
const parent = asRecord$5(node["parent"]);
|
|
123462
|
+
const labels = asRecord$5(node["labels"]);
|
|
122907
123463
|
return {
|
|
122908
123464
|
id: node["id"],
|
|
122909
123465
|
identifier: node["identifier"],
|
|
@@ -122943,7 +123499,7 @@ const makeLinearApiTransport = Effect.gen(function* () {
|
|
|
122943
123499
|
})));
|
|
122944
123500
|
/** The one entity out of `data`, or a failure carrying Linear's own words. */
|
|
122945
123501
|
const readEntity = (response, key) => {
|
|
122946
|
-
const entity = asRecord$
|
|
123502
|
+
const entity = asRecord$5(response.data?.[key]);
|
|
122947
123503
|
if (entity !== void 0) return Effect.succeed(entity);
|
|
122948
123504
|
if (response.errors.length === 0) return Effect.void.pipe(Effect.as(void 0));
|
|
122949
123505
|
if (response.errors.some((message) => /not found|does not exist/iu.test(message))) return Effect.void.pipe(Effect.as(void 0));
|
|
@@ -122952,7 +123508,7 @@ const makeLinearApiTransport = Effect.gen(function* () {
|
|
|
122952
123508
|
detail: response.errors.join("; ")
|
|
122953
123509
|
}));
|
|
122954
123510
|
};
|
|
122955
|
-
const nodesOf = (response, key) => asArray(asRecord$
|
|
123511
|
+
const nodesOf = (response, key) => asArray(asRecord$5(response.data?.[key])?.["nodes"]).map(asRecord$5).filter((node) => node !== void 0);
|
|
122956
123512
|
const resolveTeamId = (apiKey, team) => Effect.gen(function* () {
|
|
122957
123513
|
const response = yield* request(apiKey, `query($filter: TeamFilter) { teams(filter: $filter, first: 2) { nodes { id } } }`, { filter: { or: [{ key: { eqIgnoreCase: team } }, { name: { eqIgnoreCase: team } }] } });
|
|
122958
123514
|
const id = asString(nodesOf(response, "teams")[0]?.["id"]);
|
|
@@ -122965,7 +123521,7 @@ const makeLinearApiTransport = Effect.gen(function* () {
|
|
|
122965
123521
|
const teamIdOfIssue = (apiKey, issueId) => Effect.gen(function* () {
|
|
122966
123522
|
const response = yield* request(apiKey, `query($id: String!) { issue(id: $id) { team { id } } }`, { id: issueId });
|
|
122967
123523
|
const issue = yield* readEntity(response, "issue");
|
|
122968
|
-
const id = asString(asRecord$
|
|
123524
|
+
const id = asString(asRecord$5(issue?.["team"])?.["id"]);
|
|
122969
123525
|
if (id === void 0) return yield* new LinearUnavailable({
|
|
122970
123526
|
reason: "failed",
|
|
122971
123527
|
detail: `Linear has no issue "${issueId}" to read a team from.`
|
|
@@ -123085,7 +123641,7 @@ const makeLinearApiTransport = Effect.gen(function* () {
|
|
|
123085
123641
|
input
|
|
123086
123642
|
});
|
|
123087
123643
|
const payload = yield* readEntity(response, issueId === void 0 ? "issueCreate" : "issueUpdate");
|
|
123088
|
-
const issue = asRecord$
|
|
123644
|
+
const issue = asRecord$5(payload?.["issue"]);
|
|
123089
123645
|
if (issue === void 0) return yield* new LinearUnavailable({
|
|
123090
123646
|
reason: "failed",
|
|
123091
123647
|
detail: response.errors.length > 0 ? response.errors.join("; ") : "Linear accepted the write but returned no issue."
|
|
@@ -123115,11 +123671,11 @@ const makeLinearApiTransport = Effect.gen(function* () {
|
|
|
123115
123671
|
reason: "not_authorized",
|
|
123116
123672
|
detail: "Linear rejected this API key."
|
|
123117
123673
|
}) : error));
|
|
123118
|
-
if (asString(asRecord$
|
|
123674
|
+
if (asString(asRecord$5(response.data?.["viewer"])?.["id"]) === void 0) return yield* new LinearUnavailable({
|
|
123119
123675
|
reason: "not_authorized",
|
|
123120
123676
|
detail: response.errors.length > 0 ? response.errors.join("; ") : "Linear rejected this API key."
|
|
123121
123677
|
});
|
|
123122
|
-
const organization = asRecord$
|
|
123678
|
+
const organization = asRecord$5(response.data?.["organization"]);
|
|
123123
123679
|
return { workspace: asString(organization?.["name"]) ?? asString(organization?.["urlKey"]) ?? null };
|
|
123124
123680
|
});
|
|
123125
123681
|
return {
|
|
@@ -123340,7 +123896,7 @@ const STATUS_FROM_LINEAR_TYPE = {
|
|
|
123340
123896
|
completed: "done",
|
|
123341
123897
|
canceled: "cancelled"
|
|
123342
123898
|
};
|
|
123343
|
-
const asRecord$
|
|
123899
|
+
const asRecord$4 = (value) => typeof value === "object" && value !== null && !Array.isArray(value) ? value : void 0;
|
|
123344
123900
|
const text = (value) => typeof value === "string" && value.trim().length > 0 ? value.trim() : void 0;
|
|
123345
123901
|
/**
|
|
123346
123902
|
* A person's name out of whatever Linear put in the field: an object when the
|
|
@@ -123349,14 +123905,14 @@ const text = (value) => typeof value === "string" && value.trim().length > 0 ? v
|
|
|
123349
123905
|
const personName = (value) => {
|
|
123350
123906
|
const direct = text(value);
|
|
123351
123907
|
if (direct !== void 0) return direct;
|
|
123352
|
-
const record = asRecord$
|
|
123908
|
+
const record = asRecord$4(value);
|
|
123353
123909
|
if (record === void 0) return null;
|
|
123354
123910
|
return text(record["displayName"]) ?? text(record["name"]) ?? text(record["email"]) ?? null;
|
|
123355
123911
|
};
|
|
123356
123912
|
/** Label names, from either `["Bug"]` or `[{ name: "Bug" }]`. */
|
|
123357
123913
|
const labelNames = (value) => {
|
|
123358
123914
|
if (!Array.isArray(value)) return [];
|
|
123359
|
-
return value.map((entry) => text(entry) ?? text(asRecord$
|
|
123915
|
+
return value.map((entry) => text(entry) ?? text(asRecord$4(entry)?.["name"])).filter((name) => name !== void 0);
|
|
123360
123916
|
};
|
|
123361
123917
|
/**
|
|
123362
123918
|
* The p4code status an issue is in.
|
|
@@ -123401,7 +123957,7 @@ const statusToLinearState = (status) => {
|
|
|
123401
123957
|
* Medium, and quietly rewrite the local row with it.
|
|
123402
123958
|
*/
|
|
123403
123959
|
const priorityFromLinear = (value) => {
|
|
123404
|
-
const numeric = typeof value === "number" ? value : asRecord$
|
|
123960
|
+
const numeric = typeof value === "number" ? value : asRecord$4(value)?.["value"];
|
|
123405
123961
|
return typeof numeric === "number" ? PRIORITY_FROM_LINEAR[numeric] ?? "none" : "none";
|
|
123406
123962
|
};
|
|
123407
123963
|
const priorityToLinear = (priority) => PRIORITY_TO_LINEAR[priority];
|
|
@@ -126920,7 +127476,7 @@ function toAssetFilePath(repositoryPath, root) {
|
|
|
126920
127476
|
const REGISTRY_SEARCH_URL = "https://www.skills.sh/api/search";
|
|
126921
127477
|
const GITHUB_API_URL = "https://api.github.com";
|
|
126922
127478
|
const GITHUB_RAW_URL = "https://raw.githubusercontent.com";
|
|
126923
|
-
const REQUEST_TIMEOUT_MS
|
|
127479
|
+
const REQUEST_TIMEOUT_MS = 15e3;
|
|
126924
127480
|
/** More than a person reads, few enough that the panel is a list and not a feed. */
|
|
126925
127481
|
const MAX_SEARCH_RESULTS = 25;
|
|
126926
127482
|
/**
|
|
@@ -126960,7 +127516,7 @@ const emptyFetch = (id, unavailable) => ({
|
|
|
126960
127516
|
const make$29 = Effect.gen(function* () {
|
|
126961
127517
|
const http = yield* HttpClient.HttpClient;
|
|
126962
127518
|
const request = Effect.fn("SkillRegistry.request")(function* (url) {
|
|
126963
|
-
return yield* http.execute(HttpClientRequest.get(url).pipe(HttpClientRequest.setHeader("accept", "application/json"), HttpClientRequest.setHeader("user-agent", "p4code"))).pipe(Effect.timeout(REQUEST_TIMEOUT_MS
|
|
127519
|
+
return yield* http.execute(HttpClientRequest.get(url).pipe(HttpClientRequest.setHeader("accept", "application/json"), HttpClientRequest.setHeader("user-agent", "p4code"))).pipe(Effect.timeout(REQUEST_TIMEOUT_MS));
|
|
126964
127520
|
});
|
|
126965
127521
|
const search = (query) => Effect.gen(function* () {
|
|
126966
127522
|
const response = yield* request(`${REGISTRY_SEARCH_URL}?q=${encodeURIComponent(query)}`).pipe(Effect.orElseSucceed(() => null));
|
|
@@ -130108,6 +130664,7 @@ const RPC_REQUIRED_SCOPE = /* @__PURE__ */ new Map([
|
|
|
130108
130664
|
[WS_METHODS.storageCleanupSweep, AuthOrchestrationOperateScope],
|
|
130109
130665
|
[WS_METHODS.worktreeCleanupPreview, AuthOrchestrationReadScope],
|
|
130110
130666
|
[WS_METHODS.worktreeCleanupSweep, AuthOrchestrationOperateScope],
|
|
130667
|
+
[WS_METHODS.hubAnalyticsQuery, AuthOrchestrationReadScope],
|
|
130111
130668
|
[WS_METHODS.hubGetSyncStatus, AuthOrchestrationReadScope],
|
|
130112
130669
|
[WS_METHODS.hubConnect, AuthOrchestrationOperateScope],
|
|
130113
130670
|
[WS_METHODS.hubDisconnect, AuthOrchestrationOperateScope],
|
|
@@ -131135,6 +131692,13 @@ const makeWsRpcLayer = (currentSession, previewAutomationBroker, computerUseBrok
|
|
|
131135
131692
|
[WS_METHODS.storageCleanupSweep]: (_input) => observeRpcEffect$1(WS_METHODS.storageCleanupSweep, storageCleanup.sweep, { "rpc.aggregate": "storageCleanup" }),
|
|
131136
131693
|
[WS_METHODS.worktreeCleanupPreview]: (_input) => observeRpcEffect$1(WS_METHODS.worktreeCleanupPreview, worktreeCleanup.preview, { "rpc.aggregate": "worktreeCleanup" }),
|
|
131137
131694
|
[WS_METHODS.worktreeCleanupSweep]: (_input) => observeRpcEffect$1(WS_METHODS.worktreeCleanupSweep, worktreeCleanup.sweep, { "rpc.aggregate": "worktreeCleanup" }),
|
|
131695
|
+
[WS_METHODS.hubAnalyticsQuery]: (query) => observeRpcEffect$1(WS_METHODS.hubAnalyticsQuery, Effect.gen(function* () {
|
|
131696
|
+
const state = yield* hubLink.current;
|
|
131697
|
+
if (Option.isNone(state.settings)) return yield* new HubLinkError({ detail: "No hub is configured." });
|
|
131698
|
+
const transport = yield* makeHubActivityTransport(state.settings.value);
|
|
131699
|
+
const membership = yield* transport.identity();
|
|
131700
|
+
return yield* transport.queryFacts(query, membership);
|
|
131701
|
+
}).pipe(Effect.mapError((cause) => new HubLinkError({ detail: cause instanceof HubLinkError ? cause.detail : "The hub could not be reached." }))), { "rpc.aggregate": "hub" }),
|
|
131138
131702
|
[WS_METHODS.hubGetSyncStatus]: (_input) => observeRpcEffect$1(WS_METHODS.hubGetSyncStatus, assetSync.status, { "rpc.aggregate": "hub" }),
|
|
131139
131703
|
[WS_METHODS.hubConnect]: (input) => observeRpcEffect$1(WS_METHODS.hubConnect, hubLink.connect(input).pipe(Effect.mapError((cause) => new HubLinkError({ detail: cause.message })), Effect.andThen(assetSync.status)), { "rpc.aggregate": "hub" }),
|
|
131140
131704
|
[WS_METHODS.hubDisconnect]: (_input) => observeRpcEffect$1(WS_METHODS.hubDisconnect, hubLink.disconnect.pipe(Effect.mapError((cause) => new HubLinkError({ detail: cause.message })), Effect.andThen(assetSync.status)), { "rpc.aggregate": "hub" }),
|
|
@@ -132332,6 +132896,23 @@ const makeProviderService = Effect.fn("makeProviderService")(function* (options)
|
|
|
132332
132896
|
...event,
|
|
132333
132897
|
requestId: RuntimeRequestId.make(String(pending.requestId))
|
|
132334
132898
|
};
|
|
132899
|
+
/**
|
|
132900
|
+
* Stamp a compacted event with how long its pass actually took.
|
|
132901
|
+
*
|
|
132902
|
+
* The service owns both ends of a compaction, so it is the only place that
|
|
132903
|
+
* can measure one; every reader downstream would be guessing from
|
|
132904
|
+
* surrounding timestamps instead.
|
|
132905
|
+
*/
|
|
132906
|
+
const withCompactionMeasurement = (event, pending, nowMs) => event.type === "thread.state.changed" && event.payload.state === "compacted" ? {
|
|
132907
|
+
...event,
|
|
132908
|
+
payload: {
|
|
132909
|
+
...event.payload,
|
|
132910
|
+
detail: {
|
|
132911
|
+
...typeof event.payload.detail === "object" && event.payload.detail !== null ? event.payload.detail : {},
|
|
132912
|
+
durationMs: Math.max(0, nowMs - pending.startedAtMs)
|
|
132913
|
+
}
|
|
132914
|
+
}
|
|
132915
|
+
} : event;
|
|
132335
132916
|
const compactionTerminal = (event) => event.type === "turn.completed" ? event.payload.state : event.type === "runtime.error" || event.type === "turn.aborted" || event.type === "session.exited" ? event.type : null;
|
|
132336
132917
|
const processFallbackCompactionEvent = (pending, event) => Effect.gen(function* () {
|
|
132337
132918
|
if (pendingCompactions.get(event.threadId) !== pending) {
|
|
@@ -132341,7 +132922,7 @@ const makeProviderService = Effect.fn("makeProviderService")(function* (options)
|
|
|
132341
132922
|
const matchesTurn = event.turnId !== void 0 && event.turnId === pending.expectedTurnId;
|
|
132342
132923
|
if (matchesTurn && isCompactedEvent(event)) {
|
|
132343
132924
|
pending.compactedEventObserved = true;
|
|
132344
|
-
yield* publishRuntimeEvent(withCompactionRequestId(event, pending));
|
|
132925
|
+
yield* publishRuntimeEvent(withCompactionMeasurement(withCompactionRequestId(event, pending), pending, yield* Clock.currentTimeMillis));
|
|
132345
132926
|
return;
|
|
132346
132927
|
}
|
|
132347
132928
|
yield* publishRuntimeEvent(event);
|
|
@@ -132354,7 +132935,10 @@ const makeProviderService = Effect.fn("makeProviderService")(function* (options)
|
|
|
132354
132935
|
type: "thread.state.changed",
|
|
132355
132936
|
payload: {
|
|
132356
132937
|
state: "compacted",
|
|
132357
|
-
detail: {
|
|
132938
|
+
detail: {
|
|
132939
|
+
source: "provider-native-command",
|
|
132940
|
+
durationMs: Math.max(0, (yield* Clock.currentTimeMillis) - pending.startedAtMs)
|
|
132941
|
+
}
|
|
132358
132942
|
},
|
|
132359
132943
|
...pending.requestId !== void 0 ? { requestId: RuntimeRequestId.make(String(pending.requestId)) } : {}
|
|
132360
132944
|
};
|
|
@@ -132386,7 +132970,7 @@ const makeProviderService = Effect.fn("makeProviderService")(function* (options)
|
|
|
132386
132970
|
if (pendingCompaction.native) {
|
|
132387
132971
|
const compacted = isCompactedEvent(canonicalEvent);
|
|
132388
132972
|
const terminal = compacted ? "completed" : canonicalEvent.type === "turn.completed" && canonicalEvent.payload.state === "completed" ? null : compactionTerminal(canonicalEvent);
|
|
132389
|
-
yield* publishRuntimeEvent(compacted ? withCompactionRequestId(canonicalEvent, pendingCompaction) : canonicalEvent);
|
|
132973
|
+
yield* publishRuntimeEvent(compacted ? withCompactionMeasurement(withCompactionRequestId(canonicalEvent, pendingCompaction), pendingCompaction, yield* Clock.currentTimeMillis) : canonicalEvent);
|
|
132390
132974
|
if (terminal !== null) yield* settleCompaction(canonicalEvent.threadId, pendingCompaction, terminal);
|
|
132391
132975
|
return;
|
|
132392
132976
|
}
|
|
@@ -132685,6 +133269,7 @@ const makeProviderService = Effect.fn("makeProviderService")(function* (options)
|
|
|
132685
133269
|
const completion = yield* Deferred.make();
|
|
132686
133270
|
const pending = {
|
|
132687
133271
|
completion,
|
|
133272
|
+
startedAtMs: yield* Clock.currentTimeMillis,
|
|
132688
133273
|
native: compaction.type === "native",
|
|
132689
133274
|
providerInstanceId: routed.instanceId,
|
|
132690
133275
|
requestId,
|
|
@@ -136107,7 +136692,7 @@ const watchesAsFusionSupervisor = Effect.fn("mcp.watch.watchesAsFusionSupervisor
|
|
|
136107
136692
|
return (yield* (yield* ProjectionSnapshotQuery).getActiveThreadPairs().pipe(Effect.orElseSucceed(() => []))).some((pair) => pair.watcherThreadId === readerThreadId && pair.implementerThreadId === watchedThreadId);
|
|
136108
136693
|
});
|
|
136109
136694
|
/** Bounded so one report cannot dominate a thread's activity storage. */
|
|
136110
|
-
const clip = (value, limit) => {
|
|
136695
|
+
const clip$1 = (value, limit) => {
|
|
136111
136696
|
const trimmed = value?.trim();
|
|
136112
136697
|
if (!trimmed) return null;
|
|
136113
136698
|
return trimmed.length <= limit ? trimmed : `${trimmed.slice(0, limit)}…`;
|
|
@@ -136143,8 +136728,8 @@ const WatchToolkitHandlersLive = WatchToolkit.toLayer({
|
|
|
136143
136728
|
serverReadActivityId: serverEvidenceReadActivityId(input.threadId, input.throughSequence),
|
|
136144
136729
|
closedAt: createdAt
|
|
136145
136730
|
});
|
|
136146
|
-
const summary = clip(input.summary, THREAD_EVIDENCE_REPORT_MAX_CHARS);
|
|
136147
|
-
const reason = clip(input.reason, 500);
|
|
136731
|
+
const summary = clip$1(input.summary, THREAD_EVIDENCE_REPORT_MAX_CHARS);
|
|
136732
|
+
const reason = clip$1(input.reason, 500);
|
|
136148
136733
|
const outcome = summary === null && input.outcome === "summarized" ? "failed" : input.outcome;
|
|
136149
136734
|
return {
|
|
136150
136735
|
recorded: yield* engine.dispatch({
|
|
@@ -136166,7 +136751,7 @@ const WatchToolkitHandlersLive = WatchToolkit.toLayer({
|
|
|
136166
136751
|
throughSequence: input.throughSequence,
|
|
136167
136752
|
outcome,
|
|
136168
136753
|
summary,
|
|
136169
|
-
model: clip(input.model, MODEL_ID_MAX_CHARS),
|
|
136754
|
+
model: clip$1(input.model, MODEL_ID_MAX_CHARS),
|
|
136170
136755
|
reason: reason ?? (outcome === "summarized" ? null : "the supervisor gave no reason"),
|
|
136171
136756
|
context,
|
|
136172
136757
|
timing
|
|
@@ -136354,118 +136939,6 @@ const ComputerUseMcpLive = ComputerUseToolkitRegistrationLive.pipe(Layer.provide
|
|
|
136354
136939
|
}).pipe(Layer.provide(McpAuthMiddlewareLive))));
|
|
136355
136940
|
const layer$4 = Layer.mergeAll(Layer.fresh(StandardMcpLive), Layer.fresh(ComputerUseMcpLive));
|
|
136356
136941
|
//#endregion
|
|
136357
|
-
//#region src/hub/HubActivityTransport.ts
|
|
136358
|
-
const REQUEST_TIMEOUT_MS = 8e3;
|
|
136359
|
-
const CLEANUP_TIMEOUT_MS = 1e3;
|
|
136360
|
-
const MAX_RESPONSE_BYTES = 4096;
|
|
136361
|
-
const MAX_REQUEST_BYTES = 16 * 1024;
|
|
136362
|
-
const MAX_BEARER_CHARS = 8192;
|
|
136363
|
-
var HubActivityUnavailable = class extends Data.TaggedError("HubActivityUnavailable") {};
|
|
136364
|
-
var HubActivityUnauthorized = class extends Data.TaggedError("HubActivityUnauthorized") {};
|
|
136365
|
-
/** All errors are payload-free; late responses still cancel their bodies after owner interruption. */
|
|
136366
|
-
const makeHubActivityTransport = Effect.fn("HubActivityTransport.make")(function* (settings, fetcher = fetch) {
|
|
136367
|
-
const origin = yield* Effect.try({
|
|
136368
|
-
try: () => {
|
|
136369
|
-
const url = new URL(settings.baseUrl);
|
|
136370
|
-
if (!["http:", "https:"].includes(url.protocol) || url.username || url.password || url.search || url.hash || url.pathname !== "/" || settings.token.length === 0 || settings.token.length > MAX_BEARER_CHARS || !/^[\x21-\x7e]+$/.test(settings.token)) throw new HubActivityUnavailable();
|
|
136371
|
-
return url.origin;
|
|
136372
|
-
},
|
|
136373
|
-
catch: () => new HubActivityUnavailable()
|
|
136374
|
-
});
|
|
136375
|
-
const request = Effect.fn("HubActivityTransport.request")(function* (path, body, membership) {
|
|
136376
|
-
return yield* Effect.tryPromise({
|
|
136377
|
-
try: async (signal) => {
|
|
136378
|
-
const controller = new AbortController();
|
|
136379
|
-
const abort = () => controller.abort();
|
|
136380
|
-
signal.addEventListener("abort", abort, { once: true });
|
|
136381
|
-
if (signal.aborted) abort();
|
|
136382
|
-
const timer = setTimeout(abort, REQUEST_TIMEOUT_MS);
|
|
136383
|
-
let reader;
|
|
136384
|
-
const cancel = async (work) => {
|
|
136385
|
-
let timeout;
|
|
136386
|
-
try {
|
|
136387
|
-
await Promise.race([Promise.resolve().then(work).catch(() => void 0), new Promise((resolve) => {
|
|
136388
|
-
timeout = setTimeout(resolve, CLEANUP_TIMEOUT_MS);
|
|
136389
|
-
})]);
|
|
136390
|
-
} finally {
|
|
136391
|
-
if (timeout) clearTimeout(timeout);
|
|
136392
|
-
}
|
|
136393
|
-
};
|
|
136394
|
-
try {
|
|
136395
|
-
if (controller.signal.aborted) throw new HubActivityUnavailable();
|
|
136396
|
-
const response = await fetcher(`${origin}${path}`, {
|
|
136397
|
-
method: body === void 0 ? "GET" : "POST",
|
|
136398
|
-
headers: {
|
|
136399
|
-
authorization: `Bearer ${settings.token}`,
|
|
136400
|
-
...membership ? {
|
|
136401
|
-
"x-p4-hub-account": membership.accountId,
|
|
136402
|
-
"x-p4-hub-environment": membership.environmentId
|
|
136403
|
-
} : {},
|
|
136404
|
-
...body === void 0 ? {} : { "content-type": "application/json" }
|
|
136405
|
-
},
|
|
136406
|
-
body,
|
|
136407
|
-
signal: controller.signal,
|
|
136408
|
-
redirect: "error",
|
|
136409
|
-
credentials: "omit",
|
|
136410
|
-
cache: "no-store"
|
|
136411
|
-
});
|
|
136412
|
-
if (controller.signal.aborted) {
|
|
136413
|
-
await cancel(() => response.body?.cancel() ?? Promise.resolve());
|
|
136414
|
-
throw new HubActivityUnavailable();
|
|
136415
|
-
}
|
|
136416
|
-
reader = response.body?.getReader();
|
|
136417
|
-
if (response.status === 401 || response.status === 403) throw new HubActivityUnauthorized();
|
|
136418
|
-
if (response.status !== 200 || !/^application\/json(?:\s*;|$)/i.test(response.headers.get("content-type") ?? "") || !reader) throw new HubActivityUnavailable();
|
|
136419
|
-
let length = 0;
|
|
136420
|
-
const chunks = [];
|
|
136421
|
-
while (true) {
|
|
136422
|
-
const item = await reader.read();
|
|
136423
|
-
if (controller.signal.aborted) throw new HubActivityUnavailable();
|
|
136424
|
-
if (item.done) break;
|
|
136425
|
-
length += item.value.byteLength;
|
|
136426
|
-
if (length > MAX_RESPONSE_BYTES) throw new HubActivityUnavailable();
|
|
136427
|
-
if (item.value.byteLength) chunks.push(item.value);
|
|
136428
|
-
}
|
|
136429
|
-
const bytes = new Uint8Array(length);
|
|
136430
|
-
let offset = 0;
|
|
136431
|
-
for (const chunk of chunks) {
|
|
136432
|
-
bytes.set(chunk, offset);
|
|
136433
|
-
offset += chunk.byteLength;
|
|
136434
|
-
}
|
|
136435
|
-
return Schema$1.decodeUnknownSync(Schema$1.UnknownFromJsonString)(new TextDecoder("utf-8", { fatal: true }).decode(bytes));
|
|
136436
|
-
} finally {
|
|
136437
|
-
controller.abort();
|
|
136438
|
-
clearTimeout(timer);
|
|
136439
|
-
signal.removeEventListener("abort", abort);
|
|
136440
|
-
if (reader) await cancel(() => reader.cancel());
|
|
136441
|
-
}
|
|
136442
|
-
},
|
|
136443
|
-
catch: (error) => error instanceof HubActivityUnauthorized ? error : new HubActivityUnavailable()
|
|
136444
|
-
}).pipe(Effect.timeout(9e3), Effect.mapError((error) => error instanceof HubActivityUnauthorized ? error : new HubActivityUnavailable()));
|
|
136445
|
-
});
|
|
136446
|
-
return {
|
|
136447
|
-
identity: Effect.fn("HubActivityTransport.identity")(function* () {
|
|
136448
|
-
const membership = yield* request("/v1/identity").pipe(Effect.flatMap(Schema$1.decodeUnknownEffect(HubServerMembership, { onExcessProperty: "error" })), Effect.mapError((error) => error instanceof HubActivityUnauthorized ? error : new HubActivityUnavailable()));
|
|
136449
|
-
const destination = yield* Schema$1.encodeUnknownEffect(Schema$1.UnknownFromJsonString)([
|
|
136450
|
-
origin,
|
|
136451
|
-
membership.accountId,
|
|
136452
|
-
membership.environmentId
|
|
136453
|
-
]);
|
|
136454
|
-
return {
|
|
136455
|
-
...membership,
|
|
136456
|
-
destination: NodeCrypto.createHash("sha256").update(destination).digest("hex")
|
|
136457
|
-
};
|
|
136458
|
-
}),
|
|
136459
|
-
post: Effect.fn("HubActivityTransport.post")(function* (input, membership) {
|
|
136460
|
-
const activity = yield* Schema$1.decodeUnknownEffect(HubAgentActivityInput)(input, { onExcessProperty: "error" }).pipe(Effect.mapError(() => new HubActivityUnavailable()));
|
|
136461
|
-
const body = yield* Schema$1.encodeEffect(Schema$1.fromJsonString(HubAgentActivityInput))(activity);
|
|
136462
|
-
if (Buffer.byteLength(body) > MAX_REQUEST_BYTES) return yield* Effect.fail(new HubActivityUnavailable());
|
|
136463
|
-
const result = yield* request("/v1/agent-activity", body, membership);
|
|
136464
|
-
return yield* Schema$1.decodeUnknownEffect(Schema$1.Struct({ accepted: Schema$1.Boolean }))(result, { onExcessProperty: "error" }).pipe(Effect.mapError(() => new HubActivityUnavailable()));
|
|
136465
|
-
})
|
|
136466
|
-
};
|
|
136467
|
-
});
|
|
136468
|
-
//#endregion
|
|
136469
136942
|
//#region src/hub/HubActivityProjection.ts
|
|
136470
136943
|
const titles = {
|
|
136471
136944
|
running: "Agent working",
|
|
@@ -136596,6 +137069,407 @@ const makeHubActivityOutbound = Effect.fn("HubActivityOutbound.make")(function*
|
|
|
136596
137069
|
})
|
|
136597
137070
|
};
|
|
136598
137071
|
});
|
|
137072
|
+
const encodeFact = Schema$1.encodeEffect(Schema$1.fromJsonString(HubAnalyticsFactInput));
|
|
137073
|
+
const decodeStoredFact = Schema$1.decodeUnknownEffect(Schema$1.fromJsonString(HubAnalyticsFactInput));
|
|
137074
|
+
/** Stage one derived fact. Returns false when this id was already staged. */
|
|
137075
|
+
const stageAnalyticsFact = Effect.fn("AnalyticsFactOutbox.stage")(function* (fact) {
|
|
137076
|
+
const sql = yield* SqlClient.SqlClient;
|
|
137077
|
+
const payload = yield* encodeFact(fact);
|
|
137078
|
+
const now = yield* Clock.currentTimeMillis;
|
|
137079
|
+
return (yield* sql`
|
|
137080
|
+
INSERT INTO analytics_fact_outbox (event_id, kind, version, occurred_at, payload, pending, created_at)
|
|
137081
|
+
VALUES (${fact.eventId}, ${fact.kind}, ${fact.version}, ${fact.occurredAt}, ${payload}, 1, ${now})
|
|
137082
|
+
ON CONFLICT(event_id) DO NOTHING
|
|
137083
|
+
RETURNING event_id`).length === 1;
|
|
137084
|
+
});
|
|
137085
|
+
/** The oldest undelivered facts, in the order they were captured. */
|
|
137086
|
+
const pendingAnalyticsFacts = Effect.fn("AnalyticsFactOutbox.pending")(function* (limit = 16) {
|
|
137087
|
+
return yield* (yield* SqlClient.SqlClient)`
|
|
137088
|
+
SELECT event_id, payload FROM analytics_fact_outbox WHERE pending = 1
|
|
137089
|
+
ORDER BY created_at, event_id LIMIT ${limit}`;
|
|
137090
|
+
});
|
|
137091
|
+
/**
|
|
137092
|
+
* Mark one fact delivered.
|
|
137093
|
+
*
|
|
137094
|
+
* The hub answers `accepted: false` for a fact it already holds, which is the
|
|
137095
|
+
* same outcome as a fresh accept from here: the row is delivered either way,
|
|
137096
|
+
* and a retry that raced an earlier success must not stay pending forever.
|
|
137097
|
+
*/
|
|
137098
|
+
const acknowledgeAnalyticsFact = Effect.fn("AnalyticsFactOutbox.acknowledge")(function* (eventId) {
|
|
137099
|
+
return (yield* (yield* SqlClient.SqlClient)`UPDATE analytics_fact_outbox SET pending = 0
|
|
137100
|
+
WHERE event_id = ${eventId} AND pending = 1 RETURNING event_id`).length === 1;
|
|
137101
|
+
});
|
|
137102
|
+
/** Where a source's scan left off, so a restart resumes instead of replaying. */
|
|
137103
|
+
const readAnalyticsCursor = Effect.fn("AnalyticsFactOutbox.readCursor")(function* (source) {
|
|
137104
|
+
return (yield* (yield* SqlClient.SqlClient)`SELECT after_sequence FROM analytics_fact_cursors WHERE source = ${source}`)[0]?.after_sequence ?? 0;
|
|
137105
|
+
});
|
|
137106
|
+
const writeAnalyticsCursor = Effect.fn("AnalyticsFactOutbox.writeCursor")(function* (source, afterSequence) {
|
|
137107
|
+
yield* (yield* SqlClient.SqlClient)`INSERT INTO analytics_fact_cursors (source, after_sequence)
|
|
137108
|
+
VALUES (${source}, ${afterSequence})
|
|
137109
|
+
ON CONFLICT(source) DO UPDATE SET after_sequence = excluded.after_sequence`;
|
|
137110
|
+
});
|
|
137111
|
+
//#endregion
|
|
137112
|
+
//#region src/analytics/analyticsFacts.ts
|
|
137113
|
+
const MAX_QUESTION_CHARS = 2e3;
|
|
137114
|
+
const MAX_HEADER_CHARS = 120;
|
|
137115
|
+
const MAX_OPTION_LABEL_CHARS = 200;
|
|
137116
|
+
const MAX_OPTION_DESCRIPTION_CHARS = 600;
|
|
137117
|
+
const MAX_PRESENTED_OPTIONS = 12;
|
|
137118
|
+
const MAX_CUSTOM_ANSWER_CHARS = 2e3;
|
|
137119
|
+
const MAX_REASON_CHARS = 200;
|
|
137120
|
+
const MAX_MODEL_CHARS = 120;
|
|
137121
|
+
const clip = (value, max) => value.length <= max ? value : value.slice(0, max);
|
|
137122
|
+
const asRecord$3 = (value) => typeof value === "object" && value !== null && !Array.isArray(value) ? value : null;
|
|
137123
|
+
const asFiniteInt$1 = (value) => typeof value === "number" && Number.isFinite(value) ? Math.round(value) : null;
|
|
137124
|
+
/** `question:<requestId>:<questionId>`, so a replayed resolution derives the same id. */
|
|
137125
|
+
const questionFactId = (requestId, questionId) => `question:${requestId}:${questionId}`;
|
|
137126
|
+
/**
|
|
137127
|
+
* One fact per question in a resolved request.
|
|
137128
|
+
*
|
|
137129
|
+
* The presented options travel with the answer because the answer alone says
|
|
137130
|
+
* nothing: "Patch" only means something beside what else was offered, and a
|
|
137131
|
+
* custom answer only means something beside what it refused.
|
|
137132
|
+
*/
|
|
137133
|
+
function questionFactsFrom(input) {
|
|
137134
|
+
const questions = Array.isArray(input.questions) ? input.questions : [];
|
|
137135
|
+
const answers = asRecord$3(input.answers) ?? {};
|
|
137136
|
+
const facts = [];
|
|
137137
|
+
for (const raw of questions) {
|
|
137138
|
+
const question = asRecord$3(raw);
|
|
137139
|
+
if (question === null) continue;
|
|
137140
|
+
const questionId = typeof question.id === "string" ? question.id : null;
|
|
137141
|
+
const prompt = typeof question.question === "string" ? question.question : null;
|
|
137142
|
+
if (questionId === null || prompt === null || prompt.length === 0) continue;
|
|
137143
|
+
const options = (Array.isArray(question.options) ? question.options : []).slice(0, MAX_PRESENTED_OPTIONS).flatMap((entry) => {
|
|
137144
|
+
const option = asRecord$3(entry);
|
|
137145
|
+
const label = typeof option?.label === "string" ? option.label : null;
|
|
137146
|
+
return label === null || label.length === 0 ? [] : [{
|
|
137147
|
+
label: clip(label, MAX_OPTION_LABEL_CHARS),
|
|
137148
|
+
description: clip(typeof option?.description === "string" ? option.description : "", MAX_OPTION_DESCRIPTION_CHARS)
|
|
137149
|
+
}];
|
|
137150
|
+
});
|
|
137151
|
+
const given = answers[questionId];
|
|
137152
|
+
const selected = (Array.isArray(given) ? given : given === void 0 ? [] : [given]).flatMap((value) => typeof value === "string" && value.length > 0 ? [value] : []);
|
|
137153
|
+
const indexes = selected.map((label) => options.findIndex((option) => option.label === label));
|
|
137154
|
+
const custom = selected.find((_, position) => indexes[position] === -1);
|
|
137155
|
+
const answer = selected.length === 0 ? { kind: "dismissed" } : custom !== void 0 ? {
|
|
137156
|
+
kind: "custom",
|
|
137157
|
+
text: clip(custom, MAX_CUSTOM_ANSWER_CHARS)
|
|
137158
|
+
} : {
|
|
137159
|
+
kind: "option",
|
|
137160
|
+
optionIndexes: indexes,
|
|
137161
|
+
labels: selected.map((label) => clip(label, MAX_OPTION_LABEL_CHARS))
|
|
137162
|
+
};
|
|
137163
|
+
facts.push({
|
|
137164
|
+
eventId: questionFactId(input.requestId, questionId),
|
|
137165
|
+
occurredAt: input.occurredAt,
|
|
137166
|
+
kind: "question.answered",
|
|
137167
|
+
version: 1,
|
|
137168
|
+
payload: {
|
|
137169
|
+
questionId,
|
|
137170
|
+
threadId: input.threadId,
|
|
137171
|
+
header: clip(typeof question.header === "string" ? question.header : "", MAX_HEADER_CHARS),
|
|
137172
|
+
question: clip(prompt, MAX_QUESTION_CHARS),
|
|
137173
|
+
multiSelect: question.multiSelect === true,
|
|
137174
|
+
options,
|
|
137175
|
+
answer
|
|
137176
|
+
}
|
|
137177
|
+
});
|
|
137178
|
+
}
|
|
137179
|
+
return facts;
|
|
137180
|
+
}
|
|
137181
|
+
/** `fusion:<threadId>:<throughSequence>`: one handoff covers one range, once. */
|
|
137182
|
+
const fusionFactId = (threadId, throughSequence) => `fusion:${threadId}:${throughSequence}`;
|
|
137183
|
+
/**
|
|
137184
|
+
* One review, read from the two rows that recorded it.
|
|
137185
|
+
*
|
|
137186
|
+
* The outcome, the model and the duration come from the report, because that
|
|
137187
|
+
* is the row the server wrote them on; the coverage and the summary come from
|
|
137188
|
+
* the handoff, because that is the row that carried them. Neither is guessed
|
|
137189
|
+
* from the other, and a handoff with no report yields nothing at all.
|
|
137190
|
+
*/
|
|
137191
|
+
function fusionHandoffFactFrom(input) {
|
|
137192
|
+
const payload = asRecord$3(input.payload);
|
|
137193
|
+
const report = asRecord$3(input.reportPayload);
|
|
137194
|
+
if (payload === null || report === null) return null;
|
|
137195
|
+
const outcome = report.outcome;
|
|
137196
|
+
if (outcome !== "summarized" && outcome !== "fallback" && outcome !== "failed" && outcome !== "unreported") return null;
|
|
137197
|
+
const coverage = asRecord$3(payload.coverage) ?? {};
|
|
137198
|
+
const context = asRecord$3(report.context);
|
|
137199
|
+
const timing = asRecord$3(report.timing);
|
|
137200
|
+
const summary = typeof payload.summary === "string" ? payload.summary : null;
|
|
137201
|
+
const reason = typeof report.reason === "string" ? clip(report.reason, MAX_REASON_CHARS) : null;
|
|
137202
|
+
return {
|
|
137203
|
+
eventId: fusionFactId(input.threadId, input.throughSequence),
|
|
137204
|
+
occurredAt: input.occurredAt,
|
|
137205
|
+
kind: "fusion.handoff",
|
|
137206
|
+
version: 1,
|
|
137207
|
+
payload: {
|
|
137208
|
+
fusionVersion: input.fusionVersion,
|
|
137209
|
+
threadId: input.threadId,
|
|
137210
|
+
status: outcome,
|
|
137211
|
+
model: typeof report.model === "string" ? clip(report.model, MAX_MODEL_CHARS) : null,
|
|
137212
|
+
reason,
|
|
137213
|
+
transcriptEvents: asFiniteInt$1(coverage.transcriptEvents) ?? 0,
|
|
137214
|
+
transcriptIncluded: asFiniteInt$1(coverage.transcriptIncluded) ?? 0,
|
|
137215
|
+
transcriptDropped: asFiniteInt$1(coverage.transcriptDropped) ?? 0,
|
|
137216
|
+
transcriptClippedFields: asFiniteInt$1(coverage.transcriptClippedFields) ?? 0,
|
|
137217
|
+
ledgerRows: asFiniteInt$1(coverage.ledgerRows) ?? 0,
|
|
137218
|
+
ledgerTotalRows: asFiniteInt$1(coverage.ledgerTotalRows) ?? 0,
|
|
137219
|
+
summaryChars: summary === null ? null : summary.length,
|
|
137220
|
+
contextDeltaTokens: asFiniteInt$1(context?.deltaTokens),
|
|
137221
|
+
durationMs: asFiniteInt$1(timing?.durationMs)
|
|
137222
|
+
}
|
|
137223
|
+
};
|
|
137224
|
+
}
|
|
137225
|
+
/** `compression:<threadId>:<turnId>`: one pass per turn, once. */
|
|
137226
|
+
const compressionFactId = (threadId, turnId) => `compression:${threadId}:${turnId}`;
|
|
137227
|
+
/**
|
|
137228
|
+
* A compression pass, only when it was actually measured.
|
|
137229
|
+
*
|
|
137230
|
+
* Both token readings and the duration have to come from recorded state: a
|
|
137231
|
+
* pass whose before or after was never written is not reported as zero, and
|
|
137232
|
+
* the delta is derived from the two readings rather than supplied beside them.
|
|
137233
|
+
*/
|
|
137234
|
+
function compressionFactFrom(input) {
|
|
137235
|
+
const before = asFiniteInt$1(input.beforeTokens);
|
|
137236
|
+
const after = asFiniteInt$1(input.afterTokens);
|
|
137237
|
+
const durationMs = asFiniteInt$1(input.durationMs);
|
|
137238
|
+
if (before === null || after === null || durationMs === null || durationMs < 0) return null;
|
|
137239
|
+
if (before < 0 || after < 0) return null;
|
|
137240
|
+
return {
|
|
137241
|
+
eventId: compressionFactId(input.threadId, input.turnId),
|
|
137242
|
+
occurredAt: input.occurredAt,
|
|
137243
|
+
kind: "compression.applied",
|
|
137244
|
+
version: 1,
|
|
137245
|
+
payload: {
|
|
137246
|
+
mode: input.mode,
|
|
137247
|
+
threadId: input.threadId,
|
|
137248
|
+
beforeTokens: before,
|
|
137249
|
+
afterTokens: after,
|
|
137250
|
+
deltaTokens: before - after,
|
|
137251
|
+
durationMs
|
|
137252
|
+
}
|
|
137253
|
+
};
|
|
137254
|
+
}
|
|
137255
|
+
//#endregion
|
|
137256
|
+
//#region src/analytics/AnalyticsFactCapture.ts
|
|
137257
|
+
const ANALYTICS_QUESTION_SOURCE = "events.user-input";
|
|
137258
|
+
const ANALYTICS_FUSION_SOURCE = "events.fusion-evidence";
|
|
137259
|
+
const ANALYTICS_COMPRESSION_SOURCE = "events.context-compaction";
|
|
137260
|
+
const decodeJson$1 = Schema$1.decodeUnknownEffect(Schema$1.fromJsonString(Schema$1.Unknown));
|
|
137261
|
+
const asRecord$2 = (value) => typeof value === "object" && value !== null && !Array.isArray(value) ? value : null;
|
|
137262
|
+
const asFiniteInt = (value) => typeof value === "number" && Number.isFinite(value) ? Math.round(value) : null;
|
|
137263
|
+
const asUsedTokens = (payload) => asFiniteInt(asRecord$2(payload)?.usedTokens);
|
|
137264
|
+
/** One appended activity, read out of its event envelope. */
|
|
137265
|
+
const readActivityEvent = Effect.fn("AnalyticsFactCapture.activity")(function* (row) {
|
|
137266
|
+
const payload = asRecord$2(yield* decodeJson$1(row.payload_json));
|
|
137267
|
+
const activity = asRecord$2(payload?.activity);
|
|
137268
|
+
const threadId = typeof payload?.threadId === "string" ? payload.threadId : null;
|
|
137269
|
+
if (activity === null || threadId === null) return null;
|
|
137270
|
+
return {
|
|
137271
|
+
sequence: row.sequence,
|
|
137272
|
+
threadId,
|
|
137273
|
+
turnId: typeof activity.turnId === "string" ? activity.turnId : null,
|
|
137274
|
+
createdAt: typeof activity.createdAt === "string" ? activity.createdAt : "",
|
|
137275
|
+
payload: asRecord$2(activity.payload) ?? {}
|
|
137276
|
+
};
|
|
137277
|
+
});
|
|
137278
|
+
/** Candidate rows of one activity kind, after a source's own cursor. */
|
|
137279
|
+
const readCandidates = Effect.fn("AnalyticsFactCapture.candidates")(function* (kind, after, limit) {
|
|
137280
|
+
return yield* (yield* SqlClient.SqlClient)`
|
|
137281
|
+
SELECT sequence, payload_json FROM orchestration_events
|
|
137282
|
+
WHERE sequence > ${after} AND event_type = 'thread.activity-appended'
|
|
137283
|
+
AND json_extract(payload_json, '$.activity.kind') = ${kind}
|
|
137284
|
+
ORDER BY sequence ASC LIMIT ${limit}`;
|
|
137285
|
+
});
|
|
137286
|
+
/**
|
|
137287
|
+
* The request a resolution answers, matched on the exact request id.
|
|
137288
|
+
*
|
|
137289
|
+
* A newer question in the same thread must never lend its options to an older
|
|
137290
|
+
* answer, which is what a "most recent request" lookup would do whenever two
|
|
137291
|
+
* requests were open at once.
|
|
137292
|
+
*/
|
|
137293
|
+
const readQuestionRequest = Effect.fn("AnalyticsFactCapture.questionRequest")(function* (threadId, requestId, throughSequence) {
|
|
137294
|
+
const [row] = yield* (yield* SqlClient.SqlClient)`
|
|
137295
|
+
SELECT sequence, payload_json FROM orchestration_events
|
|
137296
|
+
WHERE sequence <= ${throughSequence} AND event_type = 'thread.activity-appended'
|
|
137297
|
+
AND json_extract(payload_json, '$.activity.kind') = 'user-input.requested'
|
|
137298
|
+
AND json_extract(payload_json, '$.threadId') = ${threadId}
|
|
137299
|
+
AND json_extract(payload_json, '$.activity.payload.requestId') = ${requestId}
|
|
137300
|
+
ORDER BY sequence DESC LIMIT 1`;
|
|
137301
|
+
if (row === void 0) return null;
|
|
137302
|
+
return (yield* readActivityEvent(row))?.payload ?? null;
|
|
137303
|
+
});
|
|
137304
|
+
/** The handoff a report closes, found by the range the report names. */
|
|
137305
|
+
const readHandoff = Effect.fn("AnalyticsFactCapture.handoff")(function* (threadId, throughSequence, beforeSequence) {
|
|
137306
|
+
const [row] = yield* (yield* SqlClient.SqlClient)`
|
|
137307
|
+
SELECT sequence, payload_json FROM orchestration_events
|
|
137308
|
+
WHERE sequence <= ${beforeSequence} AND event_type = 'thread.activity-appended'
|
|
137309
|
+
AND json_extract(payload_json, '$.activity.kind') = 'fusion.evidence.handoff'
|
|
137310
|
+
AND json_extract(payload_json, '$.activity.payload.watchedThreadId') = ${threadId}
|
|
137311
|
+
AND json_extract(payload_json, '$.activity.payload.throughSequence') = ${throughSequence}
|
|
137312
|
+
ORDER BY sequence DESC LIMIT 1`;
|
|
137313
|
+
if (row === void 0) return null;
|
|
137314
|
+
return (yield* readActivityEvent(row))?.payload ?? null;
|
|
137315
|
+
});
|
|
137316
|
+
/**
|
|
137317
|
+
* The nearest context reading on one side of a compaction, in the same turn.
|
|
137318
|
+
*
|
|
137319
|
+
* Correlating on the turn is the point: a later turn's context update must
|
|
137320
|
+
* never be able to close an old pass, which is exactly what "nearest reading
|
|
137321
|
+
* in this thread" would let it do once the pass itself was never completed.
|
|
137322
|
+
*/
|
|
137323
|
+
const readContextReading = Effect.fn("AnalyticsFactCapture.contextReading")(function* (threadId, turnId, sequence, side) {
|
|
137324
|
+
const sql = yield* SqlClient.SqlClient;
|
|
137325
|
+
const row = (side === "before" ? yield* sql`
|
|
137326
|
+
SELECT sequence, payload_json FROM orchestration_events
|
|
137327
|
+
WHERE sequence < ${sequence} AND event_type = 'thread.activity-appended'
|
|
137328
|
+
AND json_extract(payload_json, '$.activity.kind') = 'context-window.updated'
|
|
137329
|
+
AND json_extract(payload_json, '$.threadId') = ${threadId}
|
|
137330
|
+
AND json_extract(payload_json, '$.activity.turnId') = ${turnId}
|
|
137331
|
+
ORDER BY sequence DESC LIMIT 1` : yield* sql`
|
|
137332
|
+
SELECT sequence, payload_json FROM orchestration_events
|
|
137333
|
+
WHERE sequence > ${sequence} AND event_type = 'thread.activity-appended'
|
|
137334
|
+
AND json_extract(payload_json, '$.activity.kind') = 'context-window.updated'
|
|
137335
|
+
AND json_extract(payload_json, '$.threadId') = ${threadId}
|
|
137336
|
+
AND json_extract(payload_json, '$.activity.turnId') = ${turnId}
|
|
137337
|
+
ORDER BY sequence ASC LIMIT 1`)[0];
|
|
137338
|
+
return row === void 0 ? null : yield* readActivityEvent(row);
|
|
137339
|
+
});
|
|
137340
|
+
const stageAll = Effect.fn("AnalyticsFactCapture.stageAll")(function* (facts) {
|
|
137341
|
+
let staged = 0;
|
|
137342
|
+
for (const fact of facts) if (yield* stageAnalyticsFact(fact)) staged += 1;
|
|
137343
|
+
return staged;
|
|
137344
|
+
});
|
|
137345
|
+
/** Questions: one fact per question in each resolved request. */
|
|
137346
|
+
const captureQuestionFacts = Effect.fn("AnalyticsFactCapture.questions")(function* (limit = 128) {
|
|
137347
|
+
const after = yield* readAnalyticsCursor(ANALYTICS_QUESTION_SOURCE);
|
|
137348
|
+
const rows = yield* readCandidates("user-input.resolved", after, limit);
|
|
137349
|
+
let cursor = after;
|
|
137350
|
+
let staged = 0;
|
|
137351
|
+
for (const row of rows) {
|
|
137352
|
+
const event = yield* readActivityEvent(row);
|
|
137353
|
+
const requestId = typeof event?.payload.requestId === "string" ? event.payload.requestId : null;
|
|
137354
|
+
if (event !== null && requestId !== null) {
|
|
137355
|
+
const request = yield* readQuestionRequest(event.threadId, requestId, event.sequence);
|
|
137356
|
+
if (request !== null) staged += yield* stageAll(questionFactsFrom({
|
|
137357
|
+
requestId,
|
|
137358
|
+
threadId: event.threadId,
|
|
137359
|
+
occurredAt: event.createdAt,
|
|
137360
|
+
questions: request.questions,
|
|
137361
|
+
answers: event.payload.answers
|
|
137362
|
+
}));
|
|
137363
|
+
}
|
|
137364
|
+
cursor = row.sequence;
|
|
137365
|
+
}
|
|
137366
|
+
if (cursor !== after) yield* writeAnalyticsCursor(ANALYTICS_QUESTION_SOURCE, cursor);
|
|
137367
|
+
return {
|
|
137368
|
+
staged,
|
|
137369
|
+
cursor
|
|
137370
|
+
};
|
|
137371
|
+
});
|
|
137372
|
+
/**
|
|
137373
|
+
* Fusion: staged from the evidence report, never before it.
|
|
137374
|
+
*
|
|
137375
|
+
* The report is what carries the model, the context delta and the measured
|
|
137376
|
+
* duration, so a fact staged at handoff time would be missing exactly the
|
|
137377
|
+
* numbers it exists to carry - and could never gain them, because the fact is
|
|
137378
|
+
* append-only.
|
|
137379
|
+
*/
|
|
137380
|
+
const captureFusionFacts = Effect.fn("AnalyticsFactCapture.fusion")(function* (input) {
|
|
137381
|
+
const after = yield* readAnalyticsCursor(ANALYTICS_FUSION_SOURCE);
|
|
137382
|
+
const rows = yield* readCandidates("fusion.evidence.summary", after, input.limit ?? 128);
|
|
137383
|
+
let cursor = after;
|
|
137384
|
+
let staged = 0;
|
|
137385
|
+
for (const row of rows) {
|
|
137386
|
+
const event = yield* readActivityEvent(row);
|
|
137387
|
+
const watchedThreadId = typeof event?.payload.watchedThreadId === "string" ? event.payload.watchedThreadId : null;
|
|
137388
|
+
const throughSequence = asFiniteInt(event?.payload.throughSequence);
|
|
137389
|
+
if (event !== null && watchedThreadId !== null && throughSequence !== null) {
|
|
137390
|
+
const handoff = yield* readHandoff(watchedThreadId, throughSequence, event.sequence);
|
|
137391
|
+
if (handoff !== null) {
|
|
137392
|
+
const fact = fusionHandoffFactFrom({
|
|
137393
|
+
threadId: watchedThreadId,
|
|
137394
|
+
throughSequence,
|
|
137395
|
+
occurredAt: event.createdAt,
|
|
137396
|
+
fusionVersion: input.fusionVersion,
|
|
137397
|
+
payload: handoff,
|
|
137398
|
+
reportPayload: event.payload
|
|
137399
|
+
});
|
|
137400
|
+
if (fact !== null) staged += yield* stageAll([fact]);
|
|
137401
|
+
}
|
|
137402
|
+
}
|
|
137403
|
+
cursor = row.sequence;
|
|
137404
|
+
}
|
|
137405
|
+
if (cursor !== after) yield* writeAnalyticsCursor(ANALYTICS_FUSION_SOURCE, cursor);
|
|
137406
|
+
return {
|
|
137407
|
+
staged,
|
|
137408
|
+
cursor
|
|
137409
|
+
};
|
|
137410
|
+
});
|
|
137411
|
+
/**
|
|
137412
|
+
* Compression: only a pass the service measured and the provider reported.
|
|
137413
|
+
*
|
|
137414
|
+
* The duration comes from the compaction event the provider service stamped,
|
|
137415
|
+
* the tokens from the recorded context readings either side. A pass whose
|
|
137416
|
+
* later reading has not landed yet holds back only this cursor, so a question
|
|
137417
|
+
* or a handoff behind it is still captured on the same tick.
|
|
137418
|
+
*/
|
|
137419
|
+
const captureCompressionFacts = Effect.fn("AnalyticsFactCapture.compression")(function* (limit = 128) {
|
|
137420
|
+
const after = yield* readAnalyticsCursor(ANALYTICS_COMPRESSION_SOURCE);
|
|
137421
|
+
const rows = yield* readCandidates("context-compaction", after, limit);
|
|
137422
|
+
let cursor = after;
|
|
137423
|
+
let staged = 0;
|
|
137424
|
+
for (const row of rows) {
|
|
137425
|
+
const event = yield* readActivityEvent(row);
|
|
137426
|
+
if (event === null || event.turnId === null) {
|
|
137427
|
+
cursor = row.sequence;
|
|
137428
|
+
continue;
|
|
137429
|
+
}
|
|
137430
|
+
const durationMs = asFiniteInt(asRecord$2(event.payload.detail)?.durationMs);
|
|
137431
|
+
const before = yield* readContextReading(event.threadId, event.turnId, event.sequence, "before");
|
|
137432
|
+
const afterReading = yield* readContextReading(event.threadId, event.turnId, event.sequence, "after");
|
|
137433
|
+
if (afterReading === null) break;
|
|
137434
|
+
const mode = asRecord$2(before?.payload)?.compressMode ?? afterReading.payload.compressMode;
|
|
137435
|
+
const fact = typeof mode === "string" ? compressionFactFrom({
|
|
137436
|
+
threadId: event.threadId,
|
|
137437
|
+
turnId: event.turnId,
|
|
137438
|
+
occurredAt: event.createdAt,
|
|
137439
|
+
mode,
|
|
137440
|
+
beforeTokens: asUsedTokens(before?.payload),
|
|
137441
|
+
afterTokens: asUsedTokens(afterReading.payload),
|
|
137442
|
+
durationMs
|
|
137443
|
+
}) : null;
|
|
137444
|
+
if (fact !== null) staged += yield* stageAll([fact]);
|
|
137445
|
+
cursor = row.sequence;
|
|
137446
|
+
}
|
|
137447
|
+
if (cursor !== after) yield* writeAnalyticsCursor(ANALYTICS_COMPRESSION_SOURCE, cursor);
|
|
137448
|
+
return {
|
|
137449
|
+
staged,
|
|
137450
|
+
cursor
|
|
137451
|
+
};
|
|
137452
|
+
});
|
|
137453
|
+
/** One tick over every source. A source that stalls never blocks another. */
|
|
137454
|
+
const captureAnalyticsFacts = Effect.fn("AnalyticsFactCapture.run")(function* (input) {
|
|
137455
|
+
const questions = yield* captureQuestionFacts(input.limit);
|
|
137456
|
+
const fusion = yield* captureFusionFacts(input);
|
|
137457
|
+
const compression = yield* captureCompressionFacts(input.limit);
|
|
137458
|
+
return { staged: questions.staged + fusion.staged + compression.staged };
|
|
137459
|
+
});
|
|
137460
|
+
//#endregion
|
|
137461
|
+
//#region src/analytics/AnalyticsDelivery.ts
|
|
137462
|
+
const runAnalyticsTick = Effect.fn("AnalyticsDelivery.tick")(function* (transport, identity, fusionVersion) {
|
|
137463
|
+
yield* captureAnalyticsFacts({ fusionVersion });
|
|
137464
|
+
let delivered = 0;
|
|
137465
|
+
for (const row of yield* pendingAnalyticsFacts()) {
|
|
137466
|
+
const fact = yield* decodeStoredFact(row.payload);
|
|
137467
|
+
yield* transport.postFact(fact, identity);
|
|
137468
|
+
yield* acknowledgeAnalyticsFact(row.event_id);
|
|
137469
|
+
delivered += 1;
|
|
137470
|
+
}
|
|
137471
|
+
return { delivered };
|
|
137472
|
+
});
|
|
136599
137473
|
//#endregion
|
|
136600
137474
|
//#region src/hub/HubTunnelReconnect.ts
|
|
136601
137475
|
const HUB_TUNNEL_RETRY_INITIAL_MS = 1e3;
|
|
@@ -136634,6 +137508,7 @@ const runHubActivityTick = Effect.fn("HubActivityRuntime.tick")(function* (outbo
|
|
|
136634
137508
|
yield* transport.post(activity, identity);
|
|
136635
137509
|
yield* outbound.acknowledge(pending.thread_id, pending.revision);
|
|
136636
137510
|
}
|
|
137511
|
+
yield* runAnalyticsTick(transport, identity, version$1).pipe(Effect.catchCause((cause) => Effect.logDebug("analytics tick deferred", { cause })));
|
|
136637
137512
|
});
|
|
136638
137513
|
/** A credential generation owns every request, staging page and delay. Old destinations remain isolated. */
|
|
136639
137514
|
const runHubActivityEmission = Effect.fn("HubActivityRuntime.run")(function* (settings, ready) {
|
|
@@ -141591,22 +142466,95 @@ function describeHandoffEvent(event, implementerThreadId, clip) {
|
|
|
141591
142466
|
}
|
|
141592
142467
|
}
|
|
141593
142468
|
/**
|
|
142469
|
+
* An activity line's evidence fields, its kind dropped and its title read the
|
|
142470
|
+
* way the ledger reads it: a provider spells the opening fragment's title
|
|
142471
|
+
* "<title> started", which is the same call rather than a different one.
|
|
142472
|
+
*/
|
|
142473
|
+
function activityFields(content) {
|
|
142474
|
+
const [, ...fields] = content.split(" | ");
|
|
142475
|
+
const [title, ...rest] = fields;
|
|
142476
|
+
return title === void 0 ? fields : [title.replace(/ started$/u, ""), ...rest];
|
|
142477
|
+
}
|
|
142478
|
+
/** What a line is a fragment of, or null when it stands alone. */
|
|
142479
|
+
function fragmentIdentity(event) {
|
|
142480
|
+
if (event.type === "thread.message-sent") {
|
|
142481
|
+
const messageId = asTrimmed(event.payload.messageId);
|
|
142482
|
+
return messageId === null ? null : `message:${messageId}`;
|
|
142483
|
+
}
|
|
142484
|
+
if (event.type === "thread.activity-appended") {
|
|
142485
|
+
const data = asRecord(asRecord(event.payload.activity.payload)?.data);
|
|
142486
|
+
const toolCallId = asTrimmed(data?.toolCallId);
|
|
142487
|
+
return toolCallId === null ? null : `tool:${toolCallId}`;
|
|
142488
|
+
}
|
|
142489
|
+
return null;
|
|
142490
|
+
}
|
|
142491
|
+
/**
|
|
142492
|
+
* Fragments whose evidence a later line of the same message or call already
|
|
142493
|
+
* carries, so the transcript spends its budget on evidence rather than on the
|
|
142494
|
+
* same text streamed twenty times.
|
|
142495
|
+
*
|
|
142496
|
+
* A streaming message is folded only when a later fragment literally contains
|
|
142497
|
+
* it, and a tool fragment only when every field it carries reappears later, so
|
|
142498
|
+
* nothing unique is ever folded away. What a folded line did carry alone - the
|
|
142499
|
+
* call's identity, its status and the sequence it started at - is exactly what
|
|
142500
|
+
* the ledger records beside this transcript.
|
|
142501
|
+
*/
|
|
142502
|
+
function supersededLines(lines) {
|
|
142503
|
+
const groups = /* @__PURE__ */ new Map();
|
|
142504
|
+
for (const line of lines) {
|
|
142505
|
+
if (line.identity === null) continue;
|
|
142506
|
+
const group = groups.get(line.identity);
|
|
142507
|
+
if (group === void 0) groups.set(line.identity, [line]);
|
|
142508
|
+
else group.push(line);
|
|
142509
|
+
}
|
|
142510
|
+
const superseded = /* @__PURE__ */ new Set();
|
|
142511
|
+
for (const group of groups.values()) {
|
|
142512
|
+
if (group.length < 2) continue;
|
|
142513
|
+
const last = group[group.length - 1];
|
|
142514
|
+
for (let index = 0; index < group.length - 1; index += 1) {
|
|
142515
|
+
const line = group[index];
|
|
142516
|
+
if (line.shape === "message") {
|
|
142517
|
+
if (last.shape === "message" && last.content.includes(line.content)) superseded.add(line);
|
|
142518
|
+
continue;
|
|
142519
|
+
}
|
|
142520
|
+
const laterFields = new Set(group.slice(index + 1).flatMap((later) => later.fields));
|
|
142521
|
+
if (line.fields.every((field) => laterFields.has(field))) superseded.add(line);
|
|
142522
|
+
}
|
|
142523
|
+
}
|
|
142524
|
+
return superseded;
|
|
142525
|
+
}
|
|
142526
|
+
/**
|
|
141594
142527
|
* Flatten the range, newest kept when the bound bites.
|
|
141595
142528
|
*
|
|
141596
|
-
*
|
|
141597
|
-
*
|
|
141598
|
-
*
|
|
142529
|
+
* Duplicate fragments are folded before the bound is applied, because dropping
|
|
142530
|
+
* a unique event to make room for the twelfth copy of a streaming message is a
|
|
142531
|
+
* loss this handoff can simply avoid. Dropping the oldest is still the lesser
|
|
142532
|
+
* evil once the bound does bite, and unlike in the triage window the loss is
|
|
142533
|
+
* reported rather than absorbed: a range that did not fit is a range this
|
|
142534
|
+
* handoff must not call complete. Clipped fields are counted on the lines
|
|
142535
|
+
* actually handed over, so a field clipped inside a folded or dropped line is
|
|
142536
|
+
* not charged against coverage twice.
|
|
141599
142537
|
*/
|
|
141600
142538
|
function buildHandoffEvidence(events, implementerThreadId, maxChars) {
|
|
141601
|
-
const
|
|
141602
|
-
const lines = [];
|
|
142539
|
+
const rendered = [];
|
|
141603
142540
|
for (const event of events) {
|
|
142541
|
+
const clip = makeClipper();
|
|
141604
142542
|
const text = describeHandoffEvent(event, implementerThreadId, clip);
|
|
141605
|
-
if (text
|
|
142543
|
+
if (text === null) continue;
|
|
142544
|
+
const content = text.replace(/^#\d+ /u, "");
|
|
142545
|
+
const shape = event.type === "thread.message-sent" ? "message" : "activity";
|
|
142546
|
+
rendered.push({
|
|
141606
142547
|
sequence: event.sequence,
|
|
141607
|
-
text
|
|
142548
|
+
text,
|
|
142549
|
+
clippedFields: clip.clipped,
|
|
142550
|
+
identity: fragmentIdentity(event),
|
|
142551
|
+
shape,
|
|
142552
|
+
content,
|
|
142553
|
+
fields: shape === "activity" ? activityFields(content) : []
|
|
141608
142554
|
});
|
|
141609
142555
|
}
|
|
142556
|
+
const superseded = supersededLines(rendered);
|
|
142557
|
+
const lines = rendered.filter((line) => !superseded.has(line));
|
|
141610
142558
|
const kept = [];
|
|
141611
142559
|
let used = 0;
|
|
141612
142560
|
for (let index = lines.length - 1; index >= 0; index -= 1) {
|
|
@@ -141618,10 +142566,11 @@ function buildHandoffEvidence(events, implementerThreadId, maxChars) {
|
|
|
141618
142566
|
}
|
|
141619
142567
|
return {
|
|
141620
142568
|
transcript: kept.map((line) => line.text).join("\n"),
|
|
141621
|
-
eventCount:
|
|
142569
|
+
eventCount: rendered.length,
|
|
141622
142570
|
includedCount: kept.length,
|
|
141623
142571
|
droppedCount: lines.length - kept.length,
|
|
141624
|
-
|
|
142572
|
+
coalescedCount: superseded.size,
|
|
142573
|
+
clippedFields: kept.reduce((total, line) => total + line.clippedFields, 0),
|
|
141625
142574
|
firstSequence: kept[0]?.sequence ?? null,
|
|
141626
142575
|
lastSequence: kept[kept.length - 1]?.sequence ?? null,
|
|
141627
142576
|
includedSequences: new Set(kept.map((line) => line.sequence))
|
|
@@ -141677,6 +142626,29 @@ const HANDOFF_SUMMARY_INSTRUCTIONS = [
|
|
|
141677
142626
|
"cite at least one sequence, and never a sequence absent from the transcript",
|
|
141678
142627
|
"name the user's requirements and corrections, and quote the exact text of any failure or error"
|
|
141679
142628
|
];
|
|
142629
|
+
/** Enough sequences to show the shape of a citation without dictating the summary. */
|
|
142630
|
+
const CITATION_EXAMPLE_COUNT = 2;
|
|
142631
|
+
/** The lowest and highest sequence handed over, as the citations they must be copied as. */
|
|
142632
|
+
function citationExamples(includedSequences) {
|
|
142633
|
+
const sorted = [...includedSequences].sort((left, right) => left - right);
|
|
142634
|
+
return (sorted.length <= CITATION_EXAMPLE_COUNT ? sorted : [sorted[0], sorted[sorted.length - 1]]).map((sequence) => `#${sequence}`);
|
|
142635
|
+
}
|
|
142636
|
+
/**
|
|
142637
|
+
* The citation rules carrying real sequences, because `#<sequence>` read as a
|
|
142638
|
+
* placeholder is what a summarizer answers without any number at all.
|
|
142639
|
+
*/
|
|
142640
|
+
function handoffSummaryInstructions(input) {
|
|
142641
|
+
const examples = citationExamples(input.includedSequences);
|
|
142642
|
+
return examples.length === 0 ? HANDOFF_SUMMARY_INSTRUCTIONS : [...HANDOFF_SUMMARY_INSTRUCTIONS, `copy citations from the line starts of this transcript, for example ${examples.join(" and ")}`];
|
|
142643
|
+
}
|
|
142644
|
+
/**
|
|
142645
|
+
* The single repair attempt: the same rules, plus what the discarded answer
|
|
142646
|
+
* got wrong. Nothing here relaxes the rule - a second miss falls back.
|
|
142647
|
+
*/
|
|
142648
|
+
function handoffSummaryRepairInstructions(input) {
|
|
142649
|
+
const invented = citedSequences(input.summary).filter((sequence) => !input.includedSequences.has(sequence)).slice(0, CITATION_EXAMPLE_COUNT).map((sequence) => `#${sequence}`);
|
|
142650
|
+
return [...handoffSummaryInstructions(input), input.rejection === "summary-uncited" || invented.length === 0 ? "your previous answer was discarded because it cited no sequence from the transcript; every claim needs one" : `your previous answer was discarded because it cited ${invented.join(" and ")}, which this transcript does not contain; never write a number that is not at a line start`];
|
|
142651
|
+
}
|
|
141680
142652
|
/** How a summarized handoff opens, so a caller can tell one from a fallback. */
|
|
141681
142653
|
const SERVER_HANDOFF_SUMMARY_PREFIX = "Server evidence handoff for builder thread";
|
|
141682
142654
|
/**
|
|
@@ -141706,6 +142678,7 @@ const EMPTY_EVIDENCE = {
|
|
|
141706
142678
|
eventCount: 0,
|
|
141707
142679
|
includedCount: 0,
|
|
141708
142680
|
droppedCount: 0,
|
|
142681
|
+
coalescedCount: 0,
|
|
141709
142682
|
clippedFields: 0,
|
|
141710
142683
|
firstSequence: null,
|
|
141711
142684
|
lastSequence: null,
|
|
@@ -141765,19 +142738,20 @@ function resolveHandoffModel(settings) {
|
|
|
141765
142738
|
};
|
|
141766
142739
|
}
|
|
141767
142740
|
/**
|
|
141768
|
-
*
|
|
142741
|
+
* Why a summary may not stand in for reading the range, or null when it may.
|
|
141769
142742
|
*
|
|
141770
|
-
* Empty
|
|
141771
|
-
*
|
|
141772
|
-
*
|
|
141773
|
-
*
|
|
142743
|
+
* Empty and uncited are refused because a claim with no sequence behind it
|
|
142744
|
+
* cannot be checked against the ledger, and a citation outside the window is
|
|
142745
|
+
* refused because it is evidence of a model describing something other than
|
|
142746
|
+
* this turn. The two are named apart because they fail differently: one is a
|
|
142747
|
+
* summarizer that ignored the rule, the other one that read something else.
|
|
141774
142748
|
*/
|
|
141775
|
-
function
|
|
142749
|
+
function summaryRejection(input) {
|
|
141776
142750
|
const trimmed = input.summary.trim();
|
|
141777
|
-
if (trimmed.length === 0) return
|
|
142751
|
+
if (trimmed.length === 0) return "summary-uncited";
|
|
141778
142752
|
const cited = citedSequences(trimmed);
|
|
141779
|
-
if (cited.length === 0) return
|
|
141780
|
-
return cited.every((sequence) => input.includedSequences.has(sequence));
|
|
142753
|
+
if (cited.length === 0) return "summary-uncited";
|
|
142754
|
+
return cited.every((sequence) => input.includedSequences.has(sequence)) ? null : "summary-citation-invalid";
|
|
141781
142755
|
}
|
|
141782
142756
|
const buildFusionEvidenceHandoff = Effect.fn("fusionEvidenceHandoff.build")(function* (input) {
|
|
141783
142757
|
const engine = yield* OrchestrationEngineService;
|
|
@@ -141833,20 +142807,51 @@ const buildFusionEvidenceHandoff = Effect.fn("fusionEvidenceHandoff.build")(func
|
|
|
141833
142807
|
if (driver === void 0 || !HARDENED_SUMMARY_DRIVERS.has(driver)) return yield* fallback("provider-profile-unhardened");
|
|
141834
142808
|
if (textGeneration.summarizeFusionEvidence === void 0) return yield* fallback("provider-cannot-summarize");
|
|
141835
142809
|
const summarize = textGeneration.summarizeFusionEvidence;
|
|
141836
|
-
const
|
|
142810
|
+
const runSummary = (instructions) => Effect.suspend(() => summarize({
|
|
141837
142811
|
cwd: serverConfig.cwd,
|
|
141838
142812
|
transcript: evidence.transcript,
|
|
141839
142813
|
maxTranscriptChars: HANDOFF_TRANSCRIPT_MAX_CHARS,
|
|
141840
|
-
instructions
|
|
142814
|
+
instructions,
|
|
141841
142815
|
modelSelection: model
|
|
141842
|
-
})).pipe(Effect.map((result) => Option.some(result)), Effect.
|
|
141843
|
-
|
|
141844
|
-
|
|
141845
|
-
|
|
141846
|
-
|
|
141847
|
-
|
|
141848
|
-
|
|
141849
|
-
|
|
142816
|
+
})).pipe(Effect.map((result) => Option.some(result)), Effect.catchCause(() => Effect.succeed(Option.none())));
|
|
142817
|
+
/** The hardened identity checks, which a repair may never talk its way past. */
|
|
142818
|
+
const identityRejection = (result) => result.agentName !== P4_SUMMARIZER_AGENT.name && result.agentName !== "summarizer" ? "summarizer-agent-mismatch" : result.requestedModel !== model.model ? "summarizer-model-mismatch" : null;
|
|
142819
|
+
const noSummary = (reason) => ({
|
|
142820
|
+
produced: Option.none(),
|
|
142821
|
+
reason
|
|
142822
|
+
});
|
|
142823
|
+
const attempt = yield* Effect.gen(function* () {
|
|
142824
|
+
const first = yield* runSummary(handoffSummaryInstructions({ includedSequences: evidence.includedSequences }));
|
|
142825
|
+
if (Option.isNone(first)) return noSummary("summary-unavailable");
|
|
142826
|
+
const identity = identityRejection(first.value);
|
|
142827
|
+
if (identity !== null) return noSummary(identity);
|
|
142828
|
+
const rejection = summaryRejection({
|
|
142829
|
+
summary: first.value.summary,
|
|
142830
|
+
includedSequences: evidence.includedSequences
|
|
142831
|
+
});
|
|
142832
|
+
if (rejection === null) return {
|
|
142833
|
+
produced: first,
|
|
142834
|
+
reason: null
|
|
142835
|
+
};
|
|
142836
|
+
const repaired = yield* runSummary(handoffSummaryRepairInstructions({
|
|
142837
|
+
rejection,
|
|
142838
|
+
summary: first.value.summary,
|
|
142839
|
+
includedSequences: evidence.includedSequences
|
|
142840
|
+
}));
|
|
142841
|
+
if (Option.isNone(repaired)) return noSummary(rejection);
|
|
142842
|
+
const repairedIdentity = identityRejection(repaired.value);
|
|
142843
|
+
if (repairedIdentity !== null) return noSummary(repairedIdentity);
|
|
142844
|
+
const repairedRejection = summaryRejection({
|
|
142845
|
+
summary: repaired.value.summary,
|
|
142846
|
+
includedSequences: evidence.includedSequences
|
|
142847
|
+
});
|
|
142848
|
+
return repairedRejection === null ? {
|
|
142849
|
+
produced: repaired,
|
|
142850
|
+
reason: null
|
|
142851
|
+
} : noSummary(repairedRejection);
|
|
142852
|
+
}).pipe(Effect.timeoutOption(HANDOFF_TIMEOUT), Effect.map(Option.getOrElse(() => noSummary("summary-unavailable"))));
|
|
142853
|
+
const produced = attempt.produced;
|
|
142854
|
+
if (Option.isNone(produced)) return yield* fallback(attempt.reason ?? "summary-unavailable");
|
|
141850
142855
|
const preparedAt = DateTime.formatIso(yield* DateTime.now);
|
|
141851
142856
|
return {
|
|
141852
142857
|
status: "summarized",
|
|
@@ -142133,6 +143138,25 @@ const make$3 = Effect.gen(function* () {
|
|
|
142133
143138
|
*/
|
|
142134
143139
|
const triageSkipStreak = /* @__PURE__ */ new Map();
|
|
142135
143140
|
/**
|
|
143141
|
+
* Consecutive status turns coalesced per pair. In-memory like the counters
|
|
143142
|
+
* above, and safe there: the classification is recomputed from the same
|
|
143143
|
+
* events on replay, so a restart costs at most one extra wake rather than a
|
|
143144
|
+
* wrong one.
|
|
143145
|
+
*/
|
|
143146
|
+
const statusCoalesceStreak = /* @__PURE__ */ new Map();
|
|
143147
|
+
/**
|
|
143148
|
+
* The monitor tasks each pair currently has open.
|
|
143149
|
+
*
|
|
143150
|
+
* A progress row carries no task type, so the only thing that makes it a
|
|
143151
|
+
* wait rather than unclassified work is the `task.started` that named it -
|
|
143152
|
+
* and that row belongs to an earlier turn, outside the window this turn is
|
|
143153
|
+
* described over. Held per pair so an open watch stays recognisable across
|
|
143154
|
+
* the turns it reports through, and dropped the moment the task ends.
|
|
143155
|
+
* In-memory like the counters above, and safe the same way: a restart
|
|
143156
|
+
* forgets the watch and costs one extra wake rather than a wrong one.
|
|
143157
|
+
*/
|
|
143158
|
+
const openMonitorTaskIds = /* @__PURE__ */ new Map();
|
|
143159
|
+
/**
|
|
142136
143160
|
* Gate detection and human-override resolution act on the CURRENT persisted
|
|
142137
143161
|
* gate state, so replaying them against historical events is wrong twice
|
|
142138
143162
|
* over: an old plan event would open a fresh gate, and an old user message
|
|
@@ -142551,15 +143575,28 @@ const make$3 = Effect.gen(function* () {
|
|
|
142551
143575
|
/**
|
|
142552
143576
|
* What the builder did between two review boundaries, read from the event
|
|
142553
143577
|
* window rather than remembered: whether supervisor advice started the turn,
|
|
142554
|
-
* whether a human spoke into it,
|
|
142555
|
-
*
|
|
142556
|
-
*
|
|
142557
|
-
* see rather than fund.
|
|
143578
|
+
* whether a human spoke into it, whether any tool ran, and whether anything
|
|
143579
|
+
* happened that a supervisor has to see. A text-only builder question counts
|
|
143580
|
+
* as idle on purpose: one or two such rounds pass unhindered, and a longer
|
|
143581
|
+
* tool-free exchange is a debate the user should see rather than fund.
|
|
143582
|
+
*
|
|
143583
|
+
* `waitMarked`, `changedState`, `failedTool` and `gateActivity` are read from
|
|
143584
|
+
* activity kinds and typed task classifications alone; nothing here inspects
|
|
143585
|
+
* prose. Only a live, named wait marks a turn as waiting: a wake scheduled
|
|
143586
|
+
* for a real time, or a Monitor-tool task and its own progress. A command, a
|
|
143587
|
+
* background shell and an unnamed task all count as work, because any of
|
|
143588
|
+
* them can change the repository, and every ending - a cancelled wake, a
|
|
143589
|
+
* completed task, a failure, a decline - is a result rather than a wait.
|
|
142558
143590
|
*/
|
|
142559
143591
|
const describeBuilderTurn = (input) => {
|
|
142560
143592
|
let startedByAdvice = false;
|
|
142561
143593
|
let humanMessage = false;
|
|
142562
143594
|
let ranTools = false;
|
|
143595
|
+
let changedState = false;
|
|
143596
|
+
let failedTool = false;
|
|
143597
|
+
let gateActivity = false;
|
|
143598
|
+
let waitMarked = false;
|
|
143599
|
+
const monitorTaskIds = input.monitorTaskIds;
|
|
142563
143600
|
let summary;
|
|
142564
143601
|
const assistantMessages = /* @__PURE__ */ new Map();
|
|
142565
143602
|
for (const event of input.events) {
|
|
@@ -142573,12 +143610,39 @@ const make$3 = Effect.gen(function* () {
|
|
|
142573
143610
|
if (event.payload.text.startsWith("[fusion-advice]")) startedByAdvice = true;
|
|
142574
143611
|
else if (!isFusionProtocolText(event.payload.text)) humanMessage = true;
|
|
142575
143612
|
}
|
|
142576
|
-
if (event.type === "thread.activity-appended" && event.payload.threadId === input.implementerThreadId
|
|
143613
|
+
if (event.type === "thread.activity-appended" && event.payload.threadId === input.implementerThreadId) {
|
|
143614
|
+
const activity = event.payload.activity;
|
|
143615
|
+
const activityPayload = activity.payload ?? {};
|
|
143616
|
+
const unsuccessful = activityPayload.status === "failed" || activityPayload.status === "declined";
|
|
143617
|
+
if (activity.kind.startsWith("tool.")) {
|
|
143618
|
+
ranTools = true;
|
|
143619
|
+
if (activity.kind === "tool.denied" || unsuccessful) failedTool = true;
|
|
143620
|
+
else changedState = true;
|
|
143621
|
+
} else if (activity.kind === "wake.scheduled") if (activityPayload.wakeAt === null || activityPayload.wakeAt === void 0) changedState = true;
|
|
143622
|
+
else waitMarked = true;
|
|
143623
|
+
else if (activity.kind.startsWith("task.")) {
|
|
143624
|
+
if ((unsuccessful || activity.kind === "task.completed") && activityPayload.taskId !== void 0) monitorTaskIds.delete(activityPayload.taskId);
|
|
143625
|
+
if (unsuccessful) failedTool = true;
|
|
143626
|
+
else if (activity.kind === "task.completed") changedState = true;
|
|
143627
|
+
else if (activity.kind === "task.started") {
|
|
143628
|
+
const taskType = activityPayload.taskType;
|
|
143629
|
+
if (taskType !== void 0 && MONITOR_TOOL_TASK_TYPES.has(taskType)) {
|
|
143630
|
+
waitMarked = true;
|
|
143631
|
+
if (activityPayload.taskId !== void 0) monitorTaskIds.add(activityPayload.taskId);
|
|
143632
|
+
} else if (taskType === void 0 || !INERT_TASK_TYPES.has(taskType)) changedState = true;
|
|
143633
|
+
} else if (activity.kind === "task.progress" && activityPayload.taskId !== void 0 && monitorTaskIds.has(activityPayload.taskId)) waitMarked = true;
|
|
143634
|
+
}
|
|
143635
|
+
}
|
|
143636
|
+
if ((event.type === "thread-pair.gate-opened" || event.type === "thread-pair.gate-advanced" || event.type === "thread-pair.gate-resolved") && event.payload.pairId === input.pairId) gateActivity = true;
|
|
142577
143637
|
}
|
|
142578
143638
|
return {
|
|
142579
143639
|
startedByAdvice,
|
|
142580
143640
|
humanMessage,
|
|
142581
143641
|
ranTools,
|
|
143642
|
+
changedState,
|
|
143643
|
+
failedTool,
|
|
143644
|
+
gateActivity,
|
|
143645
|
+
waitMarked,
|
|
142582
143646
|
summary
|
|
142583
143647
|
};
|
|
142584
143648
|
};
|
|
@@ -143158,7 +144222,7 @@ const make$3 = Effect.gen(function* () {
|
|
|
143158
144222
|
});
|
|
143159
144223
|
return;
|
|
143160
144224
|
}
|
|
143161
|
-
const idleCycles = turn.startedByAdvice && !turn.humanMessage && !turn.ranTools ? (idleAdviceCycles.get(currentPair.id) ?? 0) + 1 : 0;
|
|
144225
|
+
const idleCycles = turn.startedByAdvice && !turn.humanMessage && !turn.ranTools && !turn.waitMarked && !turn.changedState && !turn.failedTool ? (idleAdviceCycles.get(currentPair.id) ?? 0) + 1 : 0;
|
|
143162
144226
|
idleAdviceCycles.set(currentPair.id, idleCycles);
|
|
143163
144227
|
if (idleCycles >= 3) {
|
|
143164
144228
|
idleAdviceCycles.delete(currentPair.id);
|
|
@@ -143170,6 +144234,18 @@ const make$3 = Effect.gen(function* () {
|
|
|
143170
144234
|
});
|
|
143171
144235
|
return;
|
|
143172
144236
|
}
|
|
144237
|
+
const statusOnly = turn.startedByAdvice && !turn.humanMessage && turn.waitMarked && !turn.changedState && !turn.failedTool && !turn.gateActivity;
|
|
144238
|
+
const streak = statusCoalesceStreak.get(currentPair.id);
|
|
144239
|
+
const alreadyCounted = streak !== void 0 && completion.sequence <= streak.throughSequence;
|
|
144240
|
+
const coalesced = statusOnly ? alreadyCounted ? streak.count : (streak?.count ?? 0) + 1 : 0;
|
|
144241
|
+
if (statusOnly && coalesced < 5) {
|
|
144242
|
+
statusCoalesceStreak.set(currentPair.id, {
|
|
144243
|
+
count: coalesced,
|
|
144244
|
+
throughSequence: Math.max(streak?.throughSequence ?? 0, completion.sequence)
|
|
144245
|
+
});
|
|
144246
|
+
return;
|
|
144247
|
+
}
|
|
144248
|
+
statusCoalesceStreak.delete(currentPair.id);
|
|
143173
144249
|
const triageSettings = yield* readTriageSettings;
|
|
143174
144250
|
if (triageSettings !== null) {
|
|
143175
144251
|
if (currentPair.triagedFromSequence !== null && completion.sequence <= currentPair.triagedFromSequence) return;
|
|
@@ -143256,13 +144332,18 @@ const make$3 = Effect.gen(function* () {
|
|
|
143256
144332
|
if (pair.reviewExperiment) completions = completions.filter((completion) => completion.payload.state === "completed").slice(-1);
|
|
143257
144333
|
let boundary = activationCompletion?.sequence ?? pair.lastReviewedImplementerSequence;
|
|
143258
144334
|
for (const completion of completions.filter((candidate) => candidate.payload.state === "completed")) {
|
|
144335
|
+
const monitorTaskIds = openMonitorTaskIds.get(pair.id) ?? /* @__PURE__ */ new Set();
|
|
143259
144336
|
const turn = describeBuilderTurn({
|
|
143260
144337
|
events,
|
|
143261
144338
|
implementerThreadId: pair.implementerThreadId,
|
|
144339
|
+
pairId: pair.id,
|
|
143262
144340
|
afterSequence: boundary,
|
|
143263
144341
|
throughSequence: completion.sequence,
|
|
143264
|
-
turnId: completion.payload.turnId
|
|
144342
|
+
turnId: completion.payload.turnId,
|
|
144343
|
+
monitorTaskIds
|
|
143265
144344
|
});
|
|
144345
|
+
if (monitorTaskIds.size === 0) openMonitorTaskIds.delete(pair.id);
|
|
144346
|
+
else openMonitorTaskIds.set(pair.id, monitorTaskIds);
|
|
143266
144347
|
boundary = completion.sequence;
|
|
143267
144348
|
yield* processReview(pair, completion, turn);
|
|
143268
144349
|
}
|
|
@@ -144891,11 +145972,11 @@ const WorkspaceEntriesLayerLive = layer$17.pipe(Layer.provide(layer$70));
|
|
|
144891
145972
|
const WorkspaceFileSystemLayerLive = layer$16.pipe(Layer.provide(layer$70), Layer.provide(WorkspaceEntriesLayerLive));
|
|
144892
145973
|
const WorkspaceLayerLive = Layer.mergeAll(layer$70, WorkspaceEntriesLayerLive, WorkspaceFileSystemLayerLive);
|
|
144893
145974
|
const ProjectFaviconResolverLayerLive = layer$65.pipe(Layer.provide(layer$70), Layer.provide(layer$66));
|
|
144894
|
-
const AuthLayerLive = layer$
|
|
145975
|
+
const AuthLayerLive = layer$115.pipe(Layer.provideMerge(PersistenceLayerLive), Layer.provide(layer$119));
|
|
144895
145976
|
const ProviderRuntimeLayerLive = Layer.mergeAll(ProviderSessionReaperLive, ProviderHealthRefresherLive).pipe(Layer.provideMerge(ProviderLayerLive), Layer.provideMerge(OrchestrationLayerLive));
|
|
144896
|
-
const RuntimeCoreDependenciesLive = ReactorLayerLive.pipe(Layer.provideMerge(layer$27), Layer.provideMerge(Layer.mergeAll(PullRequestServiceLive, CheckpointingLayerLive, SourceControlProviderRegistryLayerLive)), Layer.provideMerge(GitLayerLive), Layer.provideMerge(VcsLayerLive), Layer.provideMerge(ProviderAuthServiceLive), Layer.provideMerge(ProviderRuntimeLayerLive), Layer.provideMerge(Layer.mergeAll(TerminalLayerLive, PreviewLayerLive, RoutedTaskRepositoryLive)), Layer.provideMerge(Layer.mergeAll(PersistenceLayerLive, layer$89)), Layer.provideMerge(layer$83), Layer.provideMerge(ProviderRegistryLive), Layer.provideMerge(layer$60), Layer.provideMerge(Layer.mergeAll(layer$59, layer$72.pipe(Layer.provide(layer$85)), layer$74, layer$69)), Layer.provideMerge(layer$54), Layer.provideMerge(layer$55), Layer.provideMerge(layer$56), Layer.provideMerge(layer$57), Layer.provideMerge(layer$58)).pipe(Layer.provideMerge(ProviderInstanceRegistryHydrationLive), Layer.provideMerge(AntigravityInstallation.layer), Layer.provideMerge(ProviderEventLoggersLive), Layer.provideMerge(OpenCodeRuntimeLive), Layer.provideMerge(layer$62), Layer.provideMerge(JevWorkspaceAdvisorLive.pipe(Layer.provide(TypeSafeClientLive))), Layer.provideMerge(layer$93.pipe(Layer.provide(layer$
|
|
144897
|
-
const UsageLayerLive = layer$25.pipe(Layer.provide(layer$93.pipe(Layer.provide(layer$
|
|
144898
|
-
const RuntimeDependenciesLive = layer$24.pipe(Layer.provideMerge(RuntimeCoreDependenciesLive)).pipe(Layer.provideMerge(UsageLayerLive), Layer.provideMerge(layer$12), Layer.provideMerge(layer$11), Layer.provideMerge(layer$10), Layer.provideMerge(layer$80), Layer.provideMerge(layer$82), Layer.provideMerge(layer$81), Layer.provide(layer$
|
|
145977
|
+
const RuntimeCoreDependenciesLive = ReactorLayerLive.pipe(Layer.provideMerge(layer$27), Layer.provideMerge(Layer.mergeAll(PullRequestServiceLive, CheckpointingLayerLive, SourceControlProviderRegistryLayerLive)), Layer.provideMerge(GitLayerLive), Layer.provideMerge(VcsLayerLive), Layer.provideMerge(ProviderAuthServiceLive), Layer.provideMerge(ProviderRuntimeLayerLive), Layer.provideMerge(Layer.mergeAll(TerminalLayerLive, PreviewLayerLive, RoutedTaskRepositoryLive)), Layer.provideMerge(Layer.mergeAll(PersistenceLayerLive, layer$89)), Layer.provideMerge(layer$83), Layer.provideMerge(ProviderRegistryLive), Layer.provideMerge(layer$60), Layer.provideMerge(Layer.mergeAll(layer$59, layer$72.pipe(Layer.provide(layer$85)), layer$74, layer$69)), Layer.provideMerge(layer$54), Layer.provideMerge(layer$55), Layer.provideMerge(layer$56), Layer.provideMerge(layer$57), Layer.provideMerge(layer$58)).pipe(Layer.provideMerge(ProviderInstanceRegistryHydrationLive), Layer.provideMerge(AntigravityInstallation.layer), Layer.provideMerge(ProviderEventLoggersLive), Layer.provideMerge(OpenCodeRuntimeLive), Layer.provideMerge(layer$62), Layer.provideMerge(JevWorkspaceAdvisorLive.pipe(Layer.provide(TypeSafeClientLive))), Layer.provideMerge(layer$93.pipe(Layer.provide(layer$119))), Layer.provideMerge(SupervisorSecretsLive.pipe(Layer.provide(layer$119))), Layer.provideMerge(WorkspaceLayerLive), Layer.provideMerge(ProjectFaviconResolverLayerLive), Layer.provideMerge(layer$84), Layer.provideMerge(layer$68), Layer.provideMerge(remoteComputerUseBindingsLayer.pipe(Layer.provide(layer$89))), Layer.provideMerge(layer$77), Layer.provideMerge(AuthLayerLive), Layer.provideMerge(layer$5.pipe(Layer.provideMerge(LinearClientLive), Layer.provideMerge(Layer.mergeAll(LinearMcpClientLive, TrackerSecretsLive)), Layer.provideMerge(Layer.mergeAll(layer$90, layer$21).pipe(Layer.provideMerge(layer$91), Layer.provideMerge(layer$92))))), Layer.provideMerge(layer$119));
|
|
145978
|
+
const UsageLayerLive = layer$25.pipe(Layer.provide(layer$93.pipe(Layer.provide(layer$119))));
|
|
145979
|
+
const RuntimeDependenciesLive = layer$24.pipe(Layer.provideMerge(RuntimeCoreDependenciesLive)).pipe(Layer.provideMerge(UsageLayerLive), Layer.provideMerge(layer$12), Layer.provideMerge(layer$11), Layer.provideMerge(layer$10), Layer.provideMerge(layer$80), Layer.provideMerge(layer$82), Layer.provideMerge(layer$81), Layer.provide(layer$123));
|
|
144899
145980
|
/**
|
|
144900
145981
|
* Hub asset sync.
|
|
144901
145982
|
*
|
|
@@ -145547,7 +146628,7 @@ const serviceCommand = Command.make("service").pipe(Command.withDescription("Man
|
|
|
145547
146628
|
]));
|
|
145548
146629
|
//#endregion
|
|
145549
146630
|
//#region src/bin.ts
|
|
145550
|
-
const CliRuntimeLayer = Layer.mergeAll(NodeServices.layer, layer$
|
|
146631
|
+
const CliRuntimeLayer = Layer.mergeAll(NodeServices.layer, layer$123, FetchHttpClient.layer);
|
|
145551
146632
|
/**
|
|
145552
146633
|
* Prints an update reminder before dispatching, at most once a day.
|
|
145553
146634
|
*
|