@flowdesk/core 0.1.12 → 0.1.14

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 (122) hide show
  1. package/README.md +1 -1
  2. package/dist/agent-profiles.js +0 -1
  3. package/dist/agent-registry.d.ts +43 -0
  4. package/dist/agent-registry.d.ts.map +1 -0
  5. package/dist/agent-registry.js +309 -0
  6. package/dist/agent-registry.js.map +1 -0
  7. package/dist/audit.js +0 -1
  8. package/dist/authority-promotion.js +0 -1
  9. package/dist/bootstrap-foundation.js +0 -1
  10. package/dist/chat-control-authority.js +0 -1
  11. package/dist/chat-hook-authority-probe.js +0 -1
  12. package/dist/chat-routing.js +0 -1
  13. package/dist/command-manifest.js +0 -1
  14. package/dist/config-policy.js +0 -1
  15. package/dist/connector-gateway.js +0 -1
  16. package/dist/connector-profile.js +0 -1
  17. package/dist/controlled-conformance-doc-write.js +0 -1
  18. package/dist/controlled-redacted-audit-export-write.js +0 -1
  19. package/dist/controlled-workspace-file-write.d.ts +36 -0
  20. package/dist/controlled-workspace-file-write.d.ts.map +1 -0
  21. package/dist/controlled-workspace-file-write.js +130 -0
  22. package/dist/controlled-workspace-file-write.js.map +1 -0
  23. package/dist/core-completion-safety-contracts.js +0 -1
  24. package/dist/dispatch-attempt-manifest.js +0 -1
  25. package/dist/dispatch-idempotency.js +0 -1
  26. package/dist/external-auth-policy.js +0 -1
  27. package/dist/fake-remote-connector-adapter.js +0 -1
  28. package/dist/fake-runtime.js +0 -1
  29. package/dist/fallback-decision.js +0 -1
  30. package/dist/fallback-regate-plan.js +0 -1
  31. package/dist/fds1-schema-probe-result.js +0 -1
  32. package/dist/guard-boundary.js +0 -1
  33. package/dist/guarded-dry-run.js +0 -1
  34. package/dist/hook-harness.js +0 -1
  35. package/dist/index.d.ts +9 -0
  36. package/dist/index.d.ts.map +1 -1
  37. package/dist/index.js +9 -0
  38. package/dist/index.js.map +1 -1
  39. package/dist/lane-heartbeat.js +0 -1
  40. package/dist/lane-lifecycle-record.js +0 -1
  41. package/dist/lane-observability.js +0 -1
  42. package/dist/lane-stall-projection.d.ts +2 -1
  43. package/dist/lane-stall-projection.d.ts.map +1 -1
  44. package/dist/lane-stall-projection.js +66 -9
  45. package/dist/lane-stall-projection.js.map +1 -1
  46. package/dist/managed-dispatch-evidence-shape.js +0 -1
  47. package/dist/model-availability-cache.js +0 -1
  48. package/dist/operational-intelligence.js +0 -1
  49. package/dist/pending-abort.js +0 -1
  50. package/dist/plan.js +0 -1
  51. package/dist/planning-evidence-common.d.ts +18 -0
  52. package/dist/planning-evidence-common.d.ts.map +1 -0
  53. package/dist/planning-evidence-common.js +95 -0
  54. package/dist/planning-evidence-common.js.map +1 -0
  55. package/dist/pre-dispatch-audit-record.js +0 -1
  56. package/dist/production-approval-source.js +0 -1
  57. package/dist/production-enablement.js +0 -1
  58. package/dist/production-verification.js +0 -1
  59. package/dist/provider-failures.js +0 -1
  60. package/dist/provider-usage-collector.d.ts +1 -0
  61. package/dist/provider-usage-collector.d.ts.map +1 -1
  62. package/dist/provider-usage-collector.js +179 -29
  63. package/dist/provider-usage-collector.js.map +1 -1
  64. package/dist/redaction.js +0 -1
  65. package/dist/release1-contracts.js +0 -1
  66. package/dist/remote-write-connector-gate.js +0 -1
  67. package/dist/retry-plan.d.ts +20 -2
  68. package/dist/retry-plan.d.ts.map +1 -1
  69. package/dist/retry-plan.js +57 -3
  70. package/dist/retry-plan.js.map +1 -1
  71. package/dist/retry.js +0 -1
  72. package/dist/reviewer-lane-conformance.js +0 -1
  73. package/dist/runtime-lane-productization.js +0 -1
  74. package/dist/sanitized-auth-capture.js +0 -1
  75. package/dist/schema-artifacts.js +0 -1
  76. package/dist/schema-registry.d.ts.map +1 -1
  77. package/dist/schema-registry.js +5 -1
  78. package/dist/schema-registry.js.map +1 -1
  79. package/dist/session-evidence.d.ts.map +1 -1
  80. package/dist/session-evidence.js +44 -3
  81. package/dist/session-evidence.js.map +1 -1
  82. package/dist/state-paths.d.ts +1 -1
  83. package/dist/state-paths.d.ts.map +1 -1
  84. package/dist/state-paths.js +24 -0
  85. package/dist/state-paths.js.map +1 -1
  86. package/dist/state-store.js +0 -1
  87. package/dist/status.js +0 -1
  88. package/dist/task-agent-assignment.d.ts +30 -0
  89. package/dist/task-agent-assignment.d.ts.map +1 -0
  90. package/dist/task-agent-assignment.js +77 -0
  91. package/dist/task-agent-assignment.js.map +1 -0
  92. package/dist/task-graph.d.ts +31 -0
  93. package/dist/task-graph.d.ts.map +1 -0
  94. package/dist/task-graph.js +122 -0
  95. package/dist/task-graph.js.map +1 -0
  96. package/dist/task-model-selection.d.ts +36 -0
  97. package/dist/task-model-selection.d.ts.map +1 -0
  98. package/dist/task-model-selection.js +99 -0
  99. package/dist/task-model-selection.js.map +1 -0
  100. package/dist/task-result.d.ts +38 -1
  101. package/dist/task-result.d.ts.map +1 -1
  102. package/dist/task-result.js +148 -1
  103. package/dist/task-result.js.map +1 -1
  104. package/dist/tool-contract-fixtures.js +0 -1
  105. package/dist/top-tier-reviewer-lane-probe.js +0 -1
  106. package/dist/usage-health.js +0 -1
  107. package/dist/validators.d.ts.map +1 -1
  108. package/dist/validators.js +10 -2
  109. package/dist/validators.js.map +1 -1
  110. package/dist/workflow-authoring-result.d.ts +25 -0
  111. package/dist/workflow-authoring-result.d.ts.map +1 -0
  112. package/dist/workflow-authoring-result.js +63 -0
  113. package/dist/workflow-authoring-result.js.map +1 -0
  114. package/dist/workflow-dispatch-plan.d.ts +65 -0
  115. package/dist/workflow-dispatch-plan.d.ts.map +1 -0
  116. package/dist/workflow-dispatch-plan.js +228 -0
  117. package/dist/workflow-dispatch-plan.js.map +1 -0
  118. package/dist/workflow-synthesis.d.ts +18 -0
  119. package/dist/workflow-synthesis.d.ts.map +1 -0
  120. package/dist/workflow-synthesis.js +40 -0
  121. package/dist/workflow-synthesis.js.map +1 -0
  122. package/package.json +1 -1
