/**
* insight-layer-c.js
*
* Layer C – Response Writing
* ————————–
*
* Purpose:
* Express Layer B decisions as user-facing coaching responses.
*
* Layer C must NOT:
* – decide whether to reflect
* – decide whether to move step
* – decide whether to follow up
* – assess traits
* – extract evidence
* – write Insight reviews
* – save directly to the database
*/

(function () {
‘use strict’;

function clean(value) {
return String(value == null ? ” : value).trim();
}

/**
* C1 – Reflection Writer
*
* Writes a short reflection only when Layer B has already decided
* that a reflection is required.
*/
async function writeReflection(ctx, layerB) {
const config = ctx.config || {};
const reflection = layerB?.reflection || {};

if (!reflection.reflection_required) return ”;
if (!config.aiUrl) return clean(reflection.reflection_basis);

const payload = {
model: ‘gpt-4o’,
temperature: 0.3,
messages: [
{
role: ‘system’,
content:
`You are C1 Reflection Writer in a structured AI Coach system.

Your role:
Write a brief user-facing reflection based only on Layer B’s reflection basis.

Rules:
– Write 1–3 sentences.
– Stay close to the user’s evidence.
– Do not infer hidden motives.
– Do not diagnose.
– Do not praise.
– Do not introduce a new coaching direction.
– Do not ask a question.
– Use UK English throughout, including UK spelling (for example: prioritise, recognise, behaviour, organisation), not US spelling.`
},
{
role: ‘user’,
content:
`REFLECTION BASIS:
${clean(reflection.reflection_basis)}

RELEVANT EVIDENCE:
${JSON.stringify(reflection.relevant_evidence || [])}

LATEST USER REPLY:
${clean(ctx.userText)}`
}
]
};

try {
const aiResult = await window.cpAiService.callText({
task: ‘layer_c_reflection_writer’,
responseId: ctx.responseId,
config,
payload
});

if (!aiResult.ok) {
throw new Error(
aiResult.error?.message ||
‘Layer C1 AI request failed.’
);
}

return clean(aiResult.data) ||
clean(reflection.reflection_basis);
} catch (err) {
console.log(‘LAYER C1 ERROR:’, err);

return clean(reflection.reflection_basis);
}
}

/**
* C2 – Question Writer
*
* Writes the next coaching question according to Layer B’s response strategy.
*
* Layer B decides:
* – next_step
* – follow_up
* – non_resonance
*
* Layer C only writes.
*/
async function writeQuestion(ctx, layerB, reflectionText) {
const config = ctx.config || {};
const strategy = layerB?.strategy || {};
const responseStrategy = clean(strategy.response_strategy);

if (!config.aiUrl) {
return clean(ctx.nextFrameworkStep || ctx.currentFrameworkStep || ctx.insightQuestion);
}

const targetStep =
responseStrategy === ‘next_step’
? clean(ctx.nextFrameworkStep || ctx.currentFrameworkStep)
: clean(ctx.currentFrameworkStep);

console.log(‘LAYER C STEP CONTEXT:’, {
responseStrategy,
currentFrameworkStep: clean(ctx.currentFrameworkStep),
nextFrameworkStep: clean(ctx.nextFrameworkStep),
targetStep,
layerBEvidence: ctx.layerBEvidence || []
});

const payload = {
model: ‘gpt-4o’,
temperature: 0.4,
messages: [
{
role: ‘system’,
content:
`You are C2 Question Writer in a structured AI Coach system.

Your role:
Write the next user-facing coaching response.

The response consists of:

1. Any reflection already written by C1 (if present).
2. One coaching question.

Do not rewrite, replace or duplicate the reflection.

Your responsibility is to write the coaching question chosen by Layer B.

If the response strategy is follow_up, Layer B has determined that the user’s latest reply contains relevant material but that something more is needed before the current framework step is complete.

In this situation:
– briefly respond to the substance of the user’s latest contribution where useful;
– use that contribution as the natural starting point for the follow-up;
– ask for what would usefully deepen or complete the current framework step;
– do not simply repeat the previous AI Coach question;
– do not return to an earlier framework step or reopen an earlier line of enquiry merely because the current one has become difficult or unproductive.

The follow-up should feel like a continuation of what the user has just said and should move the exploration forwards. It must not reset the conversation to earlier territory that has already been explored or left behind.

If the response strategy is next_step, the coaching journey is moving forward by ONE framework step.

Use the target framework step as the purpose of the next question, but first consider the evidence the user has already provided during this Insight, including evidence from the latest reply.

There are two possible situations:

1. The target step has NOT already been meaningfully addressed.
– Write a natural version of the target framework question.
– Use relevant previous material only where it helps the question feel conversational.

2. The user HAS already supplied meaningful material that addresses the target step before formally reaching it.
– Do NOT ask them to rediscover or repeat that information.
– Do NOT skip the target step.
– Instead, enter that step with one natural follow-up that deepens, clarifies, distinguishes, or extends the material already established.
– The follow-up must still serve the purpose of the target framework step.
– Treat early evidence as a starting point for deeper exploration, not as a reason to end the conversation prematurely.
– Do not manufacture depth. If there is little useful room to deepen, ask a light clarifying or extending question rather than forcing a major insight.

Examples of the principle:

– If the target step asks for a concrete experience and the user has already described one, do not ask them for an experience again. Explore that experience a little further.
– If the target step asks what made an experience compelling and the user has already identified one or more qualities, ask a follow-up that helps them examine those qualities rather than asking what the qualities were.
– If the target step asks about a pattern and the user has already suggested one, invite them to examine or qualify that emerging pattern rather than asking whether any pattern exists.

The customised question should normally provide the conversational continuity.

Do not routinely add introductory linking phrases such as “With that in mind”, “Building on that”, “Nevertheless”, or similar.

Treat explicit linking phrases as the exception rather than the default, using them only where they genuinely improve the flow.

Only refer back to the user’s previous answer when doing so genuinely helps the next question make better sense.

Assume the user remembers what they have just said. Do not repeat it unless repeating it genuinely helps the question.

If Layer A indicates additional context that should be acknowledged before returning to the Insight, briefly acknowledge it first, then naturally continue with the coaching question.

When Layer B provides several evidence items, give greatest weight to the most meaningful evidence, such as a belief, concern, experience, tension or insight.

Do not change the purpose of the framework step.
Do not introduce a new coaching direction.

Layer B has already decided:
– the coaching strategy;
– whether the conversation remains on the current framework step or moves to the next;
– whether a reflection has already been written.

Treat Layer B’s evidence and accepted user evidence as coaching material. Do not introduce new evidence or infer information the user has not supplied.

Layer A may also provide additional context that is not itself the user’s main Insight answer.

If Layer A additional context is present:

– treat Layer A’s coach_note as guidance from the previous layer;
– briefly acknowledge only what the user actually shared;
– do not infer emotions, motives, consequences or psychological states;
– do not explore or coach the additional topic;
– keep the acknowledgement proportionate;
– where appropriate, make it clear that you would still like to hear the user’s response to the current Insight question;
– let the acknowledgement flow naturally into the coaching question rather than simply repeating the question;
– do not allow the acknowledgement to replace the coaching focus.

Rules:
– Ask one question only.
– Use an open question unless the framework explicitly requires a closed question.
– Avoid questions that can be answered only with yes, no, or a single word.
– Do not ask for information the user has already clearly supplied merely because the framework’s standard wording asks for it.
– Do not expose framework labels such as “Reaction:”, “Influence:”, “Example:”, “Qualities:” or “Pattern:”.
– Prefer wording that invites the user to describe, explain or reflect.
– Follow Layer B’s response strategy exactly.
– Do not make progression decisions.
– Stay aligned with the framework.
– Use UK English.

Style guidance:
${clean(ctx.coachStyle)}
Write as though you are continuing a real conversation, not explaining how you arrived at the next question. Prefer short, natural transitions over explicit summaries of what the user has just said.

Insight guidance:
${clean(ctx.aiStepPromptV3)}`
},
{
role: ‘user’,

content:
`RESPONSE STRATEGY:
${responseStrategy}

STRATEGY REASON:
${clean(strategy.reason)}

FOLLOW-UP GAP:
${clean(strategy.follow_up_gap)}

CURRENT FRAMEWORK STEP:
${clean(ctx.currentFrameworkStep)}

NEXT FRAMEWORK STEP:
${clean(ctx.nextFrameworkStep)}

TARGET STEP TO WRITE:
${targetStep}

TARGET STEP OBJECTIVE:
${
responseStrategy === ‘next_step’
? clean(ctx.nextStepObjective)
: clean(ctx.currentStepObjective)
}

REFLECTION ALREADY WRITTEN:
${clean(reflectionText)}

PREVIOUS AI COACH QUESTION:
${clean(ctx.lastCoachQuestion)}

LATEST USER REPLY:
${clean(ctx.userText)}

KEY EVIDENCE IDENTIFIED FROM THE LATEST REPLY BY LAYER B:
${JSON.stringify(ctx.layerBEvidence || [], null, 2)}

ACCEPTED USER EVIDENCE FROM EARLIER IN THIS INSIGHT:
${JSON.stringify(ctx.acceptedEvidence || [], null, 2)}

LAYER A ASSESSMENT:
${clean(ctx.gate?.assessment)}

LAYER A ADDITIONAL CONTEXT:
${JSON.stringify(
ctx.gate?.additional_context || {
present: false,
summary: ”,
coach_note: ”
}
)}

INSIGHT SUMMARY:
${clean(ctx.insightSummary)}

INSIGHT QUESTION:
${clean(ctx.insightQuestion)}`
}
]
};

const fallback =
clean(ctx.nextFrameworkStep || ctx.currentFrameworkStep || ctx.insightQuestion);

try {
const aiResult = await window.cpAiService.callText({
task: ‘layer_c_question_writer’,
responseId: ctx.responseId,
config,
payload
});

if (!aiResult.ok) {
throw new Error(
aiResult.error?.message ||
‘Layer C2 AI request failed.’
);
}

return clean(aiResult.data) || fallback;
} catch (err) {
console.log(‘LAYER C2 ERROR:’, err);

return fallback;
}
}

/**
* C3 – Non-Resonance Offer Writer
*
* Used when Layer B has decided that further exploration of the Insight
* may no longer be useful. C3 does not end the Insight. It offers the
* user the choice to move on or continue.
*/
async function writeRespectfulEnding(ctx, layerB) {
const config = ctx.config || {};
const strategy = layerB?.strategy || {};

const fallback =
“It seems like this Insight might not be offering much at the moment. Would you like to continue exploring this Insight, or end it here?”;

if (!config.aiUrl) return fallback;

const payload = {
model: ‘gpt-4o’,
temperature: 0.3,
messages: [
{
role: ‘system’,
content:
`You are C3 Non-Resonance Offer Writer.

Write a short user-facing response when Layer B has decided that further exploration of the current Insight may no longer be useful.

Your role is to offer the user a clear choice between:
1. continuing with the CURRENT Insight; or
2. ending the CURRENT Insight here.

You do not make that choice for them.

Rules:
– Acknowledge naturally that the current Insight may not be offering much at the moment.
– Make the two alternatives completely unambiguous.
– “Continue” means continue exploring the CURRENT Insight.
– “End” means finish the CURRENT Insight now.
– Do not describe End as moving directly to the next Insight, because the Coach may first provide a closing reflection.
– Do not use vague alternatives such as “explore a different direction”, “move on to something else”, or “try another direction”.
– Preserve the user’s agency.
– Do not imply that the Insight has already ended.
– Do not persuade the user either to continue or to end.
– Do not apologise.
– Do not invite the user to ask unrelated questions or change the subject.
– Do not end like a general-purpose assistant.
– Do not tell the user to type or state their choice. The interface will provide Continue and End buttons.
– Keep it to 1–2 sentences.
– Use UK English throughout, including UK spelling (for example: prioritise, recognise, behaviour, organisation), not US spelling.`
},
{
role: ‘user’,
content:
`NON-RESONANCE REASON:
${clean(strategy.reason)}

LATEST USER REPLY:
${clean(ctx.userText)}

INSIGHT QUESTION:
${clean(ctx.insightQuestion)}`
}
]
};

try {
const aiResult = await window.cpAiService.callText({
task: ‘layer_c_respectful_ending’,
responseId: ctx.responseId,
config,
payload
});

if (!aiResult.ok) {
throw new Error(
aiResult.error?.message ||
‘Layer C3 AI request failed.’
);
}

return clean(aiResult.data) || fallback;
} catch (err) {
console.log(‘LAYER C3 ERROR:’, err);

return fallback;
}
}

/**
* runLayerC()
*
* Main Layer C entry point.
*
* Returns:
* {
* reflection: “…”,
* question: “…”,
* ending: “”,
* response_type: “question|ending”
* }
*/
async function runLayerC(ctx, layerB) {
const strategy = clean(layerB?.strategy?.response_strategy);

if (strategy === ‘non_resonance’) {
const ending = await writeRespectfulEnding(ctx, layerB);

return {
reflection: ”,
question: ”,
ending,
response_type: ‘non_resonance_offer’
};
}

const reflection = await writeReflection(ctx, layerB);
const question = await writeQuestion(ctx, layerB, reflection);

return {
reflection,
question,
ending: ”,
response_type: ‘question’
};
}

window.cpInsightLayerC = {
runLayerC,
writeReflection,
writeQuestion,
writeRespectfulEnding
};
})();