@piagent/platform 1.3.0-ie.9 → 1.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (52) hide show
  1. package/CHANGELOG.md +36 -3
  2. package/README.md +1 -1
  3. package/docs/capability-packs.md +9 -2
  4. package/docs/command-reference-vietnamese.md +2 -2
  5. package/docs/distribution-standard.md +7 -7
  6. package/docs/operator-manual-vietnamese.md +7 -7
  7. package/docs/release-install-policy.md +29 -21
  8. package/docs/security-threat-model.md +1 -1
  9. package/docs/team-onboarding.md +2 -2
  10. package/docs-site/architecture.html +1 -1
  11. package/docs-site/benchmark.html +1 -1
  12. package/docs-site/commands.html +1 -1
  13. package/docs-site/context-engine.html +1 -1
  14. package/docs-site/en/architecture.html +1 -1
  15. package/docs-site/en/benchmark.html +1 -1
  16. package/docs-site/en/commands.html +1 -1
  17. package/docs-site/en/context-engine.html +1 -1
  18. package/docs-site/en/index.html +1 -1
  19. package/docs-site/en/mcp-approval.html +1 -1
  20. package/docs-site/en/mcp-auth.html +1 -1
  21. package/docs-site/en/mcp-servers.html +1 -1
  22. package/docs-site/en/mcp.html +1 -1
  23. package/docs-site/en/permissions.html +1 -1
  24. package/docs-site/en/profiles.html +1 -1
  25. package/docs-site/en/quickstart.html +1 -1
  26. package/docs-site/en/runtime.html +1 -1
  27. package/docs-site/en/scope.html +1 -1
  28. package/docs-site/en/security.html +1 -1
  29. package/docs-site/en/team.html +1 -1
  30. package/docs-site/en/whats-new.html +1 -1
  31. package/docs-site/en/workflows.html +1 -1
  32. package/docs-site/index.html +1 -1
  33. package/docs-site/mcp-approval.html +1 -1
  34. package/docs-site/mcp-auth.html +1 -1
  35. package/docs-site/mcp-servers.html +1 -1
  36. package/docs-site/mcp.html +1 -1
  37. package/docs-site/permissions.html +1 -1
  38. package/docs-site/profiles.html +1 -1
  39. package/docs-site/quickstart.html +1 -1
  40. package/docs-site/runtime.html +1 -1
  41. package/docs-site/scope.html +1 -1
  42. package/docs-site/security.html +1 -1
  43. package/docs-site/team.html +1 -1
  44. package/docs-site/whats-new.html +1 -1
  45. package/docs-site/workflows.html +1 -1
  46. package/package.json +2 -3
  47. package/packages/piagent-core/capabilities/capability-verification-cache.js +280 -0
  48. package/packages/piagent-core/capabilities/runtime-integrity.js +10 -1
  49. package/packages/piagent-core/extensions/document-intake.ts +24 -10
  50. package/packages/piagent-core/extensions/piagent-guard.ts +28 -36
  51. package/packages/piagent-core/extensions/state-retention.js +77 -44
  52. package/packages/piagent-core/package.json +1 -1
@@ -1,3 +1,4 @@
1
+ import crypto from "node:crypto";
1
2
  import fs from "node:fs";
2
3
  import path from "node:path";
3
4
  import { ensurePrivateStateDirectory, resolveLocalStatePath } from "./local-state-path.js";
@@ -16,75 +17,107 @@ function processAlive(pid) {
16
17
  }
17
18
  }
18
19
 
