genesis-compiler 1.7.7 → 1.7.8
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/docs/prompt-integration.md +21 -3
- package/package.json +1 -1
- package/src/cli.js +21 -8
- package/src/index/host-context-resolver.js +14 -1
|
@@ -82,7 +82,25 @@ JSON on stdin:
|
|
|
82
82
|
```
|
|
83
83
|
|
|
84
84
|
For `genesis hook turn`, the input has the same shape with `scope: "turn"`.
|
|
85
|
-
The executable returns
|
|
85
|
+
The executable returns exactly one JSON result:
|
|
86
|
+
|
|
87
|
+
- `{"kind":"unclaimed"}`: this native conversation is not managed by the host;
|
|
88
|
+
Genesis delivers its standalone guide.
|
|
89
|
+
- `{"kind":"contribution","text":"..."}`: Genesis composes this bounded host
|
|
90
|
+
contribution with its own session guide (or bounded turn contribution).
|
|
91
|
+
- `{"kind":"delivery","text":"..."}`: the host owns the complete session
|
|
92
|
+
delivery; Genesis forwards it without adding its standalone copy. Empty text
|
|
93
|
+
means the host already delivers through a native system field. This result
|
|
94
|
+
is valid only for session scope.
|
|
95
|
+
|
|
96
|
+
Malformed results and configured-resolver failures fail the hook. An unavailable
|
|
97
|
+
claimed owner must fail rather than returning `unclaimed`. Ownership belongs to
|
|
98
|
+
the native conversation: these results do not change project hook files, so
|
|
99
|
+
independent CLI and managed conversations can use the same project. The host
|
|
100
|
+
owns durable bindings, ancestry verification and restoration across reconnection.
|
|
101
|
+
No host framework dependency is added to Genesis.
|
|
102
|
+
|
|
103
|
+
Codex and Claude Code
|
|
86
104
|
hook input and OpenCode's transform callbacks supply the native session id;
|
|
87
105
|
Genesis extracts that id but does not understand the host registry or the
|
|
88
106
|
provider's prompt-placement rules. The Codex and Claude prompt hooks and OpenCode message
|
|
@@ -104,8 +122,8 @@ Codex hook settings. A host can render these fields as an actionable failure
|
|
|
104
122
|
instead of displaying the runtime stack.
|
|
105
123
|
|
|
106
124
|
The OpenCode session adapter also includes verified native `parentSessionIds`
|
|
107
|
-
in `GENESIS_HOST_CONTEXT_INPUT` for host command shims
|
|
108
|
-
receives the actual child's `providerSessionId`; Genesis does not interpret a
|
|
125
|
+
in `GENESIS_HOST_CONTEXT_INPUT` for host command shims and in the resolver
|
|
126
|
+
request when present (at most 32 IDs). The resolver receives the actual child's `providerSessionId`; Genesis does not interpret a
|
|
109
127
|
host's ownership rules.
|
|
110
128
|
|
|
111
129
|
After updating Genesis, hosts can inspect all generated integrations with
|
package/package.json
CHANGED
package/src/cli.js
CHANGED
|
@@ -426,8 +426,8 @@ function writeResult(command, result) {
|
|
|
426
426
|
line(process.stdout, `${command}: ${result.status}`);
|
|
427
427
|
}
|
|
428
428
|
|
|
429
|
-
async function
|
|
430
|
-
if (!environment[HOST_CONTEXT_RESOLVER_ENV]) return
|
|
429
|
+
async function hookSessionInput(environment = process.env) {
|
|
430
|
+
if (!environment[HOST_CONTEXT_RESOLVER_ENV]) return {};
|
|
431
431
|
let source = environment[HOST_CONTEXT_INPUT_ENV];
|
|
432
432
|
if (source === undefined && !process.stdin.isTTY) {
|
|
433
433
|
const chunks = [];
|
|
@@ -441,7 +441,7 @@ async function hookSessionId(environment = process.env) {
|
|
|
441
441
|
}
|
|
442
442
|
source = Buffer.concat(chunks).toString('utf8');
|
|
443
443
|
}
|
|
444
|
-
if (!String(source || '').trim()) return
|
|
444
|
+
if (!String(source || '').trim()) return {};
|
|
445
445
|
let input;
|
|
446
446
|
try {
|
|
447
447
|
input = JSON.parse(source);
|
|
@@ -449,7 +449,15 @@ async function hookSessionId(environment = process.env) {
|
|
|
449
449
|
fail('HOST_CONTEXT_INPUT_INVALID', 'The host hook input must contain JSON.');
|
|
450
450
|
}
|
|
451
451
|
const sessionId = input?.sessionId ?? input?.session_id;
|
|
452
|
-
|
|
452
|
+
const parentSessionIds = input?.parentSessionIds ?? [];
|
|
453
|
+
if (!Array.isArray(parentSessionIds) || parentSessionIds.length > 32 ||
|
|
454
|
+
parentSessionIds.some(id => typeof id !== 'string' || !id.trim())) {
|
|
455
|
+
fail('HOST_CONTEXT_INPUT_INVALID', 'Native parent session ids must be a bounded list of strings.');
|
|
456
|
+
}
|
|
457
|
+
return {
|
|
458
|
+
providerSessionId: typeof sessionId === 'string' && sessionId.trim() ? sessionId.trim() : null,
|
|
459
|
+
...(parentSessionIds.length ? { parentSessionIds } : {}),
|
|
460
|
+
};
|
|
453
461
|
}
|
|
454
462
|
|
|
455
463
|
async function cliStackPackages(projectRoot, supplied) {
|
|
@@ -574,19 +582,24 @@ async function execute({ command, operands, options }, { signal } = {}) {
|
|
|
574
582
|
if (scope === 'session' && process.env[SESSION_CONTEXT_INSTALLED_ENV] === '1') {
|
|
575
583
|
return { kind: 'session', status: 'ready', output: '' };
|
|
576
584
|
}
|
|
577
|
-
const
|
|
585
|
+
const nativeSession = await hookSessionInput();
|
|
578
586
|
const hostContext = await resolveConfiguredHostContext({
|
|
579
587
|
projectRoot,
|
|
580
|
-
|
|
588
|
+
...nativeSession,
|
|
581
589
|
scope,
|
|
582
590
|
});
|
|
591
|
+
// Ownership is resolved for this native conversation, not for the project.
|
|
592
|
+
// A host-owned delivery already contains everything its runtime requires.
|
|
593
|
+
if (hostContext?.kind === 'delivery') {
|
|
594
|
+
return { kind: 'session', status: 'ready', output: hostContext.text };
|
|
595
|
+
}
|
|
583
596
|
if (scope === 'turn') {
|
|
584
597
|
return hostContext === null
|
|
585
598
|
? { kind: 'turn', status: 'ready', output: '' }
|
|
586
599
|
: {
|
|
587
600
|
kind: 'turn',
|
|
588
601
|
...await projectTurnContext({
|
|
589
|
-
hostDriver: () => hostContext,
|
|
602
|
+
hostDriver: () => hostContext.text,
|
|
590
603
|
hostDriverInput: { scope: 'turn' },
|
|
591
604
|
}),
|
|
592
605
|
};
|
|
@@ -595,7 +608,7 @@ async function execute({ command, operands, options }, { signal } = {}) {
|
|
|
595
608
|
kind: 'session',
|
|
596
609
|
...await projectSessionContext({
|
|
597
610
|
...(hostContext === null ? {} : {
|
|
598
|
-
hostDriver: () => hostContext,
|
|
611
|
+
hostDriver: () => hostContext.text,
|
|
599
612
|
hostDriverInput: { scope: 'session' },
|
|
600
613
|
}),
|
|
601
614
|
projectRoot,
|
|
@@ -55,6 +55,7 @@ export async function resolveConfiguredHostContext({
|
|
|
55
55
|
environment = process.env,
|
|
56
56
|
projectRoot,
|
|
57
57
|
providerSessionId = null,
|
|
58
|
+
parentSessionIds = [],
|
|
58
59
|
scope = 'session',
|
|
59
60
|
} = {}) {
|
|
60
61
|
const command = configuredResolver(environment);
|
|
@@ -66,8 +67,20 @@ export async function resolveConfiguredHostContext({
|
|
|
66
67
|
input: {
|
|
67
68
|
scope,
|
|
68
69
|
providerSessionId: providerSessionId || null,
|
|
70
|
+
...(parentSessionIds.length ? { parentSessionIds } : {}),
|
|
69
71
|
data: resolverData(environment),
|
|
70
72
|
},
|
|
71
73
|
});
|
|
72
|
-
|
|
74
|
+
let result;
|
|
75
|
+
try { result = JSON.parse(output); } catch {
|
|
76
|
+
throw resolverError('The host context resolver must return a JSON delivery result.');
|
|
77
|
+
}
|
|
78
|
+
if (!result || typeof result !== 'object' || Array.isArray(result) ||
|
|
79
|
+
!['unclaimed', 'contribution', 'delivery'].includes(result.kind) ||
|
|
80
|
+
Object.keys(result).some((key) => !['kind', 'text'].includes(key)) ||
|
|
81
|
+
(result.kind === 'unclaimed' ? Object.hasOwn(result, 'text') : typeof result.text !== 'string') ||
|
|
82
|
+
(result.kind === 'delivery' && scope !== 'session')) {
|
|
83
|
+
throw resolverError('Invalid host context delivery result.');
|
|
84
|
+
}
|
|
85
|
+
return result.kind === 'unclaimed' ? null : result;
|
|
73
86
|
}
|