@@ -0,0 +1 @@
1
+ {"version":3,"file":"planning-evidence-common.d.ts","sourceRoot":"","sources":["../src/planning-evidence-common.ts"],"names":[],"mappings":"AAAA,OAAO,EAEN,KAAK,gBAAgB,EAKrB,MAAM,iBAAiB,CAAC;AAEzB,OAAO,EAAE,OAAO,EAAE,KAAK,gBAAgB,EAAE,KAAK,EAAE,MAAM,iBAAiB,CAAC;AAExE,eAAO,MAAM,mCAAmC,qIAMtC,CAAC;AAiBX,wBAAgB,wBAAwB,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAEzF;AAED,wBAAgB,+BAA+B,CAC9C,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC9B,OAAO,EAAE,WAAW,CAAC,MAAM,CAAC,EAC5B,KAAK,EAAE,MAAM,GACX,MAAM,EAAE,CAIV;AAED,wBAAgB,mBAAmB,CAClC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC9B,IAAI,EAAE,SAAS,MAAM,EAAE,EACvB,KAAK,EAAE,MAAM,GACX,MAAM,EAAE,CAIV;AAED,wBAAgB,wBAAwB,CACvC,KAAK,EAAE,OAAO,EACd,KAAK,EAAE,MAAM,EACb,SAAS,EAAE,MAAM,EACjB,OAAO,GAAE;IAAE,UAAU,CAAC,EAAE,OAAO,CAAC;IAAC,mBAAmB,CAAC,EAAE,OAAO,CAAA;CAAO,GACnE,gBAAgB,CAWlB;AAED,wBAAgB,yBAAyB,CAAC,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,GAAG,gBAAgB,CAMzF;AAED,wBAAgB,wBAAwB,CAAC,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,GAAG,gBAAgB,CAExF;AAED,wBAAgB,yBAAyB,CAAC,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,GAAG,gBAAgB,CAEzF;AAED,wBAAgB,8BAA8B,CAC7C,KAAK,EAAE,OAAO,EACd,KAAK,EAAE,MAAM,EACb,QAAQ,EAAE,MAAM,GACd,gBAAgB,CAOlB;AAED,wBAAgB,8BAA8B,CAC7C,KAAK,EAAE,OAAO,EACd,KAAK,EAAE,MAAM,EACb,QAAQ,EAAE,MAAM,GACd,gBAAgB,CAOlB;AAED,wBAAgB,8BAA8B,CAC7C,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC9B,KAAK,EAAE,MAAM,GACX,MAAM,EAAE,CAWV;AAED,wBAAgB,mCAAmC,CAClD,KAAK,EAAE,OAAO,EACd,KAAK,EAAE,MAAM,GACX,gBAAgB,CAElB"}
@@ -0,0 +1,95 @@
1
+ import { invalid, valid, validateNoForbiddenRawPayloads, validateOpaqueId, validateOpaqueRef, } from "./validators.js";
2
+ export { invalid, valid } from "./validators.js";
3
+ export const FLOWDESK_PLANNING_AUTHORITY_KEYS_V1 = [
4
+ "dispatch_authority_enabled",
5
+ "provider_call_made",
6
+ "runtime_execution",
7
+ "actual_lane_launch",
8
+ "write_authority_enabled",
9
+ ];
10
+ const AUTHORITY_TRUE_KEYS = new Set([
11
+ ...FLOWDESK_PLANNING_AUTHORITY_KEYS_V1,
12
+ "realOpenCodeDispatch",
13
+ "providerCall",
14
+ "actualLaneLaunch",
15
+ "runtimeExecution",
16
+ "fallbackAuthority",
17
+ "toolAuthority",
18
+ "hardCancelOrNoReplyAuthority",
19
+ "fallback_allowed",
20
+ "reselection_allowed",
21
+ ]);
22
+ const AUTHORITY_SMUGGLING_PATTERN = /\b(?:approve(?:d|s)?|approval|authorize(?:d|s)?|authorization|guard(?:ed)?\s+approval|dispatch(?:able|ed|es)?|dispatch\s*-?authority|real\s*-?(?:opencode\s*-?)?dispatch|provider\s*-?(?:call|payload|response)|runtime\s*-?execution|runtime[-_\s]*lane[-_\s]*launch|actual\s*-?lane\s*-?launch|lane\s*-?launch|fallback|reselection|reselect|retry\s+with\s+(?:another|different)|switch\s+(?:provider|model)|no\s*-?reply|hard\s*-?(?:cancel|stop)|opencode\s+run|hidden\s+injection)\b/i;
23
+ export function isPlanningEvidenceRecord(value) {
24
+ return typeof value === "object" && value !== null && !Array.isArray(value);
25
+ }
26
+ export function rejectUnknownPlanningProperties(value, allowed, label) {
27
+ return Object.keys(value)
28
+ .filter((key) => !allowed.has(key))
29
+ .map((key) => `${label} has unknown property: ${key}`);
30
+ }
31
+ export function requirePlanningKeys(value, keys, label) {
32
+ return keys
33
+ .filter((key) => !(key in value))
34
+ .map((key) => `${label} missing required field: ${key}`);
35
+ }
36
+ export function validatePlanningSafeText(value, label, maxLength, options = {}) {
37
+ if (typeof value !== "string")
38
+ return invalid(`${label} must be a string`);
39
+ if (!options.allowEmpty && value.trim().length === 0)
40
+ return invalid(`${label} must be a non-empty string`);
41
+ if (value.length > maxLength)
42
+ return invalid(`${label} exceeds ${maxLength} chars`);
43
+ const redaction = validateNoForbiddenRawPayloads(value, label);
44
+ if (!redaction.ok)
45
+ return redaction;
46
+ if (!options.allowAuthorityWords && AUTHORITY_SMUGGLING_PATTERN.test(value)) {
47
+ return invalid(`${label} contains authority-smuggling language`);
48
+ }
49
+ return valid();
50
+ }
51
+ export function validatePlanningTimestamp(value, label) {
52
+ if (typeof value !== "string" || value.length === 0)
53
+ return invalid(`${label} must be a timestamp string`);
54
+ return Number.isFinite(Date.parse(value))
55
+ ? valid()
56
+ : invalid(`${label} must be a parseable timestamp`);
57
+ }
58
+ export function validatePlanningOpaqueId(value, label) {
59
+ return validateOpaqueId(value, label);
60
+ }
61
+ export function validatePlanningOpaqueRef(value, label) {
62
+ return validateOpaqueRef(value, label);
63
+ }
64
+ export function validatePlanningOpaqueRefArray(value, label, maxItems) {
65
+ if (!Array.isArray(value))
66
+ return invalid(`${label} must be an array`);
67
+ if (value.length > maxItems)
68
+ return invalid(`${label} exceeds ${maxItems} items`);
69
+ const errors = value.flatMap((item, index) => validateOpaqueRef(item, `${label}[${index}]`).errors);
70
+ return errors.length === 0 ? valid() : invalid(...errors);
71
+ }
72
+ export function validatePlanningSafeLabelArray(value, label, maxItems) {
73
+ if (!Array.isArray(value))
74
+ return invalid(`${label} must be an array`);
75
+ if (value.length > maxItems)
76
+ return invalid(`${label} exceeds ${maxItems} items`);
77
+ const errors = value.flatMap((item, index) => validatePlanningSafeText(item, `${label}[${index}]`, 120).errors);
78
+ return errors.length === 0 ? valid() : invalid(...errors);
79
+ }
80
+ export function validatePlanningAuthorityFalse(value, label) {
81
+ const errors = [];
82
+ for (const key of FLOWDESK_PLANNING_AUTHORITY_KEYS_V1) {
83
+ if (value[key] !== false)
84
+ errors.push(`${label}.${key} must be false`);
85
+ }
86
+ for (const [key, nested] of Object.entries(value)) {
87
+ if (AUTHORITY_TRUE_KEYS.has(key) && nested === true) {
88
+ errors.push(`${label}.${key} cannot be true`);
89
+ }
90
+ }
91
+ return errors;
92
+ }
93
+ export function validatePlanningNoForbiddenPayloads(value, label) {
94
+ return validateNoForbiddenRawPayloads(value, label);
95
+ }
@@ -0,0 +1 @@
1
+ {"version":3,"file":"planning-evidence-common.js","sourceRoot":"","sources":["../src/planning-evidence-common.ts"],"names":[],"mappings":"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"}
@@ -58,4 +58,3 @@ export function validateFlowDeskPreDispatchAuditRecordV1(value) {
58
58
  errors.push(...validateNoForbiddenRawPayloads(value, "pre_dispatch_audit_record").errors);
59
59
  return errors.length === 0 ? valid() : invalid(...errors);
60
60
  }
