@junghanacs/entwurf 0.12.8 → 0.12.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.
Files changed (52) hide show
  1. package/AGENTS.md +4 -4
  2. package/CHANGELOG.md +20 -1
  3. package/DELIVERY.md +1 -1
  4. package/README.md +122 -10
  5. package/demo/README.md +2 -2
  6. package/docs/setup-clean-host.md +14 -3
  7. package/mcp/entwurf-bridge/dist/mcp/entwurf-bridge/src/index.js +9 -15
  8. package/mcp/entwurf-bridge/dist/pi-extensions/lib/acp/models.js +12 -12
  9. package/mcp/entwurf-bridge/dist/pi-extensions/lib/entwurf-fact-provider.js +1 -1
  10. package/mcp/entwurf-bridge/dist/pi-extensions/lib/entwurf-v2-production.js +5 -2
  11. package/mcp/entwurf-bridge/dist/pi-extensions/lib/entwurf-v2-spawn-production.js +8 -2
  12. package/mcp/entwurf-bridge/dist/pi-extensions/lib/meta-sender-identity.js +15 -4
  13. package/mcp/entwurf-bridge/dist/pi-extensions/lib/meta-session.js +483 -34
  14. package/mcp/entwurf-bridge/dist/pi-extensions/meta-bridge-hook.js +8 -3
  15. package/mcp/entwurf-bridge/dist/scripts/agy-imprint.js +14 -2
  16. package/mcp/entwurf-bridge/dist/scripts/meta-bridge-fresh-cut.js +155 -29
  17. package/mcp/entwurf-bridge/src/index.ts +10 -12
  18. package/package.json +7 -7
  19. package/pi-extensions/entwurf-control.ts +12 -12
  20. package/pi-extensions/lib/acp/models.ts +12 -12
  21. package/pi-extensions/lib/entwurf-fact-provider.ts +9 -2
  22. package/pi-extensions/lib/entwurf-v2-production.ts +8 -2
  23. package/pi-extensions/lib/entwurf-v2-spawn-production.ts +8 -2
  24. package/pi-extensions/lib/meta-sender-identity.ts +15 -5
  25. package/pi-extensions/lib/meta-session.ts +526 -38
  26. package/pi-extensions/meta-bridge-hook.ts +8 -2
  27. package/run.sh +30 -26
  28. package/scripts/agy-imprint.ts +15 -1
  29. package/scripts/check-acp-session-reuse.ts +1 -1
  30. package/scripts/check-acp-session-store.ts +3 -3
  31. package/scripts/check-agy-sender-identity.ts +83 -0
  32. package/scripts/check-entwurf-control-rpc.ts +2 -2
  33. package/scripts/check-entwurf-fact-provider.ts +9 -1
  34. package/scripts/check-entwurf-facts.ts +1 -1
  35. package/scripts/check-entwurf-mailbox-guard.ts +6 -2
  36. package/scripts/check-entwurf-resume-args.ts +6 -6
  37. package/scripts/check-entwurf-session-identity.ts +7 -6
  38. package/scripts/check-entwurf-v2-production.ts +4 -2
  39. package/scripts/check-entwurf-v2-spawn-production.ts +2 -2
  40. package/scripts/check-fresh-cut-gate.sh +305 -4
  41. package/scripts/check-meta-identity-consumers.ts +501 -1
  42. package/scripts/check-meta-listing.ts +91 -9
  43. package/scripts/check-meta-receiver-marker.ts +54 -0
  44. package/scripts/check-model-lock.ts +1 -1
  45. package/scripts/meta-bridge-fresh-cut.ts +164 -28
  46. package/scripts/pi_settings_io.py +65 -0
  47. package/scripts/register-pi-package.py +183 -37
  48. package/scripts/register-pi-provider.py +68 -10
  49. package/scripts/smoke-acp-socket-citizen-live.ts +2 -2
  50. package/scripts/smoke-meta-install-state.sh +1 -1
  51. package/scripts/smoke-pi-attach.ts +7 -2
  52. package/scripts/smoke-user-scope-citizen.sh +177 -0
@@ -35,7 +35,7 @@ import { buildResumePiArgs } from "./entwurf-resume-args.ts";
35
35
  import type { LockClaim } from "./entwurf-v2-lock.ts";
36
36
  import { releaseLock } from "./entwurf-v2-lock.ts";
37
37
  import type { SpawnBgPlan, SpawnBgResumeDeps, SpawnedChild } from "./entwurf-v2-spawn.ts";
