@gitroll/tacitt-cli 0.6.9 → 0.6.10

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.
Files changed (41) hide show
  1. package/dist/{agent-setup-CODlN6zl.mjs → agent-setup-LPd8WEsN.mjs} +2 -2
  2. package/dist/agent-setup-worker.mjs +5 -5
  3. package/dist/cli.mjs +19 -10
  4. package/dist/codex-title-worker.mjs +1 -1
  5. package/dist/configured-preview-worker.mjs +5 -5
  6. package/dist/configured-session-worker.mjs +5 -5
  7. package/dist/{frame-Dj5Zs6UW.mjs → frame-ByT1xayJ.mjs} +1 -1
  8. package/dist/{heap-Dt3-d27v.mjs → heap-CM298Yu0.mjs} +8 -6
  9. package/dist/inspection-worker.mjs +4 -4
  10. package/dist/{job-BQXssW46.mjs → job-BHIwElNF.mjs} +5 -5
  11. package/dist/{locate-BWkblijS.mjs → locate-Cwv6WH8n.mjs} +25 -2
  12. package/dist/{model-CSx2dP_P.mjs → model-zvn5GUJ9.mjs} +1 -1
  13. package/dist/operation-executor.mjs +3 -3
  14. package/dist/preview-worker.mjs +1 -1
  15. package/dist/privacy-validator.mjs +1 -1
  16. package/dist/{query-CfhcS4gd.mjs → query-By41gaKm.mjs} +424 -145
  17. package/dist/{run-xlOaKCk8.mjs → run-ZQx7p4Rw.mjs} +4 -4
  18. package/dist/{src-V9GHLZQs.mjs → src-3jvMvSQn.mjs} +3 -3
  19. package/dist/{src-DzKuzGVH.mjs → src-BqtdFVq7.mjs} +484 -178
  20. package/dist/{src-BgsEnMsy.mjs → src-D_DfY--2.mjs} +4 -4
  21. package/dist/store-database-worker.mjs +1 -1
  22. package/dist/web/assets/index-B14e6_-z.js +174 -0
  23. package/dist/web/index.html +1 -1
  24. package/package.json +1 -1
  25. package/skills/tacitt/INDEX.md +6 -6
  26. package/skills/tacitt/SKILL.md +2 -2
  27. package/skills/tacitt/references/INDEX.md +3 -3
  28. package/skills/tacitt/references/classification.md +12 -2
  29. package/skills/tacitt/references/commands.md +10 -2
  30. package/skills/tacitt/references/journey.md +9 -1
  31. package/skills/tacitt/references/send.md +29 -8
  32. package/skills/tacitt/scripts/INDEX.md +37 -37
  33. package/skills/tacitt/scripts/journey-classification-validation.d.mts +13 -0
  34. package/skills/tacitt/scripts/journey-classification-validation.mjs +38 -0
  35. package/skills/tacitt/scripts/journey-state.d.mts +6 -0
  36. package/skills/tacitt/scripts/journey-state.mjs +24 -1
  37. package/skills/tacitt/scripts/journey.d.mts +5 -0
  38. package/skills/tacitt/scripts/journey.mjs +33 -3
  39. package/skills/tacitt/scripts/privacy-validator.mjs +11 -1
  40. package/skills/tacitt/scripts/review.mjs +14 -2
  41. package/dist/web/assets/index-9UfCT_2C.js +0 -174
@@ -1,4 +1,4 @@
1
- import { $ as bCountSchema, $n as detectStatic, A as bDuplicateMirror, B as bEvidenceReader, Bt as sessionPeriodSchema, C as validateBUnit, Cn as inspectionSemanticVersions, Ct as cardsSubmitSchema, D as uncoveredBObligations, Dn as createStaticProcessor, Dt as privacyResolveSchema, E as bLiterals, Et as packageConfirmActionSchema, Fn as entityDictionaryVersion, Ft as termTypeInfo, G as subtractBRanges, Gn as validateProfile, H as codePointLength, Ht as journeyProfileAddressActionSchema, I as bIssue, It as termTypeSchema, J as bRetainedGroupSchema, Jn as validatePlan, Jt as respondProfileLinks, Kn as orderProfile, L as bOverlaps, Lt as termTypes, M as bLegacyBindings, Mt as reviewTermsResultSchema, N as bUnitFragments, Nt as termAssessmentSchema, O as indexBRanges, On as staticDefaults, Ot as privacyTermKey, P as bAdoptedObligations, Pn as cleanupImplementationVersion, Pt as termOverrideValueSchema, Q as bAnchorSchema, Qt as safeCode, R as unownedBObligations, Rt as deselectOutsidePeriod, S as validateBFields, Sn as currentDetectorVersion, St as cardsConfirmActionSchema, T as bLiteralTerms, Tn as gapReasons, Tt as emptyJourneyPrivacy, U as codePointSlice, Ut as journeyProfileLinksActionSchema, V as bDigest, Vt as emptyJourneyProfile, W as createCodePointIndex, X as bSessionDecisionSchema, Xt as ApplicationError, Y as bReviewSubmissionSchema, Yn as copyData, Yt as classificationTitleMaskSchema, Z as bTermSchema, Zt as UnfinishedJourneyError, _ as semanticFindingSchema, _t as hideConfirmSchema, a as reviewChunkSchema, an as observeSource, at as journeyExceptionSchema, b as validateBReview, bt as journeyConfigurationSchema, c as textDigest, cn as inventorySchema, cr as scalarBefore, ct as journeyPreferenceScopeSchema, dt as nativeJourneySchema, en as familyNeedsReview, et as bIdSchema, f as reviewAlignmentAuditSchema, fn as nativeSourceIdentitySchema, ft as journeyGuidanceSchema, g as validateReviewAlignment, h as reviewFindingsDigest, ht as journeyDescribeSchema, i as reviewBatchSchema, in as sourceKey, ir as createEntityMatcher, it as journeyDigestSchema, jt as reviewTermsInputSchema, kn as staticOptions, l as validateReview, lr as defaultDetectionLimits, lt as journeyProjectTypeSchema, m as reviewAlignmentSchema, mt as delegatedDecisionSchema, n as compileBFindings, nn as reviewFamilies, nt as journeyAccessSchema, o as reviewDigest, on as sourceSnapshotId, ot as journeyIdSchema, p as reviewAlignmentInputSchema, pt as journeyInstructionInputSchema, q as bRepresentationSchema, r as reviewBatchChunks, rn as sourceGraph, rt as journeyChoiceSchema, s as reviewSubmitSchema, sn as fileIdentitySchema, sr as scalarAfter, st as journeyKindSchema, tn as familySelected, tr as staticCategories, tt as bMethod, u as findingReferenceKey, un as sourcePartSchema, ut as journeyWorkTypeSchema, vt as hidePlanInputSchema, w as bLiteralAdmitted, x as createBAnalysisUnits, xt as cardAnswerActionSchema, y as validateBClassification, yt as hidePlanResourceSchema, z as bAnchorKey } from "./locate-BWkblijS.mjs";
1
+ import { $ as bCountSchema, $n as detectStatic, A as bDuplicateMirror, B as bEvidenceReader, Bt as sessionPeriodSchema, C as validateBUnit, Cn as inspectionSemanticVersions, Ct as cardsSubmitSchema, D as uncoveredBObligations, Dn as createStaticProcessor, Dt as privacyResolveSchema, E as bLiterals, Et as packageConfirmActionSchema, Fn as entityDictionaryVersion, Ft as termTypeInfo, G as subtractBRanges, Gn as validateProfile, H as codePointLength, Ht as journeyProfileAddressActionSchema, I as bIssue, It as termTypeSchema, J as bRetainedGroupSchema, Jn as validatePlan, Jt as respondProfileLinks, Kn as orderProfile, L as bOverlaps, Lt as termTypes, M as bLegacyBindings, Mt as reviewTermsResultSchema, N as bUnitFragments, Nt as termAssessmentSchema, O as indexBRanges, On as staticDefaults, Ot as privacyTermKey, P as bAdoptedObligations, Pn as cleanupImplementationVersion, Pt as termOverrideValueSchema, Q as bAnchorSchema, Qt as safeCode, R as unownedBObligations, Rt as deselectOutsidePeriod, S as validateBFields, Sn as currentDetectorVersion, St as cardsConfirmActionSchema, T as bLiteralTerms, Tn as gapReasons, Tt as emptyJourneyPrivacy, U as codePointSlice, Ut as journeyProfileLinksActionSchema, V as bDigest, Vt as emptyJourneyProfile, W as createCodePointIndex, X as bSessionDecisionSchema, Xt as ApplicationError, Y as bReviewSubmissionSchema, Yn as copyData, Yt as classificationTitleMaskSchema, Z as bTermSchema, Zt as UnfinishedJourneyError, _ as semanticFindingSchema, _t as hideConfirmSchema, a as reviewChunkSchema, an as observeSource, at as journeyExceptionSchema, b as validateBReview, bt as journeyConfigurationSchema, c as textDigest, cn as inventorySchema, cr as scalarBefore, ct as journeyPreferenceScopeSchema, dt as nativeJourneySchema, en as familyNeedsReview, et as bIdSchema, f as reviewAlignmentAuditSchema, fn as nativeSourceIdentitySchema, ft as journeyGuidanceSchema, g as validateReviewAlignment, h as reviewFindingsDigest, ht as journeyDescribeSchema, i as reviewBatchSchema, in as sourceKey, ir as createEntityMatcher, it as journeyDigestSchema, jt as reviewTermsInputSchema, kn as staticOptions, l as validateReview, lr as defaultDetectionLimits, lt as journeyProjectTypeSchema, m as reviewAlignmentSchema, mt as delegatedDecisionSchema, n as compileBFindings, nn as reviewFamilies, nt as journeyAccessSchema, o as reviewDigest, on as sourceSnapshotId, ot as journeyIdSchema, p as reviewAlignmentInputSchema, pt as journeyInstructionInputSchema, q as bRepresentationSchema, r as reviewBatchChunks, rn as sourceGraph, rt as journeyChoiceSchema, s as reviewSubmitSchema, sn as fileIdentitySchema, sr as scalarAfter, st as journeyKindSchema, tn as familySelected, tr as staticCategories, tt as bMethod, u as findingReferenceKey, un as sourcePartSchema, ut as journeyWorkTypeSchema, vt as hidePlanInputSchema, w as bLiteralAdmitted, x as createBAnalysisUnits, xt as cardAnswerActionSchema, y as validateBClassification, yt as hidePlanResourceSchema, z as bAnchorKey } from "./locate-Cwv6WH8n.mjs";
2
2
  import { z } from "zod";
