@danmoisan/drm-copilot-mcp 1.1.11 → 1.1.12
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/out/mcp-server.js +366 -159
- package/package.json +1 -1
- package/resources/claude-customizations/.claude/agents/epic-orchestrator.md +1 -1
- package/resources/claude-customizations/.claude/agents/epic-planner.md +4 -1
- package/resources/claude-customizations/.claude/agents/pr-author.md +15 -0
- package/resources/claude-customizations/.claude/hooks/enforce-epic-merge-gate-authorization.ps1 +443 -0
- package/resources/claude-customizations/.claude/hooks/enforce-epic-merge-gate.ps1 +33 -37
- package/resources/claude-customizations/.claude/hooks/enforce-model-routing-receipt.ps1 +120 -5
- package/resources/claude-customizations/.claude/hooks/enforce-orchestration-preimplementation-gate-epic-scope.ps1 +127 -0
- package/resources/claude-customizations/.claude/hooks/enforce-orchestration-preimplementation-gate-helpers.ps1 +163 -15
- package/resources/claude-customizations/.claude/hooks/enforce-orchestration-preimplementation-gate.ps1 +14 -27
- package/resources/claude-customizations/.claude/hooks/enforce-parallel-worktree-removal-gate.ps1 +66 -5
- package/resources/claude-customizations/.claude/hooks/enforce-pr-author-skill-helpers.ps1 +134 -10
- package/resources/claude-customizations/.claude/hooks/enforce-pr-author-skill.epic-base-branch.ps1 +37 -8
- package/resources/claude-customizations/.claude/hooks/enforce-pr-author-skill.ps1 +3 -1
- package/resources/claude-customizations/.claude/hooks/enforce-prd-feature-before-planner-helpers.ps1 +332 -0
- package/resources/claude-customizations/.claude/hooks/enforce-prd-feature-before-planner.ps1 +211 -182
- package/resources/claude-customizations/.claude/lib/codex-routing/CodexDeployment.psm1 +1 -0
- package/resources/claude-customizations/.claude/lib/worktree-resolution/EpicScopeReadiness.psm1 +176 -0
- package/resources/claude-customizations/.claude/lib/worktree-resolution/EpicScopeResolution.psm1 +369 -0
- package/resources/claude-customizations/.claude/lib/worktree-resolution/WorktreeItemResolution.psm1 +392 -0
- package/resources/claude-customizations/.claude/lib/worktree-resolution/WorktreeResolution.psm1 +500 -0
- package/resources/claude-customizations/.claude/lib/worktree-resolution/WorktreeTargetResolution.psm1 +347 -0
- package/resources/claude-customizations/.claude/rules/orchestrator-state.md +27 -0
- package/resources/claude-customizations/.claude/skills/cleanup-merged-worktrees/SKILL.md +12 -0
- package/resources/claude-customizations/.claude/skills/epic-orchestrate/SKILL.md +14 -1
- package/resources/claude-customizations/.claude/skills/epic-plan/SKILL.md +4 -0
- package/resources/claude-customizations/.claude/skills/orchestrate/SKILL.md +4 -1
- package/resources/claude-customizations/.claude/skills/parallel-orchestrate/SKILL.md +39 -6
- package/resources/claude-customizations/.claude/skills/pr-author/SKILL.md +15 -0
- package/resources/claude-customizations/.claude/skills/quota-throttling/SKILL.md +221 -0
- package/resources/claude-customizations/pack-manifests/core.json +9 -0
- package/resources/codex-and-agents-customizations/.agents/skills/codex-model-routing/SKILL.md +13 -14
- package/resources/codex-and-agents-customizations/.codex/config.toml +1 -1
- package/resources/codex-and-agents-customizations/.codex/hooks/enforce-epic-merge-gate.ps1 +193 -0
- package/resources/codex-and-agents-customizations/.codex/hooks/enforce-epic-planning-only.ps1 +15 -5
- package/resources/codex-and-agents-customizations/.codex/hooks/enforce-orchestration-preimplementation-gate-helpers.ps1 +163 -15
- package/resources/codex-and-agents-customizations/.codex/scripts/Resolve-CodexDeployment.ps1 +87 -0
- package/resources/codex-and-agents-customizations/.codex/scripts/Resolve-CodexTopology.ps1 +96 -0
- package/resources/codex-and-agents-customizations/.codex/scripts/codex-routing-cli-common.ps1 +370 -0
- package/resources/codex-and-agents-customizations/.codex/scripts/epic-child-launch-contract.ps1 +475 -0
- package/resources/codex-and-agents-customizations/.codex/scripts/epic-child-launch-runtime.ps1 +445 -0
- package/resources/codex-and-agents-customizations/.codex/scripts/epic-child-persistence-runtime.ps1 +118 -0
- package/resources/codex-and-agents-customizations/.codex/scripts/epic-child-sandbox-preflight.ps1 +69 -0
- package/resources/codex-and-agents-customizations/.codex/scripts/launch-epic-child-wave.ps1 +471 -0
- package/resources/codex-and-agents-customizations/.codex/scripts/post-codex-worktree-session.ps1 +280 -0
- package/resources/codex-and-agents-customizations/.codex/scripts/resume-epic-child.ps1 +263 -0
- package/resources/codex-and-agents-customizations/.codex-variants/csharp-legacy/agents/csharp-typed-engineer.toml +2 -1
- package/resources/codex-and-agents-customizations/pack-manifests/core.json +18 -1
- package/resources/lib/codex-routing/CodexDeployment.psm1 +315 -0
- package/resources/lib/codex-routing/CodexTopology.psm1 +394 -0
- package/resources/powershell/PoshQC/settings/pester.runsettings.psd1 +26 -0
package/resources/codex-and-agents-customizations/.codex/scripts/post-codex-worktree-session.ps1
ADDED
|
@@ -0,0 +1,280 @@
|
|
|
1
|
+
[CmdletBinding()]
|
|
2
|
+
param(
|
|
3
|
+
[string]$SourceRoot = "",
|
|
4
|
+
[string]$WorktreeRoot = ""
|
|
5
|
+
)
|
|
6
|
+
|
|
7
|
+
Set-StrictMode -Version Latest
|
|
8
|
+
$ErrorActionPreference = 'Stop'
|
|
9
|
+
|
|
10
|
+
function ConvertTo-NormalizedRootPath {
|
|
11
|
+
[CmdletBinding()]
|
|
12
|
+
param(
|
|
13
|
+
[Parameter(Mandatory = $true)]
|
|
14
|
+
[string]$Path
|
|
15
|
+
)
|
|
16
|
+
|
|
17
|
+
return $Path.Replace('\', '/').TrimEnd('/')
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function Join-NormalizedPath {
|
|
21
|
+
[CmdletBinding()]
|
|
22
|
+
[OutputType([string])]
|
|
23
|
+
param(
|
|
24
|
+
[Parameter(Mandatory = $true)]
|
|
25
|
+
[string]$Root,
|
|
26
|
+
|
|
27
|
+
[Parameter(Mandatory = $true)]
|
|
28
|
+
[string]$ChildPath
|
|
29
|
+
)
|
|
30
|
+
|
|
31
|
+
$normalizedRoot = ConvertTo-NormalizedRootPath -Path $Root
|
|
32
|
+
$normalizedChildPath = $ChildPath.Replace('\', '/').TrimStart('/')
|
|
33
|
+
return "$normalizedRoot/$normalizedChildPath"
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function Get-NormalizedParentPath {
|
|
37
|
+
[CmdletBinding()]
|
|
38
|
+
param(
|
|
39
|
+
[Parameter(Mandatory = $true)]
|
|
40
|
+
[string]$Path
|
|
41
|
+
)
|
|
42
|
+
|
|
43
|
+
return (Split-Path -Parent $Path).Replace('\', '/').TrimEnd('/')
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function Get-RelativeCustomizationPath {
|
|
47
|
+
[CmdletBinding()]
|
|
48
|
+
param(
|
|
49
|
+
[Parameter(Mandatory = $true)]
|
|
50
|
+
[string]$SourceFolder,
|
|
51
|
+
|
|
52
|
+
[Parameter(Mandatory = $true)]
|
|
53
|
+
[string]$SourcePath
|
|
54
|
+
)
|
|
55
|
+
|
|
56
|
+
$normalizedSourceFolder = ConvertTo-NormalizedRootPath -Path $SourceFolder
|
|
57
|
+
$normalizedSourcePath = $SourcePath.Replace('\', '/')
|
|
58
|
+
return $normalizedSourcePath.Substring($normalizedSourceFolder.Length).TrimStart('/')
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function Test-TransientCustomizationPath {
|
|
62
|
+
[CmdletBinding()]
|
|
63
|
+
[OutputType([bool])]
|
|
64
|
+
param(
|
|
65
|
+
[Parameter(Mandatory = $true)]
|
|
66
|
+
[string]$RelativePath
|
|
67
|
+
)
|
|
68
|
+
|
|
69
|
+
$normalizedRelativePath = $RelativePath.Replace('\', '/').TrimStart('/')
|
|
70
|
+
$firstSegment = ($normalizedRelativePath -split '/')[0]
|
|
71
|
+
$blockedSegments = @(
|
|
72
|
+
'.cache',
|
|
73
|
+
'.venv',
|
|
74
|
+
'cache',
|
|
75
|
+
'caches',
|
|
76
|
+
'credentials',
|
|
77
|
+
'dist',
|
|
78
|
+
'logs',
|
|
79
|
+
'node_modules',
|
|
80
|
+
'out',
|
|
81
|
+
'sessions',
|
|
82
|
+
'tmp',
|
|
83
|
+
'venv'
|
|
84
|
+
)
|
|
85
|
+
|
|
86
|
+
if ($blockedSegments -contains $firstSegment) {
|
|
87
|
+
return $true
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
return $normalizedRelativePath -match '(^|/)(.*\.log|.*\.tmp|.*\.cache|credentials(\..*)?)$'
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
function Resolve-CodexCustomizationSourceRoot {
|
|
94
|
+
[CmdletBinding()]
|
|
95
|
+
param(
|
|
96
|
+
[string]$SourceRoot = "",
|
|
97
|
+
|
|
98
|
+
[Parameter(Mandatory = $true)]
|
|
99
|
+
[string]$WorktreeRoot,
|
|
100
|
+
|
|
101
|
+
[string]$ScriptRoot = $PSScriptRoot,
|
|
102
|
+
|
|
103
|
+
[scriptblock]$InvokeGit = {
|
|
104
|
+
param([string[]]$GitArgs)
|
|
105
|
+
git @GitArgs 2>$null
|
|
106
|
+
}
|
|
107
|
+
)
|
|
108
|
+
|
|
109
|
+
if (-not [string]::IsNullOrWhiteSpace($SourceRoot)) {
|
|
110
|
+
return ConvertTo-NormalizedRootPath -Path $SourceRoot
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
$normalizedWorktreeRoot = ConvertTo-NormalizedRootPath -Path $WorktreeRoot
|
|
114
|
+
$commonDir = [string]::Join("`n", @(& $InvokeGit -GitArgs @('-C', $normalizedWorktreeRoot, 'rev-parse', '--git-common-dir'))).Trim()
|
|
115
|
+
if (-not [string]::IsNullOrWhiteSpace($commonDir)) {
|
|
116
|
+
$normalizedCommonDir = ConvertTo-NormalizedRootPath -Path $commonDir
|
|
117
|
+
if ($normalizedCommonDir.EndsWith('/.git', [System.StringComparison]::OrdinalIgnoreCase)) {
|
|
118
|
+
return Get-NormalizedParentPath -Path $normalizedCommonDir
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
$worktreeList = @(& $InvokeGit -GitArgs @('-C', $normalizedWorktreeRoot, 'worktree', 'list', '--porcelain'))
|
|
123
|
+
foreach ($line in $worktreeList) {
|
|
124
|
+
if ($line -match '^worktree\s+(.+)$') {
|
|
125
|
+
return ConvertTo-NormalizedRootPath -Path $Matches[1]
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
$fallbackPath = [System.IO.Path]::GetFullPath((Join-Path -Path $ScriptRoot -ChildPath '../..'))
|
|
130
|
+
return ConvertTo-NormalizedRootPath -Path $fallbackPath
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
function Get-CodexCustomizationCopyPlan {
|
|
134
|
+
[CmdletBinding()]
|
|
135
|
+
[OutputType([pscustomobject])]
|
|
136
|
+
param(
|
|
137
|
+
[Parameter(Mandatory = $true)]
|
|
138
|
+
[string]$SourceRoot,
|
|
139
|
+
|
|
140
|
+
[Parameter(Mandatory = $true)]
|
|
141
|
+
[string]$WorktreeRoot,
|
|
142
|
+
|
|
143
|
+
[scriptblock]$TestPath = {
|
|
144
|
+
param([string]$LiteralPath)
|
|
145
|
+
Test-Path -LiteralPath $LiteralPath -PathType Container
|
|
146
|
+
},
|
|
147
|
+
|
|
148
|
+
[scriptblock]$GetChildItem = {
|
|
149
|
+
param([string]$LiteralPath)
|
|
150
|
+
Get-ChildItem -LiteralPath $LiteralPath -File -Recurse
|
|
151
|
+
}
|
|
152
|
+
)
|
|
153
|
+
|
|
154
|
+
$normalizedSourceRoot = ConvertTo-NormalizedRootPath -Path $SourceRoot
|
|
155
|
+
$normalizedWorktreeRoot = ConvertTo-NormalizedRootPath -Path $WorktreeRoot
|
|
156
|
+
if ($normalizedSourceRoot.Equals($normalizedWorktreeRoot, [System.StringComparison]::OrdinalIgnoreCase)) {
|
|
157
|
+
return
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
foreach ($customizationFolder in @('.codex', '.agents')) {
|
|
161
|
+
$sourceFolder = Join-NormalizedPath -Root $normalizedSourceRoot -ChildPath $customizationFolder
|
|
162
|
+
if (-not (& $TestPath -LiteralPath $sourceFolder)) {
|
|
163
|
+
continue
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
foreach ($file in @(& $GetChildItem -LiteralPath $sourceFolder | Sort-Object -Property FullName)) {
|
|
167
|
+
$fullNameProperty = $file.PSObject.Properties['FullName']
|
|
168
|
+
if ($null -eq $fullNameProperty) {
|
|
169
|
+
throw "Customization file object is missing FullName."
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
$sourcePath = [string]$fullNameProperty.Value
|
|
173
|
+
$relativePath = Get-RelativeCustomizationPath -SourceFolder $sourceFolder -SourcePath $sourcePath
|
|
174
|
+
if (Test-TransientCustomizationPath -RelativePath $relativePath) {
|
|
175
|
+
[pscustomobject]@{
|
|
176
|
+
Action = 'Skip'
|
|
177
|
+
CustomizationFolder = $customizationFolder
|
|
178
|
+
SourcePath = $sourcePath.Replace('\', '/')
|
|
179
|
+
DestinationPath = ''
|
|
180
|
+
Reason = 'transient or machine-local path'
|
|
181
|
+
}
|
|
182
|
+
continue
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
[pscustomobject]@{
|
|
186
|
+
Action = 'Copy'
|
|
187
|
+
CustomizationFolder = $customizationFolder
|
|
188
|
+
SourcePath = $sourcePath.Replace('\', '/')
|
|
189
|
+
DestinationPath = Join-NormalizedPath -Root $normalizedWorktreeRoot -ChildPath "$customizationFolder/$relativePath"
|
|
190
|
+
Reason = ''
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
function Invoke-CodexCustomizationCopyPlan {
|
|
197
|
+
[CmdletBinding()]
|
|
198
|
+
param(
|
|
199
|
+
[Parameter(Mandatory = $true)]
|
|
200
|
+
[AllowEmptyCollection()]
|
|
201
|
+
[object[]]$CopyOperation,
|
|
202
|
+
|
|
203
|
+
[scriptblock]$NewDirectory = {
|
|
204
|
+
param([string]$LiteralPath)
|
|
205
|
+
New-Item -ItemType Directory -Force -Path $LiteralPath | Out-Null
|
|
206
|
+
},
|
|
207
|
+
|
|
208
|
+
[scriptblock]$CopyFile = {
|
|
209
|
+
param(
|
|
210
|
+
[string]$SourcePath,
|
|
211
|
+
[string]$DestinationPath
|
|
212
|
+
)
|
|
213
|
+
Copy-Item -LiteralPath $SourcePath -Destination $DestinationPath -Force
|
|
214
|
+
}
|
|
215
|
+
)
|
|
216
|
+
|
|
217
|
+
foreach ($operation in @($CopyOperation | Where-Object { $_.Action -ne 'Skip' })) {
|
|
218
|
+
$destinationDirectory = Get-NormalizedParentPath -Path $operation.DestinationPath
|
|
219
|
+
& $NewDirectory -LiteralPath $destinationDirectory
|
|
220
|
+
& $CopyFile -SourcePath $operation.SourcePath -DestinationPath $operation.DestinationPath
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
function Write-CodexCustomizationCopySummary {
|
|
225
|
+
[CmdletBinding()]
|
|
226
|
+
param(
|
|
227
|
+
[Parameter(Mandatory = $true)]
|
|
228
|
+
[string]$SourceRoot,
|
|
229
|
+
|
|
230
|
+
[Parameter(Mandatory = $true)]
|
|
231
|
+
[string]$WorktreeRoot,
|
|
232
|
+
|
|
233
|
+
[Parameter(Mandatory = $true)]
|
|
234
|
+
[AllowEmptyCollection()]
|
|
235
|
+
[object[]]$CopyOperation,
|
|
236
|
+
|
|
237
|
+
[Parameter(Mandatory = $true)]
|
|
238
|
+
[AllowEmptyCollection()]
|
|
239
|
+
[object[]]$SkippedOperation,
|
|
240
|
+
|
|
241
|
+
[scriptblock]$WriteLog = {
|
|
242
|
+
param([string]$Message)
|
|
243
|
+
Write-Information -MessageData $Message -InformationAction Continue
|
|
244
|
+
}
|
|
245
|
+
)
|
|
246
|
+
|
|
247
|
+
& $WriteLog -Message "SourceRoot: $(ConvertTo-NormalizedRootPath -Path $SourceRoot)"
|
|
248
|
+
& $WriteLog -Message "WorktreeRoot: $(ConvertTo-NormalizedRootPath -Path $WorktreeRoot)"
|
|
249
|
+
& $WriteLog -Message "Copied: $(@($CopyOperation).Count)"
|
|
250
|
+
foreach ($operation in @($CopyOperation | Select-Object -First 5)) {
|
|
251
|
+
& $WriteLog -Message " copied $($operation.CustomizationFolder): $($operation.DestinationPath)"
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
& $WriteLog -Message "Skipped: $(@($SkippedOperation).Count)"
|
|
255
|
+
foreach ($operation in @($SkippedOperation | Select-Object -First 5)) {
|
|
256
|
+
& $WriteLog -Message " skipped $($operation.CustomizationFolder): $($operation.Reason)"
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
if ($MyInvocation.InvocationName -ne '.') {
|
|
261
|
+
$effectiveWorktreeRoot = if ([string]::IsNullOrWhiteSpace($WorktreeRoot)) {
|
|
262
|
+
(Get-Location).Path
|
|
263
|
+
}
|
|
264
|
+
else {
|
|
265
|
+
$WorktreeRoot
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
$effectiveSourceRoot = Resolve-CodexCustomizationSourceRoot -SourceRoot $SourceRoot -WorktreeRoot $effectiveWorktreeRoot
|
|
269
|
+
$plan = @(
|
|
270
|
+
Get-CodexCustomizationCopyPlan -SourceRoot $effectiveSourceRoot -WorktreeRoot $effectiveWorktreeRoot
|
|
271
|
+
)
|
|
272
|
+
$copyOperations = @($plan | Where-Object { $_.Action -ne 'Skip' })
|
|
273
|
+
$skippedOperations = @($plan | Where-Object { $_.Action -eq 'Skip' })
|
|
274
|
+
Invoke-CodexCustomizationCopyPlan -CopyOperation $copyOperations
|
|
275
|
+
Write-CodexCustomizationCopySummary `
|
|
276
|
+
-SourceRoot $effectiveSourceRoot `
|
|
277
|
+
-WorktreeRoot $effectiveWorktreeRoot `
|
|
278
|
+
-CopyOperation $copyOperations `
|
|
279
|
+
-SkippedOperation $skippedOperations
|
|
280
|
+
}
|
|
@@ -0,0 +1,263 @@
|
|
|
1
|
+
# Resume one launcher-authorized Codex epic child with its sealed runtime controls.
|
|
2
|
+
[CmdletBinding(SupportsShouldProcess)]
|
|
3
|
+
param(
|
|
4
|
+
[Parameter(Mandatory)][string] $ReceiptPath,
|
|
5
|
+
[AllowEmptyString()][string] $Prompt = '',
|
|
6
|
+
[AllowEmptyString()][string] $LastMessagePath = ''
|
|
7
|
+
)
|
|
8
|
+
|
|
9
|
+
$ErrorActionPreference = 'Stop'
|
|
10
|
+
. (Join-Path $PSScriptRoot 'epic-child-launch-contract.ps1')
|
|
11
|
+
. (Join-Path $PSScriptRoot 'epic-child-launch-runtime.ps1')
|
|
12
|
+
|
|
13
|
+
function Test-CodexChildResumeSpecEntry {
|
|
14
|
+
[OutputType([string[]])]
|
|
15
|
+
param([Parameter(Mandatory)] $Receipt, [Parameter(Mandatory)] $Spec)
|
|
16
|
+
$errors = [System.Collections.Generic.List[string]]::new()
|
|
17
|
+
$entry = @($Spec.launches | Where-Object { [string]$_.launch_id -eq [string]$Receipt.launch_id })
|
|
18
|
+
if ($entry.Count -ne 1) {
|
|
19
|
+
return @('sealed launch specification must contain the receipt launch_id exactly once.')
|
|
20
|
+
}
|
|
21
|
+
$entry = $entry[0]
|
|
22
|
+
$pairs = @(
|
|
23
|
+
@('delegation_id', 'delegation_id'), @('feature_folder', 'feature_folder'),
|
|
24
|
+
@('deployment_agent', 'deployment_agent'), @('model', 'model'),
|
|
25
|
+
@('model_reasoning_effort', 'model_reasoning_effort'), @('permissions', 'permissions'),
|
|
26
|
+
@('execution_context', 'execution_context'), @('worktree_path', 'worktree_path'),
|
|
27
|
+
@('branch_name', 'branch_name')
|
|
28
|
+
)
|
|
29
|
+
foreach ($pair in $pairs) {
|
|
30
|
+
if ([string]$entry.($pair[0]) -cne [string]$Receipt.($pair[1])) {
|
|
31
|
+
$errors.Add("sealed launch specification $($pair[0]) differs from the receipt.")
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
if (-not (Test-CodexChildIssueEqual -Left $entry.issue_num -Right $Receipt.issue_num)) {
|
|
35
|
+
$errors.Add('sealed launch specification issue_num differs from the receipt.')
|
|
36
|
+
}
|
|
37
|
+
if ((Get-CodexChildSha256 -Value ([string]$entry.prompt)) -ne [string]$Receipt.prompt_sha256) {
|
|
38
|
+
$errors.Add('sealed launch specification prompt hash differs from the receipt.')
|
|
39
|
+
}
|
|
40
|
+
return $errors.ToArray()
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function Get-CodexChildResumeContext {
|
|
44
|
+
[CmdletBinding()]
|
|
45
|
+
param([Parameter(Mandatory)][string] $Path)
|
|
46
|
+
if (-not [System.IO.Path]::IsPathFullyQualified($Path)) {
|
|
47
|
+
throw 'EPIC_CHILD_RESUME_BLOCKED: ReceiptPath must be an absolute path.'
|
|
48
|
+
}
|
|
49
|
+
$canonicalReceipt = Get-CodexChildCanonicalPath -Path $Path -BasePath (Get-Location).Path
|
|
50
|
+
$receipt = ConvertFrom-CodexChildLaunchJson -Raw (Get-Content -Raw -LiteralPath $canonicalReceipt) -Name 'launch receipt'
|
|
51
|
+
$errors = [System.Collections.Generic.List[string]]::new()
|
|
52
|
+
$lifecycleErrors = if ([string]$receipt.state -eq 'active') {
|
|
53
|
+
@(Get-CodexChildActiveReceiptErrorList -Receipt $receipt)
|
|
54
|
+
} else {
|
|
55
|
+
@(Get-CodexChildTerminalReceiptErrorList -Receipt $receipt)
|
|
56
|
+
}
|
|
57
|
+
foreach ($receiptError in $lifecycleErrors) {
|
|
58
|
+
$errors.Add($receiptError)
|
|
59
|
+
}
|
|
60
|
+
if ((Get-CodexChildCanonicalPath -Path ([string]$receipt.receipt_path) -BasePath (Get-Location).Path) -ne $canonicalReceipt) {
|
|
61
|
+
$errors.Add('receipt_path does not identify the loaded sealed receipt.')
|
|
62
|
+
}
|
|
63
|
+
$worktree = Get-CodexChildCanonicalPath -Path ([string]$receipt.worktree_path) `
|
|
64
|
+
-BasePath ([string]$receipt.trusted_repository_root)
|
|
65
|
+
$top = Get-CodexChildGitScalar -GitArgs @('-C', $worktree, 'rev-parse', '--show-toplevel')
|
|
66
|
+
if ((Get-CodexChildCanonicalPath -Path $top -BasePath $worktree) -ne $worktree) {
|
|
67
|
+
$errors.Add('worktree_path is not the exact Git worktree root.')
|
|
68
|
+
}
|
|
69
|
+
if ((Get-CodexChildCommonDirectory -WorktreePath $worktree) -ne [string]$receipt.git_common_directory) {
|
|
70
|
+
$errors.Add('worktree no longer shares the sealed Git common directory.')
|
|
71
|
+
}
|
|
72
|
+
$branch = Get-CodexChildGitScalar -GitArgs @('-C', $worktree, 'branch', '--show-current')
|
|
73
|
+
if ($branch -cne [string]$receipt.branch_name) { $errors.Add('live branch differs from the sealed receipt.') }
|
|
74
|
+
$head = Get-CodexChildGitScalar -GitArgs @('-C', $worktree, 'rev-parse', 'HEAD')
|
|
75
|
+
foreach ($ancestor in @([string]$receipt.integration_head, [string]$receipt.child_head)) {
|
|
76
|
+
if (-not (Test-CodexChildGit -GitArgs @('-C', $worktree, 'merge-base', '--is-ancestor', $ancestor, $head))) {
|
|
77
|
+
$errors.Add('worktree HEAD no longer descends from the sealed launch ancestry.')
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
$trustedObjects = Get-CodexChildSurfaceObjectMap -RepositoryPath ([string]$receipt.trusted_repository_root) `
|
|
81
|
+
-Commit ([string]$receipt.trusted_repository_head)
|
|
82
|
+
$currentObjects = Get-CodexChildSurfaceObjectMap -RepositoryPath $worktree -Commit $head
|
|
83
|
+
if (-not (Test-CodexChildSurfaceObjectsEqual -Expected $receipt.trusted_surface_objects -Actual $trustedObjects) -or
|
|
84
|
+
-not (Test-CodexChildSurfaceObjectsEqual -Expected $receipt.trusted_surface_objects -Actual $currentObjects) -or
|
|
85
|
+
(Get-CodexChildSurfaceFingerprint -SurfaceObjects $currentObjects) -ne [string]$receipt.trusted_surface_sha256) {
|
|
86
|
+
$errors.Add('trusted Codex instructions, skills, configuration, or routing surfaces have changed.')
|
|
87
|
+
}
|
|
88
|
+
$surfaceStatus = @(Invoke-CodexChildGit -GitArgs @(
|
|
89
|
+
'-C', $worktree, 'status', '--porcelain=v1', '--untracked-files=all', '--',
|
|
90
|
+
'.codex', '.agents', 'AGENTS.md', 'config/orchestration-routing.json'
|
|
91
|
+
))
|
|
92
|
+
if (-not (Test-CodexChildCustomizationClean -StatusLines $surfaceStatus)) {
|
|
93
|
+
$errors.Add('trusted customization surfaces are not clean against worktree HEAD.')
|
|
94
|
+
}
|
|
95
|
+
$profilePath = Get-CodexChildCanonicalPath -Path ([string]$receipt.profile_path) -BasePath $worktree
|
|
96
|
+
$expectedProfile = Get-CodexChildCanonicalPath -Path ".codex/agents/$($receipt.deployment_agent).toml" -BasePath $worktree
|
|
97
|
+
$profileRaw = Get-Content -Raw -LiteralPath $profilePath
|
|
98
|
+
$agentProfile = ConvertFrom-CodexAgentProfile -ProfileRaw $profileRaw
|
|
99
|
+
if ($profilePath -ne $expectedProfile -or
|
|
100
|
+
(Get-FileHash -LiteralPath $profilePath -Algorithm SHA256).Hash.ToLowerInvariant() -cne [string]$receipt.profile_sha256 -or
|
|
101
|
+
[string]$agentProfile.name -cne [string]$receipt.deployment_agent -or [string]$agentProfile.model -cne [string]$receipt.model -or
|
|
102
|
+
[string]$agentProfile.model_reasoning_effort -cne [string]$receipt.model_reasoning_effort -or
|
|
103
|
+
[string]$agentProfile.default_permissions -cne [string]$receipt.permissions -or
|
|
104
|
+
(Get-CodexChildSha256 -Value ([string]$agentProfile.developer_instructions) -ne [string]$receipt.developer_instructions_sha256) -or
|
|
105
|
+
(Get-CodexChildSha256 -Value ([string]$agentProfile.skills_config) -ne [string]$receipt.skills_config_sha256)) {
|
|
106
|
+
$errors.Add('checked-in generated deployment profile differs from the sealed receipt.')
|
|
107
|
+
}
|
|
108
|
+
$specPath = Get-CodexChildCanonicalPath -Path ([string]$receipt.spec_path) -BasePath ([string]$receipt.trusted_repository_root)
|
|
109
|
+
if ((Get-FileHash -LiteralPath $specPath -Algorithm SHA256).Hash.ToLowerInvariant() -ne [string]$receipt.spec_sha256) {
|
|
110
|
+
$errors.Add('sealed launch specification hash has changed.')
|
|
111
|
+
}
|
|
112
|
+
$spec = ConvertFrom-CodexChildLaunchJson -Raw (Get-Content -Raw -LiteralPath $specPath) -Name 'launch spec'
|
|
113
|
+
if ([string]$receipt.checkpoint_kind -cne [string]$spec.checkpoint_kind) {
|
|
114
|
+
$errors.Add('checkpoint_kind differs from the sealed launch specification.')
|
|
115
|
+
}
|
|
116
|
+
$expectedLockPath = Get-CodexChildSemanticWaveLockPath -Spec $spec -ArtifactRoot (Split-Path $specPath -Parent)
|
|
117
|
+
$receiptLockPath = Get-CodexChildCanonicalPath -Path ([string]$receipt.wave_lock_path) `
|
|
118
|
+
-BasePath ([string]$receipt.trusted_repository_root)
|
|
119
|
+
if ($receiptLockPath -cne $expectedLockPath) {
|
|
120
|
+
$errors.Add('wave_lock_path does not match the sealed semantic wave identity.')
|
|
121
|
+
}
|
|
122
|
+
foreach ($specError in @(Test-CodexChildResumeSpecEntry -Receipt $receipt -Spec $spec)) {
|
|
123
|
+
$errors.Add($specError)
|
|
124
|
+
}
|
|
125
|
+
$codexRuntime = Get-CodexChildCommandContext
|
|
126
|
+
$codexHome = Get-CodexChildCanonicalPath -Path ([string]$receipt.codex_home_path) `
|
|
127
|
+
-BasePath (Get-CodexChildAuthorityRoot)
|
|
128
|
+
if (-not $codexHome.StartsWith((Get-CodexChildAuthorityRoot) + [System.IO.Path]::DirectorySeparatorChar,
|
|
129
|
+
[System.StringComparison]::OrdinalIgnoreCase) -or -not (Test-Path -LiteralPath $codexHome -PathType Container)) {
|
|
130
|
+
$errors.Add('isolated CODEX_HOME is missing or outside launcher authority.')
|
|
131
|
+
}
|
|
132
|
+
$expectedDeniedPaths = [string[]]@($codexRuntime.DeniedPaths) + @($codexHome)
|
|
133
|
+
if ([string]$codexRuntime.CommandPath -ne [string]$receipt.codex_command_path -or
|
|
134
|
+
(@($expectedDeniedPaths) | ConvertTo-Json -Compress) -ne (@($receipt.codex_denied_paths) | ConvertTo-Json -Compress)) {
|
|
135
|
+
$errors.Add('Codex executable or package paths differ from the sealed runtime deny set.')
|
|
136
|
+
}
|
|
137
|
+
if ($errors.Count -gt 0) { throw "EPIC_CHILD_RESUME_BLOCKED: $($errors -join ' ')" }
|
|
138
|
+
return [pscustomobject]@{ Receipt = $receipt; Profile = $agentProfile; CodexRuntime = $codexRuntime }
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
function Get-CodexChildResumeStartInfo {
|
|
142
|
+
[CmdletBinding()]
|
|
143
|
+
param(
|
|
144
|
+
[Parameter(Mandatory)] $Context,
|
|
145
|
+
[AllowEmptyString()][string] $ResumePrompt = '',
|
|
146
|
+
[AllowEmptyString()][string] $OutputPath = ''
|
|
147
|
+
)
|
|
148
|
+
$receipt = $Context.Receipt; $agentProfile = $Context.Profile
|
|
149
|
+
$commandPath = [string]$Context.CodexRuntime.CommandPath
|
|
150
|
+
$isPowerShellShim = $commandPath.EndsWith('.ps1', [System.StringComparison]::OrdinalIgnoreCase)
|
|
151
|
+
$info = [System.Diagnostics.ProcessStartInfo]::new($(if ($isPowerShellShim) { 'pwsh' } else { $commandPath }))
|
|
152
|
+
$info.UseShellExecute = $false; $info.WorkingDirectory = [string]$receipt.worktree_path
|
|
153
|
+
if ($isPowerShellShim) { foreach ($item in @('-NoProfile', '-File', $commandPath)) { $info.ArgumentList.Add($item) } }
|
|
154
|
+
$permissionOverride = Get-CodexChildPermissionOverride -DeniedPaths ([string[]]$receipt.codex_denied_paths)
|
|
155
|
+
$projectsOverride = Get-CodexChildProjectsOverride -WorktreePath ([string]$receipt.worktree_path)
|
|
156
|
+
$shellOverrides = Get-CodexChildShellEnvironmentOverrideList -WorktreePath ([string]$receipt.worktree_path)
|
|
157
|
+
foreach ($item in @(
|
|
158
|
+
'exec', 'resume', '--ignore-user-config', '-c', $projectsOverride, '-c', $permissionOverride,
|
|
159
|
+
'-c', $shellOverrides[0], '-c', $shellOverrides[1],
|
|
160
|
+
'-m', [string]$receipt.model, '-c', "model_reasoning_effort=$($receipt.model_reasoning_effort)",
|
|
161
|
+
'-c', 'default_permissions="epic-child-workspace"', '-c', 'approval_policy="never"',
|
|
162
|
+
'-c', ('developer_instructions=' + ([string]$agentProfile.developer_instructions | ConvertTo-Json -Compress)),
|
|
163
|
+
'-c', "skills.config=$($agentProfile.skills_config)", '--strict-config',
|
|
164
|
+
'--dangerously-bypass-hook-trust', '--json'
|
|
165
|
+
)) { $info.ArgumentList.Add($item) }
|
|
166
|
+
if ($IsWindows) { $info.ArgumentList.Insert(2, 'windows.sandbox="elevated"'); $info.ArgumentList.Insert(2, '-c') }
|
|
167
|
+
if (-not [string]::IsNullOrWhiteSpace($OutputPath)) { $info.ArgumentList.Add('-o'); $info.ArgumentList.Add($OutputPath) }
|
|
168
|
+
$info.ArgumentList.Add([string]$receipt.codex_session_id)
|
|
169
|
+
if (-not [string]::IsNullOrWhiteSpace($ResumePrompt)) { $info.ArgumentList.Add($ResumePrompt) }
|
|
170
|
+
$environment = @{
|
|
171
|
+
CODEX_EPIC_CHILD_LAUNCH_ID = [string]$receipt.launch_id
|
|
172
|
+
CODEX_EPIC_CHILD_LAUNCH_RECEIPT = [string]$receipt.receipt_path
|
|
173
|
+
CODEX_EPIC_CHILD_LAUNCH_SPEC = [string]$receipt.spec_path
|
|
174
|
+
CODEX_EPIC_CHILD_EXPECTED_WORKTREE = [string]$receipt.worktree_path
|
|
175
|
+
CODEX_EPIC_CHILD_DELEGATION_ID = [string]$receipt.delegation_id
|
|
176
|
+
CODEX_EPIC_CHILD_EXECUTION_CONTEXT = [string]$receipt.execution_context
|
|
177
|
+
CODEX_EPIC_CHILD_AGENT = [string]$receipt.deployment_agent
|
|
178
|
+
CODEX_EPIC_CHILD_MODEL = [string]$receipt.model
|
|
179
|
+
CODEX_EPIC_CHILD_REASONING_EFFORT = [string]$receipt.model_reasoning_effort
|
|
180
|
+
CODEX_EPIC_CHILD_PROFILE_SHA256 = [string]$receipt.profile_sha256
|
|
181
|
+
CODEX_EPIC_CHILD_SESSION_ID = [string]$receipt.codex_session_id
|
|
182
|
+
}
|
|
183
|
+
foreach ($item in $environment.GetEnumerator()) { $info.Environment[$item.Key] = $item.Value }
|
|
184
|
+
$info.Environment['CODEX_HOME'] = [string]$receipt.codex_home_path
|
|
185
|
+
return $info
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
function Set-CodexChildResumeWaveStatus {
|
|
189
|
+
[CmdletBinding(SupportsShouldProcess)]
|
|
190
|
+
param([Parameter(Mandatory)] $Receipt)
|
|
191
|
+
if (-not $PSCmdlet.ShouldProcess([string]$Receipt.status_path, 'Update epic-child wave status from resume receipt')) {
|
|
192
|
+
return
|
|
193
|
+
}
|
|
194
|
+
$status = ConvertFrom-CodexChildLaunchJson -Raw `
|
|
195
|
+
(Get-Content -Raw -LiteralPath ([string]$Receipt.status_path)) -Name 'wave status'
|
|
196
|
+
$property = $status.launches.PSObject.Properties[[string]$Receipt.launch_id]
|
|
197
|
+
if ($null -eq $property) { throw 'EPIC_CHILD_RESUME_BLOCKED: wave status lacks the receipt launch_id.' }
|
|
198
|
+
$entry = $property.Value
|
|
199
|
+
$entry.state = [string]$Receipt.state; $entry.codex_session_id = [string]$Receipt.codex_session_id
|
|
200
|
+
$entry.receipt_path = [string]$Receipt.receipt_path
|
|
201
|
+
if ([string]$Receipt.state -eq 'completed') {
|
|
202
|
+
$entry.exit_code = 0; $entry.completed_at = [string]$Receipt.completed_at
|
|
203
|
+
} elseif ([string]$Receipt.state -eq 'failed') {
|
|
204
|
+
$entry.exit_code = [int]$Receipt.exit_code; $entry.failed_at = [string]$Receipt.failed_at
|
|
205
|
+
}
|
|
206
|
+
$status.updated_at = [datetimeoffset]::UtcNow.ToString('o')
|
|
207
|
+
Write-CodexChildJsonAtomic -Path ([string]$Receipt.status_path) -Value $status
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
if ($MyInvocation.InvocationName -eq '.') { return }
|
|
211
|
+
$context = Get-CodexChildResumeContext -Path $ReceiptPath
|
|
212
|
+
$outputPath = if ([string]::IsNullOrWhiteSpace($LastMessagePath)) {
|
|
213
|
+
Join-Path (Split-Path ([string]$context.Receipt.receipt_path) -Parent) "$($context.Receipt.launch_id).resume.last-message.txt"
|
|
214
|
+
} else { Get-CodexChildCanonicalPath -Path $LastMessagePath -BasePath ([string]$context.Receipt.worktree_path) }
|
|
215
|
+
if (-not $PSCmdlet.ShouldProcess([string]$context.Receipt.worktree_path, "Resume Codex epic child $($context.Receipt.launch_id)")) { return }
|
|
216
|
+
$receipt = $context.Receipt
|
|
217
|
+
$waveLock = Enter-CodexChildWaveLock -Path ([string]$receipt.wave_lock_path)
|
|
218
|
+
$authOwned = $false
|
|
219
|
+
$process = $null; $processStarted = $false
|
|
220
|
+
try {
|
|
221
|
+
Restore-CodexChildIsolatedAuth -HomePath ([string]$receipt.codex_home_path) `
|
|
222
|
+
-OriginalAuthPath ([string]$context.CodexRuntime.OriginalAuthPath) -Confirm:$false
|
|
223
|
+
$authOwned = $true
|
|
224
|
+
$probeInfo = Get-CodexChildSandboxProbeStartInfo -WorktreePath ([string]$receipt.worktree_path) `
|
|
225
|
+
-CodexHomePath ([string]$receipt.codex_home_path) -CommandPath ([string]$context.CodexRuntime.CommandPath) `
|
|
226
|
+
-DeniedPaths ([string[]]$receipt.codex_denied_paths) `
|
|
227
|
+
-DeniedProbePath ([string]$context.CodexRuntime.OriginalAuthPath)
|
|
228
|
+
Assert-CodexChildSandboxPreflight -StartInfo $probeInfo
|
|
229
|
+
Set-CodexChildReceiptState -Receipt $receipt -State launching
|
|
230
|
+
$process = [System.Diagnostics.Process]::new()
|
|
231
|
+
$process.StartInfo = Get-CodexChildResumeStartInfo -Context $context -ResumePrompt $Prompt -OutputPath $outputPath
|
|
232
|
+
if (-not $process.Start()) { throw 'failed to start Codex resume process.' }
|
|
233
|
+
$processStarted = $true
|
|
234
|
+
Set-CodexChildReceiptState -Receipt $receipt -State active -SessionId ([string]$receipt.codex_session_id)
|
|
235
|
+
$process.WaitForExit()
|
|
236
|
+
if ($process.ExitCode -eq 0) {
|
|
237
|
+
Set-CodexChildReceiptState -Receipt $receipt -State completed -ExitCode 0
|
|
238
|
+
} else {
|
|
239
|
+
Set-CodexChildReceiptState -Receipt $receipt -State failed -ExitCode $process.ExitCode `
|
|
240
|
+
-FailureReason 'Codex resume process returned a nonzero exit code.'
|
|
241
|
+
}
|
|
242
|
+
Set-CodexChildResumeWaveStatus -Receipt $receipt
|
|
243
|
+
$exitCode = $process.ExitCode
|
|
244
|
+
} catch {
|
|
245
|
+
if ($processStarted -and -not $process.HasExited) {
|
|
246
|
+
$process.Kill($true); $process.WaitForExit()
|
|
247
|
+
}
|
|
248
|
+
if ([string]$receipt.state -in @('launching', 'active')) {
|
|
249
|
+
$failureExitCode = if ($processStarted -and $process.HasExited) { [int]$process.ExitCode } else { -1 }
|
|
250
|
+
Set-CodexChildReceiptState -Receipt $receipt -State failed -ExitCode $failureExitCode -FailureReason ([string]$_)
|
|
251
|
+
Set-CodexChildResumeWaveStatus -Receipt $receipt
|
|
252
|
+
}
|
|
253
|
+
throw "EPIC_CHILD_RESUME_BLOCKED: $_"
|
|
254
|
+
} finally {
|
|
255
|
+
try {
|
|
256
|
+
if ($authOwned) {
|
|
257
|
+
Remove-CodexChildIsolatedAuth -HomePath ([string]$receipt.codex_home_path) -Confirm:$false
|
|
258
|
+
}
|
|
259
|
+
} finally {
|
|
260
|
+
$waveLock.Dispose()
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
exit $exitCode
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
name = "csharp-typed-engineer"
|
|
2
2
|
description = "Converted subagent"
|
|
3
|
-
|
|
3
|
+
model = "gpt-5.6-terra"
|
|
4
|
+
model_reasoning_effort = "high"
|
|
4
5
|
developer_instructions = '''
|
|
5
6
|
Canonical migration source: .github/agents/csharp-typed-engineer.agent.md
|
|
6
7
|
Read the canonical source agent file first and follow it exactly.
|
|
@@ -44,12 +44,27 @@
|
|
|
44
44
|
".codex/hooks/hook-command-scanner.ps1",
|
|
45
45
|
".codex/hooks/record-subagent-routing-attestation.ps1",
|
|
46
46
|
".codex/hooks/validate-codex-subagent-routing.ps1",
|
|
47
|
+
".codex/hooks/check-powershell-test-purity.ps1",
|
|
48
|
+
".codex/hooks/check-python-test-purity.ps1",
|
|
49
|
+
".codex/hooks/enforce-checkpoint-monotonic.ps1",
|
|
50
|
+
".codex/hooks/enforce-completion-consistency.ps1",
|
|
51
|
+
".codex/hooks/enforce-completion-helpers.ps1",
|
|
52
|
+
".codex/hooks/enforce-evidence-locations.ps1",
|
|
53
|
+
".codex/hooks/enforce-orchestration-preimplementation-gate.ps1",
|
|
54
|
+
".codex/hooks/enforce-powershell-batch-budget.ps1",
|
|
55
|
+
".codex/hooks/enforce-promotion-mcp-only.ps1",
|
|
56
|
+
".codex/hooks/enforce-python-batch-budget.ps1",
|
|
57
|
+
".codex/hooks/validate-bash.ps1",
|
|
58
|
+
".codex/hooks/validate-feature-review-coverage.ps1",
|
|
47
59
|
".codex/scripts/epic-child-launch-contract.ps1",
|
|
48
60
|
".codex/scripts/epic-child-launch-runtime.ps1",
|
|
49
61
|
".codex/scripts/epic-child-persistence-runtime.ps1",
|
|
50
62
|
".codex/scripts/epic-child-sandbox-preflight.ps1",
|
|
51
63
|
".codex/scripts/launch-epic-child-wave.ps1",
|
|
52
64
|
".codex/scripts/resume-epic-child.ps1",
|
|
65
|
+
".codex/scripts/codex-routing-cli-common.ps1",
|
|
66
|
+
".codex/scripts/Resolve-CodexDeployment.ps1",
|
|
67
|
+
".codex/scripts/Resolve-CodexTopology.ps1",
|
|
53
68
|
".codex/prompts/generate-pr.md",
|
|
54
69
|
".codex/prompts/orchestrate-work.md",
|
|
55
70
|
".codex/agents/feature-reviewer.toml",
|
|
@@ -98,6 +113,8 @@
|
|
|
98
113
|
".codex/agents/commit-steward-c3-elevated.toml",
|
|
99
114
|
".codex/agents/commit-steward-c4.toml",
|
|
100
115
|
"config/orchestration-handoff-registry.json",
|
|
101
|
-
"config/orchestration-handoff.schema.json"
|
|
116
|
+
"config/orchestration-handoff.schema.json",
|
|
117
|
+
".codex/lib/codex-routing/CodexDeployment.psm1",
|
|
118
|
+
".codex/lib/codex-routing/CodexTopology.psm1"
|
|
102
119
|
]
|
|
103
120
|
}
|