@rpamis/comet 0.4.0-beta.7 → 0.4.0-beta.9
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/LICENSE +21 -21
- package/README.md +3 -3
- package/assets/manifest.json +1 -1
- package/assets/skills/comet/reference/comet-yaml-fields.md +2 -2
- package/assets/skills/comet/scripts/comet-entry-runtime.mjs +118 -6
- package/assets/skills/comet/scripts/comet-hook-router.mjs +244 -26
- package/assets/skills/comet/scripts/comet-runtime.mjs +221 -104
- package/assets/skills/comet-archive/SKILL.md +29 -20
- package/assets/skills/comet-native/SKILL.md +23 -15
- package/assets/skills/comet-native/reference/artifacts.md +19 -4
- package/assets/skills/comet-native/reference/commands.md +8 -2
- package/assets/skills/comet-native/reference/recovery.md +1 -1
- package/assets/skills/comet-native/scripts/comet-native-runtime.mjs +1136 -471
- package/assets/skills-zh/comet/reference/comet-yaml-fields.md +2 -2
- package/assets/skills-zh/comet-archive/SKILL.md +29 -20
- package/assets/skills-zh/comet-native/SKILL.md +23 -15
- package/assets/skills-zh/comet-native/reference/artifacts.md +20 -5
- package/assets/skills-zh/comet-native/reference/commands.md +8 -2
- package/assets/skills-zh/comet-native/reference/recovery.md +1 -1
- package/dist/app/commands/init.d.ts.map +1 -1
- package/dist/app/commands/init.js +3 -6
- package/dist/app/commands/init.js.map +1 -1
- package/dist/domains/bundle/bundle-platform.d.ts.map +1 -1
- package/dist/domains/bundle/bundle-platform.js +1 -1
- package/dist/domains/bundle/bundle-platform.js.map +1 -1
- package/dist/domains/comet-classic/classic-guard.d.ts.map +1 -1
- package/dist/domains/comet-classic/classic-guard.js +3 -2
- package/dist/domains/comet-classic/classic-guard.js.map +1 -1
- package/dist/domains/comet-entry/current-selection.d.ts.map +1 -1
- package/dist/domains/comet-entry/current-selection.js +6 -8
- package/dist/domains/comet-entry/current-selection.js.map +1 -1
- package/dist/domains/comet-entry/project-status.d.ts.map +1 -1
- package/dist/domains/comet-entry/project-status.js +5 -1
- package/dist/domains/comet-entry/project-status.js.map +1 -1
- package/dist/domains/comet-native/native-archive-transaction.d.ts.map +1 -1
- package/dist/domains/comet-native/native-archive-transaction.js +5 -6
- package/dist/domains/comet-native/native-archive-transaction.js.map +1 -1
- package/dist/domains/comet-native/native-atomic-file.d.ts.map +1 -1
- package/dist/domains/comet-native/native-atomic-file.js +11 -7
- package/dist/domains/comet-native/native-atomic-file.js.map +1 -1
- package/dist/domains/comet-native/native-bounded-file.d.ts.map +1 -1
- package/dist/domains/comet-native/native-bounded-file.js +11 -7
- package/dist/domains/comet-native/native-bounded-file.js.map +1 -1
- package/dist/domains/comet-native/native-build-evidence.js +2 -2
- package/dist/domains/comet-native/native-build-evidence.js.map +1 -1
- package/dist/domains/comet-native/native-change.d.ts +4 -2
- package/dist/domains/comet-native/native-change.d.ts.map +1 -1
- package/dist/domains/comet-native/native-change.js +18 -4
- package/dist/domains/comet-native/native-change.js.map +1 -1
- package/dist/domains/comet-native/native-check-receipt-storage.d.ts.map +1 -1
- package/dist/domains/comet-native/native-check-receipt-storage.js +11 -7
- package/dist/domains/comet-native/native-check-receipt-storage.js.map +1 -1
- package/dist/domains/comet-native/native-check-receipt.d.ts.map +1 -1
- package/dist/domains/comet-native/native-check-receipt.js +16 -11
- package/dist/domains/comet-native/native-check-receipt.js.map +1 -1
- package/dist/domains/comet-native/native-cli.d.ts.map +1 -1
- package/dist/domains/comet-native/native-cli.js +120 -16
- package/dist/domains/comet-native/native-cli.js.map +1 -1
- package/dist/domains/comet-native/native-config.d.ts +5 -1
- package/dist/domains/comet-native/native-config.d.ts.map +1 -1
- package/dist/domains/comet-native/native-config.js +78 -0
- package/dist/domains/comet-native/native-config.js.map +1 -1
- package/dist/domains/comet-native/native-continuation.d.ts +2 -1
- package/dist/domains/comet-native/native-continuation.d.ts.map +1 -1
- package/dist/domains/comet-native/native-continuation.js +13 -5
- package/dist/domains/comet-native/native-continuation.js.map +1 -1
- package/dist/domains/comet-native/native-diagnostics.d.ts +7 -3
- package/dist/domains/comet-native/native-diagnostics.d.ts.map +1 -1
- package/dist/domains/comet-native/native-diagnostics.js +19 -9
- package/dist/domains/comet-native/native-diagnostics.js.map +1 -1
- package/dist/domains/comet-native/native-evidence-retention.d.ts.map +1 -1
- package/dist/domains/comet-native/native-evidence-retention.js +5 -3
- package/dist/domains/comet-native/native-evidence-retention.js.map +1 -1
- package/dist/domains/comet-native/native-evidence-storage.d.ts.map +1 -1
- package/dist/domains/comet-native/native-evidence-storage.js +11 -7
- package/dist/domains/comet-native/native-evidence-storage.js.map +1 -1
- package/dist/domains/comet-native/native-file-identity.d.ts +3 -0
- package/dist/domains/comet-native/native-file-identity.d.ts.map +1 -0
- package/dist/domains/comet-native/native-file-identity.js +2 -0
- package/dist/domains/comet-native/native-file-identity.js.map +1 -0
- package/dist/domains/comet-native/native-findings.d.ts.map +1 -1
- package/dist/domains/comet-native/native-findings.js +19 -1
- package/dist/domains/comet-native/native-findings.js.map +1 -1
- package/dist/domains/comet-native/native-guards.d.ts +2 -1
- package/dist/domains/comet-native/native-guards.d.ts.map +1 -1
- package/dist/domains/comet-native/native-guards.js +14 -0
- package/dist/domains/comet-native/native-guards.js.map +1 -1
- package/dist/domains/comet-native/native-lock.d.ts.map +1 -1
- package/dist/domains/comet-native/native-lock.js +30 -35
- package/dist/domains/comet-native/native-lock.js.map +1 -1
- package/dist/domains/comet-native/native-protected-file.d.ts.map +1 -1
- package/dist/domains/comet-native/native-protected-file.js +9 -8
- package/dist/domains/comet-native/native-protected-file.js.map +1 -1
- package/dist/domains/comet-native/native-root-move.d.ts.map +1 -1
- package/dist/domains/comet-native/native-root-move.js +2 -0
- package/dist/domains/comet-native/native-root-move.js.map +1 -1
- package/dist/domains/comet-native/native-run-store.d.ts.map +1 -1
- package/dist/domains/comet-native/native-run-store.js +9 -8
- package/dist/domains/comet-native/native-run-store.js.map +1 -1
- package/dist/domains/comet-native/native-snapshot.d.ts +5 -1
- package/dist/domains/comet-native/native-snapshot.d.ts.map +1 -1
- package/dist/domains/comet-native/native-snapshot.js +225 -5
- package/dist/domains/comet-native/native-snapshot.js.map +1 -1
- package/dist/domains/comet-native/native-transitions.d.ts +2 -1
- package/dist/domains/comet-native/native-transitions.d.ts.map +1 -1
- package/dist/domains/comet-native/native-transitions.js +34 -11
- package/dist/domains/comet-native/native-transitions.js.map +1 -1
- package/dist/domains/comet-native/native-types.d.ts +16 -0
- package/dist/domains/comet-native/native-types.d.ts.map +1 -1
- package/dist/domains/comet-native/native-verification-runtime.d.ts.map +1 -1
- package/dist/domains/comet-native/native-verification-runtime.js +5 -3
- package/dist/domains/comet-native/native-verification-runtime.js.map +1 -1
- package/dist/domains/comet-native/native-verification-scope.d.ts +2 -1
- package/dist/domains/comet-native/native-verification-scope.d.ts.map +1 -1
- package/dist/domains/comet-native/native-verification-scope.js +6 -1
- package/dist/domains/comet-native/native-verification-scope.js.map +1 -1
- package/dist/domains/dashboard/native-collector.d.ts.map +1 -1
- package/dist/domains/dashboard/native-collector.js +4 -1
- package/dist/domains/dashboard/native-collector.js.map +1 -1
- package/dist/domains/dashboard/web/index.html +14 -14
- package/dist/domains/skill/platform-install.d.ts.map +1 -1
- package/dist/domains/skill/platform-install.js +9 -0
- package/dist/domains/skill/platform-install.js.map +1 -1
- package/dist/domains/skill/project-instructions.d.ts.map +1 -1
- package/dist/domains/skill/project-instructions.js +4 -2
- package/dist/domains/skill/project-instructions.js.map +1 -1
- package/dist/domains/workflow-contract/project-config.d.ts +1 -1
- package/dist/domains/workflow-contract/project-config.d.ts.map +1 -1
- package/dist/domains/workflow-contract/project-config.js +24 -2
- package/dist/domains/workflow-contract/project-config.js.map +1 -1
- package/dist/platform/fs/file-identity.d.ts +9 -0
- package/dist/platform/fs/file-identity.d.ts.map +1 -0
- package/dist/platform/fs/file-identity.js +21 -0
- package/dist/platform/fs/file-identity.js.map +1 -0
- package/dist/platform/fs/file-system.d.ts.map +1 -1
- package/dist/platform/fs/file-system.js +13 -9
- package/dist/platform/fs/file-system.js.map +1 -1
- package/dist/platform/fs/race-safe-read.d.ts +47 -0
- package/dist/platform/fs/race-safe-read.d.ts.map +1 -0
- package/dist/platform/fs/race-safe-read.js +132 -0
- package/dist/platform/fs/race-safe-read.js.map +1 -0
- package/eval/README.md +1 -1
- package/eval/local/tasks/comet-native-clarification-depth/environment/Dockerfile +27 -0
- package/eval/local/tasks/comet-native-clarification-depth/environment/test_wordcount.py +25 -0
- package/eval/local/tasks/comet-native-clarification-depth/environment/wordcount.py +36 -0
- package/eval/local/tasks/comet-native-clarification-depth/instruction.md +17 -0
- package/eval/local/tasks/comet-native-clarification-depth/task.toml +47 -0
- package/eval/local/tasks/comet-native-clarification-depth/validation/test_native_clarification_depth.py +618 -0
- package/eval/local/tasks/comet-native-clarification-modes/validation/test_native_clarification_modes.py +1 -1
- package/eval/local/tasks/index.yaml +6 -0
- package/eval/local/tests/conftest.py +146 -2
- package/eval/local/tests/scaffold/test_conftest_helpers.py +188 -1
- package/eval/local/tests/scaffold/test_tasks.py +440 -8
- package/eval/local/tests/scaffold/test_utils.py +114 -1
- package/eval/local/tests/tasks/test_tasks.py +2 -0
- package/eval/pyproject.toml +4 -4
- package/eval/scaffold/python/aligned_comparison.py +1 -0
- package/eval/scaffold/python/tasks.py +2 -0
- package/eval/scaffold/shell/docker.sh +1 -1
- package/eval/scaffold/shell/run-claude-loop.sh +25 -2
- package/package.json +8 -1
|
@@ -57,7 +57,7 @@ export async function fileExists(filePath) {
|
|
|
57
57
|
return true;
|
|
58
58
|
}
|
|
59
59
|
catch (error) {
|
|
60
|
-
if (
|
|
60
|
+
if (isMissingPathError(error))
|
|
61
61
|
return false;
|
|
62
62
|
throw error;
|
|
63
63
|
}
|
|
@@ -94,13 +94,17 @@ export async function readDir(dirPath) {
|
|
|
94
94
|
}
|
|
95
95
|
}
|
|
96
96
|
/**
|
|
97
|
-
* Returns true when an error means
|
|
98
|
-
*
|
|
99
|
-
*
|
|
100
|
-
*
|
|
97
|
+
* Returns true when an error means a path lookup found a file where it
|
|
98
|
+
* expected to be able to descend into a directory (ENOTDIR) — e.g. a stray
|
|
99
|
+
* `.DS_Store` sitting where a plugin marketplace directory was expected —
|
|
100
|
+
* treated the same as the path simply not being there (ENOENT). Only for
|
|
101
|
+
* read-only existence checks: a removal call hitting ENOTDIR means the
|
|
102
|
+
* on-disk shape is not what the caller expected, which is a real anomaly
|
|
103
|
+
* worth surfacing rather than masking as "already gone".
|
|
101
104
|
*/
|
|
102
|
-
function
|
|
103
|
-
|
|
105
|
+
function isMissingPathError(error) {
|
|
106
|
+
const code = error?.code;
|
|
107
|
+
return code === 'ENOENT' || code === 'ENOTDIR';
|
|
104
108
|
}
|
|
105
109
|
/**
|
|
106
110
|
* Remove a file. Returns true if the file existed and was removed.
|
|
@@ -113,7 +117,7 @@ export async function removeFile(filePath) {
|
|
|
113
117
|
return true;
|
|
114
118
|
}
|
|
115
119
|
catch (error) {
|
|
116
|
-
if (
|
|
120
|
+
if (error?.code === 'ENOENT')
|
|
117
121
|
return false;
|
|
118
122
|
throw error;
|
|
119
123
|
}
|
|
@@ -135,7 +139,7 @@ export async function removeDir(dirPath) {
|
|
|
135
139
|
return true;
|
|
136
140
|
}
|
|
137
141
|
catch (error) {
|
|
138
|
-
if (
|
|
142
|
+
if (error?.code === 'ENOENT')
|
|
139
143
|
return false;
|
|
140
144
|
throw error;
|
|
141
145
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"file-system.js","sourceRoot":"","sources":["../../../platform/fs/file-system.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,IAAI,EAAE,EAAE,MAAM,IAAI,CAAC;AACpC,OAAO,IAAI,MAAM,MAAM,CAAC;AAExB;;;GAGG;AACH,KAAK,UAAU,kBAAkB,CAAC,QAAgB;IAChD,IAAI,CAAC;QACH,OAAO,MAAM,EAAE,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC;IACrC,CAAC;IAAC,MAAM,CAAC;QACP,2EAA2E;QAC3E,MAAM,GAAG,GAAG,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC;QACnC,IAAI,GAAG,KAAK,QAAQ;YAAE,OAAO,QAAQ,CAAC,CAAC,kBAAkB;QAEzD,MAAM,WAAW,GAAG,MAAM,kBAAkB,CAAC,GAAG,CAAC,CAAC;QAClD,MAAM,IAAI,GAAG,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC;QAErC,kEAAkE;QAClE,IAAI,CAAC;YACH,MAAM,IAAI,GAAG,MAAM,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,IAAI,CAAC,CAAC,CAAC;YAC1D,IAAI,IAAI,CAAC,cAAc,EAAE,EAAE,CAAC;gBAC1B,MAAM,MAAM,GAAG,MAAM,EAAE,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,IAAI,CAAC,CAAC,CAAC;gBAC/D,OAAO,IAAI,CAAC,OAAO,CAAC,WAAW,EAAE,MAAM,CAAC,CAAC;YAC3C,CAAC;QACH,CAAC;QAAC,MAAM,CAAC;YACP,uCAAuC;QACzC,CAAC;QAED,OAAO,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,IAAI,CAAC,CAAC;IACtC,CAAC;AACH,CAAC;AAED;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,SAAS,CAAC,GAAW;IACzC,MAAM,QAAQ,GAAG,MAAM,kBAAkB,CAAC,GAAG,CAAC,CAAC;IAC/C,MAAM,EAAE,CAAC,KAAK,CAAC,QAAQ,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;AAChD,CAAC;AAED;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,QAAQ,CAAC,GAAW,EAAE,IAAY;IACtD,MAAM,YAAY,GAAG,MAAM,kBAAkB,CAAC,IAAI,CAAC,CAAC;IACpD,MAAM,SAAS,CAAC,IAAI,CAAC,OAAO,CAAC,YAAY,CAAC,CAAC,CAAC;IAC5C,MAAM,EAAE,CAAC,QAAQ,CAAC,GAAG,EAAE,YAAY,CAAC,CAAC;AACvC,CAAC;AAED;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,UAAU,CAAC,QAAgB;IAC/C,IAAI,CAAC;QACH,MAAM,EAAE,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;QAC1B,OAAO,IAAI,CAAC;IACd,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,IAAI,
|
|
1
|
+
{"version":3,"file":"file-system.js","sourceRoot":"","sources":["../../../platform/fs/file-system.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import type { BigIntStats, Stats } from 'fs';
|
|
2
|
+
import { type FileObjectIdentity } from './file-identity.js';
|
|
3
|
+
export type RaceSafeReadCheckpoint = 'pre-open' | 'post-open' | 'post-read';
|
|
4
|
+
export interface RaceSafeReadContext {
|
|
5
|
+
realPath: string;
|
|
6
|
+
identity: FileObjectIdentity;
|
|
7
|
+
}
|
|
8
|
+
export type RaceSafeReadFailureReason = 'not-regular-file' | 'too-large' | 'changed';
|
|
9
|
+
export declare class RaceSafeReadError extends Error {
|
|
10
|
+
readonly reason: RaceSafeReadFailureReason;
|
|
11
|
+
constructor(reason: RaceSafeReadFailureReason, message: string, options?: {
|
|
12
|
+
cause?: unknown;
|
|
13
|
+
});
|
|
14
|
+
}
|
|
15
|
+
export interface RaceSafeReadOptions {
|
|
16
|
+
/** Use BigIntStats precision (dev/ino/size/mtimeNs/ctimeNs) end to end. */
|
|
17
|
+
bigint?: boolean;
|
|
18
|
+
/** Prefixed into built-in error messages, e.g. `${label} changed while reading`. */
|
|
19
|
+
label?: string;
|
|
20
|
+
/** Test-only synchronization points; behavior is passed through unchanged. */
|
|
21
|
+
hooks?: {
|
|
22
|
+
afterOpen?: () => void | Promise<void>;
|
|
23
|
+
beforeFinalCheck?: () => void | Promise<void>;
|
|
24
|
+
};
|
|
25
|
+
/**
|
|
26
|
+
* Called inside the same synchronization window as the built-in identity
|
|
27
|
+
* checks at each checkpoint. A thrown error aborts the read exactly like an
|
|
28
|
+
* identity mismatch. Use this to stack domain-specific checks (for example
|
|
29
|
+
* Native directory-chain verification) without opening a second race window.
|
|
30
|
+
*/
|
|
31
|
+
verify?: (checkpoint: RaceSafeReadCheckpoint, context: RaceSafeReadContext) => void | Promise<void>;
|
|
32
|
+
}
|
|
33
|
+
export interface RaceSafeReadResult {
|
|
34
|
+
bytes: Buffer;
|
|
35
|
+
stat: Stats | BigIntStats;
|
|
36
|
+
realPath: string;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Read a regular file through one descriptor with identity re-verification
|
|
40
|
+
* before open, after open, and after the bounded read, so a path swapped for
|
|
41
|
+
* a symlink or replacement file between checks is always rejected.
|
|
42
|
+
*
|
|
43
|
+
* ENOENT (and every other system error except ELOOP) propagates unchanged so
|
|
44
|
+
* callers can keep their own missing-file semantics.
|
|
45
|
+
*/
|
|
46
|
+
export declare function readFileRaceSafe(file: string, maxBytes: number, options?: RaceSafeReadOptions): Promise<RaceSafeReadResult>;
|
|
47
|
+
//# sourceMappingURL=race-safe-read.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"race-safe-read.d.ts","sourceRoot":"","sources":["../../../platform/fs/race-safe-read.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,WAAW,EAAE,KAAK,EAAE,MAAM,IAAI,CAAC;AAE7C,OAAO,EAGL,KAAK,kBAAkB,EACxB,MAAM,oBAAoB,CAAC;AAE5B,MAAM,MAAM,sBAAsB,GAAG,UAAU,GAAG,WAAW,GAAG,WAAW,CAAC;AAE5E,MAAM,WAAW,mBAAmB;IAClC,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,kBAAkB,CAAC;CAC9B;AAED,MAAM,MAAM,yBAAyB,GAAG,kBAAkB,GAAG,WAAW,GAAG,SAAS,CAAC;AAErF,qBAAa,iBAAkB,SAAQ,KAAK;IAC1C,QAAQ,CAAC,MAAM,EAAE,yBAAyB,CAAC;gBAE/B,MAAM,EAAE,yBAAyB,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE;QAAE,KAAK,CAAC,EAAE,OAAO,CAAA;KAAE;CAK9F;AAED,MAAM,WAAW,mBAAmB;IAClC,2EAA2E;IAC3E,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,oFAAoF;IACpF,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,8EAA8E;IAC9E,KAAK,CAAC,EAAE;QACN,SAAS,CAAC,EAAE,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;QACvC,gBAAgB,CAAC,EAAE,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;KAC/C,CAAC;IACF;;;;;OAKG;IACH,MAAM,CAAC,EAAE,CACP,UAAU,EAAE,sBAAsB,EAClC,OAAO,EAAE,mBAAmB,KACzB,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CAC3B;AAED,MAAM,WAAW,kBAAkB;IACjC,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,KAAK,GAAG,WAAW,CAAC;IAC1B,QAAQ,EAAE,MAAM,CAAC;CAClB;AAgCD;;;;;;;GAOG;AACH,wBAAsB,gBAAgB,CACpC,IAAI,EAAE,MAAM,EACZ,QAAQ,EAAE,MAAM,EAChB,OAAO,GAAE,mBAAwB,GAChC,OAAO,CAAC,kBAAkB,CAAC,CAgG7B"}
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
import { constants as fsConstants, promises as fs } from 'fs';
|
|
2
|
+
import { hasComparableFileObject, sameFileObject, } from './file-identity.js';
|
|
3
|
+
export class RaceSafeReadError extends Error {
|
|
4
|
+
reason;
|
|
5
|
+
constructor(reason, message, options) {
|
|
6
|
+
super(message, options);
|
|
7
|
+
this.name = 'RaceSafeReadError';
|
|
8
|
+
this.reason = reason;
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
function birthtimeOf(stat) {
|
|
12
|
+
return 'birthtimeNs' in stat && typeof stat.birthtimeNs === 'bigint'
|
|
13
|
+
? stat.birthtimeNs
|
|
14
|
+
: stat.birthtimeMs;
|
|
15
|
+
}
|
|
16
|
+
function ctimeOf(stat) {
|
|
17
|
+
return 'ctimeNs' in stat && typeof stat.ctimeNs === 'bigint' ? stat.ctimeNs : stat.ctimeMs;
|
|
18
|
+
}
|
|
19
|
+
function identityOf(stat) {
|
|
20
|
+
return { dev: stat.dev, ino: stat.ino, birthtime: birthtimeOf(stat) };
|
|
21
|
+
}
|
|
22
|
+
function sameStatIdentity(left, right) {
|
|
23
|
+
const leftObject = identityOf(left);
|
|
24
|
+
const rightObject = identityOf(right);
|
|
25
|
+
if (hasComparableFileObject(leftObject, rightObject)) {
|
|
26
|
+
return sameFileObject(leftObject, rightObject);
|
|
27
|
+
}
|
|
28
|
+
return (sameFileObject(leftObject, rightObject) &&
|
|
29
|
+
birthtimeOf(left) === birthtimeOf(right) &&
|
|
30
|
+
ctimeOf(left) === ctimeOf(right) &&
|
|
31
|
+
left.size === right.size);
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Read a regular file through one descriptor with identity re-verification
|
|
35
|
+
* before open, after open, and after the bounded read, so a path swapped for
|
|
36
|
+
* a symlink or replacement file between checks is always rejected.
|
|
37
|
+
*
|
|
38
|
+
* ENOENT (and every other system error except ELOOP) propagates unchanged so
|
|
39
|
+
* callers can keep their own missing-file semantics.
|
|
40
|
+
*/
|
|
41
|
+
export async function readFileRaceSafe(file, maxBytes, options = {}) {
|
|
42
|
+
if (!Number.isSafeInteger(maxBytes) || maxBytes < 1) {
|
|
43
|
+
throw new Error('race-safe read byte limit must be a positive integer');
|
|
44
|
+
}
|
|
45
|
+
const label = options.label ?? 'file';
|
|
46
|
+
const bigint = options.bigint === true;
|
|
47
|
+
const before = await fs.lstat(file, { bigint });
|
|
48
|
+
if (!before.isFile() || before.isSymbolicLink()) {
|
|
49
|
+
throw new RaceSafeReadError('not-regular-file', `${label} must be a regular file`);
|
|
50
|
+
}
|
|
51
|
+
if (BigInt(before.size) > BigInt(maxBytes)) {
|
|
52
|
+
throw new RaceSafeReadError('too-large', `${label} exceeds ${maxBytes} bytes`);
|
|
53
|
+
}
|
|
54
|
+
const beforeRealPath = await fs.realpath(file);
|
|
55
|
+
await options.verify?.('pre-open', { realPath: beforeRealPath, identity: identityOf(before) });
|
|
56
|
+
// POSIX: O_NOFOLLOW rejects a symlink swapped in since the lstat above and
|
|
57
|
+
// O_NONBLOCK prevents a FIFO from blocking the open. Windows: Node exposes
|
|
58
|
+
// neither flag, so this branch opens with plain O_RDONLY and the
|
|
59
|
+
// lstat/realpath identity checks below - which run identically on every
|
|
60
|
+
// platform - are the sole and sufficient guard against both swaps.
|
|
61
|
+
const flags = process.platform === 'win32'
|
|
62
|
+
? fsConstants.O_RDONLY
|
|
63
|
+
: fsConstants.O_RDONLY | fsConstants.O_NOFOLLOW | fsConstants.O_NONBLOCK;
|
|
64
|
+
let handle;
|
|
65
|
+
try {
|
|
66
|
+
handle = await fs.open(file, flags);
|
|
67
|
+
}
|
|
68
|
+
catch (error) {
|
|
69
|
+
if (error.code === 'ELOOP') {
|
|
70
|
+
throw new RaceSafeReadError('not-regular-file', `${label} must be a regular file`, {
|
|
71
|
+
cause: error,
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
throw error;
|
|
75
|
+
}
|
|
76
|
+
try {
|
|
77
|
+
const [opened, pathAfterOpen, realPathAfterOpen] = await Promise.all([
|
|
78
|
+
handle.stat({ bigint }),
|
|
79
|
+
fs.lstat(file, { bigint }),
|
|
80
|
+
fs.realpath(file),
|
|
81
|
+
]);
|
|
82
|
+
if (!opened.isFile() ||
|
|
83
|
+
!pathAfterOpen.isFile() ||
|
|
84
|
+
pathAfterOpen.isSymbolicLink() ||
|
|
85
|
+
realPathAfterOpen !== beforeRealPath ||
|
|
86
|
+
!sameStatIdentity(before, opened) ||
|
|
87
|
+
!sameStatIdentity(before, pathAfterOpen)) {
|
|
88
|
+
throw new RaceSafeReadError('changed', `${label} changed while opening`);
|
|
89
|
+
}
|
|
90
|
+
await options.verify?.('post-open', {
|
|
91
|
+
realPath: realPathAfterOpen,
|
|
92
|
+
identity: identityOf(opened),
|
|
93
|
+
});
|
|
94
|
+
await options.hooks?.afterOpen?.();
|
|
95
|
+
const chunks = [];
|
|
96
|
+
let total = 0;
|
|
97
|
+
const buffer = Buffer.allocUnsafe(Math.min(64 * 1024, maxBytes + 1));
|
|
98
|
+
for (;;) {
|
|
99
|
+
const remaining = maxBytes + 1 - total;
|
|
100
|
+
const { bytesRead } = await handle.read(buffer, 0, Math.min(buffer.length, remaining), null);
|
|
101
|
+
if (bytesRead === 0)
|
|
102
|
+
break;
|
|
103
|
+
total += bytesRead;
|
|
104
|
+
if (total > maxBytes) {
|
|
105
|
+
throw new RaceSafeReadError('too-large', `${label} exceeds ${maxBytes} bytes`);
|
|
106
|
+
}
|
|
107
|
+
chunks.push(Buffer.from(buffer.subarray(0, bytesRead)));
|
|
108
|
+
}
|
|
109
|
+
await options.hooks?.beforeFinalCheck?.();
|
|
110
|
+
const [afterHandle, afterPath, afterRealPath] = await Promise.all([
|
|
111
|
+
handle.stat({ bigint }),
|
|
112
|
+
fs.lstat(file, { bigint }),
|
|
113
|
+
fs.realpath(file),
|
|
114
|
+
]);
|
|
115
|
+
if (!afterPath.isFile() ||
|
|
116
|
+
afterPath.isSymbolicLink() ||
|
|
117
|
+
afterRealPath !== beforeRealPath ||
|
|
118
|
+
!sameStatIdentity(before, afterHandle) ||
|
|
119
|
+
!sameStatIdentity(before, afterPath)) {
|
|
120
|
+
throw new RaceSafeReadError('changed', `${label} changed while reading`);
|
|
121
|
+
}
|
|
122
|
+
await options.verify?.('post-read', {
|
|
123
|
+
realPath: afterRealPath,
|
|
124
|
+
identity: identityOf(afterHandle),
|
|
125
|
+
});
|
|
126
|
+
return { bytes: Buffer.concat(chunks, total), stat: afterHandle, realPath: afterRealPath };
|
|
127
|
+
}
|
|
128
|
+
finally {
|
|
129
|
+
await handle.close();
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
//# sourceMappingURL=race-safe-read.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"race-safe-read.js","sourceRoot":"","sources":["../../../platform/fs/race-safe-read.ts"],"names":[],"mappings":"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"}
|
package/eval/README.md
CHANGED
|
@@ -58,7 +58,7 @@ cp .env.example .env
|
|
|
58
58
|
| ----------------------------------------------------------------- | ---- | --------------------------------------------------------------------------------------------------- |
|
|
59
59
|
| `ANTHROPIC_API_KEY` | ✅\* | Anthropic API 密钥;也可以由系统环境变量提供 |
|
|
60
60
|
| `BENCH_CC_MODEL` | ❌ | Claude 模型覆盖(默认用 CLI 配置) |
|
|
61
|
-
| `BENCH_SIMULATOR_PROMPT_FILE` | ❌ |
|
|
61
|
+
| `BENCH_SIMULATOR_PROMPT_FILE` | ❌ | 显式覆盖任务级用户模拟器提示词;任务未配置时回退到 `eval/simulator-instruction.md` |
|
|
62
62
|
| `ANTHROPIC_AUTH_TOKEN` / `ANTHROPIC_BASE_URL` / `ANTHROPIC_MODEL` | ❌ | 用 Anthropic 兼容代理时的认证与模型配置;设置 `ANTHROPIC_AUTH_TOKEN` 后可不设置 `ANTHROPIC_API_KEY` |
|
|
63
63
|
| `BENCH_LLM_JUDGE` | ❌ | 设为 `1` 启用 LLM-as-judge 评分 |
|
|
64
64
|
| `BENCH_JUDGE_MODEL` | ❌ | Judge 模型;启用 `BENCH_LLM_JUDGE=1` 时必须显式设置,不能复用主模型 |
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
FROM python:3.12-slim
|
|
2
|
+
|
|
3
|
+
WORKDIR /workspace
|
|
4
|
+
|
|
5
|
+
RUN apt-get update && apt-get install -y --no-install-recommends \
|
|
6
|
+
git nodejs npm ca-certificates && \
|
|
7
|
+
rm -rf /var/lib/apt/lists/*
|
|
8
|
+
|
|
9
|
+
RUN npm config set registry https://registry.npmmirror.com && \
|
|
10
|
+
npm install -g @anthropic-ai/claude-code@latest && \
|
|
11
|
+
npm config delete registry
|
|
12
|
+
|
|
13
|
+
RUN pip install --no-cache-dir pyyaml pytest
|
|
14
|
+
|
|
15
|
+
COPY wordcount.py /workspace/wordcount.py
|
|
16
|
+
COPY test_wordcount.py /workspace/test_wordcount.py
|
|
17
|
+
|
|
18
|
+
RUN useradd --create-home --shell /bin/bash agent && \
|
|
19
|
+
chown -R agent:agent /workspace
|
|
20
|
+
USER agent
|
|
21
|
+
ENV HOME=/home/agent
|
|
22
|
+
ENV PYTHONPATH=/workspace
|
|
23
|
+
RUN git config --global user.email "agent@eval.local" && \
|
|
24
|
+
git config --global user.name "Comet Native Clarification Depth Eval Agent" && \
|
|
25
|
+
git config --global init.defaultBranch master
|
|
26
|
+
|
|
27
|
+
CMD ["bash"]
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
"""Baseline tests for the word-count CLI."""
|
|
2
|
+
|
|
3
|
+
import subprocess
|
|
4
|
+
import sys
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def run_cli(*args: str, input_text: str) -> str:
|
|
8
|
+
result = subprocess.run(
|
|
9
|
+
[sys.executable, "wordcount.py", *args],
|
|
10
|
+
input=input_text,
|
|
11
|
+
capture_output=True,
|
|
12
|
+
text=True,
|
|
13
|
+
check=True,
|
|
14
|
+
)
|
|
15
|
+
return result.stdout
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def test_count_words():
|
|
19
|
+
assert "Words: 3" in run_cli(input_text="hello world foo")
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def test_count_lines():
|
|
23
|
+
output = run_cli("--lines", input_text="hello\nworld\nfoo")
|
|
24
|
+
assert "Words: 3" in output
|
|
25
|
+
assert "Lines: 3" in output
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
"""Simple word count CLI tool."""
|
|
2
|
+
|
|
3
|
+
import argparse
|
|
4
|
+
import sys
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def count_words(text: str) -> int:
|
|
8
|
+
"""Count words in text."""
|
|
9
|
+
return len(text.split())
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def count_lines(text: str) -> int:
|
|
13
|
+
"""Count lines in text."""
|
|
14
|
+
return len(text.splitlines())
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def main():
|
|
18
|
+
parser = argparse.ArgumentParser(description="Count words and lines in text")
|
|
19
|
+
parser.add_argument("file", nargs="?", help="Input file (reads stdin if omitted)")
|
|
20
|
+
parser.add_argument("--lines", action="store_true", help="Also count lines")
|
|
21
|
+
parser.add_argument("--words", action="store_true", default=True, help="Count words (default)")
|
|
22
|
+
args = parser.parse_args()
|
|
23
|
+
|
|
24
|
+
if args.file:
|
|
25
|
+
with open(args.file) as source:
|
|
26
|
+
text = source.read()
|
|
27
|
+
else:
|
|
28
|
+
text = sys.stdin.read()
|
|
29
|
+
|
|
30
|
+
print(f"Words: {count_words(text)}")
|
|
31
|
+
if args.lines:
|
|
32
|
+
print(f"Lines: {count_lines(text)}")
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
if __name__ == "__main__":
|
|
36
|
+
main()
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
You are working on a Python project named `wordcount-cli`.
|
|
2
|
+
|
|
3
|
+
Begin by invoking the `/comet-native` Skill. Initialize Native under `docs/comet/`, create one change named `add-sentence-counting`, and add an opt-in `--sentences` CLI flag that prints `Sentences: N` after any requested `Lines: N` output while preserving the existing word and line counters.
|
|
4
|
+
|
|
5
|
+
The fixed interface contract is:
|
|
6
|
+
|
|
7
|
+
- only the ASCII terminal characters `.`, `!`, and `?` can create sentence boundaries;
|
|
8
|
+
- non-empty input with no terminal character counts as one sentence;
|
|
9
|
+
- when `--sentences` is present, it prints independently of `--lines`; the flag behavior and output order described above are not open product decisions.
|
|
10
|
+
|
|
11
|
+
The input domain is ordinary ASCII prose. Numbers, URLs, file paths, code, markup, quoted data, and Unicode-specific segmentation are out of scope.
|
|
12
|
+
|
|
13
|
+
The contract deliberately leaves three areas unresolved: empty or whitespace-only input, consecutive terminal characters, and terminal-looking characters embedded inside ordinary prose tokens. Resolve them in that listed order through the configured clarification protocol. An answer about whether an embedded character creates a boundary may introduce later decisions about how the recognized token collection is maintained and its exact contents; handle each dependent decision immediately after its parent.
|
|
14
|
+
|
|
15
|
+
The requested interface is intentionally underspecified. Follow the already configured Native Sequential clarification protocol and resolve every undefined user-visible behavior before implementation. Investigate repository-owned facts yourself, and do not change the configured clarification mode.
|
|
16
|
+
|
|
17
|
+
Complete Shape, Build, Verify, and Archive using only the Comet Native Skill and its bundled runtime.
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
[metadata]
|
|
2
|
+
name = "comet-native-clarification-depth"
|
|
3
|
+
description = "Measure whether Native Sequential discovers hidden user-owned decisions before implementation"
|
|
4
|
+
difficulty = "hard"
|
|
5
|
+
category = "comet-native"
|
|
6
|
+
tags = ["comet-native", "clarification", "sequential", "multi-turn", "decision-depth"]
|
|
7
|
+
default_treatments = ["COMET_NATIVE_SEQUENTIAL"]
|
|
8
|
+
|
|
9
|
+
[template]
|
|
10
|
+
required = []
|
|
11
|
+
|
|
12
|
+
[environment]
|
|
13
|
+
description = "Python word-count workspace with only the self-contained Comet Native Skill available."
|
|
14
|
+
dockerfile = "environment/Dockerfile"
|
|
15
|
+
timeout_sec = 2400
|
|
16
|
+
|
|
17
|
+
[validation]
|
|
18
|
+
test_scripts = "test_native_clarification_depth.py"
|
|
19
|
+
target_artifacts = [".comet/config.yaml", "docs/comet/archive", "docs/comet/specs"]
|
|
20
|
+
timeout = 120
|
|
21
|
+
|
|
22
|
+
[evaluation]
|
|
23
|
+
profile = "generic"
|
|
24
|
+
required_skills = ["comet-native"]
|
|
25
|
+
expected_artifacts = [".comet/config.yaml", "docs/comet/archive", "docs/comet/specs"]
|
|
26
|
+
require_skill_invocation = true
|
|
27
|
+
rubric_criteria = [
|
|
28
|
+
"Investigates repository-owned facts without asking the user",
|
|
29
|
+
"Discovers all hidden user-owned behavior decisions one at a time",
|
|
30
|
+
"Provides a recommendation and practical impact for each decision",
|
|
31
|
+
"Obtains explicit shared-understanding confirmation before Build",
|
|
32
|
+
"Completes Build, Verify, and Archive using only the Comet Native Skill and bundled runtime",
|
|
33
|
+
]
|
|
34
|
+
|
|
35
|
+
[interaction]
|
|
36
|
+
mode = "auto_user"
|
|
37
|
+
max_turns = 8
|
|
38
|
+
decision_replies = [
|
|
39
|
+
"It prints Sentences: 0.",
|
|
40
|
+
"They count as one sentence boundary.",
|
|
41
|
+
"Recognized abbreviations such as e.g. and Dr. always suppress their periods, including at end-of-input; every other occurrence of ., !, or ? remains a sentence boundary, and the non-empty fallback still makes an abbreviation-only input one sentence.",
|
|
42
|
+
"Use a small explicit list. Ask me for the exact entries separately.",
|
|
43
|
+
"Use exactly Dr., Mr., Mrs., Ms., Prof., Sr., Jr., e.g., i.e., etc., vs., Inc., Ltd., Corp., St., and Ave., matched case-sensitively exactly as written.",
|
|
44
|
+
"confirmed",
|
|
45
|
+
]
|
|
46
|
+
decision_patterns = ["Question:", "Recommendation:", "Impact:", "shared understanding", "Please confirm"]
|
|
47
|
+
continue_prompt = "Please read the persisted Native state and continue the same confirmed change through verification and archive."
|