3
3
  import { createHash, createHmac, createSecretKey, randomUUID } from "node:crypto";
4
4
  import { setTimeout as setTimeout$1 } from "node:timers/promises";
@@ -14,6 +14,13 @@ const processingPlanSchema = z.unknown().transform((value, context) => {
14
14
  return z.NEVER;
15
15
  }
16
16
  });
17
+ //#endregion
18
+ //#region packages/application/src/processing/settings/data.ts
19
+ /**
20
+ * Sessions in one frozen configuration. Packaging admits at most 60,000 source parts per run
21
+ * (the artifact's entry bound) and every selected session has a part, so this matches it.
22
+ */
23
+ const maximumConfigurationSessions = 6e4;
17
24
  var SettingsError = class extends Error {
18
25
  code;
19
26
  field;
@@ -1220,6 +1227,18 @@ function isSkippedWork(work) {
1220
1227
  function isIncludedWork(work) {
1221
1228
  return isSelectedWork(work) && !isSkippedWork(work);
1222
1229
  }
1230
+ /**
1231
+ * ADR 0274: Public needs a recorded choice: the person's panel click or named conversation
1232
+ * answer (origin `explicit`), or, under ADR 0275, the account proof that every root was already
1233
+ * Public (origin and channel `account`). A Public Work stored before `accessChoice` existed is
1234
+ * still pending. Mirrored by `phaseOf` in the Skill's `journey-state.mjs` and by the panel's
1235
+ * `WorkAccess`.
1236
+ */
1237
+ function hasPublicChoice(work) {
1238
+ if (work.access !== "public") return true;
1239
+ if (!work.accessConfirmed || !work.accessChoice) return false;
1240
+ return work.accessChoice.channel === "account" ? work.accessOrigin === "account" : work.accessOrigin === "explicit";
1241
+ }
1223
1242
  function selectedWorks(state) {
1224
1243
  return state.works.filter(isSelectedWork);
1225
1244
  }
@@ -1466,6 +1485,26 @@ const sendItemSchema = z.object({
1466
1485
  title: z.string(),
1467
1486
  access
1468
1487
  }).strict().optional(),
1488
+ /** Safe preflight facts; local requested-access conflicts have no account owner. */
1489
+ accessConflict: z.object({
1490
+ sessionKey: journeyDigestSchema,
1491
+ requestedAccess: access,
1492
+ accountWorkId: journeyIdSchema.nullable(),
1493
+ accountAccess: z.enum([
1494
+ "public",
1495
+ "private",
1496
+ "removed",
1497
+ "mixed"
1498
+ ]).nullable(),
1499
+ rule: z.enum([
1500
+ "held-elsewhere",
1501
+ "destination-access",
1502
+ "requested-access",
1503
+ "content-access"
1504
+ ])
1505
+ }).strict().optional(),
1506
+ /** Distinct conflicting session keys across all rules and account holders. */
1507
+ accessConflictSessionCount: count$13.positive().optional(),
1469
1508
  /** Membership readback retained for visible conflict recovery. */
1470
1509
  membershipConflict: z.object({
1471
1510
  revision: count$13,
@@ -1721,6 +1760,16 @@ const journeyPropagateActionSchema = z.object({
1721
1760
  //#region packages/application/src/journey/scope/model.ts
1722
1761
  const count$11 = z.number().int().nonnegative().max(Number.MAX_SAFE_INTEGER);
1723
1762
  const safeText = z.string().trim().min(1).max(240);
1763
+ /**
1764
+ * One Work's stored account access (ADR 0274 D3(b), refined by ADR 0275): `public-all` when
1765
+ * every root key is held Public, `public` when the held keys are Public and other roots are new.
1766
+ */
1767
+ const accountWorkAccessSchema = z.enum([
1768
+ "private",
1769
+ "public",
1770
+ "public-all",
1771
+ "mixed"
1772
+ ]);
1724
1773
  const journeyScopeConfirmActionSchema = z.object({ kind: z.literal("scope-confirm") }).strict();
1725
1774
  const journeyWorkTypeActionSchema = z.object({
1726
1775
  kind: z.literal("work-type"),
@@ -1730,7 +1779,11 @@ const journeyWorkTypeActionSchema = z.object({
1730
1779
  const journeyWorksAccessActionSchema = z.object({
1731
1780
  kind: z.literal("works-access"),
1732
1781
  workIds: z.array(journeyIdSchema).min(1).max(1e4),
1733
- access: journeyAccessSchema
1782
+ access: journeyAccessSchema,
1783
+ accessChoice: z.object({
1784
+ channel: z.literal("conversation"),
1785
+ at: z.iso.datetime()
1786
+ }).strict().optional()
1734
1787
  }).strict();
1735
1788
  /** Static-only Work facts for proposing project types and scan scope; no source bodies. */
1736
1789
  const journeyCatalogQuerySchema = z.object({
@@ -1930,7 +1983,11 @@ const journeyActionSchema = z.discriminatedUnion("kind", [
1930
1983
  z.object({
1931
1984
  kind: z.literal("work-access"),
1932
1985
  workId: journeyIdSchema,
1933
- access: journeyAccessSchema
1986
+ access: journeyAccessSchema,
1987
+ accessChoice: z.object({
1988
+ channel: z.literal("conversation"),
1989
+ at: z.iso.datetime()
1990
+ }).strict().optional()
1934
1991
  }).strict(),
1935
1992
  z.object({
1936
1993
  kind: z.literal("work-title"),
@@ -2049,13 +2106,22 @@ function privacyDecisionDigest(state) {
2049
2106
  function assertJourneyScanned(state) {
2050
2107
  if (!state.consent || state.scope.status !== "confirmed" || !state.readingComplete || state.works.some((work) => isIncludedWork(work) && (!work.classificationMetadata || work.totalBatches === 0 && !hasCertifiedNoBatchReview(work, state.snapshotId) || work.completedBatches !== work.totalBatches || work.status === "blocked"))) throw new ApplicationError("SCOPE_NOT_READY");
2051
2108
  }
2109
+ /** Legacy model, delegate or unrecorded confirmation is not the person's Public choice. */
2110
+ function assertPublicChoices(state) {
2111
+ if (state.works.some((work) => isIncludedWork(work) && !hasPublicChoice(work))) throw new ApplicationError("SCOPE_NOT_READY");
2112
+ }
2052
2113
  /** Track B (ADR 0172): preview and packaging need every selected Work's static base. */
2053
2114
  function assertJourneyStaticReady(state) {
2054
2115
  if (state.works.some((work) => work.access !== "drop" && work.rootSessionIds.length > 0 && !isSkippedWork(work) && !hasCurrentStaticReview(state, work))) throw new ApplicationError("SCOPE_NOT_READY");
2055
2116
  }
2056
- /** Cards need only Track A, AI review and metadata; this final gate also needs Track B. */
2057
- function assertJourneyPrivacyReady(state, packaging = false) {
2117
+ /**
2118
+ * Cards need only Track A, AI review and metadata; this final gate also needs Track B.
2119
+ * `publicChoices` defaults to `packaging`: the Send click checks it before packaging, and the
2120
+ * follow-up of an already uploaded Send skips it because nothing more goes out (ADR 0274).
2121
+ */
2122
+ function assertJourneyPrivacyReady(state, packaging = false, { publicChoices = packaging } = {}) {
2058
2123
  assertJourneyScanned(state);
2124
+ if (publicChoices) assertPublicChoices(state);
2059
2125
  if (activePrivacyTerms(state).some((term) => term.disposition === "pending") || state.privacy.cards.length > 0 && (state.privacy.cards.some((card) => !card.answer) || state.privacy.confirmedAnswersRevision !== state.privacy.answersRevision)) throw new ApplicationError("CONFIRMATION_REQUIRED");
2060
2126
  assertJourneyStaticReady(state);
2061
2127
  if (packaging && state.privacy.packageApproval !== privacyDecisionDigest(state)) throw new ApplicationError("CONFIRMATION_REQUIRED");
@@ -2085,15 +2151,23 @@ function appendDecisionCards(state, input) {
2085
2151
  const open = new Set(activePrivacyTerms(state).filter((term) => term.disposition === "pending").map(privacyTermKey).filter((key) => !coveredBefore.has(key)));
2086
2152
  const extend = input.extend ?? [];
2087
2153
  const covered = [...input.cards.flatMap((card) => card.termKeys), ...extend.flatMap((item) => item.termKeys)];
2088
- if (!covered.length || state.privacy.cards.length + input.cards.length > 20 || new Set(input.cards.map((card) => card.id)).size !== input.cards.length || input.cards.some((card) => existing.has(card.id)) || new Set(extend.map((item) => item.cardId)).size !== extend.length || extend.some((item) => !existing.has(item.cardId)) || new Set(covered).size !== covered.length || covered.some((key) => !open.has(key))) throw new ApplicationError("INVALID_SELECTION");
2154
+ if (!covered.length) throw new ApplicationError("INVALID_SELECTION", "cards.append.empty");
2155
+ if (state.privacy.cards.length + input.cards.length > 20) throw new ApplicationError("INVALID_SELECTION", "cards.append.limit");
2156
+ if (new Set(input.cards.map((card) => card.id)).size !== input.cards.length) throw new ApplicationError("INVALID_SELECTION", "cards.append.duplicate-card");
2157
+ if (input.cards.some((card) => existing.has(card.id))) throw new ApplicationError("INVALID_SELECTION", "cards.append.existing-card");
2158
+ if (new Set(extend.map((item) => item.cardId)).size !== extend.length) throw new ApplicationError("INVALID_SELECTION", "cards.append.duplicate-extension");
2159
+ if (extend.some((item) => !existing.has(item.cardId))) throw new ApplicationError("INVALID_SELECTION", "cards.append.unknown-card");
2160
+ if (new Set(covered).size !== covered.length) throw new ApplicationError("INVALID_SELECTION", "cards.append.duplicate-term");
2161
+ if (covered.some((key) => !open.has(key))) throw new ApplicationError("INVALID_SELECTION", "cards.append.term-not-open");
2089
2162
  const terms = new Map(state.privacy.terms.map((term) => [privacyTermKey(term), term]));
2090
2163
  const ownValues = ownIdentityValues(state);
2091
2164
  for (const extension of extend) {
2092
2165
  const ownCard = !!state.privacy.cards.find((item) => item.id === extension.cardId)?.termKeys.some((key) => terms.get(key)?.type === "own-identity");
2093
- if (extension.termKeys.some((key) => {
2166
+ for (const key of extension.termKeys) {
2094
2167
  const term = terms.get(key);
2095
- return term?.type === "own-identity" ? !ownCard : term?.type === "people" && ownCard && !ownValues.has(identityValue(term.value));
2096
- })) throw new ApplicationError("INVALID_SELECTION");
2168
+ if (term?.type === "own-identity" && !ownCard) throw new ApplicationError("INVALID_SELECTION", "cards.append.own-identity-card");
2169
+ if (term?.type === "people" && ownCard && !ownValues.has(identityValue(term.value))) throw new ApplicationError("INVALID_SELECTION", "cards.append.other-person");
2170
+ }
2097
2171
  }
2098
2172
  const additions = new Map(extend.map((item) => [item.cardId, item.termKeys]));
2099
2173
  const inherited = new Map(extend.flatMap((item) => item.termKeys.map((key) => [key, item.cardId])));
@@ -2303,6 +2377,7 @@ function respondPrivacy(state, action, source) {
2303
2377
  };
2304
2378
  }
2305
2379
  if (action.kind === "package-confirm") {
2380
+ assertPublicChoices(state);
2306
2381
  assertJourneyPrivacyReady(state);
2307
2382
  return {
2308
2383
  ...state,
@@ -2356,7 +2431,7 @@ const journeyRenderSchema = z.object({
2356
2431
  /** One Work, or several in one call where each Work renders or fails on its own. */
2357
2432
  const journeyRenderInputSchema = z.union([journeyRenderSchema, z.object({
2358
2433
  journeyId: journeyIdSchema,
2359
- workIds: z.array(journeyIdSchema).min(1).max(256)
2434
+ workIds: z.array(journeyIdSchema).min(1).max(maximumConfigurationSessions)
2360
2435
  }).strict()]);
2361
2436
  const journeyPreviewSchema = journeyRenderSchema.extend({ cursor: z.object({
2362
2437
  sha256: journeyDigestSchema,
@@ -4830,6 +4905,174 @@ function resetWorkScan(work) {
4830
4905
  };
4831
4906
  }
4832
4907
  //#endregion
4908
+ //#region packages/application/src/journey/scope/respond.ts
4909
+ /**
4910
+ * Account-seeded access (ADR 0274 D3(b)). With `held`, the time the account inventory was read,
4911
+ * a Work whose every root is held Public is confirmed Public with the account as its recorded
4912
+ * choice (ADR 0275); without it, Public stays a suggestion.
4913
+ */
4914
+ function accountSeededAccess(account, held) {
4915
+ const accessChoice = account === "public-all" && held !== void 0 ? {
4916
+ channel: "account",
4917
+ at: held
4918
+ } : void 0;
4919
+ return {
4920
+ access: account === "public" || account === "public-all" ? "public" : "private",
4921
+ accessOrigin: "account",
4922
+ accessConfirmed: account === "private" || accessChoice !== void 0,
4923
+ accountAccessConflict: account === "mixed",
4924
+ accessChoice
4925
+ };
4926
+ }
4927
+ /** Classification confirmation initializes visibility and scan membership for this journey. */
4928
+ function confirmScope(state, now, decision, accountAccess) {
4929
+ if (state.scope.status === "confirmed") return state;
4930
+ if (state.classificationRevision === null || !state.works.length || state.works.some((work) => work.placement !== "placed" && work.placement !== "classifying" || work.placement === "placed" && !(work.status === "blocked" && work.access === "drop" && work.accessConfirmed) && (!work.description.trim() || !work.projectType))) throw new ApplicationError("SCOPE_NOT_READY");
4931
+ return withReadingProgress({
4932
+ ...state,
4933
+ scope: {
4934
+ status: "confirmed",
4935
+ actor: decision ? "delegate" : "human",
4936
+ at: now
4937
+ },
4938
+ works: state.works.map((work) => {
4939
+ if (work.placement === "classifying") return work;
4940
+ if (work.accessOrigin === "explicit" && work.accessConfirmed) return work;
4941
+ const account = accountAccess?.get(work.id);
4942
+ if (account) return {
4943
+ ...work,
4944
+ ...accountSeededAccess(account, now)
4945
+ };
4946
+ if (work.accessOrigin === "account") return work;
4947
+ const access = work.accessConfirmed ? work.access : work.classificationRecommendation?.access ?? work.access;
4948
+ return {
4949
+ ...work,
4950
+ access,
4951
+ accessOrigin: work.accessConfirmed || !work.classificationRecommendation ? work.accessOrigin : "model",
4952
+ accessConfirmed: access !== "public"
4953
+ };
4954
+ })
4955
+ });
4956
+ }
4957
+ function setWorkType(state, workId, type, decision) {
4958
+ if (state.scope.status === "confirmed") throw new ApplicationError("INVALID_TRANSITION");
4959
+ const projectType = decision ? {
4960
+ type,
4961
+ reason: "Chosen by your delegate for this journey.",
4962
+ origin: "delegate"
4963
+ } : {
4964
+ type,
4965
+ reason: "Chosen by you.",
4966
+ origin: "explicit"
4967
+ };
4968
+ return {
4969
+ ...state,
4970
+ scope: {
4971
+ status: "pending",
4972
+ actor: null,
4973
+ at: null
4974
+ },
4975
+ privacy: {
4976
+ ...state.privacy,
4977
+ cards: [],
4978
+ confirmedAnswersRevision: null,
4979
+ packageApproval: null
4980
+ },
4981
+ works: state.works.map((work) => work.id !== workId || decision && work.projectType?.origin === "explicit" ? work : {
4982
+ ...work,
4983
+ projectType
4984
+ })
4985
+ };
4986
+ }
4987
+ /** One rule for single and bulk choices; selection changes recompute reading completion. */
4988
+ function setWorksAccess(state, workIds, access, decision, accessChoice) {
4989
+ if (decision && access === "public") throw new ApplicationError("CONFIRMATION_REQUIRED");
4990
+ if (new Set(workIds).size !== workIds.length) throw new ApplicationError("INVALID_SELECTION");
4991
+ if (workIds.some((id) => !state.works.some((work) => work.id === id))) throw new ApplicationError("UNKNOWN_WORKSPACE");
4992
+ if (state.scope.status === "confirmed") return changeConfirmedAccess(state, workIds, access, decision, accessChoice);
4993
+ return withReadingProgress({
4994
+ ...state,
4995
+ scope: {
4996
+ status: "pending",
4997
+ actor: null,
4998
+ at: null
4999
+ },
5000
+ privacy: {
5001
+ ...state.privacy,
5002
+ cards: [],
5003
+ confirmedAnswersRevision: null,
5004
+ packageApproval: null
5005
+ },
5006
+ works: state.works.map((work) => !workIds.includes(work.id) ? work : {
5007
+ ...work,
5008
+ revision: work.revision + 1,
5009
+ artifact: null,
5010
+ status: work.status === "ready" ? "review" : work.status,
5011
+ access,
5012
+ accessConfirmed: true,
5013
+ accountAccessConflict: false,
5014
+ accessChoice: access === "public" ? accessChoice : void 0,
5015
+ accessOrigin: decision && work.accessOrigin !== "explicit" ? "delegate" : "explicit"
5016
+ })
5017
+ });
5018
+ }
5019
+ /**
5020
+ * After confirmation the person, never a delegate, may still change any Work's visibility
5021
+ * (D2, superseding ADR 0162's lock and ADR 0188's blocked-only Drop). Drop stops that Work's
5022
+ * scan; a Work moved out of Drop is read again; reading progress is recomputed.
5023
+ */
5024
+ function changeConfirmedAccess(state, workIds, access, decision, accessChoice) {
5025
+ if (decision || state.phase === "sending" || state.phase === "completed" || state.works.some((work) => workIds.includes(work.id) && work.access === access && work.accessConfirmed && hasPublicChoice(work))) throw new ApplicationError("INVALID_TRANSITION");
5026
+ if (access === "public" && state.works.every((work) => !workIds.includes(work.id) || work.access === "public" && work.accessConfirmed)) return {
5027
+ ...state,
5028
+ works: state.works.map((work) => workIds.includes(work.id) && accessChoice ? {
5029
+ ...work,
5030
+ accessOrigin: "explicit",
5031
+ accessChoice
5032
+ } : work)
5033
+ };
5034
+ return withReadingProgress({
5035
+ ...state,
5036
+ privacy: {
5037
+ ...state.privacy,
5038
+ packageApproval: null
5039
+ },
5040
+ works: state.works.map((work) => workIds.includes(work.id) ? {
5041
+ ...work,
5042
+ revision: work.revision + 1,
5043
+ artifact: null,
5044
+ status: work.status === "ready" ? "review" : work.status,
5045
+ access,
5046
+ accessConfirmed: true,
5047
+ accountAccessConflict: false,
5048
+ accessChoice: access === "public" ? accessChoice : void 0,
5049
+ accessOrigin: "explicit"
5050
+ } : work)
5051
+ });
5052
+ }
5053
+ /** Model suggestions fill or refresh model types only; a person's or delegate's type stays. */
5054
+ function applyClassification(state, items) {
5055
+ if (state.scope.status === "confirmed") throw new ApplicationError("INVALID_TRANSITION");
5056
+ const byWork = new Map(items.map((item) => [item.workId, item]));
5057
+ if (byWork.size !== items.length) throw new ApplicationError("INVALID_SELECTION");
5058
+ if (items.some((item) => !state.works.some((work) => work.id === item.workId))) throw new ApplicationError("UNKNOWN_WORKSPACE");
5059
+ return {
5060
+ ...state,
5061
+ works: state.works.map((work) => {
5062
+ const item = byWork.get(work.id);
5063
+ if (!item || work.projectType && work.projectType.origin !== "model") return work;
5064
+ return {
5065
+ ...work,
5066
+ projectType: {
5067
+ type: item.type,
5068
+ reason: item.reason,
5069
+ origin: "model"
5070
+ }
5071
+ };
5072
+ })
5073
+ };
5074
+ }
5075
+ //#endregion
4833
5076
  //#region packages/application/src/journey/scope/proposal.ts
4834
5077
  const count$3 = z.number().int().nonnegative().max(Number.MAX_SAFE_INTEGER);
4835
5078
  const workspaces = z.array(z.string().min(1).max(128));
@@ -4851,6 +5094,7 @@ const classificationCommitSchema = z.object({
4851
5094
  /** `classificationPartition` of the state the proposal was prepared against. */
4852
5095
  partition: journeyDigestSchema,
4853
5096
  late: z.boolean(),
5097
+ accountAccess: z.record(z.string(), accountWorkAccessSchema).optional(),
4854
5098
  items: z.array(journeyClassificationSubmitSchema.shape.items.element.extend({
4855
5099
  workId: journeyIdSchema,
4856
5100
  workspaceIds: workspaces
@@ -4874,13 +5118,22 @@ function classificationPartition(state) {
4874
5118
  work.rootSessionIds.toSorted()
4875
5119
  ]))).digest("hex");
4876
5120
  }
5121
+ /**
5122
+ * Two placed Works one late item may join: the same access, origin, confirmation, recorded
5123
+ * Public choice and account access conflict, as local-host isolation.ts compares. The Skill's `validateLatePlacedWorks` mirrors this rule before commit.
5124
+ */
5125
+ function sameAccessRecord(work, other) {
5126
+ return work.access === other?.access && work.accessOrigin === other.accessOrigin && work.accessConfirmed === other.accessConfirmed && work.accessChoice?.channel === other.accessChoice?.channel && work.accessChoice?.at === other.accessChoice?.at && work.accountAccessConflict === other.accountAccessConflict;
5127
+ }
4877
5128
  /** The proposal's Works built from the given state's current Works (E2). */
4878
5129
  function classificationWorks(state, plan) {
4879
5130
  const late = plan.late;
4880
5131
  const works = [];
4881
5132
  for (const item of plan.items) {
4882
5133
  const existing = state.works.find((work) => work.placement !== "classifying" && work.rootSessionIds.length === item.rootSessionIds.length && work.rootSessionIds.every((root) => item.rootSessionIds.includes(root)));
4883
- const extended = late ? state.works.find((work) => work.placement === "placed" && work.rootSessionIds.every((root) => item.rootSessionIds.includes(root))) : void 0;
5134
+ const owners = late ? state.works.filter((work) => work.placement === "placed" && work.rootSessionIds.some((root) => item.rootSessionIds.includes(root))) : [];
5135
+ if (owners.some((work) => !sameAccessRecord(work, owners[0]))) throw new ApplicationError("INVALID_SELECTION", "classification.late.several-works");
5136
+ const extended = owners.find((work) => work.rootSessionIds.every((root) => item.rootSessionIds.includes(root)));
4884
5137
  if (late && existing) {
4885
5138
  works.push(existing);
4886
5139
  continue;
@@ -4891,6 +5144,9 @@ function classificationWorks(state, plan) {
4891
5144
  sourceWorkspaceIds: [.../* @__PURE__ */ new Set([...extended.sourceWorkspaceIds, ...item.workspaceIds])]
4892
5145
  } : newJourneyWork(item.workId, item.projectName, item.rootSessionIds, item.workspaceIds, "placed"));
4893
5146
  const { classificationMetadata: _display, ...original } = base;
5147
+ const account = plan.accountAccess?.[base.id];
5148
+ const inherit = late && account && base.accessOrigin !== "explicit";
5149
+ const unproven = extended && base.accessChoice?.channel === "account" && account !== "public-all";
4894
5150
  works.push({
4895
5151
  ...original,
4896
5152
  projectName: item.projectName,
@@ -4905,8 +5161,13 @@ function classificationWorks(state, plan) {
4905
5161
  ...late && !extended ? {
4906
5162
  access: item.recommendation.access,
4907
5163
  accessOrigin: "model",
4908
- accessConfirmed: true
5164
+ accessConfirmed: item.recommendation.access !== "public"
5165
+ } : {},
5166
+ ...unproven ? {
5167
+ accessConfirmed: false,
5168
+ accessChoice: void 0
4909
5169
  } : {},
5170
+ ...inherit && account ? accountSeededAccess(account, plan.at) : {},
4910
5171
  classificationRecommendation: item.recommendation,
4911
5172
  suggestion: null,
4912
5173
  artifact: null,
@@ -5179,7 +5440,7 @@ function assertDelegatedDecision(state, action, decision, source) {
5179
5440
  }
5180
5441
  if (action.kind === "cards-confirm") return;
5181
5442
  if (action.kind === "work-access" || action.kind === "works-access") {
5182
- if (action.access === "drop") throw new ApplicationError("CONFIRMATION_REQUIRED");
5443
+ if (action.access === "drop" || action.access === "public") throw new ApplicationError("CONFIRMATION_REQUIRED");
5183
5444
  const ids = action.kind === "work-access" ? [action.workId] : action.workIds;
5184
5445
  if (state.works.some((work) => ids.includes(work.id) && work.accessOrigin === "explicit" && work.access !== action.access)) throw new ApplicationError("CONFIRMATION_REQUIRED");
5185
5446
  return;
@@ -5259,135 +5520,6 @@ function propagateGroups(state, action) {
5259
5520
  };
5260
5521
  }
5261
5522
  //#endregion
5262
- //#region packages/application/src/journey/scope/respond.ts
5263
- /** Classification confirmation fixes visibility and scan membership for this journey. */
5264
- function confirmScope(state, now, decision) {
5265
- if (state.scope.status === "confirmed") return state;
5266
- if (state.classificationRevision === null || !state.works.length || state.works.some((work) => work.placement !== "placed" && work.placement !== "classifying" || work.placement === "placed" && !(work.status === "blocked" && work.access === "drop" && work.accessConfirmed) && (!work.description.trim() || !work.projectType))) throw new ApplicationError("SCOPE_NOT_READY");
5267
- return withReadingProgress({
5268
- ...state,
5269
- scope: {
5270
- status: "confirmed",
5271
- actor: decision ? "delegate" : "human",
5272
- at: now
5273
- },
5274
- works: state.works.map((work) => work.placement === "classifying" ? work : work.accessConfirmed || !work.classificationRecommendation ? {
5275
- ...work,
5276
- accessConfirmed: true
5277
- } : {
5278
- ...work,
5279
- access: work.classificationRecommendation.access,
5280
- accessOrigin: "model",
5281
- accessConfirmed: true
5282
- })
5283
- });
5284
- }
5285
- function setWorkType(state, workId, type, decision) {
5286
- if (state.scope.status === "confirmed") throw new ApplicationError("INVALID_TRANSITION");
5287
- const projectType = decision ? {
5288
- type,
5289
- reason: "Chosen by your delegate for this journey.",
5290
- origin: "delegate"
5291
- } : {
5292
- type,
5293
- reason: "Chosen by you.",
5294
- origin: "explicit"
5295
- };
5296
- return {
5297
- ...state,
5298
- scope: {
5299
- status: "pending",
5300
- actor: null,
5301
- at: null
5302
- },
5303
- privacy: {
5304
- ...state.privacy,
5305
- cards: [],
5306
- confirmedAnswersRevision: null,
5307
- packageApproval: null
5308
- },
5309
- works: state.works.map((work) => work.id !== workId || decision && work.projectType?.origin === "explicit" ? work : {
5310
- ...work,
5311
- projectType
5312
- })
5313
- };
5314
- }
5315
- /** One rule for single and bulk choices; selection changes recompute reading completion. */
5316
- function setWorksAccess(state, workIds, access, decision) {
5317
- if (new Set(workIds).size !== workIds.length) throw new ApplicationError("INVALID_SELECTION");
5318
- if (workIds.some((id) => !state.works.some((work) => work.id === id))) throw new ApplicationError("UNKNOWN_WORKSPACE");
5319
- if (state.scope.status === "confirmed") return changeConfirmedAccess(state, workIds, access, decision);
5320
- return withReadingProgress({
5321
- ...state,
5322
- scope: {
5323
- status: "pending",
5324
- actor: null,
5325
- at: null
5326
- },
5327
- privacy: {
5328
- ...state.privacy,
5329
- cards: [],
5330
- confirmedAnswersRevision: null,
5331
- packageApproval: null
5332
- },
5333
- works: state.works.map((work) => !workIds.includes(work.id) ? work : {
5334
- ...work,
5335
- revision: work.revision + 1,
5336
- artifact: null,
5337
- status: work.status === "ready" ? "review" : work.status,
5338
- access,
5339
- accessConfirmed: true,
5340
- accessOrigin: decision && work.accessOrigin !== "explicit" ? "delegate" : "explicit"
5341
- })
5342
- });
5343
- }
5344
- /**
5345
- * After confirmation the person, never a delegate, may still change any Work's visibility
5346
- * (D2, superseding ADR 0162's lock and ADR 0188's blocked-only Drop). Drop stops that Work's
5347
- * scan; a Work moved out of Drop is read again; reading progress is recomputed.
5348
- */
5349
- function changeConfirmedAccess(state, workIds, access, decision) {
5350
- if (decision || state.phase === "sending" || state.phase === "completed" || state.works.some((work) => workIds.includes(work.id) && work.access === access)) throw new ApplicationError("INVALID_TRANSITION");
5351
- return withReadingProgress({
5352
- ...state,
5353
- privacy: {
5354
- ...state.privacy,
5355
- packageApproval: null
5356
- },
5357
- works: state.works.map((work) => workIds.includes(work.id) ? {
5358
- ...work,
5359
- revision: work.revision + 1,
5360
- artifact: null,
5361
- status: work.status === "ready" ? "review" : work.status,
5362
- access,
5363
- accessConfirmed: true,
5364
- accessOrigin: "explicit"
5365
- } : work)
5366
- });
5367
- }
5368
- /** Model suggestions fill or refresh model types only; a person's or delegate's type stays. */
5369
- function applyClassification(state, items) {
5370
- if (state.scope.status === "confirmed") throw new ApplicationError("INVALID_TRANSITION");
5371
- const byWork = new Map(items.map((item) => [item.workId, item]));
5372
- if (byWork.size !== items.length) throw new ApplicationError("INVALID_SELECTION");
5373
- if (items.some((item) => !state.works.some((work) => work.id === item.workId))) throw new ApplicationError("UNKNOWN_WORKSPACE");
5374
- return {
5375
- ...state,
5376
- works: state.works.map((work) => {
5377
- const item = byWork.get(work.id);
5378
- if (!item || work.projectType && work.projectType.origin !== "model") return work;
5379
- return {
5380
- ...work,
5381
- projectType: {
5382
- type: item.type,
5383
- reason: item.reason,
5384
- origin: "model"
5385
- }
5386
- };
5387
- })
5388
- };
5389
- }
5390
- //#endregion
5391
5523
  //#region packages/application/src/journey/respond.ts
5392
5524
  /** A changed privacy instruction requires a new read of the same frozen roots. */
5393
5525
  function invalidateAnalysisIntent(state) {
@@ -5411,7 +5543,7 @@ function invalidateAnalysisIntent(state) {
5411
5543
  })
5412
5544
  });
5413
5545
  }
5414
- function respondToJourney(state, action, source, now, decision, preferenceScope) {
5546
+ function respondToJourney(state, action, source, now, decision, preferenceScope, accountAccess) {
5415
5547
  if (preferenceScope && (decision || !["group", "groups-hide"].includes(action.kind))) throw new ApplicationError("INVALID_ARGUMENTS");
5416
5548
  if (state.abandonedAt) throw new ApplicationError("INVALID_TRANSITION");
5417
5549
  if (action.kind === "abandon") {
@@ -5525,10 +5657,14 @@ function respondToJourney(state, action, source, now, decision, preferenceScope)
5525
5657
  };
5526
5658
  if (action.kind === "scope-confirm") {
5527
5659
  if (source !== "conversation" || decision) throw new ApplicationError("CONFIRMATION_REQUIRED");
5528
- return confirmScope(state, now);
5660
+ return confirmScope(state, now, void 0, accountAccess);
5529
5661
  }
5530
5662
  if (action.kind === "card-answer" || action.kind === "cards-confirm" || action.kind === "package-confirm") throw new ApplicationError("INVALID_TRANSITION");
5531
- if (action.kind === "works-access") return setWorksAccess(state, action.workIds, action.access, decision);
5663
+ if ((action.kind === "work-access" || action.kind === "works-access") && action.access === "public" && source === "conversation" && (!action.accessChoice || action.accessChoice.channel !== "conversation" || !Number.isFinite(Date.parse(action.accessChoice.at)))) throw new ApplicationError("CONFIRMATION_REQUIRED");
5664
+ if (action.kind === "works-access") return setWorksAccess(state, action.workIds, action.access, decision, source === "panel" ? {
5665
+ channel: source,
5666
+ at: now
5667
+ } : action.accessChoice);
5532
5668
  if (action.kind === "group" || action.kind === "groups-hide") {
5533
5669
  const ids = action.kind === "group" ? [action.groupId] : action.groupIds;
5534
5670
  if (new Set(ids).size !== ids.length || ids.some((id) => !state.groups.some((group) => group.id === id))) throw new ApplicationError("UNKNOWN_ITEM");
@@ -5559,7 +5695,10 @@ function respondToJourney(state, action, source, now, decision, preferenceScope)
5559
5695
  })
5560
5696
  });
5561
5697
  }
5562
- return setWorksAccess(state, [workId], action.access, decision);
5698
+ return setWorksAccess(state, [workId], action.access, decision, source === "panel" ? {
5699
+ channel: source,
5700
+ at: now
5701
+ } : action.accessChoice);
5563
5702
  }
5564
5703
  //#endregion
5565
5704
  //#region packages/application/src/journey/questions.ts
@@ -6351,6 +6490,59 @@ function journeyView(state, now) {
6351
6490
  }
6352
6491
  /** Works rendered at once by one several-Work call. */
6353
6492
  const renderConcurrency = 4;
6493
+ const inFlightRenders = /* @__PURE__ */ new WeakMap();
6494
+ const renderScopeTails = /* @__PURE__ */ new WeakMap();
6495
+ function waitForRender(pending, signal) {
6496
+ signal.throwIfAborted();
6497
+ return new Promise((resolve, reject) => {
6498
+ const abort = () => reject(signal.reason);
6499
+ signal.addEventListener("abort", abort, { once: true });
6500
+ pending.then(resolve, reject).finally(() => signal.removeEventListener("abort", abort));
6501
+ });
6502
+ }
6503
+ async function acquireRenderScope(port, signal) {
6504
+ const previous = renderScopeTails.get(port);
6505
+ const gate = Promise.withResolvers();
6506
+ const tail = (previous ?? Promise.resolve()).then(() => gate.promise);
6507
+ renderScopeTails.set(port, tail);
6508
+ const release = () => {
6509
+ gate.resolve();
6510
+ if (renderScopeTails.get(port) === tail) renderScopeTails.delete(port);
6511
+ };
6512
+ try {
6513
+ await waitForRender(previous ?? Promise.resolve(), signal);
6514
+ return {
6515
+ release,
6516
+ waited: previous !== void 0
6517
+ };
6518
+ } catch (error) {
6519
+ tail.then(() => {
6520
+ if (renderScopeTails.get(port) === tail) renderScopeTails.delete(port);
6521
+ });
6522
+ gate.resolve();
6523
+ throw error;
6524
+ }
6525
+ }
6526
+ async function joinRender(port, journeyId, workId, run, signal, retryConflict = true) {
6527
+ let pending = inFlightRenders.get(port);
6528
+ if (!pending) {
6529
+ pending = /* @__PURE__ */ new Map();
6530
+ inFlightRenders.set(port, pending);
6531
+ }
6532
+ const key = `${journeyId}:${workId}`;
6533
+ const existing = pending.get(key);
6534
+ if (existing) try {
6535
+ return await waitForRender(existing, signal);
6536
+ } catch (error) {
6537
+ signal.throwIfAborted();
6538
+ if (retryConflict && safeCode(error) === "REVISION_CONFLICT") return joinRender(port, journeyId, workId, run, signal, false);
6539
+ throw error;
6540
+ }
6541
+ const entries = pending;
6542
+ const result = Promise.resolve().then(run).finally(() => entries.delete(key));
6543
+ entries.set(key, result);
6544
+ return result;
6545
+ }
6354
6546
  function createNativeJourney(storage, now = () => (/* @__PURE__ */ new Date()).toISOString(), local) {
6355
6547
  /** Callers present after their write is durable, so a concurrent commit must not turn it into an error. */
6356
6548
  async function present(committed) {
@@ -6634,7 +6826,8 @@ function createNativeJourney(storage, now = () => (/* @__PURE__ */ new Date()).t
6634
6826
  const current = await storage.current(input.journeyId);
6635
6827
  if (!current) throw new ApplicationError("UNKNOWN_ITEM");
6636
6828
  const expectedRevision = rebase ? current.revision : input.expectedRevision;
6637
- const state = current.revision === expectedRevision ? respondToJourney(current, input.action, source, now(), input.decision, input.preferenceScope) : current;
6829
+ const accountAccess = current.revision === expectedRevision && input.action.kind === "scope-confirm" && current.scope.status !== "confirmed" && source === "conversation" && !input.decision ? await local?.accountAccess?.(current, new AbortController().signal) : void 0;
6830
+ const state = current.revision === expectedRevision ? respondToJourney(current, input.action, source, now(), input.decision, input.preferenceScope, accountAccess) : current;
6638
6831
  const groupIds = input.action.kind === "group" ? [input.action.groupId] : input.action.kind === "groups-hide" ? input.action.groupIds : [];
6639
6832
  const preferenceScopes = input.decision ? [] : state.groups.flatMap((group) => groupIds.includes(group.id) && group.preferenceScope ? [{
6640
6833
  groupId: group.id,
@@ -6750,7 +6943,7 @@ function createNativeJourney(storage, now = () => (/* @__PURE__ */ new Date()).t
6750
6943
  const release = await port.renderActivity?.begin(input.journeyId, workIds);
6751
6944
  let result;
6752
6945
  try {
6753
- result = "workId" in input ? { state: await port.render(input.journeyId, input.workId, signal) } : {
6946
+ result = "workId" in input ? { state: await joinRender(port, input.journeyId, input.workId, () => port.render(input.journeyId, input.workId, signal), signal) } : {
6754
6947
  renders: await renderSeveral(port, input.journeyId, workIds, signal),
6755
6948
  state: await currentState(input.journeyId)
6756
6949
  };
@@ -6770,35 +6963,51 @@ function createNativeJourney(storage, now = () => (/* @__PURE__ */ new Date()).t
6770
6963
  }
6771
6964
  async function renderSeveral(port, journeyId, workIds, signal) {
6772
6965
  const renders = [];
6773
- let next = 0;
6774
- const scope = await port.renderScope?.(journeyId, workIds.length, signal);
6775
- const renderWork = scope ? (workId) => scope.render(workId) : (workId) => port.render(journeyId, workId, signal);
6966
+ const lease = await acquireRenderScope(port, signal);
6776
6967
  try {
6777
- const failure = (await Promise.allSettled(Array.from({ length: Math.min(scope?.concurrency ?? renderConcurrency, workIds.length) }, async () => {
6778
- for (let workId = workIds[next++]; workId !== void 0; workId = workIds[next++]) {
6779
- try {
6780
- await renderWork(workId);
6781
- renders.push({
6782
- workId,
6783
- status: "rendered"
6784
- });
6785
- } catch (error) {
6786
- signal.throwIfAborted();
6787
- port.renderFailed?.(workId, error);
6788
- renders.push({
6789
- workId,
6790
- status: "failed",
6791
- code: safeCode(error)
6792
- });
6968
+ const current = lease.waited ? await currentState(journeyId) : void 0;
6969
+ const completed = new Set(current?.works.filter((work) => work.artifact?.revision === work.revision && work.status === "ready" && !current.pendingHideId && current.phase !== "paused").map((work) => work.id));
6970
+ const remaining = workIds.filter((workId) => {
6971
+ if (!completed.has(workId)) return true;
6972
+ renders.push({
6973
+ workId,
6974
+ status: "rendered"
6975
+ });
6976
+ return false;
6977
+ });
6978
+ let next = 0;
6979
+ const scope = remaining.length ? await port.renderScope?.(journeyId, remaining.length, signal) : void 0;
6980
+ const renderWork = scope ? (workId) => scope.render(workId) : (workId) => port.render(journeyId, workId, signal);
6981
+ try {
6982
+ const failure = (await Promise.allSettled(Array.from({ length: Math.min(scope?.concurrency ?? renderConcurrency, remaining.length) }, async () => {
6983
+ for (let workId = remaining[next++]; workId !== void 0; workId = remaining[next++]) {
6984
+ try {
6985
+ await joinRender(port, journeyId, workId, () => renderWork(workId), signal);
6986
+ renders.push({
6987
+ workId,
6988
+ status: "rendered"
6989
+ });
6990
+ } catch (error) {
6991
+ signal.throwIfAborted();
6992
+ port.renderFailed?.(workId, error);
6993
+ renders.push({
6994
+ workId,
6995
+ status: "failed",
6996
+ code: safeCode(error)
6997
+ });
6998
+ }
6999
+ await new Promise((resolve) => setImmediate(resolve));
6793
7000
  }
6794
- await new Promise((resolve) => setImmediate(resolve));
6795
- }
6796
- }))).find((item) => item.status === "rejected");
6797
- if (failure) throw failure.reason;
7001
+ }))).find((item) => item.status === "rejected");
7002
+ if (failure) throw failure.reason;
7003
+ } finally {
7004
+ await scope?.close();
7005
+ }
7006
+ const order = new Map(workIds.map((workId, index) => [workId, index]));
7007
+ return renders.toSorted((a, b) => (order.get(a.workId) ?? 0) - (order.get(b.workId) ?? 0));
6798
7008
  } finally {
6799
- await scope?.close();
7009
+ lease.release();
6800
7010
  }
6801
- return renders.toSorted((a, b) => workIds.indexOf(a.workId) - workIds.indexOf(b.workId));
6802
7011
  }
6803
7012
  async function findings(value, signal = new AbortController().signal) {
6804
7013
  if (!local) throw new ApplicationError("INVALID_TRANSITION");
@@ -6888,7 +7097,32 @@ function createNativeJourney(storage, now = () => (/* @__PURE__ */ new Date()).t
6888
7097
  const current = await storage.current(input.journeyId);
6889
7098
  if (!current) throw new ApplicationError("UNKNOWN_ITEM");
6890
7099
  const expectedRevision = rebase ? current.revision : input.expectedRevision;
6891
- const state = current.revision !== expectedRevision ? current : "resolutions" in input ? resolvePrivacyTerms(current, input) : publishDecisionCards(current, input);
7100
+ let state;
7101
+ try {
7102
+ state = current.revision !== expectedRevision ? current : "resolutions" in input ? resolvePrivacyTerms(current, input) : publishDecisionCards(current, input);
7103
+ } catch (error) {
7104
+ if (!rebase || current.revision <= input.expectedRevision || !(error instanceof ApplicationError) || error.code !== "INVALID_SELECTION") throw error;
7105
+ const receipt = await storage.commit({
7106
+ expectedRevision: input.expectedRevision,
7107
+ requestKey: input.idempotencyKey,
7108
+ requestHash: digest$1(input),
7109
+ state: current,
7110
+ event: {
7111
+ journeyId: current.journeyId,
7112
+ kind: "response",
7113
+ source: "conversation",
7114
+ at: now()
7115
+ }
7116
+ }).catch((probeError) => {
7117
+ if (probeError instanceof ApplicationError && probeError.code === "OPERATION_CONFLICT") throw error;
7118
+ throw probeError;
7119
+ });
7120
+ if (receipt.status === "replayed") return {
7121
+ ...receipt,
7122
+ state: await present(receipt.state)
7123
+ };
7124
+ throw error;
7125
+ }
6892
7126
  const result = await storage.commit({
6893
7127
  expectedRevision,
6894
7128
  requestKey: input.idempotencyKey,
@@ -11749,6 +11983,48 @@ const storedCredentialSchema = accountProfileSchema.extend({
11749
11983
  expiresAt: z.string().min(1).max(64)
11750
11984
  }).refine((value) => profileUrlMatchesIdentity(value.profileUrl, value.identityUrl));
11751
11985
  //#endregion
11986
+ //#region packages/application/src/account/renew.ts
11987
+ const renewBeforeMs = 12e4;
11988
+ const renewTimeoutMs = 3e4;
11989
+ const renewals = /* @__PURE__ */ new WeakMap();
11990
+ function renew(account, credential) {
11991
+ function configured(value) {
11992
+ return value?.identityUrl === account.configuration.identityUrl && value.clientId === account.configuration.clientId ? value : null;
11993
+ }
11994
+ const running = renewals.get(account);
11995
+ if (running) return running;
11996
+ const renewing = (async () => {
11997
+ try {
11998
+ const stored = configured((await account.credentials.update((state) => state)).credential);
11999
+ if (!stored || stored.refreshToken !== credential.refreshToken) return stored;
12000
+ const token = await account.identity.refresh(credential.refreshToken, AbortSignal.timeout(renewTimeoutMs));
12001
+ if (!token) return configured((await account.credentials.update((state) => {
12002
+ if (state.credential?.refreshToken === credential.refreshToken) {
12003
+ state.credential = null;
12004
+ state.connection = null;
12005
+ state.lastError = "SIGN_IN_EXPIRED";
12006
+ }
12007
+ return state;
12008
+ })).credential);
12009
+ return configured((await account.credentials.update((value) => {
12010
+ if (value.credential?.refreshToken === credential.refreshToken) value.credential = {
12011
+ ...value.credential,
12012
+ accessToken: token.accessToken,
12013
+ refreshToken: token.refreshToken ?? credential.refreshToken,
12014
+ scope: token.scope,
12015
+ issuedAt: account.now(),
12016
+ expiresAt: token.expiresAt
12017
+ };
12018
+ return value;
12019
+ })).credential);
12020
+ } finally {
12021
+ renewals.delete(account);
12022
+ }
12023
+ })();
12024
+ renewals.set(account, renewing);
12025
+ return renewing;
12026
+ }
12027
+ //#endregion
11752
12028
  //#region packages/application/src/account/poll-interval.ts
11753
12029
  /** RFC 8628 section 3.5: each `slow_down` lengthens the interval by five seconds. */
11754
12030
  const slowDownStepMs = 5e3;
@@ -12037,11 +12313,41 @@ function createSharedAccount(ports) {
12037
12313
  return stop(true);
12038
12314
  },
12039
12315
  async verify() {
12040
- const saved = (await read()).credential;
12316
+ let saved = (await read()).credential;
12041
12317
  if (!saved || !belongsToConfiguration(saved)) throw new ApplicationError("ACCOUNT_SIGNED_OUT");
12042
- const result = await ports.connection.check(saved.accessToken, shutdown.signal);
12318
+ let result;
12319
+ let renewing = false;
12320
+ try {
12321
+ const left = Date.parse(saved.expiresAt) - Date.parse(ports.now());
12322
+ if (saved.refreshToken && (!Number.isFinite(left) || left <= 12e4)) {
12323
+ renewing = true;
12324
+ saved = await renew(ports, {
12325
+ ...saved,
12326
+ refreshToken: saved.refreshToken
12327
+ });
12328
+ renewing = false;
12329
+ if (!saved) return view(await read());
12330
+ }
12331
+ result = await ports.connection.check(saved.accessToken, shutdown.signal);
12332
+ if (result === "rejected" && saved.refreshToken) {
12333
+ renewing = true;
12334
+ saved = await renew(ports, {
12335
+ ...saved,
12336
+ refreshToken: saved.refreshToken
12337
+ });
12338
+ renewing = false;
12339
+ if (!saved) return view(await read());
12340
+ result = await ports.connection.check(saved.accessToken, shutdown.signal);
12341
+ }
12342
+ } catch (error) {
12343
+ if (safeCode(error) !== "IDENTITY_UNAVAILABLE" && !(renewing && saved && !expired(saved.expiresAt, ports.now()))) throw error;
12344
+ const detail = typeof error === "object" && error !== null && "detail" in error ? ` (${String(error.detail)})` : "";
12345
+ console.error(`Account verification failed: ${safeCode(error)}${detail}`);
12346
+ result = "unavailable";
12347
+ }
12348
+ const checked = saved;
12043
12349
  return view(await update((state) => {
12044
- if (sameCredential(state.credential, saved)) {
12350
+ if (checked && sameCredential(state.credential, checked)) {
12045
12351
  if (result === "rejected") {
12046
12352
  state.credential = null;
12047
12353
  state.connection = null;
@@ -13448,4 +13754,4 @@ function equivalenceDuplicates(batch) {
13448
13754
  return bindings.sort();
13449
13755
  }
13450
13756
  //#endregion
13451
- export { settingsBatchSchema as $, reviewTransportBytes as $n, isReadingComplete as $r, projectPreparationProgress as $t, deviceAuthorizationSchema as A, maximumHideIndexTokens as An, applyCardAnswers as Ar, nativeAdapterVersions as At, inspectionStartSchema as B, confirmedHideTerms as Bn, staticSessionResourceSchema as Br, classificationWorks as Bt, rebindReviewOverlay as C, confirmedHideDeltaSchema as Cn, journeyFindingImpactSchema as Cr, sendPrepareSchema as Ct, staticallyHiddenFindings as D, termHideAdditions as Dn, journeyRenderSchema as Dr, currentJourneyAdapters as Dt, guardedFindingText as E, settleConfirmedHides as En, journeyRenderInputSchema as Er, settleCorrections as Et, sourceIdentityEvidence as F, bTermMatcher as Fn, nativeRespondSchema as Fr, journeyBatchReadSchema as Ft, validateInspectionClaim as G, retireWorkDescription as Gn, sendArtifactSchema as Gr, journeyContextSchema as Gt, validateInspectionBatches as H, journeyConfirmedHides as Hn, agentSourceSchema as Hr, newJourneyWork as Ht, withSourceEvidence as I, bWordBounded as In, mergePrivacyTerms as Ir, journeyReviewStorageQuerySchema as It, validateInspectionInputs as J, workCorrectionBinding as Jn, sendResolutionSchema as Jr, hidePublishSchema as Jt, validateInspectionFence as K, workDecisionDigest as Kn, sendReceiptSchema as Kr, hideBeginSchema as Kt, agentSetupKey as L, confirmedHideGroupIds as Ln, hasCurrentStaticReview as Lr, applyClassificationProposal as Lt, profileUrlMatchesIdentity as M, bContainment as Mn, privacyDecisionDigest as Mr, validateAttemptAppend as Mt, readAccountAvatarUrl as N, bExceptionRanges as Nn, nativeEventsSchema as Nr, journeyGuidance as Nt, createAccount as O, addHideIndexTokens as On, displayedScopeDigest as Or, obsoleteJourney as Ot, storedCredentialSchema as P, bMaskedHits as Pn, nativeGetSchema as Pr, applyClassification as Pt, inspectionInputs as Q, reviewRepairResourceSchema as Qn, isIncludedWork as Qr, journeyPolicyRespondSchema as Qt, createApplication as R, confirmedHideKey as Rn, staticReviewDigest as Rr, classificationCommitSchema as Rt, rebindReviewAlignment as S, confirmedHideAdditions as Sn, journeyClassificationSessionsSchema as Sr, sendChoiceSchema as St, reviewBatchReuse as T, renderedHideValue as Tn, journeyPreviewSchema as Tr, sendStartSchema as Tt, validateInspectionRequest as U, maskedHitsStale as Un, agentSourcesSchema as Ur, reviewCompileSchema as Ut, validateInspectionBatch as V, dictionaryHides as Vn, agentSourcePathSegment as Vr, retireScanGeneration as Vt, inspectionQuerySchema as W, syncTermGroups as Wn, journeySendSchema as Wr, journeyContextQuerySchema as Wt, validateInspectionContribution as X, reviewCandidateResourceSchema as Xn, hasCompleteWorkDescription as Xr, journeySearchSaveSchema as Xt, validateInspectionQuery as Y, reviewCandidateInputSchema as Yn, sendSettingsSchema as Yr, journeySearchResultSchema as Yt, validateDetectionSummary as Z, reviewRepairInputSchema as Zn, isClassifying as Zr, searchScopeDigest as Zt, changeJourney as _, selectFrozenConfiguration as _i, sessionUsefulnessInputSchema as _n, emptyPreparationFacts as _r, emptyTermsRevision as _t, bCrossSegmentReviewSchema as a, packageCommandSchema as ai, reviewAttemptResourceSchema as an, scanPreparationDigest as ar, defaultAgentSetupInclusion as at, isGenericPublicTerm as b, journeyCommitSchema as bn, preparationStartSchema as br, replaceSensitiveTermsSchema as bt, reconstructBFields as c, gitMetadataBudgetBytes as ci, sessionReconcileInputSchema as cn, scanGitSeedsResource as cr, resolveFailureLocation as ct, resolveGroupSplit as d, sourceChangePolicy as di, sessionRereadInputSchema as dn, validateScanExclusion as dr, manualUploadBindingSchema as dt, isSelectedWork as ei, journeyResourceQuerySchema as en, bReviewDigest as er, settingsQuerySchema as et, selectedValueMembers as f, recommendParallelism as fi, sessionRereadResourceSchema as fn, resolveSourceSelection as fr, manualUploadChoiceSchema as ft, semanticGroupKey as g, freezeConfiguration as gi, sessionReviewStatusSchema as gn, journeyPreparationProgressSchema as gr, candidateSortAvailable as gt, hideScopeDigest as h, defaultScopedSettings as hi, sessionReviewResourceSchema as hn, journeyInventorySchema as hr, manualUploadStartSchema as ht, validateBCrossSegmentReview as i, commandSchema as ii, sortingApplySchema as in, scanCanonicalResourceSchema as ir, classifyAgentSetup as it, deviceProfileScopeSchema as j, packHideIndex as jn, assertJourneyPrivacyReady as jr, attemptBudgetCodePoints as jt, accountProfileSchema as k, hideKeyId as kn, requestedScope as kr, metadataProcessingVersion as kt, chooseCheapPlacement as l, producerVersion as li, sessionReconcileResultSchema as ln, verifiedScanGitSeeds as lr, validateFailureLocation as lt, activeJourneyTerms as m, effectiveTreatmentSchema as mi, sessionReviewQuerySchema as mn, journeyInventoryReadSchema as mr, manualUploadSchema as mt, scanBatchEquivalenceDigest as n, selectedWorks as ni, journeyIsolateSchema as nn, planBRepair as nr, reviewInventory as nt, bRereadDigest as o, selectionBatchSchema as oi, reviewAttemptsQuerySchema as on, scanSessionResourceSchema as or, maximumAgentSetupSessions as ot, splitSelectorValues as p, approvedTreatmentSchema as pi, sessionReviewInputSchema as pn, journeyInventoryQuerySchema as pr, manualUploadPrepareSchema as pt, validateInspectionHead as q, workPrivacyDecisions as qn, sendReplaceResultSchema as qr, hideFinishSchema as qt, locateTermEvidence as r, withReadingProgress as ri, journeyPlaceSchema as rn, scanAliasResourceSchema as rr, appendSensitiveTerms as rt, reconcileBSession as s, selectionSchema as si, reviewAttemptsResultSchema as sn, scanGitSeedsId as sr, artifactFailureDetailSchema as st, scanBatchContextDigest as t, isSkippedWork as ti, journeyResourceSchema as tn, mergeBRepair as tr, readWorkspaceOutcomes as tt, reviseGroupMembership as u, stateSchema as ui, sessionReconciliationResourceSchema as un, scanExclusionResourceSchema as ur, manualUploadActionSchema as ut, projectJourneyInventory as v, staticCatalogRef as vi, sessionUsefulnessResourceSchema as vn, preparationBeginSchema as vr, maximumTermsFileBytes as vt, rebindReviewSubmit as w, readConfirmedHideChange as wn, journeyFindingsSchema as wr, sendRequestSchema as wt, bReviewScope as x, journeyEventSchema as xn, validatePreparationFacts as xr, sendActionSchema as xt, preparationScope as y, journeyCommitResultSchema as yn, preparationSettlementSchema as yr, normalizeSensitiveTerms as yt, inspectionCancelSchema as z, confirmedHideKinds as zn, staticSessionId as zr, classificationPartition as zt };
13757
+ export { validateDetectionSummary as $, reviewRepairInputSchema as $n, isClassifying as $r, searchScopeDigest as $t, renewBeforeMs as A, addHideIndexTokens as An, displayedScopeDigest as Ar, obsoleteJourney as At, createApplication as B, confirmedHideKey as Bn, staticReviewDigest as Br, classificationPartition as Bt, rebindReviewOverlay as C, journeyEventSchema as Cn, validatePreparationFacts as Cr, sendActionSchema as Ct, staticallyHiddenFindings as D, renderedHideValue as Dn, journeyPreviewSchema as Dr, sendStartSchema as Dt, guardedFindingText as E, readConfirmedHideChange as En, journeyFindingsSchema as Er, sendRequestSchema as Et, readAccountAvatarUrl as F, bExceptionRanges as Fn, nativeEventsSchema as Fr, journeyGuidance as Ft, validateInspectionRequest as G, maskedHitsStale as Gn, agentSourcesSchema as Gr, reviewCompileSchema as Gt, inspectionStartSchema as H, confirmedHideTerms as Hn, staticSessionResourceSchema as Hr, applyClassification as Ht, storedCredentialSchema as I, bMaskedHits as In, nativeGetSchema as Ir, journeyBatchReadSchema as It, validateInspectionFence as J, workDecisionDigest as Jn, sendReceiptSchema as Jr, hideBeginSchema as Jt, inspectionQuerySchema as K, syncTermGroups as Kn, journeySendSchema as Kr, journeyContextQuerySchema as Kt, sourceIdentityEvidence as L, bTermMatcher as Ln, nativeRespondSchema as Lr, journeyReviewStorageQuerySchema as Lt, deviceAuthorizationSchema as M, maximumHideIndexTokens as Mn, applyCardAnswers as Mr, nativeAdapterVersions as Mt, deviceProfileScopeSchema as N, packHideIndex as Nn, assertJourneyPrivacyReady as Nr, attemptBudgetCodePoints as Nt, createAccount as O, settleConfirmedHides as On, journeyRenderInputSchema as Or, settleCorrections as Ot, profileUrlMatchesIdentity as P, bContainment as Pn, privacyDecisionDigest as Pr, validateAttemptAppend as Pt, validateInspectionContribution as Q, reviewCandidateResourceSchema as Qn, hasCompleteWorkDescription as Qr, journeySearchSaveSchema as Qt, withSourceEvidence as R, bWordBounded as Rn, mergePrivacyTerms as Rr, applyClassificationProposal as Rt, rebindReviewAlignment as S, journeyCommitSchema as Sn, preparationStartSchema as Sr, replaceSensitiveTermsSchema as St, reviewBatchReuse as T, confirmedHideDeltaSchema as Tn, journeyFindingImpactSchema as Tr, sendPrepareSchema as Tt, validateInspectionBatch as U, dictionaryHides as Un, agentSourcePathSegment as Ur, retireScanGeneration as Ut, inspectionCancelSchema as V, confirmedHideKinds as Vn, staticSessionId as Vr, classificationWorks as Vt, validateInspectionBatches as W, journeyConfirmedHides as Wn, agentSourceSchema as Wr, newJourneyWork as Wt, validateInspectionInputs as X, workCorrectionBinding as Xn, sendResolutionSchema as Xr, hidePublishSchema as Xt, validateInspectionHead as Y, workPrivacyDecisions as Yn, sendReplaceResultSchema as Yr, hideFinishSchema as Yt, validateInspectionQuery as Z, reviewCandidateInputSchema as Zn, sendSettingsSchema as Zr, journeySearchResultSchema as Zt, changeJourney as _, defaultScopedSettings as _i, sessionReviewResourceSchema as _n, journeyInventorySchema as _r, manualUploadStartSchema as _t, bCrossSegmentReviewSchema as a, withReadingProgress as ai, journeyPlaceSchema as an, scanAliasResourceSchema as ar, appendSensitiveTerms as at, isGenericPublicTerm as b, staticCatalogRef as bi, sessionUsefulnessResourceSchema as bn, preparationBeginSchema as br, maximumTermsFileBytes as bt, reconstructBFields as c, selectionBatchSchema as ci, reviewAttemptsQuerySchema as cn, scanSessionResourceSchema as cr, maximumAgentSetupSessions as ct, resolveGroupSplit as d, producerVersion as di, sessionReconcileResultSchema as dn, verifiedScanGitSeeds as dr, validateFailureLocation as dt, isIncludedWork as ei, journeyPolicyRespondSchema as en, reviewRepairResourceSchema as er, inspectionInputs as et, selectedValueMembers as f, stateSchema as fi, sessionReconciliationResourceSchema as fn, scanExclusionResourceSchema as fr, manualUploadActionSchema as ft, semanticGroupKey as g, effectiveTreatmentSchema as gi, sessionReviewQuerySchema as gn, journeyInventoryReadSchema as gr, manualUploadSchema as gt, hideScopeDigest as h, approvedTreatmentSchema as hi, sessionReviewInputSchema as hn, journeyInventoryQuerySchema as hr, manualUploadPrepareSchema as ht, validateBCrossSegmentReview as i, selectedWorks as ii, journeyIsolateSchema as in, planBRepair as ir, reviewInventory as it, accountProfileSchema as j, hideKeyId as jn, requestedScope as jr, metadataProcessingVersion as jt, renew as k, termHideAdditions as kn, journeyRenderSchema as kr, currentJourneyAdapters as kt, chooseCheapPlacement as l, selectionSchema as li, reviewAttemptsResultSchema as ln, scanGitSeedsId as lr, artifactFailureDetailSchema as lt, activeJourneyTerms as m, recommendParallelism as mi, sessionRereadResourceSchema as mn, resolveSourceSelection as mr, manualUploadChoiceSchema as mt, scanBatchEquivalenceDigest as n, isSelectedWork as ni, journeyResourceQuerySchema as nn, bReviewDigest as nr, settingsQuerySchema as nt, bRereadDigest as o, commandSchema as oi, sortingApplySchema as on, scanCanonicalResourceSchema as or, classifyAgentSetup as ot, splitSelectorValues as p, sourceChangePolicy as pi, sessionRereadInputSchema as pn, validateScanExclusion as pr, manualUploadBindingSchema as pt, validateInspectionClaim as q, retireWorkDescription as qn, sendArtifactSchema as qr, journeyContextSchema as qt, locateTermEvidence as r, isSkippedWork as ri, journeyResourceSchema as rn, mergeBRepair as rr, readWorkspaceOutcomes as rt, reconcileBSession as s, packageCommandSchema as si, reviewAttemptResourceSchema as sn, scanPreparationDigest as sr, defaultAgentSetupInclusion as st, scanBatchContextDigest as t, isReadingComplete as ti, projectPreparationProgress as tn, reviewTransportBytes as tr, settingsBatchSchema as tt, reviseGroupMembership as u, gitMetadataBudgetBytes as ui, sessionReconcileInputSchema as un, scanGitSeedsResource as ur, resolveFailureLocation as ut, projectJourneyInventory as v, freezeConfiguration as vi, sessionReviewStatusSchema as vn, journeyPreparationProgressSchema as vr, candidateSortAvailable as vt, rebindReviewSubmit as w, confirmedHideAdditions as wn, journeyClassificationSessionsSchema as wr, sendChoiceSchema as wt, bReviewScope as x, journeyCommitResultSchema as xn, preparationSettlementSchema as xr, normalizeSensitiveTerms as xt, preparationScope as y, selectFrozenConfiguration as yi, sessionUsefulnessInputSchema as yn, emptyPreparationFacts as yr, emptyTermsRevision as yt, agentSetupKey as z, confirmedHideGroupIds as zn, hasCurrentStaticReview as zr, classificationCommitSchema as zt };