@junghanacs/entwurf 0.14.2 → 0.15.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.
- package/AGENTS.md +9 -5
- package/CHANGELOG.md +149 -0
- package/DELIVERY.md +72 -59
- package/README.md +120 -20
- package/VERIFY.md +10 -7
- package/demo/README.md +1 -1
- package/docs/acp-backend-rail.md +2 -2
- package/docs/external-mcp-host.md +26 -4
- package/docs/setup-clean-host.md +118 -18
- package/mcp/entwurf-bridge/dist/mcp/entwurf-bridge/src/index.js +8 -6
- package/mcp/entwurf-bridge/dist/pi-extensions/entwurf-capabilities.json +1 -0
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/copilot-fresh-preflight.js +253 -0
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/meta-sender-identity.js +10 -2
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/meta-session.js +102 -28
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/mux-fresh-call.js +77 -11
- package/mcp/entwurf-bridge/dist/pi-extensions/meta-bridge-hook-copilot.js +288 -0
- package/mcp/entwurf-bridge/src/index.ts +10 -6
- package/mcp/entwurf-bridge/tsconfig.build.json +2 -1
- package/package.json +22 -11
- package/pi/copilot-receive/entwurf-receive/extension.mjs +323 -0
- package/pi/entwurf-capabilities.json +1 -0
- package/pi/meta-bridge-copilot/.claude-plugin/marketplace.json +11 -0
- package/pi/meta-bridge-copilot/entwurf-meta-receive-copilot/.claude-plugin/plugin.json +5 -0
- package/pi/meta-bridge-copilot/entwurf-meta-receive-copilot/hooks/hooks.json +7 -0
- package/pi/meta-bridge-copilot/entwurf-meta-receive-copilot/scripts/copilot-hook-launch.sh +85 -0
- package/pi-extensions/entwurf-control.ts +17 -8
- package/pi-extensions/lib/copilot-fresh-preflight.ts +251 -0
- package/pi-extensions/lib/meta-sender-identity.ts +10 -2
- package/pi-extensions/lib/meta-session.ts +102 -29
- package/pi-extensions/lib/mux-fresh-call.ts +82 -11
- package/pi-extensions/meta-bridge-hook-copilot.ts +323 -0
- package/run.sh +1079 -186
- package/scripts/agy-bridge.sh +4 -2
- package/scripts/check-copilot-birth-hook.ts +1174 -0
- package/scripts/check-copilot-launch.ts +395 -0
- package/scripts/check-copilot-receive-arm.ts +734 -0
- package/scripts/check-copilot-statusline.ts +122 -0
- package/scripts/check-entwurf-capabilities.ts +25 -6
- package/scripts/check-fresh-cut-gate.sh +104 -27
- package/scripts/check-gate-qualification.ts +10 -1
- package/scripts/check-install-container.sh +2 -2
- package/scripts/check-install-surface.ts +1 -1
- package/scripts/check-meta-capability-source.ts +89 -5
- package/scripts/check-meta-doctor-oracle.sh +26 -0
- package/scripts/check-meta-manifest-schema.py +38 -0
- package/scripts/check-meta-receiver-marker.ts +25 -2
- package/scripts/check-meta-session.ts +32 -0
- package/scripts/check-setup-qualification.sh +126 -0
- package/scripts/copilot-bridge-doctor.sh +242 -0
- package/scripts/copilot-bridge-install.sh +384 -0
- package/scripts/copilot-bridge-oracle.sh +223 -0
- package/scripts/copilot-bridge-uninstall.sh +137 -0
- package/scripts/copilot-launch.sh +236 -0
- package/scripts/copilot-mcp-bridge.sh +177 -0
- package/scripts/copilot-mcp-config.py +221 -0
- package/scripts/copilot-receive-bridge.sh +483 -0
- package/scripts/copilot-statusline-bridge.sh +149 -0
- package/scripts/copilot-statusline-config.py +201 -0
- package/scripts/copilot-statusline.sh +68 -0
- package/scripts/dev-bin.sh +53 -9
- package/scripts/fake-copilot-vendor.sh +67 -0
- package/scripts/meta-bridge-hook-log.sh +17 -5
- package/scripts/mutants/capability-cache.json +64 -0
- package/scripts/mutants/copilot-birth.json +244 -0
- package/scripts/mutants/copilot-launch.json +187 -0
- package/scripts/mutants/copilot-receive.json +263 -0
- package/scripts/mutants/fresh-cut.json +17 -0
- package/scripts/mutants/mux-fresh-call.json +216 -2
- package/scripts/mutants/pack-install.json +17 -0
- package/scripts/mutants/pi-package-ownership.json +92 -0
- package/scripts/mutants/setup-verdict.json +148 -0
- package/scripts/mutants/source-install.json +32 -0
- package/scripts/pi_settings_io.py +22 -0
- package/scripts/raw-async-delivery/README.md +170 -125
- package/scripts/raw-async-delivery/copilot-enqueue-addressed.sh +35 -0
- package/scripts/raw-async-delivery/copilot-extension-receive/extension.mjs +123 -0
- package/scripts/register-pi-package.py +525 -7
- package/scripts/register-pi-provider.py +162 -12
- package/scripts/smoke-agy-install-state.sh +86 -30
- package/scripts/smoke-agy-statusline-state.sh +9 -6
- package/scripts/smoke-copilot-mcp-state.sh +153 -0
- package/scripts/smoke-copilot-statusline-state.sh +131 -0
- package/scripts/smoke-mux-fresh-call-live.ts +2 -0
- package/scripts/smoke-mux-lifecycle-live.ts +3 -1
- package/scripts/smoke-setup-verdict.sh +291 -0
- package/scripts/smoke-user-scope-citizen.sh +388 -1
- package/scripts/tsconfig.json +1 -0
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* entwurf mailbox receiver — GitHub Copilot CLI extension.
|
|
3
|
+
*
|
|
4
|
+
* WHAT THIS REPLACES. The earlier Copilot probe reached the TUI through the
|
|
5
|
+
* hidden `--ui-server` loopback port, and that rail was refused admission for a
|
|
6
|
+
* reason no amount of care could fix from outside: its authentication was not
|
|
7
|
+
* established (an unauthenticated SDK client connected; a token-bearing one got
|
|
8
|
+
* AUTHENTICATION_NOT_CONFIGURED). This rail does not answer that objection — it
|
|
9
|
+
* removes it. An extension is forked by the CLI itself and speaks JSON-RPC over
|
|
10
|
+
* its own stdio, so there is no port, no token, and no listener to authenticate.
|
|
11
|
+
* The trust boundary is the process fork.
|
|
12
|
+
*
|
|
13
|
+
* WHAT IT DOES. `joinSession()` attaches to the CLI's foreground session; the
|
|
14
|
+
* extension then watches ONE per-session signal file and calls `session.send()`
|
|
15
|
+
* when it changes. `fs.watch` -> `session.send()` is the pattern the bundled SDK
|
|
16
|
+
* documents itself (copilot-sdk/docs/examples.md, "Detecting when the plan file
|
|
17
|
+
* is created or edited"), not a mechanism discovered by inspection.
|
|
18
|
+
*
|
|
19
|
+
* ADDRESSED, NEVER BROADCAST. Each receiver owns `<root>/<sessionId>/` and
|
|
20
|
+
* watches only its own signal, so a sender pokes exactly one session rather than
|
|
21
|
+
* broadcasting. A two-process isolation run was observed, but its decisive B log
|
|
22
|
+
* was not preserved; managed admission must rerun that D3 control.
|
|
23
|
+
*
|
|
24
|
+
* LAUNCH CONTRACT. Extensions sit behind an experimental feature flag; without
|
|
25
|
+
* it the CLI never scans for them and this file is inert with no error:
|
|
26
|
+
*
|
|
27
|
+
* COPILOT_CLI_ENABLED_FEATURE_FLAGS=EXTENSIONS copilot --model auto
|
|
28
|
+
*
|
|
29
|
+
* `--experimental` is NOT required (measured separately). Discovery scopes are
|
|
30
|
+
* user (`~/.copilot/extensions/`), plugin, session, and — interactive mode only
|
|
31
|
+
* — project (`.github/extensions/`).
|
|
32
|
+
*/
|
|
33
|
+
|
|
34
|
+
import {
|
|
35
|
+
appendFileSync,
|
|
36
|
+
existsSync,
|
|
37
|
+
mkdirSync,
|
|
38
|
+
readdirSync,
|
|
39
|
+
readFileSync,
|
|
40
|
+
renameSync,
|
|
41
|
+
watch,
|
|
42
|
+
writeFileSync,
|
|
43
|
+
} from "node:fs";
|
|
44
|
+
import { homedir } from "node:os";
|
|
45
|
+
import { join } from "node:path";
|
|
46
|
+
import { joinSession } from "@github/copilot-sdk/extension";
|
|
47
|
+
|
|
48
|
+
const root = process.env.COPILOT_MAILBOX_ROOT ?? join(homedir(), ".copilot", "mailbox");
|
|
49
|
+
|
|
50
|
+
const session = await joinSession();
|
|
51
|
+
const box = join(root, session.sessionId);
|
|
52
|
+
mkdirSync(box, { recursive: true });
|
|
53
|
+
const logPath = join(box, "ext.log");
|
|
54
|
+
const log = (line) => appendFileSync(logPath, `${new Date().toISOString()} ${line}\n`);
|
|
55
|
+
|
|
56
|
+
// The sender needs an address, and scraping ~/.copilot storage for one is what
|
|
57
|
+
// the old command-hook probe failed at (its sessionStart input carries no
|
|
58
|
+
// sessionId). The receiver publishes its own identity instead, so discovery is
|
|
59
|
+
// a file this process wrote about itself rather than an inference about it.
|
|
60
|
+
writeFileSync(
|
|
61
|
+
join(box, "ready.json"),
|
|
62
|
+
JSON.stringify(
|
|
63
|
+
{
|
|
64
|
+
sessionId: session.sessionId,
|
|
65
|
+
pid: process.pid,
|
|
66
|
+
cwd: process.cwd(),
|
|
67
|
+
armedAt: new Date().toISOString(),
|
|
68
|
+
},
|
|
69
|
+
null,
|
|
70
|
+
2,
|
|
71
|
+
),
|
|
72
|
+
);
|
|
73
|
+
|
|
74
|
+
const signal = join(box, "inbox.signal");
|
|
75
|
+
if (!existsSync(signal)) writeFileSync(signal, "");
|
|
76
|
+
log(`ARMED sessionId=${session.sessionId} signal=${signal}`);
|
|
77
|
+
|
|
78
|
+
for (const type of ["user.message", "assistant.turn_start", "assistant.message", "session.idle"]) {
|
|
79
|
+
session.on(type, (event) => {
|
|
80
|
+
const d = event?.data ?? {};
|
|
81
|
+
log(
|
|
82
|
+
`EVENT ${type} ${JSON.stringify({
|
|
83
|
+
turnId: d.turnId,
|
|
84
|
+
content: typeof d.content === "string" ? d.content.slice(0, 200) : undefined,
|
|
85
|
+
source: d.source,
|
|
86
|
+
})}`,
|
|
87
|
+
);
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// Serialized because a burst of writes to one signal file is normal and two
|
|
92
|
+
// overlapping drains would rename the same `.msg` twice.
|
|
93
|
+
let busy = false;
|
|
94
|
+
async function drain(why) {
|
|
95
|
+
if (busy) return;
|
|
96
|
+
busy = true;
|
|
97
|
+
try {
|
|
98
|
+
const pending = readdirSync(box)
|
|
99
|
+
.filter((f) => f.endsWith(".msg"))
|
|
100
|
+
.sort();
|
|
101
|
+
for (const name of pending) {
|
|
102
|
+
const from = join(box, name);
|
|
103
|
+
const to = `${from}.delivered`;
|
|
104
|
+
// Rename BEFORE sending: a crash between the two loses one delivery,
|
|
105
|
+
// but sending before renaming would replay it on every later poke.
|
|
106
|
+
renameSync(from, to);
|
|
107
|
+
const body = readFileSync(to, "utf-8");
|
|
108
|
+
log(`DELIVER (${why}) ${name} bytes=${body.length}`);
|
|
109
|
+
await session.send({ prompt: body, mode: "enqueue" });
|
|
110
|
+
log(`SENT ${name}`);
|
|
111
|
+
}
|
|
112
|
+
} catch (err) {
|
|
113
|
+
log(`ERROR ${String(err)}`);
|
|
114
|
+
} finally {
|
|
115
|
+
busy = false;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
watch(signal, () => {
|
|
120
|
+
void drain("signal");
|
|
121
|
+
});
|
|
122
|
+
// Messages that arrived while nothing was armed are still owed a delivery.
|
|
123
|
+
void drain("startup");
|