20
+ function ensurePrivateParent(filePath, projectRoot) {
21
+ const safeFilePath = projectRoot
22
+ ? resolveLocalStatePath(projectRoot, filePath, { label: "Local JSONL state" })
23
+ : filePath;
24
+ if (projectRoot) ensurePrivateStateDirectory(projectRoot, path.dirname(filePath), "Local JSONL state directory");
25
+ else fs.mkdirSync(path.dirname(safeFilePath), { recursive: true, mode: 0o700 });
26
+ try {
27
+ fs.chmodSync(path.dirname(safeFilePath), 0o700);
28
+ } catch {
29
+ // Best effort on filesystems that do not expose POSIX modes.
30
+ }
31
+ return safeFilePath;
32
+ }
33
+
34
+ function publishOwnedLock(lockPath) {
35
+ let candidatePath;
36
+ let descriptor;
37
+ while (true) {
38
+ const candidate = `${lockPath}.${process.pid}.${crypto.randomBytes(12).toString("hex")}.candidate`;
39
+ try {
40
+ descriptor = fs.openSync(candidate, "wx", 0o600);
41
+ candidatePath = candidate;
42
+ break;
43
+ } catch (error) {
44
+ if (error?.code !== "EEXIST") throw error;
45
+ }
46
+ }
47
+ try {
48
+ fs.writeFileSync(descriptor, `${process.pid}\n`);
49
+ fs.closeSync(descriptor);
50
+ descriptor = undefined;
51
+ try {
52
+ fs.linkSync(candidatePath, lockPath);
53
+ const stat = fs.lstatSync(candidatePath);
54
+ return { device: stat.dev, inode: stat.ino };
55
+ } catch (error) {
56
+ if (error?.code === "EEXIST") return undefined;
57
+ throw error;
58
+ }
59
+ } finally {
60
+ if (descriptor !== undefined) {
61
+ try { fs.closeSync(descriptor); } catch {}
62
+ }
63
+ fs.rmSync(candidatePath, { force: true });
64
+ }
65
+ }
66
+
67
+ function releaseOwnedLock(lockPath, owner) {
68
+ try {
69
+ const stat = fs.lstatSync(lockPath);
70
+ if (!stat.isFile() || stat.isSymbolicLink() || stat.dev !== owner.device || stat.ino !== owner.inode) return;
71
+ fs.rmSync(lockPath, { force: true });
72
+ } catch (error) {
73
+ if (error?.code !== "ENOENT") throw error;
74
+ }
75
+ }
76
+
19
77
  function recoverDeadLock(lockPath) {
20
78
  const recoveryPath = `${lockPath}.recovery`;
21
- let recoveryDescriptor;
79
+ const recoveryOwner = publishOwnedLock(recoveryPath);
80
+ // A recovery owner is never stolen automatically: without a kernel
81
+ // compare-and-delete primitive, doing so can delete a live successor. A
82
+ // crashed recovery owner therefore fails closed and needs operator cleanup.
83
+ if (!recoveryOwner) return false;
22
84
  try {
23
- recoveryDescriptor = fs.openSync(recoveryPath, "wx", 0o600);
24
85
  let stat;
25
- let owner = null;
86
+ let ownerText;
26
87
  try {
27
88
  stat = fs.lstatSync(lockPath);
28
89
  if (!stat.isFile() || stat.isSymbolicLink()) return false;
29
- owner = Number.parseInt(fs.readFileSync(lockPath, "utf8").trim(), 10);
90
+ ownerText = fs.readFileSync(lockPath, "utf8").trim();
30
91
  } catch (error) {
31
92
  return error?.code === "ENOENT";
32
93
  }
33
- const validDeadOwner = Number.isInteger(owner) && owner > 0 && !processAlive(owner);
34
- const malformedAndStale = (!Number.isInteger(owner) || owner <= 0) && Date.now() - stat.mtimeMs > LOCK_STALE_MS;
94
+ const parsedOwner = /^[1-9]\d*$/.test(ownerText) ? Number(ownerText) : undefined;
95
+ const owner = Number.isSafeInteger(parsedOwner) && parsedOwner > 0 ? parsedOwner : undefined;
96
+ const validDeadOwner = owner !== undefined && !processAlive(owner);
97
+ const malformedAndStale = owner === undefined && Date.now() - stat.mtimeMs > LOCK_STALE_MS;
35
98
  if (!validDeadOwner && !malformedAndStale) return false;
36
99
  fs.rmSync(lockPath, { force: true });
37
100
  return true;
38
- } catch (error) {
39
- if (error?.code === "EEXIST") return false;
40
- throw error;
41
101
  } finally {
42
- if (recoveryDescriptor !== undefined) {
43
- try { fs.closeSync(recoveryDescriptor); } catch {}
44
- fs.rmSync(recoveryPath, { force: true });
45
- }
102
+ releaseOwnedLock(recoveryPath, recoveryOwner);
46
103
  }
47
104
  }
48
105
 
49
- function ensurePrivateParent(filePath, projectRoot) {
50
- const safeFilePath = projectRoot
51
- ? resolveLocalStatePath(projectRoot, filePath, { label: "Local JSONL state" })
52
- : filePath;
53
- if (projectRoot) ensurePrivateStateDirectory(projectRoot, path.dirname(filePath), "Local JSONL state directory");
54
- else fs.mkdirSync(path.dirname(safeFilePath), { recursive: true, mode: 0o700 });
55
- try {
56
- fs.chmodSync(path.dirname(safeFilePath), 0o700);
57
- } catch {
58
- // Best effort on filesystems that do not expose POSIX modes.
59
- }
60
- return safeFilePath;
61
- }
62
-
63
106
  function withFileLock(filePath, action) {
64
107
  const lockPath = `${filePath}.lock`;
65
108
  const deadline = Date.now() + LOCK_WAIT_MS;
66
- let descriptor;
67
- while (descriptor === undefined) {
68
- try {
69
- descriptor = fs.openSync(lockPath, "wx", 0o600);
70
- fs.writeFileSync(descriptor, `${process.pid}\n`);
71
- } catch (error) {
72
- if (error?.code !== "EEXIST") throw error;
73
- if (recoverDeadLock(lockPath)) continue;
74
- if (Date.now() >= deadline) {
75
- throw new Error(`Timed out waiting for state lock: ${path.basename(filePath)}`);
76
- }
77
- Atomics.wait(sleepBuffer, 0, 0, 10);
109
+ let owner;
110
+ while (!(owner = publishOwnedLock(lockPath))) {
111
+ if (recoverDeadLock(lockPath)) continue;
112
+ if (Date.now() >= deadline) {
113
+ throw new Error(`Timed out waiting for state lock: ${path.basename(filePath)}`);
78
114
  }
115
+ Atomics.wait(sleepBuffer, 0, 0, 10);
79
116
  }
80
117
  try {
81
118
  return action();
82
119
  } finally {
83
- try {
84
- fs.closeSync(descriptor);
85
- } finally {
86
- fs.rmSync(lockPath, { force: true });
87
- }
120
+ releaseOwnedLock(lockPath, owner);
88
121
  }
89
122
  }
90
123
 
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@piagent/core",
3
- "version": "1.3.0-ie.9",
3
+ "version": "1.3.1",
4
4
  "type": "module",
5
5
  "private": true,
6
6
  "license": "MIT",