61
- //# sourceMappingURL=pre-dispatch-audit-record.js.map
@@ -223,4 +223,3 @@ export function consumeFlowDeskProductionApprovalSourceV1(input) {
223
223
  ...disabledApprovalAuthority,
224
224
  };
225
225
  }
226
- //# sourceMappingURL=production-approval-source.js.map
@@ -669,4 +669,3 @@ export function evaluateFlowDeskProductionEnablementV1(input) {
669
669
  : ["/flowdesk-doctor", "/flowdesk-status"],
670
670
  };
671
671
  }
672
- //# sourceMappingURL=production-enablement.js.map
@@ -123,4 +123,3 @@ export function validateFlowDeskConfiguredVerificationResultV1(value, expectedWo
123
123
  .errors);
124
124
  return errors.length === 0 ? valid() : invalid(...errors);
125
125
  }
126
- //# sourceMappingURL=production-verification.js.map
@@ -113,4 +113,3 @@ export function getProviderFailureDiagnosticMappingsV1() {
113
113
  export function providerFailureClassIsDiagnosticOnlyV1(failureClass) {
114
114
  return failureClass !== "none";
115
115
  }
116
- //# sourceMappingURL=provider-failures.js.map
@@ -60,6 +60,7 @@ export interface FlowDeskProviderUsageCollectorResultV1 {
60
60
  providerHealthSnapshot: FlowDeskProviderHealthSnapshotV1;
61
61
  usageAuthorityEvidence?: FlowDeskManagedDispatchBetaUsageAuthorityEvidenceV1;
62
62
  bucketSnapshot?: FlowDeskProviderUsageCollectorBucketSnapshotV1;
63
+ additionalSnapshots?: readonly FlowDeskUsageSnapshotV1[];
63
64
  redacted_reason?: string;
64
65
  }
65
66
  export declare function collectManagedDispatchBetaUsageEvidenceV1(target: FlowDeskProviderUsageCollectorTargetV1, acquisition: FlowDeskProviderUsageAcquisitionConfigV1 | undefined, options?: FlowDeskProviderUsageCollectorOptionsV1): Promise<FlowDeskProviderUsageCollectorResultV1>;
@@ -1 +1 @@
1
- {"version":3,"file":"provider-usage-collector.d.ts","sourceRoot":"","sources":["../src/provider-usage-collector.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"provider-usage-collector.d.ts","sourceRoot":"","sources":["../src/provider-usage-collector.ts"],"names":[],"mappings":"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"}
@@ -22,8 +22,10 @@ export async function collectManagedDispatchBetaUsageEvidenceV1(target, acquisit
22
22
  : refusedCollection(target.providerFamily, target.modelFamily, "provider usage acquisition is disabled");
23
23
  const bucket = collection.bucket;
24
24
  const resetAt = bucket?.resetAt;
25
- const usageOk = bucket !== undefined && bucket.remaining !== null && bucket.remaining > 0 && resetAt !== undefined && bucket.uncertainty === "available";
26
- const usageSnapshot = usageOk
25
+ const remaining = bucket?.remaining ?? null;
26
+ const usageKnown = bucket !== undefined && remaining !== null && resetAt !== undefined && bucket.uncertainty === "available";
27
+ const usageOk = usageKnown && remaining > 0;
28
+ const usageSnapshot = usageKnown
27
29
  ? {
28
30
  schema_version: "flowdesk.usage_snapshot.v1",
29
31
  snapshot_id: target.usageSnapshotId,
@@ -32,8 +34,8 @@ export async function collectManagedDispatchBetaUsageEvidenceV1(target, acquisit
32
34
  freshness: "fresh",
33
35
  freshness_ttl: ttlMinutes,
34
36
  reset_time: resetAt,
35
- reset_bucket: bucket.resetBucket,
36
- dispatchability: "dispatchable",
37
+ reset_bucket: remaining === 0 ? `0% ${bucket.resetBucket}` : bucket.resetBucket,
38
+ dispatchability: usageOk ? "dispatchable" : "non_dispatchable",
37
39
  uncertainty_flags: [],
38
40
  source_ref: target.sourceRef
39
41
  }
@@ -62,6 +64,28 @@ export async function collectManagedDispatchBetaUsageEvidenceV1(target, acquisit
62
64
  uncertainty: bucket.uncertainty
63
65
  }
64
66
  : undefined;
67
+ const additionalSnapshots = [];
68
+ for (const addBucket of collection.additionalBuckets ?? []) {
69
+ const addResetAt = addBucket.resetAt;
70
+ const addRemaining = addBucket.remaining;
71
+ const addKnown = addRemaining !== null && addResetAt !== undefined && addBucket.uncertainty === "available";
72
+ const addOk = addKnown && addRemaining > 0;
73
+ additionalSnapshots.push(addKnown
74
+ ? {
75
+ schema_version: "flowdesk.usage_snapshot.v1",
76
+ snapshot_id: `${target.usageSnapshotId}-${addBucket.resetBucket}`,
77
+ provider_family: target.providerFamily,
78
+ model_family: target.modelFamily,
79
+ freshness: "fresh",
80
+ freshness_ttl: ttlMinutes,
81
+ reset_time: addResetAt,
82
+ reset_bucket: addBucket.remaining !== null ? `${addBucket.remaining}% ${addBucket.resetBucket}` : addBucket.resetBucket,
83
+ dispatchability: addOk ? "dispatchable" : "non_dispatchable",
84
+ uncertainty_flags: [],
85
+ source_ref: target.sourceRef,
86
+ }
87
+ : unknownUsageSnapshot({ ...target, usageSnapshotId: `${target.usageSnapshotId}-${addBucket.resetBucket}` }, { ...collection, bucket: addBucket }, ttlMinutes));
88
+ }
65
89
  if (!usageOk || collection.authProfileRef === undefined || collection.authEvidenceRef === undefined || collection.credentialScopeRef === undefined || collection.accountBoundaryRef === undefined || collection.quotaEvidenceRef === undefined) {
66
90
  return {
67
91
  ok: false,
@@ -69,6 +93,7 @@ export async function collectManagedDispatchBetaUsageEvidenceV1(target, acquisit
69
93
  usageSnapshot,
70
94
  providerHealthSnapshot,
71
95
  ...(bucketSnapshot !== undefined ? { bucketSnapshot } : {}),
96
+ ...(additionalSnapshots.length > 0 ? { additionalSnapshots } : {}),
72
97
  redacted_reason: collection.redactedReason ?? "usage evidence is unavailable"
73
98
  };
74
99
  }
@@ -78,6 +103,7 @@ export async function collectManagedDispatchBetaUsageEvidenceV1(target, acquisit
78
103
  usageSnapshot,
79
104
  providerHealthSnapshot,
80
105
  ...(bucketSnapshot !== undefined ? { bucketSnapshot } : {}),
106
+ ...(additionalSnapshots.length > 0 ? { additionalSnapshots } : {}),
81
107
  usageAuthorityEvidence: {
82
108
  schema_version: "flowdesk.managed_dispatch_beta.usage_authority_evidence.v1",
83
109
  authority_ref: target.authorityRef,
@@ -135,7 +161,10 @@ async function collectClaudeUsage(acquisition, options, observedAt) {
135
161
  return refusedCollection("claude", "claude", "claude usage endpoint refused");
136
162
  const payload = JSON.parse(await response.text());
137
163
  const buckets = claudeOAuthBuckets(payload, observedAt, defaults());
138
- return availableCollection("claude", "claude", "claude-oauth", "claude-oauth", firstDispatchableBucket(buckets) ?? buckets[0] ?? defaults()[0]);
164
+ const primaryBucket = firstDispatchableBucket(buckets) ?? buckets[0] ?? defaults()[0];
165
+ const additionalBuckets = buckets.filter((b) => b !== primaryBucket);
166
+ const collection = availableCollection("claude", "claude", "claude-oauth", "claude-oauth", primaryBucket);
167
+ return { ...collection, additionalBuckets };
139
168
  }
140
169
  catch {
141
170
  return refusedCollection("claude", "claude", "claude usage collection failed");
@@ -165,8 +194,11 @@ async function collectCodexUsage(acquisition, options, observedAt) {
165
194
  });
166
195
  if (!response.ok)
167
196
  return refusedCollection("openai", "gpt", "codex usage endpoint refused");
168
- const bucketValue = codexLiveBucket(JSON.parse(await response.text()), observedAt);
169
- return availableCollection("openai", "gpt", "codex-live", firstNonEmpty(accountId, "codex-account"), bucketValue);
197
+ const buckets = codexLiveBuckets(JSON.parse(await response.text()), observedAt);
198
+ const primaryBucket = firstDispatchableBucket(buckets) ?? buckets[0] ?? bucket("openai-gpt-5h", "%", null, undefined, "unknown");
199
+ const additionalBuckets = buckets.filter((b) => b !== primaryBucket);
200
+ const collection = availableCollection("openai", "gpt", "codex-live", firstNonEmpty(accountId, "codex-account"), primaryBucket);
201
+ return { ...collection, additionalBuckets };
170
202
  }
171
203
  catch {
172
204
  return refusedCollection("openai", "gpt", "codex usage collection failed");
@@ -180,20 +212,23 @@ async function collectGeminiUsage(acquisition, options, observedAt) {
180
212
  return refusedCollection("gemini", "gemini", "fetch is unavailable");
181
213
  const homeDir = normalizeHomeDir(acquisition.homeDir, options.env);
182
214
  const filesystem = options.filesystem ?? defaultFilesystem;
215
+ const openCodeCreds = readOpenCodeGeminiOAuthCredentials(homeDir, options.env, filesystem);
183
216
  const credsPath = geminiCredentialPaths(homeDir, options.env).find((candidate) => filesystem.exists(candidate));
184
- if (!credsPath)
217
+ if (!credsPath && !openCodeCreds)
185
218
  return refusedCollection("gemini", "gemini", "gemini auth evidence is missing");
186
219
  try {
187
- const creds = JSON.parse(filesystem.readFile(credsPath));
188
- const credsRecord = isRecord(creds) ? creds : {};
189
- const refreshToken = stringField(credsRecord, "refresh_token");
190
- const cachedAccessToken = stringField(credsRecord, "access_token");
191
- const cachedExpiryRaw = credsRecord.expiry_date;
192
- const cachedExpiryMs = typeof cachedExpiryRaw === "number" ? cachedExpiryRaw : typeof cachedExpiryRaw === "string" ? Number.parseInt(cachedExpiryRaw, 10) : NaN;
220
+ const geminiCliCreds = credsPath ? readGeminiCliOAuthCredentials(credsPath, filesystem) : null;
221
+ const selectedCreds = selectGeminiOAuthCredentials(openCodeCreds, geminiCliCreds, observedAt);
222
+ if (!selectedCreds)
223
+ return refusedCollection("gemini", "gemini", "gemini auth evidence is missing");
224
+ const refreshToken = selectedCreds.refreshToken;
225
+ const cachedAccessToken = selectedCreds.accessToken;
226
+ const cachedExpiryMs = selectedCreds.expiresAt ?? NaN;
193
227
  const cachedTokenStillValid = cachedAccessToken !== "" && Number.isFinite(cachedExpiryMs) && cachedExpiryMs > observedAt + 5 * 60_000;
194
228
  const env = options.env ?? {};
195
- const clientId = firstNonEmpty(acquisition.geminiOAuthClientId, env.FLOWDESK_GEMINI_OAUTH_CLIENT_ID);
196
- const clientSecret = firstNonEmpty(acquisition.geminiOAuthClientSecret, env.FLOWDESK_GEMINI_OAUTH_CLIENT_SECRET);
229
+ const inferredClient = readOpenCodeGeminiAuthOAuthClient(homeDir, env, filesystem);
230
+ const clientId = firstNonEmpty(acquisition.geminiOAuthClientId, env.FLOWDESK_GEMINI_OAUTH_CLIENT_ID, inferredClient?.clientId);
231
+ const clientSecret = firstNonEmpty(acquisition.geminiOAuthClientSecret, env.FLOWDESK_GEMINI_OAUTH_CLIENT_SECRET, inferredClient?.clientSecret);
197
232
  let accessToken = "";
198
233
  if (cachedTokenStillValid) {
199
234
  accessToken = cachedAccessToken;
@@ -213,19 +248,101 @@ async function collectGeminiUsage(acquisition, options, observedAt) {
213
248
  return refusedCollection("gemini", "gemini", "gemini oauth client evidence is missing and cached access token is expired");
214
249
  return refusedCollection("gemini", "gemini", "gemini token refresh failed");
215
250
  }
216
- let projectId = firstNonEmpty(env.GOOGLE_CLOUD_PROJECT, env.GOOGLE_CLOUD_PROJECT_ID, acquisition.geminiProjectId);
251
+ let projectId = firstNonEmpty(env.GOOGLE_CLOUD_PROJECT, env.GOOGLE_CLOUD_PROJECT_ID, acquisition.geminiProjectId, selectedCreds.projectId);
217
252
  const details = await codeAssistPost("loadCodeAssist", { cloudaicompanionProject: projectId, metadata: { ideType: "IDE_UNSPECIFIED", platform: "PLATFORM_UNSPECIFIED", pluginType: "GEMINI", duetProject: projectId } }, accessToken, fetcher);
218
253
  projectId = firstNonEmpty(projectId, stringField(details, "cloudaicompanionProject"));
219
254
  if (!projectId)
220
255
  return refusedCollection("gemini", "gemini", "gemini project boundary is missing");
221
256
  const quota = await codeAssistPost("retrieveUserQuota", { project: projectId }, accessToken, fetcher);
222
- const bucketValue = geminiQuotaBucket(quota, observedAt);
223
- return availableCollection("gemini", "gemini", "gemini-code-assist", projectId, bucketValue);
257
+ const buckets = geminiQuotaBuckets(quota, observedAt);
258
+ const primaryBucket = firstDispatchableBucket(buckets) ?? firstKnownBucket(buckets) ?? buckets[0] ?? bucket("gemini-unknown-5h", "%", null, undefined, "unknown");
259
+ const additionalBuckets = buckets.filter((b) => b !== primaryBucket);
260
+ const collection = availableCollection("gemini", "gemini", "gemini-code-assist", projectId, primaryBucket);
261
+ return { ...collection, additionalBuckets };
224
262
  }
225
263
  catch {
226
264
  return refusedCollection("gemini", "gemini", "gemini usage collection failed");
227
265
  }
228
266
  }
267
+ function readGeminiCliOAuthCredentials(credsPath, filesystem) {
268
+ try {
269
+ const creds = JSON.parse(filesystem.readFile(credsPath));
270
+ const record = isRecord(creds) ? creds : {};
271
+ const refreshToken = stringField(record, "refresh_token");
272
+ const accessToken = stringField(record, "access_token");
273
+ const expiryRaw = record.expiry_date;
274
+ const expiresAt = typeof expiryRaw === "number" ? expiryRaw : typeof expiryRaw === "string" ? Number.parseInt(expiryRaw, 10) : undefined;
275
+ if (!refreshToken && !accessToken)
276
+ return null;
277
+ return { accessToken, refreshToken, ...(expiresAt === undefined || !Number.isFinite(expiresAt) ? {} : { expiresAt }) };
278
+ }
279
+ catch {
280
+ return null;
281
+ }
282
+ }
283
+ function readOpenCodeGeminiOAuthCredentials(homeDir, env, filesystem) {
284
+ const fromEnv = authRecordFromOpenCodeAuthContent(env?.OPENCODE_AUTH_CONTENT);
285
+ if (fromEnv)
286
+ return fromEnv;
287
+ for (const authPath of openCodeAuthPaths(homeDir, env)) {
288
+ if (!filesystem.exists(authPath))
289
+ continue;
290
+ try {
291
+ const parsed = JSON.parse(filesystem.readFile(authPath));
292
+ const fromFile = authRecordFromOpenCodeAuthDatabase(parsed);
293
+ if (fromFile)
294
+ return fromFile;
295
+ }
296
+ catch { }
297
+ }
298
+ return null;
299
+ }
300
+ function authRecordFromOpenCodeAuthContent(value) {
301
+ if (!value)
302
+ return null;
303
+ try {
304
+ return authRecordFromOpenCodeAuthDatabase(JSON.parse(value));
305
+ }
306
+ catch {
307
+ return null;
308
+ }
309
+ }
310
+ function authRecordFromOpenCodeAuthDatabase(value) {
311
+ if (!isRecord(value))
312
+ return null;
313
+ return openCodeGeminiAuthRecordToCredentials(value.google) ?? openCodeGeminiAuthRecordToCredentials(value.gemini);
314
+ }
315
+ function openCodeGeminiAuthRecordToCredentials(value) {
316
+ if (!isRecord(value) || value.type !== "oauth")
317
+ return null;
318
+ const accessToken = stringField(value, "access");
319
+ const expiresAt = numberField(value, "expires");
320
+ const refresh = stringField(value, "refresh");
321
+ const [refreshToken = "", projectId = "", managedProjectId = ""] = refresh.split("|", 3);
322
+ if (!refreshToken && !accessToken)
323
+ return null;
324
+ return { accessToken, refreshToken, ...(expiresAt === undefined ? {} : { expiresAt }), ...(firstNonEmpty(projectId, managedProjectId) ? { projectId: firstNonEmpty(projectId, managedProjectId) } : {}) };
325
+ }
326
+ function selectGeminiOAuthCredentials(openCodeCreds, geminiCliCreds, observedAt) {
327
+ const candidates = [openCodeCreds, geminiCliCreds].filter((candidate) => candidate !== null);
328
+ const fresh = candidates.find((candidate) => candidate.accessToken && candidate.expiresAt !== undefined && candidate.expiresAt > observedAt + 5 * 60_000);
329
+ return fresh ?? candidates.find((candidate) => candidate.refreshToken) ?? candidates[0] ?? null;
330
+ }
331
+ function readOpenCodeGeminiAuthOAuthClient(homeDir, env, filesystem) {
332
+ for (const candidate of openCodeGeminiAuthPackageEntrypoints(homeDir, env)) {
333
+ if (!filesystem.exists(candidate))
334
+ continue;
335
+ try {
336
+ const source = filesystem.readFile(candidate);
337
+ const clientId = source.match(/GEMINI_CLIENT_ID\s*=\s*["']([^"']+)["']/)?.[1] ?? "";
338
+ const clientSecret = source.match(/GEMINI_CLIENT_SECRET\s*=\s*["']([^"']+)["']/)?.[1] ?? "";
339
+ if (clientId && clientSecret)
340
+ return { clientId, clientSecret };
341
+ }
342
+ catch { }
343
+ }
344
+ return null;
345
+ }
229
346
  function availableCollection(providerFamily, modelFamily, authProfile, accountBoundary, providerBucket) {
230
347
  if (providerBucket.remaining === null || providerBucket.remaining <= 0 || providerBucket.uncertainty !== "available") {
231
348
  return { providerFamily, modelFamily, bucket: providerBucket, failureClass: "rate_limited", availabilityState: "unavailable", redactedReason: "usage is not available" };
@@ -276,12 +393,15 @@ function claudeUsageBucket(resetBucket, defaultBucket, value, observedAt) {
276
393
  const calculated = calculateRemainingUsagePercent({ utilizationPercent: numberField(rawBucket, "utilization"), resetAt: stringField(rawBucket, "resets_at"), observedAt, expiredResetBehavior: "reset_to_full" });
277
394
  return bucket(resetBucket, "%", calculated.remaining, calculated.reset_at, calculated.uncertainty);
278
395
  }
279
- function codexLiveBucket(payload, observedAt) {
396
+ function codexLiveBuckets(payload, observedAt) {
280
397
  const record = isRecord(payload) ? payload : {};
281
398
  const rateLimitPayload = isRecord(record.rate_limit_status) ? record.rate_limit_status : record;
282
399
  const details = firstRecord(rateLimitPayload.rate_limit, record.rate_limit);
283
400
  const primary = firstRecord(details?.primary_window, details?.primary, details);
284
- return codexRateLimitBucket("openai-gpt-5h", primary, observedAt);
401
+ const secondary = isRecord(details?.secondary_window) ? details.secondary_window : undefined;
402
+ const primaryBucket = codexRateLimitBucket("openai-gpt-5h", primary, observedAt);
403
+ const secondaryBucket = secondary !== undefined ? codexRateLimitBucket("openai-weekly", secondary, observedAt) : undefined;
404
+ return secondaryBucket !== undefined ? [primaryBucket, secondaryBucket] : [primaryBucket];
285
405
  }
286
406
  function codexRateLimitBucket(resetBucket, rateLimit, observedAt) {
287
407
  if (!rateLimit)
@@ -293,9 +413,9 @@ function codexRateLimitBucket(resetBucket, rateLimit, observedAt) {
293
413
  const calculated = calculateRemainingUsagePercent({ remainingPercent: reportedRemainingPercent, usedPercent, resetAtUnixSeconds: resetUnix, resetAfterSeconds, observedAt, expiredResetBehavior: "stale" });
294
414
  return bucket(resetBucket, "%", calculated.remaining, calculated.reset_at, calculated.uncertainty);
295
415
  }
296
- function geminiQuotaBucket(quota, observedAt) {
416
+ function geminiQuotaBuckets(quota, observedAt) {
297
417
  const quotaBuckets = Array.isArray(quota.buckets) ? quota.buckets.filter(isRecord) : [];
298
- let selected;
418
+ const selectedByBucket = new Map();
299
419
  for (const quotaBucket of quotaBuckets) {
300
420
  const tokenType = stringField(quotaBucket, "tokenType");
301
421
  if (tokenType && tokenType !== "REQUESTS")
@@ -315,20 +435,38 @@ function geminiQuotaBucket(quota, observedAt) {
315
435
  resetBucket = "gemini-pro-weekly";
316
436
  }
317
437
  else {
318
- resetBucket = "gemini-pro-5h";
438
+ resetBucket = "gemini-pro-daily";
319
439
  }
320
440
  }
321
441
  if (!resetBucket)
322
442
  continue;
323
443
  const remainingPercent = calculateRemainingUsagePercent({ remainingFraction: numberField(quotaBucket, "remainingFraction"), remainingAmount: stringField(quotaBucket, "remainingAmount"), resetAt: stringField(quotaBucket, "resetTime"), observedAt, expiredResetBehavior: "reset_to_full" });
324
444
  const candidate = bucket(resetBucket, "%", remainingPercent.remaining, remainingPercent.reset_at, remainingPercent.uncertainty);
325
- if (selected === undefined || selected.remaining === null || (candidate.remaining !== null && candidate.remaining < selected.remaining))
326
- selected = candidate;
445
+ const existing = selectedByBucket.get(resetBucket);
446
+ if (existing === undefined || existing.remaining === null || (candidate.remaining !== null && candidate.remaining < existing.remaining))
447
+ selectedByBucket.set(resetBucket, candidate);
327
448
  }
328
- return selected ?? bucket("gemini-unknown-5h", "%", null, undefined, "unknown");
449
+ const buckets = [...selectedByBucket.values()].sort((a, b) => geminiBucketRank(a.resetBucket) - geminiBucketRank(b.resetBucket));
450
+ return buckets.length > 0 ? buckets : [bucket("gemini-unknown-5h", "%", null, undefined, "unknown")];
451
+ }
452
+ function geminiBucketRank(resetBucket) {
453
+ if (resetBucket === "gemini-pro-daily")
454
+ return 0;
455
+ if (resetBucket === "gemini-pro-weekly")
456
+ return 1;
457
+ if (resetBucket === "gemini-flash-daily")
458
+ return 2;
459
+ if (resetBucket === "gemini-flash-lite-daily")
460
+ return 3;
461
+ return 99;
329
462
  }
330
463
  function firstDispatchableBucket(buckets) {
331
- return buckets.find((candidate) => candidate.remaining !== null && candidate.remaining > 0 && candidate.resetAt !== undefined && candidate.uncertainty === "available");
464
+ return buckets
465
+ .filter((candidate) => candidate.remaining !== null && candidate.remaining > 0 && candidate.resetAt !== undefined && candidate.uncertainty === "available")
466
+ .sort((a, b) => (a.remaining ?? Number.POSITIVE_INFINITY) - (b.remaining ?? Number.POSITIVE_INFINITY))[0];
467
+ }
468
+ function firstKnownBucket(buckets) {
469
+ return buckets.find((candidate) => candidate.remaining !== null && candidate.resetAt !== undefined && candidate.uncertainty === "available");
332
470
  }
333
471
  async function readClaudeOAuthCredentials(homeDir, filesystem, options) {
334
472
  const keychainCreds = readClaudeKeychainCredentials(options);
@@ -466,7 +604,7 @@ function calculateRemainingUsagePercent(input) {
466
604
  return { remaining: null, used: null, ...(resetAt ? { reset_at: resetAt } : {}), uncertainty: "unknown" };
467
605
  }
468
606
  function availableResult(remaining, used, resetAt) {
469
- return { remaining, used, ...(resetAt ? { reset_at: resetAt } : {}), uncertainty: remaining > 0 ? "available" : "insufficient" };
607
+ return { remaining, used, ...(resetAt ? { reset_at: resetAt } : {}), uncertainty: "available" };
470
608
  }
471
609
  function resolveResetAt(input, observedAt) {
472
610
  if (typeof input.resetAt === "string" && input.resetAt.trim() !== "") {
@@ -493,6 +631,18 @@ function geminiCredentialPaths(homeDir, env) {
493
631
  const geminiHome = env?.GEMINI_CLI_HOME;
494
632
  return uniqueNonEmpty([geminiHome ? path.join(geminiHome, ".gemini", "oauth_creds.json") : undefined, geminiHome ? path.join(geminiHome, "oauth_creds.json") : undefined, path.join(homeDir, ".gemini", "oauth_creds.json")]);
495
633
  }
634
+ function openCodeAuthPaths(homeDir, env) {
635
+ const dataHome = firstNonEmpty(env?.OPENCODE_DATA_DIR, env?.XDG_DATA_HOME ? path.join(env.XDG_DATA_HOME, "opencode") : undefined, path.join(homeDir, ".local", "share", "opencode"));
636
+ return uniqueNonEmpty([dataHome ? path.join(dataHome, "auth.json") : undefined]);
637
+ }
638
+ function openCodeGeminiAuthPackageEntrypoints(homeDir, env) {
639
+ const cacheHome = firstNonEmpty(env?.OPENCODE_CACHE_DIR, env?.XDG_CACHE_HOME ? path.join(env.XDG_CACHE_HOME, "opencode") : undefined, path.join(homeDir, ".cache", "opencode"));
640
+ return uniqueNonEmpty([
641
+ cacheHome ? path.join(cacheHome, "packages", "opencode-gemini-auth@latest", "node_modules", "opencode-gemini-auth", "dist", "index.js") : undefined,
642
+ cacheHome ? path.join(cacheHome, "packages", "opencode-gemini-auth@latest", "dist", "index.js") : undefined,
643
+ cacheHome ? path.join(cacheHome, "packages", "opencode-gemini-auth", "node_modules", "opencode-gemini-auth", "dist", "index.js") : undefined,
644
+ ]);
645
+ }
496
646
  function claudeKeychainServiceName(env) {
497
647
  const configDir = env?.CLAUDE_CONFIG_DIR;
498
648
  const defaultService = configDir ? `Claude Code-credentials-${createHash("sha256").update(configDir).digest("hex").slice(0, 8)}` : "Claude Code-credentials";