@unbrained/pm-cli 2026.10.11 → 2026.10.12
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/.claude-plugin/marketplace.json +1 -1
- package/CHANGELOG.md +18 -0
- package/dist/cli/commander-usage.d.ts +5 -5
- package/dist/cli/commander-usage.js +3 -3
- package/dist/cli/error-guidance.d.ts +2 -0
- package/dist/cli/error-guidance.js +2 -2
- package/dist/cli/help-json-payload.js +1 -1
- package/dist/cli/main.d.ts +1 -0
- package/dist/cli/main.js +4 -4
- package/dist/cli/register-operations.d.ts +9 -4
- package/dist/cli/register-operations.js +1 -1
- package/dist/cli-bundle/bundle-manifest.json +1 -1
- package/dist/cli-bundle/chunks/{chunk-2JYTSTQP.js → chunk-3LQTJN4J.js} +2 -2
- package/dist/cli-bundle/chunks/{chunk-YMLF6QT7.js → chunk-6PZPUJW3.js} +28 -28
- package/dist/cli-bundle/chunks/{chunk-RUT3CCAG.js → chunk-B465HPS5.js} +2 -2
- package/dist/cli-bundle/chunks/{chunk-ESRACL6K.js → chunk-DT3KUUKY.js} +2 -2
- package/dist/cli-bundle/chunks/{chunk-SSY4OSJR.js → chunk-GUVKPOY6.js} +2 -2
- package/dist/cli-bundle/chunks/chunk-ILQEZ4Y6.js +2 -0
- package/dist/cli-bundle/chunks/{chunk-RBPOO46D.js → chunk-JRRXWH5B.js} +2 -2
- package/dist/cli-bundle/chunks/{chunk-MEWBSWE3.js → chunk-LH5QDBMN.js} +2 -2
- package/dist/cli-bundle/chunks/{chunk-KNW3RSLS.js → chunk-M5NDSW6E.js} +30 -30
- package/dist/cli-bundle/chunks/{chunk-K2UTE75T.js → chunk-MHPU2LDF.js} +2 -2
- package/dist/cli-bundle/chunks/{chunk-OBLO45UM.js → chunk-QM3M7GB5.js} +2 -2
- package/dist/cli-bundle/chunks/{chunk-S445XEYT.js → chunk-UABZJXIS.js} +2 -2
- package/dist/cli-bundle/chunks/{chunk-ZWRLAOA4.js → chunk-X2I2EEAS.js} +2 -2
- package/dist/cli-bundle/chunks/{register-list-query-T6BOR67M.js → register-list-query-OR33UEML.js} +2 -2
- package/dist/cli-bundle/chunks/{register-mutation-6NXR7SAJ.js → register-mutation-R34EJ3NH.js} +2 -2
- package/dist/cli-bundle/chunks/register-operations-UHSMEHSF.js +2 -0
- package/dist/cli-bundle/chunks/{register-setup-MAYFPKBX.js → register-setup-5E3PT22L.js} +2 -2
- package/dist/cli-bundle/focused-chunks/{chunk-SIEGIPNT.js → chunk-7QIZB2F2.js} +7 -7
- package/dist/cli-bundle/focused-chunks/{chunk-PLTTMQAW.js → chunk-AJG3DBXR.js} +2 -2
- package/dist/cli-bundle/focused-chunks/chunk-AXBO53EQ.js +2 -0
- package/dist/cli-bundle/focused-chunks/{chunk-W5AE7GKZ.js → chunk-CYEZLKLL.js} +2 -2
- package/dist/cli-bundle/focused-chunks/{chunk-PBCYGO7X.js → chunk-FXRWJEQ5.js} +2 -2
- package/dist/cli-bundle/focused-chunks/{chunk-GVGFUD52.js → chunk-IKJFAU32.js} +2 -2
- package/dist/cli-bundle/focused-chunks/{chunk-DMSADMEU.js → chunk-OGOLIS5R.js} +2 -2
- package/dist/cli-bundle/focused-chunks/chunk-PPH5N26M.js +2 -0
- package/dist/cli-bundle/focused-chunks/{chunk-WAOXID5J.js → chunk-RNAWRTQV.js} +2 -2
- package/dist/cli-bundle/focused-chunks/{chunk-B3YW6H2I.js → chunk-RZVNGUT7.js} +2 -2
- package/dist/cli-bundle/focused-chunks/{chunk-52UJEHEV.js → chunk-SV6YYZLR.js} +2 -2
- package/dist/cli-bundle/focused-chunks/{chunk-EIOQNLBQ.js → chunk-TS46OZD5.js} +2 -2
- package/dist/cli-bundle/focused-chunks/{chunk-KNJR2WUO.js → chunk-VBTLX5SN.js} +62 -62
- package/dist/cli-bundle/focused-chunks/{chunk-JGFSSR3G.js → chunk-YCOJLIDI.js} +3 -3
- package/dist/cli-bundle/focused-chunks/chunk-YDJT33JU.js +2 -0
- package/dist/cli-bundle/focused-chunks/{chunk-AU6VJGTY.js → chunk-Z4T3637Y.js} +2 -2
- package/dist/cli-bundle/main.js +22 -22
- package/dist/cli-bundle/sdk-authoring.js +1 -1
- package/dist/cli-bundle/sdk-contracts.js +1 -1
- package/dist/cli-bundle/sdk-core.js +31 -31
- package/dist/cli-bundle/sdk-governance.js +1 -1
- package/dist/cli-bundle/sdk-graph.js +1 -1
- package/dist/cli-bundle/sdk-merge.js +24 -24
- package/dist/cli-bundle/sdk-query.js +1 -1
- package/dist/cli-bundle/sdk-runtime.js +1 -1
- package/dist/cli-bundle/sdk-testing.js +1 -1
- package/dist/cli-bundle/sdk.js +8 -8
- package/dist/core/history/event-classification.d.ts +1 -1
- package/dist/core/history/event-classification.js +1 -1
- package/dist/core/item/item-format.d.ts +2 -0
- package/dist/core/item/item-format.js +2 -2
- package/dist/core/telemetry/runtime.d.ts +1 -1
- package/dist/core/telemetry/runtime.js +2 -2
- package/dist/core/test/background-runs.d.ts +1 -0
- package/dist/core/test/background-runs.js +2 -2
- package/dist/sdk/agent/command-suggestions.d.ts +2 -0
- package/dist/sdk/agent/command-suggestions.js +1 -1
- package/dist/sdk/cli-contracts/flag-contracts.js +1 -1
- package/dist/sdk/cli-contracts/flag-lexicon-contracts.js +1 -1
- package/dist/sdk/cli-contracts/grammar-contracts.js +1 -1
- package/dist/sdk/cli-contracts/registration-helpers.d.ts +1 -4
- package/dist/sdk/cli-contracts/registration-helpers.js +1 -1
- package/dist/sdk/cli-contracts/tool-parameter-tables.js +1 -1
- package/dist/sdk/cli-contracts/tool-schema.d.ts +1 -1
- package/dist/sdk/cli-contracts/tool-schema.js +1 -1
- package/dist/sdk/core.d.ts +1 -0
- package/dist/sdk/core.js +1 -1
- package/dist/sdk/generated/generated-error-code-catalog-part-1.js +1 -1
- package/dist/sdk/generated/generated-error-code-catalog-part-2.js +1 -1
- package/dist/sdk/governance/artifact-ledgers.js +1 -1
- package/dist/sdk/index.d.ts +2 -0
- package/dist/sdk/index.js +1 -1
- package/dist/sdk/public-surface.json +1 -1
- package/dist/sdk/test/batch.js +1 -1
- package/dist/sdk/test/command-args.d.ts +4 -0
- package/dist/sdk/test/command-args.js +2 -0
- package/dist/sdk/test/entry.d.ts +3 -0
- package/dist/sdk/test/entry.js +1 -1
- package/dist/sdk/test/execution.d.ts +15 -4
- package/dist/sdk/test/execution.js +2 -2
- package/dist/sdk/test/prerequisites.d.ts +6 -0
- package/dist/sdk/test/prerequisites.js +2 -0
- package/dist/sdk/test/trust.js +1 -1
- package/dist/sdk/test/update.d.ts +55 -0
- package/dist/sdk/test/update.js +2 -0
- package/dist/types.d.ts +4 -0
- package/dist/types.js +1 -1
- package/docs/LINKED_TEST_EDITS.md +79 -0
- package/docs/RELEASING.md +19 -8
- package/docs/SDK.md +178 -636
- package/docs/SDK_EVIDENCE_TRACEABILITY.md +4 -0
- package/docs/TESTING.md +13 -0
- package/docs/generated/FLAG_LEXICON_BUDGETS.md +2 -2
- package/docs/generated/REFUSAL_CLOSURE_CENSUS.md +8 -3
- package/docs/performance/runner-qualification.md +36 -0
- package/marketplace.json +1 -1
- package/package.json +1 -1
- package/packages/pm-beads/package.json +1 -1
- package/packages/pm-calendar/package.json +1 -1
- package/packages/pm-command-kit/package.json +1 -1
- package/packages/pm-digital-twin/package.json +1 -1
- package/packages/pm-governance-audit/package.json +1 -1
- package/packages/pm-guide-shell/package.json +1 -1
- package/packages/pm-kanban/package.json +1 -1
- package/packages/pm-lifecycle-hooks/package.json +1 -1
- package/packages/pm-linked-test-adapters/package.json +1 -1
- package/packages/pm-search-advanced/package.json +1 -1
- package/packages/pm-templates/package.json +1 -1
- package/packages/pm-todos/package.json +1 -1
- package/packages/pm-vcs/package.json +1 -1
- package/plugins/pm-claude/.claude-plugin/plugin.json +1 -1
- package/plugins/pm-claude/package.json +1 -1
- package/plugins/pm-codex/.codex-plugin/plugin.json +1 -1
- package/plugins/pm-codex/package.json +1 -1
- package/plugins/pm-codex/plugin.json +1 -1
- package/runtime-dependencies.json +1 -1
- package/dist/cli-bundle/chunks/chunk-BXSXT5Z6.js +0 -2
- package/dist/cli-bundle/chunks/register-operations-PDOBQZUJ.js +0 -2
- package/dist/cli-bundle/focused-chunks/chunk-2CJWTLQG.js +0 -2
- package/dist/cli-bundle/focused-chunks/chunk-MJQ253F3.js +0 -2
- package/dist/cli-bundle/focused-chunks/chunk-WOEZJUA2.js +0 -2
|
@@ -10,7 +10,7 @@ export declare const HISTORY_EVENT_CLASSIFICATION_VERSION: 1;
|
|
|
10
10
|
/** Semantic effect of an immutable history event on project recency. */
|
|
11
11
|
export type HistoryEventClass = "substantive" | "maintenance";
|
|
12
12
|
/** Operations whose meaning is always governance or storage maintenance. */
|
|
13
|
-
export declare const MAINTENANCE_HISTORY_OPERATIONS: readonly ["docs_add", "files_add", "history:author-acknowledge", "history_author_acknowledge", "history_compact", "history_compact_baseline", "history_redact", "history_repair", "history_salvage", "normalize", "release", "test_run_track", "tests_add", "tests_remove", "update_audit", "update_ownership_bypass"];
|
|
13
|
+
export declare const MAINTENANCE_HISTORY_OPERATIONS: readonly ["docs_add", "files_add", "history:author-acknowledge", "history_author_acknowledge", "history_compact", "history_compact_baseline", "history_redact", "history_repair", "history_salvage", "normalize", "release", "test_run_track", "tests_add", "tests_remove", "tests_update", "update_audit", "update_ownership_bypass"];
|
|
14
14
|
/** Operations whose meaning always advances lifecycle or working context. */
|
|
15
15
|
export declare const SUBSTANTIVE_HISTORY_OPERATIONS: readonly ["append", "cancel", "claim", "close", "comment_add", "create", "learning_add", "note_add", "reopen"];
|
|
16
16
|
/**
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
(function(){try{var e=typeof window<"u"?window:typeof global<"u"?global:typeof globalThis<"u"?globalThis:typeof self<"u"?self:{},n=new e.Error().stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="
|
|
1
|
+
(function(){try{var e=typeof window<"u"?window:typeof global<"u"?global:typeof globalThis<"u"?globalThis:typeof self<"u"?self:{},n=new e.Error().stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="3c2c29c0-0034-54d4-9dbd-2e9af047784e")}catch{}})();const HISTORY_EVENT_CLASSIFICATION_VERSION=1,MAINTENANCE_HISTORY_OPERATIONS=["docs_add","files_add","history:author-acknowledge","history_author_acknowledge","history_compact","history_compact_baseline","history_redact","history_repair","history_salvage","normalize","release","test_run_track","tests_add","tests_remove","tests_update","update_audit","update_ownership_bypass"],SUBSTANTIVE_HISTORY_OPERATIONS=["append","cancel","claim","close","comment_add","create","learning_add","note_add","reopen"],MAINTENANCE_OPERATIONS=new Set(MAINTENANCE_HISTORY_OPERATIONS),SUBSTANTIVE_OPERATIONS=new Set(SUBSTANTIVE_HISTORY_OPERATIONS),MAINTENANCE_UPDATE_FIELDS=new Set(["actual_result","affected_version","assignee","blocked_by","blocked_reason","close_reason","confidence","customer_impact","definition_of_ready","dependencies","docs","environment","expected_result","files","fixed_version","goal","impact","learnings","notes","objective","order","outcome","parent","release","reminders","reporter","repro_steps","resolution","reviewer","severity","sprint","tests","test_runs","unblock_note","updated_at","value","why_now"]);function topLevelPatchField(operation){const segments=operation.path.split("/").filter(Boolean);return segments[0]==="metadata"||segments[0]==="front_matter"?segments[1]:segments[0]==="body"?"body":void 0}function classifyHistoryEvent(entry){return entry.event_class==="substantive"||entry.event_class==="maintenance"?entry.event_class:MAINTENANCE_OPERATIONS.has(entry.op)?"maintenance":SUBSTANTIVE_OPERATIONS.has(entry.op)||entry.op!=="update"?"substantive":Array.isArray(entry.patch)&&entry.patch.length>0&&entry.patch.every(operation=>{const field=topLevelPatchField(operation);return field!==void 0&&MAINTENANCE_UPDATE_FIELDS.has(field)})?"maintenance":"substantive"}export{HISTORY_EVENT_CLASSIFICATION_VERSION,MAINTENANCE_HISTORY_OPERATIONS,SUBSTANTIVE_HISTORY_OPERATIONS,classifyHistoryEvent};
|
|
2
2
|
//# sourceMappingURL=event-classification.js.map
|
|
@@ -26,6 +26,8 @@ declare function runtimeFieldRequiredForType(definition: RuntimeFieldRegistry["d
|
|
|
26
26
|
declare function buildKnownItemMetadataKeys(runtimeContext: RuntimeSchemaValidationContext): Set<string>;
|
|
27
27
|
declare function normalizeTestRunSummaries(values: ItemTestRunSummary[] | undefined): ItemTestRunSummary[] | undefined;
|
|
28
28
|
declare function sortTests(values: LinkedTest[] | undefined): LinkedTest[] | undefined;
|
|
29
|
+
/** Normalize one linked-test entry consistently for storage and metadata comparisons. */
|
|
30
|
+
export declare function normalizeLinkedTest(value: LinkedTest): LinkedTest;
|
|
29
31
|
declare function normalizePlanStepLinks(value: unknown): PlanStepLink[] | undefined;
|
|
30
32
|
declare function normalizePlanStepFiles(value: unknown): PlanStepFile[] | undefined;
|
|
31
33
|
declare function normalizePlanStepTests(value: unknown): {
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
(function(){try{var e=typeof window<"u"?window:typeof global<"u"?global:typeof globalThis<"u"?globalThis:typeof self<"u"?self:{},n=new e.Error().stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="e2f15e0b-06b0-5e02-a91e-2886f94ffc63")}catch{}})();import{encode as encodeToon}from"@toon-format/toon";import{isDeepStrictEqual}from"node:util";import{CONFIDENCE_TEXT_VALUES,ISSUE_SEVERITY_VALUES,PLAN_STEP_STATUS_VALUES,RECURRENCE_FREQUENCY_VALUES,RECURRENCE_WEEKDAY_VALUES,STATUS_VALUES,weekdayOrderIndex}from"../../types/index.js";import{coerceRuntimeFieldValue}from"../schema/runtime-field-values.js";import{resolveRuntimeFieldRegistry,resolveRuntimeStatusRegistry}from"../schema/runtime-schema.js";import{BASELINE_ITEM_FORMAT_VERSION,CURRENT_ITEM_FORMAT_VERSION,effectiveItemFormatVersion,normalizeItemFormatVersion}from"./item-format-version.js";import{normalizeStatusInput}from"./status.js";import{decodeToonItemContent}from"./toon-decode.js";import{EXIT_CODE,ITEM_METADATA_KEY_ORDER}from"../shared/constants.js";import{findFirstMergeConflictMarker}from"../shared/conflict-markers.js";import{PmCliError}from"../shared/errors.js";import{orderObject}from"../shared/serialization.js";import{compareTimestampStrings,isTimestampLiteral}from"../shared/time.js";import{coerceFiniteNumber}from"../shared/primitives.js";const LINKED_TEST_PM_CONTEXT_MODE_VALUES=new Set(["schema","tracker","auto","none"]),LINKED_TEST_WORKSPACE_CONTEXT_MODE_VALUES=new Set(["source","isolated","snapshot"]),LINKED_TEST_TRUST_REASON_VALUES=new Set(["legacy","local_mutation","local_source_ref","acknowledged","invalid_provenance","foreign_source_ref"]),ITEM_TEST_RUN_STATUS_VALUES=new Set(["passed","failed","stopped","canceled"]),PLAN_STEP_STATUS_SET=new Set(PLAN_STEP_STATUS_VALUES);function normalizePathValue(value){return value.replaceAll("\\","/")}function firstNonZeroComparison(comparisons){for(const comparison of comparisons)if(comparison!==0)return comparison;return 0}function deleteUndefinedFields(record){for(const[key,value]of Object.entries(record))value===void 0&&delete record[key]}const REQUIRED_STRING_FIELDS=["id","title","description","created_at","updated_at"],STATIC_ITEM_METADATA_FIELD_SET=new Set(ITEM_METADATA_KEY_ORDER);function resolveRuntimeSchemaValidationContext(options){if(options?.schema)return{statusRegistry:resolveRuntimeStatusRegistry(options.schema),fieldRegistry:resolveRuntimeFieldRegistry(options.schema),unknownFieldPolicy:options.schema.unknown_field_policy??"allow",extensionFieldNames:new Set(options.extensionFieldNames??[]),onWarning:options.onWarning}}function runtimeFieldRequiredForType(definition,typeName){return definition.required?definition.required_types.length===0?!0:definition.required_types.map(value=>value.toLowerCase()).includes(typeName.trim().toLowerCase()):!1}function validationError(message){const field=message.match(/^([a-z_][\w.]*)\s/u)?.[1];throw new PmCliError(`Invalid item metadata: ${message}`,EXIT_CODE.GENERIC_FAILURE,{code:"item_document_invalid",reason:message.includes("required")?"missing_required_field":"schema_validation_error",...field?{field}:{},required:"Provide an item document whose metadata satisfies the active runtime schema.",why:message})}function documentSyntaxError(format,message){throw new PmCliError(message,EXIT_CODE.GENERIC_FAILURE,{code:"item_document_invalid",reason:"syntax_error",format,required:`Provide a syntactically valid ${format==="toon"?"TOON":"JSON Markdown"} item document.`,why:"The document could not be decoded far enough to validate item metadata."})}function assertSupportedItemFormat(metadata,required){if(!required||typeof metadata!="object"||metadata===null)return;const formatVersion=effectiveItemFormatVersion(metadata);if(!(formatVersion<=CURRENT_ITEM_FORMAT_VERSION))throw new PmCliError(`Stored item uses format version ${formatVersion}; this runtime supports up to ${CURRENT_ITEM_FORMAT_VERSION}. Upgrade pm before reading or changing this item.`,EXIT_CODE.CONFLICT,{code:"item_format_version_unsupported",format_version:formatVersion,required:"Upgrade to a pm runtime supporting the stored item format.",nextSteps:["Preserve the original item and history; do not lower pm_format_version to bypass compatibility checks."]})}function buildKnownItemMetadataKeys(runtimeContext){const knownKeys=new Set(STATIC_ITEM_METADATA_FIELD_SET);for(const definition of runtimeContext.fieldRegistry?.definitions??[])knownKeys.add(definition.metadata_key);for(const fieldName of runtimeContext.extensionFieldNames)knownKeys.add(fieldName);return knownKeys}function assertItemMetadataCondition(condition,message){condition||validationError(message)}function requireItemMetadataRecord(value,message){return(typeof value!="object"||value===null||Array.isArray(value))&&validationError(message),value}function assertTimestampField(record,fieldName){const rawValue=record[fieldName];assertItemMetadataCondition(typeof rawValue=="string",`${fieldName} must be a string`),assertItemMetadataCondition(isTimestampLiteral(rawValue),`${fieldName} must be a valid ISO timestamp`)}function assertPositiveIntegerField(record,fieldName,messagePrefix){const value=record[fieldName];value!==void 0&&assertItemMetadataCondition(typeof value=="number"&&Number.isInteger(value)&&value>=1,`${messagePrefix} must be an integer >= 1`)}function assertOptionalTimestampString(record,fieldName,messagePrefix){const value=record[fieldName];value!==void 0&&(assertItemMetadataCondition(typeof value=="string",`${messagePrefix} must be a string`),assertItemMetadataCondition(isTimestampLiteral(value),`${messagePrefix} must be a valid ISO timestamp`))}function assertRecurrenceWeekdays(value){if(value!==void 0){assertItemMetadataCondition(Array.isArray(value),"event.recurrence.by_weekday must be an array");for(const weekday of value){assertItemMetadataCondition(typeof weekday=="string","event.recurrence.by_weekday entries must be strings");const normalizedWeekday=weekday.trim().toLowerCase();assertItemMetadataCondition(RECURRENCE_WEEKDAY_VALUES.includes(normalizedWeekday),`event.recurrence.by_weekday entries must be one of: ${RECURRENCE_WEEKDAY_VALUES.join(", ")}`)}}}function assertRecurrenceMonthDays(value){if(value!==void 0){assertItemMetadataCondition(Array.isArray(value),"event.recurrence.by_month_day must be an array");for(const day of value)assertItemMetadataCondition(typeof day=="number"&&Number.isInteger(day)&&day>=1&&day<=31,"event.recurrence.by_month_day entries must be integers 1..31")}}function assertRecurrenceExdates(value){if(value!==void 0){assertItemMetadataCondition(Array.isArray(value),"event.recurrence.exdates must be an array");for(const exdate of value)assertItemMetadataCondition(typeof exdate=="string","event.recurrence.exdates entries must be strings"),assertItemMetadataCondition(isTimestampLiteral(exdate),"event.recurrence.exdates entries must be valid ISO timestamps")}}function assertValidRecurrenceRule(recurrence){const recurrenceRecord=requireItemMetadataRecord(recurrence,"event.recurrence must be an object");assertItemMetadataCondition(typeof recurrenceRecord.freq=="string","event.recurrence.freq must be a string");const frequency=recurrenceRecord.freq.trim().toLowerCase();assertItemMetadataCondition(RECURRENCE_FREQUENCY_VALUES.includes(frequency),`event.recurrence.freq must be one of: ${RECURRENCE_FREQUENCY_VALUES.join(", ")}`),assertPositiveIntegerField(recurrenceRecord,"interval","event.recurrence.interval"),assertPositiveIntegerField(recurrenceRecord,"count","event.recurrence.count"),assertOptionalTimestampString(recurrenceRecord,"until","event.recurrence.until"),assertRecurrenceWeekdays(recurrenceRecord.by_weekday),assertRecurrenceMonthDays(recurrenceRecord.by_month_day),assertRecurrenceExdates(recurrenceRecord.exdates)}function assertRequiredItemMetadataFields(record){for(const fieldName of REQUIRED_STRING_FIELDS)assertItemMetadataCondition(typeof record[fieldName]=="string",`${fieldName} is required and must be a string`);const itemType=record.type;assertItemMetadataCondition(typeof itemType=="string"&&itemType.trim().length>0,"type must be a non-empty string");const formatVersion=record.pm_format_version;return formatVersion!==void 0&&assertItemMetadataCondition(typeof formatVersion=="number"&&Number.isInteger(formatVersion)&&formatVersion>=BASELINE_ITEM_FORMAT_VERSION,`pm_format_version must be an integer >= ${BASELINE_ITEM_FORMAT_VERSION}`),itemType}function assertStatusItemMetadataField(record,runtimeContext){const status=record.status;assertItemMetadataCondition(typeof status=="string"&&status.trim().length>0,"status must be a non-empty string");const statusRegistry=runtimeContext?.statusRegistry,normalizedStatus=normalizeStatusInput(status,statusRegistry),statusDomain=statusRegistry?statusRegistry.definitions.map(definition=>definition.id):[...STATUS_VALUES];assertItemMetadataCondition(normalizedStatus!==void 0,`status must be one of: ${statusDomain.join(", ")}`)}function assertPriorityAndTags(record){const priority=record.priority;assertItemMetadataCondition(typeof priority=="number"&&Number.isInteger(priority)&&[0,1,2,3,4].includes(priority),"priority must be an integer 0..4");const tags=record.tags;assertItemMetadataCondition(Array.isArray(tags),"tags must be an array");for(const tag of tags)assertItemMetadataCondition(typeof tag=="string","tags entries must be strings")}function assertConfidenceItemMetadataField(record){const confidence=record.confidence;if(confidence!==void 0){if(typeof confidence=="number"){assertItemMetadataCondition(Number.isInteger(confidence)&&confidence>=0&&confidence<=100,"confidence number value must be an integer 0..100");return}if(typeof confidence=="string"){const normalizedConfidence=confidence.trim().toLowerCase(),isKnownTextConfidence=normalizedConfidence==="med"||CONFIDENCE_TEXT_VALUES.includes(normalizedConfidence);assertItemMetadataCondition(isKnownTextConfidence,`confidence string value must be one of: ${[...CONFIDENCE_TEXT_VALUES,"med"].join(", ")}`);return}assertItemMetadataCondition(!1,"confidence must be a number or string")}}function assertSeverityItemMetadataField(record){const severity=record.severity;if(severity===void 0)return;typeof severity!="string"&&validationError("severity must be a string");const normalizedSeverity=severity.trim().toLowerCase(),isKnownSeverity=normalizedSeverity==="med"||ISSUE_SEVERITY_VALUES.includes(normalizedSeverity);assertItemMetadataCondition(isKnownSeverity,`severity value must be one of: ${[...ISSUE_SEVERITY_VALUES,"med"].join(", ")}`)}function assertRegressionItemMetadataField(record){record.regression!==void 0&&typeof record.regression!="boolean"&&validationError("regression must be a boolean")}function assertReminderEntries(record){const reminders=record.reminders;if(reminders!==void 0){assertItemMetadataCondition(Array.isArray(reminders),"reminders must be an array");for(const reminder of reminders){const reminderRecord=requireItemMetadataRecord(reminder,"reminders entries must be objects");assertItemMetadataCondition(typeof reminderRecord.at=="string","reminder.at must be a string"),assertItemMetadataCondition(isTimestampLiteral(reminderRecord.at),"reminder.at must be a valid ISO timestamp"),assertItemMetadataCondition(typeof reminderRecord.text=="string","reminder.text must be a string"),assertItemMetadataCondition(reminderRecord.text.trim().length>0,"reminder.text must not be empty")}}}function assertEventStringFields(eventRecord){for(const stringField of["title","description","location","timezone"])eventRecord[stringField]!==void 0&&(assertItemMetadataCondition(typeof eventRecord[stringField]=="string",`event.${stringField} must be a string`),assertItemMetadataCondition(eventRecord[stringField].trim().length>0,`event.${stringField} must not be empty`))}function assertEventRecurrence(eventRecord){eventRecord.recurrence!==void 0&&(assertValidRecurrenceRule(eventRecord.recurrence),eventRecord.recurrence.until!==void 0&&assertItemMetadataCondition(compareTimestampStrings(eventRecord.recurrence.until,eventRecord.start_at)>=0,"event.recurrence.until must be at or after event.start_at"))}function assertCalendarEventEntry(event){const eventRecord=requireItemMetadataRecord(event,"events entries must be objects");assertItemMetadataCondition(typeof eventRecord.start_at=="string","event.start_at must be a string"),assertItemMetadataCondition(isTimestampLiteral(eventRecord.start_at),"event.start_at must be a valid ISO timestamp"),eventRecord.end_at!==void 0&&(assertItemMetadataCondition(typeof eventRecord.end_at=="string","event.end_at must be a string"),assertItemMetadataCondition(isTimestampLiteral(eventRecord.end_at),"event.end_at must be a valid ISO timestamp"),assertItemMetadataCondition(compareTimestampStrings(eventRecord.end_at,eventRecord.start_at)>0,"event.end_at must be after event.start_at")),assertEventStringFields(eventRecord),eventRecord.all_day!==void 0&&assertItemMetadataCondition(typeof eventRecord.all_day=="boolean","event.all_day must be a boolean"),assertEventRecurrence(eventRecord)}function assertCalendarEvents(record){const events=record.events;if(events!==void 0){assertItemMetadataCondition(Array.isArray(events),"events must be an array");for(const event of events)assertCalendarEventEntry(event)}}function assertOptionalStringFields(record){for(const fieldName of["source_type","source_owner","design","external_ref"]){const value=record[fieldName];value!==void 0&&assertItemMetadataCondition(typeof value=="string",`${fieldName} must be a string`)}}function assertTypeOptionsField(record){const typeOptions=record.type_options;if(typeOptions===void 0)return;const typeOptionsRecord=requireItemMetadataRecord(typeOptions,"type_options must be an object");for(const[optionKey,optionValue]of Object.entries(typeOptionsRecord))assertItemMetadataCondition(optionKey.trim().length>0,"type_options keys must be non-empty"),assertItemMetadataCondition(typeof optionValue=="string","type_options values must be strings"),assertItemMetadataCondition(optionValue.trim().length>0,"type_options values must be non-empty strings")}function coerceRuntimeItemMetadataFields(record,itemType,runtimeContext){if(runtimeContext?.fieldRegistry)for(const definition of runtimeContext.fieldRegistry.definitions){const fieldValue=record[definition.metadata_key];if(fieldValue===void 0){runtimeFieldRequiredForType(definition,itemType)&&validationError(`missing required schema field: ${definition.metadata_key}`);continue}try{record[definition.metadata_key]=coerceRuntimeFieldValue(definition,fieldValue,`metadata field "${definition.metadata_key}"`,"metadata")}catch(error){validationError(String(error?.message).replace(/^Invalid\s+/u,""))}}}function assertRuntimeUnknownItemMetadataFields(record,runtimeContext){if(!runtimeContext||runtimeContext.unknownFieldPolicy==="allow")return;const knownKeys=buildKnownItemMetadataKeys(runtimeContext),unknownKeys=Object.keys(record).filter(key=>!knownKeys.has(key)).sort((left,right)=>left.localeCompare(right));unknownKeys.length!==0&&(runtimeContext.unknownFieldPolicy==="reject"?validationError(`unknown schema fields are not allowed: ${unknownKeys.join(", ")}`):runtimeContext.onWarning?.(`item_unknown_schema_fields:${unknownKeys.join(",")}`))}function assertValidItemMetadata(itemMetadata,runtimeContext){const record=requireItemMetadataRecord(itemMetadata,"item metadata must be an object"),itemType=assertRequiredItemMetadataFields(record);assertStatusItemMetadataField(record,runtimeContext),assertPriorityAndTags(record),assertConfidenceItemMetadataField(record),assertSeverityItemMetadataField(record),assertRegressionItemMetadataField(record),assertTimestampField(record,"created_at"),assertTimestampField(record,"updated_at"),record.deadline!==void 0&&assertTimestampField(record,"deadline"),assertReminderEntries(record),assertCalendarEvents(record),assertOptionalTimestampString(record,"closed_at","closed_at"),assertOptionalTimestampString(record,"completed_at","completed_at"),assertOptionalStringFields(record),assertTypeOptionsField(record),coerceRuntimeItemMetadataFields(record,itemType,runtimeContext),assertRuntimeUnknownItemMetadataFields(record,runtimeContext)}function sortDependencies(values){if(!(!values||values.length===0))return[...values].map(value=>({id:value.id.trim(),kind:value.kind,created_at:value.created_at,author:value.author?.trim()||void 0,source_kind:value.source_kind?.trim()||void 0,author_source:value.author_source})).sort((a,b)=>{const byCreated=compareTimestampStrings(a.created_at,b.created_at);if(byCreated!==0)return byCreated;const byId=a.id.localeCompare(b.id);if(byId!==0)return byId;const byKind=a.kind.localeCompare(b.kind);return byKind!==0?byKind:(a.source_kind??"").localeCompare(b.source_kind??"")})}function sortLogValues(values){if(!(!values||values.length===0))return[...values].sort((a,b)=>{const byCreated=compareTimestampStrings(a.created_at,b.created_at);if(byCreated!==0)return byCreated;const byText=a.text.localeCompare(b.text);return byText!==0?byText:a.author.localeCompare(b.author)})}function sortReminders(values){if(!values||values.length===0)return;const normalized=[...values].map(value=>({at:value.at,text:value.text.trim()})).filter(value=>value.text.length>0).sort((a,b)=>{const byAt=compareTimestampStrings(a.at,b.at);return byAt!==0?byAt:a.text.localeCompare(b.text)});return normalized.length===0?void 0:normalized}function normalizeRecurrenceRule(value){if(!value)return;const normalizedFrequency=value.freq.trim().toLowerCase();if(!RECURRENCE_FREQUENCY_VALUES.includes(normalizedFrequency))return;const byWeekday=Array.from(new Set((value.by_weekday??[]).map(weekday=>weekday.trim().toLowerCase()).filter(weekday=>RECURRENCE_WEEKDAY_VALUES.includes(weekday)))).sort((a,b)=>weekdayOrderIndex(a)-weekdayOrderIndex(b)),byMonthDay=Array.from(new Set((value.by_month_day??[]).filter(day=>Number.isInteger(day)&&day>=1&&day<=31).map(day=>day))).sort((a,b)=>a-b),exdates=Array.from(new Set((value.exdates??[]).map(timestamp=>timestamp.trim()).filter(timestamp=>isTimestampLiteral(timestamp)))).sort((a,b)=>compareTimestampStrings(a,b)),normalized={freq:normalizedFrequency,interval:value.interval!==void 0&&value.interval>1?value.interval:void 0,count:value.count,until:value.until?.trim()||void 0,by_weekday:byWeekday.length>0?byWeekday:void 0,by_month_day:byMonthDay.length>0?byMonthDay:void 0,exdates:exdates.length>0?exdates:void 0};for(const[key,fieldValue]of Object.entries(normalized))fieldValue===void 0&&delete normalized[key];return normalized}function sortEvents(values){return!Array.isArray(values)||values.length===0?void 0:[...values].map(normalizeCalendarEvent).sort(compareCalendarEvents)}function normalizeCalendarEvent(value){const event={start_at:value.start_at,end_at:value.end_at||void 0,title:value.title?.trim()||void 0,description:value.description?.trim()||void 0,location:value.location?.trim()||void 0,all_day:value.all_day,timezone:value.timezone?.trim()||void 0,recurrence:normalizeRecurrenceRule(value.recurrence)};return deleteUndefinedFields(event),event}function compareCalendarEvents(a,b){return firstNonZeroComparison([compareTimestampStrings(a.start_at,b.start_at),(a.end_at??"").localeCompare(b.end_at??""),(a.title??"").localeCompare(b.title??""),+!!a.all_day-+!!b.all_day,(a.timezone??"").localeCompare(b.timezone??""),(a.location??"").localeCompare(b.location??""),(a.description??"").localeCompare(b.description??""),JSON.stringify(a.recurrence??{}).localeCompare(JSON.stringify(b.recurrence??{}))])}function normalizeFiles(values){if(!(!values||values.length===0))return values.map(value=>({path:normalizePathValue(value.path),scope:value.scope,note:value.note?.trim()||void 0}))}function normalizeTestRunSummaries(values){if(!values||values.length===0)return;const normalized=values.map(normalizeTestRunSummary).filter(value=>value.run_id.length>0&&value.started_at.length>0&&value.finished_at.length>0&&value.recorded_at.length>0).sort((a,b)=>firstNonZeroComparison([compareTimestampStrings(a.recorded_at,b.recorded_at),a.run_id.localeCompare(b.run_id),a.kind.localeCompare(b.kind)]));return normalized.length>0?normalized:void 0}function normalizeNonNegativeInteger(value){return typeof value=="number"&&Number.isFinite(value)&&value>=0?Math.floor(value):void 0}function normalizePositiveInteger(value){return typeof value=="number"&&Number.isFinite(value)&&value>=1?Math.floor(value):void 0}function stringOrEmpty(value){return typeof value=="string"?value:""}function normalizeTestRunMeasurements(value){if(!Array.isArray(value))return;const normalized=value.filter(measurement=>measurement!==null&&typeof measurement=="object"&&typeof measurement.name=="string"&&measurement.name.trim().length>0&&typeof measurement.value=="number"&&Number.isFinite(measurement.value)&&typeof measurement.recorded_at=="string"&&measurement.recorded_at.trim().length>0).map(measurement=>({name:measurement.name.trim(),value:measurement.value,unit:typeof measurement.unit=="string"&&measurement.unit.trim()||void 0,threshold:typeof measurement.threshold=="number"&&Number.isFinite(measurement.threshold)?measurement.threshold:void 0,recorded_at:measurement.recorded_at.trim()})),deduplicated=[...new Map(normalized.map(measurement=>[measurement.name,measurement])).values()].sort((left,right)=>left.name.localeCompare(right.name)).slice(0,32);return deduplicated.length>0?deduplicated:void 0}function normalizeTestRunEnum(value,allowedValues){return typeof value=="string"&&allowedValues.has(value)?value:void 0}function normalizeTestRunExecutions(values){if(!Array.isArray(values))return;const executions=values.filter(execution=>execution&&typeof execution.command=="string"&&execution.command.trim().length>0).slice(0,32).map(execution=>({command:execution.command.trim(),requested_pm_context_mode:normalizeTestRunEnum(execution.requested_pm_context_mode,LINKED_TEST_PM_CONTEXT_MODE_VALUES),pm_context_mode:normalizeTestRunEnum(execution.pm_context_mode,LINKED_TEST_PM_CONTEXT_MODE_VALUES),workspace_context_mode:normalizeTestRunEnum(execution.workspace_context_mode,LINKED_TEST_WORKSPACE_CONTEXT_MODE_VALUES),trust_reason:normalizeTestRunEnum(execution.trust_reason,LINKED_TEST_TRUST_REASON_VALUES)}));return executions.length>0?executions:void 0}function normalizeTestRunSummary(value){const runId=typeof value.run_id=="string"?value.run_id.trim():"",kind=value.kind==="test"||value.kind==="test-all"?value.kind:"test",status=ITEM_TEST_RUN_STATUS_VALUES.has(value.status)?value.status:"failed",summary={run_id:runId,kind,status,started_at:stringOrEmpty(value.started_at),finished_at:stringOrEmpty(value.finished_at),recorded_at:stringOrEmpty(value.recorded_at),attempt:normalizePositiveInteger(value.attempt),resumed_from:value.resumed_from?.trim()||void 0,passed:normalizeNonNegativeInteger(value.passed)??0,failed:normalizeNonNegativeInteger(value.failed)??0,skipped:normalizeNonNegativeInteger(value.skipped)??0,items:normalizeNonNegativeInteger(value.items),linked_tests:normalizeNonNegativeInteger(value.linked_tests),fail_on_skipped_triggered:value.fail_on_skipped_triggered===!0?!0:void 0,measurements:normalizeTestRunMeasurements(value.measurements),executions:normalizeTestRunExecutions(value.executions)};return deleteUndefinedFields(summary),summary}function sortTests(values){if(!(!Array.isArray(values)||values.length===0))return values.map(normalizeLinkedTest)}function normalizeTrimmedStringList(value){if(!Array.isArray(value))return;const normalized=[...new Set(value.filter(entry=>typeof entry=="string").map(entry=>entry.trim()).filter(entry=>entry.length>0))].sort((a,b)=>a.localeCompare(b));return normalized.length>0?normalized:void 0}function isPlainObjectRecord(value){if(!value||typeof value!="object"||Array.isArray(value))return!1;const prototype=Object.getPrototypeOf(value);return prototype===Object.prototype||prototype===null}function isUnsafeObjectKey(key){return key==="__proto__"||key==="constructor"||key==="prototype"}function normalizeStringRecord(value){if(!isPlainObjectRecord(value))return;const entries=Object.entries(value).map(([key,recordValue])=>[key.trim(),String(recordValue).trim()]).filter(([key,recordValue])=>key.length>0&&!isUnsafeObjectKey(key)&&recordValue.length>0).sort(([left],[right])=>left.localeCompare(right));return entries.length>0?Object.fromEntries(entries):void 0}function normalizeNumericRecord(value){if(!isPlainObjectRecord(value))return;const entries=Object.entries(value).map(([key,recordValue])=>[key.trim(),Number(recordValue)]).filter(([key,recordValue])=>key.length>0&&!isUnsafeObjectKey(key)&&Number.isFinite(recordValue)).sort(([left],[right])=>left.localeCompare(right));return entries.length>0?Object.fromEntries(entries):void 0}function normalizeLinkedTestContextMode(value){const normalized=value?.trim().toLowerCase();if(!(!normalized||!LINKED_TEST_PM_CONTEXT_MODE_VALUES.has(normalized)))return normalized}function normalizeLinkedTestWorkspaceContextMode(value){const normalized=value?.trim().toLowerCase();if(!(!normalized||!LINKED_TEST_WORKSPACE_CONTEXT_MODE_VALUES.has(normalized)))return normalized}function normalizeLinkedTestProvenance(value,invalid){if(invalid===!0)return{provenance_invalid:!0};if(value===void 0)return{};if(!isPlainObjectRecord(value))return{provenance_invalid:!0};const author=trimStringOrUndefined(value.author),createdAt=trimStringOrUndefined(value.created_at),sourceKind=value.source_kind;return!author||!createdAt||!isTimestampLiteral(createdAt)||sourceKind!=="local_mutation"&&sourceKind!=="merge_union"?{provenance_invalid:!0}:{provenance:{author,created_at:createdAt,source_kind:sourceKind,source_ref:trimStringOrUndefined(value.source_ref)}}}function normalizeLinkedTest(value){const test={command:value.command?.trim()||void 0,path:value.path?normalizePathValue(value.path):void 0,scope:value.scope,timeout_seconds:typeof value.timeout_seconds=="number"&&Number.isFinite(value.timeout_seconds)&&value.timeout_seconds>0?value.timeout_seconds:void 0,pm_context_mode:normalizeLinkedTestContextMode(value.pm_context_mode),workspace_context_mode:normalizeLinkedTestWorkspaceContextMode(value.workspace_context_mode),...normalizeLinkedTestProvenance(value.provenance,value.provenance_invalid),env_set:normalizeStringRecord(value.env_set),env_clear:normalizeTrimmedStringList(value.env_clear),shared_host_safe:value.shared_host_safe===!0?!0:void 0,assert_stdout_contains:normalizeTrimmedStringList(value.assert_stdout_contains),assert_stdout_regex:normalizeTrimmedStringList(value.assert_stdout_regex),assert_stderr_contains:normalizeTrimmedStringList(value.assert_stderr_contains),assert_stderr_regex:normalizeTrimmedStringList(value.assert_stderr_regex),assert_stdout_min_lines:normalizeNonNegativeInteger(value.assert_stdout_min_lines),assert_json_field_equals:normalizeStringRecord(value.assert_json_field_equals),assert_json_field_gte:normalizeNumericRecord(value.assert_json_field_gte),note:value.note?.trim()||void 0};return deleteUndefinedFields(test),test}function sortDocs(values){if(!(!values||values.length===0))return[...values].map(value=>({path:normalizePathValue(value.path),scope:value.scope,note:value.note?.trim()||void 0})).sort((a,b)=>{const byScope=a.scope.localeCompare(b.scope);if(byScope!==0)return byScope;const byPath=a.path.localeCompare(b.path);return byPath!==0?byPath:(a.note??"").localeCompare(b.note??"")})}function trimStringOrUndefined(value){if(typeof value!="string")return;const trimmed=value.trim();return trimmed.length>0?trimmed:void 0}function normalizePlanStepLinks(value){if(!Array.isArray(value))return;const out=[];for(const entry of value){if(entry===null||typeof entry!="object")continue;const record=entry,id=trimStringOrUndefined(record.id),kind=trimStringOrUndefined(record.kind);if(!id||!kind)continue;const link={id,kind},note=trimStringOrUndefined(record.note);note&&(link.note=note),record.required_before_step===!0&&(link.required_before_step=!0),out.push(link)}return out.length>0?out:void 0}function normalizePlanStepFiles(value){if(!Array.isArray(value))return;const out=[];for(const entry of value){if(entry===null||typeof entry!="object")continue;const record=entry,path=trimStringOrUndefined(record.path);if(!path)continue;const file={path:normalizePathValue(path)};(record.scope==="project"||record.scope==="global")&&(file.scope=record.scope);const note=trimStringOrUndefined(record.note);note&&(file.note=note),out.push(file)}return out.length>0?out:void 0}function normalizePlanStepTests(value){if(!Array.isArray(value))return;const out=[];for(const entry of value){if(entry===null||typeof entry!="object")continue;const record=entry,command=trimStringOrUndefined(record.command),path=trimStringOrUndefined(record.path);if(!command&&!path)continue;const test={};command&&(test.command=command),path&&(test.path=normalizePathValue(path));const note=trimStringOrUndefined(record.note);note&&(test.note=note),out.push(test)}return out.length>0?out:void 0}function normalizePlanStepDocs(value){if(!Array.isArray(value))return;const out=[];for(const entry of value){if(entry===null||typeof entry!="object")continue;const record=entry,path=trimStringOrUndefined(record.path);if(!path)continue;const doc={path:normalizePathValue(path)};(record.scope==="project"||record.scope==="global")&&(doc.scope=record.scope);const note=trimStringOrUndefined(record.note);note&&(doc.note=note),out.push(doc)}return out.length>0?out:void 0}function normalizePlanStepEntry(entry){if(entry===null||typeof entry!="object")return;const record=entry,id=trimStringOrUndefined(record.id),title=trimStringOrUndefined(record.title),statusRaw=trimStringOrUndefined(record.status),status=statusRaw&&PLAN_STEP_STATUS_SET.has(statusRaw)?statusRaw:void 0,order=coerceFiniteNumber(record.order)??Number.NaN,created_at=typeof record.created_at=="string"?record.created_at:"",updated_at=typeof record.updated_at=="string"?record.updated_at:"";if(!id||!title||!status||!Number.isFinite(order)||!created_at||!updated_at)return;const step={id,order,title,status,created_at,updated_at};return applyPlanStepOptionalFields(step,record),step}function applyPlanStepOptionalFields(step,record){const stringFields=[["body","body"],["owner","owner"],["evidence","evidence"],["blocked_reason","blocked_reason"],["superseded_by","superseded_by"]];for(const[target,source]of stringFields){const value=trimStringOrUndefined(record[source]);value&&(step[target]=value)}const completedAt=typeof record.completed_at=="string"?record.completed_at:void 0;completedAt&&completedAt.length>0&&(step.completed_at=completedAt);const collectionFields=[["linked_items","linked_items",normalizePlanStepLinks],["files","files",normalizePlanStepFiles],["tests","tests",normalizePlanStepTests],["docs","docs",normalizePlanStepDocs]];for(const[target,source,normalize]of collectionFields){const value=normalize(record[source]);value&&(step[target]=value)}}function normalizePlanSteps(value){if(!Array.isArray(value))return;const out=value.flatMap(entry=>{const step=normalizePlanStepEntry(entry);return step?[step]:[]});return out.sort((left,right)=>left.order-right.order),out.length>0?out:void 0}function normalizePlanDecisions(value){if(!Array.isArray(value))return;const out=[];for(const entry of value){if(entry===null||typeof entry!="object")continue;const record=entry,ts=typeof record.ts=="string"?record.ts:"",author=typeof record.author=="string"?record.author:"",decision=trimStringOrUndefined(record.decision);if(!ts||!author||!decision)continue;const item={ts,author,decision},rationale=trimStringOrUndefined(record.rationale);rationale&&(item.rationale=rationale);const evidence=trimStringOrUndefined(record.evidence);evidence&&(item.evidence=evidence);const stepId=trimStringOrUndefined(record.step_id);stepId&&(item.step_id=stepId),out.push(item)}return out.length>0?out:void 0}function normalizePlanDiscoveries(value){if(!Array.isArray(value))return;const out=[];for(const entry of value){if(entry===null||typeof entry!="object")continue;const record=entry,ts=typeof record.ts=="string"?record.ts:"",author=typeof record.author=="string"?record.author:"",text=trimStringOrUndefined(record.text);if(!ts||!author||!text)continue;const item={ts,author,text},stepId=trimStringOrUndefined(record.step_id);stepId&&(item.step_id=stepId),out.push(item)}return out.length>0?out:void 0}function normalizePlanValidation(value){if(!Array.isArray(value))return;const out=[];for(const entry of value){if(entry===null||typeof entry!="object")continue;const record=entry,text=trimStringOrUndefined(record.text);if(!text)continue;const item={text},command=trimStringOrUndefined(record.command);command&&(item.command=command);const expected=trimStringOrUndefined(record.expected);expected&&(item.expected=expected),out.push(item)}return out.length>0?out:void 0}function normalizeTypeOptions(values){if(!values)return;const normalizedEntries=Object.entries(values).map(([key,value])=>[key.trim(),value.trim()]).filter(([key,value])=>key.length>0&&value.length>0).sort((left,right)=>left[0].localeCompare(right[0]));if(normalizedEntries.length!==0)return Object.fromEntries(normalizedEntries)}function normalizeBody(body){let start=0;for(;start<body.length&&body[start]===`
|
|
1
|
+
(function(){try{var e=typeof window<"u"?window:typeof global<"u"?global:typeof globalThis<"u"?globalThis:typeof self<"u"?self:{},n=new e.Error().stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="fb315276-2021-54aa-a880-5b4eb508e8d6")}catch{}})();import{encode as encodeToon}from"@toon-format/toon";import{isDeepStrictEqual}from"node:util";import{CONFIDENCE_TEXT_VALUES,ISSUE_SEVERITY_VALUES,PLAN_STEP_STATUS_VALUES,RECURRENCE_FREQUENCY_VALUES,RECURRENCE_WEEKDAY_VALUES,STATUS_VALUES,weekdayOrderIndex}from"../../types/index.js";import{coerceRuntimeFieldValue}from"../schema/runtime-field-values.js";import{resolveRuntimeFieldRegistry,resolveRuntimeStatusRegistry}from"../schema/runtime-schema.js";import{BASELINE_ITEM_FORMAT_VERSION,CURRENT_ITEM_FORMAT_VERSION,effectiveItemFormatVersion,normalizeItemFormatVersion}from"./item-format-version.js";import{normalizeStatusInput}from"./status.js";import{decodeToonItemContent}from"./toon-decode.js";import{EXIT_CODE,ITEM_METADATA_KEY_ORDER}from"../shared/constants.js";import{findFirstMergeConflictMarker}from"../shared/conflict-markers.js";import{PmCliError}from"../shared/errors.js";import{orderObject}from"../shared/serialization.js";import{compareTimestampStrings,isTimestampLiteral}from"../shared/time.js";import{coerceFiniteNumber}from"../shared/primitives.js";const LINKED_TEST_PM_CONTEXT_MODE_VALUES=new Set(["schema","tracker","auto","none"]),LINKED_TEST_WORKSPACE_CONTEXT_MODE_VALUES=new Set(["source","isolated","snapshot"]),LINKED_TEST_TRUST_REASON_VALUES=new Set(["legacy","local_mutation","local_source_ref","acknowledged","invalid_provenance","foreign_source_ref"]),ITEM_TEST_RUN_STATUS_VALUES=new Set(["passed","failed","stopped","canceled"]),PLAN_STEP_STATUS_SET=new Set(PLAN_STEP_STATUS_VALUES);function normalizePathValue(value){return value.replaceAll("\\","/")}function firstNonZeroComparison(comparisons){for(const comparison of comparisons)if(comparison!==0)return comparison;return 0}function deleteUndefinedFields(record){for(const[key,value]of Object.entries(record))value===void 0&&delete record[key]}const REQUIRED_STRING_FIELDS=["id","title","description","created_at","updated_at"],STATIC_ITEM_METADATA_FIELD_SET=new Set(ITEM_METADATA_KEY_ORDER);function resolveRuntimeSchemaValidationContext(options){if(options?.schema)return{statusRegistry:resolveRuntimeStatusRegistry(options.schema),fieldRegistry:resolveRuntimeFieldRegistry(options.schema),unknownFieldPolicy:options.schema.unknown_field_policy??"allow",extensionFieldNames:new Set(options.extensionFieldNames??[]),onWarning:options.onWarning}}function runtimeFieldRequiredForType(definition,typeName){return definition.required?definition.required_types.length===0?!0:definition.required_types.map(value=>value.toLowerCase()).includes(typeName.trim().toLowerCase()):!1}function validationError(message){const field=message.match(/^([a-z_][\w.]*)\s/u)?.[1];throw new PmCliError(`Invalid item metadata: ${message}`,EXIT_CODE.GENERIC_FAILURE,{code:"item_document_invalid",reason:message.includes("required")?"missing_required_field":"schema_validation_error",...field?{field}:{},required:"Provide an item document whose metadata satisfies the active runtime schema.",why:message})}function documentSyntaxError(format,message){throw new PmCliError(message,EXIT_CODE.GENERIC_FAILURE,{code:"item_document_invalid",reason:"syntax_error",format,required:`Provide a syntactically valid ${format==="toon"?"TOON":"JSON Markdown"} item document.`,why:"The document could not be decoded far enough to validate item metadata."})}function assertSupportedItemFormat(metadata,required){if(!required||typeof metadata!="object"||metadata===null)return;const formatVersion=effectiveItemFormatVersion(metadata);if(!(formatVersion<=CURRENT_ITEM_FORMAT_VERSION))throw new PmCliError(`Stored item uses format version ${formatVersion}; this runtime supports up to ${CURRENT_ITEM_FORMAT_VERSION}. Upgrade pm before reading or changing this item.`,EXIT_CODE.CONFLICT,{code:"item_format_version_unsupported",format_version:formatVersion,required:"Upgrade to a pm runtime supporting the stored item format.",nextSteps:["Preserve the original item and history; do not lower pm_format_version to bypass compatibility checks."]})}function buildKnownItemMetadataKeys(runtimeContext){const knownKeys=new Set(STATIC_ITEM_METADATA_FIELD_SET);for(const definition of runtimeContext.fieldRegistry?.definitions??[])knownKeys.add(definition.metadata_key);for(const fieldName of runtimeContext.extensionFieldNames)knownKeys.add(fieldName);return knownKeys}function assertItemMetadataCondition(condition,message){condition||validationError(message)}function requireItemMetadataRecord(value,message){return(typeof value!="object"||value===null||Array.isArray(value))&&validationError(message),value}function assertTimestampField(record,fieldName){const rawValue=record[fieldName];assertItemMetadataCondition(typeof rawValue=="string",`${fieldName} must be a string`),assertItemMetadataCondition(isTimestampLiteral(rawValue),`${fieldName} must be a valid ISO timestamp`)}function assertPositiveIntegerField(record,fieldName,messagePrefix){const value=record[fieldName];value!==void 0&&assertItemMetadataCondition(typeof value=="number"&&Number.isInteger(value)&&value>=1,`${messagePrefix} must be an integer >= 1`)}function assertOptionalTimestampString(record,fieldName,messagePrefix){const value=record[fieldName];value!==void 0&&(assertItemMetadataCondition(typeof value=="string",`${messagePrefix} must be a string`),assertItemMetadataCondition(isTimestampLiteral(value),`${messagePrefix} must be a valid ISO timestamp`))}function assertRecurrenceWeekdays(value){if(value!==void 0){assertItemMetadataCondition(Array.isArray(value),"event.recurrence.by_weekday must be an array");for(const weekday of value){assertItemMetadataCondition(typeof weekday=="string","event.recurrence.by_weekday entries must be strings");const normalizedWeekday=weekday.trim().toLowerCase();assertItemMetadataCondition(RECURRENCE_WEEKDAY_VALUES.includes(normalizedWeekday),`event.recurrence.by_weekday entries must be one of: ${RECURRENCE_WEEKDAY_VALUES.join(", ")}`)}}}function assertRecurrenceMonthDays(value){if(value!==void 0){assertItemMetadataCondition(Array.isArray(value),"event.recurrence.by_month_day must be an array");for(const day of value)assertItemMetadataCondition(typeof day=="number"&&Number.isInteger(day)&&day>=1&&day<=31,"event.recurrence.by_month_day entries must be integers 1..31")}}function assertRecurrenceExdates(value){if(value!==void 0){assertItemMetadataCondition(Array.isArray(value),"event.recurrence.exdates must be an array");for(const exdate of value)assertItemMetadataCondition(typeof exdate=="string","event.recurrence.exdates entries must be strings"),assertItemMetadataCondition(isTimestampLiteral(exdate),"event.recurrence.exdates entries must be valid ISO timestamps")}}function assertValidRecurrenceRule(recurrence){const recurrenceRecord=requireItemMetadataRecord(recurrence,"event.recurrence must be an object");assertItemMetadataCondition(typeof recurrenceRecord.freq=="string","event.recurrence.freq must be a string");const frequency=recurrenceRecord.freq.trim().toLowerCase();assertItemMetadataCondition(RECURRENCE_FREQUENCY_VALUES.includes(frequency),`event.recurrence.freq must be one of: ${RECURRENCE_FREQUENCY_VALUES.join(", ")}`),assertPositiveIntegerField(recurrenceRecord,"interval","event.recurrence.interval"),assertPositiveIntegerField(recurrenceRecord,"count","event.recurrence.count"),assertOptionalTimestampString(recurrenceRecord,"until","event.recurrence.until"),assertRecurrenceWeekdays(recurrenceRecord.by_weekday),assertRecurrenceMonthDays(recurrenceRecord.by_month_day),assertRecurrenceExdates(recurrenceRecord.exdates)}function assertRequiredItemMetadataFields(record){for(const fieldName of REQUIRED_STRING_FIELDS)assertItemMetadataCondition(typeof record[fieldName]=="string",`${fieldName} is required and must be a string`);const itemType=record.type;assertItemMetadataCondition(typeof itemType=="string"&&itemType.trim().length>0,"type must be a non-empty string");const formatVersion=record.pm_format_version;return formatVersion!==void 0&&assertItemMetadataCondition(typeof formatVersion=="number"&&Number.isInteger(formatVersion)&&formatVersion>=BASELINE_ITEM_FORMAT_VERSION,`pm_format_version must be an integer >= ${BASELINE_ITEM_FORMAT_VERSION}`),itemType}function assertStatusItemMetadataField(record,runtimeContext){const status=record.status;assertItemMetadataCondition(typeof status=="string"&&status.trim().length>0,"status must be a non-empty string");const statusRegistry=runtimeContext?.statusRegistry,normalizedStatus=normalizeStatusInput(status,statusRegistry),statusDomain=statusRegistry?statusRegistry.definitions.map(definition=>definition.id):[...STATUS_VALUES];assertItemMetadataCondition(normalizedStatus!==void 0,`status must be one of: ${statusDomain.join(", ")}`)}function assertPriorityAndTags(record){const priority=record.priority;assertItemMetadataCondition(typeof priority=="number"&&Number.isInteger(priority)&&[0,1,2,3,4].includes(priority),"priority must be an integer 0..4");const tags=record.tags;assertItemMetadataCondition(Array.isArray(tags),"tags must be an array");for(const tag of tags)assertItemMetadataCondition(typeof tag=="string","tags entries must be strings")}function assertConfidenceItemMetadataField(record){const confidence=record.confidence;if(confidence!==void 0){if(typeof confidence=="number"){assertItemMetadataCondition(Number.isInteger(confidence)&&confidence>=0&&confidence<=100,"confidence number value must be an integer 0..100");return}if(typeof confidence=="string"){const normalizedConfidence=confidence.trim().toLowerCase(),isKnownTextConfidence=normalizedConfidence==="med"||CONFIDENCE_TEXT_VALUES.includes(normalizedConfidence);assertItemMetadataCondition(isKnownTextConfidence,`confidence string value must be one of: ${[...CONFIDENCE_TEXT_VALUES,"med"].join(", ")}`);return}assertItemMetadataCondition(!1,"confidence must be a number or string")}}function assertSeverityItemMetadataField(record){const severity=record.severity;if(severity===void 0)return;typeof severity!="string"&&validationError("severity must be a string");const normalizedSeverity=severity.trim().toLowerCase(),isKnownSeverity=normalizedSeverity==="med"||ISSUE_SEVERITY_VALUES.includes(normalizedSeverity);assertItemMetadataCondition(isKnownSeverity,`severity value must be one of: ${[...ISSUE_SEVERITY_VALUES,"med"].join(", ")}`)}function assertRegressionItemMetadataField(record){record.regression!==void 0&&typeof record.regression!="boolean"&&validationError("regression must be a boolean")}function assertReminderEntries(record){const reminders=record.reminders;if(reminders!==void 0){assertItemMetadataCondition(Array.isArray(reminders),"reminders must be an array");for(const reminder of reminders){const reminderRecord=requireItemMetadataRecord(reminder,"reminders entries must be objects");assertItemMetadataCondition(typeof reminderRecord.at=="string","reminder.at must be a string"),assertItemMetadataCondition(isTimestampLiteral(reminderRecord.at),"reminder.at must be a valid ISO timestamp"),assertItemMetadataCondition(typeof reminderRecord.text=="string","reminder.text must be a string"),assertItemMetadataCondition(reminderRecord.text.trim().length>0,"reminder.text must not be empty")}}}function assertEventStringFields(eventRecord){for(const stringField of["title","description","location","timezone"])eventRecord[stringField]!==void 0&&(assertItemMetadataCondition(typeof eventRecord[stringField]=="string",`event.${stringField} must be a string`),assertItemMetadataCondition(eventRecord[stringField].trim().length>0,`event.${stringField} must not be empty`))}function assertEventRecurrence(eventRecord){eventRecord.recurrence!==void 0&&(assertValidRecurrenceRule(eventRecord.recurrence),eventRecord.recurrence.until!==void 0&&assertItemMetadataCondition(compareTimestampStrings(eventRecord.recurrence.until,eventRecord.start_at)>=0,"event.recurrence.until must be at or after event.start_at"))}function assertCalendarEventEntry(event){const eventRecord=requireItemMetadataRecord(event,"events entries must be objects");assertItemMetadataCondition(typeof eventRecord.start_at=="string","event.start_at must be a string"),assertItemMetadataCondition(isTimestampLiteral(eventRecord.start_at),"event.start_at must be a valid ISO timestamp"),eventRecord.end_at!==void 0&&(assertItemMetadataCondition(typeof eventRecord.end_at=="string","event.end_at must be a string"),assertItemMetadataCondition(isTimestampLiteral(eventRecord.end_at),"event.end_at must be a valid ISO timestamp"),assertItemMetadataCondition(compareTimestampStrings(eventRecord.end_at,eventRecord.start_at)>0,"event.end_at must be after event.start_at")),assertEventStringFields(eventRecord),eventRecord.all_day!==void 0&&assertItemMetadataCondition(typeof eventRecord.all_day=="boolean","event.all_day must be a boolean"),assertEventRecurrence(eventRecord)}function assertCalendarEvents(record){const events=record.events;if(events!==void 0){assertItemMetadataCondition(Array.isArray(events),"events must be an array");for(const event of events)assertCalendarEventEntry(event)}}function assertOptionalStringFields(record){for(const fieldName of["source_type","source_owner","design","external_ref"]){const value=record[fieldName];value!==void 0&&assertItemMetadataCondition(typeof value=="string",`${fieldName} must be a string`)}}function assertTypeOptionsField(record){const typeOptions=record.type_options;if(typeOptions===void 0)return;const typeOptionsRecord=requireItemMetadataRecord(typeOptions,"type_options must be an object");for(const[optionKey,optionValue]of Object.entries(typeOptionsRecord))assertItemMetadataCondition(optionKey.trim().length>0,"type_options keys must be non-empty"),assertItemMetadataCondition(typeof optionValue=="string","type_options values must be strings"),assertItemMetadataCondition(optionValue.trim().length>0,"type_options values must be non-empty strings")}function coerceRuntimeItemMetadataFields(record,itemType,runtimeContext){if(runtimeContext?.fieldRegistry)for(const definition of runtimeContext.fieldRegistry.definitions){const fieldValue=record[definition.metadata_key];if(fieldValue===void 0){runtimeFieldRequiredForType(definition,itemType)&&validationError(`missing required schema field: ${definition.metadata_key}`);continue}try{record[definition.metadata_key]=coerceRuntimeFieldValue(definition,fieldValue,`metadata field "${definition.metadata_key}"`,"metadata")}catch(error){validationError(String(error?.message).replace(/^Invalid\s+/u,""))}}}function assertRuntimeUnknownItemMetadataFields(record,runtimeContext){if(!runtimeContext||runtimeContext.unknownFieldPolicy==="allow")return;const knownKeys=buildKnownItemMetadataKeys(runtimeContext),unknownKeys=Object.keys(record).filter(key=>!knownKeys.has(key)).sort((left,right)=>left.localeCompare(right));unknownKeys.length!==0&&(runtimeContext.unknownFieldPolicy==="reject"?validationError(`unknown schema fields are not allowed: ${unknownKeys.join(", ")}`):runtimeContext.onWarning?.(`item_unknown_schema_fields:${unknownKeys.join(",")}`))}function assertValidItemMetadata(itemMetadata,runtimeContext){const record=requireItemMetadataRecord(itemMetadata,"item metadata must be an object"),itemType=assertRequiredItemMetadataFields(record);assertStatusItemMetadataField(record,runtimeContext),assertPriorityAndTags(record),assertConfidenceItemMetadataField(record),assertSeverityItemMetadataField(record),assertRegressionItemMetadataField(record),assertTimestampField(record,"created_at"),assertTimestampField(record,"updated_at"),record.deadline!==void 0&&assertTimestampField(record,"deadline"),assertReminderEntries(record),assertCalendarEvents(record),assertOptionalTimestampString(record,"closed_at","closed_at"),assertOptionalTimestampString(record,"completed_at","completed_at"),assertOptionalStringFields(record),assertTypeOptionsField(record),coerceRuntimeItemMetadataFields(record,itemType,runtimeContext),assertRuntimeUnknownItemMetadataFields(record,runtimeContext)}function sortDependencies(values){if(!(!values||values.length===0))return[...values].map(value=>({id:value.id.trim(),kind:value.kind,created_at:value.created_at,author:value.author?.trim()||void 0,source_kind:value.source_kind?.trim()||void 0,author_source:value.author_source})).sort((a,b)=>{const byCreated=compareTimestampStrings(a.created_at,b.created_at);if(byCreated!==0)return byCreated;const byId=a.id.localeCompare(b.id);if(byId!==0)return byId;const byKind=a.kind.localeCompare(b.kind);return byKind!==0?byKind:(a.source_kind??"").localeCompare(b.source_kind??"")})}function sortLogValues(values){if(!(!values||values.length===0))return[...values].sort((a,b)=>{const byCreated=compareTimestampStrings(a.created_at,b.created_at);if(byCreated!==0)return byCreated;const byText=a.text.localeCompare(b.text);return byText!==0?byText:a.author.localeCompare(b.author)})}function sortReminders(values){if(!values||values.length===0)return;const normalized=[...values].map(value=>({at:value.at,text:value.text.trim()})).filter(value=>value.text.length>0).sort((a,b)=>{const byAt=compareTimestampStrings(a.at,b.at);return byAt!==0?byAt:a.text.localeCompare(b.text)});return normalized.length===0?void 0:normalized}function normalizeRecurrenceRule(value){if(!value)return;const normalizedFrequency=value.freq.trim().toLowerCase();if(!RECURRENCE_FREQUENCY_VALUES.includes(normalizedFrequency))return;const byWeekday=Array.from(new Set((value.by_weekday??[]).map(weekday=>weekday.trim().toLowerCase()).filter(weekday=>RECURRENCE_WEEKDAY_VALUES.includes(weekday)))).sort((a,b)=>weekdayOrderIndex(a)-weekdayOrderIndex(b)),byMonthDay=Array.from(new Set((value.by_month_day??[]).filter(day=>Number.isInteger(day)&&day>=1&&day<=31).map(day=>day))).sort((a,b)=>a-b),exdates=Array.from(new Set((value.exdates??[]).map(timestamp=>timestamp.trim()).filter(timestamp=>isTimestampLiteral(timestamp)))).sort((a,b)=>compareTimestampStrings(a,b)),normalized={freq:normalizedFrequency,interval:value.interval!==void 0&&value.interval>1?value.interval:void 0,count:value.count,until:value.until?.trim()||void 0,by_weekday:byWeekday.length>0?byWeekday:void 0,by_month_day:byMonthDay.length>0?byMonthDay:void 0,exdates:exdates.length>0?exdates:void 0};for(const[key,fieldValue]of Object.entries(normalized))fieldValue===void 0&&delete normalized[key];return normalized}function sortEvents(values){return!Array.isArray(values)||values.length===0?void 0:[...values].map(normalizeCalendarEvent).sort(compareCalendarEvents)}function normalizeCalendarEvent(value){const event={start_at:value.start_at,end_at:value.end_at||void 0,title:value.title?.trim()||void 0,description:value.description?.trim()||void 0,location:value.location?.trim()||void 0,all_day:value.all_day,timezone:value.timezone?.trim()||void 0,recurrence:normalizeRecurrenceRule(value.recurrence)};return deleteUndefinedFields(event),event}function compareCalendarEvents(a,b){return firstNonZeroComparison([compareTimestampStrings(a.start_at,b.start_at),(a.end_at??"").localeCompare(b.end_at??""),(a.title??"").localeCompare(b.title??""),+!!a.all_day-+!!b.all_day,(a.timezone??"").localeCompare(b.timezone??""),(a.location??"").localeCompare(b.location??""),(a.description??"").localeCompare(b.description??""),JSON.stringify(a.recurrence??{}).localeCompare(JSON.stringify(b.recurrence??{}))])}function normalizeFiles(values){if(!(!values||values.length===0))return values.map(value=>({path:normalizePathValue(value.path),scope:value.scope,note:value.note?.trim()||void 0}))}function normalizeTestRunSummaries(values){if(!values||values.length===0)return;const normalized=values.map(normalizeTestRunSummary).filter(value=>value.run_id.length>0&&value.started_at.length>0&&value.finished_at.length>0&&value.recorded_at.length>0).sort((a,b)=>firstNonZeroComparison([compareTimestampStrings(a.recorded_at,b.recorded_at),a.run_id.localeCompare(b.run_id),a.kind.localeCompare(b.kind)]));return normalized.length>0?normalized:void 0}function normalizeNonNegativeInteger(value){return typeof value=="number"&&Number.isFinite(value)&&value>=0?Math.floor(value):void 0}function normalizePositiveInteger(value){return typeof value=="number"&&Number.isFinite(value)&&value>=1?Math.floor(value):void 0}function stringOrEmpty(value){return typeof value=="string"?value:""}function normalizeTestRunMeasurements(value){if(!Array.isArray(value))return;const normalized=value.filter(measurement=>measurement!==null&&typeof measurement=="object"&&typeof measurement.name=="string"&&measurement.name.trim().length>0&&typeof measurement.value=="number"&&Number.isFinite(measurement.value)&&typeof measurement.recorded_at=="string"&&measurement.recorded_at.trim().length>0).map(measurement=>({name:measurement.name.trim(),value:measurement.value,unit:typeof measurement.unit=="string"&&measurement.unit.trim()||void 0,threshold:typeof measurement.threshold=="number"&&Number.isFinite(measurement.threshold)?measurement.threshold:void 0,recorded_at:measurement.recorded_at.trim()})),deduplicated=[...new Map(normalized.map(measurement=>[measurement.name,measurement])).values()].sort((left,right)=>left.name.localeCompare(right.name)).slice(0,32);return deduplicated.length>0?deduplicated:void 0}function normalizeTestRunEnum(value,allowedValues){return typeof value=="string"&&allowedValues.has(value)?value:void 0}function normalizeTestRunExecutions(values){if(!Array.isArray(values))return;const executions=values.filter(execution=>execution&&typeof execution.command=="string"&&execution.command.trim().length>0).slice(0,32).map(execution=>({command:execution.command.trim(),requested_pm_context_mode:normalizeTestRunEnum(execution.requested_pm_context_mode,LINKED_TEST_PM_CONTEXT_MODE_VALUES),pm_context_mode:normalizeTestRunEnum(execution.pm_context_mode,LINKED_TEST_PM_CONTEXT_MODE_VALUES),workspace_context_mode:normalizeTestRunEnum(execution.workspace_context_mode,LINKED_TEST_WORKSPACE_CONTEXT_MODE_VALUES),trust_reason:normalizeTestRunEnum(execution.trust_reason,LINKED_TEST_TRUST_REASON_VALUES)}));return executions.length>0?executions:void 0}function normalizeTestRunSummary(value){const runId=typeof value.run_id=="string"?value.run_id.trim():"",kind=value.kind==="test"||value.kind==="test-all"?value.kind:"test",status=ITEM_TEST_RUN_STATUS_VALUES.has(value.status)?value.status:"failed",summary={run_id:runId,kind,status,started_at:stringOrEmpty(value.started_at),finished_at:stringOrEmpty(value.finished_at),recorded_at:stringOrEmpty(value.recorded_at),attempt:normalizePositiveInteger(value.attempt),resumed_from:value.resumed_from?.trim()||void 0,passed:normalizeNonNegativeInteger(value.passed)??0,failed:normalizeNonNegativeInteger(value.failed)??0,skipped:normalizeNonNegativeInteger(value.skipped)??0,items:normalizeNonNegativeInteger(value.items),linked_tests:normalizeNonNegativeInteger(value.linked_tests),fail_on_skipped_triggered:value.fail_on_skipped_triggered===!0?!0:void 0,measurements:normalizeTestRunMeasurements(value.measurements),executions:normalizeTestRunExecutions(value.executions)};return deleteUndefinedFields(summary),summary}function sortTests(values){if(!(!Array.isArray(values)||values.length===0))return values.map(normalizeLinkedTest)}function normalizeTrimmedStringList(value){if(!Array.isArray(value))return;const normalized=[...new Set(value.filter(entry=>typeof entry=="string").map(entry=>entry.trim()).filter(entry=>entry.length>0))].sort((a,b)=>a.localeCompare(b));return normalized.length>0?normalized:void 0}function isPlainObjectRecord(value){if(!value||typeof value!="object"||Array.isArray(value))return!1;const prototype=Object.getPrototypeOf(value);return prototype===Object.prototype||prototype===null}function isUnsafeObjectKey(key){return key==="__proto__"||key==="constructor"||key==="prototype"}function normalizeStringRecord(value){if(!isPlainObjectRecord(value))return;const entries=Object.entries(value).map(([key,recordValue])=>[key.trim(),String(recordValue).trim()]).filter(([key,recordValue])=>key.length>0&&!isUnsafeObjectKey(key)&&recordValue.length>0).sort(([left],[right])=>left.localeCompare(right));return entries.length>0?Object.fromEntries(entries):void 0}function normalizeNumericRecord(value){if(!isPlainObjectRecord(value))return;const entries=Object.entries(value).map(([key,recordValue])=>[key.trim(),Number(recordValue)]).filter(([key,recordValue])=>key.length>0&&!isUnsafeObjectKey(key)&&Number.isFinite(recordValue)).sort(([left],[right])=>left.localeCompare(right));return entries.length>0?Object.fromEntries(entries):void 0}function normalizeLinkedTestContextMode(value){const normalized=value?.trim().toLowerCase();if(!(!normalized||!LINKED_TEST_PM_CONTEXT_MODE_VALUES.has(normalized)))return normalized}function normalizeLinkedTestWorkspaceContextMode(value){const normalized=value?.trim().toLowerCase();if(!(!normalized||!LINKED_TEST_WORKSPACE_CONTEXT_MODE_VALUES.has(normalized)))return normalized}function normalizeLinkedTestProvenance(value,invalid){if(invalid===!0)return{provenance_invalid:!0};if(value===void 0)return{};if(!isPlainObjectRecord(value))return{provenance_invalid:!0};const author=trimStringOrUndefined(value.author),createdAt=trimStringOrUndefined(value.created_at),sourceKind=value.source_kind;return!author||!createdAt||!isTimestampLiteral(createdAt)||sourceKind!=="local_mutation"&&sourceKind!=="merge_union"?{provenance_invalid:!0}:{provenance:{author,created_at:createdAt,source_kind:sourceKind,source_ref:trimStringOrUndefined(value.source_ref)}}}function normalizeLinkedTest(value){const test={command:value.command?.trim()||void 0,path:value.path?normalizePathValue(value.path):void 0,scope:value.scope,timeout_seconds:typeof value.timeout_seconds=="number"&&Number.isFinite(value.timeout_seconds)&&value.timeout_seconds>0?value.timeout_seconds:void 0,pm_context_mode:normalizeLinkedTestContextMode(value.pm_context_mode),workspace_context_mode:normalizeLinkedTestWorkspaceContextMode(value.workspace_context_mode),...normalizeLinkedTestProvenance(value.provenance,value.provenance_invalid),updated_by:trimStringOrUndefined(value.updated_by),updated_at:trimStringOrUndefined(value.updated_at),env_set:normalizeStringRecord(value.env_set),env_clear:normalizeTrimmedStringList(value.env_clear),shared_host_safe:value.shared_host_safe===!0?!0:void 0,assert_stdout_contains:normalizeTrimmedStringList(value.assert_stdout_contains),assert_stdout_regex:normalizeTrimmedStringList(value.assert_stdout_regex),assert_stderr_contains:normalizeTrimmedStringList(value.assert_stderr_contains),assert_stderr_regex:normalizeTrimmedStringList(value.assert_stderr_regex),assert_stdout_min_lines:normalizeNonNegativeInteger(value.assert_stdout_min_lines),assert_json_field_equals:normalizeStringRecord(value.assert_json_field_equals),assert_json_field_gte:normalizeNumericRecord(value.assert_json_field_gte),note:value.note?.trim()||void 0};return deleteUndefinedFields(test),test}function sortDocs(values){if(!(!values||values.length===0))return[...values].map(value=>({path:normalizePathValue(value.path),scope:value.scope,note:value.note?.trim()||void 0})).sort((a,b)=>{const byScope=a.scope.localeCompare(b.scope);if(byScope!==0)return byScope;const byPath=a.path.localeCompare(b.path);return byPath!==0?byPath:(a.note??"").localeCompare(b.note??"")})}function trimStringOrUndefined(value){if(typeof value!="string")return;const trimmed=value.trim();return trimmed.length>0?trimmed:void 0}function normalizePlanStepLinks(value){if(!Array.isArray(value))return;const out=[];for(const entry of value){if(entry===null||typeof entry!="object")continue;const record=entry,id=trimStringOrUndefined(record.id),kind=trimStringOrUndefined(record.kind);if(!id||!kind)continue;const link={id,kind},note=trimStringOrUndefined(record.note);note&&(link.note=note),record.required_before_step===!0&&(link.required_before_step=!0),out.push(link)}return out.length>0?out:void 0}function normalizePlanStepFiles(value){if(!Array.isArray(value))return;const out=[];for(const entry of value){if(entry===null||typeof entry!="object")continue;const record=entry,path=trimStringOrUndefined(record.path);if(!path)continue;const file={path:normalizePathValue(path)};(record.scope==="project"||record.scope==="global")&&(file.scope=record.scope);const note=trimStringOrUndefined(record.note);note&&(file.note=note),out.push(file)}return out.length>0?out:void 0}function normalizePlanStepTests(value){if(!Array.isArray(value))return;const out=[];for(const entry of value){if(entry===null||typeof entry!="object")continue;const record=entry,command=trimStringOrUndefined(record.command),path=trimStringOrUndefined(record.path);if(!command&&!path)continue;const test={};command&&(test.command=command),path&&(test.path=normalizePathValue(path));const note=trimStringOrUndefined(record.note);note&&(test.note=note),out.push(test)}return out.length>0?out:void 0}function normalizePlanStepDocs(value){if(!Array.isArray(value))return;const out=[];for(const entry of value){if(entry===null||typeof entry!="object")continue;const record=entry,path=trimStringOrUndefined(record.path);if(!path)continue;const doc={path:normalizePathValue(path)};(record.scope==="project"||record.scope==="global")&&(doc.scope=record.scope);const note=trimStringOrUndefined(record.note);note&&(doc.note=note),out.push(doc)}return out.length>0?out:void 0}function normalizePlanStepEntry(entry){if(entry===null||typeof entry!="object")return;const record=entry,id=trimStringOrUndefined(record.id),title=trimStringOrUndefined(record.title),statusRaw=trimStringOrUndefined(record.status),status=statusRaw&&PLAN_STEP_STATUS_SET.has(statusRaw)?statusRaw:void 0,order=coerceFiniteNumber(record.order)??Number.NaN,created_at=typeof record.created_at=="string"?record.created_at:"",updated_at=typeof record.updated_at=="string"?record.updated_at:"";if(!id||!title||!status||!Number.isFinite(order)||!created_at||!updated_at)return;const step={id,order,title,status,created_at,updated_at};return applyPlanStepOptionalFields(step,record),step}function applyPlanStepOptionalFields(step,record){const stringFields=[["body","body"],["owner","owner"],["evidence","evidence"],["blocked_reason","blocked_reason"],["superseded_by","superseded_by"]];for(const[target,source]of stringFields){const value=trimStringOrUndefined(record[source]);value&&(step[target]=value)}const completedAt=typeof record.completed_at=="string"?record.completed_at:void 0;completedAt&&completedAt.length>0&&(step.completed_at=completedAt);const collectionFields=[["linked_items","linked_items",normalizePlanStepLinks],["files","files",normalizePlanStepFiles],["tests","tests",normalizePlanStepTests],["docs","docs",normalizePlanStepDocs]];for(const[target,source,normalize]of collectionFields){const value=normalize(record[source]);value&&(step[target]=value)}}function normalizePlanSteps(value){if(!Array.isArray(value))return;const out=value.flatMap(entry=>{const step=normalizePlanStepEntry(entry);return step?[step]:[]});return out.sort((left,right)=>left.order-right.order),out.length>0?out:void 0}function normalizePlanDecisions(value){if(!Array.isArray(value))return;const out=[];for(const entry of value){if(entry===null||typeof entry!="object")continue;const record=entry,ts=typeof record.ts=="string"?record.ts:"",author=typeof record.author=="string"?record.author:"",decision=trimStringOrUndefined(record.decision);if(!ts||!author||!decision)continue;const item={ts,author,decision},rationale=trimStringOrUndefined(record.rationale);rationale&&(item.rationale=rationale);const evidence=trimStringOrUndefined(record.evidence);evidence&&(item.evidence=evidence);const stepId=trimStringOrUndefined(record.step_id);stepId&&(item.step_id=stepId),out.push(item)}return out.length>0?out:void 0}function normalizePlanDiscoveries(value){if(!Array.isArray(value))return;const out=[];for(const entry of value){if(entry===null||typeof entry!="object")continue;const record=entry,ts=typeof record.ts=="string"?record.ts:"",author=typeof record.author=="string"?record.author:"",text=trimStringOrUndefined(record.text);if(!ts||!author||!text)continue;const item={ts,author,text},stepId=trimStringOrUndefined(record.step_id);stepId&&(item.step_id=stepId),out.push(item)}return out.length>0?out:void 0}function normalizePlanValidation(value){if(!Array.isArray(value))return;const out=[];for(const entry of value){if(entry===null||typeof entry!="object")continue;const record=entry,text=trimStringOrUndefined(record.text);if(!text)continue;const item={text},command=trimStringOrUndefined(record.command);command&&(item.command=command);const expected=trimStringOrUndefined(record.expected);expected&&(item.expected=expected),out.push(item)}return out.length>0?out:void 0}function normalizeTypeOptions(values){if(!values)return;const normalizedEntries=Object.entries(values).map(([key,value])=>[key.trim(),value.trim()]).filter(([key,value])=>key.length>0&&value.length>0).sort((left,right)=>left[0].localeCompare(right[0]));if(normalizedEntries.length!==0)return Object.fromEntries(normalizedEntries)}function normalizeBody(body){let start=0;for(;start<body.length&&body[start]===`
|
|
2
2
|
`;)start+=1;return body.slice(start).trimEnd()}function normalizeConfidenceValue(value){if(value===void 0)return;if(typeof value=="number")return value;const normalized=value.trim().toLowerCase();if(normalized==="med")return"medium";if(CONFIDENCE_TEXT_VALUES.includes(normalized))return normalized}function normalizeSeverityValue(value){if(value===void 0)return;const normalized=value.trim().toLowerCase();if(normalized==="med")return"medium";if(ISSUE_SEVERITY_VALUES.includes(normalized))return normalized}function emptyStringToUndefined(value){return value||void 0}function copyExtensionItemMetadataFields(normalized,source){const targetRecord=normalized,sourceRecord=source;for(const[key,value]of Object.entries(sourceRecord))isUnsafeObjectKey(key)||Object.prototype.hasOwnProperty.call(targetRecord,key)||value===void 0||(targetRecord[key]=value)}function coerceNormalizedRuntimeFields(normalized,runtimeContext){if(!runtimeContext?.fieldRegistry)return;const record=normalized;for(const definition of runtimeContext.fieldRegistry.definitions){const currentValue=record[definition.metadata_key];currentValue!==void 0&&(record[definition.metadata_key]=coerceRuntimeFieldValue(definition,currentValue,`metadata field "${definition.metadata_key}"`,"metadata"))}}function assertNormalizedUnknownFields(normalized,runtimeContext){if(!runtimeContext||runtimeContext.unknownFieldPolicy==="allow")return;const knownKeys=buildKnownItemMetadataKeys(runtimeContext),unknownKeys=Object.keys(normalized).filter(key=>!knownKeys.has(key)).sort((left,right)=>left.localeCompare(right));unknownKeys.length>0&&runtimeContext.unknownFieldPolicy==="reject"&&validationError(`unknown schema fields are not allowed: ${unknownKeys.join(", ")}`),unknownKeys.length>0&&runtimeContext.onWarning?.(`item_unknown_schema_fields:${unknownKeys.join(",")}`)}function normalizeItemMetadata(itemMetadata,options={}){const runtimeContext=resolveRuntimeSchemaValidationContext(options),normalizedStatus=normalizeStatusInput(itemMetadata.status,runtimeContext?.statusRegistry)??itemMetadata.status,tags=Array.from(new Set((Array.isArray(itemMetadata.tags)?itemMetadata.tags:[]).filter(tag=>typeof tag=="string").map(tag=>tag.trim().toLowerCase()).filter(Boolean))).sort((a,b)=>a.localeCompare(b)),normalized={id:itemMetadata.id,title:itemMetadata.title,description:itemMetadata.description,type:itemMetadata.type,pm_format_version:normalizeItemFormatVersion(itemMetadata.pm_format_version),source_type:trimStringOrUndefined(itemMetadata.source_type),type_options:normalizeTypeOptions(itemMetadata.type_options),status:normalizedStatus,priority:itemMetadata.priority,tags,created_at:itemMetadata.created_at,updated_at:itemMetadata.updated_at,dependencies:sortDependencies(itemMetadata.dependencies),comments:sortLogValues(itemMetadata.comments),notes:sortLogValues(itemMetadata.notes),learnings:sortLogValues(itemMetadata.learnings),files:normalizeFiles(itemMetadata.files),tests:sortTests(itemMetadata.tests),test_runs:normalizeTestRunSummaries(itemMetadata.test_runs),docs:sortDocs(itemMetadata.docs),deadline:emptyStringToUndefined(itemMetadata.deadline),reminders:sortReminders(itemMetadata.reminders),events:sortEvents(itemMetadata.events),closed_at:emptyStringToUndefined(itemMetadata.closed_at),completed_at:emptyStringToUndefined(itemMetadata.completed_at),assignee:trimStringOrUndefined(itemMetadata.assignee),claim_principal:trimStringOrUndefined(itemMetadata.claim_principal),source_owner:trimStringOrUndefined(itemMetadata.source_owner),author:emptyStringToUndefined(itemMetadata.author),estimated_minutes:itemMetadata.estimated_minutes,acceptance_criteria:itemMetadata.acceptance_criteria??void 0,design:itemMetadata.design??void 0,external_ref:itemMetadata.external_ref??void 0,definition_of_ready:trimStringOrUndefined(itemMetadata.definition_of_ready),order:itemMetadata.order,goal:trimStringOrUndefined(itemMetadata.goal),objective:trimStringOrUndefined(itemMetadata.objective),value:trimStringOrUndefined(itemMetadata.value),impact:trimStringOrUndefined(itemMetadata.impact),outcome:trimStringOrUndefined(itemMetadata.outcome),why_now:trimStringOrUndefined(itemMetadata.why_now),parent:trimStringOrUndefined(itemMetadata.parent),reviewer:trimStringOrUndefined(itemMetadata.reviewer),risk:itemMetadata.risk??void 0,confidence:normalizeConfidenceValue(itemMetadata.confidence),sprint:trimStringOrUndefined(itemMetadata.sprint),release:trimStringOrUndefined(itemMetadata.release),blocked_by:trimStringOrUndefined(itemMetadata.blocked_by),blocked_reason:trimStringOrUndefined(itemMetadata.blocked_reason),unblock_note:trimStringOrUndefined(itemMetadata.unblock_note),reporter:trimStringOrUndefined(itemMetadata.reporter),severity:normalizeSeverityValue(itemMetadata.severity),environment:trimStringOrUndefined(itemMetadata.environment),repro_steps:trimStringOrUndefined(itemMetadata.repro_steps),resolution:trimStringOrUndefined(itemMetadata.resolution),expected_result:trimStringOrUndefined(itemMetadata.expected_result),actual_result:trimStringOrUndefined(itemMetadata.actual_result),affected_version:trimStringOrUndefined(itemMetadata.affected_version),fixed_version:trimStringOrUndefined(itemMetadata.fixed_version),component:trimStringOrUndefined(itemMetadata.component),regression:itemMetadata.regression,customer_impact:trimStringOrUndefined(itemMetadata.customer_impact),close_reason:emptyStringToUndefined(itemMetadata.close_reason),plan_mode:trimStringOrUndefined(itemMetadata.plan_mode),plan_scope:trimStringOrUndefined(itemMetadata.plan_scope),plan_harness:trimStringOrUndefined(itemMetadata.plan_harness),plan_resume_context:trimStringOrUndefined(itemMetadata.plan_resume_context),plan_steps:normalizePlanSteps(itemMetadata.plan_steps),plan_decisions:normalizePlanDecisions(itemMetadata.plan_decisions),plan_discoveries:normalizePlanDiscoveries(itemMetadata.plan_discoveries),plan_validation:normalizePlanValidation(itemMetadata.plan_validation)};return copyExtensionItemMetadataFields(normalized,itemMetadata),coerceNormalizedRuntimeFields(normalized,runtimeContext),assertNormalizedUnknownFields(normalized,runtimeContext),deleteUndefinedFields(normalized),normalized}function orderItemMetadata(itemMetadata){return orderObject(itemMetadata,ITEM_METADATA_KEY_ORDER)}function findJsonObjectEnd(content){let depth=0,inString=!1,escaped=!1;for(let i=0;i<content.length;i+=1){const char=content[i];if(inString){if(escaped){escaped=!1;continue}if(char==="\\"){escaped=!0;continue}char==='"'&&(inString=!1);continue}if(char==='"'){inString=!0;continue}if(char==="{"){depth+=1;continue}if(char==="}"&&(depth-=1,depth===0))return i}return-1}function splitFrontMatter(content){if(!content.startsWith("{"))return{frontMatter:"",body:content};const end=findJsonObjectEnd(content);if(end<0)return{frontMatter:"",body:content};const frontMatter=content.slice(0,end+1),body=content.slice(end+1).replace(/^\r?\n+/,"");return{frontMatter,body}}function stripLeadingYamlDocument(content){const normalizedContent=content.charCodeAt(0)===65279?content.slice(1):content,opener=normalizedContent.match(/^[ \t]*---[ \t]*(?:\r?\n|$)/);if(!opener)return{content:normalizedContent,stripped:!1};let cursor=opener[0].length;for(;cursor<normalizedContent.length;){const nextNewline=normalizedContent.indexOf(`
|
|
3
3
|
`,cursor),lineEnd=nextNewline===-1?normalizedContent.length:nextNewline,line=normalizedContent.slice(cursor,lineEnd).replace(/\r$/,""),afterLine=nextNewline===-1?lineEnd:lineEnd+1;if(line.trim()==="---")return{content:normalizedContent.slice(afterLine).replace(/^\s+/,""),stripped:!0};cursor=afterLine}return{content:normalizedContent,stripped:!1}}function normalizeParsedItemMetadata(itemMetadata,options){return normalizeItemMetadata(itemMetadata,{...options,onWarning:void 0})}function parseJsonMarkdownItemDocument(content,runtimeContext,options={}){const normalized=stripLeadingYamlDocument(content);normalized.stripped&&options.onWarning?.("json_markdown_leading_yaml_frontmatter_ignored");const{frontMatter,body}=splitFrontMatter(normalized.content);frontMatter||(content.trimStart().startsWith("{")&&documentSyntaxError("json_markdown","JSON front matter is not valid JSON"),documentSyntaxError("json_markdown","JSON Markdown item document is missing JSON front matter"));let parsed;try{parsed=JSON.parse(frontMatter)}catch{documentSyntaxError("json_markdown","JSON front matter is not valid JSON")}return assertSupportedItemFormat(parsed,options.requireSupportedFormat),assertValidItemMetadata(parsed,runtimeContext),{metadata:normalizeParsedItemMetadata(parsed,options),body:normalizeBody(body)}}function parseToonItemDocument(content,runtimeContext,options={}){let parsed;try{parsed=decodeToonItemContent(content).value}catch{documentSyntaxError("toon","TOON item document is not valid TOON")}assertItemMetadataCondition(typeof parsed=="object"&&parsed!==null&&!Array.isArray(parsed),"TOON item document must be an object");const record=parsed;if(assertSupportedItemFormat(Object.prototype.hasOwnProperty.call(record,"front_matter")?record.front_matter:record,options.requireSupportedFormat),!REQUIRED_STRING_FIELDS.some(fieldName=>Object.prototype.hasOwnProperty.call(record,fieldName))&&!Object.prototype.hasOwnProperty.call(record,"front_matter")&&documentSyntaxError("toon","TOON input does not have the shape of an item document"),Object.prototype.hasOwnProperty.call(record,"front_matter"))return assertValidItemMetadata(record.front_matter,runtimeContext),assertItemMetadataCondition(record.body===void 0||typeof record.body=="string","TOON item document body must be a string"),{metadata:normalizeParsedItemMetadata(record.front_matter,options),body:normalizeBody(typeof record.body=="string"?record.body:"")};const{body,...itemMetadataRecord}=record;return assertItemMetadataCondition(body===void 0||typeof body=="string","TOON item document body must be a string"),assertValidItemMetadata(itemMetadataRecord,runtimeContext),{metadata:normalizeParsedItemMetadata(itemMetadataRecord,options),body:normalizeBody(typeof body=="string"?body:"")}}function serializeJsonMarkdownItemDocument(document,options={}){const normalizedMetadata=normalizeItemMetadata(document.metadata,options),orderedMetadata=orderItemMetadata(normalizedMetadata),serializedMetadata=JSON.stringify(orderedMetadata,null,2),normalizedBody=normalizeBody(document.body??"");return normalizedBody?`${serializedMetadata}
|
|
4
4
|
|
|
5
5
|
${normalizedBody}
|
|
6
6
|
`:`${serializedMetadata}
|
|
7
7
|
`}function serializeToonItemDocument(document,options={}){const normalizedMetadata=normalizeItemMetadata(document.metadata,options),orderedMetadata=orderItemMetadata(normalizedMetadata),normalizedBody=normalizeBody(document.body??""),payload={...orderedMetadata,body:normalizedBody},encodablePayload=JSON.parse(JSON.stringify(payload)),serialized=encodeToon(encodablePayload);let decoded;try{decoded=decodeToonItemContent(serialized).value}catch(error){throw new PmCliError("Refusing to persist a TOON item document that the configured decoder cannot read back.",EXIT_CODE.GENERIC_FAILURE,{code:"item_document_roundtrip_failed",reason:"encode_decode_error",format:"toon",required:"Upgrade or repair the TOON codec before retrying the mutation; no item bytes were written.",why:error instanceof Error?error.message:String(error)})}if(!isDeepStrictEqual(decoded,encodablePayload)){const decodedRecord=typeof decoded=="object"&&decoded!==null&&!Array.isArray(decoded)?decoded:{},field=Object.keys(encodablePayload).find(key=>!isDeepStrictEqual(encodablePayload[key],decodedRecord[key]))??"document";throw new PmCliError(`Refusing to persist a lossy TOON item document; round-trip mismatch at field "${field}".`,EXIT_CODE.GENERIC_FAILURE,{code:"item_document_roundtrip_failed",reason:"encode_decode_mismatch",format:"toon",field,required:"Upgrade or repair the TOON codec before retrying the mutation; no item bytes were written.",why:"The encoded document decoded to a value different from the canonical item payload."})}return`${serialized}
|
|
8
|
-
`}function parseItemDocument(content,options={}){const conflictMarker=findFirstMergeConflictMarker(content);if(conflictMarker)throw new PmCliError(`Merge conflict markers detected in item document at line ${conflictMarker.line} (${conflictMarker.marker}). Resolve <<<<<<< ======= >>>>>>> markers and retry.`,EXIT_CODE.GENERIC_FAILURE,{code:"merge_conflict_markers_detected",reason:"merge_conflict",required:"Resolve merge-conflict markers in the item file before parsing or mutation commands.",why:"Partially merged documents can corrupt item metadata and history integrity.",examples:["git status","git add <resolved-file> && git commit"],nextSteps:["Resolve conflicts, save the file, then rerun the pm command."]});const format=options.format??"toon",runtimeContext=resolveRuntimeSchemaValidationContext(options);return format==="toon"?parseToonItemDocument(content,runtimeContext,options):parseJsonMarkdownItemDocument(content,runtimeContext,options)}function serializeItemDocument(document,options={}){return(options.format??"toon")==="toon"?serializeToonItemDocument(document,options):serializeJsonMarkdownItemDocument(document,options)}function canonicalDocument(document,options={}){return{metadata:normalizeItemMetadata(document.metadata,options),body:normalizeBody(document.body??"")}}const _testOnlyItemFormat={buildKnownItemMetadataKeys,firstNonZeroComparison,normalizeBody,normalizePlanDecisions,normalizePlanDiscoveries,normalizePlanStepDocs,normalizePlanStepFiles,normalizePlanStepLinks,normalizePlanSteps,normalizePlanStepTests,normalizePlanValidation,normalizeTestRunSummaries,normalizeTypeOptions,runtimeFieldRequiredForType,sortTests};export{_testOnlyItemFormat,canonicalDocument,normalizeItemMetadata,parseItemDocument,serializeItemDocument,splitFrontMatter};
|
|
8
|
+
`}function parseItemDocument(content,options={}){const conflictMarker=findFirstMergeConflictMarker(content);if(conflictMarker)throw new PmCliError(`Merge conflict markers detected in item document at line ${conflictMarker.line} (${conflictMarker.marker}). Resolve <<<<<<< ======= >>>>>>> markers and retry.`,EXIT_CODE.GENERIC_FAILURE,{code:"merge_conflict_markers_detected",reason:"merge_conflict",required:"Resolve merge-conflict markers in the item file before parsing or mutation commands.",why:"Partially merged documents can corrupt item metadata and history integrity.",examples:["git status","git add <resolved-file> && git commit"],nextSteps:["Resolve conflicts, save the file, then rerun the pm command."]});const format=options.format??"toon",runtimeContext=resolveRuntimeSchemaValidationContext(options);return format==="toon"?parseToonItemDocument(content,runtimeContext,options):parseJsonMarkdownItemDocument(content,runtimeContext,options)}function serializeItemDocument(document,options={}){return(options.format??"toon")==="toon"?serializeToonItemDocument(document,options):serializeJsonMarkdownItemDocument(document,options)}function canonicalDocument(document,options={}){return{metadata:normalizeItemMetadata(document.metadata,options),body:normalizeBody(document.body??"")}}const _testOnlyItemFormat={buildKnownItemMetadataKeys,firstNonZeroComparison,normalizeBody,normalizePlanDecisions,normalizePlanDiscoveries,normalizePlanStepDocs,normalizePlanStepFiles,normalizePlanStepLinks,normalizePlanSteps,normalizePlanStepTests,normalizePlanValidation,normalizeTestRunSummaries,normalizeTypeOptions,runtimeFieldRequiredForType,sortTests};export{_testOnlyItemFormat,canonicalDocument,normalizeItemMetadata,normalizeLinkedTest,parseItemDocument,serializeItemDocument,splitFrontMatter};
|
|
9
9
|
//# sourceMappingURL=item-format.js.map
|
|
@@ -228,7 +228,7 @@ declare function buildOtelSpanRequest(activeCommand: ActiveTelemetryCommand, out
|
|
|
228
228
|
endpoint: string;
|
|
229
229
|
payload: unknown;
|
|
230
230
|
} | null;
|
|
231
|
-
/** Build a bounded
|
|
231
|
+
/** Build a bounded telemetry-only preview, omitting linked-test collections and undefined fields while preserving the caller's receipt. */
|
|
232
232
|
declare function summarizeResult(result: unknown, captureLevel?: Exclude<TelemetryCaptureLevel, "minimal">): Record<string, unknown>;
|
|
233
233
|
/** Build invocation metadata for the selected capture level; minimal capture retains digests while richer levels include sanitized inputs and hashed runtime context. */
|
|
234
234
|
declare function buildCommandStartPayload(params: {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
(function(){try{var e=typeof window<"u"?window:typeof global<"u"?global:typeof globalThis<"u"?globalThis:typeof self<"u"?self:{},n=new e.Error().stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="
|
|
1
|
+
(function(){try{var e=typeof window<"u"?window:typeof global<"u"?global:typeof globalThis<"u"?globalThis:typeof self<"u"?self:{},n=new e.Error().stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="4a63e054-a0e2-5826-976f-9076f6905388")}catch{}})();import crypto from"node:crypto";import{spawn}from"node:child_process";import{mkdirSync,rmSync,statSync}from"node:fs";import{mkdir,readdir,rm,stat}from"node:fs/promises";import{request as httpRequest}from"node:http";import{request as httpsRequest}from"node:https";import os from"node:os";import path from"node:path";import{acquireLock}from"../lock/lock.js";import{isActiveCommandUnextended,runWithIsolatedExtensionRuntime}from"../extensions/index.js";import{resolveAutomaticReadSampleRate}from"./read-sampling.js";import{PM_TELEMETRY_SOURCE_CONTEXT_VALUES,resolveTelemetryEnvironmentPolicy}from"./policy.js";import{resolvePmPackageRootFromModule}from"../packages/root.js";import{isFileMissingError,appendLineAtomic,readFileIfExists,writeFileAtomic}from"../fs/fs-utils.js";import{resolveTelemetryErrorCategory}from"../shared/constants.js";import{nowIso}from"../shared/time.js";import{detectAgentIdentity,readAuthorEnvironment}from"../shared/author.js";import{resolveGlobalPmRoot}from"../store/paths.js";import{isConfigurationOnlySettingsRead,readSettings,runWithConfigurationOnlySettings,writeSettings}from"../store/settings.js";import{clearSettingsReadCache}from"../store/settings-read-cache.js";import{deriveTelemetryCommandResolution,deriveTelemetryCommandTaxonomy,inferTelemetryErrorCode}from"./observability.js";const TELEMETRY_QUEUE_RELATIVE_PATH=path.join("runtime","telemetry","events.jsonl"),TELEMETRY_STATE_RELATIVE_PATH=path.join("runtime","telemetry","state.json"),TELEMETRY_OTEL_SPANS_RELATIVE_PATH=path.join("runtime","telemetry","otel-spans.jsonl"),TELEMETRY_OTEL_SPANS_FLUSH_BATCH_SIZE=8,TELEMETRY_OTEL_SPANS_MAX_PENDING=500,TELEMETRY_SCHEMA_VERSION=1,TELEMETRY_CLIENT_SCHEMA_VERSION=1,TELEMETRY_FLUSH_BATCH_SIZE=100,TELEMETRY_MAX_RETRY_DELAY_MS=36e5,TELEMETRY_RETRY_BASE_DELAY_MS=3e4,TELEMETRY_HTTP_TIMEOUT_DEFAULT_MS=2e4,TELEMETRY_HTTP_TIMEOUT_MIN_MS=1e3,TELEMETRY_HTTP_TIMEOUT_MAX_MS=25e3,TELEMETRY_FOREGROUND_LOCK_WAIT_MS=250,MILLISECONDS_PER_DAY=864e5,TELEMETRY_MAX_EVENT_BYTES=65536,TELEMETRY_SANITIZE_MAX_DEPTH=6,TELEMETRY_SANITIZE_MAX_ARRAY_ITEMS=20,TELEMETRY_MAX_QUEUE_ENTRY_ATTEMPTS=15,TELEMETRY_RESULT_PREVIEW_MAX_BYTES=8192,TELEMETRY_QUEUE_REWRITE_RETRY_DELAYS_MS=[25,50,100,200],TELEMETRY_QUEUE_TMP_ORPHAN_MAX_AGE_MS=3600*1e3,TELEMETRY_FLUSH_LOCK_STALE_MS=6e4,TELEMETRY_FLUSH_SPAWN_LOCK_STALE_MS=6e4,OTEL_TRACES_ENDPOINT_ENV="OTEL_EXPORTER_OTLP_TRACES_ENDPOINT",OTEL_BASE_ENDPOINT_ENV="OTEL_EXPORTER_OTLP_ENDPOINT",OTEL_SERVICE_NAME_ENV="OTEL_SERVICE_NAME",PM_TELEMETRY_OTEL_DISABLED_ENV="PM_TELEMETRY_OTEL_DISABLED",PM_TELEMETRY_OTEL_DISABLED_VALUES=new Set(["1","true","yes","on"]),PM_TELEMETRY_INLINE_FLUSH_ENV="PM_TELEMETRY_INLINE_FLUSH",PM_TELEMETRY_FLUSH_CHILD_ENV="PM_TELEMETRY_FLUSH_CHILD",PM_TELEMETRY_SOURCE_CONTEXT_ENV="PM_TELEMETRY_SOURCE_CONTEXT",PM_TELEMETRY_HTTP_TIMEOUT_MS_ENV="PM_TELEMETRY_HTTP_TIMEOUT_MS",NATIVE_FETCH=globalThis.fetch;import{PM_TELEMETRY_SOURCE_CONTEXT_VALUES as PM_TELEMETRY_SOURCE_CONTEXT_VALUES2}from"./policy.js";let sourceContextWarningEmitted=!1,_lastFlushPromise=Promise.resolve(),_queueMutationPromise=Promise.resolve();const deferredReadStarts=new WeakMap;async function waitForPendingFlush(){await _lastFlushPromise,await _queueMutationPromise}const PM_TELEMETRY_SOURCE_CONTEXT_SET=new Set(PM_TELEMETRY_SOURCE_CONTEXT_VALUES),BOOLEAN_TRUE_VALUES=new Set(["1","true","yes","on"]),TELEMETRY_HARNESSES=new Set(["claude-code","codex","pi","opencode","cursor","aider","gemini-cli","ci"]),PROCESS_SESSION_ID=crypto.randomUUID(),SENSITIVE_KEYWORDS=["token","secret","password","passwd","api_key","apikey","authorization","cookie","session","credentials","bearer"],REDACTION_INPUT_KEYS=new Set(["literal","literals","regex","regexes","replacement","replacements","setjson","set_json","update"]),SENSITIVE_INLINE_KEY_PATTERN="(?:token|secret|password|passwd|api[_-]?key|apikey|authorization|cookie|session|credentials|bearer|literals?|regex(?:es)?|replacements?)",INLINE_SENSITIVE_ASSIGNMENT_PATTERN=new RegExp(`\\b(${SENSITIVE_INLINE_KEY_PATTERN})\\s*([:=])\\s*([^\\s,;]+)`,"giu"),INLINE_SENSITIVE_FLAG_PATTERN=new RegExp(`(--${SENSITIVE_INLINE_KEY_PATTERN})(=|\\s+)([^\\s,;]+)`,"giu"),ABSOLUTE_PATH_TOKEN_PATTERN=/(^|[\s"'`(=])\/(?:[^\s"'`),;]+)/g,BEARER_TOKEN_PATTERN=/bearer\s+[a-z0-9._=-]+/giu,PRIVATE_IP_PATTERN=/\b(?:10\.(?:25[0-5]|2[0-4]\d|[01]?\d?\d)\.(?:25[0-5]|2[0-4]\d|[01]?\d?\d)\.(?:25[0-5]|2[0-4]\d|[01]?\d?\d)|172\.(?:1[6-9]|2\d|3[01])\.(?:25[0-5]|2[0-4]\d|[01]?\d?\d)\.(?:25[0-5]|2[0-4]\d|[01]?\d?\d)|192\.168\.(?:25[0-5]|2[0-4]\d|[01]?\d?\d)\.(?:25[0-5]|2[0-4]\d|[01]?\d?\d))\b/g;function queuePath(globalPmRoot){return path.join(globalPmRoot,TELEMETRY_QUEUE_RELATIVE_PATH)}function otelSpansQueuePath(globalPmRoot){return path.join(globalPmRoot,TELEMETRY_OTEL_SPANS_RELATIVE_PATH)}function telemetryRuntimeDirectory(globalPmRoot){return path.dirname(queuePath(globalPmRoot))}function runtimeStatePath(globalPmRoot){return path.join(globalPmRoot,TELEMETRY_STATE_RELATIVE_PATH)}function flushLockPath(globalPmRoot){return path.join(globalPmRoot,"runtime","telemetry","flush.lock")}function flushSpawnLockPath(globalPmRoot){return path.join(globalPmRoot,"runtime","telemetry","flush.spawn.lock")}function telemetryFlushRunnerPath(){return path.join(resolvePmPackageRootFromModule(import.meta.url,["../../.."]),"dist","cli","telemetry-flush.js")}function shouldFlushInline(){if(parseBooleanTrueLike(process.env[PM_TELEMETRY_INLINE_FLUSH_ENV])||parseBooleanTrueLike(process.env[PM_TELEMETRY_FLUSH_CHILD_ENV]))return!0;const nodeEnv=(process.env.NODE_ENV??"").trim().toLowerCase();return typeof process.env.VITEST=="string"||typeof process.env.VITEST_WORKER_ID=="string"||nodeEnv==="test"}async function readRuntimeState(globalPmRoot){const raw=await readFileIfExists(runtimeStatePath(globalPmRoot));if(!raw||raw.trim().length===0)return{};try{const parsed=JSON.parse(raw);return typeof parsed!="object"||parsed===null||Array.isArray(parsed)?{}:parsed}catch{return{}}}async function writeRuntimeState(globalPmRoot,patch){try{await withQueueMutation(async()=>{const next={...await readRuntimeState(globalPmRoot),...patch},normalized=Object.fromEntries(Object.entries(next).filter(([,value])=>value!==void 0).sort((left,right)=>left[0].localeCompare(right[0])));await writeFileAtomic(runtimeStatePath(globalPmRoot),`${JSON.stringify(normalized,null,2)}
|
|
2
2
|
`,{createParentDirectories:!parseBooleanTrueLike(process.env[PM_TELEMETRY_FLUSH_CHILD_ENV])})},globalPmRoot)}catch{}}function isSensitiveKey(key){const normalized=key.trim().toLowerCase().replaceAll("-","_").replaceAll(/[^a-z0-9_]+/g,"_"),tokens=normalized.split("_").filter(token=>token.length>0);return REDACTION_INPUT_KEYS.has(normalized)||SENSITIVE_KEYWORDS.some(keyword=>normalized===keyword||normalized.endsWith(`_${keyword}`)||tokens.includes(keyword))}function redactInlineSensitiveAssignments(input){return input.replaceAll(INLINE_SENSITIVE_ASSIGNMENT_PATTERN,(_match,key,delimiter)=>`${key}${delimiter}[redacted]`).replaceAll(INLINE_SENSITIVE_FLAG_PATTERN,(_match,flag,delimiter)=>`${flag}${delimiter}[redacted]`)}function redactAbsolutePathTokens(input){return input.replaceAll(ABSOLUTE_PATH_TOKEN_PATTERN,(_match,prefix)=>`${prefix}[redacted_path]`)}function isAsciiLetterOrDigitCode(code){const isUpper=code>=65&&code<=90,isLower=code>=97&&code<=122,isDigit=code>=48&&code<=57;return isUpper||isLower||isDigit}function isEmailLocalCharacter(character){if(!character)return!1;const code=character.charCodeAt(0);return isAsciiLetterOrDigitCode(code)?!0:character==="."||character==="_"||character==="%"||character==="+"||character==="-"}function isEmailDomainCharacter(character){if(!character)return!1;const code=character.charCodeAt(0);return isAsciiLetterOrDigitCode(code)?!0:character==="."||character==="-"}function looksLikeEmailToken(token){const atIndex=token.indexOf("@");if(atIndex<=0||atIndex!==token.lastIndexOf("@")||atIndex===token.length-1)return!1;const localPart=token.slice(0,atIndex),domainPart=token.slice(atIndex+1),lastDot=domainPart.lastIndexOf(".");if(lastDot<=0||lastDot===domainPart.length-1||domainPart.length-(lastDot+1)<2)return!1;for(const character of localPart)if(!isEmailLocalCharacter(character))return!1;for(const character of domainPart)if(!isEmailDomainCharacter(character))return!1;return!0}function redactEmailTokens(input){let redacted="",cursor=0;for(;cursor<input.length;){const atIndex=input.indexOf("@",cursor);if(atIndex<0){redacted+=input.slice(cursor);break}let start=atIndex-1;for(;start>=cursor&&isEmailLocalCharacter(input[start]);)start-=1;start+=1;let end=atIndex+1;for(;end<input.length&&isEmailDomainCharacter(input[end]);)end+=1;const candidate=input.slice(start,end);if(looksLikeEmailToken(candidate)){redacted+=input.slice(cursor,start),redacted+="[redacted_email]",cursor=end;continue}redacted+=input.slice(cursor,atIndex+1),cursor=atIndex+1}return redacted}function sanitizeCommonSensitiveTokens(input){return redactEmailTokens(input).replaceAll(BEARER_TOKEN_PATTERN,"bearer [redacted_token]").replaceAll(PRIVATE_IP_PATTERN,"[redacted_ip]")}function sanitizeStringRedacted(input){const withoutCommonSensitiveTokens=sanitizeCommonSensitiveTokens(input),withoutInlineSecrets=redactInlineSensitiveAssignments(withoutCommonSensitiveTokens),withoutAbsolutePaths=redactAbsolutePathTokens(withoutInlineSecrets),trimmed=withoutAbsolutePaths.trim();return trimmed.startsWith("/")&&trimmed.length>1?"[redacted_path]":withoutAbsolutePaths.length>512?`${withoutAbsolutePaths.slice(0,509)}...`:withoutAbsolutePaths}function sanitizeStringMax(input){const withoutCommonSensitiveTokens=sanitizeCommonSensitiveTokens(input),withoutInlineSecrets=redactInlineSensitiveAssignments(withoutCommonSensitiveTokens),withoutAbsolutePaths=redactAbsolutePathTokens(withoutInlineSecrets),trimmed=withoutAbsolutePaths.trim();return trimmed.startsWith("/")&&trimmed.length>1?"[redacted_path]":withoutAbsolutePaths.length>2048?`${withoutAbsolutePaths.slice(0,2045)}...`:withoutAbsolutePaths}function sanitizeString(input,captureLevel="redacted"){return captureLevel==="max"?sanitizeStringMax(input):sanitizeStringRedacted(input)}function sanitizeValue(value,keyHint,captureLevel="redacted",depth=0){if(keyHint&&isSensitiveKey(keyHint))return"[redacted]";if(value==null)return value;if(typeof value=="string")return sanitizeString(value,captureLevel);if(typeof value=="number"||typeof value=="boolean")return value;if(depth>=TELEMETRY_SANITIZE_MAX_DEPTH)return"[depth_truncated]";if(Array.isArray(value))return value.slice(0,TELEMETRY_SANITIZE_MAX_ARRAY_ITEMS).map(entry=>sanitizeValue(entry,void 0,captureLevel,depth+1));if(typeof value=="object"){const record=value,sanitized={};for(const[key,nested]of Object.entries(record))sanitized[key]=sanitizeValue(nested,key,captureLevel,depth+1);return sanitized}return String(value)}function sanitizeCommandArgs(args,captureLevel="redacted"){const sanitized=[];let nextIsSensitiveValue=!1;for(const rawArg of args){const arg=sanitizeString(rawArg,captureLevel);if(nextIsSensitiveValue){sanitized.push("[redacted]"),nextIsSensitiveValue=!1;continue}if(arg.startsWith("--")){const withoutPrefix=arg.slice(2),delimiterIndex=withoutPrefix.indexOf("=");if(delimiterIndex>=0){const key=withoutPrefix.slice(0,delimiterIndex),value=withoutPrefix.slice(delimiterIndex+1);isSensitiveKey(key)?sanitized.push(`--${key}=[redacted]`):sanitized.push(`--${key}=${sanitizeString(value,captureLevel)}`);continue}if(isSensitiveKey(withoutPrefix)){sanitized.push(`--${withoutPrefix}`),nextIsSensitiveValue=!0;continue}}sanitized.push(arg)}return sanitized}function normalizeCaptureLevel(value){const normalized=value?.trim().toLowerCase();return normalized==="minimal"||normalized==="redacted"||normalized==="max"?normalized:"redacted"}function parseBooleanTrueLike(value){return BOOLEAN_TRUE_VALUES.has((value??"").trim().toLowerCase())}function resolveTelemetryHttpTimeoutMs(){const rawConfigured=process.env[PM_TELEMETRY_HTTP_TIMEOUT_MS_ENV]?.trim();if(!rawConfigured)return TELEMETRY_HTTP_TIMEOUT_DEFAULT_MS;const configured=Number(rawConfigured);return Number.isFinite(configured)?Math.min(TELEMETRY_HTTP_TIMEOUT_MAX_MS,Math.max(TELEMETRY_HTTP_TIMEOUT_MIN_MS,Math.trunc(configured))):TELEMETRY_HTTP_TIMEOUT_DEFAULT_MS}async function postTelemetryJson(endpoint,headers,body){const url=new URL(endpoint);if(globalThis.fetch!==NATIVE_FETCH){const controller=new AbortController;let timeout;const deadline=new Promise((_resolve,reject)=>{timeout=setTimeout(()=>{controller.abort(),reject(new Error("telemetry_http_timeout"))},resolveTelemetryHttpTimeoutMs())});try{return(await Promise.race([globalThis.fetch(endpoint,{method:"POST",headers,body,signal:controller.signal}),deadline])).status}finally{clearTimeout(timeout)}}const request=url.protocol==="https:"?httpsRequest:url.protocol==="http:"?httpRequest:void 0;if(!request)throw new Error(`telemetry_http_protocol_unsupported_${url.protocol}`);return new Promise((resolve,reject)=>{const timeoutMs=resolveTelemetryHttpTimeoutMs(),requestOptions={method:"POST",headers:{...headers,"content-length":Buffer.byteLength(body)},signal:AbortSignal.timeout(timeoutMs),autoSelectFamilyAttemptTimeout:Math.min(5e3,timeoutMs)},clientRequest=request(url,requestOptions,response=>{response.once("error",reject),response.once("end",()=>resolve(response.statusCode)),response.resume()});clientRequest.once("error",reject),clientRequest.end(body)})}function normalizePmVersion(value){const trimmed=typeof value=="string"?value.trim():"";return trimmed.length>0?trimmed:"0.0.0"}function normalizeTelemetryErrorCode(value){const normalized=value?.trim();return normalized&&normalized.length>0?normalized:void 0}function normalizeTelemetryExitCode(exitCode,ok){return Number.isFinite(exitCode)?Math.max(0,Math.trunc(exitCode)):ok?0:1}function normalizeTelemetryErrorCategory(params){if(!params.ok)return typeof params.errorCategory=="string"&¶ms.errorCategory.trim().length>0?params.errorCategory:typeof params.errorCode=="string"&¶ms.errorCode.trim().length>0?resolveTelemetryErrorCategory(params.errorCode):"unknown"}function resolveTelemetrySourceContext(globalOptions){const override=(process.env[PM_TELEMETRY_SOURCE_CONTEXT_ENV]??"").trim().toLowerCase();if(PM_TELEMETRY_SOURCE_CONTEXT_SET.has(override))return{source_context:override,source_context_source:"env_override"};const source=override.length>0?"env_override_rejected":"inferred";return source==="env_override_rejected"&&!sourceContextWarningEmitted&&!globalOptions.quiet&&(sourceContextWarningEmitted=!0,process.stderr.write(`[pm] warning: PM_TELEMETRY_SOURCE_CONTEXT is not one of ${PM_TELEMETRY_SOURCE_CONTEXT_VALUES.join("|")}; ignoring override.
|
|
3
|
-
`)),{source_context:inferTelemetrySourceContext(globalOptions),source_context_source:source}}function inferTelemetrySourceContext(globalOptions){const nodeEnv=(process.env.NODE_ENV??"").trim().toLowerCase();if(typeof process.env.VITEST=="string"||typeof process.env.VITEST_WORKER_ID=="string"||nodeEnv==="test")return"test";const nonTty=process.stdin.isTTY!==!0||process.stdout.isTTY!==!0,ci=parseBooleanTrueLike(process.env.CI),scriptLikeMode=globalOptions.json===!0||globalOptions.quiet===!0;return nonTty||ci||scriptLikeMode?"automation":"user"}function hashWithInstallationId(installationId,value){return crypto.createHash("sha256").update(`${installationId}:${value}`).digest("hex")}function buildAuthorContextPayloadFields(captureLevel,installationId){const author=(readAuthorEnvironment()??"").trim(),agent=detectAgentIdentity({env:process.env,argv:process.argv}),hasAgentContext=agent.harness!==void 0||agent.model!==void 0;return captureLevel==="minimal"?{has_author_context:author.length>0,has_agent_context:hasAgentContext}:{agent_harness:agent.harness?TELEMETRY_HARNESSES.has(agent.harness)?agent.harness:"other":"none",ci:parseBooleanTrueLike(process.env.CI),...author.length>0?{author_context_hash:hashWithInstallationId(installationId,author)}:{},...agent.harness?{agent_harness_hash:hashWithInstallationId(installationId,agent.harness)}:{},...agent.model?{agent_model_hash:hashWithInstallationId(installationId,agent.model)}:{},...agent.model_source?{agent_model_source:agent.model_source}:{}}}function normalizeForHash(value,depth=0){if(value==null||typeof value=="string"||typeof value=="number"||typeof value=="boolean")return value;if(depth>=TELEMETRY_SANITIZE_MAX_DEPTH)return"[depth_truncated]";if(Array.isArray(value))return value.slice(0,TELEMETRY_SANITIZE_MAX_ARRAY_ITEMS).map(entry=>normalizeForHash(entry,depth+1));if(typeof value=="object"){const record=value,normalized={},sortedKeys=Object.keys(record).sort((left,right)=>left.localeCompare(right));for(const key of sortedKeys)normalized[key]=normalizeForHash(record[key],depth+1);return normalized}return String(value)}function hashTelemetryValue(installationId,value){return hashWithInstallationId(installationId,JSON.stringify(normalizeForHash(value)))}function hashCommandArgs(installationId,args){return{hashes:args.map(arg=>hashWithInstallationId(installationId,arg)),digest:hashWithInstallationId(installationId,args.join("\0"))}}function hashTelemetryErrorFingerprint(installationId,command,errorCode2,errorMessage){const normalizedMessage=sanitizeString(typeof errorMessage=="string"?errorMessage:"","redacted"),normalizedCode=typeof errorCode2=="string"&&errorCode2.length>0?errorCode2:"unknown_error",fingerprintSource=`${command}\0${normalizedCode}\0${normalizedMessage}`;return hashWithInstallationId(installationId,fingerprintSource)}function resolveOtelTracesEndpoint(){if(PM_TELEMETRY_OTEL_DISABLED_VALUES.has((process.env[PM_TELEMETRY_OTEL_DISABLED_ENV]??"").trim().toLowerCase()))return null;const directEndpoint=(process.env[OTEL_TRACES_ENDPOINT_ENV]??"").trim();if(directEndpoint.length>0)try{return new URL(directEndpoint).toString()}catch{return null}const baseEndpoint=(process.env[OTEL_BASE_ENDPOINT_ENV]??"").trim();if(baseEndpoint.length===0)return null;try{const url=new URL(baseEndpoint),normalizedPath=url.pathname.replace(/\/+$/,"");return normalizedPath.endsWith("/v1/traces")||(url.pathname=`${normalizedPath.length===0?"":normalizedPath}/v1/traces`),url.toString()}catch{return null}}function isoToUnixNano(iso){const parsedMs=Date.parse(iso),epochMs=Number.isNaN(parsedMs)?Date.now():parsedMs;return`${BigInt(epochMs)*1000000n}`}function otlpStringAttribute(key,value){return{key,value:{stringValue:value}}}function otlpBoolAttribute(key,value){return{key,value:{boolValue:value}}}function otlpIntAttribute(key,value){return{key,value:{intValue:String(Math.max(0,Math.trunc(value)))}}}function buildOtelSpanRequest(activeCommand,outcome,finishedAtIso,durationMs,sampleRate=1){const endpoint=activeCommand.otel_traces_endpoint;if(typeof endpoint!="string"||endpoint.trim().length===0||[activeCommand.otel_trace_id,activeCommand.otel_span_id].some(id=>typeof id!="string"||id.length===0))return null;const serviceNameCandidate=sanitizeString((process.env[OTEL_SERVICE_NAME_ENV]??"").trim()),serviceName=serviceNameCandidate.length>0?serviceNameCandidate:"pm-cli",normalizedExitCode=normalizeTelemetryExitCode(outcome.exit_code,outcome.ok),normalizedErrorCode=normalizeTelemetryErrorCode(outcome.error_code),normalizedErrorCategory=normalizeTelemetryErrorCategory({ok:outcome.ok,errorCode:normalizedErrorCode,errorCategory:outcome.error_category}),attributes=[otlpStringAttribute("pm.command",sanitizeString(activeCommand.command)),otlpStringAttribute("pm.version",activeCommand.pm_version),otlpStringAttribute("pm.source_context",activeCommand.source_context),otlpStringAttribute("pm.source_context_source",activeCommand.source_context_source),otlpStringAttribute("pm.installation_id",activeCommand.installation_id),otlpStringAttribute("pm.session_id",PROCESS_SESSION_ID),otlpStringAttribute("pm.pm_root_hash",activeCommand.pm_root_hash),otlpStringAttribute("pm.cwd_hash",activeCommand.cwd_hash),otlpBoolAttribute("pm.ok",outcome.ok),otlpIntAttribute("pm.exit_code",normalizedExitCode),otlpIntAttribute("pm.duration_ms",durationMs),otlpStringAttribute("pm.sample_rate",String(sampleRate))];typeof normalizedErrorCode=="string"&&attributes.push(otlpStringAttribute("pm.error_code",normalizedErrorCode)),typeof normalizedErrorCategory=="string"&&attributes.push(otlpStringAttribute("pm.error_category",normalizedErrorCategory)),typeof outcome.error=="string"&&outcome.error.trim().length>0&&attributes.push(otlpStringAttribute("pm.error",sanitizeString(outcome.error)));const payload={resourceSpans:[{resource:{attributes:[otlpStringAttribute("service.name",serviceName)]},scopeSpans:[{scope:{name:"pm-cli.telemetry",version:"1"},spans:[{traceId:activeCommand.otel_trace_id,spanId:activeCommand.otel_span_id,name:`pm.command.${sanitizeString(activeCommand.command)}`,kind:1,startTimeUnixNano:isoToUnixNano(activeCommand.started_at),endTimeUnixNano:isoToUnixNano(finishedAtIso),attributes,status:{code:outcome.ok?1:2,message:outcome.ok?"":sanitizeString(typeof outcome.error=="string"?outcome.error:"command_failed")}}]}]}]};return{endpoint,payload}}function summarizeResult(result,captureLevel="redacted"){if(result==null)return{type:"nullish"};if(typeof result=="string")return{type:"string",value:sanitizeString(result,captureLevel)};if(typeof result=="number"||typeof result=="boolean")return{type:typeof result,value:result};if(Array.isArray(result))return{type:"array",length:result.length,sample:result.slice(0,5).map(entry=>sanitizeValue(entry,void 0,captureLevel))};if(typeof result=="object"){const record=result,keys=Object.keys(record).sort((left,right)=>left.localeCompare(right)),sanitized={};let previewBytes=0;for(const key of keys.slice(0,25)){const sanitizedValue=sanitizeValue(record[key],key,captureLevel);if(sanitizedValue===void 0)continue;const entrySize=JSON.stringify(sanitizedValue).length;if(previewBytes+entrySize>TELEMETRY_RESULT_PREVIEW_MAX_BYTES){sanitized[key]="[preview_truncated]";break}previewBytes+=entrySize,sanitized[key]=sanitizedValue}return{type:"object",key_count:keys.length,keys_preview:keys.slice(0,50),preview:sanitized}}return{type:typeof result,value:String(result)}}function buildCommandStartPayload(params){const{captureLevel,context,pmVersion,sourceContext,pmRootHash,cwdHash,installationId}=params,commandTaxonomy=deriveTelemetryCommandTaxonomy(context.command),hashedArgs=hashCommandArgs(installationId,context.args),commandInvocationDigest=hashWithInstallationId(installationId,`${context.command}\0${context.args.join("\0")}`),commandOptionsDigest=hashTelemetryValue(installationId,context.options),globalOptionsDigest=hashTelemetryValue(installationId,context.global),authorContextFields=buildAuthorContextPayloadFields(captureLevel,installationId);return captureLevel==="minimal"?{capture_level:captureLevel,pm_version:pmVersion,source_context:sourceContext.source_context,source_context_source:sourceContext.source_context_source,command_taxonomy:commandTaxonomy,command_args_digest:hashedArgs.digest,command_invocation_digest:commandInvocationDigest,command_options_digest:commandOptionsDigest,global_options_digest:globalOptionsDigest,...authorContextFields}:{pm_version:pmVersion,source_context:sourceContext.source_context,source_context_source:sourceContext.source_context_source,command_taxonomy:commandTaxonomy,...authorContextFields,command_args:sanitizeCommandArgs(context.args,captureLevel),command_args_hashes:hashedArgs.hashes,command_args_digest:hashedArgs.digest,command_invocation_digest:commandInvocationDigest,command_options:sanitizeValue(context.options,void 0,captureLevel),command_options_digest:commandOptionsDigest,global_options:sanitizeValue(context.global,void 0,captureLevel),global_options_digest:globalOptionsDigest,pm_root_hash:pmRootHash,cwd_hash:cwdHash,capture_level:captureLevel,runtime:{node:process.version,platform:process.platform,arch:process.arch,hostname_hash:hashWithInstallationId(installationId,os.hostname()),stdin_tty:process.stdin.isTTY===!0,stdout_tty:process.stdout.isTTY===!0}}}function buildCommandFinishPayload(params){const{captureLevel,pmVersion,sourceContext,outcome,durationMs,startedAt,command,installationId,commandTaxonomy,exitCode,errorCode:errorCode2,errorCategory,commandResolution,resolutionStage}=params,errorFingerprint=outcome.ok===!1?hashTelemetryErrorFingerprint(installationId,command,errorCode2,outcome.error):void 0,authorContextFields=buildAuthorContextPayloadFields(captureLevel,installationId);return captureLevel==="minimal"?{capture_level:captureLevel,pm_version:pmVersion,source_context:sourceContext.source_context,source_context_source:sourceContext.source_context_source,command_taxonomy:commandTaxonomy,command_resolution:commandResolution,resolution_stage:resolutionStage,ok:outcome.ok,exit_code:exitCode,error_code:errorCode2,error_category:errorCategory,error:outcome.error?sanitizeString(outcome.error,"redacted"):void 0,error_fingerprint:errorFingerprint,duration_ms:durationMs,...authorContextFields}:{capture_level:captureLevel,pm_version:pmVersion,source_context:sourceContext.source_context,source_context_source:sourceContext.source_context_source,command_taxonomy:commandTaxonomy,command_resolution:commandResolution,resolution_stage:resolutionStage,...authorContextFields,ok:outcome.ok,exit_code:exitCode,error_code:errorCode2,error_category:errorCategory,error:outcome.error?sanitizeString(outcome.error,captureLevel):void 0,error_fingerprint:errorFingerprint,duration_ms:durationMs,started_at:startedAt,result_summary:summarizeResult(outcome.result,captureLevel)}}function buildCommandErrorPayload(params){const{captureLevel,pmVersion,sourceContext,command,commandTaxonomy,commandResolution,resolutionStage,args,options,pmRootHash,cwdHash,installationId,errorCode:errorCode2,errorMessage,errorCategory,exitCode}=params,attemptedArgHashes=hashCommandArgs(installationId,args),attemptedCommandDigest=hashWithInstallationId(installationId,command),attemptedOptionsDigest=hashTelemetryValue(installationId,options),errorFingerprint=hashTelemetryErrorFingerprint(installationId,command,errorCode2,errorMessage),authorContextFields=buildAuthorContextPayloadFields(captureLevel,installationId);return captureLevel==="minimal"?{capture_level:captureLevel,pm_version:pmVersion,source_context:sourceContext.source_context,source_context_source:sourceContext.source_context_source,command_taxonomy:commandTaxonomy,command_resolution:commandResolution,resolution_stage:resolutionStage,attempted_command_digest:attemptedCommandDigest,attempted_args_digest:attemptedArgHashes.digest,attempted_options_digest:attemptedOptionsDigest,error_code:errorCode2,error_category:errorCategory,exit_code:exitCode,error:sanitizeString(errorMessage,"redacted"),error_fingerprint:errorFingerprint,...authorContextFields}:{capture_level:captureLevel,pm_version:pmVersion,source_context:sourceContext.source_context,source_context_source:sourceContext.source_context_source,command_taxonomy:commandTaxonomy,command_resolution:commandResolution,resolution_stage:resolutionStage,...authorContextFields,attempted_command:sanitizeString(command,captureLevel),attempted_command_digest:attemptedCommandDigest,attempted_args:sanitizeCommandArgs(args,captureLevel),attempted_args_hashes:attemptedArgHashes.hashes,attempted_args_digest:attemptedArgHashes.digest,attempted_options:sanitizeValue(options,void 0,captureLevel),attempted_options_digest:attemptedOptionsDigest,error_code:errorCode2,error_category:errorCategory,exit_code:exitCode,error:sanitizeString(errorMessage,captureLevel),error_fingerprint:errorFingerprint,pm_root_hash:pmRootHash,cwd_hash:cwdHash,runtime:{node:process.version,platform:process.platform,arch:process.arch,hostname_hash:hashWithInstallationId(installationId,os.hostname()),stdin_tty:process.stdin.isTTY===!0,stdout_tty:process.stdout.isTTY===!0}}}async function ensureInstallationId(globalPmRoot){const settings=await readSettings(globalPmRoot);if(!settings.telemetry.enabled||resolveTelemetryEnvironmentPolicy().telemetry_disabled)return null;if(settings.telemetry.installation_id.trim().length===0){if(isConfigurationOnlySettingsRead(globalPmRoot))return null;settings.telemetry.installation_id=crypto.randomUUID(),await writeSettings(globalPmRoot,settings,"telemetry:install_id")}return{installationId:settings.telemetry.installation_id,endpoint:settings.telemetry.endpoint,retentionDays:Math.max(1,Math.trunc(settings.telemetry.retention_days)),captureLevel:normalizeCaptureLevel(settings.telemetry.capture_level)}}async function appendTelemetryEvent(globalPmRoot,event){let serialized=JSON.stringify({client_schema_version:TELEMETRY_CLIENT_SCHEMA_VERSION,event,attempts:0});if(serialized.length>TELEMETRY_MAX_EVENT_BYTES){const trimmed={...event,payload:{...event.payload,result_summary:{truncated:!0,reason:"payload_size_exceeded",original_bytes:serialized.length}}};serialized=JSON.stringify({client_schema_version:TELEMETRY_CLIENT_SCHEMA_VERSION,event:trimmed,attempts:0})}await appendLineAtomic(queuePath(globalPmRoot),serialized)}async function enqueueTelemetryEvent(globalPmRoot,event){await withQueueMutation(()=>appendTelemetryEvent(globalPmRoot,event),globalPmRoot)}function parseQueueLines(raw){const entries=[];for(const line of raw.split(`
|
|
3
|
+
`)),{source_context:inferTelemetrySourceContext(globalOptions),source_context_source:source}}function inferTelemetrySourceContext(globalOptions){const nodeEnv=(process.env.NODE_ENV??"").trim().toLowerCase();if(typeof process.env.VITEST=="string"||typeof process.env.VITEST_WORKER_ID=="string"||nodeEnv==="test")return"test";const nonTty=process.stdin.isTTY!==!0||process.stdout.isTTY!==!0,ci=parseBooleanTrueLike(process.env.CI),scriptLikeMode=globalOptions.json===!0||globalOptions.quiet===!0;return nonTty||ci||scriptLikeMode?"automation":"user"}function hashWithInstallationId(installationId,value){return crypto.createHash("sha256").update(`${installationId}:${value}`).digest("hex")}function buildAuthorContextPayloadFields(captureLevel,installationId){const author=(readAuthorEnvironment()??"").trim(),agent=detectAgentIdentity({env:process.env,argv:process.argv}),hasAgentContext=agent.harness!==void 0||agent.model!==void 0;return captureLevel==="minimal"?{has_author_context:author.length>0,has_agent_context:hasAgentContext}:{agent_harness:agent.harness?TELEMETRY_HARNESSES.has(agent.harness)?agent.harness:"other":"none",ci:parseBooleanTrueLike(process.env.CI),...author.length>0?{author_context_hash:hashWithInstallationId(installationId,author)}:{},...agent.harness?{agent_harness_hash:hashWithInstallationId(installationId,agent.harness)}:{},...agent.model?{agent_model_hash:hashWithInstallationId(installationId,agent.model)}:{},...agent.model_source?{agent_model_source:agent.model_source}:{}}}function normalizeForHash(value,depth=0){if(value==null||typeof value=="string"||typeof value=="number"||typeof value=="boolean")return value;if(depth>=TELEMETRY_SANITIZE_MAX_DEPTH)return"[depth_truncated]";if(Array.isArray(value))return value.slice(0,TELEMETRY_SANITIZE_MAX_ARRAY_ITEMS).map(entry=>normalizeForHash(entry,depth+1));if(typeof value=="object"){const record=value,normalized={},sortedKeys=Object.keys(record).sort((left,right)=>left.localeCompare(right));for(const key of sortedKeys)normalized[key]=normalizeForHash(record[key],depth+1);return normalized}return String(value)}function hashTelemetryValue(installationId,value){return hashWithInstallationId(installationId,JSON.stringify(normalizeForHash(value)))}function hashCommandArgs(installationId,args){return{hashes:args.map(arg=>hashWithInstallationId(installationId,arg)),digest:hashWithInstallationId(installationId,args.join("\0"))}}function hashTelemetryErrorFingerprint(installationId,command,errorCode2,errorMessage){const normalizedMessage=sanitizeString(typeof errorMessage=="string"?errorMessage:"","redacted"),normalizedCode=typeof errorCode2=="string"&&errorCode2.length>0?errorCode2:"unknown_error",fingerprintSource=`${command}\0${normalizedCode}\0${normalizedMessage}`;return hashWithInstallationId(installationId,fingerprintSource)}function resolveOtelTracesEndpoint(){if(PM_TELEMETRY_OTEL_DISABLED_VALUES.has((process.env[PM_TELEMETRY_OTEL_DISABLED_ENV]??"").trim().toLowerCase()))return null;const directEndpoint=(process.env[OTEL_TRACES_ENDPOINT_ENV]??"").trim();if(directEndpoint.length>0)try{return new URL(directEndpoint).toString()}catch{return null}const baseEndpoint=(process.env[OTEL_BASE_ENDPOINT_ENV]??"").trim();if(baseEndpoint.length===0)return null;try{const url=new URL(baseEndpoint),normalizedPath=url.pathname.replace(/\/+$/,"");return normalizedPath.endsWith("/v1/traces")||(url.pathname=`${normalizedPath.length===0?"":normalizedPath}/v1/traces`),url.toString()}catch{return null}}function isoToUnixNano(iso){const parsedMs=Date.parse(iso),epochMs=Number.isNaN(parsedMs)?Date.now():parsedMs;return`${BigInt(epochMs)*1000000n}`}function otlpStringAttribute(key,value){return{key,value:{stringValue:value}}}function otlpBoolAttribute(key,value){return{key,value:{boolValue:value}}}function otlpIntAttribute(key,value){return{key,value:{intValue:String(Math.max(0,Math.trunc(value)))}}}function buildOtelSpanRequest(activeCommand,outcome,finishedAtIso,durationMs,sampleRate=1){const endpoint=activeCommand.otel_traces_endpoint;if(typeof endpoint!="string"||endpoint.trim().length===0||[activeCommand.otel_trace_id,activeCommand.otel_span_id].some(id=>typeof id!="string"||id.length===0))return null;const serviceNameCandidate=sanitizeString((process.env[OTEL_SERVICE_NAME_ENV]??"").trim()),serviceName=serviceNameCandidate.length>0?serviceNameCandidate:"pm-cli",normalizedExitCode=normalizeTelemetryExitCode(outcome.exit_code,outcome.ok),normalizedErrorCode=normalizeTelemetryErrorCode(outcome.error_code),normalizedErrorCategory=normalizeTelemetryErrorCategory({ok:outcome.ok,errorCode:normalizedErrorCode,errorCategory:outcome.error_category}),attributes=[otlpStringAttribute("pm.command",sanitizeString(activeCommand.command)),otlpStringAttribute("pm.version",activeCommand.pm_version),otlpStringAttribute("pm.source_context",activeCommand.source_context),otlpStringAttribute("pm.source_context_source",activeCommand.source_context_source),otlpStringAttribute("pm.installation_id",activeCommand.installation_id),otlpStringAttribute("pm.session_id",PROCESS_SESSION_ID),otlpStringAttribute("pm.pm_root_hash",activeCommand.pm_root_hash),otlpStringAttribute("pm.cwd_hash",activeCommand.cwd_hash),otlpBoolAttribute("pm.ok",outcome.ok),otlpIntAttribute("pm.exit_code",normalizedExitCode),otlpIntAttribute("pm.duration_ms",durationMs),otlpStringAttribute("pm.sample_rate",String(sampleRate))];typeof normalizedErrorCode=="string"&&attributes.push(otlpStringAttribute("pm.error_code",normalizedErrorCode)),typeof normalizedErrorCategory=="string"&&attributes.push(otlpStringAttribute("pm.error_category",normalizedErrorCategory)),typeof outcome.error=="string"&&outcome.error.trim().length>0&&attributes.push(otlpStringAttribute("pm.error",sanitizeString(outcome.error)));const payload={resourceSpans:[{resource:{attributes:[otlpStringAttribute("service.name",serviceName)]},scopeSpans:[{scope:{name:"pm-cli.telemetry",version:"1"},spans:[{traceId:activeCommand.otel_trace_id,spanId:activeCommand.otel_span_id,name:`pm.command.${sanitizeString(activeCommand.command)}`,kind:1,startTimeUnixNano:isoToUnixNano(activeCommand.started_at),endTimeUnixNano:isoToUnixNano(finishedAtIso),attributes,status:{code:outcome.ok?1:2,message:outcome.ok?"":sanitizeString(typeof outcome.error=="string"?outcome.error:"command_failed")}}]}]}]};return{endpoint,payload}}function summarizeResult(result,captureLevel="redacted"){if(result==null)return{type:"nullish"};if(typeof result=="string")return{type:"string",value:sanitizeString(result,captureLevel)};if(typeof result=="number"||typeof result=="boolean")return{type:typeof result,value:result};if(Array.isArray(result))return{type:"array",length:result.length,sample:result.slice(0,5).map(entry=>sanitizeValue(entry,void 0,captureLevel))};if(typeof result=="object"){const record=result,keys=Object.keys(record).filter(key=>key!=="tests").sort((left,right)=>left.localeCompare(right)),sanitized={};let previewBytes=0;for(const key of keys.slice(0,25)){const sanitizedValue=sanitizeValue(record[key],key,captureLevel);if(sanitizedValue===void 0)continue;const entrySize=JSON.stringify(sanitizedValue).length;if(previewBytes+entrySize>TELEMETRY_RESULT_PREVIEW_MAX_BYTES){sanitized[key]="[preview_truncated]";break}previewBytes+=entrySize,sanitized[key]=sanitizedValue}return{type:"object",key_count:keys.length,keys_preview:keys.slice(0,50),preview:sanitized}}return{type:typeof result,value:String(result)}}function buildCommandStartPayload(params){const{captureLevel,context,pmVersion,sourceContext,pmRootHash,cwdHash,installationId}=params,commandTaxonomy=deriveTelemetryCommandTaxonomy(context.command),hashedArgs=hashCommandArgs(installationId,context.args),commandInvocationDigest=hashWithInstallationId(installationId,`${context.command}\0${context.args.join("\0")}`),commandOptionsDigest=hashTelemetryValue(installationId,context.options),globalOptionsDigest=hashTelemetryValue(installationId,context.global),authorContextFields=buildAuthorContextPayloadFields(captureLevel,installationId);return captureLevel==="minimal"?{capture_level:captureLevel,pm_version:pmVersion,source_context:sourceContext.source_context,source_context_source:sourceContext.source_context_source,command_taxonomy:commandTaxonomy,command_args_digest:hashedArgs.digest,command_invocation_digest:commandInvocationDigest,command_options_digest:commandOptionsDigest,global_options_digest:globalOptionsDigest,...authorContextFields}:{pm_version:pmVersion,source_context:sourceContext.source_context,source_context_source:sourceContext.source_context_source,command_taxonomy:commandTaxonomy,...authorContextFields,command_args:sanitizeCommandArgs(context.args,captureLevel),command_args_hashes:hashedArgs.hashes,command_args_digest:hashedArgs.digest,command_invocation_digest:commandInvocationDigest,command_options:sanitizeValue(context.options,void 0,captureLevel),command_options_digest:commandOptionsDigest,global_options:sanitizeValue(context.global,void 0,captureLevel),global_options_digest:globalOptionsDigest,pm_root_hash:pmRootHash,cwd_hash:cwdHash,capture_level:captureLevel,runtime:{node:process.version,platform:process.platform,arch:process.arch,hostname_hash:hashWithInstallationId(installationId,os.hostname()),stdin_tty:process.stdin.isTTY===!0,stdout_tty:process.stdout.isTTY===!0}}}function buildCommandFinishPayload(params){const{captureLevel,pmVersion,sourceContext,outcome,durationMs,startedAt,command,installationId,commandTaxonomy,exitCode,errorCode:errorCode2,errorCategory,commandResolution,resolutionStage}=params,errorFingerprint=outcome.ok===!1?hashTelemetryErrorFingerprint(installationId,command,errorCode2,outcome.error):void 0,authorContextFields=buildAuthorContextPayloadFields(captureLevel,installationId);return captureLevel==="minimal"?{capture_level:captureLevel,pm_version:pmVersion,source_context:sourceContext.source_context,source_context_source:sourceContext.source_context_source,command_taxonomy:commandTaxonomy,command_resolution:commandResolution,resolution_stage:resolutionStage,ok:outcome.ok,exit_code:exitCode,error_code:errorCode2,error_category:errorCategory,error:outcome.error?sanitizeString(outcome.error,"redacted"):void 0,error_fingerprint:errorFingerprint,duration_ms:durationMs,...authorContextFields}:{capture_level:captureLevel,pm_version:pmVersion,source_context:sourceContext.source_context,source_context_source:sourceContext.source_context_source,command_taxonomy:commandTaxonomy,command_resolution:commandResolution,resolution_stage:resolutionStage,...authorContextFields,ok:outcome.ok,exit_code:exitCode,error_code:errorCode2,error_category:errorCategory,error:outcome.error?sanitizeString(outcome.error,captureLevel):void 0,error_fingerprint:errorFingerprint,duration_ms:durationMs,started_at:startedAt,result_summary:summarizeResult(outcome.result,captureLevel)}}function buildCommandErrorPayload(params){const{captureLevel,pmVersion,sourceContext,command,commandTaxonomy,commandResolution,resolutionStage,args,options,pmRootHash,cwdHash,installationId,errorCode:errorCode2,errorMessage,errorCategory,exitCode}=params,attemptedArgHashes=hashCommandArgs(installationId,args),attemptedCommandDigest=hashWithInstallationId(installationId,command),attemptedOptionsDigest=hashTelemetryValue(installationId,options),errorFingerprint=hashTelemetryErrorFingerprint(installationId,command,errorCode2,errorMessage),authorContextFields=buildAuthorContextPayloadFields(captureLevel,installationId);return captureLevel==="minimal"?{capture_level:captureLevel,pm_version:pmVersion,source_context:sourceContext.source_context,source_context_source:sourceContext.source_context_source,command_taxonomy:commandTaxonomy,command_resolution:commandResolution,resolution_stage:resolutionStage,attempted_command_digest:attemptedCommandDigest,attempted_args_digest:attemptedArgHashes.digest,attempted_options_digest:attemptedOptionsDigest,error_code:errorCode2,error_category:errorCategory,exit_code:exitCode,error:sanitizeString(errorMessage,"redacted"),error_fingerprint:errorFingerprint,...authorContextFields}:{capture_level:captureLevel,pm_version:pmVersion,source_context:sourceContext.source_context,source_context_source:sourceContext.source_context_source,command_taxonomy:commandTaxonomy,command_resolution:commandResolution,resolution_stage:resolutionStage,...authorContextFields,attempted_command:sanitizeString(command,captureLevel),attempted_command_digest:attemptedCommandDigest,attempted_args:sanitizeCommandArgs(args,captureLevel),attempted_args_hashes:attemptedArgHashes.hashes,attempted_args_digest:attemptedArgHashes.digest,attempted_options:sanitizeValue(options,void 0,captureLevel),attempted_options_digest:attemptedOptionsDigest,error_code:errorCode2,error_category:errorCategory,exit_code:exitCode,error:sanitizeString(errorMessage,captureLevel),error_fingerprint:errorFingerprint,pm_root_hash:pmRootHash,cwd_hash:cwdHash,runtime:{node:process.version,platform:process.platform,arch:process.arch,hostname_hash:hashWithInstallationId(installationId,os.hostname()),stdin_tty:process.stdin.isTTY===!0,stdout_tty:process.stdout.isTTY===!0}}}async function ensureInstallationId(globalPmRoot){const settings=await readSettings(globalPmRoot);if(!settings.telemetry.enabled||resolveTelemetryEnvironmentPolicy().telemetry_disabled)return null;if(settings.telemetry.installation_id.trim().length===0){if(isConfigurationOnlySettingsRead(globalPmRoot))return null;settings.telemetry.installation_id=crypto.randomUUID(),await writeSettings(globalPmRoot,settings,"telemetry:install_id")}return{installationId:settings.telemetry.installation_id,endpoint:settings.telemetry.endpoint,retentionDays:Math.max(1,Math.trunc(settings.telemetry.retention_days)),captureLevel:normalizeCaptureLevel(settings.telemetry.capture_level)}}async function appendTelemetryEvent(globalPmRoot,event){let serialized=JSON.stringify({client_schema_version:TELEMETRY_CLIENT_SCHEMA_VERSION,event,attempts:0});if(serialized.length>TELEMETRY_MAX_EVENT_BYTES){const trimmed={...event,payload:{...event.payload,result_summary:{truncated:!0,reason:"payload_size_exceeded",original_bytes:serialized.length}}};serialized=JSON.stringify({client_schema_version:TELEMETRY_CLIENT_SCHEMA_VERSION,event:trimmed,attempts:0})}await appendLineAtomic(queuePath(globalPmRoot),serialized)}async function enqueueTelemetryEvent(globalPmRoot,event){await withQueueMutation(()=>appendTelemetryEvent(globalPmRoot,event),globalPmRoot)}function parseQueueLines(raw){const entries=[];for(const line of raw.split(`
|
|
4
4
|
`)){const trimmed=line.trim();if(trimmed.length!==0)try{const parsed=JSON.parse(trimmed);parsed&&typeof parsed=="object"&&parsed.event&&typeof parsed.event=="object"&&typeof parsed.attempts=="number"&&(parsed.client_schema_version===void 0||typeof parsed.client_schema_version=="number"&&Number.isFinite(parsed.client_schema_version))&&entries.push(parsed)}catch{}}return entries}function nextRetryIso(attempts){const delay=Math.min(TELEMETRY_RETRY_BASE_DELAY_MS*2**Math.max(attempts-1,0),TELEMETRY_MAX_RETRY_DELAY_MS);return new Date(Date.now()+delay).toISOString()}function isDueForRetryAt(nextAttemptAfter){if(typeof nextAttemptAfter!="string"||nextAttemptAfter.trim().length===0)return!0;const dueAtMs=Date.parse(nextAttemptAfter);return Number.isNaN(dueAtMs)?!0:dueAtMs<=Date.now()}function isDueForRetry(entry){return isDueForRetryAt(entry.next_attempt_after)}function retentionCutoffMs(retentionDays){const normalizedDays=Number.isFinite(retentionDays)?Math.max(1,Math.trunc(retentionDays)):1;return Date.now()-normalizedDays*MILLISECONDS_PER_DAY}function isExpiredQueueEntry(entry,cutoffMs){const occurredAt=entry.event?.occurred_at;if(typeof occurredAt!="string"||occurredAt.trim().length===0)return!1;const occurredAtMs=Date.parse(occurredAt);return Number.isNaN(occurredAtMs)?!1:occurredAtMs<cutoffMs}function pruneExpiredQueueEntries(entries,retentionDays){const cutoffMs=retentionCutoffMs(retentionDays),retained=[];let prunedCount=0;for(const entry of entries){if(JSON.stringify(entry).length>TELEMETRY_MAX_EVENT_BYTES||isExpiredQueueEntry(entry,cutoffMs)||entry.attempts>=TELEMETRY_MAX_QUEUE_ENTRY_ATTEMPTS){prunedCount+=1;continue}retained.push(entry)}return{entries:retained,prunedCount}}async function rewriteQueue(globalPmRoot,entries){const serialized=entries.map(entry=>JSON.stringify(entry)).join(`
|
|
5
5
|
`),contents=serialized.length>0?`${serialized}
|
|
6
6
|
`:"";for(let attempt=0;;attempt+=1)try{await writeFileAtomic(queuePath(globalPmRoot),contents,{createParentDirectories:!parseBooleanTrueLike(process.env[PM_TELEMETRY_FLUSH_CHILD_ENV])});return}catch(error){const retryDelay=TELEMETRY_QUEUE_REWRITE_RETRY_DELAYS_MS[attempt];if(retryDelay===void 0||!isRetryableQueueRewriteError(error))throw error;await sleep(retryDelay)}}function isRetryableQueueRewriteError(error){if(typeof error!="object"||error===null||!("code"in error))return!1;const code=error.code;return code==="EACCES"||code==="EBUSY"||code==="EPERM"}function sleep(milliseconds){return new Promise(resolve=>{setTimeout(resolve,milliseconds)})}async function appendPendingOtelSpan(globalPmRoot,request){const pending={id:crypto.randomUUID(),endpoint:request.endpoint,payload:request.payload,enqueued_at:nowIso(),attempts:0},serialized=JSON.stringify(pending);serialized.length>TELEMETRY_MAX_EVENT_BYTES||await appendLineAtomic(otelSpansQueuePath(globalPmRoot),serialized)}async function enqueuePendingOtelSpan(globalPmRoot,request){await withQueueMutation(()=>appendPendingOtelSpan(globalPmRoot,request),globalPmRoot)}function backfillPendingOtelSpanId(entry){const material=JSON.stringify([entry.endpoint,entry.enqueued_at,entry.payload]);return crypto.createHash("sha256").update(material).digest("hex").slice(0,32)}function parsePendingOtelSpanLines(raw){const entries=[];for(const line of raw.split(`
|
|
@@ -228,6 +228,7 @@ declare function readLinuxCpuSeconds(pid: number): Promise<{
|
|
|
228
228
|
cpu_system_seconds?: number;
|
|
229
229
|
}>;
|
|
230
230
|
declare function buildResourceSnapshot(record: BackgroundTestRunRecord): Promise<BackgroundRunResourceSnapshot | undefined>;
|
|
231
|
+
/** Derive worker totals from aggregate or per-test receipts, counting refusals as unsuccessful runs. */
|
|
231
232
|
declare function evaluateWorkerResult(kind: BackgroundTestRunKind, payload: unknown): WorkerEvaluationResult;
|
|
232
233
|
declare function parseProgressLine(stderrLine: string): Partial<BackgroundRunProgress> | null;
|
|
233
234
|
declare function splitLines(value: string): string[];
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
(function(){try{var e=typeof window<"u"?window:typeof global<"u"?global:typeof globalThis<"u"?globalThis:typeof self<"u"?self:{},n=new e.Error().stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="
|
|
2
|
-
`)}async function readBackgroundTestRunRecord(pmRoot,runId){const recordPath=getTestRunRecordPath(pmRoot,runId),raw=await readFileIfExists(recordPath);return raw?parseBackgroundRunRecord(raw,recordPath):null}async function listBackgroundRunRecordPaths(pmRoot){const recordsRoot=getTestRunsRecordsPath(pmRoot);return await pathExists(recordsRoot)?(await fs.readdir(recordsRoot,{withFileTypes:!0})).filter(entry=>entry.isFile()&&entry.name.endsWith(".json")).map(entry=>path.join(recordsRoot,entry.name)).sort((left,right)=>right.localeCompare(left)):[]}function parseLinuxRssStatus(raw){const match=raw.match(/^VmRSS:\s+(\d+)\s+kB$/m);if(match)return Number.parseInt(match[1],10)*1024}async function readLinuxRssBytes(pid){if(process.platform==="linux")try{const raw=await fs.readFile(`/proc/${pid}/status`,"utf8");return parseLinuxRssStatus(raw)}catch{return}}function parseLinuxCpuStat(raw){const closeParenIndex=raw.lastIndexOf(")");if(closeParenIndex<0)return{};const parts=raw.slice(closeParenIndex+2).trim().split(/\s+/),utimeTicks=Number.parseInt(parts[11],10),stimeTicks=Number.parseInt(parts[12],10);return!Number.isFinite(utimeTicks)||!Number.isFinite(stimeTicks)?{}:{cpu_user_seconds:utimeTicks/PROC_STAT_TICKS_PER_SECOND,cpu_system_seconds:stimeTicks/PROC_STAT_TICKS_PER_SECOND}}async function readLinuxCpuSeconds(pid){if(process.platform!=="linux")return{};try{const raw=await fs.readFile(`/proc/${pid}/stat`,"utf8");return parseLinuxCpuStat(raw)}catch{return{}}}async function buildResourceSnapshot(record){const pid=record.child_pid??record.worker_pid;if(!isPidRunning(pid))return;const rssBytes=await readLinuxRssBytes(pid),cpu=await readLinuxCpuSeconds(pid),startedAtMs=record.started_at?Date.parse(record.started_at):Number.NaN,uptimeSeconds=Number.isFinite(startedAtMs)?Math.max(0,(nowMs()-startedAtMs)/1e3):void 0;return{recorded_at:nowIso(),rss_bytes:rssBytes,cpu_user_seconds:cpu.cpu_user_seconds,cpu_system_seconds:cpu.cpu_system_seconds,uptime_seconds:uptimeSeconds}}function readCount(value){return typeof value=="number"&&Number.isFinite(value)?value:0}function evaluateWorkerResult(kind,payload){if(!payload||typeof payload!="object")return{summary:{passed:0,failed:1,skipped:0},parsedResult:null};const record=payload;if(kind==="test-all"){const totals=record.totals??{};return{summary:{items:typeof totals.items=="number"?totals.items:void 0,linked_tests:typeof totals.linked_tests=="number"?totals.linked_tests:void 0,passed:readCount(totals.passed),failed:readCount(totals.failed),skipped:readCount(totals.skipped),fail_on_skipped_triggered:record.fail_on_skipped_triggered===!0?!0:void 0},parsedResult:payload}}const runResults=Array.isArray(record.run_results)?record.run_results:[];let passed=0,failed=0,skipped=0;for(const entry of runResults){if(!entry||typeof entry!="object")continue;const status=entry.status;if(status==="passed"){passed+=1;continue}if(status==="failed"){failed+=1;continue}skipped+=1}return{summary:{passed,failed,skipped,fail_on_skipped_triggered:record.fail_on_skipped_triggered===!0?!0:void 0},parsedResult:payload}}function parseProgressLine(stderrLine){const line=stderrLine.trim();if(line.length===0)return null;const testAllMatch=line.match(/\[pm test-all\]\s+item\s+(\d+)\/(\d+)\s+(start|end)\s+id=([^\s]+)/i);if(testAllMatch){const itemIndex=Number.parseInt(testAllMatch[1],10),itemTotal=Number.parseInt(testAllMatch[2],10);return{item_index:itemIndex,item_total:itemTotal,item_id:testAllMatch[4],linked_test_index:void 0,linked_test_total:void 0,current_command:void 0,elapsed_ms:void 0,heartbeat_at:nowIso(),phase:testAllMatch[3]?.toLowerCase()==="end"?"finished":"running"}}const linkedTestMatch=line.match(/\[pm test\]\s+linked-test\s+(\d+)\/(\d+)\s+(start|running|end)(?:.*elapsed_ms=(\d+))?/i);if(!linkedTestMatch)return null;const index=Number.parseInt(linkedTestMatch[1],10),total=Number.parseInt(linkedTestMatch[2],10),elapsed=linkedTestMatch[4]?Number.parseInt(linkedTestMatch[4],10):void 0,commandMatch=line.match(/\scommand="((?:\\.|[^"\\])*)"/),currentCommand=commandMatch?commandMatch[1].replaceAll('\\"','"').replaceAll("\\\\","\\"):void 0;return{linked_test_index:index,linked_test_total:total,current_command:currentCommand,elapsed_ms:Number.isFinite(elapsed)?elapsed:void 0,heartbeat_at:nowIso(),phase:linkedTestMatch[3]?.toLowerCase()==="end"?"finished":"running"}}function splitLines(value){return value.split(/\r?\n/).map(entry=>entry.trimEnd()).filter(entry=>entry.length>0)}function tailLines(value,limit){if(limit<=0)return[];const lines=splitLines(value);return lines.length<=limit?lines:lines.slice(lines.length-limit)}async function resolveBackgroundCliEntry(cwd){const configured=process.env.PM_BACKGROUND_CLI_ENTRY?.trim();if(configured&&configured.length>0){const resolvedConfigured=path.resolve(cwd,configured);if(await pathExists(resolvedConfigured))return resolvedConfigured}const distEntry=path.resolve(cwd,"dist","cli.js");if(await pathExists(distEntry))return distEntry;const argvEntry=process.argv[1]?.trim();if(argvEntry&&argvEntry.length>0){const resolvedArgvEntry=path.resolve(cwd,argvEntry);if(await pathExists(resolvedArgvEntry))return resolvedArgvEntry}throw new PmCliError("Unable to resolve a CLI entrypoint for background test runs. Build the project or set PM_BACKGROUND_CLI_ENTRY.",EXIT_CODE.GENERIC_FAILURE)}async function refreshRunIfStale(pmRoot,record){if(!BACKGROUND_RUN_ACTIVE_STATUSES.has(record.status)||isPidRunning(record.worker_pid)||record.finished_at)return record;const next={...record,status:"failed",finished_at:nowIso(),error:record.error??"Background test run worker exited before writing terminal status."};return await writeBackgroundRunRecord(pmRoot,next),next}async function startBackgroundTestRun(options){await ensureBackgroundRunStorage(options.pmRoot);const normalizedArgs=normalizeCommandArgs(options.commandArgs);if(normalizedArgs.length===0)throw new PmCliError("Background test run requires command arguments.",EXIT_CODE.USAGE);const fingerprint=buildBackgroundTestRunFingerprint(options.kind,normalizedArgs,options.pmRoot),existingRuns=await listBackgroundTestRuns(options.pmRoot,{});for(const existing of existingRuns){const refreshed=await refreshRunIfStale(options.pmRoot,existing);if(refreshed.fingerprint===fingerprint&&!(!BACKGROUND_RUN_ACTIVE_STATUSES.has(refreshed.status)||!isPidRunning(refreshed.worker_pid)))return{started:!1,run:{...refreshed,duplicate_of:refreshed.id},duplicate_of:refreshed.id}}const runId=buildRunId(),createdAt=nowIso(),stdoutPath=getTestRunStdoutPath(options.pmRoot,runId),stderrPath=getTestRunStderrPath(options.pmRoot,runId),resultPath=getTestRunResultPath(options.pmRoot,runId);await fs.writeFile(stdoutPath,"","utf8"),await fs.writeFile(stderrPath,"","utf8");const record={id:runId,kind:options.kind,status:"queued",created_at:createdAt,updated_at:createdAt,requested_by:options.requestedBy,fingerprint,command_args:normalizedArgs,command_label:summarizeCommandLabel(normalizedArgs),pm_root:options.pmRoot,global_pm_root:options.globalPmRoot,target_id:options.targetId,status_filter:options.statusFilter,attempt:typeof options.attempt=="number"&&Number.isFinite(options.attempt)&&options.attempt>=1?Math.floor(options.attempt):1,resumed_from:options.resumedFrom,resumed_by:options.resumedBy,stdout_path:stdoutPath,stderr_path:stderrPath,result_path:resultPath,progress:{phase:"queued",message:"Queued for background execution.",heartbeat_at:createdAt}};return await writeBackgroundRunRecord(options.pmRoot,record),{started:!0,run:record}}async function spawnBackgroundTestRunWorker(options){const record=await readBackgroundTestRunRecord(options.pmRoot,options.runId);if(!record)throw new PmCliError(`Background test run ${options.runId} not found`,EXIT_CODE.NOT_FOUND);if(BACKGROUND_RUN_TERMINAL_STATUSES.has(record.status))throw new PmCliError(`Background test run ${record.id} is already terminal (${record.status}).`,EXIT_CODE.CONFLICT);const cliEntry=await resolveBackgroundCliEntry(process.cwd()),args=[];options.noExtensions===!0&&args.push("--no-extensions"),args.push("--path",record.pm_root,"test-runs-worker",record.id);const env={...process.env,PM_PATH:record.pm_root,PM_GLOBAL_PATH:record.global_pm_root,PM_BACKGROUND_TEST_RUN_ID:record.id,PM_BACKGROUND_TEST_RUN_ATTEMPT:String(record.attempt),PM_BACKGROUND_TEST_RUN_RESUMED_FROM:record.resumed_from??"",FORCE_COLOR:"0"},child=spawn(process.execPath,[cliEntry,...args],{cwd:process.cwd(),env,detached:!0,stdio:"ignore",windowsHide:!0});child.unref();const next={...record,status:"queued",worker_pid:child.pid,progress:{phase:"queued",message:"Worker process started.",heartbeat_at:nowIso()}};return await writeBackgroundRunRecord(options.pmRoot,next),next}async function appendFileOrdered(queue,filePath,text){await queue,await fs.appendFile(filePath,text,"utf8")}function resolvePositiveEnvInteger(name,fallback){const parsed=Number.parseInt(process.env[name]??"",10);return Number.isFinite(parsed)&&parsed>0?parsed:fallback}function buildBackgroundWorkerEnv(record){return{...process.env,PM_PATH:record.pm_root,PM_GLOBAL_PATH:record.global_pm_root,FORCE_COLOR:"0"}}function buildBackgroundWorkerChildArgs(record,noExtensions){return noExtensions?["--no-extensions",...record.command_args]:[...record.command_args]}function createBackgroundRunRecordWriteScheduler(pmRoot,record){let writeQueue=Promise.resolve();return{schedule(){writeQueue=writeQueue.then(async()=>{try{await writeBackgroundRunRecord(pmRoot,record)}catch{}})},async flush(){await writeQueue}}}function requestChildSignal(child,signal){try{child.kill(signal)}catch{}}async function requestBackgroundWorkerStop(record,child,state,scheduleRecordWrite){state.stopRequested||(state.stopRequested=!0,record.stop_requested_at=nowIso(),record.progress={...record.progress,phase:"stopping",message:"Stop requested for background run.",heartbeat_at:nowIso()},scheduleRecordWrite(),requestChildSignal(child,"SIGTERM"),setTimeout(()=>requestChildSignal(child,"SIGKILL"),resolvePositiveEnvInteger("PM_BACKGROUND_RUN_FORCE_KILL_DELAY_MS",DEFAULT_BACKGROUND_RUN_FORCE_KILL_DELAY_MS)).unref?.())}function buildItemBackgroundProgress(line,progressPatch,phase){return{phase,message:line,heartbeat_at:nowIso(),item_index:progressPatch.item_index,item_total:progressPatch.item_total,item_id:progressPatch.item_id,linked_test_index:void 0,linked_test_total:void 0,current_command:void 0,elapsed_ms:void 0}}function buildLinkedTestBackgroundProgress(line,record,progressPatch,phase){return{phase,message:line,heartbeat_at:nowIso(),item_index:progressPatch.item_index??record.progress?.item_index,item_total:progressPatch.item_total??record.progress?.item_total,item_id:progressPatch.item_id??record.progress?.item_id,linked_test_index:progressPatch.linked_test_index,linked_test_total:progressPatch.linked_test_total,current_command:progressPatch.current_command,elapsed_ms:progressPatch.elapsed_ms}}function applyBackgroundWorkerProgressLine(record,line,stopRequested){const progressPatch=parseProgressLine(line);if(!progressPatch)return!1;const phase=record.progress?.phase==="stopping"?"stopping":"running";return record.progress=progressPatch.item_id?buildItemBackgroundProgress(line,progressPatch,phase):buildLinkedTestBackgroundProgress(line,record,progressPatch,phase),progressPatch.phase==="finished"&&!stopRequested&&(record.progress.phase="running"),!0}function parseBackgroundWorkerStdout(stdoutBuffer){if(stdoutBuffer.trim().length===0)return null;try{return JSON.parse(stdoutBuffer)}catch{return null}}function resolveBackgroundWorkerFinalStatus(record,stopRequested){return stopRequested?"stopped":record.exit_code===0&&(record.summary?.failed??0)===0&&record.summary?.fail_on_skipped_triggered!==!0?"passed":"failed"}function buildFinishedBackgroundProgress(record,stopRequested){return{phase:"finished",message:stopRequested?"Background run stopped.":`Background run finished with status=${record.status}.`,heartbeat_at:nowIso(),item_index:record.progress?.item_index,item_total:record.progress?.item_total,item_id:record.progress?.item_id,linked_test_index:record.progress?.linked_test_index,linked_test_total:record.progress?.linked_test_total,current_command:record.progress?.current_command,elapsed_ms:record.progress?.elapsed_ms}}async function writeBackgroundWorkerResult(record,parsedResult,stdoutBuffer){if(parsedResult!==null){await writeFileAtomic(record.result_path,`${JSON.stringify(parsedResult,null,2)}
|
|
1
|
+
(function(){try{var e=typeof window<"u"?window:typeof global<"u"?global:typeof globalThis<"u"?globalThis:typeof self<"u"?self:{},n=new e.Error().stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="7c89b3de-0b7c-529b-a655-87d3e83bd46c")}catch{}})();import fs from"node:fs/promises";import{spawn}from"node:child_process";import{createHash,randomBytes}from"node:crypto";import path from"node:path";import{ensureDir,pathExists,readFileIfExists,writeFileAtomic}from"../fs/fs-utils.js";import{PmCliError}from"../shared/errors.js";import{EXIT_CODE}from"../shared/constants.js";import{nowIso}from"../shared/time.js";import{getTestRunRecordPath,getTestRunResultPath,getTestRunStderrPath,getTestRunStdoutPath,getTestRunsPath,getTestRunsRecordsPath,getTestRunsResultsPath,getTestRunsStderrPath,getTestRunsStdoutPath}from"../store/paths.js";const BACKGROUND_RUN_ACTIVE_STATUSES=new Set(["queued","running"]),BACKGROUND_RUN_TERMINAL_STATUSES=new Set(["passed","failed","stopped","canceled"]),DEFAULT_BACKGROUND_RUN_RESOURCE_SNAPSHOT_INTERVAL_MS=3e3,DEFAULT_BACKGROUND_RUN_FORCE_KILL_DELAY_MS=3e3,DEFAULT_BACKGROUND_RUN_HEARTBEAT_STALE_MS=3e4,DEFAULT_BACKGROUND_RUN_LOG_TAIL_LINES=100,PROC_STAT_TICKS_PER_SECOND=100;function nowMs(){return Date.now()}function normalizeCommandArgs(args){return args.map(entry=>entry.trim()).filter(entry=>entry.length>0)}function summarizeCommandLabel(args){const joined=args.join(" ").replaceAll(/\s+/g," ").trim();return joined.length<=180?joined:`${joined.slice(0,177)}...`}function buildRunId(){const timePart=Date.now().toString(36),randomPart=randomBytes(3).toString("hex");return`tr-${timePart}-${randomPart}`}function buildBackgroundTestRunFingerprint(kind,commandArgs,pmRoot){const payload={kind,command:normalizeCommandArgs(commandArgs),pm_root:path.resolve(pmRoot)};return createHash("sha256").update(JSON.stringify(payload)).digest("hex")}function isPidRunning(pid){if(!Number.isInteger(pid)||!pid||pid<=0)return!1;try{return process.kill(pid,0),!0}catch(error){return(typeof error=="object"&&error!==null&&"code"in error?error.code:void 0)==="EPERM"}}async function ensureBackgroundRunStorage(pmRoot){await ensureDir(getTestRunsPath(pmRoot)),await ensureDir(getTestRunsRecordsPath(pmRoot)),await ensureDir(getTestRunsStdoutPath(pmRoot)),await ensureDir(getTestRunsStderrPath(pmRoot)),await ensureDir(getTestRunsResultsPath(pmRoot))}async function parseBackgroundRunRecord(raw,recordPath){try{const parsed=JSON.parse(raw);if(!parsed||typeof parsed!="object"||typeof parsed.id!="string")throw new Error("invalid run record payload");return parsed}catch(error){throw new PmCliError(`Failed to parse background test run record at ${recordPath}: ${error instanceof Error?error.message:String(error)}`,EXIT_CODE.GENERIC_FAILURE)}}async function writeBackgroundRunRecord(pmRoot,record){const next={...record,updated_at:nowIso()};await writeFileAtomic(getTestRunRecordPath(pmRoot,record.id),`${JSON.stringify(next,null,2)}
|
|
2
|
+
`)}async function readBackgroundTestRunRecord(pmRoot,runId){const recordPath=getTestRunRecordPath(pmRoot,runId),raw=await readFileIfExists(recordPath);return raw?parseBackgroundRunRecord(raw,recordPath):null}async function listBackgroundRunRecordPaths(pmRoot){const recordsRoot=getTestRunsRecordsPath(pmRoot);return await pathExists(recordsRoot)?(await fs.readdir(recordsRoot,{withFileTypes:!0})).filter(entry=>entry.isFile()&&entry.name.endsWith(".json")).map(entry=>path.join(recordsRoot,entry.name)).sort((left,right)=>right.localeCompare(left)):[]}function parseLinuxRssStatus(raw){const match=raw.match(/^VmRSS:\s+(\d+)\s+kB$/m);if(match)return Number.parseInt(match[1],10)*1024}async function readLinuxRssBytes(pid){if(process.platform==="linux")try{const raw=await fs.readFile(`/proc/${pid}/status`,"utf8");return parseLinuxRssStatus(raw)}catch{return}}function parseLinuxCpuStat(raw){const closeParenIndex=raw.lastIndexOf(")");if(closeParenIndex<0)return{};const parts=raw.slice(closeParenIndex+2).trim().split(/\s+/),utimeTicks=Number.parseInt(parts[11],10),stimeTicks=Number.parseInt(parts[12],10);return!Number.isFinite(utimeTicks)||!Number.isFinite(stimeTicks)?{}:{cpu_user_seconds:utimeTicks/PROC_STAT_TICKS_PER_SECOND,cpu_system_seconds:stimeTicks/PROC_STAT_TICKS_PER_SECOND}}async function readLinuxCpuSeconds(pid){if(process.platform!=="linux")return{};try{const raw=await fs.readFile(`/proc/${pid}/stat`,"utf8");return parseLinuxCpuStat(raw)}catch{return{}}}async function buildResourceSnapshot(record){const pid=record.child_pid??record.worker_pid;if(!isPidRunning(pid))return;const rssBytes=await readLinuxRssBytes(pid),cpu=await readLinuxCpuSeconds(pid),startedAtMs=record.started_at?Date.parse(record.started_at):Number.NaN,uptimeSeconds=Number.isFinite(startedAtMs)?Math.max(0,(nowMs()-startedAtMs)/1e3):void 0;return{recorded_at:nowIso(),rss_bytes:rssBytes,cpu_user_seconds:cpu.cpu_user_seconds,cpu_system_seconds:cpu.cpu_system_seconds,uptime_seconds:uptimeSeconds}}function readCount(value){return typeof value=="number"&&Number.isFinite(value)?value:0}function evaluateWorkerResult(kind,payload){if(!payload||typeof payload!="object")return{summary:{passed:0,failed:1,skipped:0},parsedResult:null};const record=payload;if(kind==="test-all"){const totals=record.totals??{};return{summary:{items:typeof totals.items=="number"?totals.items:void 0,linked_tests:typeof totals.linked_tests=="number"?totals.linked_tests:void 0,passed:readCount(totals.passed),failed:readCount(totals.failed),skipped:readCount(totals.skipped),fail_on_skipped_triggered:record.fail_on_skipped_triggered===!0?!0:void 0},parsedResult:payload}}const runResults=Array.isArray(record.run_results)?record.run_results:[];let passed=0,failed=0,skipped=0;for(const entry of runResults){if(!entry||typeof entry!="object")continue;const status=entry.status;if(status==="passed"){passed+=1;continue}if(status==="failed"||status==="refused"){failed+=1;continue}skipped+=1}return{summary:{passed,failed,skipped,fail_on_skipped_triggered:record.fail_on_skipped_triggered===!0?!0:void 0},parsedResult:payload}}function parseProgressLine(stderrLine){const line=stderrLine.trim();if(line.length===0)return null;const testAllMatch=line.match(/\[pm test-all\]\s+item\s+(\d+)\/(\d+)\s+(start|end)\s+id=([^\s]+)/i);if(testAllMatch){const itemIndex=Number.parseInt(testAllMatch[1],10),itemTotal=Number.parseInt(testAllMatch[2],10);return{item_index:itemIndex,item_total:itemTotal,item_id:testAllMatch[4],linked_test_index:void 0,linked_test_total:void 0,current_command:void 0,elapsed_ms:void 0,heartbeat_at:nowIso(),phase:testAllMatch[3]?.toLowerCase()==="end"?"finished":"running"}}const linkedTestMatch=line.match(/\[pm test\]\s+linked-test\s+(\d+)\/(\d+)\s+(start|running|end)(?:.*elapsed_ms=(\d+))?/i);if(!linkedTestMatch)return null;const index=Number.parseInt(linkedTestMatch[1],10),total=Number.parseInt(linkedTestMatch[2],10),elapsed=linkedTestMatch[4]?Number.parseInt(linkedTestMatch[4],10):void 0,commandMatch=line.match(/\scommand="((?:\\.|[^"\\])*)"/),currentCommand=commandMatch?commandMatch[1].replaceAll('\\"','"').replaceAll("\\\\","\\"):void 0;return{linked_test_index:index,linked_test_total:total,current_command:currentCommand,elapsed_ms:Number.isFinite(elapsed)?elapsed:void 0,heartbeat_at:nowIso(),phase:linkedTestMatch[3]?.toLowerCase()==="end"?"finished":"running"}}function splitLines(value){return value.split(/\r?\n/).map(entry=>entry.trimEnd()).filter(entry=>entry.length>0)}function tailLines(value,limit){if(limit<=0)return[];const lines=splitLines(value);return lines.length<=limit?lines:lines.slice(lines.length-limit)}async function resolveBackgroundCliEntry(cwd){const configured=process.env.PM_BACKGROUND_CLI_ENTRY?.trim();if(configured&&configured.length>0){const resolvedConfigured=path.resolve(cwd,configured);if(await pathExists(resolvedConfigured))return resolvedConfigured}const distEntry=path.resolve(cwd,"dist","cli.js");if(await pathExists(distEntry))return distEntry;const argvEntry=process.argv[1]?.trim();if(argvEntry&&argvEntry.length>0){const resolvedArgvEntry=path.resolve(cwd,argvEntry);if(await pathExists(resolvedArgvEntry))return resolvedArgvEntry}throw new PmCliError("Unable to resolve a CLI entrypoint for background test runs. Build the project or set PM_BACKGROUND_CLI_ENTRY.",EXIT_CODE.GENERIC_FAILURE)}async function refreshRunIfStale(pmRoot,record){if(!BACKGROUND_RUN_ACTIVE_STATUSES.has(record.status)||isPidRunning(record.worker_pid)||record.finished_at)return record;const next={...record,status:"failed",finished_at:nowIso(),error:record.error??"Background test run worker exited before writing terminal status."};return await writeBackgroundRunRecord(pmRoot,next),next}async function startBackgroundTestRun(options){await ensureBackgroundRunStorage(options.pmRoot);const normalizedArgs=normalizeCommandArgs(options.commandArgs);if(normalizedArgs.length===0)throw new PmCliError("Background test run requires command arguments.",EXIT_CODE.USAGE);const fingerprint=buildBackgroundTestRunFingerprint(options.kind,normalizedArgs,options.pmRoot),existingRuns=await listBackgroundTestRuns(options.pmRoot,{});for(const existing of existingRuns){const refreshed=await refreshRunIfStale(options.pmRoot,existing);if(refreshed.fingerprint===fingerprint&&!(!BACKGROUND_RUN_ACTIVE_STATUSES.has(refreshed.status)||!isPidRunning(refreshed.worker_pid)))return{started:!1,run:{...refreshed,duplicate_of:refreshed.id},duplicate_of:refreshed.id}}const runId=buildRunId(),createdAt=nowIso(),stdoutPath=getTestRunStdoutPath(options.pmRoot,runId),stderrPath=getTestRunStderrPath(options.pmRoot,runId),resultPath=getTestRunResultPath(options.pmRoot,runId);await fs.writeFile(stdoutPath,"","utf8"),await fs.writeFile(stderrPath,"","utf8");const record={id:runId,kind:options.kind,status:"queued",created_at:createdAt,updated_at:createdAt,requested_by:options.requestedBy,fingerprint,command_args:normalizedArgs,command_label:summarizeCommandLabel(normalizedArgs),pm_root:options.pmRoot,global_pm_root:options.globalPmRoot,target_id:options.targetId,status_filter:options.statusFilter,attempt:typeof options.attempt=="number"&&Number.isFinite(options.attempt)&&options.attempt>=1?Math.floor(options.attempt):1,resumed_from:options.resumedFrom,resumed_by:options.resumedBy,stdout_path:stdoutPath,stderr_path:stderrPath,result_path:resultPath,progress:{phase:"queued",message:"Queued for background execution.",heartbeat_at:createdAt}};return await writeBackgroundRunRecord(options.pmRoot,record),{started:!0,run:record}}async function spawnBackgroundTestRunWorker(options){const record=await readBackgroundTestRunRecord(options.pmRoot,options.runId);if(!record)throw new PmCliError(`Background test run ${options.runId} not found`,EXIT_CODE.NOT_FOUND);if(BACKGROUND_RUN_TERMINAL_STATUSES.has(record.status))throw new PmCliError(`Background test run ${record.id} is already terminal (${record.status}).`,EXIT_CODE.CONFLICT);const cliEntry=await resolveBackgroundCliEntry(process.cwd()),args=[];options.noExtensions===!0&&args.push("--no-extensions"),args.push("--path",record.pm_root,"test-runs-worker",record.id);const env={...process.env,PM_PATH:record.pm_root,PM_GLOBAL_PATH:record.global_pm_root,PM_BACKGROUND_TEST_RUN_ID:record.id,PM_BACKGROUND_TEST_RUN_ATTEMPT:String(record.attempt),PM_BACKGROUND_TEST_RUN_RESUMED_FROM:record.resumed_from??"",FORCE_COLOR:"0"},child=spawn(process.execPath,[cliEntry,...args],{cwd:process.cwd(),env,detached:!0,stdio:"ignore",windowsHide:!0});child.unref();const next={...record,status:"queued",worker_pid:child.pid,progress:{phase:"queued",message:"Worker process started.",heartbeat_at:nowIso()}};return await writeBackgroundRunRecord(options.pmRoot,next),next}async function appendFileOrdered(queue,filePath,text){await queue,await fs.appendFile(filePath,text,"utf8")}function resolvePositiveEnvInteger(name,fallback){const parsed=Number.parseInt(process.env[name]??"",10);return Number.isFinite(parsed)&&parsed>0?parsed:fallback}function buildBackgroundWorkerEnv(record){return{...process.env,PM_PATH:record.pm_root,PM_GLOBAL_PATH:record.global_pm_root,FORCE_COLOR:"0"}}function buildBackgroundWorkerChildArgs(record,noExtensions){return noExtensions?["--no-extensions",...record.command_args]:[...record.command_args]}function createBackgroundRunRecordWriteScheduler(pmRoot,record){let writeQueue=Promise.resolve();return{schedule(){writeQueue=writeQueue.then(async()=>{try{await writeBackgroundRunRecord(pmRoot,record)}catch{}})},async flush(){await writeQueue}}}function requestChildSignal(child,signal){try{child.kill(signal)}catch{}}async function requestBackgroundWorkerStop(record,child,state,scheduleRecordWrite){state.stopRequested||(state.stopRequested=!0,record.stop_requested_at=nowIso(),record.progress={...record.progress,phase:"stopping",message:"Stop requested for background run.",heartbeat_at:nowIso()},scheduleRecordWrite(),requestChildSignal(child,"SIGTERM"),setTimeout(()=>requestChildSignal(child,"SIGKILL"),resolvePositiveEnvInteger("PM_BACKGROUND_RUN_FORCE_KILL_DELAY_MS",DEFAULT_BACKGROUND_RUN_FORCE_KILL_DELAY_MS)).unref?.())}function buildItemBackgroundProgress(line,progressPatch,phase){return{phase,message:line,heartbeat_at:nowIso(),item_index:progressPatch.item_index,item_total:progressPatch.item_total,item_id:progressPatch.item_id,linked_test_index:void 0,linked_test_total:void 0,current_command:void 0,elapsed_ms:void 0}}function buildLinkedTestBackgroundProgress(line,record,progressPatch,phase){return{phase,message:line,heartbeat_at:nowIso(),item_index:progressPatch.item_index??record.progress?.item_index,item_total:progressPatch.item_total??record.progress?.item_total,item_id:progressPatch.item_id??record.progress?.item_id,linked_test_index:progressPatch.linked_test_index,linked_test_total:progressPatch.linked_test_total,current_command:progressPatch.current_command,elapsed_ms:progressPatch.elapsed_ms}}function applyBackgroundWorkerProgressLine(record,line,stopRequested){const progressPatch=parseProgressLine(line);if(!progressPatch)return!1;const phase=record.progress?.phase==="stopping"?"stopping":"running";return record.progress=progressPatch.item_id?buildItemBackgroundProgress(line,progressPatch,phase):buildLinkedTestBackgroundProgress(line,record,progressPatch,phase),progressPatch.phase==="finished"&&!stopRequested&&(record.progress.phase="running"),!0}function parseBackgroundWorkerStdout(stdoutBuffer){if(stdoutBuffer.trim().length===0)return null;try{return JSON.parse(stdoutBuffer)}catch{return null}}function resolveBackgroundWorkerFinalStatus(record,stopRequested){return stopRequested?"stopped":record.exit_code===0&&(record.summary?.failed??0)===0&&record.summary?.fail_on_skipped_triggered!==!0?"passed":"failed"}function buildFinishedBackgroundProgress(record,stopRequested){return{phase:"finished",message:stopRequested?"Background run stopped.":`Background run finished with status=${record.status}.`,heartbeat_at:nowIso(),item_index:record.progress?.item_index,item_total:record.progress?.item_total,item_id:record.progress?.item_id,linked_test_index:record.progress?.linked_test_index,linked_test_total:record.progress?.linked_test_total,current_command:record.progress?.current_command,elapsed_ms:record.progress?.elapsed_ms}}async function writeBackgroundWorkerResult(record,parsedResult,stdoutBuffer){if(parsedResult!==null){await writeFileAtomic(record.result_path,`${JSON.stringify(parsedResult,null,2)}
|
|
3
3
|
`);return}await writeFileAtomic(record.result_path,`${JSON.stringify({parse_error:"Background run output was not valid JSON.",stdout_excerpt:tailLines(stdoutBuffer,DEFAULT_BACKGROUND_RUN_LOG_TAIL_LINES)},null,2)}
|
|
4
4
|
`)}async function runBackgroundTestRunWorker(pmRoot,runId,noExtensions=!1){const loaded=await readBackgroundTestRunRecord(pmRoot,runId);if(!loaded)throw new PmCliError(`Background test run ${runId} not found`,EXIT_CODE.NOT_FOUND);const record={...loaded,status:"running",started_at:loaded.started_at??nowIso(),worker_pid:process.pid,progress:{phase:"running",message:"Worker started.",heartbeat_at:nowIso()}},cliEntry=await resolveBackgroundCliEntry(process.cwd()),env=buildBackgroundWorkerEnv(record);await writeBackgroundRunRecord(pmRoot,record);const childArgs=buildBackgroundWorkerChildArgs(record,noExtensions),child=spawn(process.execPath,[cliEntry,...childArgs],{cwd:process.cwd(),env,detached:!1,stdio:["ignore","pipe","pipe"],windowsHide:!0});record.child_pid=child.pid;const recordWriteScheduler=createBackgroundRunRecordWriteScheduler(pmRoot,record),scheduleRecordWrite=recordWriteScheduler.schedule;let stdoutWriteQueue=Promise.resolve(),stderrWriteQueue=Promise.resolve(),stdoutBuffer="",stderrBuffer="";const stopState={stopRequested:!1},onSignal=()=>{requestBackgroundWorkerStop(record,child,stopState,scheduleRecordWrite)};process.on("SIGTERM",onSignal),process.on("SIGINT",onSignal);const resourceTimer=setInterval(()=>{(async()=>(record.resource=await buildResourceSnapshot(record),record.progress.heartbeat_at=nowIso(),scheduleRecordWrite()))()},resolvePositiveEnvInteger("PM_BACKGROUND_RUN_RESOURCE_INTERVAL_MS",DEFAULT_BACKGROUND_RUN_RESOURCE_SNAPSHOT_INTERVAL_MS));resourceTimer.unref?.(),child.stdout?.on("data",chunk=>{const text=chunk.toString("utf8");stdoutBuffer+=text,stdoutWriteQueue=appendFileOrdered(stdoutWriteQueue,record.stdout_path,text)}),child.stderr?.on("data",chunk=>{const text=chunk.toString("utf8");stderrWriteQueue=appendFileOrdered(stderrWriteQueue,record.stderr_path,text),stderrBuffer+=text;const parts=stderrBuffer.split(/\r?\n/);stderrBuffer=parts.pop();let progressChanged=!1;for(const part of parts){const line=part.trimEnd();line.length>0&&applyBackgroundWorkerProgressLine(record,line,stopState.stopRequested)&&(progressChanged=!0)}progressChanged&&scheduleRecordWrite()});let exitCode=null,signal=null;try{({code:exitCode,signal}=await new Promise(resolve=>{child.on("close",(code,closeSignal)=>{resolve({code,signal:closeSignal})})}))}finally{clearInterval(resourceTimer),process.off("SIGTERM",onSignal),process.off("SIGINT",onSignal)}await stdoutWriteQueue,await stderrWriteQueue;const trailingStderrLine=stderrBuffer.trimEnd();trailingStderrLine.length>0&&applyBackgroundWorkerProgressLine(record,trailingStderrLine,stopState.stopRequested)&&scheduleRecordWrite(),stderrBuffer="";const parsedResult=parseBackgroundWorkerStdout(stdoutBuffer),evaluated=evaluateWorkerResult(record.kind,parsedResult);return record.summary=evaluated.summary,record.exit_code=typeof exitCode=="number"?exitCode:void 0,record.signal=signal??void 0,record.finished_at=nowIso(),record.status=resolveBackgroundWorkerFinalStatus(record,stopState.stopRequested),record.progress=buildFinishedBackgroundProgress(record,stopState.stopRequested),record.resource=await buildResourceSnapshot(record),await writeBackgroundWorkerResult(record,parsedResult,stdoutBuffer),await writeBackgroundRunRecord(pmRoot,record),await recordWriteScheduler.flush(),record}async function listBackgroundTestRuns(pmRoot,options){const recordPaths=await listBackgroundRunRecordPaths(pmRoot),runs=[];for(const recordPath of recordPaths){const raw=await readFileIfExists(recordPath);if(!raw)continue;const parsed=await parseBackgroundRunRecord(raw,recordPath);runs.push(parsed)}const refreshed=[];for(const run of runs)refreshed.push(await refreshRunIfStale(pmRoot,run));const sorted=(options.status?refreshed.filter(entry=>entry.status===options.status):refreshed).sort((left,right)=>{const byUpdated=Date.parse(right.updated_at)-Date.parse(left.updated_at);return Number.isFinite(byUpdated)&&byUpdated!==0?byUpdated:right.id.localeCompare(left.id)}),limit=typeof options.limit=="number"&&options.limit>=0?options.limit:void 0;return limit===void 0?sorted:sorted.slice(0,limit)}async function getBackgroundTestRunStatus(pmRoot,runId){const loaded=await readBackgroundTestRunRecord(pmRoot,runId);if(!loaded)throw new PmCliError(`Background test run ${runId} not found`,EXIT_CODE.NOT_FOUND);const refreshed=await refreshRunIfStale(pmRoot,loaded),workerAlive=isPidRunning(refreshed.worker_pid),childAlive=isPidRunning(refreshed.child_pid);refreshed.status==="running"&&!childAlive&&!refreshed.finished_at&&!workerAlive&&(refreshed.status="failed",refreshed.finished_at=nowIso(),refreshed.error=refreshed.error??"Background run process exited unexpectedly.",await writeBackgroundRunRecord(pmRoot,refreshed)),refreshed.status==="running"&&(refreshed.resource=await buildResourceSnapshot(refreshed),await writeBackgroundRunRecord(pmRoot,refreshed));const heartbeatAt=refreshed.progress?.heartbeat_at,heartbeatAtMs=heartbeatAt?Date.parse(heartbeatAt):Number.NaN,lagMs=Number.isFinite(heartbeatAtMs)?Math.max(0,nowMs()-heartbeatAtMs):void 0,staleMs=Number.parseInt(process.env.PM_BACKGROUND_RUN_HEARTBEAT_STALE_MS??"",10),staleThresholdMs=Number.isFinite(staleMs)&&staleMs>0?staleMs:DEFAULT_BACKGROUND_RUN_HEARTBEAT_STALE_MS,healthState=refreshed.status==="running"?lagMs!==void 0&&lagMs>staleThresholdMs?"stale":"healthy":"inactive";return{run:refreshed,health:{state:healthState,last_heartbeat_at:heartbeatAt,heartbeat_lag_ms:lagMs,worker_alive:workerAlive,child_alive:childAlive}}}function sendBackgroundRunStopSignal(record,force){const signal=force?"SIGKILL":"SIGTERM";if(!isPidRunning(record.worker_pid)||!record.worker_pid)return"none";try{return process.kill(record.worker_pid,signal),signal}catch{return"none"}}function buildStoppingBackgroundProgress(record,signalSent){return{phase:"stopping",message:signalSent==="none"?"Run marked stopped.":`Stop requested via ${signalSent}.`,heartbeat_at:nowIso(),item_index:record.progress?.item_index,item_total:record.progress?.item_total,item_id:record.progress?.item_id,linked_test_index:record.progress?.linked_test_index,linked_test_total:record.progress?.linked_test_total,current_command:record.progress?.current_command,elapsed_ms:record.progress?.elapsed_ms}}async function stopBackgroundTestRun(pmRoot,runId,force=!1){const loaded=await readBackgroundTestRunRecord(pmRoot,runId);if(!loaded)throw new PmCliError(`Background test run ${runId} not found`,EXIT_CODE.NOT_FOUND);const refreshed=await refreshRunIfStale(pmRoot,loaded);if(BACKGROUND_RUN_TERMINAL_STATUSES.has(refreshed.status))return{run:refreshed,signal_sent:"none"};const signalSent=sendBackgroundRunStopSignal(refreshed,force);return signalSent==="none"&&(refreshed.status="stopped",refreshed.finished_at=refreshed.finished_at??nowIso()),refreshed.stop_requested_at=nowIso(),refreshed.progress=buildStoppingBackgroundProgress(refreshed,signalSent),await writeBackgroundRunRecord(pmRoot,refreshed),{run:refreshed,signal_sent:signalSent}}async function resumeBackgroundTestRun(pmRoot,runId,requestedBy,noExtensions=!1){const loaded=await readBackgroundTestRunRecord(pmRoot,runId);if(!loaded)throw new PmCliError(`Background test run ${runId} not found`,EXIT_CODE.NOT_FOUND);const refreshed=await refreshRunIfStale(pmRoot,loaded);if(!BACKGROUND_RUN_TERMINAL_STATUSES.has(refreshed.status))throw new PmCliError(`Background test run ${runId} is not terminal and cannot be resumed.`,EXIT_CODE.CONFLICT);const started=await startBackgroundTestRun({pmRoot:refreshed.pm_root,globalPmRoot:refreshed.global_pm_root,kind:refreshed.kind,commandArgs:refreshed.command_args,requestedBy,targetId:refreshed.target_id,statusFilter:refreshed.status_filter,resumedFrom:refreshed.id,resumedBy:requestedBy,attempt:refreshed.attempt+1});if(!started.started)return started.run;const spawned=await spawnBackgroundTestRunWorker({pmRoot,runId:started.run.id,noExtensions}),prior={...refreshed,resumed_by:spawned.id};return await writeBackgroundRunRecord(pmRoot,prior),spawned}async function readBackgroundTestRunLogs(pmRoot,runId,stream,tail){const loaded=await readBackgroundTestRunRecord(pmRoot,runId);if(!loaded)throw new PmCliError(`Background test run ${runId} not found`,EXIT_CODE.NOT_FOUND);const resolvedTail=typeof tail=="number"&&Number.isFinite(tail)&&tail>=0?Math.floor(tail):DEFAULT_BACKGROUND_RUN_LOG_TAIL_LINES,stdoutRaw=stream==="stdout"||stream==="both"?await readFileIfExists(loaded.stdout_path)??"":"",stderrRaw=stream==="stderr"||stream==="both"?await readFileIfExists(loaded.stderr_path)??"":"";return{run:loaded,stream,tail:resolvedTail,stdout:stream==="stdout"||stream==="both"?tailLines(stdoutRaw,resolvedTail):[],stderr:stream==="stderr"||stream==="both"?tailLines(stderrRaw,resolvedTail):[]}}const _testOnly={buildResourceSnapshot,evaluateWorkerResult,isPidRunning,parseLinuxCpuStat,parseLinuxRssStatus,parseProgressLine,readLinuxCpuSeconds,readLinuxRssBytes,refreshRunIfStale,splitLines,tailLines};export{_testOnly,buildBackgroundTestRunFingerprint,getBackgroundTestRunStatus,listBackgroundTestRuns,readBackgroundTestRunLogs,readBackgroundTestRunRecord,resumeBackgroundTestRun,runBackgroundTestRunWorker,spawnBackgroundTestRunWorker,startBackgroundTestRun,stopBackgroundTestRun};
|
|
5
5
|
//# sourceMappingURL=background-runs.js.map
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
/** Resolve the longest exact misplaced suffix under registered namespaces only when one canonical command owns it. */
|
|
2
|
+
export declare function resolveMisnestedCommandPath(unknownPath: string, availablePaths: readonly string[]): string | undefined;
|
|
1
3
|
/** Score one command path for a guessed command token; lower is better. */
|
|
2
4
|
export declare function scoreCommandPathMatch(commandPath: string, queryToken: string): number;
|
|
3
5
|
/** Rank available command paths for one guessed token. */
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
(function(){try{var e=typeof window<"u"?window:typeof global<"u"?global:typeof globalThis<"u"?globalThis:typeof self<"u"?self:{},n=new e.Error().stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="
|
|
1
|
+
(function(){try{var e=typeof window<"u"?window:typeof global<"u"?global:typeof globalThis<"u"?globalThis:typeof self<"u"?self:{},n=new e.Error().stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="12066389-daef-5fce-b7a6-0ecffa926d66")}catch{}})();import{levenshteinDistanceWithinLimit}from"../../core/shared/levenshtein.js";import{PM_COMMAND_ALIAS_CONTRACTS,resolvePmCommandAlias,resolvePmCommandOperation}from"../cli-contracts/command-aliases.js";function resolveMisnestedCommandPath(unknownPath,availablePaths){const tokens=unknownPath.trim().split(/\s+/u);if(!(tokens.length<2||availablePaths.includes(tokens.join(" "))))for(let namespaceLength=1;namespaceLength<tokens.length;namespaceLength+=1){if(!availablePaths.includes(tokens.slice(0,namespaceLength).join(" ")))return;const suffix=tokens.slice(namespaceLength).join(" "),alias=resolvePmCommandAlias(suffix),candidates=new Set(availablePaths.filter(candidate=>candidate===suffix||candidate.endsWith(` ${suffix}`)||candidate===alias?.canonical).map(candidate=>resolvePmCommandAlias(candidate)?.canonical??candidate).filter(candidate=>availablePaths.includes(candidate)));if(candidates.size>0)return candidates.size===1?[...candidates][0]:void 0}}const COMMAND_SYNONYMS={add:["create","append"],fetch:["get"],inspect:["get","health"],log:["history","comments","notes"],ls:["list"],pause:["release"],read:["get"],remove:["delete"],rm:["delete"],show:["get"],start:["start-task"]};function scoreCommandPathMatch(commandPath,queryToken){const normalizedPath=commandPath.trim().toLowerCase(),normalizedToken=queryToken.trim().toLowerCase();if(normalizedToken.length===0)return Number.POSITIVE_INFINITY;const segments=normalizedPath.split(" "),semanticIndex=COMMAND_SYNONYMS[normalizedToken]?.indexOf(resolvePmCommandOperation(normalizedPath));if(semanticIndex!==void 0&&semanticIndex>=0)return semanticIndex;if(normalizedPath===normalizedToken)return 10;if(segments.includes(normalizedToken))return 11;if(segments.some(segment=>segment.startsWith(normalizedToken)))return 12;const maxDistance=normalizedToken.length>=5?2:1;let bestDistance=Number.POSITIVE_INFINITY;for(const segment of segments){const distance=levenshteinDistanceWithinLimit(segment,normalizedToken,maxDistance);distance!==null&&(bestDistance=Math.min(bestDistance,distance))}return Number.isFinite(bestDistance)?20+bestDistance:normalizedPath.includes(normalizedToken)?30:Number.POSITIVE_INFINITY}function rankCommandPaths(commandPaths,queryToken){return commandPaths.map(path=>({path,score:scoreCommandPathMatch(path,queryToken)})).filter(entry=>Number.isFinite(entry.score)).sort((left,right)=>left.score!==right.score?left.score-right.score:left.path===right.path?0:left.path<right.path?-1:1).map(entry=>entry.path)}function canonicalizeCommandSuggestions(candidates,availablePaths,queryToken){const available=new Set(availablePaths),aliases=PM_COMMAND_ALIAS_CONTRACTS.filter(alias=>alias.lifecycle==="deprecated").slice().sort((left,right)=>right.alias.length-left.alias.length),preferred=[],resolved=candidates.flatMap(candidate=>{const alias=aliases.find(entry=>candidate===entry.alias||candidate.startsWith(`${entry.alias} `));let canonical=candidate;if(alias){const suffix=candidate.slice(alias.alias.length);if(!available.has(`${alias.canonical}${suffix}`))return[];canonical=`${alias.canonical_argv.join(" ")}${suffix}`}return queryToken!==void 0&&scoreCommandPathMatch(candidate,queryToken)<10&&preferred.push(canonical),[canonical]});return[...new Set(preferred.length>0?preferred:resolved)]}export{canonicalizeCommandSuggestions,rankCommandPaths,resolveMisnestedCommandPath,scoreCommandPathMatch};
|
|
2
2
|
//# sourceMappingURL=command-suggestions.js.map
|