38
- import { readMetaIdentityByGardenId } from "./meta-session.ts";
38
+ import { readAddressableMetaIdentity } from "./meta-session.ts";
39
39
  import { inspectControlSocketPath, type LstatLike, mapInspectionToLiveness } from "./socket-discovery.ts";
40
40
  import { probeSocketLiveness, type SocketLiveness } from "./socket-probe.ts";
41
41
 
@@ -120,13 +120,19 @@ export interface LaunchIdentity {
120
120
  * record remembers whose it is — a mismatch means the transcriptPath is stale or
121
121
  * foreign, and resuming it would put a turn into a different being's session).
122
122
  *
123
+ * That check is per-record, and per-record is not enough: the v2 lock domain is keyed on
124
+ * GARDEN ID, so two records sharing one `nativeSessionId` would each pass their own
125
+ * integrity check and resume the SAME transcript concurrently under two different locks.
126
+ * Hence {@link readAddressableMetaIdentity} rather than the plain targeted read (#52) —
127
+ * a resume is exactly the moment a record stops being data and becomes an address.
128
+ *
123
129
  * Everything else is unchanged authority: readSessionIdentity (first model_change) for
124
130
  * provider/model/cwd, getEntwurfExplicitExtensions for bridge re-injection (#29 fail-fast).
125
131
  * Throws on anything that makes a resume impossible; each throw becomes the watcher's
126
132
  * `spawn-start-failed` (no child to watch → release), never a silent no-op.
127
133
  */
128
134
  export function resolveResumeLaunchIdentity(plan: SpawnBgPlan): LaunchIdentity {
129
- const record = readMetaIdentityByGardenId(plan.sessionId);
135
+ const record = readAddressableMetaIdentity(plan.sessionId);
130
136
  if (record.backend !== "pi") {
131
137
  throw new Error(
132
138
  `entwurf-v2-spawn-production: ${plan.sessionId} is a ${record.backend} citizen — spawn-bg resume is the pi rail.`,
@@ -16,8 +16,13 @@
16
16
  * remains compatibility for an MCP host wrapper; the hook never writes a blind grandparent
17
17
  * marker because that may be the long-lived login shell.
18
18
  *
19
- * Two guards make a marker an IDENTITY rather than a hint, and a candidate is only trusted after
20
- * BOTH pass:
19
+ * Three guards make a marker an IDENTITY rather than a hint, and a candidate is only trusted
20
+ * after ALL of them pass:
21
+ * 0. a PLAUSIBLE owner (isPlausibleOwnerPid, shared with both writers and the generation cut):
22
+ * pid <= 1 cannot own a session, and guard 1 cannot catch it — for as long as the host
23
+ * is up, init IS still the very process the marker named. This is a rule about the
24
+ * marker's CLAIM, not about the owner's state, which is why it is asked first and is
25
+ * not opt-out (#53 A).
21
26
  * 1. pid + start-key (readMetaSenderMarker): the owner is still the very process that wrote it,
22
27
  * so a dead session's pid, reused by something else, cannot inherit its garden-id.
23
28
  * 2. the backing meta-record: the record store is the authority — a marker whose record was
@@ -32,6 +37,7 @@
32
37
  */
33
38
 
34
39
  import {
40
+ isPlausibleOwnerPid,
35
41
  type MetaBackend,
36
42
  type MetaIdentity,
37
43
  type MetaSenderMarker,
@@ -148,9 +154,13 @@ export function resolveTrustedMetaSenderIdentity(opts: ResolveTrustedMetaSenderO
148
154
  const marker = readMetaSenderMarker({ markerPath: opts.markerPath });
149
155
  if (marker) markers.push(marker);
150
156
  } else {
151
- const ownerPids = (opts.ownerPids ?? [process.ppid, parentPid(process.ppid) ?? 0]).filter(
152
- (p): p is number => typeof p === "number" && p > 0,
153
- );
157
+ // The candidate filter asks the same question the marker readers ask — CAN this
158
+ // pid own a session so it uses the same predicate rather than a second `> 0`
159
+ // literal. The default set is only the bridge's parent and grandparent, so init
160
+ // enters it just when the native host itself was reparented (a detached/daemonized
161
+ // Claude): narrow, but reachable, and a candidate that cannot be an owner has no
162
+ // business reaching the read at all (#53 A).
163
+ const ownerPids = (opts.ownerPids ?? [process.ppid, parentPid(process.ppid) ?? 0]).filter(isPlausibleOwnerPid);
154
164
  for (const ownerPid of [...new Set(ownerPids)]) {
155
165
  for (const backend of META_SENDER_BACKENDS) {
156
166
  const marker = readMetaSenderMarker({ backend, ownerPid, sendersDir: opts.sendersDir });