@open-agent-toolkit/cli 0.2.51 → 0.2.52
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/assets/bundle-metadata.json +1 -1
- package/assets/docs/cli-utilities/bootstrap.md +6 -2
- package/assets/docs/cli-utilities/tool-packs.md +87 -33
- package/assets/docs/provider-sync/commands.md +30 -2
- package/assets/docs/provider-sync/config.md +19 -0
- package/assets/docs/provider-sync/manifest-and-drift.md +37 -0
- package/assets/docs/provider-sync/providers.md +69 -2
- package/assets/docs/provider-sync/scope-and-surface.md +44 -0
- package/assets/docs/reference/cli-reference.md +1 -0
- package/assets/docs/reference/oat-directory-structure.md +11 -4
- package/assets/docs/workflows/projects/evidence-layers.md +110 -1
- package/assets/docs/workflows/projects/implementation-execution.md +13 -0
- package/assets/docs/workflows/projects/orchestration-model.md +18 -0
- package/assets/public-package-versions.json +4 -4
- package/assets/skills/oat-dispatch-subagents/SKILL.md +57 -1
- package/assets/skills/oat-dispatch-subagents/references/record-schema.md +77 -1
- package/assets/skills/oat-project-autonomous/references/gate-inventory.md +1 -1
- package/assets/skills/oat-project-dispatch-subagents/SKILL.md +24 -1
- package/assets/skills/oat-project-document/references/docs/autonomy-contract.md +1 -1
- package/assets/skills/oat-project-implement/references/dispatch-and-dry-run.md +11 -0
- package/assets/skills/oat-project-implement/references/docs/autonomy-contract.md +1 -1
- package/assets/skills/oat-project-plan-writing/SKILL.md +12 -1
- package/assets/skills/oat-project-pr-final/references/docs/autonomy-contract.md +1 -1
- package/assets/skills/oat-project-quick-start/references/docs/autonomy-contract.md +1 -1
- package/assets/skills/oat-project-review-provide/SKILL.md +12 -1
- package/assets/skills/oat-project-review-provide-remote/SKILL.md +12 -1
- package/dist/agents/canonical/index.d.ts +2 -0
- package/dist/agents/canonical/index.d.ts.map +1 -1
- package/dist/agents/canonical/index.js +1 -0
- package/dist/agents/canonical/resolve.d.ts +45 -0
- package/dist/agents/canonical/resolve.d.ts.map +1 -0
- package/dist/agents/canonical/resolve.js +175 -0
- package/dist/commands/decision/agents-guidance.d.ts +2 -2
- package/dist/commands/decision/agents-guidance.d.ts.map +1 -1
- package/dist/commands/decision/agents-guidance.js +1 -4
- package/dist/commands/decision/index.d.ts.map +1 -1
- package/dist/commands/decision/index.js +25 -6
- package/dist/commands/docs/init/index.d.ts +7 -1
- package/dist/commands/docs/init/index.d.ts.map +1 -1
- package/dist/commands/docs/init/index.js +53 -20
- package/dist/commands/doctor/index.d.ts +8 -2
- package/dist/commands/doctor/index.d.ts.map +1 -1
- package/dist/commands/doctor/index.js +157 -46
- package/dist/commands/init/index.d.ts +8 -3
- package/dist/commands/init/index.d.ts.map +1 -1
- package/dist/commands/init/index.js +64 -26
- package/dist/commands/init/tools/index.d.ts +12 -12
- package/dist/commands/init/tools/index.d.ts.map +1 -1
- package/dist/commands/init/tools/index.js +430 -99
- package/dist/commands/init/tools/install-state.d.ts +2 -0
- package/dist/commands/init/tools/install-state.d.ts.map +1 -1
- package/dist/commands/init/tools/install-state.js +11 -0
- package/dist/commands/init/tools/project-guidance.d.ts +49 -0
- package/dist/commands/init/tools/project-guidance.d.ts.map +1 -0
- package/dist/commands/init/tools/project-guidance.js +144 -0
- package/dist/commands/init/tools/workflows/index.d.ts +7 -0
- package/dist/commands/init/tools/workflows/index.d.ts.map +1 -1
- package/dist/commands/init/tools/workflows/index.js +160 -7
- package/dist/commands/pjm/index.d.ts.map +1 -1
- package/dist/commands/pjm/index.js +28 -3
- package/dist/commands/pjm/init.d.ts +5 -0
- package/dist/commands/pjm/init.d.ts.map +1 -1
- package/dist/commands/pjm/init.js +19 -3
- package/dist/commands/project/dispatch/index.d.ts +10 -0
- package/dist/commands/project/dispatch/index.d.ts.map +1 -0
- package/dist/commands/project/dispatch/index.js +137 -0
- package/dist/commands/project/dispatch/record.d.ts +91 -0
- package/dist/commands/project/dispatch/record.d.ts.map +1 -0
- package/dist/commands/project/dispatch/record.js +402 -0
- package/dist/commands/project/index.d.ts.map +1 -1
- package/dist/commands/project/index.js +2 -0
- package/dist/commands/providers/inspect/inspect.d.ts +8 -1
- package/dist/commands/providers/inspect/inspect.d.ts.map +1 -1
- package/dist/commands/providers/inspect/inspect.js +146 -26
- package/dist/commands/providers/list/list.d.ts +8 -1
- package/dist/commands/providers/list/list.d.ts.map +1 -1
- package/dist/commands/providers/list/list.js +139 -23
- package/dist/commands/providers/providers.types.d.ts +25 -1
- package/dist/commands/providers/providers.types.d.ts.map +1 -1
- package/dist/commands/providers/set/index.d.ts +9 -1
- package/dist/commands/providers/set/index.d.ts.map +1 -1
- package/dist/commands/providers/set/index.js +43 -33
- package/dist/commands/remove/skill/remove-skill.d.ts.map +1 -1
- package/dist/commands/remove/skill/remove-skill.js +11 -0
- package/dist/commands/shared/agents-md.d.ts +42 -14
- package/dist/commands/shared/agents-md.d.ts.map +1 -1
- package/dist/commands/shared/agents-md.js +364 -70
- package/dist/commands/status/index.d.ts +8 -3
- package/dist/commands/status/index.d.ts.map +1 -1
- package/dist/commands/status/index.js +147 -32
- package/dist/commands/sync/apply.d.ts.map +1 -1
- package/dist/commands/sync/apply.js +220 -29
- package/dist/commands/sync/dry-run.d.ts.map +1 -1
- package/dist/commands/sync/dry-run.js +30 -6
- package/dist/commands/sync/index.d.ts +17 -2
- package/dist/commands/sync/index.d.ts.map +1 -1
- package/dist/commands/sync/index.js +106 -41
- package/dist/commands/sync/sync.types.d.ts +39 -2
- package/dist/commands/sync/sync.types.d.ts.map +1 -1
- package/dist/commands/sync/sync.utils.d.ts +5 -1
- package/dist/commands/sync/sync.utils.d.ts.map +1 -1
- package/dist/commands/sync/sync.utils.js +73 -0
- package/dist/commands/tools/info/info-tool.d.ts +2 -0
- package/dist/commands/tools/info/info-tool.d.ts.map +1 -1
- package/dist/commands/tools/info/info-tool.js +34 -12
- package/dist/commands/tools/install/index.d.ts.map +1 -1
- package/dist/commands/tools/install/index.js +19 -3
- package/dist/commands/tools/list/list-tools.d.ts +2 -0
- package/dist/commands/tools/list/list-tools.d.ts.map +1 -1
- package/dist/commands/tools/list/list-tools.js +36 -7
- package/dist/commands/tools/remove/index.d.ts.map +1 -1
- package/dist/commands/tools/remove/index.js +150 -21
- package/dist/commands/tools/remove/remove-tools.d.ts +20 -0
- package/dist/commands/tools/remove/remove-tools.d.ts.map +1 -1
- package/dist/commands/tools/remove/remove-tools.js +122 -1
- package/dist/commands/tools/shared/format-pack-inventory.d.ts +19 -0
- package/dist/commands/tools/shared/format-pack-inventory.d.ts.map +1 -1
- package/dist/commands/tools/shared/format-pack-inventory.js +109 -0
- package/dist/commands/tools/shared/pack-evidence.d.ts +66 -0
- package/dist/commands/tools/shared/pack-evidence.d.ts.map +1 -0
- package/dist/commands/tools/shared/pack-evidence.js +132 -0
- package/dist/commands/tools/shared/pack-inventory.d.ts +1 -0
- package/dist/commands/tools/shared/pack-inventory.d.ts.map +1 -1
- package/dist/commands/tools/shared/pack-lifecycle-outcome.d.ts +44 -0
- package/dist/commands/tools/shared/pack-lifecycle-outcome.d.ts.map +1 -0
- package/dist/commands/tools/shared/pack-lifecycle-outcome.js +91 -0
- package/dist/commands/tools/update/index.d.ts.map +1 -1
- package/dist/commands/tools/update/index.js +74 -4
- package/dist/commands/tools/update/update-tools.d.ts +5 -0
- package/dist/commands/tools/update/update-tools.d.ts.map +1 -1
- package/dist/commands/tools/update/update-tools.js +64 -2
- package/dist/config/oat-config.d.ts +5 -0
- package/dist/config/oat-config.d.ts.map +1 -1
- package/dist/config/oat-config.js +17 -5
- package/dist/config/user-sync-config.d.ts +1 -0
- package/dist/config/user-sync-config.d.ts.map +1 -1
- package/dist/config/user-sync-config.js +8 -0
- package/dist/drift/detector.d.ts +2 -2
- package/dist/drift/detector.d.ts.map +1 -1
- package/dist/drift/detector.js +18 -0
- package/dist/drift/strays.d.ts.map +1 -1
- package/dist/drift/strays.js +5 -0
- package/dist/engine/collection-sync.d.ts +14 -0
- package/dist/engine/collection-sync.d.ts.map +1 -0
- package/dist/engine/collection-sync.js +282 -0
- package/dist/engine/compute-plan.d.ts +8 -1
- package/dist/engine/compute-plan.d.ts.map +1 -1
- package/dist/engine/compute-plan.js +303 -12
- package/dist/engine/engine.types.d.ts +61 -0
- package/dist/engine/engine.types.d.ts.map +1 -1
- package/dist/engine/execute-plan.d.ts +22 -2
- package/dist/engine/execute-plan.d.ts.map +1 -1
- package/dist/engine/execute-plan.js +459 -27
- package/dist/engine/provider-path-safety.d.ts +1 -0
- package/dist/engine/provider-path-safety.d.ts.map +1 -1
- package/dist/engine/provider-path-safety.js +15 -0
- package/dist/engine/scanner.d.ts +2 -2
- package/dist/engine/scanner.d.ts.map +1 -1
- package/dist/engine/scanner.js +21 -4
- package/dist/fs/io.d.ts +67 -0
- package/dist/fs/io.d.ts.map +1 -1
- package/dist/fs/io.js +536 -3
- package/dist/manifest/index.d.ts +2 -2
- package/dist/manifest/index.d.ts.map +1 -1
- package/dist/manifest/index.js +1 -1
- package/dist/manifest/manager.d.ts +2 -2
- package/dist/manifest/manager.d.ts.map +1 -1
- package/dist/manifest/manager.js +20 -3
- package/dist/manifest/manifest.types.d.ts +704 -13
- package/dist/manifest/manifest.types.d.ts.map +1 -1
- package/dist/manifest/manifest.types.js +131 -15
- package/dist/providers/codex/codec/sync-extension.d.ts.map +1 -1
- package/dist/providers/codex/codec/sync-extension.js +41 -10
- package/dist/providers/cursor/codec/sync-extension.d.ts.map +1 -1
- package/dist/providers/cursor/codec/sync-extension.js +41 -10
- package/dist/providers/identity/absolute-paths.d.ts +68 -0
- package/dist/providers/identity/absolute-paths.d.ts.map +1 -0
- package/dist/providers/identity/absolute-paths.js +144 -0
- package/dist/providers/identity/claude-runtime-observation.d.ts +105 -0
- package/dist/providers/identity/claude-runtime-observation.d.ts.map +1 -0
- package/dist/providers/identity/claude-runtime-observation.fixtures.d.ts +40 -0
- package/dist/providers/identity/claude-runtime-observation.fixtures.d.ts.map +1 -0
- package/dist/providers/identity/claude-runtime-observation.fixtures.js +146 -0
- package/dist/providers/identity/claude-runtime-observation.js +229 -0
- package/dist/providers/identity/codex-runtime-observation.d.ts +75 -0
- package/dist/providers/identity/codex-runtime-observation.d.ts.map +1 -0
- package/dist/providers/identity/codex-runtime-observation.fixtures.d.ts +37 -0
- package/dist/providers/identity/codex-runtime-observation.fixtures.d.ts.map +1 -0
- package/dist/providers/identity/codex-runtime-observation.fixtures.js +539 -0
- package/dist/providers/identity/codex-runtime-observation.js +255 -0
- package/dist/providers/identity/generic-dispatch-record.d.ts +313 -0
- package/dist/providers/identity/generic-dispatch-record.d.ts.map +1 -0
- package/dist/providers/identity/generic-dispatch-record.js +539 -0
- package/dist/providers/identity/oat-dispatch-record.d.ts +1964 -0
- package/dist/providers/identity/oat-dispatch-record.d.ts.map +1 -0
- package/dist/providers/identity/oat-dispatch-record.js +691 -0
- package/dist/providers/identity/runtime-observation.d.ts +100 -0
- package/dist/providers/identity/runtime-observation.d.ts.map +1 -0
- package/dist/providers/identity/runtime-observation.js +169 -0
- package/dist/providers/shared/index.d.ts +2 -0
- package/dist/providers/shared/index.d.ts.map +1 -1
- package/dist/providers/shared/index.js +1 -0
- package/dist/providers/shared/materialization-extension.d.ts +12 -0
- package/dist/providers/shared/materialization-extension.d.ts.map +1 -1
- package/dist/providers/shared/registry.d.ts +69 -0
- package/dist/providers/shared/registry.d.ts.map +1 -0
- package/dist/providers/shared/registry.js +252 -0
- package/dist/providers/shared/restart-adviser.d.ts +26 -0
- package/dist/providers/shared/restart-adviser.d.ts.map +1 -0
- package/dist/providers/shared/restart-adviser.js +96 -0
- package/dist/shared/types.d.ts +4 -0
- package/dist/shared/types.d.ts.map +1 -1
- package/package.json +2 -2
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"manifest.types.d.ts","sourceRoot":"","sources":["../../src/manifest/manifest.types.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;
|
|
1
|
+
{"version":3,"file":"manifest.types.d.ts","sourceRoot":"","sources":["../../src/manifest/manifest.types.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAoDxB,eAAO,MAAM,qBAAqB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAED,CAAC;AAElC,eAAO,MAAM,mBAAmB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAmC9B,CAAC;AAEH,QAAA,MAAM,oCAAoC;;;;;;;;;;;;EAIxC,CAAC;AAEH,eAAO,MAAM,6BAA6B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAUxC,CAAC;AAiCH,eAAO,MAAM,gBAAgB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAOkD,CAAC;AAEhF,eAAO,MAAM,gBAAgB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAgEzB,CAAC;AAEL,eAAO,MAAM,cAAc;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;IAAgD,CAAC;AAE5E,MAAM,MAAM,eAAe,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,qBAAqB,CAAC,CAAC;AACpE,MAAM,MAAM,eAAe,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,mBAAmB,CAAC,CAAC;AAClE,8EAA8E;AAC9E,MAAM,MAAM,aAAa,GAAG,eAAe,CAAC;AAC5C,MAAM,MAAM,uBAAuB,GAAG,CAAC,CAAC,KAAK,CAC3C,OAAO,6BAA6B,CACrC,CAAC;AACF,MAAM,MAAM,8BAA8B,GAAG,CAAC,CAAC,KAAK,CAClD,OAAO,oCAAoC,CAC5C,CAAC;AACF,MAAM,MAAM,UAAU,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,gBAAgB,CAAC,CAAC;AAC1D,MAAM,MAAM,UAAU,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,gBAAgB,CAAC,CAAC;AAC1D,MAAM,MAAM,QAAQ,GAAG,IAAI,CAAC,UAAU,EAAE,SAAS,CAAC,GAAG;IACnD,OAAO,EAAE,aAAa,EAAE,CAAC;CAC1B,CAAC"}
|
|
@@ -4,18 +4,20 @@ const RelativePathSchema = z
|
|
|
4
4
|
.string()
|
|
5
5
|
.min(1)
|
|
6
6
|
.refine((value) => !value.startsWith('/') && !value.startsWith('~'), 'Path must be relative');
|
|
7
|
-
|
|
8
|
-
|
|
7
|
+
const CollectionRelativePathSchema = RelativePathSchema.refine((value) => !value.includes('\\') &&
|
|
8
|
+
value
|
|
9
|
+
.split('/')
|
|
10
|
+
.every((segment) => segment !== '' && segment !== '.' && segment !== '..'), 'Collection path must be a normalized relative POSIX path');
|
|
11
|
+
const ManifestEntryBaseSchema = z.object({
|
|
9
12
|
canonicalPath: RelativePathSchema,
|
|
10
13
|
providerPath: RelativePathSchema,
|
|
11
14
|
provider: z.string().min(1),
|
|
12
15
|
contentType: ContentTypeSchema,
|
|
13
|
-
strategy: z.enum(['symlink', 'copy']),
|
|
14
16
|
contentHash: z.string().nullable(),
|
|
15
17
|
isFile: z.boolean().default(false),
|
|
16
18
|
lastSynced: z.string().datetime(),
|
|
17
|
-
})
|
|
18
|
-
|
|
19
|
+
});
|
|
20
|
+
function validateEntryHash(entry, ctx) {
|
|
19
21
|
if (entry.strategy === 'copy' && entry.contentHash === null) {
|
|
20
22
|
ctx.addIssue({
|
|
21
23
|
code: z.ZodIssueCode.custom,
|
|
@@ -23,34 +25,148 @@ export const ManifestEntrySchema = z
|
|
|
23
25
|
path: ['contentHash'],
|
|
24
26
|
});
|
|
25
27
|
}
|
|
26
|
-
if (entry.strategy
|
|
28
|
+
if (entry.strategy !== 'copy' && entry.contentHash !== null) {
|
|
27
29
|
ctx.addIssue({
|
|
28
30
|
code: z.ZodIssueCode.custom,
|
|
29
|
-
message:
|
|
31
|
+
message: `${entry.strategy} strategy requires null contentHash`,
|
|
30
32
|
path: ['contentHash'],
|
|
31
33
|
});
|
|
32
34
|
}
|
|
35
|
+
}
|
|
36
|
+
export const ManifestEntryV1Schema = ManifestEntryBaseSchema.extend({
|
|
37
|
+
strategy: z.enum(['symlink', 'copy']),
|
|
38
|
+
}).superRefine(validateEntryHash);
|
|
39
|
+
export const ManifestEntrySchema = ManifestEntryBaseSchema.extend({
|
|
40
|
+
strategy: z.enum(['symlink', 'copy', 'collection']),
|
|
41
|
+
collectionId: z.string().min(1).optional(),
|
|
42
|
+
}).superRefine((entry, ctx) => {
|
|
43
|
+
validateEntryHash(entry, ctx);
|
|
44
|
+
if (entry.strategy === 'collection' && entry.collectionId === undefined) {
|
|
45
|
+
ctx.addIssue({
|
|
46
|
+
code: z.ZodIssueCode.custom,
|
|
47
|
+
message: 'collection strategy requires collectionId',
|
|
48
|
+
path: ['collectionId'],
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
if (entry.strategy !== 'collection' && entry.collectionId !== undefined) {
|
|
52
|
+
ctx.addIssue({
|
|
53
|
+
code: z.ZodIssueCode.custom,
|
|
54
|
+
message: 'collectionId is only valid for collection strategy',
|
|
55
|
+
path: ['collectionId'],
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
if (entry.strategy === 'collection') {
|
|
59
|
+
for (const field of ['canonicalPath', 'providerPath']) {
|
|
60
|
+
const parsed = CollectionRelativePathSchema.safeParse(entry[field]);
|
|
61
|
+
if (!parsed.success) {
|
|
62
|
+
ctx.addIssue({
|
|
63
|
+
code: z.ZodIssueCode.custom,
|
|
64
|
+
message: 'Inherited collection paths must be normalized relative POSIX paths',
|
|
65
|
+
path: [field],
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
}
|
|
33
70
|
});
|
|
34
|
-
|
|
71
|
+
const ManifestCollectionLinkIdentitySchema = z.object({
|
|
72
|
+
device: z.string().min(1),
|
|
73
|
+
inode: z.string().min(1),
|
|
74
|
+
linkText: z.string().min(1),
|
|
75
|
+
});
|
|
76
|
+
export const ManifestCollectionEntrySchema = z.object({
|
|
77
|
+
id: z.string().min(1),
|
|
78
|
+
provider: z.string().min(1),
|
|
79
|
+
contentType: ContentTypeSchema,
|
|
80
|
+
canonicalDir: CollectionRelativePathSchema,
|
|
81
|
+
providerDir: CollectionRelativePathSchema,
|
|
82
|
+
linkTarget: CollectionRelativePathSchema,
|
|
83
|
+
ownership: z.enum(['oat-created', 'adopted-exact']),
|
|
84
|
+
createdLink: ManifestCollectionLinkIdentitySchema.optional(),
|
|
85
|
+
lastVerified: z.string().datetime(),
|
|
86
|
+
});
|
|
87
|
+
function isStrictPathDescendant(parent, candidate) {
|
|
88
|
+
const parentSegments = parent.split('/');
|
|
89
|
+
const candidateSegments = candidate.split('/');
|
|
90
|
+
return (candidateSegments.length > parentSegments.length &&
|
|
91
|
+
parentSegments.every((segment, index) => candidateSegments[index] === segment));
|
|
92
|
+
}
|
|
93
|
+
function validateUniqueEntries(entries, ctx) {
|
|
94
|
+
const seen = new Set();
|
|
95
|
+
for (const [index, entry] of entries.entries()) {
|
|
96
|
+
const key = `${entry.canonicalPath}::${entry.provider}`;
|
|
97
|
+
if (seen.has(key)) {
|
|
98
|
+
ctx.addIssue({
|
|
99
|
+
code: z.ZodIssueCode.custom,
|
|
100
|
+
message: 'duplicate (canonicalPath, provider) pair',
|
|
101
|
+
path: ['entries', index],
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
else {
|
|
105
|
+
seen.add(key);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
export const ManifestV1Schema = z
|
|
35
110
|
.object({
|
|
36
111
|
version: z.literal(1),
|
|
37
112
|
oatVersion: z.string().min(1),
|
|
113
|
+
entries: z.array(ManifestEntryV1Schema),
|
|
114
|
+
lastUpdated: z.string().datetime(),
|
|
115
|
+
})
|
|
116
|
+
.superRefine((manifest, ctx) => validateUniqueEntries(manifest.entries, ctx));
|
|
117
|
+
export const ManifestV2Schema = z
|
|
118
|
+
.object({
|
|
119
|
+
version: z.literal(2),
|
|
120
|
+
oatVersion: z.string().min(1),
|
|
38
121
|
entries: z.array(ManifestEntrySchema),
|
|
122
|
+
collections: z.array(ManifestCollectionEntrySchema),
|
|
39
123
|
lastUpdated: z.string().datetime(),
|
|
40
124
|
})
|
|
41
125
|
.superRefine((manifest, ctx) => {
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
if (
|
|
126
|
+
validateUniqueEntries(manifest.entries, ctx);
|
|
127
|
+
const collections = new Map();
|
|
128
|
+
for (const [index, collection] of manifest.collections.entries()) {
|
|
129
|
+
if (collections.has(collection.id)) {
|
|
46
130
|
ctx.addIssue({
|
|
47
131
|
code: z.ZodIssueCode.custom,
|
|
48
|
-
message: 'duplicate
|
|
49
|
-
path: ['
|
|
132
|
+
message: 'duplicate collection id',
|
|
133
|
+
path: ['collections', index, 'id'],
|
|
50
134
|
});
|
|
51
135
|
}
|
|
52
136
|
else {
|
|
53
|
-
|
|
137
|
+
collections.set(collection.id, collection);
|
|
138
|
+
}
|
|
139
|
+
if (collection.linkTarget !== collection.canonicalDir) {
|
|
140
|
+
ctx.addIssue({
|
|
141
|
+
code: z.ZodIssueCode.custom,
|
|
142
|
+
message: 'collection linkTarget must exactly reference canonicalDir',
|
|
143
|
+
path: ['collections', index, 'linkTarget'],
|
|
144
|
+
});
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
for (const [index, entry] of manifest.entries.entries()) {
|
|
148
|
+
if (entry.strategy !== 'collection') {
|
|
149
|
+
continue;
|
|
150
|
+
}
|
|
151
|
+
const collection = collections.get(entry.collectionId ?? '');
|
|
152
|
+
if (collection === undefined) {
|
|
153
|
+
ctx.addIssue({
|
|
154
|
+
code: z.ZodIssueCode.custom,
|
|
155
|
+
message: 'collection entry must reference exactly one collection',
|
|
156
|
+
path: ['entries', index, 'collectionId'],
|
|
157
|
+
});
|
|
158
|
+
continue;
|
|
159
|
+
}
|
|
160
|
+
if (entry.provider !== collection.provider ||
|
|
161
|
+
entry.contentType !== collection.contentType ||
|
|
162
|
+
!isStrictPathDescendant(collection.canonicalDir, entry.canonicalPath) ||
|
|
163
|
+
!isStrictPathDescendant(collection.providerDir, entry.providerPath)) {
|
|
164
|
+
ctx.addIssue({
|
|
165
|
+
code: z.ZodIssueCode.custom,
|
|
166
|
+
message: 'collection entry provider, content type, and ancestry must match its collection',
|
|
167
|
+
path: ['entries', index],
|
|
168
|
+
});
|
|
54
169
|
}
|
|
55
170
|
}
|
|
56
171
|
});
|
|
172
|
+
export const ManifestSchema = z.union([ManifestV1Schema, ManifestV2Schema]);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sync-extension.d.ts","sourceRoot":"","sources":["../../../../src/providers/codex/codec/sync-extension.ts"],"names":[],"mappings":"AAkBA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAKpD,OAAO,EAGL,KAAK,qBAAqB,EAC1B,KAAK,0BAA0B,EAC/B,KAAK,sBAAsB,EAC3B,KAAK,wBAAwB,EAC7B,KAAK,mBAAmB,EACxB,KAAK,6BAA6B,EACnC,MAAM,mBAAmB,CAAC;
|
|
1
|
+
{"version":3,"file":"sync-extension.d.ts","sourceRoot":"","sources":["../../../../src/providers/codex/codec/sync-extension.ts"],"names":[],"mappings":"AAkBA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAKpD,OAAO,EAGL,KAAK,qBAAqB,EAC1B,KAAK,0BAA0B,EAC/B,KAAK,sBAAsB,EAC3B,KAAK,wBAAwB,EAC7B,KAAK,mBAAmB,EACxB,KAAK,6BAA6B,EACnC,MAAM,mBAAmB,CAAC;AAsB3B,MAAM,MAAM,oBAAoB,GAAG,qBAAqB,CAAC;AACzD,MAAM,MAAM,oBAAoB,GAAG,MAAM,GAAG,QAAQ,CAAC;AAErD,MAAM,WAAW,uBAAuB;IACtC,MAAM,EAAE,oBAAoB,CAAC;IAC7B,MAAM,EAAE,oBAAoB,CAAC;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,4BAA6B,SAAQ,6BAA6B,CACjF,OAAO,EACP,oBAAoB,CACrB;IACC,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,kBAAmB,SAAQ,mBAAmB,CAC7D,OAAO,EACP,oBAAoB,EACpB,0BAA0B,CAC3B;IACC,UAAU,EAAE,4BAA4B,EAAE,CAAC;IAC3C,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB,mBAAmB,EAAE,MAAM,CAAC;CAC7B;AAED,MAAM,WAAW,0BAA0B;IACzC,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB,mBAAmB,EAAE,MAAM,CAAC;CAC7B;AAED,MAAM,MAAM,yBAAyB,GAAG,0BAA0B,CAAC;AAgBnE,MAAM,WAAW,iCAAiC;IAChD,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,GAAG,CAAC,EAAE,MAAM,CAAC,UAAU,CAAC;CACzB;AAqiBD,wBAAsB,gCAAgC,CACpD,SAAS,EAAE,MAAM,EACjB,gBAAgB,EAAE,cAAc,EAAE,EAClC,qBAAqB,CAAC,EAAE,MAAM,EAAE,EAChC,OAAO,GAAE,iCAAsC,GAC9C,OAAO,CAAC,kBAAkB,CAAC,CAkL7B;AAED,wBAAsB,8BAA8B,CAClD,SAAS,EAAE,MAAM,EACjB,IAAI,EAAE,kBAAkB,GACvB,OAAO,CAAC,yBAAyB,CAAC,CA+DpC;AAED,wBAAgB,wBAAwB,CAAC,IAAI,EAAE,kBAAkB,GAAG,OAAO,CAE1E;AAED,wBAAgB,uBAAuB,CAAC,IAAI,EAAE,kBAAkB,GAAG;IACjE,iBAAiB,EAAE,MAAM,CAAC;IAC1B,OAAO,EAAE,MAAM,CAAC;CACjB,CAEA;AAED,wBAAgB,0BAA0B,CACxC,IAAI,EAAE,kBAAkB,GACvB,uBAAuB,EAAE,CAQ3B;AAED,eAAO,MAAM,6BAA6B,EAAE,wBAAwB,CAClE,kBAAkB,EAClB,sBAAsB,CAAC,iCAAiC,CAAC,CAY1D,CAAC"}
|
|
@@ -519,14 +519,17 @@ export async function computeCodexProjectExtensionPlan(scopeRoot, canonicalEntri
|
|
|
519
519
|
};
|
|
520
520
|
}
|
|
521
521
|
export async function applyCodexProjectExtensionPlan(scopeRoot, plan) {
|
|
522
|
-
const
|
|
523
|
-
applied: 0,
|
|
524
|
-
failed: 0,
|
|
525
|
-
skipped: 0,
|
|
526
|
-
};
|
|
522
|
+
const operationResults = [];
|
|
527
523
|
for (const operation of plan.operations) {
|
|
528
524
|
if (operation.action === 'skip') {
|
|
529
|
-
|
|
525
|
+
operationResults.push({
|
|
526
|
+
provider: operation.provider,
|
|
527
|
+
target: operation.target,
|
|
528
|
+
path: operation.path,
|
|
529
|
+
entryName: operation.entryName,
|
|
530
|
+
action: operation.action,
|
|
531
|
+
status: 'current',
|
|
532
|
+
});
|
|
530
533
|
continue;
|
|
531
534
|
}
|
|
532
535
|
const absolutePath = resolve(scopeRoot, operation.path);
|
|
@@ -538,13 +541,41 @@ export async function applyCodexProjectExtensionPlan(scopeRoot, plan) {
|
|
|
538
541
|
await ensureDir(dirname(absolutePath));
|
|
539
542
|
await writeFile(absolutePath, operation.content ?? '', 'utf8');
|
|
540
543
|
}
|
|
541
|
-
|
|
544
|
+
operationResults.push({
|
|
545
|
+
provider: operation.provider,
|
|
546
|
+
target: operation.target,
|
|
547
|
+
path: operation.path,
|
|
548
|
+
entryName: operation.entryName,
|
|
549
|
+
action: operation.action,
|
|
550
|
+
status: 'changed',
|
|
551
|
+
});
|
|
542
552
|
}
|
|
543
|
-
catch {
|
|
544
|
-
|
|
553
|
+
catch (error) {
|
|
554
|
+
const missing = typeof error === 'object' &&
|
|
555
|
+
error !== null &&
|
|
556
|
+
'code' in error &&
|
|
557
|
+
error.code === 'ENOENT';
|
|
558
|
+
operationResults.push({
|
|
559
|
+
provider: operation.provider,
|
|
560
|
+
target: operation.target,
|
|
561
|
+
path: operation.path,
|
|
562
|
+
entryName: operation.entryName,
|
|
563
|
+
action: operation.action,
|
|
564
|
+
status: missing ? 'missing' : 'failed',
|
|
565
|
+
failure: missing
|
|
566
|
+
? 'Materialization input was missing; restore it and retry sync.'
|
|
567
|
+
: 'Materialization failed; inspect local verbose diagnostics and retry sync.',
|
|
568
|
+
});
|
|
545
569
|
}
|
|
546
570
|
}
|
|
547
|
-
return
|
|
571
|
+
return {
|
|
572
|
+
applied: operationResults.filter(({ status }) => status === 'changed')
|
|
573
|
+
.length,
|
|
574
|
+
failed: operationResults.filter(({ status }) => status === 'failed' || status === 'missing').length,
|
|
575
|
+
skipped: operationResults.filter(({ status }) => status === 'current')
|
|
576
|
+
.length,
|
|
577
|
+
operations: operationResults,
|
|
578
|
+
};
|
|
548
579
|
}
|
|
549
580
|
export function hasCodexExtensionChanges(plan) {
|
|
550
581
|
return hasMaterializationChanges(plan);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sync-extension.d.ts","sourceRoot":"","sources":["../../../../src/providers/cursor/codec/sync-extension.ts"],"names":[],"mappings":"AAqBA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAIpD,OAAO,EAIL,KAAK,0BAA0B,EAC/B,KAAK,sBAAsB,EAC3B,KAAK,wBAAwB,EAC7B,KAAK,wBAAwB,EAC7B,KAAK,mBAAmB,EACxB,KAAK,6BAA6B,EACnC,MAAM,mBAAmB,CAAC;
|
|
1
|
+
{"version":3,"file":"sync-extension.d.ts","sourceRoot":"","sources":["../../../../src/providers/cursor/codec/sync-extension.ts"],"names":[],"mappings":"AAqBA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAIpD,OAAO,EAIL,KAAK,0BAA0B,EAC/B,KAAK,sBAAsB,EAC3B,KAAK,wBAAwB,EAC7B,KAAK,wBAAwB,EAC7B,KAAK,mBAAmB,EACxB,KAAK,6BAA6B,EACnC,MAAM,mBAAmB,CAAC;AAI3B,OAAO,EAIL,KAAK,qBAAqB,EAC3B,MAAM,WAAW,CAAC;AAMnB,OAAO,EAIL,KAAK,eAAe,EACrB,MAAM,UAAU,CAAC;AAElB,MAAM,MAAM,qBAAqB,GAAG,MAAM,CAAC;AAE3C,MAAM,WAAW,wBAAyB,SAAQ,wBAAwB,CACxE,QAAQ,EACR,qBAAqB,CACtB;IACC,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,6BAA8B,SAAQ,6BAA6B,CAClF,QAAQ,EACR,qBAAqB,CACtB;IACC,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,2BAA2B;IAC1C,aAAa,EAAE,eAAe,EAAE,CAAC;IACjC,aAAa,EAAE,OAAO,CAAC;CACxB;AAED,MAAM,WAAW,mBAAoB,SAAQ,mBAAmB,CAC9D,QAAQ,EACR,qBAAqB,EACrB,2BAA2B,CAC5B;IACC,UAAU,EAAE,6BAA6B,EAAE,CAAC;CAC7C;AAED,MAAM,MAAM,0BAA0B,GAAG,0BAA0B,CAAC;AAQpE,MAAM,WAAW,kCAAkC;IACjD,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,GAAG,CAAC,EAAE,MAAM,CAAC,UAAU,CAAC;IACxB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,aAAa,CAAC,EAAE,SAAS,qBAAqB,EAAE,CAAC;IACjD,gBAAgB,CAAC,EAAE,SAAS,qBAAqB,EAAE,CAAC;CACrD;AAoWD,wBAAsB,iCAAiC,CACrD,SAAS,EAAE,MAAM,EACjB,gBAAgB,EAAE,cAAc,EAAE,EAClC,qBAAqB,CAAC,EAAE,MAAM,EAAE,EAChC,OAAO,GAAE,kCAAuC,GAC/C,OAAO,CAAC,mBAAmB,CAAC,CAmH9B;AA6DD,wBAAsB,+BAA+B,CACnD,SAAS,EAAE,MAAM,EACjB,IAAI,EAAE,mBAAmB,GACxB,OAAO,CAAC,0BAA0B,CAAC,CA4DrC;AAED,wBAAgB,yBAAyB,CAAC,IAAI,EAAE,mBAAmB,GAAG,OAAO,CAE5E;AAED,wBAAgB,wBAAwB,CAAC,IAAI,EAAE,mBAAmB,GAAG;IACnE,iBAAiB,EAAE,MAAM,CAAC;IAC1B,OAAO,EAAE,MAAM,CAAC;CACjB,CAEA;AAED,wBAAgB,2BAA2B,CACzC,IAAI,EAAE,mBAAmB,GACxB,wBAAwB,EAAE,CAM5B;AAED,eAAO,MAAM,8BAA8B,EAAE,wBAAwB,CACnE,mBAAmB,EACnB,sBAAsB,CAAC,kCAAkC,CAAC,CAY3D,CAAC"}
|
|
@@ -343,14 +343,17 @@ async function assertSafeCursorMutationPath(scopeRoot, absolutePath) {
|
|
|
343
343
|
}
|
|
344
344
|
}
|
|
345
345
|
export async function applyCursorProjectExtensionPlan(scopeRoot, plan) {
|
|
346
|
-
const
|
|
347
|
-
applied: 0,
|
|
348
|
-
failed: 0,
|
|
349
|
-
skipped: 0,
|
|
350
|
-
};
|
|
346
|
+
const operationResults = [];
|
|
351
347
|
for (const operation of plan.operations) {
|
|
352
348
|
if (operation.action === 'skip') {
|
|
353
|
-
|
|
349
|
+
operationResults.push({
|
|
350
|
+
provider: operation.provider,
|
|
351
|
+
target: operation.target,
|
|
352
|
+
path: operation.path,
|
|
353
|
+
entryName: operation.entryName,
|
|
354
|
+
action: operation.action,
|
|
355
|
+
status: 'current',
|
|
356
|
+
});
|
|
354
357
|
continue;
|
|
355
358
|
}
|
|
356
359
|
const absolutePath = resolve(scopeRoot, operation.path);
|
|
@@ -363,13 +366,41 @@ export async function applyCursorProjectExtensionPlan(scopeRoot, plan) {
|
|
|
363
366
|
await ensureDir(dirname(absolutePath));
|
|
364
367
|
await writeFile(absolutePath, operation.content ?? '', 'utf8');
|
|
365
368
|
}
|
|
366
|
-
|
|
369
|
+
operationResults.push({
|
|
370
|
+
provider: operation.provider,
|
|
371
|
+
target: operation.target,
|
|
372
|
+
path: operation.path,
|
|
373
|
+
entryName: operation.entryName,
|
|
374
|
+
action: operation.action,
|
|
375
|
+
status: 'changed',
|
|
376
|
+
});
|
|
367
377
|
}
|
|
368
|
-
catch {
|
|
369
|
-
|
|
378
|
+
catch (error) {
|
|
379
|
+
const missing = typeof error === 'object' &&
|
|
380
|
+
error !== null &&
|
|
381
|
+
'code' in error &&
|
|
382
|
+
error.code === 'ENOENT';
|
|
383
|
+
operationResults.push({
|
|
384
|
+
provider: operation.provider,
|
|
385
|
+
target: operation.target,
|
|
386
|
+
path: operation.path,
|
|
387
|
+
entryName: operation.entryName,
|
|
388
|
+
action: operation.action,
|
|
389
|
+
status: missing ? 'missing' : 'failed',
|
|
390
|
+
failure: missing
|
|
391
|
+
? 'Materialization input was missing; restore it and retry sync.'
|
|
392
|
+
: 'Materialization failed; inspect local verbose diagnostics and retry sync.',
|
|
393
|
+
});
|
|
370
394
|
}
|
|
371
395
|
}
|
|
372
|
-
return
|
|
396
|
+
return {
|
|
397
|
+
applied: operationResults.filter(({ status }) => status === 'changed')
|
|
398
|
+
.length,
|
|
399
|
+
failed: operationResults.filter(({ status }) => status === 'failed' || status === 'missing').length,
|
|
400
|
+
skipped: operationResults.filter(({ status }) => status === 'current')
|
|
401
|
+
.length,
|
|
402
|
+
operations: operationResults,
|
|
403
|
+
};
|
|
373
404
|
}
|
|
374
405
|
export function hasCursorExtensionChanges(plan) {
|
|
375
406
|
return hasMaterializationChanges(plan);
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Absolute-path detection for dispatch records and messages.
|
|
3
|
+
*
|
|
4
|
+
* Two patterns, deliberately different. `IDENTITY_PATH_PATTERN` is strict and
|
|
5
|
+
* used where a path is never a legitimate value, so ambiguity resolves to
|
|
6
|
+
* rejection. `PROSE_PATH_PATTERN` is conservative and used where a false
|
|
7
|
+
* positive would corrupt legitimate evidence, so it under-matches by design:
|
|
8
|
+
* colon-prefixed forms, path-shaped routes inside URLs, and trailing-slash
|
|
9
|
+
* candidates survive. Prose redaction is therefore best-effort, not a
|
|
10
|
+
* guarantee — see `spec.md` NFR1, amended 2026-09-03.
|
|
11
|
+
*
|
|
12
|
+
* Before this module there were two independent notions of
|
|
13
|
+
* "looks like a path": a regex inside `redactDispatchMessage`, which only
|
|
14
|
+
* scrubbed error text, and nothing at all on the persistence boundary. The
|
|
15
|
+
* durable journal is committed to a shared repository, so a username or local
|
|
16
|
+
* layout that reaches it is in git history permanently.
|
|
17
|
+
*
|
|
18
|
+
* Message redaction and record sanitization now share this detector, because
|
|
19
|
+
* the gap between them is precisely what allowed paths to be scrubbed from a
|
|
20
|
+
* message and written verbatim into the record in the same command.
|
|
21
|
+
*/
|
|
22
|
+
/** The stable stand-in, matching the `<tier>/agents/<role>.md` precedent. */
|
|
23
|
+
export declare const REDACTED_PATH = "<redacted-path>";
|
|
24
|
+
/**
|
|
25
|
+
* Identity-field detector. Rejects on ambiguity by design.
|
|
26
|
+
*/
|
|
27
|
+
export declare function containsAbsolutePath(value: string): boolean;
|
|
28
|
+
/**
|
|
29
|
+
* Best-effort prose redaction. NOT a guarantee.
|
|
30
|
+
*
|
|
31
|
+
* Known limits, stated so they are visible rather than assumed away:
|
|
32
|
+
* - A colon-prefixed path (`cwd:/Users/alice`) survives, because `:` cannot be
|
|
33
|
+
* treated as a path delimiter without mangling every URL.
|
|
34
|
+
* - A candidate inside an http(s) URL is left alone, so a path-shaped route
|
|
35
|
+
* such as `?next=/dashboard` survives.
|
|
36
|
+
* - A candidate ending in `/` is left alone, because `/foo/bar/` is far more
|
|
37
|
+
* often a regex literal than a directory in prose.
|
|
38
|
+
* - Any path shape the pattern does not match survives.
|
|
39
|
+
*
|
|
40
|
+
* The bias is deliberate. A path leaked into prose is a disclosure; a mangled
|
|
41
|
+
* URL or regex is corrupted evidence, and corrupted evidence is worse in a
|
|
42
|
+
* provenance record. Identity and control fields carry the enforceable half of
|
|
43
|
+
* the guarantee via {@link containsAbsolutePath}.
|
|
44
|
+
*/
|
|
45
|
+
export declare function redactAbsolutePaths(value: string): string;
|
|
46
|
+
/**
|
|
47
|
+
* Recursively redact every string in a JSON value. Used for prose and nested
|
|
48
|
+
* evidence containers, where a path is plausible incidental content rather than
|
|
49
|
+
* a malformed identifier.
|
|
50
|
+
*/
|
|
51
|
+
export declare function redactAbsolutePathsDeep<T>(value: T): T;
|
|
52
|
+
/**
|
|
53
|
+
* Recursively reject any absolute path. Used for identity and control fields,
|
|
54
|
+
* where a path is never a legitimate value and silently rewriting one would
|
|
55
|
+
* corrupt the identifier it claims to be.
|
|
56
|
+
*/
|
|
57
|
+
export declare function assertNoAbsolutePath(value: unknown, path: string): void;
|
|
58
|
+
/**
|
|
59
|
+
* The publication postcondition, scoped to what is actually guaranteed.
|
|
60
|
+
*
|
|
61
|
+
* It hard-fails on a path in an identity or control field, which is the half of
|
|
62
|
+
* NFR1 that is enforceable. It deliberately does **not** claim that no absolute
|
|
63
|
+
* path remains anywhere in the revision: prose redaction is best-effort, and a
|
|
64
|
+
* postcondition that asserted more than the sanitizer can deliver would be a
|
|
65
|
+
* false guarantee rather than a check.
|
|
66
|
+
*/
|
|
67
|
+
export declare function assertJournalIdentityHasNoAbsolutePath(identityFields: Readonly<Record<string, unknown>>): void;
|
|
68
|
+
//# sourceMappingURL=absolute-paths.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"absolute-paths.d.ts","sourceRoot":"","sources":["../../../src/providers/identity/absolute-paths.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AA6BH,6EAA6E;AAC7E,eAAO,MAAM,aAAa,oBAAoB,CAAC;AAM/C;;GAEG;AACH,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAE3D;AAYD;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAOzD;AAED;;;;GAIG;AACH,wBAAgB,uBAAuB,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,GAAG,CAAC,CActD;AAED;;;;GAIG;AACH,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,GAAG,IAAI,CAmBvE;AAED;;;;;;;;GAQG;AACH,wBAAgB,sCAAsC,CACpD,cAAc,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,GAChD,IAAI,CAIN"}
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Absolute-path detection for dispatch records and messages.
|
|
3
|
+
*
|
|
4
|
+
* Two patterns, deliberately different. `IDENTITY_PATH_PATTERN` is strict and
|
|
5
|
+
* used where a path is never a legitimate value, so ambiguity resolves to
|
|
6
|
+
* rejection. `PROSE_PATH_PATTERN` is conservative and used where a false
|
|
7
|
+
* positive would corrupt legitimate evidence, so it under-matches by design:
|
|
8
|
+
* colon-prefixed forms, path-shaped routes inside URLs, and trailing-slash
|
|
9
|
+
* candidates survive. Prose redaction is therefore best-effort, not a
|
|
10
|
+
* guarantee — see `spec.md` NFR1, amended 2026-09-03.
|
|
11
|
+
*
|
|
12
|
+
* Before this module there were two independent notions of
|
|
13
|
+
* "looks like a path": a regex inside `redactDispatchMessage`, which only
|
|
14
|
+
* scrubbed error text, and nothing at all on the persistence boundary. The
|
|
15
|
+
* durable journal is committed to a shared repository, so a username or local
|
|
16
|
+
* layout that reaches it is in git history permanently.
|
|
17
|
+
*
|
|
18
|
+
* Message redaction and record sanitization now share this detector, because
|
|
19
|
+
* the gap between them is precisely what allowed paths to be scrubbed from a
|
|
20
|
+
* message and written verbatim into the record in the same command.
|
|
21
|
+
*/
|
|
22
|
+
/**
|
|
23
|
+
* Two detectors, because one regex cannot serve both jobs.
|
|
24
|
+
*
|
|
25
|
+
* `/dashboard` is a URL route, `/foo/bar/` is a regex literal, and
|
|
26
|
+
* `path:/Users/alice` is a real disclosure. Three rounds of tuning a single
|
|
27
|
+
* delimiter-sensitive pattern traded a leak for a mangled value each time, so
|
|
28
|
+
* the detectors are now split by what the field can legitimately contain.
|
|
29
|
+
*
|
|
30
|
+
* IDENTITY fields (caller, scope, selectors, routes, guidance references,
|
|
31
|
+
* authority, catalog snapshot, candidates, fallback) admit no URL, no regex and
|
|
32
|
+
* no path, so ambiguity there is resolved by rejecting. Its lookbehind includes
|
|
33
|
+
* `:` so `cwd:/Users/alice` is caught.
|
|
34
|
+
*
|
|
35
|
+
* PROSE fields carry human text and quoted evidence, where a URL or a regex is
|
|
36
|
+
* legitimate content. Redaction there is deliberately conservative and
|
|
37
|
+
* best-effort — see {@link redactAbsolutePaths}.
|
|
38
|
+
*/
|
|
39
|
+
const IDENTITY_PATH_PATTERN = /(?<=^|[\s'"`([{<=,;|:])(?:file:\/\/[^\s'"`;,)\]}]*|\\\\[^\s'"`;,)\]}]+|[A-Za-z]:[\\/][^\s'"`;,)\]}]*|\/(?!\/)[^\s'"`;,)\]}]+)/g;
|
|
40
|
+
/** As above, minus `:`, which is what makes URLs and regexes distinguishable. */
|
|
41
|
+
const PROSE_PATH_PATTERN = /(?<=^|[\s'"`([{<=,;|])(?:file:\/\/[^\s'"`;,)\]}]*|\\\\[^\s'"`;,)\]}]+|[A-Za-z]:[\\/][^\s'"`;,)\]}]*|\/(?!\/)[^\s'"`;,)\]}]+)/g;
|
|
42
|
+
/** Spans of an http(s) URL, whose internal slashes are routes, not paths. */
|
|
43
|
+
const HTTP_URL_PATTERN = /https?:\/\/[^\s'"`)\]}]*/g;
|
|
44
|
+
/** The stable stand-in, matching the `<tier>/agents/<role>.md` precedent. */
|
|
45
|
+
export const REDACTED_PATH = '<redacted-path>';
|
|
46
|
+
function fresh(pattern) {
|
|
47
|
+
return new RegExp(pattern.source, 'g');
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Identity-field detector. Rejects on ambiguity by design.
|
|
51
|
+
*/
|
|
52
|
+
export function containsAbsolutePath(value) {
|
|
53
|
+
return fresh(IDENTITY_PATH_PATTERN).test(value);
|
|
54
|
+
}
|
|
55
|
+
function httpUrlSpans(value) {
|
|
56
|
+
const spans = [];
|
|
57
|
+
const pattern = fresh(HTTP_URL_PATTERN);
|
|
58
|
+
let match;
|
|
59
|
+
while ((match = pattern.exec(value)) !== null) {
|
|
60
|
+
spans.push([match.index, match.index + match[0].length]);
|
|
61
|
+
}
|
|
62
|
+
return spans;
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Best-effort prose redaction. NOT a guarantee.
|
|
66
|
+
*
|
|
67
|
+
* Known limits, stated so they are visible rather than assumed away:
|
|
68
|
+
* - A colon-prefixed path (`cwd:/Users/alice`) survives, because `:` cannot be
|
|
69
|
+
* treated as a path delimiter without mangling every URL.
|
|
70
|
+
* - A candidate inside an http(s) URL is left alone, so a path-shaped route
|
|
71
|
+
* such as `?next=/dashboard` survives.
|
|
72
|
+
* - A candidate ending in `/` is left alone, because `/foo/bar/` is far more
|
|
73
|
+
* often a regex literal than a directory in prose.
|
|
74
|
+
* - Any path shape the pattern does not match survives.
|
|
75
|
+
*
|
|
76
|
+
* The bias is deliberate. A path leaked into prose is a disclosure; a mangled
|
|
77
|
+
* URL or regex is corrupted evidence, and corrupted evidence is worse in a
|
|
78
|
+
* provenance record. Identity and control fields carry the enforceable half of
|
|
79
|
+
* the guarantee via {@link containsAbsolutePath}.
|
|
80
|
+
*/
|
|
81
|
+
export function redactAbsolutePaths(value) {
|
|
82
|
+
const spans = httpUrlSpans(value);
|
|
83
|
+
return value.replace(fresh(PROSE_PATH_PATTERN), (match, offset) => {
|
|
84
|
+
if (match.endsWith('/'))
|
|
85
|
+
return match;
|
|
86
|
+
const inUrl = spans.some(([from, to]) => offset >= from && offset < to);
|
|
87
|
+
return inUrl ? match : REDACTED_PATH;
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Recursively redact every string in a JSON value. Used for prose and nested
|
|
92
|
+
* evidence containers, where a path is plausible incidental content rather than
|
|
93
|
+
* a malformed identifier.
|
|
94
|
+
*/
|
|
95
|
+
export function redactAbsolutePathsDeep(value) {
|
|
96
|
+
if (typeof value === 'string')
|
|
97
|
+
return redactAbsolutePaths(value);
|
|
98
|
+
if (Array.isArray(value)) {
|
|
99
|
+
return value.map((entry) => redactAbsolutePathsDeep(entry));
|
|
100
|
+
}
|
|
101
|
+
if (value !== null && typeof value === 'object') {
|
|
102
|
+
return Object.fromEntries(Object.entries(value).map(([key, entry]) => [
|
|
103
|
+
key,
|
|
104
|
+
redactAbsolutePathsDeep(entry),
|
|
105
|
+
]));
|
|
106
|
+
}
|
|
107
|
+
return value;
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* Recursively reject any absolute path. Used for identity and control fields,
|
|
111
|
+
* where a path is never a legitimate value and silently rewriting one would
|
|
112
|
+
* corrupt the identifier it claims to be.
|
|
113
|
+
*/
|
|
114
|
+
export function assertNoAbsolutePath(value, path) {
|
|
115
|
+
if (typeof value === 'string') {
|
|
116
|
+
if (containsAbsolutePath(value)) {
|
|
117
|
+
throw new Error(`A dispatch record must not carry an absolute filesystem path at ${path}.`);
|
|
118
|
+
}
|
|
119
|
+
return;
|
|
120
|
+
}
|
|
121
|
+
if (Array.isArray(value)) {
|
|
122
|
+
value.forEach((entry, index) => assertNoAbsolutePath(entry, `${path}[${index}]`));
|
|
123
|
+
return;
|
|
124
|
+
}
|
|
125
|
+
if (value === null || typeof value !== 'object')
|
|
126
|
+
return;
|
|
127
|
+
for (const [key, entry] of Object.entries(value)) {
|
|
128
|
+
assertNoAbsolutePath(entry, `${path}.${key}`);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
/**
|
|
132
|
+
* The publication postcondition, scoped to what is actually guaranteed.
|
|
133
|
+
*
|
|
134
|
+
* It hard-fails on a path in an identity or control field, which is the half of
|
|
135
|
+
* NFR1 that is enforceable. It deliberately does **not** claim that no absolute
|
|
136
|
+
* path remains anywhere in the revision: prose redaction is best-effort, and a
|
|
137
|
+
* postcondition that asserted more than the sanitizer can deliver would be a
|
|
138
|
+
* false guarantee rather than a check.
|
|
139
|
+
*/
|
|
140
|
+
export function assertJournalIdentityHasNoAbsolutePath(identityFields) {
|
|
141
|
+
for (const [field, value] of Object.entries(identityFields)) {
|
|
142
|
+
assertNoAbsolutePath(value, `<journal>.${field}`);
|
|
143
|
+
}
|
|
144
|
+
}
|