@ouro.bot/cli 0.1.0-alpha.664 → 0.1.0-alpha.666
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/changelog.json +12 -0
- package/dist/arc/flight-recorder.js +267 -4
- package/dist/heart/context-loss-sentinel.js +55 -9
- package/dist/heart/core.js +167 -4
- package/dist/heart/cross-chat-delivery.js +3 -2
- package/dist/heart/daemon/cli-exec.js +50 -1
- package/dist/heart/daemon/cli-help.js +1 -1
- package/dist/heart/daemon/cli-parse.js +36 -2
- package/dist/heart/daemon/daemon-entry.js +24 -5
- package/dist/heart/daemon/daemon.js +10 -1
- package/dist/heart/habits/habit-scheduler.js +24 -5
- package/dist/heart/habits/habit-session.js +563 -0
- package/dist/heart/mailbox/mailbox-http-hooks.js +2 -0
- package/dist/heart/mailbox/mailbox-http-routes.js +40 -0
- package/dist/heart/mailbox/mailbox-read.js +3 -1
- package/dist/heart/mailbox/readers/runtime-readers.js +56 -0
- package/dist/mailbox-ui/assets/index-CaTIFDmv.js +1 -0
- package/dist/mailbox-ui/assets/index-Du_9G9WO.css +1 -0
- package/dist/mailbox-ui/assets/vendor-CcN1XpQ9.js +61 -0
- package/dist/mailbox-ui/index.html +3 -2
- package/dist/repertoire/tools-notes.js +50 -0
- package/dist/repertoire/tools-record.js +13 -0
- package/dist/repertoire/tools-session.js +14 -0
- package/dist/repertoire/tools-surface.js +11 -0
- package/dist/repertoire/tools.js +7 -0
- package/dist/senses/inner-dialog-worker.js +153 -69
- package/dist/senses/inner-dialog.js +5 -3
- package/dist/senses/pipeline.js +0 -9
- package/dist/senses/surface-tool.js +2 -1
- package/package.json +1 -1
- package/dist/mailbox-ui/assets/index-BZ60na8O.js +0 -61
- package/dist/mailbox-ui/assets/index-DG6Xf5uL.css +0 -1
package/changelog.json
CHANGED
|
@@ -1,6 +1,18 @@
|
|
|
1
1
|
{
|
|
2
2
|
"_note": "This changelog is maintained as part of the PR/version-bump workflow. Agent-curated, not auto-generated. Agents read this file directly via read_file to understand what changed between versions.",
|
|
3
3
|
"versions": [
|
|
4
|
+
{
|
|
5
|
+
"version": "0.1.0-alpha.666",
|
|
6
|
+
"changes": [
|
|
7
|
+
"Close pre-merge habit session review gaps for route gating, executable risk policy, and Desk produced refs."
|
|
8
|
+
]
|
|
9
|
+
},
|
|
10
|
+
{
|
|
11
|
+
"version": "0.1.0-alpha.665",
|
|
12
|
+
"changes": [
|
|
13
|
+
"Ignore Sentinel-owned daemon-health git dirt when computing the Sentinel bundle signal, preventing a self-sustaining health-receipt loop while keeping real bundle dirt visible."
|
|
14
|
+
]
|
|
15
|
+
},
|
|
4
16
|
{
|
|
5
17
|
"version": "0.1.0-alpha.664",
|
|
6
18
|
"changes": [
|
|
@@ -33,11 +33,15 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
33
33
|
};
|
|
34
34
|
})();
|
|
35
35
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.isHabitRunTrigger = isHabitRunTrigger;
|
|
36
37
|
exports.flightRecorderLatestPath = flightRecorderLatestPath;
|
|
37
38
|
exports.isFlightRecorderResume = isFlightRecorderResume;
|
|
38
39
|
exports.readFlightRecorderResume = readFlightRecorderResume;
|
|
39
40
|
exports.writeFlightRecorderResume = writeFlightRecorderResume;
|
|
40
41
|
exports.recordFlightRecorderEvent = recordFlightRecorderEvent;
|
|
42
|
+
exports.isSafeHabitRunId = isSafeHabitRunId;
|
|
43
|
+
exports.readHabitRunReceipt = readHabitRunReceipt;
|
|
44
|
+
exports.listHabitRunReceipts = listHabitRunReceipts;
|
|
41
45
|
exports.writeHabitRunReceipt = writeHabitRunReceipt;
|
|
42
46
|
exports.formatFlightRecorderResume = formatFlightRecorderResume;
|
|
43
47
|
exports.createHabitRunId = createHabitRunId;
|
|
@@ -46,6 +50,13 @@ const path = __importStar(require("path"));
|
|
|
46
50
|
const crypto_1 = require("crypto");
|
|
47
51
|
const session_events_1 = require("../heart/session-events");
|
|
48
52
|
const runtime_1 = require("../nerves/runtime");
|
|
53
|
+
function isHabitRunTrigger(value) {
|
|
54
|
+
return value === "cron"
|
|
55
|
+
|| value === "launchd"
|
|
56
|
+
|| value === "poke"
|
|
57
|
+
|| value === "overdue"
|
|
58
|
+
|| value === "manual";
|
|
59
|
+
}
|
|
49
60
|
function flightRecorderDir(agentRoot) {
|
|
50
61
|
return path.join(agentRoot, "arc", "flight-recorder");
|
|
51
62
|
}
|
|
@@ -55,6 +66,9 @@ function eventsDir(agentRoot) {
|
|
|
55
66
|
function receiptsDir(agentRoot) {
|
|
56
67
|
return path.join(flightRecorderDir(agentRoot), "habit-receipts");
|
|
57
68
|
}
|
|
69
|
+
function habitReceiptPath(agentRoot, runId) {
|
|
70
|
+
return path.join(receiptsDir(agentRoot), `${runId}.json`);
|
|
71
|
+
}
|
|
58
72
|
function flightRecorderLatestPath(agentRoot) {
|
|
59
73
|
return path.join(flightRecorderDir(agentRoot), "latest.json");
|
|
60
74
|
}
|
|
@@ -311,20 +325,269 @@ function recordFlightRecorderEvent(agentRoot, input) {
|
|
|
311
325
|
});
|
|
312
326
|
return event;
|
|
313
327
|
}
|
|
314
|
-
function
|
|
315
|
-
|
|
316
|
-
|
|
328
|
+
function isSafeHabitRunId(value) {
|
|
329
|
+
return typeof value === "string"
|
|
330
|
+
&& /^[A-Za-z0-9][A-Za-z0-9_.:-]*$/.test(value)
|
|
331
|
+
&& !value.includes("..");
|
|
332
|
+
}
|
|
333
|
+
function isPlainRecord(value) {
|
|
334
|
+
return !!value && typeof value === "object" && !Array.isArray(value);
|
|
335
|
+
}
|
|
336
|
+
function isProducedRefArray(value) {
|
|
337
|
+
return Array.isArray(value)
|
|
338
|
+
&& value.every((entry) => isPlainRecord(entry)
|
|
339
|
+
&& (entry.kind === "arc"
|
|
340
|
+
|| entry.kind === "desk_task"
|
|
341
|
+
|| entry.kind === "desk_record"
|
|
342
|
+
|| entry.kind === "claim"
|
|
343
|
+
|| entry.kind === "surface"
|
|
344
|
+
|| entry.kind === "none")
|
|
345
|
+
&& typeof entry.locator === "string");
|
|
346
|
+
}
|
|
347
|
+
function isHabitSurfaceAttemptArray(value) {
|
|
348
|
+
return Array.isArray(value)
|
|
349
|
+
&& value.every((entry) => isPlainRecord(entry)
|
|
350
|
+
&& typeof entry.recipient === "string"
|
|
351
|
+
&& typeof entry.channel === "string"
|
|
352
|
+
&& (entry.reason === "needed_input"
|
|
353
|
+
|| entry.reason === "status"
|
|
354
|
+
|| entry.reason === "answer"
|
|
355
|
+
|| entry.reason === "blocked"
|
|
356
|
+
|| entry.reason === "other")
|
|
357
|
+
&& (entry.result === "sent"
|
|
358
|
+
|| entry.result === "delivered"
|
|
359
|
+
|| entry.result === "delivered_now"
|
|
360
|
+
|| entry.result === "queued"
|
|
361
|
+
|| entry.result === "deferred"
|
|
362
|
+
|| entry.result === "blocked"
|
|
363
|
+
|| entry.result === "failed"
|
|
364
|
+
|| entry.result === "unavailable")
|
|
365
|
+
&& (entry.routeKind === undefined
|
|
366
|
+
|| entry.routeKind === "family"
|
|
367
|
+
|| entry.routeKind === "originator"
|
|
368
|
+
|| entry.routeKind === "extra")
|
|
369
|
+
&& (entry.rawStatus === undefined || typeof entry.rawStatus === "string")
|
|
370
|
+
&& (entry.error === undefined || typeof entry.error === "string"));
|
|
371
|
+
}
|
|
372
|
+
function isHabitReturnRouteArray(value) {
|
|
373
|
+
return Array.isArray(value)
|
|
374
|
+
&& value.every((entry) => isPlainRecord(entry)
|
|
375
|
+
&& (entry.kind === "family" || entry.kind === "originator" || entry.kind === "extra")
|
|
376
|
+
&& typeof entry.recipient === "string"
|
|
377
|
+
&& (entry.status === "allowed" || entry.status === "unresolved")
|
|
378
|
+
&& (entry.friendId === undefined || typeof entry.friendId === "string")
|
|
379
|
+
&& (entry.channel === undefined || typeof entry.channel === "string")
|
|
380
|
+
&& (entry.key === undefined || typeof entry.key === "string")
|
|
381
|
+
&& (entry.reason === undefined || typeof entry.reason === "string"));
|
|
382
|
+
}
|
|
383
|
+
function isHabitPermissionEnvelope(value) {
|
|
384
|
+
if (!isPlainRecord(value))
|
|
385
|
+
return false;
|
|
386
|
+
return value.schemaVersion === 1
|
|
387
|
+
&& typeof value.canMessageOutward === "boolean"
|
|
388
|
+
&& isHabitReturnRouteArray(value.returnRoutes)
|
|
389
|
+
&& isStringArray(value.deniedTools)
|
|
390
|
+
&& isStringArray(value.warnings);
|
|
391
|
+
}
|
|
392
|
+
function isHabitToolPolicy(value) {
|
|
393
|
+
if (!isPlainRecord(value))
|
|
394
|
+
return false;
|
|
395
|
+
return (value.requestedTools === null || isStringArray(value.requestedTools))
|
|
396
|
+
&& isStringArray(value.grantedTools)
|
|
397
|
+
&& isStringArray(value.deniedTools)
|
|
398
|
+
&& typeof value.outwardMessagingAllowed === "boolean";
|
|
399
|
+
}
|
|
400
|
+
function isHabitRunReceipt(value) {
|
|
401
|
+
if (!isPlainRecord(value))
|
|
402
|
+
return false;
|
|
403
|
+
return value.schemaVersion === 2
|
|
404
|
+
&& isSafeHabitRunId(value.runId)
|
|
405
|
+
&& typeof value.sessionId === "string"
|
|
406
|
+
&& typeof value.habitName === "string"
|
|
407
|
+
&& (value.trigger === "cron"
|
|
408
|
+
|| value.trigger === "launchd"
|
|
409
|
+
|| value.trigger === "poke"
|
|
410
|
+
|| value.trigger === "overdue"
|
|
411
|
+
|| value.trigger === "manual")
|
|
412
|
+
&& typeof value.startedAt === "string"
|
|
413
|
+
&& typeof value.endedAt === "string"
|
|
414
|
+
&& (value.outcome === "no_change"
|
|
415
|
+
|| value.outcome === "wrote_arc"
|
|
416
|
+
|| value.outcome === "updated_desk"
|
|
417
|
+
|| value.outcome === "wrote_record"
|
|
418
|
+
|| value.outcome === "surfaced"
|
|
419
|
+
|| value.outcome === "blocked"
|
|
420
|
+
|| value.outcome === "error")
|
|
421
|
+
&& typeof value.definitionLocator === "string"
|
|
422
|
+
&& typeof value.sessionLocator === "string"
|
|
423
|
+
&& typeof value.pendingLocator === "string"
|
|
424
|
+
&& typeof value.runtimeStateLocator === "string"
|
|
425
|
+
&& typeof value.receiptLocator === "string"
|
|
426
|
+
&& (value.nextRunAt === null || typeof value.nextRunAt === "string")
|
|
427
|
+
&& isHabitPermissionEnvelope(value.permissionEnvelope)
|
|
428
|
+
&& isHabitToolPolicy(value.toolPolicy)
|
|
429
|
+
&& isProducedRefArray(value.producedRefs)
|
|
430
|
+
&& isHabitSurfaceAttemptArray(value.surfaceAttempts)
|
|
431
|
+
&& isStringArray(value.errors);
|
|
432
|
+
}
|
|
433
|
+
function isLegacyHabitRunReceipt(value) {
|
|
434
|
+
if (!isPlainRecord(value))
|
|
435
|
+
return false;
|
|
436
|
+
return value.schemaVersion === 1
|
|
437
|
+
&& isSafeHabitRunId(value.runId)
|
|
438
|
+
&& typeof value.habitName === "string"
|
|
439
|
+
&& (value.trigger === "cron"
|
|
440
|
+
|| value.trigger === "launchd"
|
|
441
|
+
|| value.trigger === "poke"
|
|
442
|
+
|| value.trigger === "overdue"
|
|
443
|
+
|| value.trigger === "manual")
|
|
444
|
+
&& typeof value.startedAt === "string"
|
|
445
|
+
&& typeof value.endedAt === "string"
|
|
446
|
+
&& (value.outcome === "no_change"
|
|
447
|
+
|| value.outcome === "wrote_arc"
|
|
448
|
+
|| value.outcome === "updated_desk"
|
|
449
|
+
|| value.outcome === "wrote_record"
|
|
450
|
+
|| value.outcome === "surfaced"
|
|
451
|
+
|| value.outcome === "blocked"
|
|
452
|
+
|| value.outcome === "error")
|
|
453
|
+
&& isProducedRefArray(value.producedRefs)
|
|
454
|
+
&& isHabitSurfaceAttemptArray(value.surfaceAttempts)
|
|
455
|
+
&& isStringArray(value.errors);
|
|
456
|
+
}
|
|
457
|
+
function warnMalformedHabitReceipt(agentRoot, runId, reason) {
|
|
458
|
+
(0, runtime_1.emitNervesEvent)({
|
|
459
|
+
level: "warn",
|
|
460
|
+
component: "mind",
|
|
461
|
+
event: "mind.flight_recorder_habit_receipt_malformed",
|
|
462
|
+
message: "flight recorder habit receipt malformed",
|
|
463
|
+
meta: { agentRoot, runId: (0, session_events_1.capStructuredRecordString)(runId), reason },
|
|
464
|
+
});
|
|
465
|
+
}
|
|
466
|
+
function normalizeLegacyHabitRunReceipt(receipt) {
|
|
467
|
+
const sawSurface = receipt.surfaceAttempts.length > 0 || receipt.producedRefs.some((ref) => ref.kind === "surface");
|
|
468
|
+
return {
|
|
469
|
+
schemaVersion: 2,
|
|
470
|
+
runId: receipt.runId,
|
|
471
|
+
sessionId: receipt.runId,
|
|
472
|
+
habitName: receipt.habitName,
|
|
473
|
+
trigger: receipt.trigger,
|
|
474
|
+
startedAt: receipt.startedAt,
|
|
475
|
+
endedAt: receipt.endedAt,
|
|
476
|
+
outcome: receipt.outcome,
|
|
477
|
+
definitionLocator: `habits/${receipt.habitName}.md`,
|
|
478
|
+
sessionLocator: `state/habit-sessions/${receipt.runId}/session.json`,
|
|
479
|
+
pendingLocator: `state/habit-sessions/${receipt.runId}/pending`,
|
|
480
|
+
runtimeStateLocator: `state/habits/${receipt.habitName}.json`,
|
|
481
|
+
receiptLocator: `arc/flight-recorder/habit-receipts/${receipt.runId}.json`,
|
|
482
|
+
nextRunAt: null,
|
|
483
|
+
permissionEnvelope: {
|
|
484
|
+
schemaVersion: 1,
|
|
485
|
+
canMessageOutward: sawSurface,
|
|
486
|
+
returnRoutes: [],
|
|
487
|
+
deniedTools: sawSurface ? [] : ["send_message", "surface"],
|
|
488
|
+
warnings: ["legacy receipt normalized without habit permission envelope"],
|
|
489
|
+
},
|
|
490
|
+
toolPolicy: {
|
|
491
|
+
requestedTools: null,
|
|
492
|
+
grantedTools: sawSurface ? ["surface"] : [],
|
|
493
|
+
deniedTools: sawSurface ? [] : ["send_message", "surface"],
|
|
494
|
+
outwardMessagingAllowed: sawSurface,
|
|
495
|
+
},
|
|
496
|
+
producedRefs: receipt.producedRefs,
|
|
497
|
+
surfaceAttempts: receipt.surfaceAttempts,
|
|
498
|
+
errors: receipt.errors,
|
|
499
|
+
};
|
|
500
|
+
}
|
|
501
|
+
function capHabitRunReceipt(receipt) {
|
|
502
|
+
return {
|
|
317
503
|
...receipt,
|
|
318
504
|
habitName: (0, session_events_1.capStructuredRecordString)(receipt.habitName),
|
|
505
|
+
definitionLocator: (0, session_events_1.capStructuredRecordString)(receipt.definitionLocator),
|
|
506
|
+
sessionLocator: (0, session_events_1.capStructuredRecordString)(receipt.sessionLocator),
|
|
507
|
+
pendingLocator: (0, session_events_1.capStructuredRecordString)(receipt.pendingLocator),
|
|
508
|
+
runtimeStateLocator: (0, session_events_1.capStructuredRecordString)(receipt.runtimeStateLocator),
|
|
509
|
+
receiptLocator: (0, session_events_1.capStructuredRecordString)(receipt.receiptLocator),
|
|
510
|
+
permissionEnvelope: {
|
|
511
|
+
...receipt.permissionEnvelope,
|
|
512
|
+
returnRoutes: receipt.permissionEnvelope.returnRoutes.map((route) => ({
|
|
513
|
+
...route,
|
|
514
|
+
recipient: (0, session_events_1.capStructuredRecordString)(route.recipient),
|
|
515
|
+
...(route.friendId ? { friendId: (0, session_events_1.capStructuredRecordString)(route.friendId) } : {}),
|
|
516
|
+
...(route.channel ? { channel: (0, session_events_1.capStructuredRecordString)(route.channel) } : {}),
|
|
517
|
+
...(route.key ? { key: (0, session_events_1.capStructuredRecordString)(route.key) } : {}),
|
|
518
|
+
...(route.reason ? { reason: (0, session_events_1.capStructuredRecordString)(route.reason) } : {}),
|
|
519
|
+
})),
|
|
520
|
+
deniedTools: cappedArray(receipt.permissionEnvelope.deniedTools),
|
|
521
|
+
warnings: cappedArray(receipt.permissionEnvelope.warnings),
|
|
522
|
+
},
|
|
523
|
+
toolPolicy: {
|
|
524
|
+
requestedTools: receipt.toolPolicy.requestedTools ? cappedArray(receipt.toolPolicy.requestedTools) : null,
|
|
525
|
+
grantedTools: cappedArray(receipt.toolPolicy.grantedTools),
|
|
526
|
+
deniedTools: cappedArray(receipt.toolPolicy.deniedTools),
|
|
527
|
+
outwardMessagingAllowed: receipt.toolPolicy.outwardMessagingAllowed,
|
|
528
|
+
},
|
|
319
529
|
producedRefs: receipt.producedRefs.map((ref) => ({ ...ref, locator: (0, session_events_1.capStructuredRecordString)(ref.locator) })),
|
|
320
530
|
surfaceAttempts: receipt.surfaceAttempts.map((attempt) => ({
|
|
321
531
|
...attempt,
|
|
322
532
|
recipient: (0, session_events_1.capStructuredRecordString)(attempt.recipient),
|
|
323
533
|
channel: (0, session_events_1.capStructuredRecordString)(attempt.channel),
|
|
534
|
+
...(attempt.rawStatus ? { rawStatus: (0, session_events_1.capStructuredRecordString)(attempt.rawStatus) } : {}),
|
|
535
|
+
...(attempt.error ? { error: (0, session_events_1.capStructuredRecordString)(attempt.error) } : {}),
|
|
324
536
|
})),
|
|
325
537
|
errors: receipt.errors.map((error) => (0, session_events_1.capStructuredRecordString)(error)),
|
|
326
538
|
};
|
|
327
|
-
|
|
539
|
+
}
|
|
540
|
+
function normalizeHabitRunReceiptForWrite(receipt) {
|
|
541
|
+
return capHabitRunReceipt(receipt.schemaVersion === 1 ? normalizeLegacyHabitRunReceipt(receipt) : receipt);
|
|
542
|
+
}
|
|
543
|
+
function readHabitRunReceipt(agentRoot, runId) {
|
|
544
|
+
if (!isSafeHabitRunId(runId)) {
|
|
545
|
+
warnMalformedHabitReceipt(agentRoot, runId, "unsafe run id");
|
|
546
|
+
return null;
|
|
547
|
+
}
|
|
548
|
+
try {
|
|
549
|
+
const parsed = JSON.parse(fs.readFileSync(habitReceiptPath(agentRoot, runId), "utf-8"));
|
|
550
|
+
const receipt = isHabitRunReceipt(parsed)
|
|
551
|
+
? parsed
|
|
552
|
+
: isLegacyHabitRunReceipt(parsed)
|
|
553
|
+
? normalizeLegacyHabitRunReceipt(parsed)
|
|
554
|
+
: null;
|
|
555
|
+
if (!receipt) {
|
|
556
|
+
warnMalformedHabitReceipt(agentRoot, runId, "invalid habit receipt shape");
|
|
557
|
+
return null;
|
|
558
|
+
}
|
|
559
|
+
(0, runtime_1.emitNervesEvent)({
|
|
560
|
+
component: "mind",
|
|
561
|
+
event: "mind.flight_recorder_habit_receipt_read",
|
|
562
|
+
message: "flight recorder habit receipt read",
|
|
563
|
+
meta: { agentRoot, runId },
|
|
564
|
+
});
|
|
565
|
+
return receipt;
|
|
566
|
+
}
|
|
567
|
+
catch (error) {
|
|
568
|
+
warnMalformedHabitReceipt(agentRoot, runId, error instanceof Error ? error.message : /* v8 ignore next -- defensive: non-Error catch branch @preserve */ String(error));
|
|
569
|
+
return null;
|
|
570
|
+
}
|
|
571
|
+
}
|
|
572
|
+
function listHabitRunReceipts(agentRoot, options = {}) {
|
|
573
|
+
const dir = receiptsDir(agentRoot);
|
|
574
|
+
if (!fs.existsSync(dir))
|
|
575
|
+
return [];
|
|
576
|
+
const receipts = fs.readdirSync(dir)
|
|
577
|
+
.filter((fileName) => fileName.endsWith(".json"))
|
|
578
|
+
.map((fileName) => readHabitRunReceipt(agentRoot, path.basename(fileName, ".json")))
|
|
579
|
+
.filter((receipt) => receipt !== null)
|
|
580
|
+
.sort((left, right) => right.endedAt.localeCompare(left.endedAt) || right.runId.localeCompare(left.runId));
|
|
581
|
+
return typeof options.limit === "number" && options.limit >= 0 ? receipts.slice(0, options.limit) : receipts;
|
|
582
|
+
}
|
|
583
|
+
function writeHabitRunReceipt(agentRoot, receipt) {
|
|
584
|
+
fs.mkdirSync(receiptsDir(agentRoot), { recursive: true });
|
|
585
|
+
const safeReceipt = normalizeHabitRunReceiptForWrite(receipt);
|
|
586
|
+
if (!isSafeHabitRunId(safeReceipt.runId)) {
|
|
587
|
+
warnMalformedHabitReceipt(agentRoot, safeReceipt.runId, "unsafe run id");
|
|
588
|
+
throw new Error(`unsafe habit run id: ${safeReceipt.runId}`);
|
|
589
|
+
}
|
|
590
|
+
atomicWriteJson(habitReceiptPath(agentRoot, safeReceipt.runId), safeReceipt);
|
|
328
591
|
recordFlightRecorderEvent(agentRoot, {
|
|
329
592
|
kind: "habit_run",
|
|
330
593
|
recordedAt: safeReceipt.endedAt,
|
|
@@ -482,7 +482,7 @@ function healthSignals(results) {
|
|
|
482
482
|
}
|
|
483
483
|
function defaultGitStatus(agentRoot) {
|
|
484
484
|
try {
|
|
485
|
-
const porcelain = (0, child_process_1.execFileSync)("git", ["status", "--porcelain"], {
|
|
485
|
+
const porcelain = (0, child_process_1.execFileSync)("git", ["status", "--porcelain=v1", "-uall"], {
|
|
486
486
|
cwd: agentRoot,
|
|
487
487
|
encoding: "utf-8",
|
|
488
488
|
stdio: ["ignore", "pipe", "pipe"],
|
|
@@ -494,7 +494,50 @@ function defaultGitStatus(agentRoot) {
|
|
|
494
494
|
return { ok: false, error: String(error) };
|
|
495
495
|
}
|
|
496
496
|
}
|
|
497
|
-
function
|
|
497
|
+
function gitStatusEntries(porcelain) {
|
|
498
|
+
return porcelain.split(/\r?\n/).filter((line) => line.trim().length > 0);
|
|
499
|
+
}
|
|
500
|
+
function normalizeGitStatusPath(entryPath) {
|
|
501
|
+
return entryPath.trim().replace(/^"|"$/g, "");
|
|
502
|
+
}
|
|
503
|
+
function gitStatusPaths(entry) {
|
|
504
|
+
const rawPath = entry.slice(3).trim();
|
|
505
|
+
const paths = [];
|
|
506
|
+
let start = 0;
|
|
507
|
+
let inQuote = false;
|
|
508
|
+
let escaped = false;
|
|
509
|
+
for (let index = 0; index < rawPath.length; index += 1) {
|
|
510
|
+
const char = rawPath[index];
|
|
511
|
+
if (escaped) {
|
|
512
|
+
escaped = false;
|
|
513
|
+
continue;
|
|
514
|
+
}
|
|
515
|
+
if (inQuote && char === "\\") {
|
|
516
|
+
escaped = true;
|
|
517
|
+
continue;
|
|
518
|
+
}
|
|
519
|
+
if (char === "\"") {
|
|
520
|
+
inQuote = !inQuote;
|
|
521
|
+
continue;
|
|
522
|
+
}
|
|
523
|
+
if (!inQuote && rawPath.startsWith(" -> ", index)) {
|
|
524
|
+
paths.push(rawPath.slice(start, index));
|
|
525
|
+
start = index + 4;
|
|
526
|
+
index += 3;
|
|
527
|
+
}
|
|
528
|
+
}
|
|
529
|
+
paths.push(rawPath.slice(start));
|
|
530
|
+
return paths.map(normalizeGitStatusPath);
|
|
531
|
+
}
|
|
532
|
+
function isSentinelGitStatusEntry(entry) {
|
|
533
|
+
const sentinelRoot = relativeSentinelRoot();
|
|
534
|
+
return gitStatusPaths(entry).every((entryPath) => {
|
|
535
|
+
const normalizedPath = entryPath.replace(/\/$/, "");
|
|
536
|
+
return normalizedPath === sentinelRoot || normalizedPath.startsWith(`${sentinelRoot}/`);
|
|
537
|
+
});
|
|
538
|
+
}
|
|
539
|
+
function bundleSignal(status, options = {}) {
|
|
540
|
+
const gitStatusCommand = "git status --porcelain=v1 -uall";
|
|
498
541
|
if (!status.ok) {
|
|
499
542
|
return {
|
|
500
543
|
id: "bundle:git",
|
|
@@ -503,11 +546,12 @@ function bundleSignal(status) {
|
|
|
503
546
|
severity: "warn",
|
|
504
547
|
verdictImpact: "watch",
|
|
505
548
|
summary: `bundle git status unavailable: ${status.error}`,
|
|
506
|
-
source: { kind: "git", locator:
|
|
507
|
-
repair: repair("agent-runnable", "bundle-cleanup", "Inspect bundle git state before assuming the local state is clean.",
|
|
549
|
+
source: { kind: "git", locator: gitStatusCommand },
|
|
550
|
+
repair: repair("agent-runnable", "bundle-cleanup", "Inspect bundle git state before assuming the local state is clean.", gitStatusCommand),
|
|
508
551
|
};
|
|
509
552
|
}
|
|
510
|
-
const dirtyEntries = status.porcelain
|
|
553
|
+
const dirtyEntries = gitStatusEntries(status.porcelain)
|
|
554
|
+
.filter((entry) => !(options.ignoreSentinelDirtyEntries && isSentinelGitStatusEntry(entry)));
|
|
511
555
|
if (dirtyEntries.length > 0) {
|
|
512
556
|
return {
|
|
513
557
|
id: "bundle:git",
|
|
@@ -516,8 +560,8 @@ function bundleSignal(status) {
|
|
|
516
560
|
severity: "warn",
|
|
517
561
|
verdictImpact: "watch",
|
|
518
562
|
summary: `bundle has ${dirtyEntries.length} uncommitted git status entr${dirtyEntries.length === 1 ? "y" : "ies"}`,
|
|
519
|
-
source: { kind: "git", locator:
|
|
520
|
-
repair: repair("agent-runnable", "bundle-cleanup", "Resolve or intentionally preserve local bundle changes before handoff.",
|
|
563
|
+
source: { kind: "git", locator: gitStatusCommand },
|
|
564
|
+
repair: repair("agent-runnable", "bundle-cleanup", "Resolve or intentionally preserve local bundle changes before handoff.", gitStatusCommand),
|
|
521
565
|
meta: { dirtyEntries },
|
|
522
566
|
};
|
|
523
567
|
}
|
|
@@ -528,7 +572,7 @@ function bundleSignal(status) {
|
|
|
528
572
|
severity: "info",
|
|
529
573
|
verdictImpact: "none",
|
|
530
574
|
summary: "bundle git status clean",
|
|
531
|
-
source: { kind: "git", locator:
|
|
575
|
+
source: { kind: "git", locator: gitStatusCommand },
|
|
532
576
|
};
|
|
533
577
|
}
|
|
534
578
|
function sentinelVerdict(signals) {
|
|
@@ -633,7 +677,9 @@ function makeReceipt(agentName, agentRoot, options, generatedAt) {
|
|
|
633
677
|
gauntletSignal(report),
|
|
634
678
|
...deriveContextLossSentinelProviderSignals(providerVisibility),
|
|
635
679
|
...healthSignals(options.daemonHealthResults ?? []),
|
|
636
|
-
bundleSignal((options.gitStatus ?? (() => defaultGitStatus(agentRoot)))()
|
|
680
|
+
bundleSignal((options.gitStatus ?? (() => defaultGitStatus(agentRoot)))(), {
|
|
681
|
+
ignoreSentinelDirtyEntries: options.trigger === "daemon_health",
|
|
682
|
+
}),
|
|
637
683
|
];
|
|
638
684
|
const verdict = sentinelVerdict(signals);
|
|
639
685
|
const latestReady = readLatestReady(agentRoot);
|